[epiphany/wip/exalm/gtk4] WIP: Port to GTK4




commit 1d9cfa3ddb9aed36c65e3b26722c170c354cefe7
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Mon Nov 29 12:20:58 2021 +0500

    WIP: Port to GTK4

 embed/ephy-about-handler.c                         |   67 +-
 embed/ephy-download.c                              |   28 +-
 embed/ephy-embed-shell.c                           |   30 +-
 embed/ephy-embed-shell.h                           |    6 +-
 embed/ephy-embed.c                                 |   90 +-
 embed/ephy-find-toolbar.c                          |  252 +--
 embed/ephy-find-toolbar.h                          |    4 +-
 embed/ephy-reader-handler.c                        |   10 +-
 embed/ephy-web-view.c                              |  183 +-
 embed/meson.build                                  |    5 +-
 lib/contrib/dzl-fuzzy-mutable-index.c              |  722 +++++++
 lib/contrib/dzl-fuzzy-mutable-index.h              |   66 +
 lib/contrib/dzl-suggestion.c                       |  547 +++++
 lib/contrib/dzl-suggestion.h                       |   77 +
 lib/ephy-dnd.c                                     |  111 -
 lib/ephy-dnd.h                                     |   49 -
 lib/ephy-file-helpers.c                            |   12 +-
 lib/ephy-file-helpers.h                            |    2 +-
 lib/ephy-flatpak-utils.c                           |    2 +-
 lib/ephy-gui.c                                     |   57 +-
 lib/ephy-gui.h                                     |    5 -
 lib/ephy-notification-container.c                  |   45 +-
 lib/ephy-notification-container.h                  |    4 +-
 lib/ephy-notification.c                            |   20 +-
 lib/ephy-notification.h                            |    4 +-
 lib/ephy-suggestion.c                              |    1 -
 lib/ephy-suggestion.h                              |    2 +-
 lib/ephy-web-app-utils.c                           |    2 +-
 lib/meson.build                                    |    5 +-
 lib/widgets/contrib/nautilus-floating-bar.c        |  239 +--
 lib/widgets/ephy-certificate-dialog.c              |   30 +-
 lib/widgets/ephy-download-widget.c                 |   53 +-
 lib/widgets/ephy-download-widget.h                 |    4 +-
 lib/widgets/ephy-downloads-paintable.c             |  329 +++
 lib/widgets/ephy-downloads-paintable.h             |   33 +
 lib/widgets/ephy-downloads-popover.c               |   48 +-
 lib/widgets/ephy-downloads-popover.h               |    2 +-
 lib/widgets/ephy-file-chooser.c                    |   15 +-
 lib/widgets/ephy-location-entry.c                  | 2143 +++++++++++---------
 lib/widgets/ephy-location-entry.h                  |   12 +-
 lib/widgets/ephy-search-entry.c                    |  497 +++++
 lib/widgets/ephy-search-entry.h                    |   59 +
 lib/widgets/ephy-security-popover.c                |   47 +-
 lib/widgets/ephy-security-popover.h                |    3 +-
 lib/widgets/ephy-title-box.c                       |   72 +-
 lib/widgets/ephy-title-box.h                       |    5 +-
 lib/widgets/ephy-title-widget.c                    |    2 +-
 lib/widgets/meson.build                            |    8 +-
 meson.build                                        |   25 +-
 org.gnome.Epiphany.Canary.json.in                  |   47 +-
 org.gnome.Epiphany.json                            |   15 +-
 src/bookmarks/ephy-add-bookmark-popover.c          |   24 +-
 src/bookmarks/ephy-bookmark-properties.c           |  118 +-
 src/bookmarks/ephy-bookmark-row.c                  |   10 +-
 src/bookmarks/ephy-bookmarks-popover.c             |  159 +-
 src/ephy-action-bar-end.c                          |  167 +-
 src/ephy-action-bar-start.c                        |  584 +++---
 src/ephy-action-bar.c                              |    5 +-
 src/ephy-action-bar.h                              |    4 +-
 src/ephy-desktop-utils.c                           |    9 -
 src/ephy-desktop-utils.h                           |    2 -
 src/ephy-encoding-dialog.c                         |   10 +-
 src/ephy-firefox-sync-dialog.c                     |  119 +-
 src/ephy-firefox-sync-dialog.h                     |    4 +-
 src/ephy-fullscreen-box.c                          |  196 +-
 src/ephy-fullscreen-box.h                          |    2 +-
 src/ephy-header-bar.c                              |  166 +-
 src/ephy-header-bar.h                              |    4 +-
 src/ephy-history-dialog.c                          |  257 ++-
 src/ephy-history-dialog.h                          |    4 +-
 src/ephy-link.c                                    |    4 +-
 src/ephy-location-controller.c                     |   76 +-
 src/ephy-lockdown.c                                |    9 +-
 src/ephy-mouse-gesture-controller.c                |  246 +--
 src/ephy-page-row.c                                |   46 +-
 src/ephy-page-row.h                                |    6 +-
 src/ephy-pages-button.c                            |    2 +-
 src/ephy-pages-popover.c                           |   59 +-
 src/ephy-pages-popover.h                           |    2 +-
 src/ephy-pages-view.c                              |   16 +-
 src/ephy-session.c                                 |   40 +-
 src/ephy-shell.c                                   |   34 +-
 src/ephy-suggestion-model.c                        |    2 +-
 src/ephy-tab-view.c                                |  250 ++-
 src/ephy-tab-view.h                                |    8 +-
 src/ephy-web-extension-dialog.c                    |   80 +-
 src/ephy-web-extension-dialog.h                    |    2 +-
 src/ephy-window.c                                  |  627 +++---
 src/ephy-window.h                                  |    7 +-
 src/meson.build                                    |    2 +-
 src/popup-commands.c                               |   10 +-
 src/preferences/ephy-data-view.c                   |  115 +-
 src/preferences/ephy-data-view.h                   |    7 +-
 src/preferences/ephy-lang-row.c                    |  112 +-
 src/preferences/ephy-lang-row.h                    |   12 +-
 src/preferences/ephy-prefs-dialog.c                |   41 +-
 src/preferences/ephy-prefs-dialog.h                |    4 +-
 src/preferences/ephy-search-engine-listbox.c       |   18 +-
 src/preferences/ephy-search-engine-listbox.h       |    5 +-
 src/preferences/ephy-search-engine-row.c           |   41 +-
 src/preferences/ephy-search-engine-row.h           |    6 +-
 src/preferences/passwords-view.c                   |   78 +-
 src/preferences/prefs-appearance-page.c            |  117 +-
 src/preferences/prefs-appearance-page.h            |    4 +-
 src/preferences/prefs-general-page.c               |  311 +--
 src/preferences/prefs-general-page.h               |    7 +-
 src/preferences/prefs-privacy-page.c               |    4 +-
 src/preferences/prefs-privacy-page.h               |    5 +-
 src/resources/about.css                            |   40 +-
 src/resources/ephy-download-done-symbolic.svg      |    1 +
 src/resources/ephy-download-symbolic.svg           |    4 +-
 src/resources/epiphany.gresource.xml               |   11 +-
 src/resources/error.css                            |   14 +-
 src/resources/gtk/action-bar-end.ui                |   61 +-
 src/resources/gtk/action-bar-start.ui              |   99 +-
 src/resources/gtk/action-bar.ui                    |   31 +-
 src/resources/gtk/bookmark-properties.ui           |   17 +-
 src/resources/gtk/bookmark-row.ui                  |   12 +-
 src/resources/gtk/bookmarks-popover.ui             |  256 ++-
 src/resources/gtk/clear-data-view.ui               |   10 +-
 src/resources/gtk/data-view.ui                     |  123 +-
 src/resources/gtk/encoding-dialog.ui               |  156 +-
 src/resources/gtk/encoding-row.ui                  |    6 +-
 src/resources/gtk/firefox-sync-dialog.ui           |  287 ++-
 src/resources/gtk/history-dialog.ui                |  191 +-
 src/resources/gtk/lang-row.ui                      |   74 +-
 src/resources/gtk/location-entry.ui                |  283 +++
 src/resources/gtk/page-menu-popover.ui             |  484 ++---
 src/resources/gtk/page-row.ui                      |  108 +-
 src/resources/gtk/pages-button.ui                  |   13 +-
 src/resources/gtk/pages-popover.ui                 |    4 +-
 src/resources/gtk/pages-view.ui                    |   37 +-
 src/resources/gtk/passwords-view.ui                |    8 +-
 src/resources/gtk/prefs-appearance-page.ui         |  105 +-
 src/resources/gtk/prefs-dialog.ui                  |    9 +-
 src/resources/gtk/prefs-general-page.ui            |  117 +-
 src/resources/gtk/prefs-lang-dialog.ui             |   13 +-
 src/resources/gtk/prefs-privacy-page.ui            |   34 +-
 src/resources/gtk/search-engine-listbox.ui         |   10 +-
 src/resources/gtk/search-engine-row.ui             |   92 +-
 src/resources/gtk/synced-tabs-dialog.ui            |   15 +-
 src/resources/gtk/web-extensions-dialog.ui         |   66 +-
 src/resources/gtk/webapp-additional-urls-dialog.ui |   35 +-
 src/resources/parse-sass.sh                        |   25 -
 src/resources/style-dark.css                       |   27 +
 src/resources/style-hc.css                         |   11 +
 src/resources/style.css                            |  194 ++
 src/resources/themes/Adwaita-dark.css              |  316 ---
 src/resources/themes/Adwaita-dark.scss             |    4 -
 src/resources/themes/Adwaita.css                   |  316 ---
 src/resources/themes/Adwaita.scss                  |    4 -
 src/resources/themes/HighContrast.css              |  314 ---
 src/resources/themes/HighContrast.scss             |    6 -
 src/resources/themes/HighContrastInverse.css       |  314 ---
 src/resources/themes/HighContrastInverse.scss      |    6 -
 src/resources/themes/_Adwaita-base.scss            |  125 --
 src/resources/themes/_Adwaita-colored-window.scss  |  217 --
 src/resources/themes/_definitions.scss             |   20 -
 src/resources/themes/_shared-base.scss             |  255 ---
 src/resources/themes/elementary.css                |  143 --
 src/resources/themes/elementary.scss               |  161 --
 src/resources/themes/shared.css                    |   85 -
 src/resources/themes/shared.scss                   |    3 -
 src/synced-tabs-dialog.c                           |    2 +-
 src/webextension/api/pageaction.c                  |    8 +-
 src/webextension/ephy-web-extension-manager.c      |  102 +-
 src/window-commands.c                              |  122 +-
 tests/ephy-embed-shell-test.c                      |    4 +-
 tests/ephy-location-entry-test.c                   |   18 +-
 tests/ephy-web-view-test.c                         |    6 +-
 tests/meson.build                                  |    1 +
 171 files changed, 7427 insertions(+), 8563 deletions(-)
---
diff --git a/embed/ephy-about-handler.c b/embed/ephy-about-handler.c
index fc0459c97..6956c28a7 100644
--- a/embed/ephy-about-handler.c
+++ b/embed/ephy-about-handler.c
@@ -164,14 +164,26 @@ ephy_about_handler_handle_about (EphyAboutHandler       *handler,
 {
   char *data;
   char *version;
-  GtkIconInfo *icon_info;
+  g_autofree char *path = NULL;
+  GtkIconTheme *icon_theme;
+  g_autoptr (GtkIconPaintable) paintable = NULL;
 
   version = g_strdup_printf (_("Version %s"), VERSION);
 
-  icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (),
+  icon_theme = gtk_icon_theme_get_for_display (gdk_display_get_default ());
+  paintable = gtk_icon_theme_lookup_icon (icon_theme,
                                           APPLICATION_ID,
+                                          NULL,
                                           256,
-                                          GTK_ICON_LOOKUP_FORCE_SVG);
+                                          1,
+                                          GTK_TEXT_DIR_LTR,
+                                          GTK_ICON_LOOKUP_FORCE_REGULAR);
+
+  if (paintable) {
+    g_autoptr (GFile) file = gtk_icon_paintable_get_file (paintable);
+
+    path = g_file_get_path (file);
+  }
 
   data = g_strdup_printf ("<html><head><title>%s</title>"
                           "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
@@ -188,7 +200,7 @@ ephy_about_handler_handle_about (EphyAboutHandler       *handler,
                           "</table>"
                           "</div></div></body></html>",
                           _("About Web"),
-                          icon_info ? gtk_icon_info_get_filename (icon_info) : "",
+                          path ? path : "",
 #if !TECH_PREVIEW
                           _("Web"),
 #else
@@ -198,8 +210,6 @@ ephy_about_handler_handle_about (EphyAboutHandler       *handler,
                           _("A simple, clean, beautiful view of the web"),
                           "WebKitGTK", webkit_get_major_version (), webkit_get_minor_version (), 
webkit_get_micro_version ());
   g_free (version);
-  if (icon_info)
-    g_object_unref (icon_info);
 
   ephy_about_handler_finish_request (request, data, -1);
 
@@ -314,8 +324,9 @@ handle_applications_finished_cb (EphyAboutHandler       *handler,
 
     g_string_append (data_str, "</table></div></body></html>");
   } else {
-    g_autoptr (GtkIconInfo) icon_info = NULL;
-    g_autofree gchar *icon = g_strconcat ("application-x-addon-symbolic", NULL);
+    GtkIconTheme *icon_theme;
+    g_autoptr (GtkIconPaintable) paintable = NULL;
+    g_autofree char *path = NULL;
 
     g_string_append_printf (data_str, "<html><head><title>%s</title>"
                             "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
@@ -323,10 +334,21 @@ handle_applications_finished_cb (EphyAboutHandler       *handler,
                             "</head><body class=\"applications-body\">",
                             _("Applications"));
 
-    icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (),
-                                            icon,
+    icon_theme = gtk_icon_theme_get_for_display (gdk_display_get_default ());
+    paintable = gtk_icon_theme_lookup_icon (icon_theme,
+                                            "application-x-addon-symbolic",
+                                            NULL,
                                             128,
+                                            1,
+                                            GTK_TEXT_DIR_LTR,
                                             0);
+
+    if (paintable) {
+      g_autoptr (GFile) file = gtk_icon_paintable_get_file (paintable);
+
+      path = g_file_get_path (file);
+    }
+
     g_string_append_printf (data_str,
                             "  <div id=\"overview\" class=\"overview-empty\">\n"
                             "    <img src=\"file://%s\"/>\n"
@@ -334,7 +356,7 @@ handle_applications_finished_cb (EphyAboutHandler       *handler,
                             "    <div><p>%s</p></div>\n"
                             "  </div>\n"
                             "</body></html>\n",
-                            icon_info ? gtk_icon_info_get_filename (icon_info) : "",
+                            path ? path : "",
                             /* Displayed when opening applications without any installed web apps. */
                             _("Applications"), _("You can add your favorite website by clicking <b>Install 
Site as Web Application…</b> within the page menu."));
   }
@@ -412,13 +434,26 @@ history_service_query_urls_cb (EphyHistoryService     *history,
   list_length = g_list_length (urls);
 
   if (list_length == 0 || !success) {
-    GtkIconInfo *icon_info;
-    g_autofree gchar *icon = g_strconcat (APPLICATION_ID, "-symbolic", NULL);
+    GtkIconTheme *icon_theme;
+    g_autoptr (GtkIconPaintable) paintable = NULL;
+    g_autofree char *path = NULL;
+    g_autofree char *icon = g_strconcat (APPLICATION_ID, "-symbolic", NULL);
 
-    icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (),
+    icon_theme = gtk_icon_theme_get_for_display (gdk_display_get_default ());
+    paintable = gtk_icon_theme_lookup_icon (icon_theme,
                                             icon,
+                                            NULL,
                                             128,
+                                            1,
+                                            GTK_TEXT_DIR_LTR,
                                             0);
+
+    if (paintable) {
+      g_autoptr (GFile) file = gtk_icon_paintable_get_file (paintable);
+
+      path = g_file_get_path (file);
+    }
+
     g_string_append_printf (data_str,
                             "  <div id=\"overview\" class=\"overview-empty\">\n"
                             "    <img src=\"file://%s\"/>\n"
@@ -426,11 +461,9 @@ history_service_query_urls_cb (EphyHistoryService     *history,
                             "    <div><p>%s</p></div>\n"
                             "  </div>\n"
                             "</body></html>\n",
-                            icon_info ? gtk_icon_info_get_filename (icon_info) : "",
+                            path ? path : "",
                             /* Displayed when opening the browser for the first time. */
                             _("Welcome to Web"), _("Start browsing and your most-visited sites will appear 
here."));
-    if (icon_info)
-      g_object_unref (icon_info);
     goto out;
   }
 
diff --git a/embed/ephy-download.c b/embed/ephy-download.c
index ca81083c3..856ef66d1 100644
--- a/embed/ephy-download.c
+++ b/embed/ephy-download.c
@@ -776,7 +776,7 @@ filename_suggested_dialog_cb (GtkDialog             *dialog,
     ephy_download_cancel (data->download);
   }
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   g_object_unref (data->directory);
   g_free (data->suggested_filename);
@@ -813,9 +813,9 @@ filename_suggested_button_cb (GtkButton             *button,
                                          _("_Cancel"));
   gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (chooser), TRUE);
 
-  gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (chooser),
-                                            data->directory,
-                                            NULL);
+  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
+                                       data->directory,
+                                       NULL);
 
   g_signal_connect (chooser, "response",
                     G_CALLBACK (filename_suggested_file_chooser_cb),
@@ -867,7 +867,7 @@ filename_suggested_cb (EphyDownload *download,
   response = webkit_download_get_response (webkit_download);
 
   box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
-  gtk_box_pack_start (GTK_BOX (message_area), box, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (message_area), box);
 
   /* Type */
   content_length = g_format_size (webkit_uri_response_get_content_length (response));
@@ -875,36 +875,34 @@ filename_suggested_cb (EphyDownload *download,
   type_text = g_strdup_printf (_("Type: %s (%s)"), g_content_type_get_description (content_type), 
content_length);
   type_label = gtk_label_new (type_text);
   gtk_widget_set_margin_top (type_label, 12);
-  gtk_box_pack_start (GTK_BOX (box), type_label, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (box), type_label);
 
   /* From */
   from_text = g_strdup_printf (_("From: %s"), ephy_string_get_host_name (webkit_uri_response_get_uri 
(response)));
   from_label = gtk_label_new (from_text);
-  gtk_box_pack_start (GTK_BOX (box), from_label, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (box), from_label);
 
   /* Question */
   question_label = gtk_label_new (_("Where do you want to save the file?"));
   gtk_widget_set_margin_top (question_label, 12);
-  gtk_box_pack_start (GTK_BOX (box), question_label, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (box), question_label);
 
   /* File Chooser Button */
   button = gtk_button_new ();
-  gtk_box_pack_start (GTK_BOX (box), button, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (box), button);
 
   button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
   gtk_widget_set_hexpand (button_box, FALSE);
-  gtk_container_add (GTK_CONTAINER (button), button_box);
+  gtk_button_set_child (GTK_BUTTON (button), button_box);
 
-  gtk_container_add (GTK_CONTAINER (button_box),
-                     gtk_image_new_from_icon_name ("folder-symbolic", GTK_ICON_SIZE_BUTTON));
+  gtk_box_append (GTK_BOX (button_box),
+                  gtk_image_new_from_icon_name ("folder-symbolic"));
 
   button_label = gtk_label_new (NULL);
   gtk_label_set_ellipsize (GTK_LABEL (button_label), PANGO_ELLIPSIZE_END);
   gtk_label_set_xalign (GTK_LABEL (button_label), 0);
   gtk_widget_set_hexpand (button_label, TRUE);
-  gtk_container_add (GTK_CONTAINER (button_box), button_label);
-
-  gtk_widget_show_all (box);
+  gtk_box_append (GTK_BOX (button_box), button_label);
 
   directory_path = g_settings_get_string (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_LAST_DOWNLOAD_DIRECTORY);
 
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index cc5f9832c..04c7a4a68 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -98,7 +98,7 @@ static EphyEmbedShell *embed_shell = NULL;
 
 static void ephy_embed_shell_tabs_catalog_iface_init (EphyTabsCatalogInterface *iface);
 
-G_DEFINE_TYPE_WITH_CODE (EphyEmbedShell, ephy_embed_shell, DZL_TYPE_APPLICATION,
+G_DEFINE_TYPE_WITH_CODE (EphyEmbedShell, ephy_embed_shell, ADW_TYPE_APPLICATION,
                          G_ADD_PRIVATE (EphyEmbedShell)
                          G_IMPLEMENT_INTERFACE (EPHY_TYPE_TABS_CATALOG,
                                                 ephy_embed_shell_tabs_catalog_iface_init))
@@ -900,27 +900,6 @@ enable_itp_setting_changed_cb (GSettings      *settings,
                                                                        EPHY_PREFS_WEB_ENABLE_ITP));
 }
 
-static void
-update_system_scrollbars (EphyEmbedShell *shell)
-{
-  EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
-  const char *theme_name;
-  gboolean enabled;
-
-  g_object_get (gtk_settings_get_default (),
-                "gtk-theme_name", &theme_name,
-                NULL);
-
-  /* Don't enable system scrollbars for Adwaita */
-  enabled = g_strcmp0 (theme_name, "Adwaita") &&
-            g_strcmp0 (theme_name, "Adwaita-dark") &&
-            g_strcmp0 (theme_name, "HighContrast") &&
-            g_strcmp0 (theme_name, "HighContrastInverse");
-
-  webkit_web_context_set_use_system_appearance_for_scrollbars (priv->web_context,
-                                                               enabled);
-}
-
 static void
 ephy_embed_shell_startup (GApplication *application)
 {
@@ -1013,13 +992,6 @@ ephy_embed_shell_startup (GApplication *application)
 
   g_signal_connect_object (EPHY_SETTINGS_WEB, "changed::enable-itp",
                            G_CALLBACK (enable_itp_setting_changed_cb), shell, 0);
-
-  update_system_scrollbars (shell);
-
-  g_signal_connect_swapped (gtk_settings_get_default (),
-                            "notify::gtk-theme-name",
-                            G_CALLBACK (update_system_scrollbars),
-                            shell);
 }
 
 static void
diff --git a/embed/ephy-embed-shell.h b/embed/ephy-embed-shell.h
index 9464d42d4..2815c19bd 100644
--- a/embed/ephy-embed-shell.h
+++ b/embed/ephy-embed-shell.h
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include <dazzle.h>
+#include <adwaita.h>
 #include <webkit2/webkit2.h>
 
 #include "ephy-downloads-manager.h"
@@ -38,7 +38,7 @@ typedef struct _EphyFiltersManager EphyFiltersManager;
 
 #define EPHY_TYPE_EMBED_SHELL (ephy_embed_shell_get_type ())
 
-G_DECLARE_DERIVABLE_TYPE (EphyEmbedShell, ephy_embed_shell, EPHY, EMBED_SHELL, DzlApplication)
+G_DECLARE_DERIVABLE_TYPE (EphyEmbedShell, ephy_embed_shell, EPHY, EMBED_SHELL, AdwApplication)
 
 typedef enum
 {
@@ -54,7 +54,7 @@ typedef enum
 
 struct _EphyEmbedShellClass
 {
-  DzlApplicationClass parent_class;
+  AdwApplicationClass parent_class;
 
   void    (* restored_window)  (EphyEmbedShell *shell);
 };
diff --git a/embed/ephy-embed.c b/embed/ephy-embed.c
index 3d81f8783..fd11ca273 100644
--- a/embed/ephy-embed.c
+++ b/embed/ephy-embed.c
@@ -163,6 +163,7 @@ ephy_embed_set_statusbar_label (EphyEmbed  *embed,
   if (label == NULL || label[0] == '\0') {
     gtk_widget_hide (embed->floating_bar);
     gtk_widget_set_halign (embed->floating_bar, GTK_ALIGN_START);
+    gtk_widget_remove_css_class (embed->floating_bar, "end");
   } else
     gtk_widget_show (embed->floating_bar);
 }
@@ -245,7 +246,7 @@ ephy_embed_destroy_top_widgets (EphyEmbed *embed)
 
   for (iter = embed->destroy_on_transition_list; iter; iter = iter->next) {
     g_signal_handlers_disconnect_by_func (iter->data, remove_from_destroy_list_cb, embed);
-    gtk_widget_destroy (GTK_WIDGET (iter->data));
+    gtk_box_remove (embed->top_widgets_vbox, GTK_WIDGET (iter->data));
   }
 
   embed->destroy_on_transition_list = NULL;
@@ -310,7 +311,7 @@ load_changed_cb (WebKitWebView   *web_view,
   }
 }
 
-static void
+static gboolean
 ephy_embed_grab_focus (GtkWidget *widget)
 {
   GtkWidget *child;
@@ -318,7 +319,9 @@ ephy_embed_grab_focus (GtkWidget *widget)
   child = GTK_WIDGET (ephy_embed_get_web_view (EPHY_EMBED (widget)));
 
   if (child)
-    gtk_widget_grab_focus (child);
+    return gtk_widget_grab_focus (child);
+
+  return FALSE;
 }
 
 
@@ -686,24 +689,35 @@ ephy_embed_mapped_cb (GtkWidget *widget,
   ephy_embed_maybe_load_delayed_request ((EphyEmbed *)widget);
 }
 
-static gboolean
-on_enter_notify_event (GtkWidget        *widget,
-                       GdkEventCrossing *event,
-                       gpointer          user_data)
+static void
+floating_bar_motion_cb (GtkEventControllerMotion *self,
+                        double                    x,
+                        double                    y,
+                        EphyEmbed                *embed)
 {
-  EphyEmbed *embed = EPHY_EMBED (user_data);
+  graphene_rect_t bounds;
 
-  if (event->window != gtk_widget_get_window (embed->floating_bar))
-    return GDK_EVENT_PROPAGATE;
+  if (!gtk_widget_get_visible (embed->floating_bar))
+    return;
 
-  if (gtk_widget_get_halign (embed->floating_bar) == GTK_ALIGN_START)
-    gtk_widget_set_halign (embed->floating_bar, GTK_ALIGN_END);
-  else
-    gtk_widget_set_halign (embed->floating_bar, GTK_ALIGN_START);
+  g_assert (gtk_widget_compute_bounds (embed->floating_bar,
+                                       GTK_WIDGET (embed),
+                                       &bounds));
 
-  gtk_widget_queue_allocate (embed->overlay);
+  if (!gtk_widget_contains (embed->floating_bar,
+                            x - bounds.origin.x,
+                            y - bounds.origin.y)) {
+    return;
+  }
 
-  return GDK_EVENT_PROPAGATE;
+  if (gtk_widget_get_halign (embed->floating_bar) == GTK_ALIGN_START) {
+    gtk_widget_set_halign (embed->floating_bar, GTK_ALIGN_END);
+    gtk_widget_add_css_class (embed->floating_bar, "end");
+    gtk_widget_queue_allocate (GTK_WIDGET (embed->overlay));
+  } else {
+    gtk_widget_set_halign (embed->floating_bar, GTK_ALIGN_START);
+    gtk_widget_remove_css_class (embed->floating_bar, "end");
+  }
 }
 
 static void
@@ -712,6 +726,7 @@ ephy_embed_constructed (GObject *object)
   EphyEmbed *embed = (EphyEmbed *)object;
   EphyEmbedShell *shell = ephy_embed_shell_get_default ();
   WebKitWebInspector *inspector;
+  GtkEventController *controller;
 
   g_signal_connect (shell, "window-restored",
                     G_CALLBACK (ephy_embed_restored_window_cb), embed);
@@ -722,18 +737,16 @@ ephy_embed_constructed (GObject *object)
   /* Skeleton */
   embed->overlay = gtk_overlay_new ();
 
-  gtk_widget_add_events (embed->overlay,
-                         GDK_ENTER_NOTIFY_MASK |
-                         GDK_LEAVE_NOTIFY_MASK);
   gtk_widget_set_vexpand (embed->overlay, TRUE);
-  gtk_container_add (GTK_CONTAINER (embed->overlay), GTK_WIDGET (embed->web_view));
+  gtk_overlay_set_child (GTK_OVERLAY (embed->overlay), GTK_WIDGET (embed->web_view));
 
   /* Floating message popup for fullscreen mode. */
   embed->fullscreen_message_label = gtk_label_new (NULL);
   gtk_widget_set_name (embed->fullscreen_message_label, "fullscreen-popup");
   gtk_widget_set_halign (embed->fullscreen_message_label, GTK_ALIGN_CENTER);
   gtk_widget_set_valign (embed->fullscreen_message_label, GTK_ALIGN_CENTER);
-  gtk_widget_set_no_show_all (embed->fullscreen_message_label, TRUE);
+  gtk_widget_set_can_target (embed->fullscreen_message_label, FALSE);
+  gtk_widget_hide (embed->fullscreen_message_label);
   gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), embed->fullscreen_message_label);
   ephy_embed_set_fullscreen_message (embed, FALSE);
 
@@ -741,14 +754,13 @@ ephy_embed_constructed (GObject *object)
   embed->floating_bar = nautilus_floating_bar_new ();
   gtk_widget_set_halign (embed->floating_bar, GTK_ALIGN_START);
   gtk_widget_set_valign (embed->floating_bar, GTK_ALIGN_END);
-  gtk_widget_set_no_show_all (embed->floating_bar, TRUE);
-  g_signal_connect_object (embed->overlay, "enter-notify-event", G_CALLBACK (on_enter_notify_event), embed, 
0);
+  gtk_widget_hide (embed->floating_bar);
 
   gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), embed->floating_bar);
 
   if (embed->progress_bar_enabled) {
     embed->progress = gtk_progress_bar_new ();
-    gtk_style_context_add_class (gtk_widget_get_style_context (embed->progress), "osd");
+    gtk_widget_add_css_class (embed->progress, "osd");
     gtk_widget_set_halign (embed->progress, GTK_ALIGN_FILL);
     gtk_widget_set_valign (embed->progress, GTK_ALIGN_START);
     gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), embed->progress);
@@ -759,22 +771,14 @@ ephy_embed_constructed (GObject *object)
                     G_CALLBACK (ephy_embed_find_toolbar_close_cb),
                     embed);
 
-  gtk_box_pack_start (GTK_BOX (embed),
-                      GTK_WIDGET (embed->find_toolbar),
-                      FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (embed), GTK_WIDGET (embed->find_toolbar));
 
   if (embed->progress_bar_enabled)
     embed->progress_update_handler_id = g_signal_connect (embed->web_view, "notify::estimated-load-progress",
                                                           G_CALLBACK (progress_update), object);
 
-  gtk_box_pack_start (GTK_BOX (embed),
-                      GTK_WIDGET (embed->top_widgets_vbox),
-                      FALSE, TRUE, 0);
-  gtk_box_pack_start (GTK_BOX (embed), embed->overlay, FALSE, TRUE, 0);
-
-  gtk_widget_show (GTK_WIDGET (embed->top_widgets_vbox));
-  gtk_widget_show (GTK_WIDGET (embed->web_view));
-  gtk_widget_show_all (embed->overlay);
+  gtk_box_append (GTK_BOX (embed), GTK_WIDGET (embed->top_widgets_vbox));
+  gtk_box_append (GTK_BOX (embed), GTK_WIDGET (embed->overlay));
 
   g_object_connect (embed->web_view,
                     "signal::notify::title", G_CALLBACK (web_view_title_changed_cb), embed,
@@ -805,12 +809,14 @@ ephy_embed_constructed (GObject *object)
     gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_INFO);
     /* Translators: this means WebDriver control. */
     label = gtk_label_new (_("Web is being controlled by automation."));
-    gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar))), label, FALSE, 
TRUE, 0);
-    gtk_widget_show (label);
+    gtk_info_bar_add_child (GTK_INFO_BAR (info_bar), label);
 
     ephy_embed_add_top_widget (embed, info_bar, EPHY_EMBED_TOP_WIDGET_POLICY_RETAIN_ON_TRANSITION);
-    gtk_widget_show (info_bar);
   }
+
+  controller = gtk_event_controller_motion_new ();
+  g_signal_connect (controller, "motion", G_CALLBACK (floating_bar_motion_cb), embed);
+  gtk_widget_add_controller (GTK_WIDGET (embed), controller);
 }
 
 static void
@@ -883,8 +889,7 @@ ephy_embed_add_top_widget (EphyEmbed                *embed,
     g_signal_connect (widget, "destroy", G_CALLBACK (remove_from_destroy_list_cb), embed);
   }
 
-  gtk_box_pack_end (embed->top_widgets_vbox,
-                    GTK_WIDGET (widget), FALSE, TRUE, 0);
+  gtk_box_prepend (embed->top_widgets_vbox, widget);
 }
 
 /**
@@ -910,8 +915,7 @@ ephy_embed_remove_top_widget (EphyEmbed *embed,
     embed->destroy_on_transition_list = list;
   }
 
-  gtk_container_remove (GTK_CONTAINER (embed->top_widgets_vbox),
-                        GTK_WIDGET (widget));
+  gtk_box_remove (embed->top_widgets_vbox, widget);
 }
 
 /**
@@ -1005,6 +1009,6 @@ ephy_embed_detach_notification_container (EphyEmbed *embed)
      * notification widget, removing it from the container will destroy the
      * singleton. To prevent this, add a reference to it before removing it
      * from the container. */
-    gtk_container_remove (GTK_CONTAINER (embed->overlay), g_object_ref (GTK_WIDGET (container)));
+    gtk_overlay_remove_overlay (GTK_OVERLAY (embed->overlay), g_object_ref (GTK_WIDGET (container)));
   }
 }
diff --git a/embed/ephy-find-toolbar.c b/embed/ephy-find-toolbar.c
index 33eb9a114..95f799d8e 100644
--- a/embed/ephy-find-toolbar.c
+++ b/embed/ephy-find-toolbar.c
@@ -23,25 +23,24 @@
 #include "ephy-find-toolbar.h"
 
 #include "ephy-debug.h"
-#include "contrib/gd-tagged-entry.h"
+#include "ephy-search-entry.h"
 
 #include <math.h>
 
+#include <adwaita.h>
 #include <gdk/gdkkeysyms.h>
 #include <glib/gi18n.h>
-#include <handy.h>
 #include <string.h>
 #include <webkit2/webkit2.h>
 
 struct _EphyFindToolbar {
-  GtkBin parent_instance;
+  AdwBin parent_instance;
 
   GCancellable *cancellable;
   WebKitWebView *web_view;
   WebKitFindController *controller;
   GtkWidget *search_bar;
-  GdTaggedEntry *entry;
-  GdTaggedEntryTag *entry_tag;
+  GtkWidget *entry;
   GtkWidget *next;
   GtkWidget *prev;
   guint num_matches;
@@ -51,7 +50,7 @@ struct _EphyFindToolbar {
   char *find_string;
 };
 
-G_DEFINE_TYPE (EphyFindToolbar, ephy_find_toolbar, GTK_TYPE_BIN)
+G_DEFINE_TYPE (EphyFindToolbar, ephy_find_toolbar, ADW_TYPE_BIN)
 
 enum {
   PROP_0,
@@ -68,12 +67,6 @@ enum {
 
 static guint signals[LAST_SIGNAL];
 
-typedef enum {
-  EPHY_FIND_RESULT_FOUND = 0,
-  EPHY_FIND_RESULT_NOTFOUND = 1,
-  EPHY_FIND_RESULT_FOUNDWRAPPED = 2
-} EphyFindResult;
-
 typedef enum {
   EPHY_FIND_DIRECTION_NEXT,
   EPHY_FIND_DIRECTION_PREV
@@ -85,61 +78,36 @@ static void ephy_find_toolbar_set_web_view (EphyFindToolbar *toolbar,
                                             WebKitWebView   *web_view);
 
 static void
-update_search_tag (EphyFindToolbar *toolbar)
+update_entry_matches (EphyFindToolbar *toolbar)
 {
-  g_autofree gchar *label = NULL;
-
-  label = g_strdup_printf ("%u/%u", toolbar->current_match, toolbar->num_matches);
-  gd_tagged_entry_tag_set_label (toolbar->entry_tag, label);
-  gd_tagged_entry_add_tag (toolbar->entry, toolbar->entry_tag);
+  ephy_search_entry_set_show_matches (EPHY_SEARCH_ENTRY (toolbar->entry), TRUE);
+  ephy_search_entry_set_n_matches (EPHY_SEARCH_ENTRY (toolbar->entry),
+                                   toolbar->num_matches);
+  ephy_search_entry_set_current_match (EPHY_SEARCH_ENTRY (toolbar->entry),
+                                       toolbar->current_match);
 }
 
 static void
 set_status (EphyFindToolbar *toolbar,
             EphyFindResult   result)
 {
-  const char *icon_name = "edit-find-symbolic";
-  const char *tooltip = NULL;
-
-  update_search_tag (toolbar);
-
-  switch (result) {
-    case EPHY_FIND_RESULT_FOUND:
-      break;
-    case EPHY_FIND_RESULT_NOTFOUND:
-      icon_name = "face-uncertain-symbolic";
-      tooltip = _("Text not found");
-      gtk_widget_error_bell (GTK_WIDGET (toolbar));
+  update_entry_matches (toolbar);
 
-      break;
-    case EPHY_FIND_RESULT_FOUNDWRAPPED:
-      icon_name = "view-wrapped-symbolic";
-      tooltip = _("Search wrapped back to the top");
-      break;
-
-    default:
-      g_assert_not_reached ();
-  }
+  if (result == EPHY_FIND_RESULT_NOTFOUND)
+    gtk_widget_error_bell (GTK_WIDGET (toolbar));
 
   gtk_widget_set_sensitive (toolbar->prev, result != EPHY_FIND_RESULT_NOTFOUND);
   gtk_widget_set_sensitive (toolbar->next, result != EPHY_FIND_RESULT_NOTFOUND);
 
-  g_object_set (toolbar->entry,
-                "primary-icon-name", icon_name,
-                "primary-icon-activatable", FALSE,
-                "primary-icon-sensitive", FALSE,
-                "primary-icon-tooltip-text", tooltip,
-                NULL);
+  ephy_search_entry_set_find_result (EPHY_SEARCH_ENTRY (toolbar->entry), result);
 }
 
 static void
 clear_status (EphyFindToolbar *toolbar)
 {
-  g_object_set (toolbar->entry,
-                "primary-icon-name", "edit-find-symbolic",
-                NULL);
-
-  gd_tagged_entry_remove_tag (toolbar->entry, toolbar->entry_tag);
+  ephy_search_entry_set_find_result (EPHY_SEARCH_ENTRY (toolbar->entry),
+                                     EPHY_FIND_RESULT_FOUND);
+  ephy_search_entry_set_show_matches (EPHY_SEARCH_ENTRY (toolbar->entry), FALSE);
 
   gtk_widget_set_sensitive (toolbar->prev, FALSE);
   gtk_widget_set_sensitive (toolbar->next, FALSE);
@@ -183,7 +151,7 @@ found_text_cb (WebKitFindController *controller,
   WebKitFindOptions options;
   EphyFindResult result;
 
-  update_search_tag (toolbar);
+  update_entry_matches (toolbar);
 
   options = webkit_find_controller_get_options (controller);
   /* FIXME: it's not possible to remove the wrap flag, so the status is now always wrapped. */
@@ -211,7 +179,7 @@ static void
 update_find_string (EphyFindToolbar *toolbar)
 {
   g_free (toolbar->find_string);
-  toolbar->find_string = g_strdup (gtk_entry_get_text (GTK_ENTRY (toolbar->entry)));
+  toolbar->find_string = g_strdup (gtk_editable_get_text (GTK_EDITABLE (toolbar->entry)));
 
   g_clear_handle_id (&toolbar->find_source_id, g_source_remove);
 
@@ -224,111 +192,12 @@ update_find_string (EphyFindToolbar *toolbar)
   g_source_set_name_by_id (toolbar->find_source_id, "[epiphany] do_search");
 }
 
-static gboolean
-entry_key_press_event_cb (GtkEntry        *entry,
-                          GdkEventKey     *event,
-                          EphyFindToolbar *toolbar)
-{
-  guint mask = gtk_accelerator_get_default_mod_mask ();
-  gboolean handled = FALSE;
-
-  if ((event->state & mask) == 0) {
-    handled = TRUE;
-    switch (event->keyval) {
-      case GDK_KEY_Escape:
-        /* Hide the toolbar when ESC is pressed */
-        ephy_find_toolbar_request_close (toolbar);
-        break;
-      default:
-        handled = FALSE;
-        break;
-    }
-  } else if ((event->state & mask) == GDK_CONTROL_MASK &&
-             (gdk_keyval_to_lower (event->keyval) == GDK_KEY_g)) {
-    handled = TRUE;
-    ephy_find_toolbar_find_next (toolbar);
-  } else if ((event->state & mask) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) &&
-             (gdk_keyval_to_lower (event->keyval) == GDK_KEY_g)) {
-    handled = TRUE;
-    ephy_find_toolbar_find_previous (toolbar);
-  } else if ((event->state & mask) == GDK_SHIFT_MASK &&
-             (event->keyval == GDK_KEY_Return ||
-              event->keyval == GDK_KEY_KP_Enter ||
-              event->keyval == GDK_KEY_ISO_Enter)) {
-    handled = TRUE;
-    ephy_find_toolbar_find_previous (toolbar);
-  }
-
-  return handled;
-}
-
 static void
-ephy_find_toolbar_grab_focus (GtkWidget *widget)
-{
-  EphyFindToolbar *toolbar = EPHY_FIND_TOOLBAR (widget);
-
-  gtk_widget_grab_focus (GTK_WIDGET (toolbar->entry));
-}
-
-static gboolean
-ephy_find_toolbar_draw (GtkWidget *widget,
-                        cairo_t   *cr)
-{
-  GtkStyleContext *context;
-
-  context = gtk_widget_get_style_context (widget);
-
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, gtk_widget_get_state_flags (widget));
-
-  gtk_render_background (context, cr, 0, 0,
-                         gtk_widget_get_allocated_width (widget),
-                         gtk_widget_get_allocated_height (widget));
-
-  gtk_render_frame (context, cr, 0, 0,
-                    gtk_widget_get_allocated_width (widget),
-                    gtk_widget_get_allocated_height (widget));
-
-  gtk_style_context_restore (context);
-
-  return GTK_WIDGET_CLASS (ephy_find_toolbar_parent_class)->draw (widget, cr);
-}
-
-static void
-search_entry_clear_cb (GtkEntry *entry,
-                       gpointer  user_data)
-{
-  gtk_entry_set_text (entry, "");
-}
-
-static void
-search_entry_changed_cb (GtkEntry        *entry,
+search_entry_changed_cb (GtkEditable     *entry,
                          EphyFindToolbar *toolbar)
 {
-  const char *str;
-  const char *primary_icon_name = "edit-find-symbolic";
-  const char *secondary_icon_name = NULL;
-  gboolean primary_active = FALSE;
-  gboolean secondary_active = FALSE;
-
-  str = gtk_entry_get_text (entry);
-
-  if (str == NULL || *str == '\0') {
-    primary_icon_name = "edit-find-symbolic";
-  } else {
-    secondary_icon_name = "edit-clear-symbolic";
-    secondary_active = TRUE;
-  }
-
-  g_object_set (entry,
-                "primary-icon-name", primary_icon_name,
-                "primary-icon-activatable", primary_active,
-                "primary-icon-sensitive", primary_active,
-                "secondary-icon-name", secondary_icon_name,
-                "secondary-icon-activatable", secondary_active,
-                "secondary-icon-sensitive", secondary_active,
-                NULL);
-
+  ephy_search_entry_set_find_result (EPHY_SEARCH_ENTRY (toolbar->entry),
+                                     EPHY_FIND_RESULT_FOUND);
   update_find_string (toolbar);
 }
 
@@ -338,7 +207,7 @@ ephy_find_toolbar_load_changed_cb (WebKitWebView   *web_view,
                                    EphyFindToolbar *toolbar)
 {
   if (load_event == WEBKIT_LOAD_STARTED &&
-      hdy_search_bar_get_search_mode (HDY_SEARCH_BAR (toolbar->search_bar))) {
+      gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (toolbar->search_bar))) {
     ephy_find_toolbar_close (toolbar);
   }
 }
@@ -349,62 +218,57 @@ ephy_find_toolbar_init (EphyFindToolbar *toolbar)
   GtkWidget *clamp;
   GtkWidget *box;
 
-  toolbar->search_bar = hdy_search_bar_new ();
-  gtk_container_add (GTK_CONTAINER (toolbar), toolbar->search_bar);
+  toolbar->search_bar = gtk_search_bar_new ();
+  adw_bin_set_child (ADW_BIN (toolbar), toolbar->search_bar);
 
-  clamp = GTK_WIDGET (hdy_clamp_new ());
-  hdy_clamp_set_maximum_size (HDY_CLAMP (clamp), 400);
-  hdy_clamp_set_tightening_threshold (HDY_CLAMP (clamp), 300);
-  gtk_container_add (GTK_CONTAINER (toolbar->search_bar), clamp);
+  clamp = GTK_WIDGET (adw_clamp_new ());
+  gtk_widget_set_hexpand (clamp, TRUE);
+  adw_clamp_set_maximum_size (ADW_CLAMP (clamp), 400);
+  adw_clamp_set_tightening_threshold (ADW_CLAMP (clamp), 300);
+  gtk_search_bar_set_child (GTK_SEARCH_BAR (toolbar->search_bar), clamp);
 
-  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-  gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked");
-  gtk_container_add (GTK_CONTAINER (clamp), box);
-
-  toolbar->entry = gd_tagged_entry_new ();
-  toolbar->entry_tag = gd_tagged_entry_tag_new ("");
-  gd_tagged_entry_tag_set_style (toolbar->entry_tag, "search-entry-occurrences-tag");
-  gd_tagged_entry_tag_set_has_close_button (toolbar->entry_tag, FALSE);
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+  adw_clamp_set_child (ADW_CLAMP (clamp), box);
 
+  toolbar->entry = ephy_search_entry_new ();
   gtk_widget_set_hexpand (GTK_WIDGET (toolbar->entry), TRUE);
-  gtk_entry_set_placeholder_text (GTK_ENTRY (toolbar->entry), _("Type to search…"));
-  gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (toolbar->entry));
+  ephy_search_entry_set_placeholder_text (EPHY_SEARCH_ENTRY (toolbar->entry), _("Type to search…"));
+  gtk_box_append (GTK_BOX (box), GTK_WIDGET (toolbar->entry));
 
   /* Prev */
-  toolbar->prev = gtk_button_new_from_icon_name ("go-up-symbolic", GTK_ICON_SIZE_MENU);
+  toolbar->prev = gtk_button_new_from_icon_name ("go-up-symbolic");
   gtk_widget_set_tooltip_text (toolbar->prev,
                                _("Find previous occurrence of the search string"));
-  gtk_container_add (GTK_CONTAINER (box), toolbar->prev);
+  gtk_box_append (GTK_BOX (box), toolbar->prev);
   gtk_widget_set_sensitive (toolbar->prev, FALSE);
 
   /* Next */
-  toolbar->next = gtk_button_new_from_icon_name ("go-down-symbolic", GTK_ICON_SIZE_MENU);
+  toolbar->next = gtk_button_new_from_icon_name ("go-down-symbolic");
   gtk_widget_set_tooltip_text (toolbar->next,
                                _("Find next occurrence of the search string"));
-  gtk_container_add (GTK_CONTAINER (box), toolbar->next);
+  gtk_box_append (GTK_BOX (box), toolbar->next);
   gtk_widget_set_sensitive (toolbar->next, FALSE);
 
   /* connect signals */
-  g_signal_connect (toolbar->entry, "icon-release",
-                    G_CALLBACK (search_entry_clear_cb), toolbar);
-  g_signal_connect (toolbar->entry, "key-press-event",
-                    G_CALLBACK (entry_key_press_event_cb), toolbar);
   g_signal_connect_after (toolbar->entry, "changed",
                           G_CALLBACK (search_entry_changed_cb), toolbar);
-  g_signal_connect_swapped (toolbar->entry, "activate",
+  g_signal_connect_swapped (toolbar->entry, "next-match",
                             G_CALLBACK (ephy_find_toolbar_find_next), toolbar);
+  g_signal_connect_swapped (toolbar->entry, "previous-match",
+                            G_CALLBACK (ephy_find_toolbar_find_previous), toolbar);
+  g_signal_connect_swapped (toolbar->entry, "stop-search",
+                            G_CALLBACK (ephy_find_toolbar_request_close), toolbar);
+
   g_signal_connect_swapped (toolbar->next, "clicked",
                             G_CALLBACK (ephy_find_toolbar_find_next), toolbar);
   g_signal_connect_swapped (toolbar->prev, "clicked",
                             G_CALLBACK (ephy_find_toolbar_find_previous), toolbar);
-  hdy_search_bar_connect_entry (HDY_SEARCH_BAR (toolbar->search_bar),
-                                GTK_ENTRY (toolbar->entry));
+  gtk_search_bar_connect_entry (GTK_SEARCH_BAR (toolbar->search_bar),
+                                GTK_EDITABLE (toolbar->entry));
 
-  search_entry_changed_cb (GTK_ENTRY (toolbar->entry), toolbar);
+  search_entry_changed_cb (GTK_EDITABLE (toolbar->entry), toolbar);
 
   toolbar->cancellable = g_cancellable_new ();
-
-  gtk_widget_show_all (GTK_WIDGET (toolbar));
 }
 
 static void
@@ -474,16 +338,12 @@ static void
 ephy_find_toolbar_class_init (EphyFindToolbarClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->dispose = ephy_find_toolbar_dispose;
   object_class->finalize = ephy_find_toolbar_finalize;
   object_class->get_property = ephy_find_toolbar_get_property;
   object_class->set_property = ephy_find_toolbar_set_property;
 
-  widget_class->draw = ephy_find_toolbar_draw;
-  widget_class->grab_focus = ephy_find_toolbar_grab_focus;
-
   signals[CLOSE] =
     g_signal_new ("close",
                   G_OBJECT_CLASS_TYPE (object_class),
@@ -514,7 +374,7 @@ ephy_find_toolbar_new (WebKitWebView *web_view)
 const char *
 ephy_find_toolbar_get_text (EphyFindToolbar *toolbar)
 {
-  return gtk_entry_get_text (GTK_ENTRY (toolbar->entry));
+  return gtk_editable_get_text (GTK_EDITABLE (toolbar->entry));
 }
 
 static void
@@ -527,7 +387,7 @@ counted_matches_cb (WebKitFindController *find_controller,
   toolbar->num_matches = match_count;
   toolbar->current_match = toolbar->num_matches ? 1 : 0;
 
-  update_search_tag (toolbar);
+  update_entry_matches (toolbar);
 }
 
 static void
@@ -614,7 +474,7 @@ ephy_find_toolbar_selection_async (GObject      *source_object,
     if (exception) {
       g_warning ("Error running javascript: %s", jsc_exception_get_message (exception));
     } else if (strlen (str_value)) {
-      gtk_entry_set_text (GTK_ENTRY (toolbar->entry), str_value);
+      gtk_editable_set_text (GTK_EDITABLE (toolbar->entry), str_value);
       gtk_editable_select_region (GTK_EDITABLE (toolbar->entry), 0, -1);
     }
   }
@@ -629,15 +489,15 @@ ephy_find_toolbar_open (EphyFindToolbar *toolbar)
 
   gtk_editable_select_region (GTK_EDITABLE (toolbar->entry), 0, -1);
 
-  hdy_search_bar_set_search_mode (HDY_SEARCH_BAR (toolbar->search_bar), TRUE);
-  hdy_search_bar_set_show_close_button (HDY_SEARCH_BAR (toolbar->search_bar), TRUE);
+  gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (toolbar->search_bar), TRUE);
+  gtk_search_bar_set_show_close_button (GTK_SEARCH_BAR (toolbar->search_bar), TRUE);
   gtk_widget_grab_focus (GTK_WIDGET (toolbar->entry));
 }
 
 void
 ephy_find_toolbar_close (EphyFindToolbar *toolbar)
 {
-  hdy_search_bar_set_search_mode (HDY_SEARCH_BAR (toolbar->search_bar), FALSE);
+  gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (toolbar->search_bar), FALSE);
 
   if (toolbar->web_view == NULL) return;
 
@@ -647,7 +507,7 @@ ephy_find_toolbar_close (EphyFindToolbar *toolbar)
 void
 ephy_find_toolbar_request_close (EphyFindToolbar *toolbar)
 {
-  if (hdy_search_bar_get_search_mode (HDY_SEARCH_BAR (toolbar->search_bar))) {
+  if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (toolbar->search_bar))) {
     g_signal_emit (toolbar, signals[CLOSE], 0);
   }
 }
diff --git a/embed/ephy-find-toolbar.h b/embed/ephy-find-toolbar.h
index 5b39d5b89..d9773f086 100644
--- a/embed/ephy-find-toolbar.h
+++ b/embed/ephy-find-toolbar.h
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
+#include <adwaita.h>
 
 #include "ephy-web-view.h"
 
@@ -29,7 +29,7 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_FIND_TOOLBAR (ephy_find_toolbar_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyFindToolbar, ephy_find_toolbar, EPHY, FIND_TOOLBAR, GtkBin)
+G_DECLARE_FINAL_TYPE (EphyFindToolbar, ephy_find_toolbar, EPHY, FIND_TOOLBAR, AdwBin)
 
 EphyFindToolbar *ephy_find_toolbar_new           (WebKitWebView *web_view);
 
diff --git a/embed/ephy-reader-handler.c b/embed/ephy-reader-handler.c
index aa2c8dfe3..ee82fdd62 100644
--- a/embed/ephy-reader-handler.c
+++ b/embed/ephy-reader-handler.c
@@ -28,9 +28,9 @@
 #include "ephy-settings.h"
 #include "ephy-web-view.h"
 
+#include <adwaita.h>
 #include <gio/gio.h>
 #include <glib/gi18n.h>
-#include <handy.h>
 #include <string.h>
 
 struct _EphyReaderHandler {
@@ -166,7 +166,7 @@ readability_js_finish_cb (GObject      *object,
   const gchar *title;
   const gchar *font_style;
   const gchar *color_scheme;
-  HdyStyleManager *style_manager;
+  AdwStyleManager *style_manager;
 
   js_result = webkit_web_view_run_javascript_finish (web_view, result, &error);
   if (!js_result) {
@@ -189,10 +189,10 @@ readability_js_finish_cb (GObject      *object,
                           g_settings_get_enum (EPHY_SETTINGS_READER,
                                                EPHY_PREFS_READER_FONT_STYLE));
 
-  style_manager = hdy_style_manager_get_default ();
+  style_manager = adw_style_manager_get_default ();
 
-  if (hdy_style_manager_get_system_supports_color_schemes (style_manager))
-    color_scheme = hdy_style_manager_get_dark (style_manager) ? "dark" : "light";
+  if (adw_style_manager_get_system_supports_color_schemes (style_manager))
+    color_scheme = adw_style_manager_get_dark (style_manager) ? "dark" : "light";
   else
     color_scheme = enum_nick (EPHY_TYPE_PREFS_READER_COLOR_SCHEME,
                               g_settings_get_enum (EPHY_SETTINGS_READER,
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index d8521a012..79725c6c9 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -51,11 +51,11 @@
 #include "ephy-web-app-utils.h"
 #include "ephy-zoom.h"
 
+#include <adwaita.h>
 #include <gio/gio.h>
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 #include <gtk/gtk.h>
-#include <handy.h>
 
 /**
  * SECTION:ephy-web-view
@@ -130,7 +130,7 @@ struct _EphyWebView {
   EphyWebViewErrorPage error_page;
 
   guint unresponsive_process_timeout_id;
-  GtkWidget *unresponsive_process_dialog;
+  GtkWindow *unresponsive_process_dialog;
 
   guint64 uid;
 };
@@ -162,20 +162,23 @@ open_response_cb (GtkFileChooser           *dialog,
                   WebKitFileChooserRequest *request)
 {
   if (response == GTK_RESPONSE_ACCEPT) {
-    GSList *file_list = gtk_file_chooser_get_filenames (dialog);
+    g_autoptr (GListModel) files = gtk_file_chooser_get_files (dialog);
     GPtrArray *file_array = g_ptr_array_new ();
     g_autoptr (GFile) current_folder = NULL;
     g_autofree char *current_folder_path = NULL;
+    guint i, n = g_list_model_get_n_items (files);
 
-    for (GSList *file = file_list; file; file = g_slist_next (file))
-      g_ptr_array_add (file_array, file->data);
+    for (i = 0; i < n; i++) {
+      g_autoptr (GFile) file = g_list_model_get_item (files, i);
+
+      g_ptr_array_add (file_array, file);
+    }
 
     g_ptr_array_add (file_array, NULL);
     webkit_file_chooser_request_select_files (request, (const char * const *)file_array->pdata);
-    g_slist_free_full (file_list, g_free);
     g_ptr_array_free (file_array, TRUE);
 
-    current_folder = gtk_file_chooser_get_current_folder_file (dialog);
+    current_folder = gtk_file_chooser_get_current_folder (dialog);
     current_folder_path = g_file_get_path (current_folder);
     g_settings_set_string (EPHY_SETTINGS_WEB,
                            EPHY_PREFS_WEB_LAST_UPLOAD_DIRECTORY,
@@ -192,14 +195,14 @@ static gboolean
 ephy_web_view_run_file_chooser (WebKitWebView            *web_view,
                                 WebKitFileChooserRequest *request)
 {
-  GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
+  GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (web_view));
   GtkFileChooser *dialog;
   gboolean allows_multiple_selection = webkit_file_chooser_request_get_select_multiple (request);
   GtkFileFilter *filter = webkit_file_chooser_request_get_mime_types_filter (request);
   g_autofree char *last_directory_path = NULL;
 
   dialog = ephy_create_file_chooser (_("Open"),
-                                     GTK_WIDGET (toplevel),
+                                     GTK_WIDGET (root),
                                      GTK_FILE_CHOOSER_ACTION_OPEN,
                                      EPHY_FILE_FILTER_ALL);
 
@@ -215,7 +218,7 @@ ephy_web_view_run_file_chooser (WebKitWebView            *web_view,
     g_autoptr (GError) error = NULL;
 
     last_directory = g_file_new_for_path (last_directory_path);
-    gtk_file_chooser_set_current_folder_file (dialog, last_directory, &error);
+    gtk_file_chooser_set_current_folder (dialog, last_directory, &error);
 
     if (error)
       g_warning ("Failed to set current folder %s: %s", last_directory_path, error->message);
@@ -310,26 +313,39 @@ ephy_web_view_set_property (GObject      *object,
   }
 }
 
-static gboolean
-ephy_web_view_button_press_event (GtkWidget      *widget,
-                                  GdkEventButton *event)
+static void
+button_pressed_cb (GtkGesture  *gesture,
+                   int          n_clicks,
+                   double       x,
+                   double       y,
+                   EphyWebView *web_view)
 {
-  /* These are the special cases WebkitWebView doesn't handle but we have an
-   * interest in handling. */
+  guint button;
+
+  button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
 
   /* Handle typical back/forward mouse buttons. */
-  if (event->button == 8) {
-    webkit_web_view_go_back (WEBKIT_WEB_VIEW (widget));
-    return TRUE;
+  if (button == 8) {
+    webkit_web_view_go_back (WEBKIT_WEB_VIEW (web_view));
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
+    return;
   }
 
-  if (event->button == 9) {
-    webkit_web_view_go_forward (WEBKIT_WEB_VIEW (widget));
-    return TRUE;
+  if (button == 9) {
+    webkit_web_view_go_forward (WEBKIT_WEB_VIEW (web_view));
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
+    return;
   }
 
-  /* Let WebKitWebView handle this. */
-  return GTK_WIDGET_CLASS (ephy_web_view_parent_class)->button_press_event (widget, event);
+  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+}
+
+static inline void
+remove_info_bar (GtkWidget *info_bar)
+{
+  EphyEmbed *embed = EPHY_EMBED (gtk_widget_get_ancestor (info_bar, EPHY_TYPE_EMBED));
+
+  ephy_embed_remove_top_widget (embed, info_bar);
 }
 
 static void
@@ -340,7 +356,7 @@ untrack_info_bar (GtkWidget **tracked_info_bar)
 
   if (*tracked_info_bar) {
     g_object_remove_weak_pointer (G_OBJECT (*tracked_info_bar), (gpointer *)tracked_info_bar);
-    gtk_widget_destroy (*tracked_info_bar);
+    remove_info_bar (*tracked_info_bar);
     *tracked_info_bar = NULL;
   }
 }
@@ -366,7 +382,6 @@ ephy_web_view_create_form_auth_save_confirmation_info_bar (EphyWebView *web_view
                                                            const char  *username)
 {
   GtkWidget *info_bar;
-  GtkWidget *content_area;
   GtkWidget *label;
   char *message;
 
@@ -383,13 +398,11 @@ ephy_web_view_create_form_auth_save_confirmation_info_bar (EphyWebView *web_view
    */
   message = g_markup_printf_escaped (_("Do you want to save your password for “%s”?"), origin);
   gtk_label_set_markup (GTK_LABEL (label), message);
-  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (label), TRUE);
   gtk_label_set_xalign (GTK_LABEL (label), 0);
   g_free (message);
 
-  content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
-  gtk_container_add (GTK_CONTAINER (content_area), label);
-  gtk_widget_show (label);
+  gtk_info_bar_add_child (GTK_INFO_BAR (info_bar), label);
 
   track_info_bar (info_bar, &web_view->password_info_bar);
 
@@ -423,7 +436,7 @@ info_bar_save_request_response_cb (GtkInfoBar      *info_bar,
 {
   g_assert (data->callback);
   data->callback (response_id, data->callback_data);
-  gtk_widget_destroy (GTK_WIDGET (info_bar));
+  remove_info_bar (GTK_WIDGET (info_bar));
 }
 
 void
@@ -610,7 +623,6 @@ password_form_focused_cb (EphyEmbedShell *shell,
 {
   GtkWidget *info_bar;
   GtkWidget *label;
-  GtkWidget *content_area;
 
   if (web_view->password_form_info_bar)
     return;
@@ -621,15 +633,13 @@ password_form_focused_cb (EphyEmbedShell *shell,
 
   /* Translators: Message appears when insecure password form is focused. */
   label = gtk_label_new (_("Heads-up: this form is not secure. If you type your password, it will not be 
kept private."));
-  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (label), TRUE);
   gtk_label_set_xalign (GTK_LABEL (label), 0);
-  gtk_widget_show (label);
 
   info_bar = gtk_info_bar_new ();
   gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
   gtk_info_bar_set_show_close_button (GTK_INFO_BAR (info_bar), TRUE);
-  content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
-  gtk_container_add (GTK_CONTAINER (content_area), label);
+  gtk_info_bar_add_child (GTK_INFO_BAR (info_bar), label);
 
   g_signal_connect (info_bar, "response", G_CALLBACK (gtk_widget_hide), NULL);
 
@@ -638,7 +648,6 @@ password_form_focused_cb (EphyEmbedShell *shell,
   ephy_embed_add_top_widget (EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (web_view),
                              info_bar,
                              EPHY_EMBED_TOP_WIDGET_POLICY_DESTROY_ON_TRANSITION);
-  gtk_widget_show (info_bar);
 }
 
 static void
@@ -879,7 +888,7 @@ on_unresponsive_dialog_response (GtkDialog *dialog,
                                                                             
(GSourceFunc)unresponsive_process_timeout_cb,
                                                                             web_view,
                                                                             NULL);
-  g_clear_pointer (&web_view->unresponsive_process_dialog, gtk_widget_destroy);
+  g_clear_pointer (&web_view->unresponsive_process_dialog, gtk_window_destroy);
 }
 
 static gboolean
@@ -890,18 +899,19 @@ unresponsive_process_timeout_cb (gpointer user_data)
   if (!gtk_widget_get_mapped (GTK_WIDGET (web_view)))
     return G_SOURCE_CONTINUE;
 
-  web_view->unresponsive_process_dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel 
(GTK_WIDGET (web_view))),
-                                                                  GTK_DIALOG_MODAL | 
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
-                                                                  GTK_MESSAGE_QUESTION,
-                                                                  GTK_BUTTONS_NONE,
-                                                                  _("The current page '%s' is unresponsive"),
-                                                                  ephy_web_view_get_address (web_view));
+  web_view->unresponsive_process_dialog =
+    GTK_WINDOW (gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (web_view))),
+                                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | 
GTK_DIALOG_USE_HEADER_BAR,
+                                        GTK_MESSAGE_QUESTION,
+                                        GTK_BUTTONS_NONE,
+                                        _("The current page '%s' is unresponsive"),
+                                        ephy_web_view_get_address (web_view)));
 
   gtk_dialog_add_button (GTK_DIALOG (web_view->unresponsive_process_dialog), _("_Wait"), GTK_RESPONSE_NO);
   gtk_dialog_add_button (GTK_DIALOG (web_view->unresponsive_process_dialog), _("_Kill"), GTK_RESPONSE_YES);
 
   g_signal_connect (web_view->unresponsive_process_dialog, "response", G_CALLBACK 
(on_unresponsive_dialog_response), web_view);
-  gtk_widget_show_all (web_view->unresponsive_process_dialog);
+  gtk_window_present (web_view->unresponsive_process_dialog);
 
   web_view->unresponsive_process_timeout_id = 0;
 
@@ -919,7 +929,7 @@ is_web_process_responsive_changed_cb (EphyWebView *web_view,
 
   if (web_view->unresponsive_process_dialog && responsive) {
     g_signal_handlers_disconnect_by_func (web_view->unresponsive_process_dialog, 
on_unresponsive_dialog_response, web_view);
-    g_clear_pointer (&web_view->unresponsive_process_dialog, gtk_widget_destroy);
+    g_clear_pointer (&web_view->unresponsive_process_dialog, gtk_window_destroy);
   }
 
   if (!responsive) {
@@ -1106,7 +1116,7 @@ decide_on_permission_request (GtkWidget             *info_bar,
   }
 
   g_object_weak_unref (G_OBJECT (info_bar), (GWeakNotify)permission_request_info_bar_destroyed_cb, data);
-  gtk_widget_destroy (info_bar);
+  remove_info_bar (info_bar);
   permission_request_data_free (data);
 }
 
@@ -1117,7 +1127,6 @@ show_permission_request_info_bar (WebKitWebView           *web_view,
 {
   PermissionRequestData *data;
   GtkWidget *info_bar;
-  GtkWidget *content_area;
   GtkWidget *label;
   char *message;
   char *origin;
@@ -1167,13 +1176,10 @@ show_permission_request_info_bar (WebKitWebView           *web_view,
 
   label = gtk_label_new (NULL);
   gtk_label_set_markup (GTK_LABEL (label), message);
-  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (label), TRUE);
   gtk_label_set_xalign (GTK_LABEL (label), 0);
 
-  content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
-  gtk_container_add (GTK_CONTAINER (content_area), label);
-
-  gtk_widget_show_all (info_bar);
+  gtk_info_bar_add_child (GTK_INFO_BAR (info_bar), label);
 
   data = permission_request_data_new (EPHY_WEB_VIEW (web_view), decision, origin);
 
@@ -1227,7 +1233,7 @@ decide_on_itp_permission_request (GtkWidget               *info_bar,
   }
 
   g_object_set_data (G_OBJECT (info_bar), "ephy-itp-decision", NULL);
-  gtk_widget_destroy (info_bar);
+  remove_info_bar (info_bar);
 }
 
 static void
@@ -1235,7 +1241,6 @@ ephy_web_view_show_itp_permission_info_bar (EphyWebView
                                             WebKitWebsiteDataAccessPermissionRequest *decision)
 {
   GtkWidget *info_bar;
-  GtkWidget *content_area;
   GtkWidget *box;
   GtkWidget *label;
   g_autofree char *message = NULL;
@@ -1256,21 +1261,17 @@ ephy_web_view_show_itp_permission_info_bar (EphyWebView
   markup = g_strdup_printf ("<span weight='bold'>%s</span>", message);
   label = gtk_label_new (NULL);
   gtk_label_set_markup (GTK_LABEL (label), markup);
-  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (label), TRUE);
   gtk_label_set_xalign (GTK_LABEL (label), 0);
-  gtk_container_add (GTK_CONTAINER (box), label);
-  gtk_widget_show (label);
+  gtk_box_append (GTK_BOX (box), label);
 
   secondary_message = g_strdup_printf (_("This will allow “%s” to track your activity."), requesting_domain);
   label = gtk_label_new (secondary_message);
-  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (label), TRUE);
   gtk_label_set_xalign (GTK_LABEL (label), 0);
-  gtk_container_add (GTK_CONTAINER (box), label);
-  gtk_widget_show (label);
+  gtk_box_append (GTK_BOX (box), label);
 
-  content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
-  gtk_container_add (GTK_CONTAINER (content_area), box);
-  gtk_widget_show (box);
+  gtk_info_bar_add_child (GTK_INFO_BAR (info_bar), box);
 
   track_info_bar (info_bar, &web_view->itp_info_bar);
 
@@ -1282,7 +1283,6 @@ ephy_web_view_show_itp_permission_info_bar (EphyWebView
   ephy_embed_add_top_widget (EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (web_view),
                              info_bar,
                              EPHY_EMBED_TOP_WIDGET_POLICY_DESTROY_ON_TRANSITION);
-  gtk_widget_show (info_bar);
 }
 
 static gboolean
@@ -1518,7 +1518,7 @@ update_security_status_for_committed_load (EphyWebView *view,
 {
   EphySecurityLevel security_level = EPHY_SECURITY_LEVEL_NO_SECURITY;
   EphyEmbed *embed = NULL;
-  GtkWidget *toplevel;
+  GtkRoot *root;
   WebKitWebContext *web_context;
   WebKitSecurityManager *security_manager;
   g_autoptr (GUri) guri = NULL;
@@ -1532,8 +1532,8 @@ update_security_status_for_committed_load (EphyWebView *view,
     return;
   }
 
-  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
-  if (EPHY_IS_EMBED_CONTAINER (toplevel))
+  root = gtk_widget_get_root (GTK_WIDGET (view));
+  if (EPHY_IS_EMBED_CONTAINER (root))
     embed = EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (view);
   web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (view));
   security_manager = webkit_web_context_get_security_manager (web_context);
@@ -2519,15 +2519,15 @@ close_web_view_cb (WebKitWebView *web_view,
                    gpointer       user_data)
 
 {
-  GtkWidget *widget = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
+  GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (web_view));
 
   LOG ("close web view");
 
-  if (EPHY_IS_EMBED_CONTAINER (widget))
-    ephy_embed_container_remove_child (EPHY_EMBED_CONTAINER (widget),
+  if (EPHY_IS_EMBED_CONTAINER (root))
+    ephy_embed_container_remove_child (EPHY_EMBED_CONTAINER (root),
                                        EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (web_view));
   else
-    gtk_widget_destroy (widget);
+    gtk_window_destroy (GTK_WINDOW (root));
 }
 
 
@@ -2587,7 +2587,7 @@ enum_nick (GType enum_type,
 static void
 reader_setting_changed_cb (EphyWebView *web_view)
 {
-  HdyStyleManager *style_manager;
+  AdwStyleManager *style_manager;
   const gchar *font_style;
   const gchar *color_scheme;
   gchar *js_snippet;
@@ -2599,10 +2599,10 @@ reader_setting_changed_cb (EphyWebView *web_view)
                           g_settings_get_enum (EPHY_SETTINGS_READER,
                                                EPHY_PREFS_READER_FONT_STYLE));
 
-  style_manager = hdy_style_manager_get_default ();
+  style_manager = adw_style_manager_get_default ();
 
-  if (hdy_style_manager_get_system_supports_color_schemes (style_manager))
-    color_scheme = hdy_style_manager_get_dark (style_manager) ? "dark" : "light";
+  if (adw_style_manager_get_system_supports_color_schemes (style_manager))
+    color_scheme = adw_style_manager_get_dark (style_manager) ? "dark" : "light";
   else
     color_scheme = enum_nick (EPHY_TYPE_PREFS_READER_COLOR_SCHEME,
                               g_settings_get_enum (EPHY_SETTINGS_READER,
@@ -3593,26 +3593,29 @@ ephy_web_view_get_security_level (EphyWebView           *view,
     *errors = view->tls_errors;
 }
 
+static void
+info_bar_response_cb (GtkInfoBar *info_bar,
+                      int         response_id,
+                      EphyEmbed  *embed)
+{
+  ephy_embed_remove_top_widget (embed, GTK_WIDGET (info_bar));
+}
+
 static void
 ephy_web_view_print_failed (EphyWebView *view,
                             GError      *error)
 {
   GtkWidget *info_bar;
   GtkWidget *label;
-  GtkContainer *content_area;
   EphyEmbed *embed = EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (view);
 
   info_bar = gtk_info_bar_new_with_buttons (_("_OK"), GTK_RESPONSE_OK, NULL);
   label = gtk_label_new (error->message);
-  content_area = GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar)));
 
-  gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR);
-  gtk_container_add (content_area, label);
-  g_signal_connect (info_bar, "response",
-                    G_CALLBACK (gtk_widget_destroy), NULL);
+  gtk_info_bar_add_child (GTK_INFO_BAR (info_bar), label);
+  g_signal_connect (info_bar, "response", G_CALLBACK (info_bar_response_cb), embed);
 
   ephy_embed_add_top_widget (embed, info_bar, EPHY_EMBED_TOP_WIDGET_POLICY_RETAIN_ON_TRANSITION);
-  gtk_widget_show_all (info_bar);
 }
 
 static void
@@ -3919,7 +3922,7 @@ ephy_web_view_dispose (GObject *object)
   g_clear_object (&view->certificate);
   g_clear_object (&view->file_monitor);
   g_clear_object (&view->icon);
-  g_clear_pointer (&view->unresponsive_process_dialog, gtk_widget_destroy);
+  g_clear_pointer (&view->unresponsive_process_dialog, gtk_window_destroy);
 
   if (view->cancellable) {
     g_cancellable_cancel (view->cancellable);
@@ -3985,6 +3988,7 @@ static void
 ephy_web_view_init (EphyWebView *web_view)
 {
   EphyEmbedShell *shell;
+  GtkGesture *gesture;
 
   shell = ephy_embed_shell_get_default ();
 
@@ -4009,12 +4013,12 @@ ephy_web_view_init (EphyWebView *web_view)
                            G_CALLBACK (reader_setting_changed_cb),
                            web_view, G_CONNECT_SWAPPED);
 
-  g_signal_connect_object (hdy_style_manager_get_default (),
+  g_signal_connect_object (adw_style_manager_get_default (),
                            "notify::system-supports-color-schemes",
                            G_CALLBACK (reader_setting_changed_cb),
                            web_view, G_CONNECT_SWAPPED);
 
-  g_signal_connect_object (hdy_style_manager_get_default (),
+  g_signal_connect_object (adw_style_manager_get_default (),
                            "notify::dark",
                            G_CALLBACK (reader_setting_changed_cb),
                            web_view, G_CONNECT_SWAPPED);
@@ -4101,13 +4105,19 @@ ephy_web_view_init (EphyWebView *web_view)
   g_signal_connect_object (shell, "reload-page",
                            G_CALLBACK (reload_page_cb),
                            web_view, 0);
+
+  gtk_widget_set_overflow (GTK_WIDGET (web_view), GTK_OVERFLOW_HIDDEN);
+
+  gesture = gtk_gesture_click_new ();
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
+  g_signal_connect (gesture, "pressed", G_CALLBACK (button_pressed_cb), web_view);
+  gtk_widget_add_controller (GTK_WIDGET (web_view), GTK_EVENT_CONTROLLER (gesture));
 }
 
 static void
 ephy_web_view_class_init (EphyWebViewClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   WebKitWebViewClass *webkit_webview_class = WEBKIT_WEB_VIEW_CLASS (klass);
 
   gobject_class->dispose = ephy_web_view_dispose;
@@ -4116,7 +4126,6 @@ ephy_web_view_class_init (EphyWebViewClass *klass)
   gobject_class->set_property = ephy_web_view_set_property;
   gobject_class->constructed = ephy_web_view_constructed;
 
-  widget_class->button_press_event = ephy_web_view_button_press_event;
 
   webkit_webview_class->run_file_chooser = ephy_web_view_run_file_chooser;
 
diff --git a/embed/meson.build b/embed/meson.build
index 28b95859c..7d94b9864 100644
--- a/embed/meson.build
+++ b/embed/meson.build
@@ -11,7 +11,7 @@ enums = gnome.mkenums_simple('ephy-embed-type-builtins',
 )
 
 libephyembed_sources = [
-  'contrib/gd-tagged-entry.c',
+#  'contrib/gd-tagged-entry.c',
   'ephy-about-handler.c',
   'ephy-downloads-manager.c',
   'ephy-download.c',
@@ -34,12 +34,11 @@ libephyembed_sources = [
 
 libephyembed_deps = [
   config_h,
-  libdazzle_dep,
   ephymisc_dep,
   gio_dep,
   glib_dep,
   gtk_dep,
-  libhandy_dep,
+  libadwaita_dep,
   libsecret_dep,
   libsoup_dep,
   m_dep,
diff --git a/lib/contrib/dzl-fuzzy-mutable-index.c b/lib/contrib/dzl-fuzzy-mutable-index.c
new file mode 100644
index 000000000..fb4648738
--- /dev/null
+++ b/lib/contrib/dzl-fuzzy-mutable-index.c
@@ -0,0 +1,722 @@
+/* dzl-fuzzy-mutable-index.c
+ *
+ * Copyright (C) 2014-2017 Christian Hergert <christian hergert me>
+ *
+ * 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 <ctype.h>
+#include <string.h>
+
+#include "dzl-fuzzy-mutable-index.h"
+
+/**
+ * SECTION:dzl-fuzzy-mutable-index
+ * @title: DzlFuzzyMutableIndex Matching
+ * @short_description: DzlFuzzyMutableIndex matching for GLib based programs.
+ *
+ * #DzlFuzzyMutableIndex provides a fulltext index that focuses around fuzzy
+ * matching words. This version of the datastructure is focused around
+ * in-memory storage. This makes mutability performance of adding or removing
+ * items from the corpus simpler.
+ *
+ * If you need mostly read-only indexes, you might consider using
+ * #DzlFuzzyIndex and #DzlFuzzyIndexBuilder which can create a disk-based file
+ * and mmap() a read-only version of the data set.
+ *
+ * It is a programming error to modify #Fuzzy while holding onto an array
+ * of #FuzzyMatch elements. The position of strings within the DzlFuzzyMutableIndexMatch
+ * may no longer be valid.
+ */
+
+G_DEFINE_BOXED_TYPE (DzlFuzzyMutableIndex, dzl_fuzzy_mutable_index,
+                     (GBoxedCopyFunc)dzl_fuzzy_mutable_index_ref,
+                     (GBoxedFreeFunc)dzl_fuzzy_mutable_index_unref)
+
+struct _DzlFuzzyMutableIndex
+{
+  volatile gint   ref_count;
+  GByteArray     *heap;
+  GArray         *id_to_text_offset;
+  GPtrArray      *id_to_value;
+  GHashTable     *char_tables;
+  GHashTable     *removed;
+  guint           in_bulk_insert : 1;
+  guint           case_sensitive : 1;
+};
+
+#pragma pack(push, 1)
+typedef struct
+{
+#ifdef G_OS_WIN32
+  guint32 id;
+  guint16 pos;
+#else
+  guint64 id : 32;
+  guint64 pos : 16;
+#endif
+} DzlFuzzyMutableIndexItem;
+#pragma pack(pop)
+
+G_STATIC_ASSERT (sizeof(DzlFuzzyMutableIndexItem) == 6);
+
+typedef struct
+{
+   DzlFuzzyMutableIndex        *fuzzy;
+   GArray      **tables;
+   gint         *state;
+   guint         n_tables;
+   gsize         max_matches;
+   const gchar  *needle;
+   GHashTable   *matches;
+} DzlFuzzyMutableIndexLookup;
+
+static gint
+dzl_fuzzy_mutable_index_item_compare (gconstpointer a,
+                                      gconstpointer b)
+{
+  gint ret;
+
+  const DzlFuzzyMutableIndexItem *fa = a;
+  const DzlFuzzyMutableIndexItem *fb = b;
+
+  if ((ret = fa->id - fb->id) == 0)
+    ret = fa->pos - fb->pos;
+
+  return ret;
+}
+
+static gint
+dzl_fuzzy_mutable_index_match_compare (gconstpointer a,
+                                       gconstpointer b)
+{
+  const DzlFuzzyMutableIndexMatch *ma = a;
+  const DzlFuzzyMutableIndexMatch *mb = b;
+
+  if (ma->score < mb->score) {
+    return 1;
+  } else if (ma->score > mb->score) {
+    return -1;
+  }
+
+  return strcmp (ma->key, mb->key);
+}
+
+DzlFuzzyMutableIndex *
+dzl_fuzzy_mutable_index_ref (DzlFuzzyMutableIndex *fuzzy)
+{
+  g_return_val_if_fail (fuzzy, NULL);
+  g_return_val_if_fail (fuzzy->ref_count > 0, NULL);
+
+  g_atomic_int_inc (&fuzzy->ref_count);
+
+  return fuzzy;
+}
+
+/**
+ * dzl_fuzzy_mutable_index_new:
+ * @case_sensitive: %TRUE if case should be preserved.
+ *
+ * Create a new #Fuzzy for fuzzy matching strings.
+ *
+ * Returns: A newly allocated #Fuzzy that should be freed with dzl_fuzzy_mutable_index_unref().
+ */
+DzlFuzzyMutableIndex *
+dzl_fuzzy_mutable_index_new (gboolean case_sensitive)
+{
+  DzlFuzzyMutableIndex *fuzzy;
+
+  fuzzy = g_slice_new0 (DzlFuzzyMutableIndex);
+  fuzzy->ref_count = 1;
+  fuzzy->heap = g_byte_array_new ();
+  fuzzy->id_to_value = g_ptr_array_new ();
+  fuzzy->id_to_text_offset = g_array_new (FALSE, FALSE, sizeof (gsize));
+  fuzzy->char_tables = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_array_unref);
+  fuzzy->case_sensitive = case_sensitive;
+  fuzzy->removed = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+  return fuzzy;
+}
+
+DzlFuzzyMutableIndex *
+dzl_fuzzy_mutable_index_new_with_free_func (gboolean       case_sensitive,
+                                            GDestroyNotify free_func)
+{
+  DzlFuzzyMutableIndex *fuzzy;
+
+  fuzzy = dzl_fuzzy_mutable_index_new (case_sensitive);
+  dzl_fuzzy_mutable_index_set_free_func (fuzzy, free_func);
+
+  return fuzzy;
+}
+
+void
+dzl_fuzzy_mutable_index_set_free_func (DzlFuzzyMutableIndex *fuzzy,
+                                       GDestroyNotify        free_func)
+{
+  g_return_if_fail (fuzzy);
+
+  g_ptr_array_set_free_func (fuzzy->id_to_value, free_func);
+}
+
+static gsize
+dzl_fuzzy_mutable_index_heap_insert (DzlFuzzyMutableIndex *fuzzy,
+                                     const gchar          *text)
+{
+  gsize ret;
+
+  g_assert (fuzzy != NULL);
+  g_assert (text != NULL);
+
+  ret = fuzzy->heap->len;
+
+  g_byte_array_append (fuzzy->heap, (guint8 *)text, strlen (text) + 1);
+
+  return ret;
+}
+
+/**
+ * dzl_fuzzy_mutable_index_begin_bulk_insert:
+ * @fuzzy: (in): A #Fuzzy.
+ *
+ * Start a bulk insertion. @fuzzy is not ready for searching until
+ * dzl_fuzzy_mutable_index_end_bulk_insert() has been called.
+ *
+ * This allows for inserting large numbers of strings and deferring
+ * the final sort until dzl_fuzzy_mutable_index_end_bulk_insert().
+ */
+void
+dzl_fuzzy_mutable_index_begin_bulk_insert (DzlFuzzyMutableIndex *fuzzy)
+{
+   g_return_if_fail (fuzzy);
+   g_return_if_fail (!fuzzy->in_bulk_insert);
+
+   fuzzy->in_bulk_insert = TRUE;
+}
+
+/**
+ * dzl_fuzzy_mutable_index_end_bulk_insert:
+ * @fuzzy: (in): A #Fuzzy.
+ *
+ * Complete a bulk insert and resort the index.
+ */
+void
+dzl_fuzzy_mutable_index_end_bulk_insert (DzlFuzzyMutableIndex *fuzzy)
+{
+   GHashTableIter iter;
+   gpointer key;
+   gpointer value;
+
+   g_return_if_fail(fuzzy);
+   g_return_if_fail(fuzzy->in_bulk_insert);
+
+   fuzzy->in_bulk_insert = FALSE;
+
+   g_hash_table_iter_init (&iter, fuzzy->char_tables);
+
+   while (g_hash_table_iter_next (&iter, &key, &value)) {
+      GArray *table = value;
+
+      g_array_sort (table, dzl_fuzzy_mutable_index_item_compare);
+   }
+}
+
+/**
+ * dzl_fuzzy_mutable_index_insert:
+ * @fuzzy: (in): A #Fuzzy.
+ * @key: (in): A UTF-8 encoded string.
+ * @value: (in): A value to associate with key.
+ *
+ * Inserts a string into the fuzzy matcher.
+ */
+void
+dzl_fuzzy_mutable_index_insert (DzlFuzzyMutableIndex *fuzzy,
+                                const gchar          *key,
+                                gpointer              value)
+{
+  const gchar *tmp;
+  gchar *downcase = NULL;
+  gsize offset;
+  guint id;
+
+  if (G_UNLIKELY (!key || !*key || (fuzzy->id_to_text_offset->len == G_MAXUINT)))
+    return;
+
+  if (!fuzzy->case_sensitive)
+    downcase = g_utf8_casefold (key, -1);
+
+  offset = dzl_fuzzy_mutable_index_heap_insert (fuzzy, key);
+  id = fuzzy->id_to_text_offset->len;
+  g_array_append_val (fuzzy->id_to_text_offset, offset);
+  g_ptr_array_add (fuzzy->id_to_value, value);
+
+  if (!fuzzy->case_sensitive)
+    key = downcase;
+
+  for (tmp = key; *tmp; tmp = g_utf8_next_char (tmp))
+    {
+      gunichar ch = g_utf8_get_char (tmp);
+      GArray *table;
+      DzlFuzzyMutableIndexItem item;
+
+      table = g_hash_table_lookup (fuzzy->char_tables, GINT_TO_POINTER (ch));
+
+      if (G_UNLIKELY (table == NULL))
+        {
+          table = g_array_new (FALSE, FALSE, sizeof (DzlFuzzyMutableIndexItem));
+          g_hash_table_insert (fuzzy->char_tables, GINT_TO_POINTER (ch), table);
+        }
+
+      item.id = id;
+      item.pos = (guint)(gsize)(tmp - key);
+
+      g_array_append_val (table, item);
+    }
+
+  if (G_UNLIKELY (!fuzzy->in_bulk_insert))
+    {
+      for (tmp = key; *tmp; tmp = g_utf8_next_char (tmp))
+        {
+          GArray *table;
+          gunichar ch;
+
+          ch = g_utf8_get_char (tmp);
+          table = g_hash_table_lookup (fuzzy->char_tables, GINT_TO_POINTER (ch));
+          g_array_sort (table, dzl_fuzzy_mutable_index_item_compare);
+        }
+    }
+
+  g_free (downcase);
+}
+
+/**
+ * dzl_fuzzy_mutable_index_unref:
+ * @fuzzy: A #Fuzzy.
+ *
+ * Decrements the reference count of fuzzy by one. When the reference count
+ * reaches zero, the structure will be freed.
+ */
+void
+dzl_fuzzy_mutable_index_unref (DzlFuzzyMutableIndex *fuzzy)
+{
+  g_return_if_fail (fuzzy);
+  g_return_if_fail (fuzzy->ref_count > 0);
+
+  if (G_UNLIKELY (g_atomic_int_dec_and_test (&fuzzy->ref_count)))
+    {
+      g_byte_array_unref (fuzzy->heap);
+      fuzzy->heap = NULL;
+
+      g_array_unref (fuzzy->id_to_text_offset);
+      fuzzy->id_to_text_offset = NULL;
+
+      g_ptr_array_unref (fuzzy->id_to_value);
+      fuzzy->id_to_value = NULL;
+
+      g_hash_table_unref (fuzzy->char_tables);
+      fuzzy->char_tables = NULL;
+
+      g_hash_table_unref (fuzzy->removed);
+      fuzzy->removed = NULL;
+
+      g_slice_free (DzlFuzzyMutableIndex, fuzzy);
+    }
+}
+
+static void
+rollback_state_to_pos (GArray *table,
+                       gint   *state,
+                       guint   id,
+                       guint   pos)
+{
+  g_assert (table != NULL);
+  g_assert (state != NULL);
+  g_assert (pos > 0);
+
+  while (*state > 0 && (guint) *state <= table->len)
+    {
+      DzlFuzzyMutableIndexItem *iter;
+
+      (*state)--;
+
+      iter = &g_array_index (table, DzlFuzzyMutableIndexItem, *state);
+
+      if (iter->id > id || (iter->id == id && (guint) *state >= pos))
+        continue;
+
+      break;
+    }
+}
+
+static gboolean
+dzl_fuzzy_mutable_index_do_match (DzlFuzzyMutableIndexLookup *lookup,
+                                  DzlFuzzyMutableIndexItem   *item,
+                                  guint                       table_index,
+                                  gint                        score)
+{
+  gboolean ret = FALSE;
+  GArray *table;
+  gint *state;
+
+  table = lookup->tables [table_index];
+  state = &lookup->state [table_index];
+
+  for (; state [0] < (gint)table->len; state [0]++)
+    {
+      DzlFuzzyMutableIndexItem *iter;
+      gpointer key;
+      gint iter_score;
+
+      iter = &g_array_index (table, DzlFuzzyMutableIndexItem, state[0]);
+
+      if ((iter->id < item->id) || ((iter->id == item->id) && (iter->pos <= item->pos)))
+        continue;
+      else if (iter->id > item->id)
+        break;
+
+      iter_score = score + (iter->pos - item->pos - 1);
+
+      if ((table_index + 1) < lookup->n_tables)
+        {
+          if (dzl_fuzzy_mutable_index_do_match (lookup, iter, table_index + 1, iter_score))
+            {
+              ret = TRUE;
+
+              /* We already found a match, but we could have a better match
+               * further in the word. We need to rollback all of our additional
+               * table state to the current position so that we can possibly
+               * advance again.
+               */
+              if ((state[0] + 1) < (gint) table->len &&
+                  g_array_index (table, DzlFuzzyMutableIndexItem, state[0] + 1).id == item->id)
+                {
+                  for (guint i = table_index + 1; i < lookup->n_tables; i++)
+                    rollback_state_to_pos (lookup->tables[i], &lookup->state[i], iter->id, iter->pos + 1);
+                }
+            }
+          continue;
+        }
+
+      key = GINT_TO_POINTER (iter->id);
+
+      if (!g_hash_table_contains (lookup->matches, key) ||
+          (iter_score < GPOINTER_TO_INT (g_hash_table_lookup (lookup->matches, key))))
+        g_hash_table_insert (lookup->matches, key, GINT_TO_POINTER (iter_score));
+
+      ret = TRUE;
+    }
+
+  return ret;
+}
+
+static inline const gchar *
+dzl_fuzzy_mutable_index_get_string (DzlFuzzyMutableIndex *fuzzy,
+                                    gint                  id)
+{
+  guint offset = g_array_index (fuzzy->id_to_text_offset, gsize, id);
+  return (const gchar *)&fuzzy->heap->data [offset];
+}
+
+/**
+ * dzl_fuzzy_mutable_index_match:
+ * @fuzzy: (in): A #Fuzzy.
+ * @needle: (in): The needle to fuzzy search for.
+ * @max_matches: (in): The max number of matches to return.
+ *
+ * DzlFuzzyMutableIndex searches within @fuzzy for strings that fuzzy match @needle.
+ * Only up to @max_matches will be returned.
+ *
+ * TODO: max_matches is not yet respected.
+ *
+ * Returns: (transfer full) (element-type DzlFuzzyMutableIndexMatch): A newly allocated
+ *   #GArray containing #FuzzyMatch elements. This should be freed when
+ *   the caller is done with it using g_array_unref().
+ *   It is a programming error to keep the structure around longer than
+ *   the @fuzzy instance.
+ */
+GArray *
+dzl_fuzzy_mutable_index_match (DzlFuzzyMutableIndex *fuzzy,
+                               const gchar          *needle,
+                               gsize                 max_matches)
+{
+  DzlFuzzyMutableIndexLookup lookup = { 0 };
+  DzlFuzzyMutableIndexMatch match;
+  DzlFuzzyMutableIndexItem *item;
+  GHashTableIter iter;
+  gpointer key;
+  gpointer value;
+  const gchar *tmp;
+  GArray *matches = NULL;
+  GArray *root;
+  gchar *downcase = NULL;
+  guint i;
+
+  g_return_val_if_fail (fuzzy, NULL);
+  g_return_val_if_fail (!fuzzy->in_bulk_insert, NULL);
+  g_return_val_if_fail (needle, NULL);
+
+  matches = g_array_new (FALSE, FALSE, sizeof (DzlFuzzyMutableIndexMatch));
+
+  if (!*needle)
+    goto cleanup;
+
+  if (!fuzzy->case_sensitive)
+    {
+      downcase = g_utf8_casefold (needle, -1);
+      needle = downcase;
+    }
+
+  lookup.fuzzy = fuzzy;
+  lookup.n_tables = g_utf8_strlen (needle, -1);
+  lookup.state = g_new0 (gint, lookup.n_tables);
+  lookup.tables = g_new0 (GArray*, lookup.n_tables);
+  lookup.needle = needle;
+  lookup.max_matches = max_matches;
+  lookup.matches = g_hash_table_new (NULL, NULL);
+
+  for (i = 0, tmp = needle; *tmp; tmp = g_utf8_next_char (tmp))
+    {
+      gunichar ch;
+      GArray *table;
+
+      ch = g_utf8_get_char (tmp);
+      table = g_hash_table_lookup (fuzzy->char_tables, GINT_TO_POINTER (ch));
+
+      if (table == NULL)
+        goto cleanup;
+
+      lookup.tables [i++] = table;
+    }
+
+  g_assert (lookup.n_tables == i);
+  g_assert (lookup.tables [0] != NULL);
+
+  root = lookup.tables [0];
+
+  if (G_LIKELY (lookup.n_tables > 1))
+    {
+      for (i = 0; i < root->len; i++)
+        {
+          item = &g_array_index (root, DzlFuzzyMutableIndexItem, i);
+
+          if (dzl_fuzzy_mutable_index_do_match (&lookup, item, 1, 0) &&
+              i + 1 < root->len &&
+              (item + 1)->id == item->id)
+            {
+              /* We found a match, but we might find another one with a higher
+               * score later on for the same item of the corpus.  We need to
+               * roll state back to the position we're starting at so that we
+               * can match all the same characters again.
+               */
+              for (guint j = 1; j < lookup.n_tables; j++)
+                rollback_state_to_pos (lookup.tables[j], &lookup.state[j], item->id, item->pos + 1);
+            }
+        }
+    }
+  else
+    {
+      guint last_id = G_MAXUINT;
+
+      for (i = 0; i < root->len; i++)
+        {
+          item = &g_array_index (root, DzlFuzzyMutableIndexItem, i);
+          match.id = GPOINTER_TO_INT (item->id);
+          if (match.id != last_id)
+            {
+              match.key = dzl_fuzzy_mutable_index_get_string (fuzzy, item->id);
+              match.value = g_ptr_array_index (fuzzy->id_to_value, item->id);
+              match.score = 1.0 / (strlen (match.key) + item->pos);
+              g_array_append_val (matches, match);
+              last_id = match.id;
+            }
+        }
+
+      goto cleanup;
+    }
+
+  g_hash_table_iter_init (&iter, lookup.matches);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      /* Ignore keys that have a tombstone record. */
+      if (g_hash_table_contains (fuzzy->removed, key))
+        continue;
+
+      match.id = GPOINTER_TO_INT (key);
+      match.key = dzl_fuzzy_mutable_index_get_string (fuzzy, match.id);
+      match.value = g_ptr_array_index (fuzzy->id_to_value, match.id);
+
+      /* If we got a perfect substring match, then this is 1.0, and avoid
+       * perturbing further or we risk non-contiguous (but shorter strings)
+       * matching at higher value.
+       */
+      if (value == NULL)
+        match.score = 1.0;
+      else
+        match.score = 1.0 / (strlen (match.key) + GPOINTER_TO_INT (value));
+
+      g_array_append_val (matches, match);
+    }
+
+  /*
+   * TODO: We could be more clever here when inserting into the array
+   *       only if it is a lower score than the end or < max items.
+   */
+
+  if (max_matches != 0)
+    {
+      g_array_sort (matches, dzl_fuzzy_mutable_index_match_compare);
+
+      if (max_matches && (matches->len > max_matches))
+        g_array_set_size (matches, max_matches);
+    }
+
+cleanup:
+  g_free (downcase);
+  g_free (lookup.state);
+  g_free (lookup.tables);
+  g_clear_pointer (&lookup.matches, g_hash_table_unref);
+
+  return matches;
+}
+
+gboolean
+dzl_fuzzy_mutable_index_contains (DzlFuzzyMutableIndex *fuzzy,
+                                  const gchar          *key)
+{
+  GArray *ar;
+  gboolean ret;
+
+  g_return_val_if_fail (fuzzy != NULL, FALSE);
+
+  ar = dzl_fuzzy_mutable_index_match (fuzzy, key, 1);
+  ret = (ar != NULL) && (ar->len > 0);
+  g_clear_pointer (&ar, g_array_unref);
+
+  return ret;
+}
+
+void
+dzl_fuzzy_mutable_index_remove (DzlFuzzyMutableIndex *fuzzy,
+                                const gchar          *key)
+{
+  GArray *ar;
+
+  g_return_if_fail (fuzzy != NULL);
+
+  if (!key || !*key)
+    return;
+
+  ar = dzl_fuzzy_mutable_index_match (fuzzy, key, 1);
+
+  if (ar != NULL && ar->len > 0)
+    {
+      for (guint i = 0; i < ar->len; i++)
+        {
+          const DzlFuzzyMutableIndexMatch *match = &g_array_index (ar, DzlFuzzyMutableIndexMatch, i);
+
+          if (g_strcmp0 (match->key, key) == 0)
+            g_hash_table_insert (fuzzy->removed, GINT_TO_POINTER (match->id), NULL);
+        }
+    }
+
+  g_clear_pointer (&ar, g_array_unref);
+}
+
+gchar *
+dzl_fuzzy_highlight (const gchar *str,
+                     const gchar *match,
+                     gboolean     case_sensitive)
+{
+  static const gchar *begin = "<b>";
+  static const gchar *end = "</b>";
+  GString *ret;
+  gunichar str_ch;
+  gunichar match_ch;
+  gboolean element_open = FALSE;
+
+  if (str == NULL || match == NULL)
+    return g_strdup (str);
+
+  ret = g_string_new (NULL);
+
+  for (; *str; str = g_utf8_next_char (str))
+    {
+      str_ch = g_utf8_get_char (str);
+      match_ch = g_utf8_get_char (match);
+
+      if (str_ch == '&')
+        {
+          const gchar *entity_end = strchr (str, ';');
+
+          if (entity_end != NULL)
+            {
+              gsize len = entity_end - str;
+
+              if (element_open)
+                {
+                  g_string_append (ret, end);
+                  element_open = FALSE;
+                }
+
+              g_string_append_len (ret, str, len + 1);
+              str += len;
+
+              continue;
+            }
+        }
+
+      if ((str_ch == match_ch) ||
+          (!case_sensitive && g_unichar_tolower (str_ch) == g_unichar_tolower (match_ch)))
+        {
+          if (!element_open)
+            {
+              g_string_append (ret, begin);
+              element_open = TRUE;
+            }
+
+          if (str_ch == '<')
+            g_string_append (ret, "&lt;");
+          else if (str_ch == '>')
+            g_string_append (ret, "&gt;");
+          else
+            g_string_append_unichar (ret, str_ch);
+
+          /* TODO: We could seek to the next char and append in a batch. */
+          match = g_utf8_next_char (match);
+        }
+      else
+        {
+          if (element_open)
+            {
+              g_string_append (ret, end);
+              element_open = FALSE;
+            }
+
+          if (str_ch == '<')
+            g_string_append (ret, "&lt;");
+          else if (str_ch == '>')
+            g_string_append (ret, "&gt;");
+          else
+            g_string_append_unichar (ret, str_ch);
+        }
+    }
+
+  if (element_open)
+    g_string_append (ret, end);
+
+  return g_string_free (ret, FALSE);
+}
diff --git a/lib/contrib/dzl-fuzzy-mutable-index.h b/lib/contrib/dzl-fuzzy-mutable-index.h
new file mode 100644
index 000000000..e2c092e65
--- /dev/null
+++ b/lib/contrib/dzl-fuzzy-mutable-index.h
@@ -0,0 +1,66 @@
+/* fuzzy.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+#ifndef DZL_FUZZY_MUTABLE_INDEX_H
+#define DZL_FUZZY_MUTABLE_INDEX_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define DZL_TYPE_FUZZY_MUTABLE_INDEX (dzl_fuzzy_mutable_index_get_type())
+
+typedef struct _DzlFuzzyMutableIndex DzlFuzzyMutableIndex;
+
+typedef struct
+{
+   const gchar *key;
+   gpointer     value;
+   gfloat       score;
+   guint        id;
+} DzlFuzzyMutableIndexMatch;
+
+GType                     dzl_fuzzy_mutable_index_get_type           (void);
+DzlFuzzyMutableIndex     *dzl_fuzzy_mutable_index_new                (gboolean              case_sensitive);
+DzlFuzzyMutableIndex     *dzl_fuzzy_mutable_index_new_with_free_func (gboolean              case_sensitive,
+                                                                      GDestroyNotify        free_func);
+void                      dzl_fuzzy_mutable_index_set_free_func      (DzlFuzzyMutableIndex *fuzzy,
+                                                                      GDestroyNotify        free_func);
+void                      dzl_fuzzy_mutable_index_begin_bulk_insert  (DzlFuzzyMutableIndex *fuzzy);
+void                      dzl_fuzzy_mutable_index_end_bulk_insert    (DzlFuzzyMutableIndex *fuzzy);
+gboolean                  dzl_fuzzy_mutable_index_contains           (DzlFuzzyMutableIndex *fuzzy,
+                                                                      const gchar          *key);
+void                      dzl_fuzzy_mutable_index_insert             (DzlFuzzyMutableIndex *fuzzy,
+                                                                      const gchar          *key,
+                                                                      gpointer              value);
+GArray                   *dzl_fuzzy_mutable_index_match              (DzlFuzzyMutableIndex *fuzzy,
+                                                                      const gchar          *needle,
+                                                                      gsize                 max_matches);
+void                      dzl_fuzzy_mutable_index_remove             (DzlFuzzyMutableIndex *fuzzy,
+                                                                      const gchar          *key);
+DzlFuzzyMutableIndex     *dzl_fuzzy_mutable_index_ref                (DzlFuzzyMutableIndex *fuzzy);
+void                      dzl_fuzzy_mutable_index_unref              (DzlFuzzyMutableIndex *fuzzy);
+gchar                    *dzl_fuzzy_highlight                        (const gchar          *str,
+                                                                      const gchar          *query,
+                                                                      gboolean              case_sensitive);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (DzlFuzzyMutableIndex, dzl_fuzzy_mutable_index_unref)
+
+G_END_DECLS
+
+#endif /* DZL_FUZZY_MUTABLE_INDEX_H */
diff --git a/lib/contrib/dzl-suggestion.c b/lib/contrib/dzl-suggestion.c
new file mode 100644
index 000000000..100ea3cab
--- /dev/null
+++ b/lib/contrib/dzl-suggestion.c
@@ -0,0 +1,547 @@
+/* dzl-suggestion.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file 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 Lesser 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 "dzl-suggestion.h"
+
+typedef struct
+{
+  gchar *title;
+  gchar *subtitle;
+  gchar *id;
+
+  /* interned string */
+  const gchar *icon_name;
+  const gchar *secondary_icon_name;
+
+  GIcon *icon;
+  GIcon *secondary_icon;
+} DzlSuggestionPrivate;
+
+enum {
+  PROP_0,
+  PROP_ICON_NAME,
+  PROP_ICON,
+  PROP_SECONDARY_ICON_NAME,
+  PROP_SECONDARY_ICON,
+  PROP_ID,
+  PROP_SUBTITLE,
+  PROP_TITLE,
+  N_PROPS
+};
+
+enum {
+  REPLACE_TYPED_TEXT,
+  SUGGEST_SUFFIX,
+  N_SIGNALS
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (DzlSuggestion, dzl_suggestion, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static GIcon *
+dzl_suggestion_real_get_icon (DzlSuggestion *self)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_assert (DZL_IS_SUGGESTION (self));
+
+  if (priv->icon_name != NULL)
+    return g_icon_new_for_string (priv->icon_name, NULL);
+
+  return NULL;
+}
+
+static GIcon *
+dzl_suggestion_real_get_secondary_icon (DzlSuggestion *self)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_assert (DZL_IS_SUGGESTION (self));
+
+  if (priv->secondary_icon_name != NULL)
+    return g_icon_new_for_string (priv->secondary_icon_name, NULL);
+
+  return NULL;
+}
+
+static void
+dzl_suggestion_finalize (GObject *object)
+{
+  DzlSuggestion *self = (DzlSuggestion *)object;
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  priv->icon_name = NULL;
+
+  g_clear_pointer (&priv->title, g_free);
+  g_clear_pointer (&priv->subtitle, g_free);
+  g_clear_pointer (&priv->id, g_free);
+
+  G_OBJECT_CLASS (dzl_suggestion_parent_class)->finalize (object);
+}
+
+static void
+dzl_suggestion_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  DzlSuggestion *self = DZL_SUGGESTION (object);
+
+  switch (prop_id)
+    {
+    case PROP_ID:
+      g_value_set_string (value, dzl_suggestion_get_id (self));
+      break;
+
+    case PROP_ICON_NAME:
+      g_value_set_static_string (value, dzl_suggestion_get_icon_name (self));
+      break;
+
+    case PROP_ICON:
+      g_value_take_object (value, dzl_suggestion_get_icon (self));
+      break;
+
+    case PROP_SECONDARY_ICON_NAME:
+      g_value_set_static_string (value, dzl_suggestion_get_secondary_icon_name (self));
+      break;
+
+    case PROP_SECONDARY_ICON:
+      g_value_take_object (value, dzl_suggestion_get_secondary_icon (self));
+      break;
+
+    case PROP_TITLE:
+      g_value_set_string (value, dzl_suggestion_get_title (self));
+      break;
+
+    case PROP_SUBTITLE:
+      g_value_set_string (value, dzl_suggestion_get_subtitle (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+dzl_suggestion_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  DzlSuggestion *self = DZL_SUGGESTION (object);
+
+  switch (prop_id)
+    {
+    case PROP_ICON_NAME:
+      dzl_suggestion_set_icon_name (self, g_value_get_string (value));
+      break;
+
+    case PROP_SECONDARY_ICON_NAME:
+      dzl_suggestion_set_secondary_icon_name (self, g_value_get_string (value));
+      break;
+
+    case PROP_ID:
+      dzl_suggestion_set_id (self, g_value_get_string (value));
+      break;
+
+    case PROP_TITLE:
+      dzl_suggestion_set_title (self, g_value_get_string (value));
+      break;
+
+    case PROP_SUBTITLE:
+      dzl_suggestion_set_subtitle (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+dzl_suggestion_class_init (DzlSuggestionClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = dzl_suggestion_finalize;
+  object_class->get_property = dzl_suggestion_get_property;
+  object_class->set_property = dzl_suggestion_set_property;
+
+  klass->get_icon = dzl_suggestion_real_get_icon;
+  klass->get_secondary_icon = dzl_suggestion_real_get_secondary_icon;
+
+  properties [PROP_ID] =
+    g_param_spec_string ("id",
+                         "Id",
+                         "The suggestion identifier",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_ICON] =
+    g_param_spec_object ("icon",
+                         "Icon",
+                         "The GIcon for the suggestion",
+                         G_TYPE_ICON,
+                         (G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_ICON_NAME] =
+    g_param_spec_string ("icon-name",
+                         "Icon Name",
+                         "The name of the icon to display",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_SECONDARY_ICON] =
+    g_param_spec_object ("secondary-icon",
+                         "Secondary Icon",
+                         "The secondary GIcon for the suggestion on the right",
+                         G_TYPE_ICON,
+                         (G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_SECONDARY_ICON_NAME] =
+    g_param_spec_string ("secondary-icon-name",
+                         "Secondary Icon Name",
+                         "The name of the secondary icon to display",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_TITLE] =
+    g_param_spec_string ("title",
+                         "Title",
+                         "The title of the suggestion",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_SUBTITLE] =
+    g_param_spec_string ("subtitle",
+                         "Subtitle",
+                         "The subtitle of the suggestion",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  signals [REPLACE_TYPED_TEXT] =
+    g_signal_new ("replace-typed-text",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (DzlSuggestionClass, replace_typed_text),
+                  g_signal_accumulator_first_wins, NULL, NULL,
+                  G_TYPE_STRING, 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+  signals [SUGGEST_SUFFIX] =
+    g_signal_new ("suggest-suffix",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (DzlSuggestionClass, suggest_suffix),
+                  g_signal_accumulator_first_wins, NULL, NULL,
+                  G_TYPE_STRING, 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
+}
+
+static void
+dzl_suggestion_init (DzlSuggestion *self)
+{
+}
+
+const gchar *
+dzl_suggestion_get_id (DzlSuggestion *self)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  return priv->id;
+}
+
+const gchar *
+dzl_suggestion_get_icon_name (DzlSuggestion *self)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  return priv->icon_name;
+}
+
+const gchar *
+dzl_suggestion_get_secondary_icon_name (DzlSuggestion *self)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  return priv->secondary_icon_name;
+}
+
+const gchar *
+dzl_suggestion_get_title (DzlSuggestion *self)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  return priv->title;
+}
+
+const gchar *
+dzl_suggestion_get_subtitle (DzlSuggestion *self)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  return priv->subtitle;
+}
+
+void
+dzl_suggestion_set_icon_name (DzlSuggestion *self,
+                              const gchar   *icon_name)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_if_fail (DZL_IS_SUGGESTION (self));
+
+  icon_name = g_intern_string (icon_name);
+
+  if (priv->icon_name != icon_name)
+    {
+      priv->icon_name = icon_name;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON_NAME]);
+    }
+}
+
+void
+dzl_suggestion_set_secondary_icon_name (DzlSuggestion *self,
+                                        const gchar   *icon_name)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_if_fail (DZL_IS_SUGGESTION (self));
+
+  icon_name = g_intern_string (icon_name);
+
+  if (priv->secondary_icon_name != icon_name)
+    {
+      priv->secondary_icon_name = icon_name;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SECONDARY_ICON_NAME]);
+    }
+}
+
+void
+dzl_suggestion_set_id (DzlSuggestion *self,
+                       const gchar   *id)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_if_fail (DZL_IS_SUGGESTION (self));
+
+  if (g_strcmp0 (priv->id, id) != 0)
+    {
+      g_free (priv->id);
+      priv->id = g_strdup (id);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]);
+    }
+}
+
+void
+dzl_suggestion_set_title (DzlSuggestion *self,
+                          const gchar   *title)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_if_fail (DZL_IS_SUGGESTION (self));
+
+  if (g_strcmp0 (priv->title, title) != 0)
+    {
+      g_free (priv->title);
+      priv->title = g_strdup (title);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+    }
+}
+
+void
+dzl_suggestion_set_subtitle (DzlSuggestion *self,
+                             const gchar   *subtitle)
+{
+  DzlSuggestionPrivate *priv = dzl_suggestion_get_instance_private (self);
+
+  g_return_if_fail (DZL_IS_SUGGESTION (self));
+
+  if (g_strcmp0 (priv->subtitle, subtitle) != 0)
+    {
+      g_free (priv->subtitle);
+      priv->subtitle = g_strdup (subtitle);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SUBTITLE]);
+    }
+}
+
+/**
+ * dzl_suggestion_suggest_suffix:
+ * @self: a #DzlSuggestion
+ * @typed_text: The user entered text
+ *
+ * This function requests potential text to append to @typed_text to make it
+ * more clear to the user what they will be activating by selecting this
+ * suggestion. For example, if they start typing "gno", a potential suggested
+ * suffix might be "me.org" to create "gnome.org".
+ *
+ * Returns: (transfer full) (nullable): Suffix to append to @typed_text
+ *   or %NULL to leave it unchanged.
+ */
+gchar *
+dzl_suggestion_suggest_suffix (DzlSuggestion *self,
+                               const gchar   *typed_text)
+{
+  gchar *ret = NULL;
+
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+  g_return_val_if_fail (typed_text != NULL, NULL);
+
+  g_signal_emit (self, signals [SUGGEST_SUFFIX], 0, typed_text, &ret);
+
+  return ret;
+}
+
+DzlSuggestion *
+dzl_suggestion_new (void)
+{
+  return g_object_new (DZL_TYPE_SUGGESTION, NULL);
+}
+
+/**
+ * dzl_suggestion_replace_typed_text:
+ * @self: An #DzlSuggestion
+ * @typed_text: the text that was typed into the entry
+ *
+ * This function is meant to be used to replace the text in the entry with text
+ * that represents the suggestion most accurately. This happens when the user
+ * presses tab while typing a suggestion. For example, if typing "gno" in the
+ * entry, you might have a suggest_suffix of "me.org" so that the user sees
+ * "gnome.org". But the replace_typed_text might include more data such as
+ * "https://gnome.org"; as it more closely represents the suggestion.
+ *
+ * Returns: (transfer full) (nullable): The replacement text to insert into
+ *   the entry when "tab" is pressed to complete the insertion.
+ */
+gchar *
+dzl_suggestion_replace_typed_text (DzlSuggestion *self,
+                                   const gchar   *typed_text)
+{
+  gchar *ret = NULL;
+
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  g_signal_emit (self, signals [REPLACE_TYPED_TEXT], 0, typed_text, &ret);
+
+  return ret;
+}
+
+/**
+ * dzl_suggestion_get_icon:
+ * @self: a #DzlSuggestion
+ *
+ * Gets the icon for the suggestion, if any.
+ *
+ * Returns: (transfer full) (nullable): a #GIcon or %NULL
+ *
+ * Since: 3.30
+ */
+GIcon *
+dzl_suggestion_get_icon (DzlSuggestion *self)
+{
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  return DZL_SUGGESTION_GET_CLASS (self)->get_icon (self);
+}
+
+/**
+ * dzl_suggestion_get_icon_surface:
+ * @self: a #DzlSuggestion
+ * @widget: a widget that may contain the surface
+ *
+ * This function allows subclasses to dynamicly generate content for the
+ * suggestion such as may be required when integrating with favicons or
+ * similar.
+ *
+ * @widget is provided so that the implementation may determine scale or
+ * any other style-specific settings from the style context.
+ *
+ * Returns: (transfer full) (nullable): a #cairo_surface_t or %NULL
+ *
+ * Since: 3.30
+ */
+cairo_surface_t *
+dzl_suggestion_get_icon_surface (DzlSuggestion *self,
+                                 GtkWidget     *widget)
+{
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  if (DZL_SUGGESTION_GET_CLASS (self)->get_icon_surface)
+    return DZL_SUGGESTION_GET_CLASS (self)->get_icon_surface (self, widget);
+
+  return NULL;
+}
+
+/**
+ * dzl_suggestion_get_secondary_icon:
+ * @self: a #DzlSuggestion
+ *
+ * Gets the secondary icon for the suggestion, if any.
+ *
+ * Returns: (transfer full) (nullable): a #GIcon or %NULL
+ *
+ * Since: 3.36
+ */
+GIcon *
+dzl_suggestion_get_secondary_icon (DzlSuggestion *self)
+{
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  return DZL_SUGGESTION_GET_CLASS (self)->get_secondary_icon (self);
+}
+
+/**
+ * dzl_suggestion_get_secondary_icon_surface:
+ * @self: a #DzlSuggestion
+ * @widget: a widget that may contain the surface
+ *
+ * This function allows subclasses to dynamicly generate content for the
+ * suggestion such as may be required when integrating with favicons or
+ * similar.
+ *
+ * @widget is provided so that the implementation may determine scale or
+ * any other style-specific settings from the style context.
+ *
+ * Returns: (transfer full) (nullable): a #cairo_surface_t or %NULL
+ *
+ * Since: 3.36
+ */
+cairo_surface_t *
+dzl_suggestion_get_secondary_icon_surface (DzlSuggestion *self,
+                                           GtkWidget     *widget)
+{
+  g_return_val_if_fail (DZL_IS_SUGGESTION (self), NULL);
+
+  if (DZL_SUGGESTION_GET_CLASS (self)->get_secondary_icon_surface)
+    return DZL_SUGGESTION_GET_CLASS (self)->get_secondary_icon_surface (self, widget);
+
+  return NULL;
+}
diff --git a/lib/contrib/dzl-suggestion.h b/lib/contrib/dzl-suggestion.h
new file mode 100644
index 000000000..58ab47ecd
--- /dev/null
+++ b/lib/contrib/dzl-suggestion.h
@@ -0,0 +1,77 @@
+/* dzl-suggestion.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file 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 Lesser 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/>.
+ */
+
+
+#ifndef DZL_SUGGESTION_H
+#define DZL_SUGGESTION_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define DZL_TYPE_SUGGESTION (dzl_suggestion_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (DzlSuggestion, dzl_suggestion, DZL, SUGGESTION, GObject)
+
+struct _DzlSuggestionClass
+{
+  GObjectClass parent_class;
+
+  gchar           *(*suggest_suffix)     (DzlSuggestion *self,
+                                          const gchar   *typed_text);
+  gchar           *(*replace_typed_text) (DzlSuggestion *self,
+                                          const gchar   *typed_text);
+  GIcon           *(*get_icon)           (DzlSuggestion *self);
+  cairo_surface_t *(*get_icon_surface)   (DzlSuggestion *self,
+                                          GtkWidget     *widget);
+  GIcon           *(*get_secondary_icon) (DzlSuggestion *self);
+  cairo_surface_t *(*get_secondary_icon_surface)   (DzlSuggestion *self,
+                                                    GtkWidget     *widget);
+};
+
+DzlSuggestion   *dzl_suggestion_new                (void);
+const gchar     *dzl_suggestion_get_id             (DzlSuggestion *self);
+void             dzl_suggestion_set_id             (DzlSuggestion *self,
+                                                    const gchar   *id);
+const gchar     *dzl_suggestion_get_icon_name      (DzlSuggestion *self);
+void             dzl_suggestion_set_icon_name      (DzlSuggestion *self,
+                                                    const gchar   *icon_name);
+const gchar     *dzl_suggestion_get_title          (DzlSuggestion *self);
+void             dzl_suggestion_set_title          (DzlSuggestion *self,
+                                                    const gchar   *title);
+const gchar     *dzl_suggestion_get_subtitle       (DzlSuggestion *self);
+void             dzl_suggestion_set_subtitle       (DzlSuggestion *self,
+                                                    const gchar   *subtitle);
+gchar           *dzl_suggestion_suggest_suffix     (DzlSuggestion *self,
+                                                    const gchar   *typed_text);
+gchar           *dzl_suggestion_replace_typed_text (DzlSuggestion *self,
+                                                    const gchar   *typed_text);
+GIcon           *dzl_suggestion_get_icon           (DzlSuggestion *self);
+cairo_surface_t *dzl_suggestion_get_icon_surface   (DzlSuggestion *self,
+                                                    GtkWidget     *widget);
+const gchar     *dzl_suggestion_get_secondary_icon_name (DzlSuggestion *self);
+void             dzl_suggestion_set_secondary_icon_name (DzlSuggestion *self,
+                                                         const gchar   *icon_name);
+
+GIcon           *dzl_suggestion_get_secondary_icon (DzlSuggestion *self);
+cairo_surface_t *dzl_suggestion_get_secondary_icon_surface (DzlSuggestion *self,
+                                                            GtkWidget     *widget);
+
+G_END_DECLS
+
+#endif /* DZL_SUGGESTION_H */
diff --git a/lib/ephy-file-helpers.c b/lib/ephy-file-helpers.c
index 1e4f827c4..9f21d1457 100644
--- a/lib/ephy-file-helpers.c
+++ b/lib/ephy-file-helpers.c
@@ -588,14 +588,11 @@ launch_application (GAppInfo *app,
   g_autoptr (GdkAppLaunchContext) context = NULL;
   g_autoptr (GError) error = NULL;
   GdkDisplay *display;
-  GdkScreen *screen;
   gboolean res;
 
   display = gdk_display_get_default ();
-  screen = gdk_screen_get_default ();
 
   context = gdk_display_get_app_launch_context (display);
-  gdk_app_launch_context_set_screen (context, screen);
 
   res = g_app_info_launch (app, files,
                            G_APP_LAUNCH_CONTEXT (context), &error);
@@ -669,15 +666,14 @@ ephy_file_launch_handler (GFile *file)
 static gboolean
 open_in_default_handler (const char *uri,
                          const char *mime_type,
-                         GdkScreen  *screen)
+                         GdkDisplay *display)
 {
   g_autoptr (GdkAppLaunchContext) context = NULL;
   g_autoptr (GAppInfo) appinfo = NULL;
   g_autoptr (GError) error = NULL;
   GList uris;
 
-  context = gdk_display_get_app_launch_context (screen ? gdk_screen_get_display (screen) : 
gdk_display_get_default ());
-  gdk_app_launch_context_set_screen (context, screen);
+  context = gdk_display_get_app_launch_context (display ? display : gdk_display_get_default ());
 
   appinfo = g_app_info_get_default_for_type (mime_type, TRUE);
   if (!appinfo) {
@@ -698,13 +694,13 @@ open_in_default_handler (const char *uri,
 
 gboolean
 ephy_file_open_uri_in_default_browser (const char *uri,
-                                       GdkScreen  *screen)
+                                       GdkDisplay *display)
 {
   if (ephy_is_running_inside_sandbox ()) {
     ephy_open_uri_via_flatpak_portal (uri);
     return TRUE;
   }
-  return open_in_default_handler (uri, "x-scheme-handler/http", screen);
+  return open_in_default_handler (uri, "x-scheme-handler/http", display);
 }
 
 /**
diff --git a/lib/ephy-file-helpers.h b/lib/ephy-file-helpers.h
index ccd015dcf..b75b9e4e1 100644
--- a/lib/ephy-file-helpers.h
+++ b/lib/ephy-file-helpers.h
@@ -69,7 +69,7 @@ char       *       ephy_sanitize_filename                   (char
 void               ephy_open_default_instance_window        (void);
 void               ephy_open_incognito_window               (const char            *uri);
 gboolean           ephy_file_open_uri_in_default_browser    (const char            *uri,
-                                                             GdkScreen             *screen);
+                                                             GdkDisplay            *display);
 gboolean           ephy_file_browse_to                      (GFile                 *file);
 void               ephy_copy_directory                      (const char            *source,
                                                              const char            *target);
diff --git a/lib/ephy-flatpak-utils.c b/lib/ephy-flatpak-utils.c
index 5b6900001..97deccdc8 100644
--- a/lib/ephy-flatpak-utils.c
+++ b/lib/ephy-flatpak-utils.c
@@ -29,7 +29,7 @@
 #include <fcntl.h>
 #include <gio/gio.h>
 #include <gio/gunixfdlist.h>
-#include <libportal-gtk3/portal-gtk3.h>
+#include <libportal-gtk4/portal-gtk4.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
diff --git a/lib/ephy-gui.c b/lib/ephy-gui.c
index bed4d7858..a35eb2ec2 100644
--- a/lib/ephy-gui.c
+++ b/lib/ephy-gui.c
@@ -53,7 +53,6 @@ void
 ephy_gui_help (GtkWidget  *parent,
                const char *page)
 {
-  GError *error = NULL;
   char *url;
 
   if (page)
@@ -61,61 +60,7 @@ ephy_gui_help (GtkWidget  *parent,
   else
     url = g_strdup ("help:epiphany");
 
-  gtk_show_uri_on_window (GTK_WINDOW (parent), url, GDK_CURRENT_TIME, &error);
-
-  if (error != NULL) {
-    GtkWidget *dialog;
-
-    dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
-                                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-                                     GTK_MESSAGE_ERROR,
-                                     GTK_BUTTONS_OK,
-                                     _("Could not display help: %s"),
-                                     error->message);
-    g_error_free (error);
-
-    g_signal_connect (dialog, "response",
-                      G_CALLBACK (gtk_widget_destroy), NULL);
-    gtk_widget_show (dialog);
-  }
+  gtk_show_uri (GTK_WINDOW (parent), url, GDK_CURRENT_TIME);
 
   g_free (url);
 }
-
-void
-ephy_gui_get_current_event (GdkEventType *otype,
-                            guint        *ostate,
-                            guint        *obutton,
-                            guint        *keyval)
-{
-  GdkEvent *event;
-  GdkEventType type = GDK_NOTHING;
-  guint state = 0, button = (guint) - 1;
-
-  event = gtk_get_current_event ();
-  if (event != NULL) {
-    type = event->type;
-
-    if (type == GDK_KEY_PRESS ||
-        type == GDK_KEY_RELEASE) {
-      state = event->key.state;
-      if (keyval)
-        *keyval = event->key.keyval;
-    } else if (type == GDK_BUTTON_PRESS ||
-               type == GDK_BUTTON_RELEASE ||
-               type == GDK_2BUTTON_PRESS ||
-               type == GDK_3BUTTON_PRESS) {
-      button = event->button.button;
-      state = event->button.state;
-    }
-
-    gdk_event_free (event);
-  }
-
-  if (otype)
-    *otype = type;
-  if (ostate)
-    *ostate = state & gtk_accelerator_get_default_mod_mask ();
-  if (obutton)
-    *obutton = button;
-}
diff --git a/lib/ephy-gui.h b/lib/ephy-gui.h
index 4092f0d64..46c397d2e 100644
--- a/lib/ephy-gui.h
+++ b/lib/ephy-gui.h
@@ -26,11 +26,6 @@ G_BEGIN_DECLS
 
 GtkWindowGroup *ephy_gui_ensure_window_group             (GtkWindow *window);
 
-void            ephy_gui_get_current_event               (GdkEventType *type,
-                                                          guint *state,
-                                                          guint *button,
-                                                          guint *keyval);
-
 void            ephy_gui_help                            (GtkWidget *parent,
                                                           const char *page);
 
diff --git a/lib/ephy-notification-container.c b/lib/ephy-notification-container.c
index f0f31e736..8a02322f2 100644
--- a/lib/ephy-notification-container.c
+++ b/lib/ephy-notification-container.c
@@ -23,13 +23,13 @@
 #include "ephy-notification-container.h"
 
 struct _EphyNotificationContainer {
-  GtkBin parent_instance;
+  AdwBin parent_instance;
 
   GtkWidget *revealer;
   GtkWidget *box;
 };
 
-G_DEFINE_TYPE (EphyNotificationContainer, ephy_notification_container, GTK_TYPE_BIN);
+G_DEFINE_TYPE (EphyNotificationContainer, ephy_notification_container, ADW_TYPE_BIN);
 
 static EphyNotificationContainer *notification_container = NULL;
 
@@ -46,10 +46,12 @@ ephy_notification_container_init (EphyNotificationContainer *self)
   gtk_widget_set_valign (GTK_WIDGET (self), GTK_ALIGN_START);
 
   self->revealer = gtk_revealer_new ();
-  gtk_container_add (GTK_CONTAINER (self), self->revealer);
+  adw_bin_set_child (ADW_BIN (self), self->revealer);
 
   self->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
-  gtk_container_add (GTK_CONTAINER (self->revealer), self->box);
+  gtk_revealer_set_child (GTK_REVEALER (self->revealer), self->box);
+
+  gtk_widget_hide (GTK_WIDGET (self));
 }
 
 static void
@@ -67,28 +69,13 @@ ephy_notification_container_get_default (void)
                        NULL);
 }
 
-static guint
-get_num_children (EphyNotificationContainer *self)
-{
-  GList *children;
-  guint retval;
-
-  g_assert (EPHY_IS_NOTIFICATION_CONTAINER (self));
-
-  children = gtk_container_get_children (GTK_CONTAINER (self->box));
-  retval = g_list_length (children);
-  g_list_free (children);
-
-  return retval;
-}
-
 static void
 notification_close_cb (EphyNotification          *notification,
                        EphyNotificationContainer *self)
 {
-  gtk_container_remove (GTK_CONTAINER (self->box), GTK_WIDGET (notification));
+  gtk_box_remove (GTK_BOX (self->box), GTK_WIDGET (notification));
 
-  if (get_num_children (self) == 0) {
+  if (!gtk_widget_get_first_child (self->box)) {
     gtk_widget_hide (GTK_WIDGET (self));
     gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), FALSE);
   }
@@ -98,24 +85,24 @@ void
 ephy_notification_container_add_notification (EphyNotificationContainer *self,
                                               GtkWidget                 *notification)
 {
-  g_autoptr (GList) children = NULL;
-  GList *list;
+  GtkWidget *child;
 
   g_assert (EPHY_IS_NOTIFICATION_CONTAINER (self));
   g_assert (GTK_IS_WIDGET (notification));
 
-  children = gtk_container_get_children (GTK_CONTAINER (self->box));
-  for (list = children; list && list->data; list = list->next) {
-    EphyNotification *child_notification = EPHY_NOTIFICATION (children->data);
+  for (child = gtk_widget_get_first_child (self->box);
+       child;
+       child = gtk_widget_get_next_sibling (child)) {
+    EphyNotification *child_notification = EPHY_NOTIFICATION (child);
 
     if (ephy_notification_is_duplicate (child_notification, EPHY_NOTIFICATION (notification))) {
-      gtk_widget_destroy (notification);
+      gtk_box_remove (GTK_BOX (self->box), notification);
       return;
     }
   }
 
-  gtk_container_add (GTK_CONTAINER (self->box), notification);
-  gtk_widget_show_all (GTK_WIDGET (self));
+  gtk_box_append (GTK_BOX (self->box), notification);
+  gtk_widget_show (GTK_WIDGET (self));
   gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), TRUE);
 
   g_signal_connect (notification, "close", G_CALLBACK (notification_close_cb), self);
diff --git a/lib/ephy-notification-container.h b/lib/ephy-notification-container.h
index 4444f83b7..df5fcdb36 100644
--- a/lib/ephy-notification-container.h
+++ b/lib/ephy-notification-container.h
@@ -20,13 +20,13 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_NOTIFICATION_CONTAINER (ephy_notification_container_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyNotificationContainer, ephy_notification_container, EPHY, NOTIFICATION_CONTAINER, 
GtkBin)
+G_DECLARE_FINAL_TYPE (EphyNotificationContainer, ephy_notification_container, EPHY, NOTIFICATION_CONTAINER, 
AdwBin)
 
 EphyNotificationContainer *ephy_notification_container_get_default      (void);
 
diff --git a/lib/ephy-notification.c b/lib/ephy-notification.c
index 43746bab3..b7d68f7ba 100644
--- a/lib/ephy-notification.c
+++ b/lib/ephy-notification.c
@@ -24,7 +24,7 @@
 #include "ephy-notification-container.h"
 
 struct _EphyNotification {
-  GtkBin parent_instance;
+  AdwBin parent_instance;
 
   GtkWidget *grid;
 
@@ -49,7 +49,7 @@ enum {
 
 static guint signals[LAST_SIGNAL];
 
-G_DEFINE_TYPE (EphyNotification, ephy_notification, GTK_TYPE_BIN);
+G_DEFINE_TYPE (EphyNotification, ephy_notification, ADW_TYPE_BIN);
 
 static void
 ephy_notification_constructed (GObject *object)
@@ -125,27 +125,24 @@ close_button_clicked_cb (GtkButton        *button,
 static void
 ephy_notification_init (EphyNotification *self)
 {
-  GtkWidget *image;
-  GtkStyleContext *context;
+  gtk_widget_add_css_class (GTK_WIDGET (self), "app-notification");
 
   self->grid = gtk_grid_new ();
-  context = gtk_widget_get_style_context (self->grid);
-  gtk_style_context_add_class (context, "app-notification");
-  gtk_container_add (GTK_CONTAINER (self), self->grid);
+  adw_bin_set_child (ADW_BIN (self), self->grid);
 
   self->head = gtk_label_new (NULL);
-  gtk_label_set_line_wrap (GTK_LABEL (self->head), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (self->head), TRUE);
   gtk_label_set_xalign (GTK_LABEL (self->head), 0);
   gtk_widget_set_hexpand (self->head, TRUE);
   gtk_grid_attach (GTK_GRID (self->grid), self->head, 0, 0, 1, 1);
 
   self->body = gtk_label_new (NULL);
-  gtk_label_set_line_wrap (GTK_LABEL (self->body), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (self->body), TRUE);
   gtk_label_set_xalign (GTK_LABEL (self->body), 0);
   gtk_widget_set_hexpand (self->body, TRUE);
   gtk_grid_attach (GTK_GRID (self->grid), self->body, 0, 1, 1, 1);
 
-  self->close_button = gtk_button_new ();
+  self->close_button = gtk_button_new_from_icon_name ("window-close-symbolic");
   gtk_widget_set_focus_on_click (self->close_button, FALSE);
   gtk_widget_set_margin_top (self->close_button, 6);
   gtk_widget_set_margin_bottom (self->close_button, 6);
@@ -154,9 +151,6 @@ ephy_notification_init (EphyNotification *self)
   gtk_style_context_add_class (gtk_widget_get_style_context (self->close_button), "flat");
   gtk_grid_attach (GTK_GRID (self->grid), self->close_button, 1, 0, 1, 2);
 
-  image = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_BUTTON);
-  gtk_button_set_image (GTK_BUTTON (self->close_button), image);
-
   g_signal_connect (self->close_button,
                     "clicked",
                     G_CALLBACK (close_button_clicked_cb),
diff --git a/lib/ephy-notification.h b/lib/ephy-notification.h
index d3226cb68..5947cbcad 100644
--- a/lib/ephy-notification.h
+++ b/lib/ephy-notification.h
@@ -21,13 +21,13 @@
 #pragma once
 
 #include <glib-object.h>
-#include <gtk/gtk.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_NOTIFICATION (ephy_notification_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyNotification, ephy_notification, EPHY, NOTIFICATION, GtkBin)
+G_DECLARE_FINAL_TYPE (EphyNotification, ephy_notification, EPHY, NOTIFICATION, AdwBin)
 
 EphyNotification *ephy_notification_new  (const char *head,
                                           const char *body);
diff --git a/lib/ephy-suggestion.c b/lib/ephy-suggestion.c
index c647688ad..08b14da8f 100644
--- a/lib/ephy-suggestion.c
+++ b/lib/ephy-suggestion.c
@@ -22,7 +22,6 @@
 
 #include "ephy-uri-helpers.h"
 
-#include <dazzle.h>
 #include <glib.h>
 
 struct _EphySuggestion {
diff --git a/lib/ephy-suggestion.h b/lib/ephy-suggestion.h
index bfd270c7c..af6b1e23c 100644
--- a/lib/ephy-suggestion.h
+++ b/lib/ephy-suggestion.h
@@ -19,7 +19,7 @@
 
 #pragma once
 
-#include <dazzle.h>
+#include "dzl-suggestion.h"
 
 G_BEGIN_DECLS
 
diff --git a/lib/ephy-web-app-utils.c b/lib/ephy-web-app-utils.c
index 756d57320..e26d5bbe6 100644
--- a/lib/ephy-web-app-utils.c
+++ b/lib/ephy-web-app-utils.c
@@ -34,7 +34,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
-#include <libportal-gtk3/portal-gtk3.h>
+#include <libportal-gtk4/portal-gtk4.h>
 #include <glib/gi18n.h>
 #include <gio/gunixoutputstream.h>
 
diff --git a/lib/meson.build b/lib/meson.build
index a8d774757..685ad527f 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -10,9 +10,10 @@ enums = gnome.mkenums_simple('ephy-lib-type-builtins',
 )
 
 libephymisc_sources = [
+  'contrib/dzl-fuzzy-mutable-index.c',
+  'contrib/dzl-suggestion.c',
   'contrib/gnome-languages.c',
   'ephy-debug.c',
-  'ephy-dnd.c',
   'ephy-favicon-helpers.c',
   'ephy-file-helpers.c',
   'ephy-flatpak-utils.c',
@@ -61,7 +62,7 @@ libephymisc_deps = [
   gsettings_desktop_schemas,
   gtk_dep,
   json_glib_dep,
-  libdazzle_dep,
+  libadwaita_dep,
   libsecret_dep,
   libsoup_dep,
   libxml_dep,
diff --git a/lib/widgets/contrib/nautilus-floating-bar.c b/lib/widgets/contrib/nautilus-floating-bar.c
index fc727df08..3f9b75c95 100644
--- a/lib/widgets/contrib/nautilus-floating-bar.c
+++ b/lib/widgets/contrib/nautilus-floating-bar.c
@@ -32,8 +32,6 @@ struct _NautilusFloatingBar {
 
   gchar *primary_label;
   GtkWidget *primary_label_widget;
-
-  guint hover_timeout_id;
 };
 
 enum {
@@ -46,225 +44,11 @@ static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
 G_DEFINE_TYPE (NautilusFloatingBar, nautilus_floating_bar,
                GTK_TYPE_BOX);
 
-static void
-nautilus_floating_bar_remove_hover_timeout (NautilusFloatingBar *self)
-{
-  if (self->hover_timeout_id != 0) {
-    g_source_remove (self->hover_timeout_id);
-    self->hover_timeout_id = 0;
-  }
-}
-
-typedef struct {
-  GtkWidget *overlay;
-  GtkWidget *floating_bar;
-  GdkDevice *device;
-  gint y_down_limit;
-  gint y_upper_limit;
-} CheckPointerData;
-
-static void
-check_pointer_data_free (gpointer data)
-{
-  g_slice_free (CheckPointerData, data);
-}
-
-static gboolean
-check_pointer_timeout (gpointer user_data)
-{
-  CheckPointerData *data = user_data;
-  gint pointer_y = -1;
-
-  gdk_window_get_device_position (gtk_widget_get_window (data->overlay), data->device,
-                                  NULL, &pointer_y, NULL);
-
-  if (pointer_y == -1 || pointer_y < data->y_down_limit || pointer_y > data->y_upper_limit) {
-    gtk_widget_show (data->floating_bar);
-    NAUTILUS_FLOATING_BAR (data->floating_bar)->hover_timeout_id = 0;
-    return G_SOURCE_REMOVE;
-  } else {
-    gtk_widget_hide (data->floating_bar);
-  }
-
-  return G_SOURCE_CONTINUE;
-}
-
-static gboolean
-overlay_enter_notify_cb (GtkWidget        *parent,
-                         GdkEventCrossing *event,
-                         gpointer          user_data)
-{
-  GtkWidget *widget = user_data;
-  CheckPointerData *data;
-  gint y_pos;
-
-  NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (widget);
-
-  if (self->hover_timeout_id != 0) {
-    g_source_remove (self->hover_timeout_id);
-  }
-
-  if (event->window != gtk_widget_get_window (widget)) {
-    return GDK_EVENT_PROPAGATE;
-  }
-
-  gdk_window_get_position (gtk_widget_get_window (widget), NULL, &y_pos);
-
-  data = g_slice_new (CheckPointerData);
-  data->overlay = parent;
-  data->floating_bar = widget;
-  data->device = gdk_event_get_device ((GdkEvent *)event);
-  data->y_down_limit = y_pos;
-  data->y_upper_limit = y_pos + gtk_widget_get_allocated_height (widget);
-
-  self->hover_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, HOVER_HIDE_TIMEOUT_INTERVAL,
-                                               check_pointer_timeout, data,
-                                               check_pointer_data_free);
-
-  g_source_set_name_by_id (self->hover_timeout_id, "[nautilus-floating-bar] overlay_enter_notify_cb");
-
-  return GDK_EVENT_STOP;
-}
-
-static void
-nautilus_floating_bar_parent_set (GtkWidget *widget,
-                                  GtkWidget *old_parent)
-{
-  GtkWidget *parent;
-
-  parent = gtk_widget_get_parent (widget);
-
-  if (old_parent != NULL) {
-    g_signal_handlers_disconnect_by_func (old_parent,
-                                          overlay_enter_notify_cb, widget);
-  }
-
-  if (parent != NULL) {
-    g_signal_connect (parent, "enter-notify-event",
-                      G_CALLBACK (overlay_enter_notify_cb), widget);
-  }
-}
-
-static void
-get_padding_and_border (GtkWidget *widget,
-                        GtkBorder *border)
-{
-  GtkStyleContext *context;
-  GtkStateFlags state;
-  GtkBorder tmp;
-
-  context = gtk_widget_get_style_context (widget);
-  state = gtk_widget_get_state_flags (widget);
-
-  gtk_style_context_get_padding (context, state, border);
-  gtk_style_context_get_border (context, state, &tmp);
-  border->top += tmp.top;
-  border->right += tmp.right;
-  border->bottom += tmp.bottom;
-  border->left += tmp.left;
-}
-
-static void
-nautilus_floating_bar_get_preferred_width (GtkWidget *widget,
-                                           gint      *minimum_size,
-                                           gint      *natural_size)
-{
-  GtkBorder border;
-
-  get_padding_and_border (widget, &border);
-
-  GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->get_preferred_width (widget,
-                                                                              minimum_size,
-                                                                              natural_size);
-
-  *minimum_size += border.left + border.right;
-  *natural_size += border.left + border.right;
-}
-
-static void
-nautilus_floating_bar_get_preferred_width_for_height (GtkWidget *widget,
-                                                      gint       height,
-                                                      gint      *minimum_size,
-                                                      gint      *natural_size)
-{
-  GtkBorder border;
-
-  get_padding_and_border (widget, &border);
-
-  GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->get_preferred_width_for_height (widget,
-                                                                                         height,
-                                                                                         minimum_size,
-                                                                                         natural_size);
-
-  *minimum_size += border.left + border.right;
-  *natural_size += border.left + border.right;
-}
-
-static void
-nautilus_floating_bar_get_preferred_height (GtkWidget *widget,
-                                            gint      *minimum_size,
-                                            gint      *natural_size)
-{
-  GtkBorder border;
-
-  get_padding_and_border (widget, &border);
-
-  GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->get_preferred_height (widget,
-                                                                               minimum_size,
-                                                                               natural_size);
-
-  *minimum_size += border.top + border.bottom;
-  *natural_size += border.top + border.bottom;
-}
-
-static void
-nautilus_floating_bar_get_preferred_height_for_width (GtkWidget *widget,
-                                                      gint       width,
-                                                      gint      *minimum_size,
-                                                      gint      *natural_size)
-{
-  GtkBorder border;
-
-  get_padding_and_border (widget, &border);
-
-  GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->get_preferred_height_for_width (widget,
-                                                                                         width,
-                                                                                         minimum_size,
-                                                                                         natural_size);
-
-  *minimum_size += border.top + border.bottom;
-  *natural_size += border.top + border.bottom;
-}
-
-static void
-nautilus_floating_bar_constructed (GObject *obj)
-{
-  NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (obj);
-  GtkWidget *w;
-
-  G_OBJECT_CLASS (nautilus_floating_bar_parent_class)->constructed (obj);
-
-  w = gtk_label_new (NULL);
-  gtk_label_set_ellipsize (GTK_LABEL (w), PANGO_ELLIPSIZE_MIDDLE);
-  gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE);
-  gtk_container_add (GTK_CONTAINER (self), w);
-  self->primary_label_widget = w;
-  gtk_widget_show (w);
-
-  g_object_set (w,
-                "margin-top", 2,
-                "margin-bottom", 2,
-                "margin-start", 12,
-                "margin-end", 12,
-                NULL);
-}
-
 static void
 nautilus_floating_bar_finalize (GObject *obj)
 {
   NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (obj);
 
-  nautilus_floating_bar_remove_hover_timeout (self);
   g_free (self->primary_label);
 
   G_OBJECT_CLASS (nautilus_floating_bar_parent_class)->finalize (obj);
@@ -309,29 +93,30 @@ nautilus_floating_bar_set_property (GObject      *object,
 static void
 nautilus_floating_bar_init (NautilusFloatingBar *self)
 {
-  GtkStyleContext *context;
+  GtkWidget *w = gtk_label_new (NULL);
+  gtk_label_set_ellipsize (GTK_LABEL (w), PANGO_ELLIPSIZE_MIDDLE);
+  gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE);
+  gtk_box_append (GTK_BOX (self), w);
+  self->primary_label_widget = w;
 
-  context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  gtk_style_context_add_class (context, "floating-bar");
+  gtk_widget_set_margin_top (w, 2);
+  gtk_widget_set_margin_bottom (w, 2);
+  gtk_widget_set_margin_start (w, 12);
+  gtk_widget_set_margin_end (w, 12);
+
+  gtk_widget_set_can_target (GTK_WIDGET (self), FALSE);
+  gtk_widget_add_css_class (GTK_WIDGET (self), "floating-bar");
 }
 
 static void
 nautilus_floating_bar_class_init (NautilusFloatingBarClass *klass)
 {
   GObjectClass *oclass = G_OBJECT_CLASS (klass);
-  GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
 
-  oclass->constructed = nautilus_floating_bar_constructed;
   oclass->set_property = nautilus_floating_bar_set_property;
   oclass->get_property = nautilus_floating_bar_get_property;
   oclass->finalize = nautilus_floating_bar_finalize;
 
-  wclass->get_preferred_width = nautilus_floating_bar_get_preferred_width;
-  wclass->get_preferred_width_for_height = nautilus_floating_bar_get_preferred_width_for_height;
-  wclass->get_preferred_height = nautilus_floating_bar_get_preferred_height;
-  wclass->get_preferred_height_for_width = nautilus_floating_bar_get_preferred_height_for_width;
-  wclass->parent_set = nautilus_floating_bar_parent_set;
-
   properties[PROP_PRIMARY_LABEL] =
     g_param_spec_string ("primary-label",
                          "Bar's primary label",
diff --git a/lib/widgets/ephy-certificate-dialog.c b/lib/widgets/ephy-certificate-dialog.c
index 30a1f2373..b07c0e4bd 100644
--- a/lib/widgets/ephy-certificate-dialog.c
+++ b/lib/widgets/ephy-certificate-dialog.c
@@ -24,7 +24,7 @@
 #include "ephy-lib-type-builtins.h"
 
 #define GCR_API_SUBJECT_TO_CHANGE
-#include <gcr/gcr.h>
+//#include <gcr/gcr.h>
 #include <glib/gi18n.h>
 
 /**
@@ -72,6 +72,7 @@ static void
 ephy_certificate_dialog_set_certificate (EphyCertificateDialog *dialog,
                                          GTlsCertificate       *certificate)
 {
+/*
   GcrCertificate *simple_certificate;
   GByteArray *certificate_data;
   GtkWidget *certificate_widget;
@@ -87,8 +88,8 @@ ephy_certificate_dialog_set_certificate (EphyCertificateDialog *dialog,
   g_object_unref (simple_certificate);
 
   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
-  gtk_box_pack_start (GTK_BOX (content_area), certificate_widget, FALSE, TRUE, 0);
-  gtk_widget_show (certificate_widget);
+  gtk_box_append (GTK_BOX (content_area), certificate_widget);
+*/
 }
 
 static char *
@@ -152,7 +153,7 @@ ephy_certificate_dialog_constructed (GObject *object)
   icon_name = ephy_security_level_to_icon_name (dialog->security_level);
   if (icon_name) {
     icon = g_themed_icon_new_with_default_fallbacks (icon_name);
-    gtk_image_set_from_gicon (GTK_IMAGE (dialog->icon), icon, GTK_ICON_SIZE_DIALOG);
+    gtk_image_set_from_gicon (GTK_IMAGE (dialog->icon), icon);
     g_object_unref (icon);
   }
 
@@ -185,7 +186,6 @@ ephy_certificate_dialog_constructed (GObject *object)
         g_assert_not_reached ();
     }
   }
-  gtk_widget_show (dialog->text);
 }
 
 static void
@@ -283,29 +283,26 @@ ephy_certificate_dialog_init (EphyCertificateDialog *dialog)
 
   gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 500);
 
-  gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);
-
   grid = gtk_grid_new ();
   gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
   gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
 
   dialog->icon = gtk_image_new ();
+  gtk_image_set_pixel_size (GTK_IMAGE (dialog->icon), 48);
   gtk_grid_attach (GTK_GRID (grid), dialog->icon,
                    0, 0, 1, 2);
-  gtk_widget_show (dialog->icon);
 
   dialog->title = gtk_label_new (NULL);
   gtk_label_set_use_markup (GTK_LABEL (dialog->title), TRUE);
-  gtk_label_set_line_wrap (GTK_LABEL (dialog->title), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (dialog->title), TRUE);
   gtk_label_set_selectable (GTK_LABEL (dialog->title), TRUE);
   gtk_label_set_xalign (GTK_LABEL (dialog->title), 0.0);
   gtk_grid_attach_next_to (GTK_GRID (grid), dialog->title,
                            dialog->icon, GTK_POS_RIGHT,
                            1, 1);
-  gtk_widget_show (dialog->title);
 
   dialog->text = gtk_label_new (NULL);
-  gtk_label_set_line_wrap (GTK_LABEL (dialog->text), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (dialog->text), TRUE);
   gtk_label_set_selectable (GTK_LABEL (dialog->text), TRUE);
   gtk_label_set_xalign (GTK_LABEL (dialog->text), 0.0);
   gtk_label_set_yalign (GTK_LABEL (dialog->text), 0.0);
@@ -315,12 +312,11 @@ ephy_certificate_dialog_init (EphyCertificateDialog *dialog)
 
   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
   gtk_box_set_spacing (GTK_BOX (content_area), 14);
-  gtk_widget_set_margin_top (content_area, 10);
-  gtk_widget_set_margin_bottom (content_area, 10);
-  gtk_widget_set_margin_start (content_area, 10);
-  gtk_widget_set_margin_end (content_area, 10);
-  gtk_box_pack_start (GTK_BOX (content_area), grid, FALSE, TRUE, 0);
-  gtk_widget_show (grid);
+  gtk_widget_set_margin_top (content_area, 12);
+  gtk_widget_set_margin_bottom (content_area, 12);
+  gtk_widget_set_margin_start (content_area, 12);
+  gtk_widget_set_margin_end (content_area, 12);
+  gtk_box_append (GTK_BOX (content_area), grid);
 }
 
 GtkWidget *
diff --git a/lib/widgets/ephy-download-widget.c b/lib/widgets/ephy-download-widget.c
index 113996794..a124d0821 100644
--- a/lib/widgets/ephy-download-widget.c
+++ b/lib/widgets/ephy-download-widget.c
@@ -30,7 +30,7 @@
 #include <webkit2/webkit2.h>
 
 struct _EphyDownloadWidget {
-  GtkEventBox parent_instance;
+  AdwBin parent_instance;
 
   EphyDownload *download;
 
@@ -41,7 +41,7 @@ struct _EphyDownloadWidget {
   GtkWidget *action_button;
 };
 
-G_DEFINE_TYPE (EphyDownloadWidget, ephy_download_widget, GTK_TYPE_EVENT_BOX)
+G_DEFINE_TYPE (EphyDownloadWidget, ephy_download_widget, ADW_TYPE_BIN)
 
 enum {
   PROP_0,
@@ -135,7 +135,7 @@ update_download_icon (EphyDownloadWidget *widget)
   } else
     icon = g_icon_new_for_string ("package-x-generic-symbolic", NULL);
 
-  gtk_image_set_from_gicon (GTK_IMAGE (widget->icon), icon, GTK_ICON_SIZE_MENU);
+  gtk_image_set_from_gicon (GTK_IMAGE (widget->icon), icon);
 }
 
 static void
@@ -211,9 +211,8 @@ download_finished_cb (EphyDownload       *download,
 {
   gtk_widget_hide (widget->progress);
   update_status_label (widget, _("Finished"));
-  gtk_image_set_from_icon_name (GTK_IMAGE (gtk_button_get_image (GTK_BUTTON (widget->action_button))),
-                                "folder-open-symbolic",
-                                GTK_ICON_SIZE_MENU);
+  gtk_button_set_icon_name (GTK_BUTTON (widget->action_button),
+                            "folder-open-symbolic");
 }
 
 static void
@@ -237,9 +236,8 @@ download_failed_cb (EphyDownload       *download,
 
   error_msg = g_strdup_printf (_("Error downloading: %s"), error->message);
   update_status_label (widget, error_msg);
-  gtk_image_set_from_icon_name (GTK_IMAGE (gtk_button_get_image (GTK_BUTTON (widget->action_button))),
-                                "list-remove-symbolic",
-                                GTK_ICON_SIZE_MENU);
+  gtk_button_set_icon_name (GTK_BUTTON (widget->action_button),
+                            "list-remove-symbolic");
 }
 
 static void
@@ -284,22 +282,13 @@ download_destination_changed_cb (WebKitDownload     *download,
   update_download_destination (widget);
 }
 
-static void
-download_drag_data_get (GtkWidget        *widget,
-                        GdkDragContext   *context,
-                        GtkSelectionData *selection_data,
-                        guint             info,
-                        guint             time,
-                        gpointer          data)
+static GdkContentProvider *
+download_drag_prepare (WebKitDownload *download)
 {
-  WebKitDownload *download = WEBKIT_DOWNLOAD (data);
-  gchar *uris[2];
-
-  uris[0] = g_strdup (webkit_download_get_destination (download));
-  uris[1] = NULL;
+  const char *uri = webkit_download_get_destination (download);
+  GFile *file = g_file_new_for_uri (uri);
 
-  gtk_selection_data_set_uris (selection_data, uris);
-  g_free (uris[0]);
+  return gdk_content_provider_new_typed (G_TYPE_FILE, file);
 }
 
 static void
@@ -373,16 +362,13 @@ ephy_download_widget_constructed (GObject *object)
   GError *error = NULL;
   PangoAttrList *status_attrs;
   GtkWidget *grid;
+  GtkDragSource *source;
 
   G_OBJECT_CLASS (ephy_download_widget_parent_class)->constructed (object);
 
   grid = gtk_grid_new ();
   gtk_widget_show (grid);
-  gtk_widget_set_margin_start (GTK_WIDGET (grid), 12);
-  gtk_widget_set_margin_end (GTK_WIDGET (grid), 12);
-  gtk_widget_set_margin_top (GTK_WIDGET (grid), 12);
-  gtk_widget_set_margin_bottom (GTK_WIDGET (grid), 12);
-  gtk_container_add (GTK_CONTAINER (widget), grid);
+  adw_bin_set_child (ADW_BIN (widget), grid);
 
   widget->icon = gtk_image_new ();
   gtk_widget_set_margin_end (widget->icon, 4);
@@ -436,7 +422,7 @@ ephy_download_widget_constructed (GObject *object)
     action_icon_name = "list-remove-symbolic";
   else
     action_icon_name = "window-close-symbolic";
-  widget->action_button = gtk_button_new_from_icon_name (action_icon_name, GTK_ICON_SIZE_MENU);
+  widget->action_button = gtk_button_new_from_icon_name (action_icon_name);
   g_signal_connect_swapped (widget->action_button, "clicked",
                             G_CALLBACK (widget_action_button_clicked_cb),
                             widget);
@@ -445,7 +431,6 @@ ephy_download_widget_constructed (GObject *object)
   gtk_style_context_add_class (gtk_widget_get_style_context (widget->action_button),
                                "circular");
   gtk_grid_attach (GTK_GRID (grid), widget->action_button, 3, 0, 1, 3);
-  gtk_widget_show (widget->action_button);
 
   download = ephy_download_get_webkit_download (widget->download);
   g_signal_connect (download, "notify::estimated-progress",
@@ -467,9 +452,11 @@ ephy_download_widget_constructed (GObject *object)
                     G_CALLBACK (download_content_type_changed_cb),
                     widget);
 
-  gtk_drag_source_set (GTK_WIDGET (widget), GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY);
-  gtk_drag_source_add_uri_targets (GTK_WIDGET (widget));
-  g_signal_connect_object (widget, "drag-data-get", G_CALLBACK (download_drag_data_get), download, 0);
+  source = gtk_drag_source_new ();
+  gtk_drag_source_set_actions (source, GDK_ACTION_COPY);
+  g_signal_connect_swapped (source, "prepare", G_CALLBACK (download_drag_prepare), download);
+
+  gtk_widget_add_controller (GTK_WIDGET (widget), GTK_EVENT_CONTROLLER (source));
 }
 
 static void
diff --git a/lib/widgets/ephy-download-widget.h b/lib/widgets/ephy-download-widget.h
index f36bd053a..72f5369b1 100644
--- a/lib/widgets/ephy-download-widget.h
+++ b/lib/widgets/ephy-download-widget.h
@@ -20,14 +20,14 @@
 
 #pragma once
 
-#include <glib-object.h>
+#include <adwaita.h>
 #include "ephy-download.h"
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_DOWNLOAD_WIDGET ephy_download_widget_get_type()
 
-G_DECLARE_FINAL_TYPE (EphyDownloadWidget, ephy_download_widget, EPHY, DOWNLOAD_WIDGET, GtkEventBox)
+G_DECLARE_FINAL_TYPE (EphyDownloadWidget, ephy_download_widget, EPHY, DOWNLOAD_WIDGET, AdwBin)
 
 GtkWidget     *ephy_download_widget_new          (EphyDownload *ephy_download);
 
diff --git a/lib/widgets/ephy-downloads-paintable.c b/lib/widgets/ephy-downloads-paintable.c
new file mode 100644
index 000000000..956eaf83b
--- /dev/null
+++ b/lib/widgets/ephy-downloads-paintable.c
@@ -0,0 +1,329 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2022 Purism SPC
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-downloads-paintable.h"
+
+#include <adwaita.h>
+
+#define SIZE 16
+
+struct _EphyDownloadsPaintable {
+  GObject parent_instance;
+
+  GtkWidget *widget;
+
+  double progress;
+
+  GtkIconPaintable *arrow_paintable;
+  GtkIconPaintable *check_paintable;
+
+  double check_progress;
+  AdwAnimation *done_animation;
+  guint timeout_id;
+};
+
+static void ephy_downloads_paintable_paintable_init (GdkPaintableInterface *iface);
+static void ephy_downloads_paintable_symbolic_paintable_init (GtkSymbolicPaintableInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EphyDownloadsPaintable, ephy_downloads_paintable, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
+                                                ephy_downloads_paintable_paintable_init)
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_SYMBOLIC_PAINTABLE,
+                                                ephy_downloads_paintable_symbolic_paintable_init))
+
+enum {
+  PROP_0,
+  PROP_WIDGET,
+  PROP_PROGRESS,
+  LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP];
+
+static void
+cache_icons (EphyDownloadsPaintable *self)
+{
+  GdkDisplay *display = gtk_widget_get_display (self->widget);
+  GtkIconTheme *theme = gtk_icon_theme_get_for_display (display);
+  int scale = gtk_widget_get_scale_factor (self->widget);
+  GtkTextDirection direction = gtk_widget_get_direction (self->widget);
+
+  g_set_object (&self->arrow_paintable,
+                gtk_icon_theme_lookup_icon (theme, "ephy-download-symbolic",
+                                            NULL, SIZE, scale, direction,
+                                            GTK_ICON_LOOKUP_FORCE_SYMBOLIC));
+  g_set_object (&self->check_paintable,
+                gtk_icon_theme_lookup_icon (theme, "ephy-download-done-symbolic",
+                                            NULL, SIZE, scale, direction,
+                                            GTK_ICON_LOOKUP_FORCE_SYMBOLIC));
+}
+
+static void
+scale_factor_changed_cb (EphyDownloadsPaintable *self)
+{
+  cache_icons (self);
+  gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
+}
+
+static void
+ephy_downloads_paintable_constructed (GObject *object)
+{
+  EphyDownloadsPaintable *self = EPHY_DOWNLOADS_PAINTABLE (object);
+
+  g_signal_connect_swapped (self->widget, "notify::scale-factor",
+                            G_CALLBACK (scale_factor_changed_cb), self);
+
+  cache_icons (self);
+
+  G_OBJECT_CLASS (ephy_downloads_paintable_parent_class)->constructed (object);
+}
+
+static void
+ephy_downloads_paintable_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  EphyDownloadsPaintable *self = EPHY_DOWNLOADS_PAINTABLE (object);
+
+  switch (prop_id) {
+    case PROP_WIDGET:
+      g_value_set_object (value, self->widget);
+      break;
+    case PROP_PROGRESS:
+      g_value_set_double (value, self->progress);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_downloads_paintable_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  EphyDownloadsPaintable *self = EPHY_DOWNLOADS_PAINTABLE (object);
+
+  switch (prop_id) {
+    case PROP_WIDGET:
+      g_set_object (&self->widget, g_value_get_object (value));
+      break;
+    case PROP_PROGRESS:
+      self->progress = g_value_get_double (value);
+      gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_downloads_paintable_dispose (GObject *object)
+{
+  EphyDownloadsPaintable *self = EPHY_DOWNLOADS_PAINTABLE (object);
+
+  g_clear_object (&self->widget);
+  g_clear_object (&self->arrow_paintable);
+  g_clear_object (&self->check_paintable);
+  g_clear_object (&self->done_animation);
+  g_clear_handle_id (&self->timeout_id, g_source_remove);
+
+  G_OBJECT_CLASS (ephy_downloads_paintable_parent_class)->dispose (object);
+}
+
+static void
+ephy_downloads_paintable_class_init (EphyDownloadsPaintableClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = ephy_downloads_paintable_constructed;
+  object_class->get_property = ephy_downloads_paintable_get_property;
+  object_class->set_property = ephy_downloads_paintable_set_property;
+  object_class->dispose = ephy_downloads_paintable_dispose;
+
+  properties[PROP_WIDGET] =
+    g_param_spec_object ("widget",
+                         "Widget",
+                         "The widget to take scale factor and frame clock from",
+                         GTK_TYPE_WIDGET,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_PROGRESS] =
+    g_param_spec_double ("progress",
+                         "Progress",
+                         "Progress value",
+                         0, 1, 0,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+ephy_downloads_paintable_init (EphyDownloadsPaintable *self)
+{
+  self->progress = 0;
+  self->check_progress = 0;
+  self->timeout_id = -1;
+}
+
+static int
+ephy_downloads_paintable_get_intrinsic_width (GdkPaintable *paintable)
+{
+  EphyDownloadsPaintable *self = EPHY_DOWNLOADS_PAINTABLE (paintable);
+
+  return SIZE * gtk_widget_get_scale_factor (self->widget);
+}
+
+static int
+ephy_downloads_paintable_get_intrinsic_height (GdkPaintable *paintable)
+{
+  EphyDownloadsPaintable *self = EPHY_DOWNLOADS_PAINTABLE (paintable);
+
+  return SIZE * gtk_widget_get_scale_factor (self->widget);
+}
+
+static void
+ephy_downloads_paintable_paintable_init (GdkPaintableInterface *iface)
+{
+  iface->get_intrinsic_width = ephy_downloads_paintable_get_intrinsic_width;
+  iface->get_intrinsic_height = ephy_downloads_paintable_get_intrinsic_height;
+}
+
+static void
+ephy_downloads_paintable_snapshot_symbolic (GtkSymbolicPaintable *paintable,
+                                            GdkSnapshot          *gdk_snapshot,
+                                            double                width,
+                                            double                height,
+                                            const GdkRGBA        *colors,
+                                            gsize                 n_colors)
+{
+  EphyDownloadsPaintable *self = EPHY_DOWNLOADS_PAINTABLE (paintable);
+  GtkSnapshot *snapshot = GTK_SNAPSHOT (gdk_snapshot);
+  cairo_t *cr;
+  double arc_end;
+  GdkRGBA rgba;
+
+  if (self->check_progress < 1) {
+    gtk_snapshot_save (snapshot);
+    gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (width / 2.0f, height / 2.0f));
+    gtk_snapshot_scale (snapshot, 1.0f - self->check_progress, 1.0f - self->check_progress);
+    gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-width / 2.0f, -height / 2.0f));
+    gtk_symbolic_paintable_snapshot_symbolic (GTK_SYMBOLIC_PAINTABLE (self->arrow_paintable),
+                                              gdk_snapshot, width, height, colors, n_colors);
+    gtk_snapshot_restore (snapshot);
+  }
+
+  if (self->check_progress > 0) {
+    gtk_snapshot_save (snapshot);
+    gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (width / 2.0f, height / 2.0f));
+    gtk_snapshot_scale (snapshot, self->check_progress, self->check_progress);
+    gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-width / 2.0f, -height / 2.0f));
+    gtk_symbolic_paintable_snapshot_symbolic (GTK_SYMBOLIC_PAINTABLE (self->check_paintable),
+                                              gdk_snapshot, width, height, colors, n_colors);
+    gtk_snapshot_restore (snapshot);
+  }
+
+  cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (-2, -2, width + 4, width + 4));
+  arc_end = self->progress * G_PI * 2 - G_PI / 2;
+
+  cairo_translate (cr, width / 2.0, height / 2.0);
+
+  gdk_cairo_set_source_rgba (cr, colors);
+  cairo_arc (cr, 0, 0, width / 2.0 + 1, -G_PI / 2, arc_end);
+  cairo_stroke (cr);
+
+  rgba = *colors;
+  rgba.alpha *= 0.25f;
+  gdk_cairo_set_source_rgba (cr, &rgba);
+  cairo_arc (cr, 0, 0, width / 2.0 + 1, arc_end, 3.0 * G_PI / 2.0);
+  cairo_stroke (cr);
+}
+
+static void
+ephy_downloads_paintable_symbolic_paintable_init (GtkSymbolicPaintableInterface *iface)
+{
+  iface->snapshot_symbolic = ephy_downloads_paintable_snapshot_symbolic;
+}
+
+GdkPaintable *
+ephy_downloads_paintable_new (GtkWidget *widget)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  return GDK_PAINTABLE (g_object_new (EPHY_TYPE_DOWNLOADS_PAINTABLE,
+                                      "widget", widget,
+                                      NULL));
+}
+
+static void
+animate_done_cb (double                  value,
+                 EphyDownloadsPaintable *self)
+{
+  self->check_progress = value;
+  gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
+}
+
+static gboolean
+animation_timeout_cb (EphyDownloadsPaintable *self)
+{
+  adw_animation_play (self->done_animation);
+  self->timeout_id = -1;
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+animation_done_done_cb (EphyDownloadsPaintable *self)
+{
+  if (self->check_progress > 0.5) {
+    int delay = adw_get_enable_animations (self->widget) ? 500 : 1000;
+
+    adw_timed_animation_set_value_from (ADW_TIMED_ANIMATION (self->done_animation), 1);
+    adw_timed_animation_set_value_to (ADW_TIMED_ANIMATION (self->done_animation), 0);
+
+    self->timeout_id = g_timeout_add (delay, G_SOURCE_FUNC (animation_timeout_cb), self);
+  } else {
+    g_clear_object (&self->done_animation);
+  }
+}
+
+void
+ephy_downloads_paintable_animate_done (EphyDownloadsPaintable *self)
+{
+  AdwAnimationTarget *target;
+
+  g_return_if_fail (EPHY_IS_DOWNLOADS_PAINTABLE (self));
+
+  if (self->done_animation)
+    return;
+
+  target = adw_callback_animation_target_new ((AdwAnimationTargetFunc)animate_done_cb, self, NULL);
+  self->done_animation = adw_timed_animation_new (self->widget, 0, 1, 500, target);
+
+  g_signal_connect_swapped (self->done_animation, "done",
+                            G_CALLBACK (animation_done_done_cb), self);
+
+  adw_timed_animation_set_easing (ADW_TIMED_ANIMATION (self->done_animation),
+                                  ADW_EASE_IN_OUT_CUBIC);
+  adw_animation_play (self->done_animation);
+}
diff --git a/lib/widgets/ephy-downloads-paintable.h b/lib/widgets/ephy-downloads-paintable.h
new file mode 100644
index 000000000..d8d1b8744
--- /dev/null
+++ b/lib/widgets/ephy-downloads-paintable.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2022 Purism SPC
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define EPHY_TYPE_DOWNLOADS_PAINTABLE (ephy_downloads_paintable_get_type())
+
+G_DECLARE_FINAL_TYPE (EphyDownloadsPaintable, ephy_downloads_paintable, EPHY, DOWNLOADS_PAINTABLE, GObject)
+
+GdkPaintable *ephy_downloads_paintable_new          (GtkWidget              *widget);
+
+void          ephy_downloads_paintable_animate_done (EphyDownloadsPaintable *self);
+
+G_END_DECLS
diff --git a/lib/widgets/ephy-downloads-popover.c b/lib/widgets/ephy-downloads-popover.c
index 420b1edae..61d4f4b6f 100644
--- a/lib/widgets/ephy-downloads-popover.c
+++ b/lib/widgets/ephy-downloads-popover.c
@@ -45,7 +45,7 @@ download_box_row_activated_cb (EphyDownloadsPopover *popover,
   EphyDownloadWidget *widget;
   EphyDownload *download;
 
-  widget = EPHY_DOWNLOAD_WIDGET (gtk_bin_get_child (GTK_BIN (row)));
+  widget = EPHY_DOWNLOAD_WIDGET (gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row)));
   download = ephy_download_widget_get_download (widget);
   if (!ephy_download_succeeded (download))
     return;
@@ -77,7 +77,6 @@ download_added_cb (EphyDownloadsPopover *popover,
 
   row = gtk_list_box_row_new ();
   gtk_list_box_prepend (GTK_LIST_BOX (popover->downloads_box), row);
-  gtk_widget_show (row);
 
   widget = ephy_download_widget_new (download);
   g_signal_connect_object (download, "completed",
@@ -86,8 +85,7 @@ download_added_cb (EphyDownloadsPopover *popover,
   g_signal_connect_object (download, "error",
                            G_CALLBACK (download_failed_cb),
                            popover, G_CONNECT_SWAPPED);
-  gtk_container_add (GTK_CONTAINER (row), widget);
-  gtk_widget_show (widget);
+  gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), widget);
 }
 
 static void
@@ -104,12 +102,12 @@ download_removed_cb (EphyDownloadsPopover *popover,
     gtk_widget_hide (GTK_WIDGET (popover));
 
   while ((row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (popover->downloads_box), i++))) {
-    GtkWidget *widget = gtk_bin_get_child (GTK_BIN (row));
+    GtkWidget *widget = gtk_list_box_row_get_child (row);
     if (!EPHY_IS_DOWNLOAD_WIDGET (widget))
       continue;
 
     if (ephy_download_widget_get_download (EPHY_DOWNLOAD_WIDGET (widget)) == download) {
-      gtk_container_remove (GTK_CONTAINER (popover->downloads_box), GTK_WIDGET (row));
+      gtk_list_box_remove (GTK_LIST_BOX (popover->downloads_box), GTK_WIDGET (row));
       break;
     }
   }
@@ -133,12 +131,12 @@ clear_button_clicked_cb (EphyDownloadsPopover *popover)
     GtkWidget *widget;
     EphyDownload *download;
 
-    widget = gtk_bin_get_child (GTK_BIN (row));
+    widget = gtk_list_box_row_get_child (row);
     download = ephy_download_widget_get_download (EPHY_DOWNLOAD_WIDGET (widget));
 
     if (!ephy_download_is_active (download)) {
       ephy_downloads_manager_remove_download (manager, download);
-      gtk_container_remove (GTK_CONTAINER (popover->downloads_box), GTK_WIDGET (row));
+      gtk_list_box_remove (GTK_LIST_BOX (popover->downloads_box), GTK_WIDGET (row));
     }
   }
   gtk_widget_set_sensitive (popover->clear_button, FALSE);
@@ -159,9 +157,11 @@ ephy_downloads_popover_init (EphyDownloadsPopover *popover)
   GList *downloads, *l;
   EphyDownloadsManager *manager = ephy_embed_shell_get_downloads_manager (ephy_embed_shell_get_default ());
 
+  gtk_widget_add_css_class (GTK_WIDGET (popover), "menu");
+
   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
 
-  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  scrolled_window = gtk_scrolled_window_new ();
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
   gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (scrolled_window),
@@ -175,8 +175,7 @@ ephy_downloads_popover_init (EphyDownloadsPopover *popover)
   gtk_list_box_set_selection_mode (GTK_LIST_BOX (popover->downloads_box), GTK_SELECTION_NONE);
   gtk_style_context_add_class (gtk_widget_get_style_context (popover->downloads_box),
                                "background");
-  gtk_container_add (GTK_CONTAINER (scrolled_window), popover->downloads_box);
-  gtk_widget_show (popover->downloads_box);
+  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), popover->downloads_box);
 
   downloads = ephy_downloads_manager_get_downloads (manager);
   for (l = downloads; l != NULL; l = g_list_next (l)) {
@@ -193,11 +192,9 @@ ephy_downloads_popover_init (EphyDownloadsPopover *popover)
 
     row = gtk_list_box_row_new ();
     gtk_list_box_prepend (GTK_LIST_BOX (popover->downloads_box), row);
-    gtk_widget_show (row);
 
     widget = ephy_download_widget_new (download);
-    gtk_container_add (GTK_CONTAINER (row), widget);
-    gtk_widget_show (widget);
+    gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), widget);
   }
 
   g_signal_connect_object (manager, "download-added",
@@ -207,8 +204,7 @@ ephy_downloads_popover_init (EphyDownloadsPopover *popover)
                            G_CALLBACK (download_removed_cb),
                            popover, G_CONNECT_SWAPPED);
 
-  gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, FALSE, TRUE, 0);
-  gtk_widget_show (scrolled_window);
+  gtk_box_append (GTK_BOX (vbox), scrolled_window);
 
   popover->clear_button = gtk_button_new_with_mnemonic (_("_Clear All"));
   gtk_widget_set_sensitive (popover->clear_button, !ephy_downloads_manager_has_active_downloads (manager));
@@ -216,19 +212,17 @@ ephy_downloads_popover_init (EphyDownloadsPopover *popover)
                             G_CALLBACK (clear_button_clicked_cb),
                             popover);
   gtk_widget_set_halign (popover->clear_button, GTK_ALIGN_END);
-  gtk_widget_set_margin_start (popover->clear_button, 12);
-  gtk_widget_set_margin_end (popover->clear_button, 12);
-  gtk_widget_set_margin_top (popover->clear_button, 12);
-  gtk_widget_set_margin_bottom (popover->clear_button, 12);
-  gtk_box_pack_start (GTK_BOX (vbox), popover->clear_button, FALSE, TRUE, 0);
-  gtk_widget_show (popover->clear_button);
-
-  gtk_container_add (GTK_CONTAINER (popover), vbox);
-  gtk_widget_show (vbox);
+  gtk_widget_set_margin_start (popover->clear_button, 6);
+  gtk_widget_set_margin_end (popover->clear_button, 6);
+  gtk_widget_set_margin_top (popover->clear_button, 6);
+  gtk_widget_set_margin_bottom (popover->clear_button, 6);
+  gtk_box_append (GTK_BOX (vbox), popover->clear_button);
+
+  gtk_popover_set_child (GTK_POPOVER (popover), vbox);
 }
 
 GtkWidget *
-ephy_downloads_popover_new (GtkWidget *relative_to)
+ephy_downloads_popover_new (void)
 {
-  return GTK_WIDGET (g_object_new (EPHY_TYPE_DOWNLOADS_POPOVER, "relative-to", relative_to, NULL));
+  return GTK_WIDGET (g_object_new (EPHY_TYPE_DOWNLOADS_POPOVER, NULL));
 }
diff --git a/lib/widgets/ephy-downloads-popover.h b/lib/widgets/ephy-downloads-popover.h
index 6c071a49f..2f8f2af61 100644
--- a/lib/widgets/ephy-downloads-popover.h
+++ b/lib/widgets/ephy-downloads-popover.h
@@ -30,6 +30,6 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (EphyDownloadsPopover, ephy_downloads_popover, EPHY, DOWNLOADS_POPOVER, GtkPopover)
 
-GtkWidget *ephy_downloads_popover_new      (GtkWidget *relative_to);
+GtkWidget *ephy_downloads_popover_new      (void);
 
 G_END_DECLS
diff --git a/lib/widgets/ephy-file-chooser.c b/lib/widgets/ephy-file-chooser.c
index 1b6dd213c..1ce5f8592 100644
--- a/lib/widgets/ephy-file-chooser.c
+++ b/lib/widgets/ephy-file-chooser.c
@@ -98,27 +98,28 @@ ephy_create_file_chooser (const char            *title,
                           GtkFileChooserAction   action,
                           EphyFileFilterDefault  default_filter)
 {
-  GtkWidget *toplevel_window = gtk_widget_get_toplevel (parent);
+  GtkRoot *root = gtk_widget_get_root (parent);
   GtkFileChooser *dialog;
   GtkFileFilter *filter[EPHY_FILE_FILTER_LAST];
-  g_autofree char *downloads_dir = NULL;
+  g_autofree char *downloads_dir_path = NULL;
+  g_autoptr (GFile) downloads_dir = NULL;
 
-  g_assert (GTK_IS_WINDOW (toplevel_window));
+  g_assert (GTK_IS_WINDOW (root));
   g_assert (default_filter >= 0 && default_filter <= EPHY_FILE_FILTER_LAST);
 
   dialog = GTK_FILE_CHOOSER (gtk_file_chooser_native_new (title,
-                                                          GTK_WINDOW (toplevel_window),
+                                                          GTK_WINDOW (root),
                                                           action,
                                                           NULL,
                                                           _("_Cancel")));
   gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (dialog), TRUE);
 
-  downloads_dir = ephy_file_get_downloads_dir ();
+  downloads_dir_path = ephy_file_get_downloads_dir ();
+  downloads_dir = g_file_new_for_path (downloads_dir_path);
   gtk_file_chooser_add_shortcut_folder (dialog, downloads_dir, NULL);
 
   if (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
-      action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
-      action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) {
+      action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) {
     gtk_file_chooser_native_set_accept_label (GTK_FILE_CHOOSER_NATIVE (dialog), _("_Open"));
   } else if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
     gtk_file_chooser_native_set_accept_label (GTK_FILE_CHOOSER_NATIVE (dialog), _("_Save"));
diff --git a/lib/widgets/ephy-location-entry.c b/lib/widgets/ephy-location-entry.c
index e6b7b1689..b9c3e2c45 100644
--- a/lib/widgets/ephy-location-entry.c
+++ b/lib/widgets/ephy-location-entry.c
@@ -37,13 +37,14 @@
 #include "ephy-title-widget.h"
 #include "ephy-uri-helpers.h"
 
-#include <dazzle.h>
 #include <gdk/gdkkeysyms.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 #include <string.h>
 #include <webkit2/webkit2.h>
 
+#define PAGE_STEP 20
+
 /**
  * SECTION:ephy-location-entry
  * @short_description: A location entry widget
@@ -53,31 +54,29 @@
  */
 
 struct _EphyLocationEntry {
-  GtkBin parent_instance;
+  GtkWidget parent_instance;
 
-  GtkWidget *overlay;
-  GtkWidget *url_entry;
-  GtkWidget *page_action_box;
-  GtkWidget *bookmark_icon;
+  GtkWidget *text;
+  GtkWidget *progress;
+  GtkWidget *security_button;
   GtkWidget *bookmark_button;
-  GtkWidget *reader_mode_icon;
   GtkWidget *reader_mode_button;
+  GList *page_actions;
 
-  GBinding *paste_binding;
-
-  GtkPopover *add_bookmark_popover;
-  GtkCssProvider *css_provider;
+  GtkWidget *suggestions_popover;
+  GtkSingleSelection *suggestions_model;
 
-  gboolean reader_mode_active;
-  gboolean button_release_is_blocked;
+  GtkWidget *context_menu;
 
   char *saved_text;
   char *jump_tab;
 
-  guint allocation_width;
   guint progress_timeout;
   gdouble progress_fraction;
 
+  gboolean reader_mode_active;
+  gboolean show_suggestions;
+
   guint dns_prefetch_handle_id;
 
   guint user_changed : 1;
@@ -91,12 +90,16 @@ struct _EphyLocationEntry {
 
 enum {
   PROP_0,
+  PROP_MODEL,
+  PROP_SHOW_SUGGESTIONS,
   PROP_ADDRESS,
   PROP_SECURITY_LEVEL,
-  LAST_PROP
+  LAST_TITLE_WIDGET_PROP,
+  LAST_PROP = PROP_ADDRESS
 };
+static GParamSpec *props[LAST_PROP] = { 0 };
 
-enum signalsEnum {
+enum {
   ACTIVATE,
   USER_CHANGED,
   READER_MODE_CHANGED,
@@ -106,309 +109,192 @@ enum signalsEnum {
 };
 static gint signals[LAST_SIGNAL] = { 0 };
 
+static void ephy_location_entry_editable_init (GtkEditableInterface *iface);
 static void ephy_location_entry_title_widget_interface_init (EphyTitleWidgetInterface *iface);
-static void schedule_dns_prefetch (EphyLocationEntry *entry,
-                                   const gchar       *url);
 
-G_DEFINE_TYPE_WITH_CODE (EphyLocationEntry, ephy_location_entry, GTK_TYPE_BIN,
+G_DEFINE_TYPE_WITH_CODE (EphyLocationEntry, ephy_location_entry, GTK_TYPE_WIDGET,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
+                                                ephy_location_entry_editable_init)
                          G_IMPLEMENT_INTERFACE (EPHY_TYPE_TITLE_WIDGET,
                                                 ephy_location_entry_title_widget_interface_init))
-static gboolean
-entry_button_release (GtkWidget *widget,
-                      GdkEvent  *event,
-                      gpointer   user_data)
-{
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (user_data);
-
-  if (((GdkEventButton *)event)->button != GDK_BUTTON_PRIMARY)
-    return GDK_EVENT_PROPAGATE;
-
-  gtk_editable_select_region (GTK_EDITABLE (entry->url_entry), 0, -1);
-
-  g_signal_handlers_block_by_func (widget, G_CALLBACK (entry_button_release), entry);
-  entry->button_release_is_blocked = TRUE;
 
-  return GDK_EVENT_STOP;
-}
+typedef struct {
+  GUri *uri;
+  EphyLocationEntry *entry;
+} PrefetchHelper;
 
 static void
-update_entry_style (EphyLocationEntry *self)
-{
-  PangoAttrList *attrs;
-  PangoAttribute *color_normal;
-  PangoAttribute *color_dimmed;
-  PangoAttribute *scaled;
-  g_autoptr (GUri) uri = NULL;
-  const char *text = gtk_entry_get_text (GTK_ENTRY (self->url_entry));
-  const char *host;
-  const char *base_domain;
-  char *sub_string;
-
-  attrs = pango_attr_list_new ();
-
-  if (self->adaptive_mode == EPHY_ADAPTIVE_MODE_NARROW) {
-    scaled = pango_attr_scale_new (PANGO_SCALE_SMALL);
-    pango_attr_list_insert (attrs, scaled);
-  }
-
-  if (gtk_widget_has_focus (self->url_entry))
-    goto out;
-
-  uri = g_uri_parse (text, G_URI_FLAGS_NONE, NULL);
-  if (!uri)
-    goto out;
-
-  host = g_uri_get_host (uri);
-  if (!host || strlen (host) == 0)
-    goto out;
-
-  base_domain = soup_tld_get_base_domain (host, NULL);
-  if (!base_domain)
-    goto out;
-
-  sub_string = strstr (text, base_domain);
-  if (!sub_string)
-    goto out;
-
-  /* Complete text is dimmed */
-  color_dimmed = pango_attr_foreground_alpha_new (32768);
-  pango_attr_list_insert (attrs, color_dimmed);
-
-  /* Base domain with normal style */
-  color_normal = pango_attr_foreground_alpha_new (65535);
-  color_normal->start_index = sub_string - text;
-  color_normal->end_index = color_normal->start_index + strlen (base_domain);
-  pango_attr_list_insert (attrs, color_normal);
-
-out:
-  gtk_entry_set_attributes (GTK_ENTRY (self->url_entry), attrs);
-  pango_attr_list_unref (attrs);
-}
-
-static gboolean
-entry_focus_in_event (GtkWidget *widget,
-                      GdkEvent  *event,
-                      gpointer   user_data)
+free_prefetch_helper (PrefetchHelper *helper)
 {
-  EphyLocationEntry *self = EPHY_LOCATION_ENTRY (user_data);
-
-  update_entry_style (self);
-  return GDK_EVENT_PROPAGATE;
+  g_uri_unref (helper->uri);
+  g_object_unref (helper->entry);
+  g_free (helper);
 }
 
 static gboolean
-entry_focus_out_event (GtkWidget *widget,
-                       GdkEvent  *event,
-                       gpointer   user_data)
+do_dns_prefetch (PrefetchHelper *helper)
 {
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (user_data);
-
-  update_entry_style (entry);
-
-  if (((GdkEventButton *)event)->button != GDK_BUTTON_PRIMARY)
-    return GDK_EVENT_PROPAGATE;
+  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
 
-  /* Unselect. */
-  gtk_editable_select_region (GTK_EDITABLE (entry->url_entry), 0, 0);
+  if (helper->uri)
+    webkit_web_context_prefetch_dns (ephy_embed_shell_get_web_context (shell), g_uri_get_host (helper->uri));
 
-  if (entry->button_release_is_blocked) {
-    g_signal_handlers_unblock_by_func (widget, G_CALLBACK (entry_button_release), entry);
-    entry->button_release_is_blocked = FALSE;
-  }
+  helper->entry->dns_prefetch_handle_id = 0;
 
-  return GDK_EVENT_PROPAGATE;
+  return G_SOURCE_REMOVE;
 }
 
+/*
+ * Note: As we do not have access to WebKitNetworkProxyMode, and because
+ * Epiphany does not ever change it, we are just checking system default proxy.
+ */
 static void
-editable_changed_cb (GtkEditable       *editable,
-                     EphyLocationEntry *entry);
-
-static void
-ephy_location_entry_activate (EphyLocationEntry *entry)
+proxy_resolver_ready_cb (GObject      *object,
+                         GAsyncResult *result,
+                         gpointer      user_data)
 {
-  g_signal_emit_by_name (entry->url_entry, "activate");
-}
+  PrefetchHelper *helper = user_data;
+  GProxyResolver *resolver = G_PROXY_RESOLVER (object);
+  g_autoptr (GError) error = NULL;
+  g_auto (GStrv) proxies = NULL;
 
-static const char *
-ephy_location_entry_title_widget_get_address (EphyTitleWidget *widget)
-{
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
+  proxies = g_proxy_resolver_lookup_finish (resolver, result, &error);
+  if (error != NULL) {
+    free_prefetch_helper (helper);
+    return;
+  }
 
-  g_assert (entry);
+  if (proxies != NULL && (g_strv_length (proxies) > 1 || g_strcmp0 (proxies[0], "direct://") != 0)) {
+    free_prefetch_helper (helper);
+    return;
+  }
 
-  return gtk_entry_get_text (GTK_ENTRY (entry->url_entry));
+  g_clear_handle_id (&helper->entry->dns_prefetch_handle_id, g_source_remove);
+  helper->entry->dns_prefetch_handle_id =
+    g_timeout_add_full (G_PRIORITY_DEFAULT,
+                        250,
+                        (GSourceFunc)do_dns_prefetch,
+                        helper,
+                        (GDestroyNotify)free_prefetch_helper);
+  g_source_set_name_by_id (helper->entry->dns_prefetch_handle_id, "[epiphany] do_dns_prefetch");
 }
 
 static void
-ephy_location_entry_title_widget_set_address (EphyTitleWidget *widget,
-                                              const char      *address)
+schedule_dns_prefetch (EphyLocationEntry *entry,
+                       const gchar       *url)
 {
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
-  GtkClipboard *clipboard;
-  const char *text;
-  g_autofree char *effective_text = NULL;
-  g_autofree char *selection = NULL;
-  int start, end;
-  const char *final_text;
-
-  g_assert (widget);
-
-  /* Setting a new text will clear the clipboard. This makes it impossible
-   * to copy&paste from the location entry of one tab into another tab, see
-   * bug #155824. So we save the selection iff the clipboard was owned by
-   * the location entry.
-   */
-  if (gtk_widget_get_realized (GTK_WIDGET (entry))) {
-    clipboard = gtk_widget_get_clipboard (GTK_WIDGET (entry->url_entry),
-                                          GDK_SELECTION_PRIMARY);
-    g_assert (clipboard != NULL);
-
-    if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry->url_entry) &&
-        gtk_editable_get_selection_bounds (GTK_EDITABLE (entry->url_entry),
-                                           &start, &end)) {
-      selection = gtk_editable_get_chars (GTK_EDITABLE (entry->url_entry),
-                                          start, end);
-    }
-  }
-
-  if (address != NULL) {
-    if (g_str_has_prefix (address, EPHY_ABOUT_SCHEME))
-      effective_text = g_strdup_printf ("about:%s",
-                                        address + strlen (EPHY_ABOUT_SCHEME) + 1);
-    text = address;
-  } else {
-    text = "";
-  }
-
-  final_text = effective_text ? effective_text : text;
-
-  entry->block_update = TRUE;
-  g_signal_handlers_block_by_func (entry->url_entry, G_CALLBACK (editable_changed_cb), entry);
-  gtk_entry_set_text (GTK_ENTRY (entry->url_entry), final_text);
-  update_entry_style (entry);
-  g_signal_handlers_unblock_by_func (entry->url_entry, G_CALLBACK (editable_changed_cb), entry);
-
-  dzl_suggestion_entry_hide_suggestions (DZL_SUGGESTION_ENTRY (entry->url_entry));
-  entry->block_update = FALSE;
+  GProxyResolver *resolver = g_proxy_resolver_get_default ();
+  PrefetchHelper *helper;
+  g_autoptr (GUri) uri = NULL;
 
-  /* Now restore the selection.
-   * Note that it's not owned by the entry anymore!
-   */
-  if (selection != NULL) {
-    gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
-                            selection, strlen (selection));
-  }
-}
+  if (resolver == NULL)
+    return;
 
-static EphySecurityLevel
-ephy_location_entry_title_widget_get_security_level (EphyTitleWidget *widget)
-{
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
+  uri = g_uri_parse (url, G_URI_FLAGS_NONE, NULL);
+  if (!uri || !g_uri_get_host (uri))
+    return;
 
-  g_assert (entry);
+  helper = g_new0 (PrefetchHelper, 1);
+  helper->entry = g_object_ref (entry);
+  helper->uri = g_steal_pointer (&uri);
 
-  return entry->security_level;
+  g_proxy_resolver_lookup_async (resolver, url, NULL, proxy_resolver_ready_cb, helper);
 }
 
 static void
-ephy_location_entry_title_widget_set_security_level (EphyTitleWidget  *widget,
-                                                     EphySecurityLevel security_level)
+editable_changed_cb (GtkEditable       *editable,
+                     EphyLocationEntry *entry);
 
+static void
+update_selected_url (EphyLocationEntry *entry)
 {
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
-  const char *icon_name;
+  DzlSuggestion *suggestion;
+  const gchar *uri;
 
-  g_assert (entry);
+  suggestion = gtk_single_selection_get_selected_item (entry->suggestions_model);
 
-  if (!entry->reader_mode_active) {
-    icon_name = ephy_security_level_to_icon_name (security_level);
-    gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->url_entry),
-                                       GTK_ENTRY_ICON_PRIMARY,
-                                       icon_name);
-  } else {
-    gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->url_entry),
-                                       GTK_ENTRY_ICON_PRIMARY,
-                                       NULL);
-  }
+  if (!suggestion)
+    return;
 
-  entry->security_level = security_level;
-}
+  uri = dzl_suggestion_get_id (suggestion);
 
-static void
-ephy_location_entry_set_property (GObject      *object,
-                                  guint         prop_id,
-                                  const GValue *value,
-                                  GParamSpec   *pspec)
-{
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
+  g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+  g_clear_pointer (&entry->jump_tab, g_free);
 
-  switch (prop_id) {
-    case PROP_ADDRESS:
-      ephy_title_widget_set_address (EPHY_TITLE_WIDGET (entry),
-                                     g_value_get_string (value));
-      break;
-    case PROP_SECURITY_LEVEL:
-      ephy_title_widget_set_security_level (EPHY_TITLE_WIDGET (entry),
-                                            g_value_get_enum (value));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  if (g_str_has_prefix (uri, "ephy-tab://")) {
+    entry->jump_tab = g_strdup (uri);
+    gtk_editable_set_text (GTK_EDITABLE (entry), dzl_suggestion_get_subtitle (suggestion));
+  } else {
+    gtk_editable_set_text (GTK_EDITABLE (entry), uri);
   }
-}
-
-static void
-ephy_location_entry_get_property (GObject    *object,
-                                  guint       prop_id,
-                                  GValue     *value,
-                                  GParamSpec *pspec)
-{
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
+  gtk_editable_set_position (GTK_EDITABLE (entry), -1);
+  g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
 
-  switch (prop_id) {
-    case PROP_ADDRESS:
-      g_value_set_string (value, ephy_title_widget_get_address (EPHY_TITLE_WIDGET (entry)));
-      break;
-    case PROP_SECURITY_LEVEL:
-      g_value_set_enum (value, ephy_title_widget_get_security_level (EPHY_TITLE_WIDGET (entry)));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-  }
+  schedule_dns_prefetch (entry, uri);
 }
 
 static void
-ephy_location_entry_constructed (GObject *object)
-{
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
-
-  G_OBJECT_CLASS (ephy_location_entry_parent_class)->constructed (object);
-
-  gtk_entry_set_input_hints (GTK_ENTRY (entry->url_entry), GTK_INPUT_HINT_NO_EMOJI);
+update_actions (EphyLocationEntry *entry)
+{
+  GdkClipboard *clipboard;
+  GtkEntryBuffer *buffer;
+  gboolean has_clipboard;
+  gboolean has_selection;
+  gboolean has_content;
+  gboolean editable;
+
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (entry));
+  buffer = gtk_text_get_buffer (GTK_TEXT (entry->text));
+
+  has_clipboard = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (clipboard), G_TYPE_STRING);
+  has_selection = gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), NULL, NULL);
+  has_content = buffer && (gtk_entry_buffer_get_length (buffer) > 0);
+  editable = gtk_editable_get_editable (GTK_EDITABLE (entry));
+
+  /* This is a copy of the GtkText logic. We need it as GtkText normally
+   * refreshes these actions upon opening context menu, and we don't allow it
+   * to do that and show our own menu instead */
+  gtk_widget_action_set_enabled (entry->text, "clipboard.cut",
+                                 editable && has_selection);
+  gtk_widget_action_set_enabled (entry->text, "clipboard.copy",
+                                 has_selection);
+  gtk_widget_action_set_enabled (entry->text, "clipboard.paste",
+                                 editable && has_clipboard);
+
+  gtk_widget_action_set_enabled (entry->text, "selection.delete",
+                                 editable && has_selection);
+  gtk_widget_action_set_enabled (entry->text, "selection.select-all",
+                                 has_content);
+
+  gtk_widget_action_set_enabled (GTK_WIDGET (entry), "clipboard.paste-and-go",
+                                 editable && has_clipboard);
+  gtk_widget_action_set_enabled (entry->text, "edit.clear",
+                                 has_content);
+  gtk_widget_action_set_enabled (entry->text, "edit.undo-extra",
+                                 entry->user_changed);
+  gtk_widget_action_set_enabled (entry->text, "edit.redo-extra",
+                                 entry->can_redo);
 }
 
 static void
-ephy_location_entry_finalize (GObject *object)
+show_context_menu (EphyLocationEntry *entry,
+                   double             x,
+                   double             y)
 {
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
-
-  g_free (entry->saved_text);
-  g_clear_pointer (&entry->jump_tab, g_free);
-
-  G_OBJECT_CLASS (ephy_location_entry_parent_class)->finalize (object);
-}
+  update_actions (entry);
 
-static void
-ephy_location_entry_get_preferred_height (GtkWidget *widget,
-                                          gint      *minimum_height,
-                                          gint      *natural_height)
-{
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
+  if (x != -1 && y != -1) {
+    GdkRectangle rect = { x, y, 1, 1 };
+    gtk_popover_set_pointing_to (GTK_POPOVER (entry->context_menu), &rect);
+  } else {
+    gtk_popover_set_pointing_to (GTK_POPOVER (entry->context_menu), NULL);
+  }
 
-  gtk_widget_get_preferred_height (entry->url_entry, minimum_height, natural_height);
+  gtk_popover_popup (GTK_POPOVER (entry->context_menu));
 }
 
 static void
-ephy_location_entry_do_copy_clipboard (GtkEntry *entry)
+copy_clipboard (EphyLocationEntry *entry)
 {
   g_autofree char *text = NULL;
   gint start;
@@ -424,867 +310,1152 @@ ephy_location_entry_do_copy_clipboard (GtkEntry *entry)
     text = ephy_uri_normalize (tmp);
   }
 
-  gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (entry),
-                                                    GDK_SELECTION_CLIPBOARD),
-                          text, -1);
+  gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (entry)), text);
 }
 
 static void
-ephy_location_entry_copy_clipboard (GtkEntry *entry,
-                                    gpointer  user_data)
+copy_clipboard_cb (EphyLocationEntry *entry)
 {
-  ephy_location_entry_do_copy_clipboard (entry);
+  copy_clipboard (entry);
 
-  g_signal_stop_emission_by_name (entry, "copy-clipboard");
+  g_signal_stop_emission_by_name (entry->text, "copy-clipboard");
 }
 
 static void
-ephy_location_entry_cut_clipboard (GtkEntry *entry)
+cut_clipboard_cb (EphyLocationEntry *entry)
 {
   if (!gtk_editable_get_editable (GTK_EDITABLE (entry))) {
     gtk_widget_error_bell (GTK_WIDGET (entry));
     return;
   }
 
-  ephy_location_entry_do_copy_clipboard (entry);
+  copy_clipboard (entry);
   gtk_editable_delete_selection (GTK_EDITABLE (entry));
 
-  g_signal_stop_emission_by_name (entry, "cut-clipboard");
+  g_signal_stop_emission_by_name (entry->text, "cut-clipboard");
 }
 
 static void
-ephy_location_entry_title_widget_interface_init (EphyTitleWidgetInterface *iface)
-{
-  iface->get_address = ephy_location_entry_title_widget_get_address;
-  iface->set_address = ephy_location_entry_title_widget_set_address;
-  iface->get_security_level = ephy_location_entry_title_widget_get_security_level;
-  iface->set_security_level = ephy_location_entry_title_widget_set_security_level;
+click_pressed_cb (EphyLocationEntry *entry,
+                  int                n_click,
+                  double             x,
+                  double             y,
+                  GtkGesture        *gesture)
+{
+  if (n_click > 1) {
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+    return;
+  }
+
+  if (gtk_widget_has_focus (entry->text)) {
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+    return;
+  }
+
+  if (gtk_widget_pick (GTK_WIDGET (entry), x, y, GTK_PICK_DEFAULT) != entry->text)
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
 }
 
 static void
-ephy_location_entry_dispose (GObject *object)
+click_released_cb (EphyLocationEntry *entry,
+                   int                n_click,
+                   double             x,
+                   double             y,
+                   GtkGesture        *gesture)
+{
+  if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), NULL, NULL)) {
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+    return;
+  }
+
+  gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
+}
+
+static void
+long_press_cb (EphyLocationEntry *entry,
+               double             x,
+               double             y,
+               GtkGesture        *gesture)
 {
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
+  if (gtk_widget_pick (GTK_WIDGET (entry), x, y, GTK_PICK_DEFAULT) == entry->text)
+    gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
 
-  g_clear_handle_id (&entry->progress_timeout, g_source_remove);
+  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+}
 
-  g_clear_object (&entry->css_provider);
+static gboolean
+key_pressed_cb (EphyLocationEntry     *entry,
+                guint                  keyval,
+                guint                  keycode,
+                GdkModifierType        state,
+                GtkEventControllerKey *controller)
+{
+  guint selected, matches;
 
-  G_OBJECT_CLASS (ephy_location_entry_parent_class)->dispose (object);
+  if (state & (GDK_SHIFT_MASK | GDK_ALT_MASK | GDK_CONTROL_MASK))
+    return FALSE;
+
+  matches = g_list_model_get_n_items (G_LIST_MODEL (entry->suggestions_model));
+  selected = gtk_single_selection_get_selected (entry->suggestions_model);
+
+  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) {
+    if (selected == 0) {
+      gtk_widget_error_bell (GTK_WIDGET (entry));
+      return TRUE;
+    }
+
+    if (selected == GTK_INVALID_LIST_POSITION)
+      selected = matches - 1;
+    else
+      selected--;
+
+    gtk_single_selection_set_selected (entry->suggestions_model, selected);
+    update_selected_url (entry);
+    return TRUE;
+  }
+
+  if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) {
+    if (selected == matches - 1) {
+      gtk_widget_error_bell (GTK_WIDGET (entry));
+      return TRUE;
+    }
+
+    if (selected == GTK_INVALID_LIST_POSITION)
+      selected = 0;
+    else
+      selected++;
+
+    gtk_single_selection_set_selected (entry->suggestions_model, selected);
+    update_selected_url (entry);
+    return TRUE;
+  }
+
+  if (keyval == GDK_KEY_Page_Up || keyval == GDK_KEY_KP_Page_Up) {
+    if (selected == 0) {
+      gtk_widget_error_bell (GTK_WIDGET (entry));
+      return TRUE;
+    }
+
+    if (selected == GTK_INVALID_LIST_POSITION)
+      selected = matches - 1;
+    else if (selected < PAGE_STEP)
+      selected = 0;
+    else
+      selected -= PAGE_STEP;
+
+    gtk_single_selection_set_selected (entry->suggestions_model, selected);
+    update_selected_url (entry);
+    return TRUE;
+  }
+
+  if (keyval == GDK_KEY_Page_Down || keyval == GDK_KEY_KP_Page_Down) {
+    if (selected == matches - 1) {
+      gtk_widget_error_bell (GTK_WIDGET (entry));
+      return TRUE;
+    }
+
+    if (selected == GTK_INVALID_LIST_POSITION)
+      selected = 0;
+    else if (selected + PAGE_STEP > matches - 1)
+      selected = matches - 1;
+    else
+      selected += PAGE_STEP;
+
+    gtk_single_selection_set_selected (entry->suggestions_model, selected);
+    update_selected_url (entry);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static void
+text_pressed_cb (EphyLocationEntry *entry,
+                 int                n_click,
+                 double             x,
+                 double             y,
+                 GtkGesture        *gesture)
+{
+  GdkEventSequence *current;
+  GdkEvent *event;
+
+  current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+  event = gtk_gesture_get_last_event (gesture, current);
+
+  if (gdk_event_triggers_context_menu (event)) {
+    show_context_menu (entry, x, y);
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+  }
+
+  if (n_click >= 2)
+    gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
+}
+
+static GIcon *
+get_suggestion_icon (GtkListItem *item,
+                     GIcon       *icon)
+{
+  DzlSuggestion *suggestion = gtk_list_item_get_item (item);
+  GtkWidget *widget = gtk_list_item_get_child (item);
+  cairo_surface_t *surface;
+
+  surface = dzl_suggestion_get_icon_surface (suggestion, widget);
+
+  if (surface) {
+    int width, height;
+
+    if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE)
+      return NULL;
+
+    width = cairo_image_surface_get_width (surface);
+    height = cairo_image_surface_get_height (surface);
+
+    return G_ICON (gdk_pixbuf_get_from_surface (surface, 0, 0, width, height));
+  }
+
+  if (icon)
+    return g_object_ref (icon);
+
+  return NULL;
 }
 
+static GIcon *
+get_suggestion_secondary_icon (GtkListItem *item,
+                               GIcon       *icon)
+{
+  DzlSuggestion *suggestion = gtk_list_item_get_item (item);
+  GtkWidget *widget = gtk_list_item_get_child (item);
+  cairo_surface_t *surface;
+
+  surface = dzl_suggestion_get_secondary_icon_surface (suggestion, widget);
+
+  if (surface) {
+    int width, height;
+
+    if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE)
+      return NULL;
+
+    width = cairo_image_surface_get_width (surface);
+    height = cairo_image_surface_get_height (surface);
+
+    return G_ICON (gdk_pixbuf_get_from_surface (surface, 0, 0, width, height));
+  }
+
+  if (icon)
+    return g_object_ref (icon);
+
+  return NULL;
+}
 
 static void
-ephy_location_entry_class_init (EphyLocationEntryClass *klass)
+update_entry_style (EphyLocationEntry *self,
+                    gboolean           focus)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  PangoAttrList *attrs;
+  PangoAttribute *color_normal;
+  PangoAttribute *color_dimmed;
+  g_autoptr (GUri) uri = NULL;
+  const char *text = gtk_editable_get_text (GTK_EDITABLE (self));
+  const char *host;
+  const char *base_domain;
+  char *sub_string;
 
-  object_class->get_property = ephy_location_entry_get_property;
-  object_class->set_property = ephy_location_entry_set_property;
-  object_class->constructed = ephy_location_entry_constructed;
-  object_class->finalize = ephy_location_entry_finalize;
-  object_class->dispose = ephy_location_entry_dispose;
+  attrs = pango_attr_list_new ();
 
-  widget_class->get_preferred_height = ephy_location_entry_get_preferred_height;
+  if (focus)
+    goto out;
 
-  g_object_class_override_property (object_class, PROP_ADDRESS, "address");
-  g_object_class_override_property (object_class, PROP_SECURITY_LEVEL, "security-level");
+  uri = g_uri_parse (text, G_URI_FLAGS_NONE, NULL);
+  if (!uri)
+    goto out;
 
-  /**
-   * EphyLocationEntry::activate:
-   * @flags: the #GdkModifierType from the activation event
-   *
-   * Emitted when the entry is activated.
-   *
-   */
-  signals[ACTIVATE] = g_signal_new ("activate", G_OBJECT_CLASS_TYPE (klass),
-                                    G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
-                                    0, NULL, NULL, NULL,
-                                    G_TYPE_NONE,
-                                    1,
-                                    GDK_TYPE_MODIFIER_TYPE);
+  host = g_uri_get_host (uri);
+  if (!host || strlen (host) == 0)
+    goto out;
 
-  /**
-   * EphyLocationEntry::user-changed:
-   * @entry: the object on which the signal is emitted
-   *
-   * Emitted when the user changes the contents of the internal #GtkEntry
-   *
-   */
-  signals[USER_CHANGED] = g_signal_new ("user_changed", G_OBJECT_CLASS_TYPE (klass),
-                                        G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
-                                        0, NULL, NULL, NULL,
-                                        G_TYPE_NONE,
-                                        0,
-                                        G_TYPE_NONE);
+  base_domain = soup_tld_get_base_domain (host, NULL);
+  if (!base_domain)
+    goto out;
 
-  /**
-   * EphyLocationEntry::reader-mode-changed:
-   * @entry: the object on which the signal is emitted
-   * @active: whether reader mode is active
-   *
-   * Emitted when the user clicks the reader mode icon inside the
-   * #EphyLocationEntry.
-   *
-   */
-  signals[READER_MODE_CHANGED] = g_signal_new ("reader-mode-changed", G_OBJECT_CLASS_TYPE (klass),
-                                               G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
-                                               0, NULL, NULL, NULL,
-                                               G_TYPE_NONE,
-                                               1,
-                                               G_TYPE_BOOLEAN);
+  sub_string = strstr (text, base_domain);
+  if (!sub_string)
+    goto out;
 
-  /**
-   * EphyLocationEntry::get-location:
-   * @entry: the object on which the signal is emitted
-   * Returns: the current page address as a string
-   *
-   * For drag and drop purposes, the location bar will request you the
-   * real address of where it is pointing to. The signal handler for this
-   * function should return the address of the currently loaded site.
-   *
-   */
-  signals[GET_LOCATION] = g_signal_new ("get-location", G_OBJECT_CLASS_TYPE (klass),
-                                        G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
-                                        0, ephy_signal_accumulator_string,
-                                        NULL, NULL,
-                                        G_TYPE_STRING,
-                                        0,
-                                        G_TYPE_NONE);
+  /* Complete text is dimmed */
+  color_dimmed = pango_attr_foreground_alpha_new (32768);
+  pango_attr_list_insert (attrs, color_dimmed);
 
-  /**
-   * EphyLocationEntry::get-title:
-   * @entry: the object on which the signal is emitted
-   * Returns: the current page title as a string
-   *
-   * For drag and drop purposes, the location bar will request you the
-   * title of where it is pointing to. The signal handler for this
-   * function should return the title of the currently loaded site.
-   *
-   */
-  signals[GET_TITLE] = g_signal_new ("get-title", G_OBJECT_CLASS_TYPE (klass),
-                                     G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
-                                     0, ephy_signal_accumulator_string,
-                                     NULL, NULL,
-                                     G_TYPE_STRING,
-                                     0,
-                                     G_TYPE_NONE);
+  /* Base domain with normal style */
+  color_normal = pango_attr_foreground_alpha_new (65535);
+  color_normal->start_index = sub_string - text;
+  color_normal->end_index = color_normal->start_index + strlen (base_domain);
+  pango_attr_list_insert (attrs, color_normal);
+
+out:
+  gtk_text_set_attributes (GTK_TEXT (self->text), attrs);
+  pango_attr_list_unref (attrs);
+}
+
+static void
+update_suggestions_popover (EphyLocationEntry *entry)
+{
+  guint n_items;
+
+  n_items = g_list_model_get_n_items (G_LIST_MODEL (entry->suggestions_model));
+
+  if (entry->show_suggestions && n_items > 0)
+    gtk_popover_popup (GTK_POPOVER (entry->suggestions_popover));
+  else
+    gtk_popover_popdown (GTK_POPOVER (entry->suggestions_popover));
+}
+
+static void
+set_show_suggestions (EphyLocationEntry *entry,
+                      gboolean           show)
+{
+  if (entry->show_suggestions == show)
+    return;
+
+  entry->show_suggestions = show;
+
+  update_suggestions_popover (entry);
+
+  g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_SHOW_SUGGESTIONS]);
+}
+
+static void
+focus_enter_cb (EphyLocationEntry *entry)
+{
+  update_entry_style (entry, TRUE);
+}
+
+static void
+focus_leave_cb (EphyLocationEntry *entry)
+{
+  update_entry_style (entry, FALSE);
+  gtk_editable_select_region (GTK_EDITABLE (entry), 0, 0);
+  set_show_suggestions (entry, FALSE);
+}
+
+static void
+emit_activate (EphyLocationEntry *entry,
+               GdkModifierType    modifiers)
+{
+  if (entry->jump_tab) {
+    g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+    gtk_editable_set_text (GTK_EDITABLE (entry), entry->jump_tab);
+    g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+    g_clear_pointer (&entry->jump_tab, g_free);
+  } else {
+    g_autofree char *text = g_strdup (gtk_editable_get_text (GTK_EDITABLE (entry)));
+    char *url = g_strstrip (text);
+    g_autofree char *new_url = NULL;
+
+    gtk_editable_set_text (GTK_EDITABLE (entry), entry->jump_tab ? entry->jump_tab : text);
+
+    if (strlen (url) > 5 && g_str_has_prefix (url, "http:") && url[5] != '/')
+      new_url = g_strdup_printf ("http://%s";, url + 5);
+    else if (strlen (url) > 6 && g_str_has_prefix (url, "https:") && url[6] != '/')
+      new_url = g_strdup_printf ("https://%s";, url + 6);
+
+    if (new_url) {
+      g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+      gtk_editable_set_text (GTK_EDITABLE (entry), new_url);
+      g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+    }
+
+    g_signal_emit (entry, signals[ACTIVATE], 0, modifiers);
+  }
+}
+
+static gboolean
+activate_shortcut_cb (EphyLocationEntry *entry,
+                      GVariant          *args)
+{
+  emit_activate (entry, g_variant_get_int32 (args));
+
+  return TRUE;
 }
 
 static void
 editable_changed_cb (GtkEditable       *editable,
                      EphyLocationEntry *entry)
 {
-  if (entry->block_update == TRUE)
+  if (entry->block_update)
     return;
-  else {
-    entry->user_changed = TRUE;
-    entry->can_redo = FALSE;
-  }
+
+  entry->user_changed = TRUE;
+  entry->can_redo = FALSE;
+  update_actions (entry);
 
   g_clear_pointer (&entry->jump_tab, g_free);
 
-  g_signal_emit (entry, signals[USER_CHANGED], 0);
+  g_signal_emit (entry, signals[USER_CHANGED], 0, gtk_editable_get_text (editable));
+
+  set_show_suggestions (entry, TRUE);
 }
 
-static gboolean
-entry_key_press_cb (GtkEntry          *entry,
-                    GdkEventKey       *event,
-                    EphyLocationEntry *location_entry)
+static void
+reader_mode_clicked_cb (EphyLocationEntry *entry)
 {
-  guint state = event->state & gtk_accelerator_get_default_mod_mask ();
+  entry->reader_mode_active = !entry->reader_mode_active;
 
+  g_signal_emit (G_OBJECT (entry), signals[READER_MODE_CHANGED], 0,
+                 entry->reader_mode_active);
+}
 
-  if (event->keyval == GDK_KEY_Escape && state == 0) {
-    ephy_location_entry_reset (location_entry);
-  }
+static void
+suggestion_activated_cb (EphyLocationEntry *entry,
+                         guint              position)
+{
+  g_autoptr (EphySuggestion) suggestion = NULL;
+  const gchar *text;
 
-  if (event->keyval == GDK_KEY_l && state == GDK_CONTROL_MASK) {
-    /* Make sure the location is activated on CTRL+l even when the
-     * completion popup is shown and have an active keyboard grab.
-     */
-    ephy_location_entry_focus (location_entry);
-  }
+  suggestion = g_list_model_get_item (G_LIST_MODEL (entry->suggestions_model), position);
+  text = ephy_suggestion_get_uri (suggestion);
 
-  if (event->keyval == GDK_KEY_Return ||
-      event->keyval == GDK_KEY_KP_Enter ||
-      event->keyval == GDK_KEY_ISO_Enter) {
-    if (location_entry->jump_tab) {
-      g_signal_handlers_block_by_func (location_entry->url_entry, G_CALLBACK (editable_changed_cb), 
location_entry);
-      gtk_entry_set_text (GTK_ENTRY (location_entry->url_entry), location_entry->jump_tab);
-      g_signal_handlers_unblock_by_func (location_entry->url_entry, G_CALLBACK (editable_changed_cb), 
location_entry);
-      g_clear_pointer (&location_entry->jump_tab, g_free);
-    } else {
-      g_autofree gchar *text = g_strdup (gtk_entry_get_text (GTK_ENTRY (location_entry->url_entry)));
-      gchar *url = g_strstrip (text);
-      g_autofree gchar *new_url = NULL;
-
-      gtk_entry_set_text (GTK_ENTRY (entry), location_entry->jump_tab ? location_entry->jump_tab : text);
-
-      if (strlen (url) > 5 && g_str_has_prefix (url, "http:") && url[5] != '/')
-        new_url = g_strdup_printf ("http://%s";, url + 5);
-      else if (strlen (url) > 6 && g_str_has_prefix (url, "https:") && url[6] != '/')
-        new_url = g_strdup_printf ("https://%s";, url + 6);
-
-      if (new_url) {
-        g_signal_handlers_block_by_func (location_entry->url_entry, G_CALLBACK (editable_changed_cb), 
location_entry);
-        gtk_entry_set_text (GTK_ENTRY (location_entry->url_entry), new_url);
-        g_signal_handlers_unblock_by_func (location_entry->url_entry, G_CALLBACK (editable_changed_cb), 
location_entry);
-      }
-
-      if (state == GDK_CONTROL_MASK) {
-        /* Remove control mask to prevent opening address in a new window */
-        event->state &= ~GDK_CONTROL_MASK;
-
-        if (!g_utf8_strchr (url, -1, ' ') && !g_utf8_strchr (url, -1, '.')) {
-          g_autofree gchar *new_url = g_strdup_printf ("www.%s.com", url);
-
-          g_signal_handlers_block_by_func (location_entry->url_entry, G_CALLBACK (editable_changed_cb), 
location_entry);
-          gtk_entry_set_text (GTK_ENTRY (location_entry->url_entry), new_url);
-          g_signal_handlers_unblock_by_func (location_entry->url_entry, G_CALLBACK (editable_changed_cb), 
location_entry);
-        }
-      }
-    }
+  g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+  gtk_editable_set_text (GTK_EDITABLE (entry), entry->jump_tab ? entry->jump_tab : text);
+  g_clear_pointer (&entry->jump_tab, g_free);
+  g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
 
-    ephy_location_entry_activate (location_entry);
+  set_show_suggestions (entry, FALSE);
 
-    return GDK_EVENT_STOP;
+  /* Now trigger the load.... */
+  emit_activate (entry, 0);
+}
+
+static void
+activate_cb (EphyLocationEntry *entry)
+{
+  if (gtk_widget_get_visible (entry->suggestions_popover)) {
+    guint position = gtk_single_selection_get_selected (entry->suggestions_model);
+
+    if (position != GTK_INVALID_LIST_POSITION) {
+      suggestion_activated_cb (entry, position);
+      return;
+    }
   }
 
-  return GDK_EVENT_PROPAGATE;
+  emit_activate (entry, 0);
 }
 
 static void
-entry_clear_activate_cb (GtkMenuItem       *item,
-                         EphyLocationEntry *entry)
+root_notify_is_active_cb (EphyLocationEntry *entry)
 {
-  entry->block_update = TRUE;
-  g_signal_handlers_block_by_func (entry->url_entry, G_CALLBACK (editable_changed_cb), entry);
-  gtk_entry_set_text (GTK_ENTRY (entry->url_entry), "");
-  g_signal_handlers_unblock_by_func (entry->url_entry, G_CALLBACK (editable_changed_cb), entry);
-  entry->block_update = FALSE;
-  entry->user_changed = TRUE;
+  GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (entry));
+
+  if (!gtk_window_is_active (GTK_WINDOW (root)))
+    set_show_suggestions (entry, FALSE);
 }
 
 static void
-paste_received (GtkClipboard      *clipboard,
-                const gchar       *text,
+update_reader_icon (EphyLocationEntry *entry)
+{
+  GdkDisplay *display;
+  GtkIconTheme *theme;
+  const gchar *name;
+
+  display = gtk_widget_get_display (GTK_WIDGET (entry));
+  theme = gtk_icon_theme_get_for_display (display);
+
+  if (gtk_icon_theme_has_icon (theme, "view-reader-symbolic"))
+    name = "view-reader-symbolic";
+  else
+    name = "ephy-reader-mode-symbolic";
+
+  gtk_button_set_icon_name (GTK_BUTTON (entry->reader_mode_button), name);
+}
+
+static void
+create_security_popup_cb (GtkMenuButton     *button,
+                          EphyLocationEntry *entry)
+{
+  g_signal_emit_by_name (entry, "lock-clicked", button);
+}
+
+static void
+paste_received (GdkClipboard      *clipboard,
+                GAsyncResult      *result,
                 EphyLocationEntry *entry)
 {
-  if (text) {
-    g_signal_handlers_block_by_func (entry->url_entry, G_CALLBACK (editable_changed_cb), entry);
-    gtk_entry_set_text (GTK_ENTRY (entry->url_entry), text);
-    ephy_location_entry_activate (entry);
-    g_signal_handlers_unblock_by_func (entry->url_entry, G_CALLBACK (editable_changed_cb), entry);
+  g_autofree char *text = NULL;
+
+  text = gdk_clipboard_read_text_finish (GDK_CLIPBOARD (clipboard), result, NULL);
+  if (text == NULL) {
+    gtk_widget_error_bell (GTK_WIDGET (entry));
+    return;
   }
+
+  g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+  gtk_editable_set_text (GTK_EDITABLE (entry), text);
+  emit_activate (entry, 0);
+  g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+
+  g_object_unref (entry);
 }
 
 static void
-entry_paste_and_go_activate_cb (GtkMenuItem       *item,
-                                EphyLocationEntry *entry)
+paste_and_go_activate (EphyLocationEntry *entry)
 {
-  GtkClipboard *clipboard;
+  GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (entry));
 
-  clipboard = gtk_clipboard_get_default (gdk_display_get_default ());
-  gtk_clipboard_request_text (clipboard,
-                              (GtkClipboardTextReceivedFunc)paste_received,
-                              entry);
+  gdk_clipboard_read_text_async (clipboard, NULL,
+                                 (GAsyncReadyCallback)paste_received,
+                                 g_object_ref (entry));
 }
 
 static void
-entry_redo_activate_cb (GtkMenuItem       *item,
-                        EphyLocationEntry *entry)
+clear_activate (EphyLocationEntry *entry)
 {
-  ephy_location_entry_undo_reset (entry);
+  entry->block_update = TRUE;
+  g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+  gtk_editable_set_text (GTK_EDITABLE (entry), "");
+  g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+  entry->block_update = FALSE;
+  entry->user_changed = TRUE;
+  update_actions (entry);
 }
 
 static void
-entry_undo_activate_cb (GtkMenuItem       *item,
-                        EphyLocationEntry *entry)
+menu_popup_activate (EphyLocationEntry *entry)
 {
-  ephy_location_entry_reset (entry);
+  show_context_menu (entry, -1, -1);
 }
 
-/* The build should fail here each time when upgrading to a new major version
- * of GTK+, so that we don't forget to update this domain.
- */
-#if GTK_MAJOR_VERSION == 3
-#define GTK_GETTEXT_DOMAIN "gtk30"
-#endif
+static const char *
+ephy_location_entry_title_widget_get_address (EphyTitleWidget *widget)
+{
+  return gtk_editable_get_text (GTK_EDITABLE (widget));
+}
 
 static void
-entry_populate_popup_cb (GtkEntry          *entry,
-                         GtkMenu           *menu,
-                         EphyLocationEntry *lentry)
-{
-  GtkWidget *clear_menuitem;
-  GtkWidget *undo_menuitem;
-  GtkWidget *redo_menuitem;
-  GtkWidget *paste_and_go_menuitem;
-  GtkWidget *separator;
-  GtkWidget *paste_menuitem = NULL;
-  GList *children, *item;
-  int pos = 0, sep = 0;
-  gboolean is_editable;
-
-  /* Translators: the mnemonic shouldn't conflict with any of the
-   * standard items in the GtkEntry context menu (Cut, Copy, Paste, Delete,
-   * Select All, Input Methods and Insert Unicode control character.)
-   */
-  clear_menuitem = gtk_menu_item_new_with_mnemonic (_("Cl_ear"));
-  g_signal_connect (clear_menuitem, "activate",
-                    G_CALLBACK (entry_clear_activate_cb), lentry);
-  is_editable = gtk_editable_get_editable (GTK_EDITABLE (entry));
-  gtk_widget_set_sensitive (clear_menuitem, is_editable);
-  gtk_widget_show (clear_menuitem);
-
-  /* search for the 2nd separator (the one after Select All) in the context
-   * menu, and insert this menu item before it.
-   * It's a bit of a hack, but there seems to be no better way to do it :/
+ephy_location_entry_title_widget_set_address (EphyTitleWidget *widget,
+                                              const char      *address)
+{
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
+//  GdkClipboard *clipboard;
+  const char *text;
+  g_autofree char *effective_text = NULL;
+//  g_autofree char *selection = NULL;
+  const char *final_text;
+
+#if 0 // FIXME
+  /* Setting a new text will clear the clipboard. This makes it impossible
+   * to copy&paste from the location entry of one tab into another tab, see
+   * bug #155824. So we save the selection iff the clipboard was owned by
+   * the location entry.
    */
-  children = gtk_container_get_children (GTK_CONTAINER (menu));
-  for (item = children; item != NULL && sep < 2; item = item->next, pos++) {
-    if (GTK_IS_SEPARATOR_MENU_ITEM (item->data))
-      sep++;
+  if (gtk_widget_get_realized (GTK_WIDGET (entry))) {
+    clipboard = gtk_widget_get_primary_clipboard (GTK_WIDGET (entry));
+    g_assert (clipboard != NULL);
+
+    if (gdk_clipboard_get_owner (clipboard) == G_OBJECT (entry->url_entry) &&
+        gtk_editable_get_selection_bounds (GTK_EDITABLE (entry->url_entry),
+                                           &start, &end)) {
+      selection = gtk_editable_get_chars (GTK_EDITABLE (entry->url_entry),
+                                          start, end);
+    }
+  }
+#endif
+
+  if (address != NULL) {
+    if (g_str_has_prefix (address, EPHY_ABOUT_SCHEME))
+      effective_text = g_strdup_printf ("about:%s",
+                                        address + strlen (EPHY_ABOUT_SCHEME) + 1);
+    text = address;
+  } else {
+    text = "";
   }
-  g_list_free (children);
 
-  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), clear_menuitem, pos - 1);
+  final_text = effective_text ? effective_text : text;
 
-  paste_and_go_menuitem = gtk_menu_item_new_with_mnemonic (_("Paste and _Go"));
+  entry->block_update = TRUE;
+  g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+  gtk_editable_set_text (GTK_EDITABLE (widget), final_text);
+  g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+  update_entry_style (entry, gtk_widget_has_focus (entry->text));
 
-  /* Search for the Paste menu item and insert right after it. */
-  children = gtk_container_get_children (GTK_CONTAINER (menu));
-  for (item = children, pos = 0; item != NULL; item = item->next, pos++) {
-    if (g_strcmp0 (gtk_menu_item_get_label (item->data), g_dgettext (GTK_GETTEXT_DOMAIN, "_Paste")) == 0) {
-      paste_menuitem = item->data;
-      break;
-    }
+  set_show_suggestions (entry, FALSE);
+  entry->block_update = FALSE;
+
+  gtk_editable_set_enable_undo (GTK_EDITABLE (entry), FALSE);
+  gtk_editable_set_enable_undo (GTK_EDITABLE (entry), TRUE);
+
+#if 0
+  /* Now restore the selection.
+   * Note that it's not owned by the entry anymore!
+   */
+  if (selection != NULL) {
+    gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+                            selection, strlen (selection));
   }
-  g_assert (paste_menuitem != NULL);
-  g_list_free (children);
-
-  g_signal_connect (paste_and_go_menuitem, "activate",
-                    G_CALLBACK (entry_paste_and_go_activate_cb), lentry);
-  lentry->paste_binding = g_object_bind_property (paste_menuitem, "sensitive",
-                                                  paste_and_go_menuitem, "sensitive",
-                                                  G_BINDING_SYNC_CREATE);
-  gtk_widget_show (paste_and_go_menuitem);
-  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), paste_and_go_menuitem, pos + 1);
-
-  undo_menuitem = gtk_menu_item_new_with_mnemonic (_("_Undo"));
-  gtk_widget_set_sensitive (undo_menuitem, lentry->user_changed);
-  g_signal_connect (undo_menuitem, "activate",
-                    G_CALLBACK (entry_undo_activate_cb), lentry);
-  gtk_widget_show (undo_menuitem);
-  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), undo_menuitem, 0);
-
-  redo_menuitem = gtk_menu_item_new_with_mnemonic (_("_Redo"));
-  gtk_widget_set_sensitive (redo_menuitem, lentry->can_redo);
-  g_signal_connect (redo_menuitem, "activate",
-                    G_CALLBACK (entry_redo_activate_cb), lentry);
-  gtk_widget_show (redo_menuitem);
-  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), redo_menuitem, 1);
-
-  separator = gtk_separator_menu_item_new ();
-  gtk_widget_show (separator);
-  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), separator, 2);
+#endif
 }
 
-static gboolean
-icon_button_icon_press_event_cb (GtkWidget            *widget,
-                                 GtkEntryIconPosition  position,
-                                 GdkEventButton       *event,
-                                 EphyLocationEntry    *entry)
-{
-  if (((event->type == GDK_BUTTON_PRESS &&
-        event->button == 1) ||
-       (event->type == GDK_TOUCH_BEGIN))) {
-    if (position == GTK_ENTRY_ICON_PRIMARY) {
-      GdkRectangle lock_position;
-      gtk_entry_get_icon_area (GTK_ENTRY (entry->url_entry), GTK_ENTRY_ICON_PRIMARY, &lock_position);
-      g_signal_emit_by_name (entry, "lock-clicked", &lock_position);
-    }
-    return TRUE;
-  }
+static EphySecurityLevel
+ephy_location_entry_title_widget_get_security_level (EphyTitleWidget *widget)
+{
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
 
-  return FALSE;
+  g_assert (entry);
+
+  return entry->security_level;
 }
 
-static GtkBorder
-get_progress_margin (EphyLocationEntry *entry)
+static void
+ephy_location_entry_title_widget_set_security_level (EphyTitleWidget   *widget,
+                                                     EphySecurityLevel  security_level)
 {
-  g_autoptr (GtkWidgetPath) path = NULL;
-  g_autoptr (GtkStyleContext) context = NULL;
-  GtkBorder margin;
-  gint pos;
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
+  const char *icon_name = NULL;
 
-  path = gtk_widget_path_copy (gtk_widget_get_path (entry->url_entry));
+  g_assert (entry);
 
-  pos = gtk_widget_path_append_type (path, GTK_TYPE_WIDGET);
-  gtk_widget_path_iter_set_object_name (path, pos, "progress");
+  if (!entry->reader_mode_active)
+    icon_name = ephy_security_level_to_icon_name (security_level);
 
-  context = gtk_style_context_new ();
-  gtk_style_context_set_path (context, path);
+  if (icon_name)
+    gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (entry->security_button),
+                                   icon_name);
 
-  gtk_style_context_get_margin (context, gtk_style_context_get_state (context), &margin);
+  gtk_widget_set_visible (entry->security_button, !!icon_name);
 
-  return margin;
+  entry->security_level = security_level;
 }
 
-static GtkBorder
-get_padding (EphyLocationEntry *entry)
+static void
+ephy_location_entry_measure (GtkWidget      *widget,
+                             GtkOrientation  orientation,
+                             int             for_size,
+                             int            *minimum,
+                             int            *natural,
+                             int            *minimum_baseline,
+                             int            *natural_baseline)
 {
-  g_autoptr (GtkWidgetPath) path = NULL;
-  g_autoptr (GtkStyleContext) context = NULL;
-  GtkBorder padding;
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
+
+  if (orientation == GTK_ORIENTATION_VERTICAL) {
+    GtkWidget *child;
+
+    for (child = gtk_widget_get_first_child (widget);
+         child;
+         child = gtk_widget_get_next_sibling (child)) {
+      int child_min, child_nat, child_min_baseline, child_nat_baseline;
+
+      if (!gtk_widget_should_layout (child))
+        return;
+
+      gtk_widget_measure (child, orientation, for_size, &child_min, &child_nat,
+                          &child_min_baseline, &child_nat_baseline);
 
-  path = gtk_widget_path_copy (gtk_widget_get_path (entry->url_entry));
+      if (minimum)
+        *minimum = MAX (*minimum, child_min);
+      if (natural)
+        *natural = MAX (*natural, child_nat);
+      if (minimum_baseline)
+        *minimum_baseline = MAX (*minimum_baseline, child_min_baseline);
+      if (natural_baseline)
+        *natural_baseline = MAX (*natural_baseline, child_nat_baseline);
+    }
+  } else {
+    int min, nat, child_min, child_nat;
+    GList *l;
 
-  /* Create a new context here, since the existing one has extra css loaded */
-  context = gtk_style_context_new ();
-  gtk_style_context_set_path (context, path);
+    gtk_widget_measure (entry->text, orientation, for_size, &min, &nat, NULL, NULL);
+
+    /* Any other icons need to be similarly added to the text width */
+    if (gtk_widget_should_layout (entry->security_button)) {
+      gtk_widget_measure (entry->security_button, orientation, for_size,
+                          &child_min, &child_nat, NULL, NULL);
+      min += child_min;
+      nat += child_nat;
+    }
 
-  gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
+    if (gtk_widget_should_layout (entry->bookmark_button)) {
+      gtk_widget_measure (entry->bookmark_button, orientation, for_size,
+                          &child_min, &child_nat, NULL, NULL);
+      min += child_min;
+      nat += child_nat;
+    }
+
+    if (gtk_widget_should_layout (entry->reader_mode_button)) {
+      gtk_widget_measure (entry->reader_mode_button, orientation, for_size,
+                          &child_min, &child_nat, NULL, NULL);
+      min += child_min;
+      nat += child_nat;
+    }
 
-  return padding;
+    /* Since progress bar spans the whole width, we MAX() it instead of adding */
+    gtk_widget_measure (entry->progress, orientation, for_size,
+                        &child_min, &child_nat, NULL, NULL);
+    min = MAX (min, child_min);
+    nat = MAX (nat, child_nat);
+
+    for (l = entry->page_actions; l; l = l->next) {
+      gtk_widget_measure (l->data, orientation, for_size,
+                          &child_min, &child_nat, NULL, NULL);
+      min = MAX (min, child_min);
+      nat = MAX (nat, child_nat);
+    }
+
+    if (minimum)
+      *minimum = min;
+    if (natural)
+      *natural = nat;
+    if (minimum_baseline)
+      *minimum_baseline = -1;
+    if (natural_baseline)
+      *natural_baseline = -1;
+  }
 }
 
 static void
-button_box_size_allocated_cb (GtkWidget    *widget,
-                              GdkRectangle *allocation,
-                              gpointer      user_data)
-{
-  EphyLocationEntry *lentry = EPHY_LOCATION_ENTRY (user_data);
-  g_autofree gchar *css = NULL;
-  GtkBorder margin, padding;
-
-  if (lentry->allocation_width == (guint)allocation->width)
+allocate_icon (GtkWidget   *widget,
+               int          height,
+               int          baseline,
+               GtkWidget   *icon,
+               GtkPackType  pack_type,
+               int         *left_pos,
+               int         *right_pos)
+{
+  GskTransform *transform;
+  int icon_width;
+
+  if (!gtk_widget_should_layout (icon))
     return;
 
-  lentry->allocation_width = allocation->width;
+  gtk_widget_measure (icon, GTK_ORIENTATION_HORIZONTAL, -1,
+                      NULL, &icon_width, NULL, NULL);
 
-  margin = get_progress_margin (lentry);
-  padding = get_padding (lentry);
+  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+    pack_type = (pack_type == GTK_PACK_START) ? GTK_PACK_END : GTK_PACK_START;
 
-  /* We are using the CSS provider here to solve UI displaying issues:
-   *  - padding-right is used to prevent text below the icons on the right side
-   *    of the entry (removing the icon button box width (allocation width).
-   *  - progress margin-right is used to allow progress bar below icons on the
-   *    right side.
-   *
-   * FIXME: Loading CSS during size_allocate is ILLEGAL and BROKEN.
-   */
-  css = g_strdup_printf (".url_entry:dir(ltr) { padding-right: %dpx; }" \
-                         ".url_entry:dir(rtl) { padding-left: %dpx; }" \
-                         ".url_entry:dir(ltr) progress { margin-right: %dpx; }" \
-                         ".url_entry:dir(rtl) progress { margin-left: %dpx; }",
-                         lentry->allocation_width,
-                         lentry->allocation_width,
-                         margin.right + padding.right - lentry->allocation_width,
-                         margin.left + padding.left - lentry->allocation_width);
-  gtk_css_provider_load_from_data (lentry->css_provider, css, -1, NULL);
+  if (pack_type == GTK_PACK_START) {
+    transform = gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (*left_pos, 0));
+    *left_pos += icon_width;
+  } else {
+    *right_pos -= icon_width;
+    transform = gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (*right_pos, 0));
+  }
+
+  gtk_widget_allocate (icon, icon_width, height, baseline, transform);
 }
 
-static gboolean
-event_button_press_event_cb (GtkWidget *widget,
-                             GdkEvent  *event,
-                             gpointer   user_data)
+static void
+ephy_location_entry_size_allocate (GtkWidget *widget,
+                                   int        width,
+                                   int        height,
+                                   int        baseline)
 {
-  return GDK_EVENT_STOP;
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
+  int icon_left_pos = 0;
+  int icon_right_pos = width;
+  GskTransform *transform;
+  GList *l;
+
+  allocate_icon (widget, height, baseline, entry->security_button,
+                 GTK_PACK_START, &icon_left_pos, &icon_right_pos);
+  allocate_icon (widget, height, baseline, entry->bookmark_button,
+                 GTK_PACK_END, &icon_left_pos, &icon_right_pos);
+  allocate_icon (widget, height, baseline, entry->reader_mode_button,
+                 GTK_PACK_END, &icon_left_pos, &icon_right_pos);
+
+  for (l = entry->page_actions; l; l = l->next) {
+    allocate_icon (widget, height, baseline, l->data,
+                   GTK_PACK_END, &icon_left_pos, &icon_right_pos);
+  }
+
+  transform = gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (icon_left_pos, 0));
+  gtk_widget_allocate (entry->text, icon_right_pos - icon_left_pos,
+                       height, baseline, transform);
+
+  gtk_widget_allocate (entry->progress, width, height, baseline, NULL);
+
+  gtk_widget_set_size_request (entry->suggestions_popover,
+                               gtk_widget_get_allocated_width (widget), -1);
+  gtk_widget_queue_resize (entry->suggestions_popover);
+
+  gtk_popover_present (GTK_POPOVER (entry->suggestions_popover));
+  gtk_popover_present (GTK_POPOVER (entry->context_menu));
 }
 
 static void
-ephy_location_entry_suggestion_activated (DzlSuggestionEntry *entry,
-                                          DzlSuggestion      *arg1,
-                                          gpointer            user_data)
+ephy_location_entry_root (GtkWidget *widget)
 {
-  EphyLocationEntry *lentry = EPHY_LOCATION_ENTRY (user_data);
-  DzlSuggestion *suggestion = dzl_suggestion_entry_get_suggestion (entry);
-  const gchar *text = ephy_suggestion_get_uri (EPHY_SUGGESTION (suggestion));
+  GtkRoot *root;
 
-  g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), user_data);
-  gtk_entry_set_text (GTK_ENTRY (entry), lentry->jump_tab ? lentry->jump_tab : text);
-  g_clear_pointer (&lentry->jump_tab, g_free);
-  g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), user_data);
+  GTK_WIDGET_CLASS (ephy_location_entry_parent_class)->root (widget);
 
-  g_signal_stop_emission_by_name (entry, "suggestion-activated");
+  root = gtk_widget_get_root (widget);
 
-  dzl_suggestion_entry_hide_suggestions (entry);
+  g_assert (GTK_IS_WINDOW (root));
 
-  /* Now trigger the load.... */
-  ephy_location_entry_activate (EPHY_LOCATION_ENTRY (lentry));
+  g_signal_connect_swapped (root, "notify::is-active",
+                            G_CALLBACK (root_notify_is_active_cb), widget);
 }
 
 static void
-suggestion_selected (DzlSuggestionEntry *entry,
-                     DzlSuggestion      *suggestion,
-                     gpointer            user_data)
+ephy_location_entry_unroot (GtkWidget *widget)
 {
-  EphyLocationEntry *lentry = EPHY_LOCATION_ENTRY (user_data);
-  const gchar *uri = dzl_suggestion_get_id (suggestion);
+  GtkRoot *root = gtk_widget_get_root (widget);
 
-  g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), user_data);
-  g_clear_pointer (&lentry->jump_tab, g_free);
+  g_signal_handlers_disconnect_by_func (root, G_CALLBACK (root_notify_is_active_cb), widget);
 
-  if (g_str_has_prefix (uri, "ephy-tab://")) {
-    lentry->jump_tab = g_strdup (uri);
-    gtk_entry_set_text (GTK_ENTRY (entry), dzl_suggestion_get_subtitle (suggestion));
-  } else {
-    gtk_entry_set_text (GTK_ENTRY (entry), uri);
+  GTK_WIDGET_CLASS (ephy_location_entry_parent_class)->unroot (widget);
+}
+
+static void
+ephy_location_entry_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
+
+  if (gtk_editable_delegate_set_property (object, prop_id, value, pspec)) {
+    if (prop_id == LAST_TITLE_WIDGET_PROP + GTK_EDITABLE_PROP_EDITABLE) {
+      gtk_accessible_update_property (GTK_ACCESSIBLE (entry),
+                                      GTK_ACCESSIBLE_PROPERTY_READ_ONLY, !g_value_get_boolean (value),
+                                      -1);
+    }
+    return;
   }
-  gtk_editable_set_position (GTK_EDITABLE (entry), -1);
-  g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), user_data);
 
-  schedule_dns_prefetch (lentry, uri);
-}
+  switch (prop_id) {
+    case PROP_MODEL:
+      ephy_location_entry_set_model (entry, g_value_get_object (value));
+      break;
+    case PROP_SHOW_SUGGESTIONS:
+      set_show_suggestions (entry, g_value_get_boolean (value));
+      break;
+    case PROP_ADDRESS:
+      ephy_title_widget_set_address (EPHY_TITLE_WIDGET (entry),
+                                     g_value_get_string (value));
+      break;
+    case PROP_SECURITY_LEVEL:
+      ephy_title_widget_set_security_level (EPHY_TITLE_WIDGET (entry),
+                                            g_value_get_enum (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_location_entry_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
+
+  if (gtk_editable_delegate_get_property (object, prop_id, value, pspec))
+    return;
 
-static void
-enter_notify_cb (GtkWidget *widget,
-                 GdkEvent  *event,
-                 gpointer   user_data)
-{
-  gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
+  switch (prop_id) {
+    case PROP_MODEL:
+      g_value_set_object (value, ephy_location_entry_get_model (entry));
+      break;
+    case PROP_SHOW_SUGGESTIONS:
+      g_value_set_boolean (value, entry->show_suggestions);
+      break;
+    case PROP_ADDRESS:
+      g_value_set_string (value, ephy_title_widget_get_address (EPHY_TITLE_WIDGET (entry)));
+      break;
+    case PROP_SECURITY_LEVEL:
+      g_value_set_enum (value, ephy_title_widget_get_security_level (EPHY_TITLE_WIDGET (entry)));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
 }
 
 static void
-leave_notify_cb (GtkWidget *widget,
-                 GdkEvent  *event,
-                 gpointer   user_data)
+ephy_location_entry_title_widget_interface_init (EphyTitleWidgetInterface *iface)
 {
-  gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT);
+  iface->get_address = ephy_location_entry_title_widget_get_address;
+  iface->set_address = ephy_location_entry_title_widget_set_address;
+  iface->get_security_level = ephy_location_entry_title_widget_get_security_level;
+  iface->set_security_level = ephy_location_entry_title_widget_set_security_level;
 }
 
-static void
-handle_forward_tab_key (GtkWidget *widget,
-                        gpointer   user_data)
+static GtkEditable *
+ephy_location_entry_get_delegate (GtkEditable *editable)
 {
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (user_data);
-  GtkWidget *popover;
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (editable);
 
-  popover = dzl_suggestion_entry_get_popover (DZL_SUGGESTION_ENTRY (entry->url_entry));
-  if (gtk_widget_is_visible (popover)) {
-    g_signal_emit_by_name (entry->url_entry, "move-suggestion", 1, G_TYPE_INT, 1, G_TYPE_NONE);
-  } else {
-    gtk_widget_child_focus (gtk_widget_get_toplevel (GTK_WIDGET (entry)), GTK_DIR_TAB_FORWARD);
-  }
+  return GTK_EDITABLE (entry->text);
 }
 
 static void
-handle_backward_tab_key (GtkWidget *widget,
-                         gpointer   user_data)
+ephy_location_entry_editable_init (GtkEditableInterface *iface)
 {
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (user_data);
-  GtkWidget *popover;
-
-  popover = dzl_suggestion_entry_get_popover (DZL_SUGGESTION_ENTRY (entry->url_entry));
-  if (gtk_widget_is_visible (popover)) {
-    g_signal_emit_by_name (entry->url_entry, "move-suggestion", -1, G_TYPE_INT, 1, G_TYPE_NONE);
-  } else {
-    gtk_widget_child_focus (gtk_widget_get_toplevel (GTK_WIDGET (entry)), GTK_DIR_TAB_BACKWARD);
-  }
+  iface->get_delegate = ephy_location_entry_get_delegate;
 }
 
 static void
-position_func (DzlSuggestionEntry *self,
-               GdkRectangle       *area,
-               gboolean           *is_absolute,
-               gpointer            user_data)
+ephy_location_entry_dispose (GObject *object)
 {
-  GtkStyleContext *style_context;
-  GtkAllocation alloc;
-  GtkStateFlags state;
-  GtkBorder margin;
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
 
-  g_assert (DZL_IS_SUGGESTION_ENTRY (self));
-  g_assert (area != NULL);
-  g_assert (is_absolute != NULL);
+  g_clear_handle_id (&entry->progress_timeout, g_source_remove);
 
-  *is_absolute = FALSE;
+  if (entry->text)
+    gtk_editable_finish_delegate (GTK_EDITABLE (entry));
 
-  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+  ephy_location_entry_page_action_clear (entry);
 
-  area->y += alloc.height;
-  area->y += 6;
-  area->height = 300;
+  gtk_widget_unparent (entry->context_menu);
+  gtk_widget_unparent (entry->text);
+  gtk_widget_unparent (entry->progress);
+  gtk_widget_unparent (entry->security_button);
+  gtk_widget_unparent (entry->bookmark_button);
+  gtk_widget_unparent (entry->reader_mode_button);
+  gtk_widget_unparent (entry->suggestions_popover);
 
-  /* Adjust for bottom margin */
-  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  state = gtk_style_context_get_state (style_context);
-  gtk_style_context_get_margin (style_context, state, &margin);
+  g_clear_object (&entry->suggestions_model);
 
-  area->y -= margin.bottom;
-  area->x += margin.left;
-  area->width -= margin.left + margin.right;
+  G_OBJECT_CLASS (ephy_location_entry_parent_class)->dispose (object);
 }
 
 static void
-update_reader_icon (EphyLocationEntry *entry)
+ephy_location_entry_finalize (GObject *object)
 {
-  GtkIconTheme *theme;
-  const gchar *name;
-
-  theme = gtk_icon_theme_get_default ();
+  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
 
-  if (gtk_icon_theme_has_icon (theme, "view-reader-symbolic"))
-    name = "view-reader-symbolic";
-  else
-    name = "ephy-reader-mode-symbolic";
+  g_free (entry->saved_text);
+  g_free (entry->jump_tab);
 
-  gtk_image_set_from_icon_name (GTK_IMAGE (entry->reader_mode_icon),
-                                name, GTK_ICON_SIZE_MENU);
+  G_OBJECT_CLASS (ephy_location_entry_parent_class)->finalize (object);
 }
 
-static void
-reader_mode_clicked_cb (EphyLocationEntry *self)
+static inline void
+register_activate_shortcuts (GtkWidgetClass  *widget_class,
+                             GdkModifierType  modifiers)
 {
-  self->reader_mode_active = !self->reader_mode_active;
-
-  g_signal_emit (G_OBJECT (self), signals[READER_MODE_CHANGED], 0,
-                 self->reader_mode_active);
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_Return, modifiers,
+                                (GtkShortcutFunc)activate_shortcut_cb,
+                                "i", modifiers);
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_ISO_Enter, modifiers,
+                                (GtkShortcutFunc)activate_shortcut_cb,
+                                "i", modifiers);
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_KP_Enter, modifiers,
+                                (GtkShortcutFunc)activate_shortcut_cb,
+                                "i", modifiers);
 }
 
 static void
-activate_cb (EphyLocationEntry *self)
+ephy_location_entry_class_init (EphyLocationEntryClass *klass)
 {
-  GdkModifierType modifiers;
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
-  ephy_gui_get_current_event (NULL, &modifiers, NULL, NULL);
+  object_class->get_property = ephy_location_entry_get_property;
+  object_class->set_property = ephy_location_entry_set_property;
+  object_class->dispose = ephy_location_entry_dispose;
+  object_class->finalize = ephy_location_entry_finalize;
 
-  g_signal_emit (G_OBJECT (self), signals[ACTIVATE], 0, modifiers);
-}
+  widget_class->measure = ephy_location_entry_measure;
+  widget_class->size_allocate = ephy_location_entry_size_allocate;
+  widget_class->root = ephy_location_entry_root;
+  widget_class->unroot = ephy_location_entry_unroot;
 
-static void
-ephy_location_entry_construct_contents (EphyLocationEntry *entry)
-{
-  GtkWidget *event;
-  GtkWidget *box;
-  GtkStyleContext *context;
-  DzlShortcutController *controller;
-
-  LOG ("EphyLocationEntry constructing contents %p", entry);
-
-  /* Overlay */
-  entry->overlay = gtk_overlay_new ();
-  gtk_widget_show (GTK_WIDGET (entry->overlay));
-  gtk_container_add (GTK_CONTAINER (entry), entry->overlay);
-
-  /* URL entry */
-  entry->url_entry = dzl_suggestion_entry_new ();
-  dzl_suggestion_entry_set_compact (DZL_SUGGESTION_ENTRY (entry->url_entry), TRUE);
-  dzl_suggestion_entry_set_position_func (DZL_SUGGESTION_ENTRY (entry->url_entry), position_func, NULL, 
NULL);
-  gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry->url_entry), GTK_ENTRY_ICON_PRIMARY, _("Show website 
security status and permissions"));
-  gtk_entry_set_width_chars (GTK_ENTRY (entry->url_entry), 0);
-  gtk_entry_set_placeholder_text (GTK_ENTRY (entry->url_entry), _("Search for websites, bookmarks, and open 
tabs"));
-  g_signal_connect_swapped (entry->url_entry, "activate", G_CALLBACK (activate_cb), entry);
-
-  /* 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 (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);
-  g_signal_connect (G_OBJECT (entry->url_entry), "changed", G_CALLBACK (editable_changed_cb), entry);
-  g_signal_connect (G_OBJECT (entry->url_entry), "suggestion-selected", G_CALLBACK (suggestion_selected), 
entry);
-  gtk_widget_show (GTK_WIDGET (entry->url_entry));
-  gtk_container_add (GTK_CONTAINER (entry->overlay), GTK_WIDGET (entry->url_entry));
-
-  /* Custom hover state. FIXME: Remove this for GTK4 */
-  gtk_widget_add_events (GTK_WIDGET (entry->url_entry), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
-  g_signal_connect (G_OBJECT (entry->url_entry), "enter-notify-event", G_CALLBACK (enter_notify_cb), entry);
-  g_signal_connect (G_OBJECT (entry->url_entry), "leave-notify-event", G_CALLBACK (leave_notify_cb), entry);
-
-  /* Event box */
-  event = gtk_event_box_new ();
-  gtk_widget_set_halign (event, GTK_ALIGN_END);
-  gtk_widget_show (event);
-  g_signal_connect (G_OBJECT (event), "button-press-event", G_CALLBACK (event_button_press_event_cb), entry);
-  gtk_overlay_add_overlay (GTK_OVERLAY (entry->overlay), event);
-
-  /* Button Box */
-  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-  gtk_container_add (GTK_CONTAINER (event), box);
-  g_signal_connect (G_OBJECT (box), "size-allocate", G_CALLBACK (button_box_size_allocated_cb), entry);
-  gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
-  gtk_widget_set_halign (box, GTK_ALIGN_END);
-  gtk_widget_show (box);
-
-  /* Page action box */
-  entry->page_action_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-  gtk_box_pack_start (GTK_BOX (box), entry->page_action_box, FALSE, TRUE, 0);
-
-  context = gtk_widget_get_style_context (box);
-  gtk_style_context_add_class (context, "entry_icon_box");
-
-  /* Reader Mode */
-  entry->reader_mode_button = gtk_button_new_from_icon_name (NULL, GTK_ICON_SIZE_MENU);
-  gtk_widget_set_tooltip_text (entry->reader_mode_button, _("Toggle reader mode"));
-  entry->reader_mode_icon = gtk_button_get_image (GTK_BUTTON (entry->reader_mode_button));
-  gtk_box_pack_start (GTK_BOX (box), entry->reader_mode_button, FALSE, TRUE, 0);
-  g_signal_connect_swapped (entry->reader_mode_button, "clicked",
-                            G_CALLBACK (reader_mode_clicked_cb), entry);
-
-  context = gtk_widget_get_style_context (entry->reader_mode_icon);
-  gtk_style_context_add_class (context, "entry_icon");
+  g_object_class_override_property (object_class, PROP_ADDRESS, "address");
+  g_object_class_override_property (object_class, PROP_SECURITY_LEVEL, "security-level");
 
-  update_reader_icon (entry);
-  g_signal_connect_object (gtk_settings_get_default (), "notify::gtk-icon-theme-name",
-                           G_CALLBACK (update_reader_icon), entry, G_CONNECT_SWAPPED);
+  props[PROP_MODEL] =
+    g_param_spec_object ("model",
+                         "Model",
+                         "The model to be used for suggestions",
+                         G_TYPE_LIST_MODEL,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
 
-  /* Bookmark */
-  entry->bookmark_icon = gtk_image_new_from_icon_name ("non-starred-symbolic", GTK_ICON_SIZE_MENU);
-  context = gtk_widget_get_style_context (entry->bookmark_icon);
-  gtk_style_context_add_class (context, "entry_icon");
-  gtk_widget_show (entry->bookmark_icon);
+  props[PROP_SHOW_SUGGESTIONS] =
+    g_param_spec_boolean ("show-suggestions",
+                          "Show suggestions",
+                          "Whether to show the suggestions popover",
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
 
-  entry->bookmark_button = gtk_menu_button_new ();
-  gtk_container_add (GTK_CONTAINER (entry->bookmark_button), entry->bookmark_icon);
-  context = gtk_widget_get_style_context (entry->bookmark_button);
-  gtk_style_context_add_class (context, "image-button");
+  g_object_class_install_properties (object_class, LAST_PROP, props);
 
-  gtk_widget_set_tooltip_text (entry->bookmark_button, _("Bookmark this page"));
-  gtk_box_pack_start (GTK_BOX (box), entry->bookmark_button, FALSE, TRUE, 0);
+  gtk_editable_install_properties (object_class, LAST_TITLE_WIDGET_PROP);
 
-  g_settings_bind (EPHY_SETTINGS_LOCKDOWN,
-                   EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING,
-                   entry->bookmark_button,
-                   "visible",
-                   G_SETTINGS_BIND_GET | G_SETTINGS_BIND_INVERT_BOOLEAN);
+  /**
+   * EphyLocationEntry::activate:
+   * @flags: the #GdkModifierType from the activation event
+   *
+   * Emitted when the entry is activated.
+   *
+   */
+  signals[ACTIVATE] = g_signal_new ("activate", G_OBJECT_CLASS_TYPE (klass),
+                                    G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
+                                    0, NULL, NULL, NULL,
+                                    G_TYPE_NONE,
+                                    1,
+                                    GDK_TYPE_MODIFIER_TYPE);
 
-  g_object_connect (entry->url_entry,
-                    "signal::icon-press", G_CALLBACK (icon_button_icon_press_event_cb), entry,
-                    "signal::populate-popup", G_CALLBACK (entry_populate_popup_cb), entry,
-                    "signal::key-press-event", G_CALLBACK (entry_key_press_cb), entry,
-                    NULL);
-
-  g_signal_connect (entry->url_entry, "suggestion-activated",
-                    G_CALLBACK (ephy_location_entry_suggestion_activated), entry);
-
-  g_signal_connect (entry->url_entry, "button-release-event", G_CALLBACK (entry_button_release), entry);
-  g_signal_connect (entry->url_entry, "focus-in-event", G_CALLBACK (entry_focus_in_event), entry);
-  g_signal_connect (entry->url_entry, "focus-out-event", G_CALLBACK (entry_focus_out_event), entry);
-
-  controller = dzl_shortcut_controller_find (entry->url_entry);
-  dzl_shortcut_controller_add_command_callback (controller,
-                                                "org.gnome.Epiphany.complete-url-forward",
-                                                "Tab",
-                                                DZL_SHORTCUT_PHASE_DISPATCH,
-                                                handle_forward_tab_key,
-                                                entry,
-                                                NULL);
-
-  dzl_shortcut_controller_add_command_callback (controller,
-                                                "org.gnome.Epiphany.complete-url-backward",
-                                                "ISO_Left_Tab",
-                                                DZL_SHORTCUT_PHASE_DISPATCH,
-                                                handle_backward_tab_key,
-                                                entry,
-                                                NULL);
-}
+  /**
+   * EphyLocationEntry::user-changed:
+   * @entry: the object on which the signal is emitted
+   *
+   * Emitted when the user changes the contents of the internal #GtkEntry
+   *
+   */
+  signals[USER_CHANGED] = g_signal_new ("user_changed", G_OBJECT_CLASS_TYPE (klass),
+                                        G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
+                                        0, NULL, NULL, NULL,
+                                        G_TYPE_NONE,
+                                        1,
+                                        G_TYPE_STRING);
 
-static void
-ephy_location_entry_init (EphyLocationEntry *le)
-{
-  LOG ("EphyLocationEntry initialising %p", le);
+  /**
+   * EphyLocationEntry::reader-mode-changed:
+   * @entry: the object on which the signal is emitted
+   * @active: whether reader mode is active
+   *
+   * Emitted when the user clicks the reader mode icon inside the
+   * #EphyLocationEntry.
+   *
+   */
+  signals[READER_MODE_CHANGED] = g_signal_new ("reader-mode-changed", G_OBJECT_CLASS_TYPE (klass),
+                                               G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
+                                               0, NULL, NULL, NULL,
+                                               G_TYPE_NONE,
+                                               1,
+                                               G_TYPE_BOOLEAN);
 
-  le->user_changed = FALSE;
-  le->block_update = FALSE;
-  le->button_release_is_blocked = FALSE;
-  le->saved_text = NULL;
+  /**
+   * EphyLocationEntry::get-location:
+   * @entry: the object on which the signal is emitted
+   * Returns: the current page address as a string
+   *
+   * For drag and drop purposes, the location bar will request you the
+   * real address of where it is pointing to. The signal handler for this
+   * function should return the address of the currently loaded site.
+   *
+   */
+  signals[GET_LOCATION] = g_signal_new ("get-location", G_OBJECT_CLASS_TYPE (klass),
+                                        G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
+                                        0, ephy_signal_accumulator_string,
+                                        NULL, NULL,
+                                        G_TYPE_STRING,
+                                        0,
+                                        G_TYPE_NONE);
 
-  ephy_location_entry_construct_contents (le);
-}
+  /**
+   * EphyLocationEntry::get-title:
+   * @entry: the object on which the signal is emitted
+   * Returns: the current page title as a string
+   *
+   * For drag and drop purposes, the location bar will request you the
+   * title of where it is pointing to. The signal handler for this
+   * function should return the title of the currently loaded site.
+   *
+   */
+  signals[GET_TITLE] = g_signal_new ("get-title", G_OBJECT_CLASS_TYPE (klass),
+                                     G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
+                                     0, ephy_signal_accumulator_string,
+                                     NULL, NULL,
+                                     G_TYPE_STRING,
+                                     0,
+                                     G_TYPE_NONE);
 
-GtkWidget *
-ephy_location_entry_new (void)
-{
-  return GTK_WIDGET (g_object_new (EPHY_TYPE_LOCATION_ENTRY, NULL));
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/epiphany/gtk/location-entry.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, EphyLocationEntry, text);
+  gtk_widget_class_bind_template_child (widget_class, EphyLocationEntry, progress);
+  gtk_widget_class_bind_template_child (widget_class, EphyLocationEntry, security_button);
+  gtk_widget_class_bind_template_child (widget_class, EphyLocationEntry, bookmark_button);
+  gtk_widget_class_bind_template_child (widget_class, EphyLocationEntry, reader_mode_button);
+  gtk_widget_class_bind_template_child (widget_class, EphyLocationEntry, suggestions_popover);
+  gtk_widget_class_bind_template_child (widget_class, EphyLocationEntry, suggestions_model);
+  gtk_widget_class_bind_template_child (widget_class, EphyLocationEntry, context_menu);
+
+  gtk_widget_class_bind_template_callback (widget_class, editable_changed_cb);
+  gtk_widget_class_bind_template_callback (widget_class, update_actions);
+  gtk_widget_class_bind_template_callback (widget_class, activate_cb);
+  gtk_widget_class_bind_template_callback (widget_class, cut_clipboard_cb);
+  gtk_widget_class_bind_template_callback (widget_class, copy_clipboard_cb);
+  gtk_widget_class_bind_template_callback (widget_class, reader_mode_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class, suggestion_activated_cb);
+  gtk_widget_class_bind_template_callback (widget_class, update_suggestions_popover);
+  gtk_widget_class_bind_template_callback (widget_class, focus_enter_cb);
+  gtk_widget_class_bind_template_callback (widget_class, focus_leave_cb);
+  gtk_widget_class_bind_template_callback (widget_class, click_pressed_cb);
+  gtk_widget_class_bind_template_callback (widget_class, click_released_cb);
+  gtk_widget_class_bind_template_callback (widget_class, long_press_cb);
+  gtk_widget_class_bind_template_callback (widget_class, key_pressed_cb);
+  gtk_widget_class_bind_template_callback (widget_class, text_pressed_cb);
+  gtk_widget_class_bind_template_callback (widget_class, get_suggestion_icon);
+  gtk_widget_class_bind_template_callback (widget_class, get_suggestion_secondary_icon);
+
+  gtk_widget_class_set_css_name (widget_class, "entry");
+
+  gtk_widget_class_install_action (widget_class, "clipboard.paste-and-go", NULL,
+                                   (GtkWidgetActionActivateFunc)paste_and_go_activate);
+  gtk_widget_class_install_action (widget_class, "edit.clear", NULL,
+                                   (GtkWidgetActionActivateFunc)clear_activate);
+  gtk_widget_class_install_action (widget_class, "edit.undo-extra", NULL,
+                                   (GtkWidgetActionActivateFunc)ephy_location_entry_reset);
+  gtk_widget_class_install_action (widget_class, "edit.redo-extra", NULL,
+                                   (GtkWidgetActionActivateFunc)ephy_location_entry_undo_reset);
+  gtk_widget_class_install_action (widget_class, "menu.popup-extra", NULL,
+                                   (GtkWidgetActionActivateFunc)menu_popup_activate);
+
+  register_activate_shortcuts (widget_class, GDK_CONTROL_MASK);
+  register_activate_shortcuts (widget_class, GDK_ALT_MASK);
+  register_activate_shortcuts (widget_class, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+  register_activate_shortcuts (widget_class, GDK_SHIFT_MASK | GDK_ALT_MASK);
+
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_Escape, 0,
+                                (GtkShortcutFunc)ephy_location_entry_reset,
+                                NULL);
 }
 
-typedef struct {
-  GUri *uri;
-  EphyLocationEntry *entry;
-} PrefetchHelper;
-
 static void
-free_prefetch_helper (PrefetchHelper *helper)
+ephy_location_entry_init (EphyLocationEntry *entry)
 {
-  g_uri_unref (helper->uri);
-  g_object_unref (helper->entry);
-  g_free (helper);
-}
+  GdkClipboard *clipboard;
 
-static gboolean
-do_dns_prefetch (PrefetchHelper *helper)
-{
-  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+  LOG ("EphyLocationEntry initialising %p", entry);
 
-  if (helper->uri)
-    webkit_web_context_prefetch_dns (ephy_embed_shell_get_web_context (shell), g_uri_get_host (helper->uri));
+  entry->user_changed = FALSE;
+  entry->block_update = FALSE;
+  entry->saved_text = NULL;
+  entry->page_actions = NULL;
 
-  helper->entry->dns_prefetch_handle_id = 0;
+  gtk_widget_init_template (GTK_WIDGET (entry));
 
-  return G_SOURCE_REMOVE;
-}
+  gtk_menu_button_set_create_popup_func (GTK_MENU_BUTTON (entry->security_button),
+                                         (GtkMenuButtonCreatePopupFunc)create_security_popup_cb,
+                                         entry,
+                                         NULL);
 
-/*
- * Note: As we do not have access to WebKitNetworkProxyMode, and because
- * Epiphany does not ever change it, we are just checking system default proxy.
- */
-static void
-proxy_resolver_ready_cb (GObject      *object,
-                         GAsyncResult *result,
-                         gpointer      user_data)
-{
-  PrefetchHelper *helper = user_data;
-  GProxyResolver *resolver = G_PROXY_RESOLVER (object);
-  g_autoptr (GError) error = NULL;
-  g_auto (GStrv) proxies = NULL;
+  g_settings_bind (EPHY_SETTINGS_LOCKDOWN,
+                   EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING,
+                   entry->bookmark_button,
+                   "visible",
+                   G_SETTINGS_BIND_GET | G_SETTINGS_BIND_INVERT_BOOLEAN);
 
-  proxies = g_proxy_resolver_lookup_finish (resolver, result, &error);
-  if (error != NULL) {
-    free_prefetch_helper (helper);
-    return;
-  }
+  update_reader_icon (entry);
+  g_signal_connect_object (gtk_settings_get_default (), "notify::gtk-icon-theme-name",
+                           G_CALLBACK (update_reader_icon), entry, G_CONNECT_SWAPPED);
 
-  if (proxies != NULL && (g_strv_length (proxies) > 1 || g_strcmp0 (proxies[0], "direct://") != 0)) {
-    free_prefetch_helper (helper);
-    return;
-  }
+  gtk_editable_init_delegate (GTK_EDITABLE (entry));
 
-  g_clear_handle_id (&helper->entry->dns_prefetch_handle_id, g_source_remove);
-  helper->entry->dns_prefetch_handle_id =
-    g_timeout_add_full (G_PRIORITY_DEFAULT,
-                        250,
-                        (GSourceFunc)do_dns_prefetch,
-                        helper,
-                        (GDestroyNotify)free_prefetch_helper);
-  g_source_set_name_by_id (helper->entry->dns_prefetch_handle_id, "[epiphany] do_dns_prefetch");
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (entry));
+  update_actions (entry);
+  g_signal_connect_object (clipboard, "changed", G_CALLBACK (update_actions),
+                           entry, G_CONNECT_SWAPPED);
 }
 
-static void
-schedule_dns_prefetch (EphyLocationEntry *entry,
-                       const gchar       *url)
+GtkWidget *
+ephy_location_entry_new (void)
 {
-  GProxyResolver *resolver = g_proxy_resolver_get_default ();
-  PrefetchHelper *helper;
-  g_autoptr (GUri) uri = NULL;
-
-  if (resolver == NULL)
-    return;
-
-  uri = g_uri_parse (url, G_URI_FLAGS_NONE, NULL);
-  if (!uri || !g_uri_get_host (uri))
-    return;
-
-  helper = g_new0 (PrefetchHelper, 1);
-  helper->entry = g_object_ref (entry);
-  helper->uri = g_steal_pointer (&uri);
-
-  g_proxy_resolver_lookup_async (resolver, url, NULL, proxy_resolver_ready_cb, helper);
+  return GTK_WIDGET (g_object_new (EPHY_TYPE_LOCATION_ENTRY, NULL));
 }
 
 /**
@@ -1329,11 +1500,12 @@ ephy_location_entry_get_can_redo (EphyLocationEntry *entry)
 void
 ephy_location_entry_undo_reset (EphyLocationEntry *entry)
 {
-  g_signal_handlers_block_by_func (entry->url_entry, G_CALLBACK (editable_changed_cb), entry);
-  gtk_entry_set_text (GTK_ENTRY (entry->url_entry), entry->saved_text);
-  g_signal_handlers_unblock_by_func (entry->url_entry, G_CALLBACK (editable_changed_cb), entry);
+  g_signal_handlers_block_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
+  gtk_editable_set_text (GTK_EDITABLE (entry), entry->saved_text);
+  g_signal_handlers_unblock_by_func (entry, G_CALLBACK (editable_changed_cb), entry);
   entry->can_redo = FALSE;
   entry->user_changed = TRUE;
+  update_actions (entry);
 }
 
 /**
@@ -1355,7 +1527,7 @@ ephy_location_entry_reset (EphyLocationEntry *entry)
 
   g_signal_emit (entry, signals[GET_LOCATION], 0, &url);
   text = url != NULL ? url : "";
-  old_text = gtk_entry_get_text (GTK_ENTRY (entry->url_entry));
+  old_text = gtk_editable_get_text (GTK_EDITABLE (entry));
   old_text = old_text != NULL ? old_text : "";
 
   g_free (entry->saved_text);
@@ -1365,6 +1537,7 @@ ephy_location_entry_reset (EphyLocationEntry *entry)
   ephy_title_widget_set_address (EPHY_TITLE_WIDGET (entry), text);
 
   entry->user_changed = FALSE;
+  update_actions (entry);
 
   return g_strcmp0 (text, old_text);
 }
@@ -1380,45 +1553,36 @@ ephy_location_entry_reset (EphyLocationEntry *entry)
 void
 ephy_location_entry_focus (EphyLocationEntry *entry)
 {
-  gtk_widget_grab_focus (GTK_WIDGET (entry->url_entry));
+  gtk_widget_grab_focus (entry->text);
 }
 
 void
 ephy_location_entry_set_bookmark_icon_state (EphyLocationEntry     *self,
                                              EphyBookmarkIconState  state)
 {
-  GtkStyleContext *context;
-
   self->icon_state = state;
 
   g_assert (EPHY_IS_LOCATION_ENTRY (self));
 
-  context = gtk_widget_get_style_context (GTK_WIDGET (self->bookmark_icon));
-
   if (self->adaptive_mode == EPHY_ADAPTIVE_MODE_NARROW)
     state = EPHY_BOOKMARK_ICON_HIDDEN;
 
   switch (state) {
     case EPHY_BOOKMARK_ICON_HIDDEN:
-      gtk_widget_set_visible (self->bookmark_button, FALSE);
-      gtk_style_context_remove_class (context, "starred");
-      gtk_style_context_remove_class (context, "non-starred");
+      gtk_widget_hide (self->bookmark_button);
+      gtk_widget_remove_css_class (self->bookmark_button, "starred");
       break;
     case EPHY_BOOKMARK_ICON_EMPTY:
-      gtk_widget_set_visible (self->bookmark_button, TRUE);
-      gtk_image_set_from_icon_name (GTK_IMAGE (self->bookmark_icon),
-                                    "non-starred-symbolic",
-                                    GTK_ICON_SIZE_MENU);
-      gtk_style_context_remove_class (context, "starred");
-      gtk_style_context_add_class (context, "non-starred");
+      gtk_widget_show (self->bookmark_button);
+      gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (self->bookmark_button),
+                                     "non-starred-symbolic");
+      gtk_widget_remove_css_class (self->bookmark_button, "starred");
       break;
     case EPHY_BOOKMARK_ICON_BOOKMARKED:
-      gtk_widget_set_visible (self->bookmark_button, TRUE);
-      gtk_image_set_from_icon_name (GTK_IMAGE (self->bookmark_icon),
-                                    "starred-symbolic",
-                                    GTK_ICON_SIZE_MENU);
-      gtk_style_context_remove_class (context, "non-starred");
-      gtk_style_context_add_class (context, "starred");
+      gtk_widget_show (self->bookmark_button);
+      gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (self->bookmark_button),
+                                     "starred-symbolic");
+      gtk_widget_add_css_class (self->bookmark_button, "starred");
       break;
     default:
       g_assert_not_reached ();
@@ -1437,9 +1601,7 @@ void
 ephy_location_entry_set_lock_tooltip (EphyLocationEntry *entry,
                                       const char        *tooltip)
 {
-  gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry->url_entry),
-                                   GTK_ENTRY_ICON_PRIMARY,
-                                   tooltip);
+  gtk_widget_set_tooltip_text (entry->security_button, tooltip);
 }
 
 void
@@ -1456,15 +1618,7 @@ ephy_location_entry_set_add_bookmark_popover (EphyLocationEntry *entry,
 void
 ephy_location_entry_show_add_bookmark_popover (EphyLocationEntry *entry)
 {
-  GtkPopover *popover = gtk_menu_button_get_popover (GTK_MENU_BUTTON (entry->bookmark_button));
-
-  gtk_popover_popup (popover);
-}
-
-GtkWidget *
-ephy_location_entry_get_entry (EphyLocationEntry *entry)
-{
-  return GTK_WIDGET (entry->url_entry);
+  gtk_menu_button_popup (GTK_MENU_BUTTON (entry->bookmark_button));
 }
 
 void
@@ -1478,10 +1632,7 @@ void
 ephy_location_entry_set_reader_mode_state (EphyLocationEntry *entry,
                                            gboolean           active)
 {
-  if (active)
-    gtk_style_context_add_class (gtk_widget_get_style_context (entry->reader_mode_icon), "selected");
-  else
-    gtk_style_context_remove_class (gtk_widget_get_style_context (entry->reader_mode_icon), "selected");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (entry->reader_mode_button), active);
 
   entry->reader_mode_active = active;
 }
@@ -1497,7 +1648,7 @@ progress_hide (gpointer user_data)
 {
   EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (user_data);
 
-  gtk_entry_set_progress_fraction (GTK_ENTRY (entry->url_entry), 0);
+  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (entry->progress), 0);
 
   g_clear_handle_id (&entry->progress_timeout, g_source_remove);
 
@@ -1513,7 +1664,7 @@ ephy_location_entry_set_fraction_internal (gpointer user_data)
   gdouble current;
 
   entry->progress_timeout = 0;
-  current = gtk_entry_get_progress_fraction (GTK_ENTRY (entry->url_entry));
+  current = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (entry->progress));
 
   if ((entry->progress_fraction - current) > 0.5 || entry->progress_fraction == 1.0)
     ms = 10;
@@ -1522,10 +1673,10 @@ ephy_location_entry_set_fraction_internal (gpointer user_data)
 
   progress = current + 0.025;
   if (progress < entry->progress_fraction) {
-    gtk_entry_set_progress_fraction (GTK_ENTRY (entry->url_entry), progress);
+    gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (entry->progress), progress);
     entry->progress_timeout = g_timeout_add (ms, ephy_location_entry_set_fraction_internal, entry);
   } else {
-    gtk_entry_set_progress_fraction (GTK_ENTRY (entry->url_entry), entry->progress_fraction);
+    gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (entry->progress), entry->progress_fraction);
     if (entry->progress_fraction == 1.0)
       entry->progress_timeout = g_timeout_add (500, progress_hide, entry);
   }
@@ -1546,9 +1697,9 @@ ephy_location_entry_set_progress (EphyLocationEntry *entry,
     /* Setting progress to 0 when it is already 0 can actually cause the
      * progress bar to be shown. Yikes....
      */
-    current_progress = gtk_entry_get_progress_fraction (GTK_ENTRY (entry->url_entry));
+    current_progress = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (entry->progress));
     if (current_progress != 0.0)
-      gtk_entry_set_progress_fraction (GTK_ENTRY (entry->url_entry), 0.0);
+      gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (entry->progress), 0.0);
     return;
   }
 
@@ -1560,14 +1711,12 @@ void
 ephy_location_entry_set_adaptive_mode (EphyLocationEntry *entry,
                                        EphyAdaptiveMode   adaptive_mode)
 {
-  if (adaptive_mode == EPHY_ADAPTIVE_MODE_NARROW)
-    dzl_suggestion_entry_set_position_func (DZL_SUGGESTION_ENTRY (entry->url_entry), 
dzl_suggestion_entry_window_position_func, NULL, NULL);
-  else
-    dzl_suggestion_entry_set_position_func (DZL_SUGGESTION_ENTRY (entry->url_entry), position_func, NULL, 
NULL);
-
   entry->adaptive_mode = adaptive_mode;
 
-  update_entry_style (entry);
+  if (adaptive_mode == EPHY_ADAPTIVE_MODE_NARROW)
+    gtk_widget_add_css_class (GTK_WIDGET (entry), "narrow");
+  else
+    gtk_widget_remove_css_class (GTK_WIDGET (entry), "narrow");
 
   ephy_location_entry_set_bookmark_icon_state (entry, entry->icon_state);
 }
@@ -1576,24 +1725,46 @@ void
 ephy_location_entry_page_action_add (EphyLocationEntry *entry,
                                      GtkWidget         *action)
 {
-  gtk_box_pack_end (GTK_BOX (entry->page_action_box), action, FALSE, TRUE, 0);
+  entry->page_actions = g_list_prepend (entry->page_actions, action);
 
-  gtk_widget_show (entry->page_action_box);
+  gtk_widget_set_parent (action, GTK_WIDGET (entry));
 }
 
-static void
-clear_page_actions (GtkWidget *child,
-                    gpointer   user_data)
+void
+ephy_location_entry_page_action_remove (EphyLocationEntry *entry,
+                                        GtkWidget         *action)
 {
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (user_data);
+  entry->page_actions = g_list_remove (entry->page_actions, action);
 
-  gtk_container_remove (GTK_CONTAINER (entry->page_action_box), child);
+  gtk_widget_unparent (action);
 }
 
 void
 ephy_location_entry_page_action_clear (EphyLocationEntry *entry)
 {
-  gtk_container_foreach (GTK_CONTAINER (entry->page_action_box), clear_page_actions, entry);
+  if (entry->page_actions)
+    g_list_free_full (entry->page_actions, (GDestroyNotify)gtk_widget_unparent);
+
+  entry->page_actions = NULL;
+}
+
+void
+ephy_location_entry_grab_focus_without_selecting (EphyLocationEntry *entry)
+{
+  gtk_text_grab_focus_without_selecting (GTK_TEXT (entry->text));
+}
+
+GListModel *
+ephy_location_entry_get_model (EphyLocationEntry *entry)
+{
+  return gtk_single_selection_get_model (entry->suggestions_model);
+}
+
+void
+ephy_location_entry_set_model (EphyLocationEntry *entry,
+                               GListModel        *model)
+{
+  gtk_single_selection_set_model (entry->suggestions_model, model);
 
-  gtk_widget_hide (entry->page_action_box);
+  g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_MODEL]);
 }
diff --git a/lib/widgets/ephy-location-entry.h b/lib/widgets/ephy-location-entry.h
index 516ca9a1f..eb726c1df 100644
--- a/lib/widgets/ephy-location-entry.h
+++ b/lib/widgets/ephy-location-entry.h
@@ -33,7 +33,7 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_LOCATION_ENTRY (ephy_location_entry_get_type())
 
-G_DECLARE_FINAL_TYPE (EphyLocationEntry, ephy_location_entry, EPHY, LOCATION_ENTRY, GtkBin)
+G_DECLARE_FINAL_TYPE (EphyLocationEntry, ephy_location_entry, EPHY, LOCATION_ENTRY, GtkWidget)
 
 
 GtkWidget      *ephy_location_entry_new                        (void);
@@ -59,8 +59,6 @@ void            ephy_location_entry_set_add_bookmark_popover   (EphyLocationEntr
 
 void            ephy_location_entry_show_add_bookmark_popover  (EphyLocationEntry *entry);
 
-GtkWidget      *ephy_location_entry_get_entry                  (EphyLocationEntry *entry);
-
 void            ephy_location_entry_set_reader_mode_visible    (EphyLocationEntry *entry,
                                                                 gboolean           visible);
 
@@ -74,10 +72,18 @@ void            ephy_location_entry_set_progress               (EphyLocationEntr
                                                                 gboolean           loading);
 void            ephy_location_entry_page_action_add            (EphyLocationEntry *entry,
                                                                 GtkWidget         *action);
+void            ephy_location_entry_page_action_remove         (EphyLocationEntry *entry,
+                                                                GtkWidget         *action);
 
 void            ephy_location_entry_page_action_clear          (EphyLocationEntry *entry);
 
 void            ephy_location_entry_set_adaptive_mode          (EphyLocationEntry *entry,
                                                                 EphyAdaptiveMode   adaptive_mode);
 
+void            ephy_location_entry_grab_focus_without_selecting (EphyLocationEntry *entry);
+
+GListModel     *ephy_location_entry_get_model                  (EphyLocationEntry *entry);
+void            ephy_location_entry_set_model                  (EphyLocationEntry *entry,
+                                                                GListModel        *model);
+
 G_END_DECLS
diff --git a/lib/widgets/ephy-search-entry.c b/lib/widgets/ephy-search-entry.c
new file mode 100644
index 000000000..6d6ff8194
--- /dev/null
+++ b/lib/widgets/ephy-search-entry.c
@@ -0,0 +1,497 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2022 Purism SPC
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-search-entry.h"
+#include "ephy-widgets-type-builtins.h"
+
+#include <glib/gi18n.h>
+
+struct _EphySearchEntry {
+  GtkWidget parent_instance;
+
+  GtkWidget *text;
+  GtkWidget *search_icon;
+  GtkWidget *clear_icon;
+  GtkWidget *matches_label;
+
+  gboolean show_matches;
+  guint n_matches;
+  guint current_match;
+  EphyFindResult find_result;
+};
+
+enum {
+  PROP_0,
+  PROP_PLACEHOLDER_TEXT,
+  PROP_SHOW_MATCHES,
+  PROP_N_MATCHES,
+  PROP_CURRENT_MATCH,
+  PROP_FIND_RESULT,
+  LAST_PROP
+};
+
+static GParamSpec *props[LAST_PROP] = {};
+
+enum {
+  NEXT_MATCH,
+  PREVIOUS_MATCH,
+  STOP_SEARCH,
+  LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL] = {};
+
+static void ephy_search_entry_editable_init (GtkEditableInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EphySearchEntry, ephy_search_entry, GTK_TYPE_WIDGET,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
+                                                ephy_search_entry_editable_init))
+
+static void
+update_matches (EphySearchEntry *self)
+{
+  g_autofree gchar *label = NULL;
+
+  label = g_strdup_printf ("%u/%u", self->current_match, self->n_matches);
+
+  gtk_label_set_label (GTK_LABEL (self->matches_label), label);
+}
+
+static void
+text_changed_cb (GtkEditable     *editable,
+                 EphySearchEntry *self)
+{
+  const char *text = gtk_editable_get_text (editable);
+
+  gtk_widget_set_visible (self->clear_icon, text && *text);
+}
+
+static void
+clear_icon_pressed_cb (GtkGesture *gesture)
+{
+  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
+}
+
+static void
+clear_icon_released_cb (EphySearchEntry *self)
+{
+  gtk_editable_set_text (GTK_EDITABLE (self), "");
+}
+
+static void
+activate_cb (EphySearchEntry *self)
+{
+  g_signal_emit (G_OBJECT (self), signals[NEXT_MATCH], 0);
+}
+
+static gboolean
+ephy_search_entry_grab_focus (GtkWidget *widget)
+{
+  EphySearchEntry *self = EPHY_SEARCH_ENTRY (widget);
+
+  return gtk_widget_grab_focus (self->text);
+}
+
+static void
+ephy_search_entry_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  EphySearchEntry *self = EPHY_SEARCH_ENTRY (object);
+
+  if (gtk_editable_delegate_set_property (object, prop_id, value, pspec)) {
+    if (prop_id == LAST_PROP + GTK_EDITABLE_PROP_EDITABLE) {
+      gtk_accessible_update_property (GTK_ACCESSIBLE (self),
+                                      GTK_ACCESSIBLE_PROPERTY_READ_ONLY, !g_value_get_boolean (value),
+                                      -1);
+    }
+    return;
+  }
+
+  switch (prop_id) {
+    case PROP_PLACEHOLDER_TEXT:
+      ephy_search_entry_set_placeholder_text (self, g_value_get_string (value));
+      break;
+    case PROP_SHOW_MATCHES:
+      ephy_search_entry_set_show_matches (self, g_value_get_boolean (value));
+      break;
+    case PROP_N_MATCHES:
+      ephy_search_entry_set_n_matches (self, g_value_get_uint (value));
+      break;
+    case PROP_CURRENT_MATCH:
+      ephy_search_entry_set_current_match (self, g_value_get_uint (value));
+      break;
+    case PROP_FIND_RESULT:
+      ephy_search_entry_set_find_result (self, g_value_get_enum (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_search_entry_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  EphySearchEntry *self = EPHY_SEARCH_ENTRY (object);
+
+  if (gtk_editable_delegate_get_property (object, prop_id, value, pspec))
+    return;
+
+  switch (prop_id) {
+    case PROP_PLACEHOLDER_TEXT:
+      g_value_set_string (value, ephy_search_entry_get_placeholder_text (self));
+      break;
+    case PROP_SHOW_MATCHES:
+      g_value_set_boolean (value, ephy_search_entry_get_show_matches (self));
+      break;
+    case PROP_N_MATCHES:
+      g_value_set_uint (value, ephy_search_entry_get_n_matches (self));
+      break;
+    case PROP_CURRENT_MATCH:
+      g_value_set_uint (value, ephy_search_entry_get_current_match (self));
+      break;
+    case PROP_FIND_RESULT:
+      g_value_set_enum (value, ephy_search_entry_get_find_result (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_search_entry_dispose (GObject *object)
+{
+  EphySearchEntry *self = EPHY_SEARCH_ENTRY (object);
+
+  if (self->text)
+    gtk_editable_finish_delegate (GTK_EDITABLE (self));
+
+  g_clear_pointer (&self->search_icon, gtk_widget_unparent);
+  g_clear_pointer (&self->text, gtk_widget_unparent);
+  g_clear_pointer (&self->clear_icon, gtk_widget_unparent);
+  g_clear_pointer (&self->matches_label, gtk_widget_unparent);
+
+  G_OBJECT_CLASS (ephy_search_entry_parent_class)->dispose (object);
+}
+
+static void
+ephy_search_entry_class_init (EphySearchEntryClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->get_property = ephy_search_entry_get_property;
+  object_class->set_property = ephy_search_entry_set_property;
+  object_class->dispose = ephy_search_entry_dispose;
+
+  widget_class->grab_focus = ephy_search_entry_grab_focus;
+
+  props[PROP_PLACEHOLDER_TEXT] =
+    g_param_spec_string ("placeholder-text",
+                         "Placeholder text",
+                         "Show text in the entry when it’s empty and unfocused",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  props[PROP_SHOW_MATCHES] =
+    g_param_spec_boolean ("show-matches",
+                          "Show matches",
+                          "Whether to show search matches",
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  props[PROP_N_MATCHES] =
+    g_param_spec_uint ("n-matches",
+                       "Number of matches",
+                       "The total number of search matches",
+                       0, G_MAXUINT, 0,
+                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  props[PROP_CURRENT_MATCH] =
+    g_param_spec_uint ("current-match",
+                       "Current match",
+                       "The current search match",
+                       0, G_MAXUINT, 0,
+                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  props[PROP_FIND_RESULT] =
+    g_param_spec_enum ("find-result",
+                       "Find result",
+                       "The current state",
+                       EPHY_TYPE_FIND_RESULT,
+                       EPHY_FIND_RESULT_FOUND,
+                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, LAST_PROP, props);
+  gtk_editable_install_properties (object_class, LAST_PROP);
+
+  signals[NEXT_MATCH] =
+    g_signal_new ("next-match",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
+  signals[PREVIOUS_MATCH] =
+    g_signal_new ("previous-match",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
+  signals[STOP_SEARCH] =
+    g_signal_new ("stop-search",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
+  gtk_widget_class_set_css_name (widget_class, "entry");
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
+
+  gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_g,
+                                       GDK_CONTROL_MASK,
+                                       "next-match", NULL);
+  gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_g,
+                                       GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                       "previous-match", NULL);
+  gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Escape, 0,
+                                       "stop-search", NULL);
+
+  gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return,
+                                       GDK_SHIFT_MASK,
+                                       "previous-match", NULL);
+  gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter,
+                                       GDK_SHIFT_MASK,
+                                       "previous-match", NULL);
+  gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter,
+                                       GDK_SHIFT_MASK,
+                                       "previous-match", NULL);
+}
+
+static void
+ephy_search_entry_init (EphySearchEntry *self)
+{
+  GtkGesture *gesture;
+
+  self->find_result = EPHY_FIND_RESULT_FOUND;
+
+  /* Icon */
+  self->search_icon = g_object_new (GTK_TYPE_IMAGE,
+                                    "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
+                                    "icon-name", "edit-find-symbolic",
+                                    NULL);
+  gtk_widget_set_parent (self->search_icon, GTK_WIDGET (self));
+
+  /* Text */
+  self->text = gtk_text_new ();
+  gtk_widget_set_hexpand (self->text, TRUE);
+  gtk_widget_set_parent (self->text, GTK_WIDGET (self));
+  g_signal_connect_after (self->text, "changed", G_CALLBACK (text_changed_cb), self);
+  g_signal_connect_swapped (self->text, "activate", G_CALLBACK (activate_cb), self);
+
+  /* Clear */
+  self->clear_icon = g_object_new (GTK_TYPE_IMAGE,
+                                   "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
+                                   "icon-name", "edit-clear-symbolic",
+                                   NULL);
+  gtk_widget_hide (self->clear_icon);
+  gtk_widget_set_parent (self->clear_icon, GTK_WIDGET (self));
+
+  gesture = gtk_gesture_click_new ();
+  g_signal_connect (gesture, "pressed", G_CALLBACK (clear_icon_pressed_cb), self);
+  g_signal_connect_swapped (gesture, "released", G_CALLBACK (clear_icon_released_cb), self);
+  gtk_widget_add_controller (self->clear_icon, GTK_EVENT_CONTROLLER (gesture));
+
+  /* Matches */
+  self->matches_label = gtk_label_new (NULL);
+  gtk_widget_add_css_class (self->matches_label, "dim-label");
+  gtk_widget_add_css_class (self->matches_label, "numeric");
+  gtk_widget_hide (self->matches_label);
+  gtk_widget_set_parent (self->matches_label, GTK_WIDGET (self));
+
+  gtk_editable_init_delegate (GTK_EDITABLE (self));
+  gtk_widget_set_hexpand (GTK_WIDGET (self), FALSE);
+
+  update_matches (self);
+}
+
+static GtkEditable *
+ephy_search_entry_get_delegate (GtkEditable *editable)
+{
+  EphySearchEntry *self = EPHY_SEARCH_ENTRY (editable);
+
+  return GTK_EDITABLE (self->text);
+}
+
+static void
+ephy_search_entry_editable_init (GtkEditableInterface *iface)
+{
+  iface->get_delegate = ephy_search_entry_get_delegate;
+}
+
+GtkWidget *
+ephy_search_entry_new (void)
+{
+  return GTK_WIDGET (g_object_new (EPHY_TYPE_SEARCH_ENTRY, NULL));
+}
+
+const char *
+ephy_search_entry_get_placeholder_text (EphySearchEntry *self)
+{
+  g_return_val_if_fail (EPHY_IS_SEARCH_ENTRY (self), NULL);
+
+  return gtk_text_get_placeholder_text (GTK_TEXT (self->text));
+}
+
+void
+ephy_search_entry_set_placeholder_text (EphySearchEntry *self,
+                                        const char      *placeholder_text)
+{
+  g_return_if_fail (EPHY_IS_SEARCH_ENTRY (self));
+
+  if (!g_strcmp0 (placeholder_text, ephy_search_entry_get_placeholder_text (self)))
+    return;
+
+  gtk_text_set_placeholder_text (GTK_TEXT (self->text), placeholder_text);
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_PLACEHOLDER_TEXT]);
+}
+
+gboolean
+ephy_search_entry_get_show_matches (EphySearchEntry *self)
+{
+  g_return_val_if_fail (EPHY_IS_SEARCH_ENTRY (self), 0);
+
+  return self->show_matches;
+}
+
+void
+ephy_search_entry_set_show_matches (EphySearchEntry *self,
+                                    gboolean         show_matches)
+{
+  g_return_if_fail (EPHY_IS_SEARCH_ENTRY (self));
+
+  if (self->show_matches == show_matches)
+    return;
+
+  self->show_matches = show_matches;
+
+  gtk_widget_set_visible (self->matches_label, show_matches);
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SHOW_MATCHES]);
+}
+
+guint
+ephy_search_entry_get_n_matches (EphySearchEntry *self)
+{
+  g_return_val_if_fail (EPHY_IS_SEARCH_ENTRY (self), 0);
+
+  return self->n_matches;
+}
+
+void
+ephy_search_entry_set_n_matches (EphySearchEntry *self,
+                                 guint            n_matches)
+{
+  g_return_if_fail (EPHY_IS_SEARCH_ENTRY (self));
+
+  if (self->n_matches == n_matches)
+    return;
+
+  self->n_matches = n_matches;
+
+  update_matches (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_N_MATCHES]);
+}
+
+guint
+ephy_search_entry_get_current_match (EphySearchEntry *self)
+{
+  g_return_val_if_fail (EPHY_IS_SEARCH_ENTRY (self), 0);
+
+  return self->current_match;
+}
+
+void
+ephy_search_entry_set_current_match (EphySearchEntry *self,
+                                     guint            current_match)
+{
+  g_return_if_fail (EPHY_IS_SEARCH_ENTRY (self));
+
+  if (self->current_match == current_match)
+    return;
+
+  self->current_match = current_match;
+
+  update_matches (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CURRENT_MATCH]);
+}
+
+EphyFindResult
+ephy_search_entry_get_find_result (EphySearchEntry *self)
+{
+  g_return_val_if_fail (EPHY_IS_SEARCH_ENTRY (self), EPHY_FIND_RESULT_FOUND);
+
+  return self->find_result;
+}
+
+void
+ephy_search_entry_set_find_result (EphySearchEntry *self,
+                                   EphyFindResult   result)
+{
+  const char *icon_name, *tooltip;
+
+  g_return_if_fail (EPHY_IS_SEARCH_ENTRY (self));
+
+  if (self->find_result == result)
+    return;
+
+  self->find_result = result;
+
+  switch (result) {
+    case EPHY_FIND_RESULT_FOUND:
+      icon_name = "edit-find-symbolic";
+      tooltip = NULL;
+      break;
+    case EPHY_FIND_RESULT_NOTFOUND:
+      icon_name = "face-uncertain-symbolic";
+      tooltip = _("Text not found");
+      break;
+    case EPHY_FIND_RESULT_FOUNDWRAPPED:
+      icon_name = "view-wrapped-symbolic";
+      tooltip = _("Search wrapped back to the top");
+      break;
+    default:
+      g_assert_not_reached ();
+  }
+
+  gtk_image_set_from_icon_name (GTK_IMAGE (self->search_icon), icon_name);
+  gtk_widget_set_tooltip_text (self->search_icon, tooltip);
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_FIND_RESULT]);
+}
diff --git a/lib/widgets/ephy-search-entry.h b/lib/widgets/ephy-search-entry.h
new file mode 100644
index 000000000..4b6989d30
--- /dev/null
+++ b/lib/widgets/ephy-search-entry.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2022 Purism SPC
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_SEARCH_ENTRY (ephy_search_entry_get_type())
+
+G_DECLARE_FINAL_TYPE (EphySearchEntry, ephy_search_entry, EPHY, SEARCH_ENTRY, GtkWidget)
+
+typedef enum {
+  EPHY_FIND_RESULT_FOUND = 0,
+  EPHY_FIND_RESULT_NOTFOUND = 1,
+  EPHY_FIND_RESULT_FOUNDWRAPPED = 2
+} EphyFindResult;
+
+GtkWidget     *ephy_search_entry_new                  (void);
+
+const char    *ephy_search_entry_get_placeholder_text (EphySearchEntry *self);
+void           ephy_search_entry_set_placeholder_text (EphySearchEntry *self,
+                                                       const char      *placeholder_text);
+
+gboolean       ephy_search_entry_get_show_matches     (EphySearchEntry *self);
+void           ephy_search_entry_set_show_matches     (EphySearchEntry *self,
+                                                       gboolean         show_matches);
+
+guint          ephy_search_entry_get_n_matches        (EphySearchEntry *self);
+void           ephy_search_entry_set_n_matches        (EphySearchEntry *self,
+                                                       guint            n_matches);
+
+guint          ephy_search_entry_get_current_match    (EphySearchEntry *self);
+void           ephy_search_entry_set_current_match    (EphySearchEntry *self,
+                                                       guint            current_match);
+
+EphyFindResult ephy_search_entry_get_find_result      (EphySearchEntry *self);
+void           ephy_search_entry_set_find_result      (EphySearchEntry *self,
+                                                       EphyFindResult   result);
+
+G_END_DECLS
diff --git a/lib/widgets/ephy-security-popover.c b/lib/widgets/ephy-security-popover.c
index b9632c05c..a10ae9fe7 100644
--- a/lib/widgets/ephy-security-popover.c
+++ b/lib/widgets/ephy-security-popover.c
@@ -208,7 +208,7 @@ ephy_security_popover_set_security_level (EphySecurityPopover *popover,
   }
 
   icon = g_themed_icon_new_with_default_fallbacks (ephy_security_level_to_icon_name (security_level));
-  gtk_image_set_from_gicon (GTK_IMAGE (popover->lock_image), icon, GTK_ICON_SIZE_BUTTON);
+  gtk_image_set_from_gicon (GTK_IMAGE (popover->lock_image), icon);
 
   g_object_unref (icon);
 }
@@ -220,18 +220,18 @@ certificate_button_clicked_cb (GtkButton *button,
   EphySecurityPopover *popover = EPHY_SECURITY_POPOVER (user_data);
   GtkWidget *dialog;
 
-  dialog = ephy_certificate_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (popover))),
+  dialog = ephy_certificate_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (popover))),
                                         popover->address,
                                         popover->certificate,
                                         popover->tls_errors,
                                         popover->security_level);
   gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
   g_signal_connect (dialog, "response",
-                    G_CALLBACK (gtk_widget_destroy),
+                    G_CALLBACK (gtk_window_destroy),
                     NULL);
 
   gtk_popover_popdown (GTK_POPOVER (popover));
-  gtk_widget_show (dialog);
+  gtk_window_present (GTK_WINDOW (dialog));
 }
 
 static void
@@ -304,32 +304,16 @@ ephy_security_popover_set_property (GObject      *object,
   }
 }
 
-static void
-ephy_security_popover_get_preferred_width (GtkWidget *widget,
-                                           gint      *minimum_width,
-                                           gint      *natural_width)
-{
-  GTK_WIDGET_CLASS (ephy_security_popover_parent_class)->get_preferred_width (widget,
-                                                                              minimum_width,
-                                                                              natural_width);
-
-  if (*natural_width > 360)
-    *natural_width = MAX (360, *minimum_width);
-}
-
 static void
 ephy_security_popover_class_init (EphySecurityPopoverClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->constructed = ephy_security_popover_constructed;
   object_class->dispose = ephy_security_popover_dispose;
   object_class->finalize = ephy_security_popover_finalize;
   object_class->set_property = ephy_security_popover_set_property;
 
-  widget_class->get_preferred_width = ephy_security_popover_get_preferred_width;
-
   /**
    * EphySecurityPopover:address:
    *
@@ -505,7 +489,7 @@ add_permission_combobox (EphySecurityPopover *popover,
   tmp = gtk_label_new (name);
   gtk_label_set_xalign (GTK_LABEL (tmp), 0.0);
   gtk_widget_set_hexpand (tmp, TRUE);
-  gtk_box_pack_start (GTK_BOX (hbox), tmp, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (hbox), tmp);
 
   widget = gtk_combo_box_text_new ();
   gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), _("Allow"));
@@ -516,7 +500,7 @@ add_permission_combobox (EphySecurityPopover *popover,
     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), _(name));
   }
 
-  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (hbox), widget);
   g_signal_connect (widget, "changed", G_CALLBACK (callback), popover);
   gtk_size_group_add_widget (size_group, widget);
 
@@ -543,16 +527,18 @@ ephy_security_popover_init (EphySecurityPopover *popover)
   gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
 
   popover->lock_image = gtk_image_new ();
-  gtk_box_pack_start (GTK_BOX (box), popover->lock_image, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (box), popover->lock_image);
 
   popover->host_label = gtk_label_new (NULL);
-  gtk_label_set_line_wrap (GTK_LABEL (popover->host_label), TRUE);
-  gtk_label_set_line_wrap_mode (GTK_LABEL (popover->host_label), PANGO_WRAP_WORD_CHAR);
+  gtk_label_set_wrap (GTK_LABEL (popover->host_label), TRUE);
+  gtk_label_set_wrap_mode (GTK_LABEL (popover->host_label), PANGO_WRAP_WORD_CHAR);
+  gtk_label_set_max_width_chars (GTK_LABEL (popover->host_label), 0);
   gtk_label_set_xalign (GTK_LABEL (popover->host_label), 0.0);
-  gtk_box_pack_start (GTK_BOX (box), popover->host_label, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (box), popover->host_label);
 
   popover->security_label = gtk_label_new (NULL);
-  gtk_label_set_line_wrap (GTK_LABEL (popover->security_label), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (popover->security_label), TRUE);
+  gtk_label_set_max_width_chars (GTK_LABEL (popover->security_label), 0);
   gtk_label_set_xalign (GTK_LABEL (popover->security_label), 0.0);
 
   gtk_grid_attach (GTK_GRID (popover->grid), box, 0, 0, 2, 1);
@@ -577,13 +563,11 @@ ephy_security_popover_init (EphySecurityPopover *popover)
   popover->access_webcam_combobox = add_permission_combobox (popover, _("Webcam access"), 
on_access_webcam_combobox_changed, combo_box_size_group, FALSE, NULL);
   popover->autoplay_combobox = add_permission_combobox (popover, _("Media autoplay"), 
on_autoplay_policy_combobox_changed, combo_box_size_group, FALSE, _("Without Sound"));
 
-  gtk_container_add (GTK_CONTAINER (popover), popover->grid);
-  gtk_widget_show_all (popover->grid);
+  gtk_popover_set_child (GTK_POPOVER (popover), popover->grid);
 }
 
 GtkWidget *
-ephy_security_popover_new (GtkWidget            *relative_to,
-                           const char           *address,
+ephy_security_popover_new (const char           *address,
                            GTlsCertificate      *certificate,
                            GTlsCertificateFlags  tls_errors,
                            EphySecurityLevel     security_level)
@@ -593,7 +577,6 @@ ephy_security_popover_new (GtkWidget            *relative_to,
   return GTK_WIDGET (g_object_new (EPHY_TYPE_SECURITY_POPOVER,
                                    "address", address,
                                    "certificate", certificate,
-                                   "relative-to", relative_to,
                                    "security-level", security_level,
                                    "tls-errors", tls_errors,
                                    NULL));
diff --git a/lib/widgets/ephy-security-popover.h b/lib/widgets/ephy-security-popover.h
index 990bc3628..c5d264737 100644
--- a/lib/widgets/ephy-security-popover.h
+++ b/lib/widgets/ephy-security-popover.h
@@ -31,8 +31,7 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (EphySecurityPopover, ephy_security_popover, EPHY, SECURITY_POPOVER, GtkPopover)
 
-GtkWidget *ephy_security_popover_new      (GtkWidget *relative_to,
-                                           const char *address,
+GtkWidget *ephy_security_popover_new      (const char *address,
                                            GTlsCertificate *certificate,
                                            GTlsCertificateFlags tls_errors,
                                            EphySecurityLevel security_level);
diff --git a/lib/widgets/ephy-title-box.c b/lib/widgets/ephy-title-box.c
index 8eecbfc5e..78aa31d45 100644
--- a/lib/widgets/ephy-title-box.c
+++ b/lib/widgets/ephy-title-box.c
@@ -33,9 +33,9 @@ enum {
 };
 
 struct _EphyTitleBox {
-  GtkEventBox parent_instance;
+  AdwBin parent_instance;
 
-  GtkWidget *lock_image;
+  GtkWidget *security_button;
   GtkWidget *title;
   GtkWidget *subtitle;
 
@@ -44,38 +44,21 @@ struct _EphyTitleBox {
 
 static void ephy_title_box_title_widget_interface_init (EphyTitleWidgetInterface *iface);
 
-G_DEFINE_TYPE_WITH_CODE (EphyTitleBox, ephy_title_box, GTK_TYPE_EVENT_BOX,
+G_DEFINE_TYPE_WITH_CODE (EphyTitleBox, ephy_title_box, ADW_TYPE_BIN,
                          G_IMPLEMENT_INTERFACE (EPHY_TYPE_TITLE_WIDGET,
                                                 ephy_title_box_title_widget_interface_init))
 
-static gboolean
-ephy_title_box_button_press_event (GtkWidget      *widget,
-                                   GdkEventButton *event)
+static void
+create_security_popup_cb (GtkMenuButton *button,
+                          EphyTitleBox  *title_box)
 {
-  EphyTitleBox *title_box = EPHY_TITLE_BOX (widget);
-  GtkAllocation lock_allocation;
-
-  if (event->button != GDK_BUTTON_PRIMARY)
-    return GDK_EVENT_PROPAGATE;
-
-  gtk_widget_get_allocation (title_box->lock_image, &lock_allocation);
-
-  if (event->x >= lock_allocation.x &&
-      event->x < lock_allocation.x + lock_allocation.width &&
-      event->y >= lock_allocation.y &&
-      event->y < lock_allocation.y + lock_allocation.height) {
-    g_signal_emit_by_name (title_box, "lock-clicked", (GdkRectangle *)&lock_allocation);
-    return GDK_EVENT_STOP;
-  }
-
-  return GDK_EVENT_PROPAGATE;
+  g_signal_emit_by_name (title_box, "lock-clicked", button);
 }
 
 static void
 ephy_title_box_constructed (GObject *object)
 {
   EphyTitleBox *title_box = EPHY_TITLE_BOX (object);
-  GtkStyleContext *context;
   GtkWidget *vbox;
   GtkWidget *hbox;
 
@@ -83,39 +66,36 @@ ephy_title_box_constructed (GObject *object)
 
   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
   gtk_widget_set_valign (vbox, GTK_ALIGN_CENTER);
-  gtk_container_add (GTK_CONTAINER (title_box), vbox);
+  adw_bin_set_child (ADW_BIN (title_box), vbox);
 
   title_box->title = gtk_label_new (NULL);
-  context = gtk_widget_get_style_context (title_box->title);
-  gtk_style_context_add_class (context, "title");
-  gtk_label_set_line_wrap (GTK_LABEL (title_box->title), FALSE);
+  gtk_widget_add_css_class (title_box->title, "title");
   gtk_label_set_single_line_mode (GTK_LABEL (title_box->title), TRUE);
   gtk_label_set_ellipsize (GTK_LABEL (title_box->title), PANGO_ELLIPSIZE_END);
   gtk_label_set_text (GTK_LABEL (title_box->title), g_get_application_name ());
-  gtk_box_pack_start (GTK_BOX (vbox), title_box->title, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (vbox), title_box->title);
 
   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
-  context = gtk_widget_get_style_context (hbox);
-  gtk_style_context_add_class (context, "subtitle");
+  gtk_widget_add_css_class (hbox, "subtitle");
   gtk_widget_set_halign (hbox, GTK_ALIGN_CENTER);
   gtk_widget_set_valign (hbox, GTK_ALIGN_BASELINE);
-  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (vbox), hbox);
 
-  title_box->lock_image = gtk_image_new ();
-  g_object_set (title_box->lock_image, "icon-size", GTK_ICON_SIZE_MENU, NULL);
-  gtk_widget_set_valign (title_box->lock_image, GTK_ALIGN_BASELINE);
-  gtk_box_pack_start (GTK_BOX (hbox), title_box->lock_image, FALSE, TRUE, 0);
+  title_box->security_button = gtk_menu_button_new ();
+  gtk_widget_set_valign (title_box->security_button, GTK_ALIGN_BASELINE);
+  gtk_box_append (GTK_BOX (hbox), title_box->security_button);
+  gtk_widget_add_css_class (title_box->security_button, "entry-icon");
+  gtk_menu_button_set_create_popup_func (GTK_MENU_BUTTON (title_box->security_button),
+                                         (GtkMenuButtonCreatePopupFunc)create_security_popup_cb,
+                                         title_box,
+                                         NULL);
 
   title_box->subtitle = gtk_label_new (NULL);
   gtk_widget_set_valign (title_box->subtitle, GTK_ALIGN_BASELINE);
-  gtk_label_set_line_wrap (GTK_LABEL (title_box->subtitle), FALSE);
   gtk_label_set_single_line_mode (GTK_LABEL (title_box->subtitle), TRUE);
   gtk_label_set_ellipsize (GTK_LABEL (title_box->subtitle), PANGO_ELLIPSIZE_END);
   gtk_label_set_selectable (GTK_LABEL (title_box->subtitle), TRUE);
-  gtk_box_pack_start (GTK_BOX (hbox), title_box->subtitle, FALSE, TRUE, 0);
-
-  gtk_widget_add_events (GTK_WIDGET (title_box), GDK_BUTTON_PRESS_MASK);
-  gtk_widget_show_all (GTK_WIDGET (title_box));
+  gtk_box_append (GTK_BOX (hbox), title_box->subtitle);
 }
 
 static const char *
@@ -161,11 +141,9 @@ ephy_title_box_title_widget_set_security_level (EphyTitleWidget   *widget,
 
   icon_name = ephy_security_level_to_icon_name (security_level);
 
-  g_object_set (title_box->lock_image,
-                "icon-name", icon_name,
-                NULL);
-
-  gtk_widget_set_visible (title_box->lock_image, icon_name != NULL);
+  gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (title_box->security_button),
+                                 icon_name);
+  gtk_widget_set_visible (title_box->security_button, icon_name != NULL);
 
   title_box->security_level = security_level;
 }
@@ -223,12 +201,10 @@ static void
 ephy_title_box_class_init (EphyTitleBoxClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->constructed = ephy_title_box_constructed;
   object_class->get_property = ephy_title_box_get_property;
   object_class->set_property = ephy_title_box_set_property;
-  widget_class->button_press_event = ephy_title_box_button_press_event;
 
   g_object_class_override_property (object_class, PROP_ADDRESS, "address");
   g_object_class_override_property (object_class, PROP_SECURITY_LEVEL, "security-level");
diff --git a/lib/widgets/ephy-title-box.h b/lib/widgets/ephy-title-box.h
index be6ab7c4e..5c5e8b1d9 100644
--- a/lib/widgets/ephy-title-box.h
+++ b/lib/widgets/ephy-title-box.h
@@ -21,8 +21,7 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
-#include <webkit2/webkit2.h>
+#include <adwaita.h>
 
 #include "ephy-security-levels.h"
 
@@ -30,7 +29,7 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_TITLE_BOX (ephy_title_box_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyTitleBox, ephy_title_box, EPHY, TITLE_BOX, GtkEventBox)
+G_DECLARE_FINAL_TYPE (EphyTitleBox, ephy_title_box, EPHY, TITLE_BOX, AdwBin)
 
 EphyTitleBox       *ephy_title_box_new                  (void);
 
diff --git a/lib/widgets/ephy-title-widget.c b/lib/widgets/ephy-title-widget.c
index 1b1d4e88d..f16ffd5f2 100644
--- a/lib/widgets/ephy-title-widget.c
+++ b/lib/widgets/ephy-title-widget.c
@@ -48,7 +48,7 @@ ephy_title_widget_default_init (EphyTitleWidgetInterface *iface)
                 0, NULL, NULL, NULL,
                 G_TYPE_NONE,
                 1,
-                GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE);
+                GTK_TYPE_MENU_BUTTON);
 }
 
 const char *
diff --git a/lib/widgets/meson.build b/lib/widgets/meson.build
index 1e8b47e5b..7ad833838 100644
--- a/lib/widgets/meson.build
+++ b/lib/widgets/meson.build
@@ -1,5 +1,5 @@
 types_headers = [
-  'ephy-location-entry.h'
+  'ephy-search-entry.h'
 ]
 
 enums = gnome.mkenums_simple('ephy-widgets-type-builtins',
@@ -9,10 +9,12 @@ enums = gnome.mkenums_simple('ephy-widgets-type-builtins',
 libephywidgets_sources = [
   'contrib/nautilus-floating-bar.c',
   'ephy-certificate-dialog.c',
+  'ephy-downloads-paintable.c',
   'ephy-downloads-popover.c',
   'ephy-download-widget.c',
   'ephy-file-chooser.c',
   'ephy-location-entry.c',
+  'ephy-search-entry.c',
   'ephy-security-popover.c',
   'ephy-title-box.c',
   'ephy-title-widget.c',
@@ -20,14 +22,12 @@ libephywidgets_sources = [
 ]
 
 libephywidgets_deps = [
-  libdazzle_dep,
   ephymisc_dep,
-  gcr_dep,
+#  gcr_dep,
   gdk_pixbuf_dep,
   gio_dep,
   glib_dep,
   gtk_dep,
-  libdazzle_dep,
   libsoup_dep,
   webkit2gtk_dep,
 ]
diff --git a/meson.build b/meson.build
index 05b0ebfc5..0a56110b1 100644
--- a/meson.build
+++ b/meson.build
@@ -73,40 +73,33 @@ conf.set_quoted('GSB_API_KEY', gsb_api_key)
 conf.set10('ENABLE_GSB', gsb_api_key != '')
 
 glib_requirement = '>= 2.67.4'
-gtk_requirement = '>= 3.24.0'
+gtk_requirement = '>= 4.5.0'
 nettle_requirement = '>= 3.4'
 webkitgtk_requirement = '>= 2.33.2'
 
 cairo_dep = dependency('cairo', version: '>= 1.2')
-gcr_dep = dependency('gcr-3', version: '>= 3.5.5')
+#gcr_dep = dependency('gcr-3', version: '>= 3.5.5')
 gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0', version: '>= 2.36.5')
 gio_dep = dependency('gio-2.0', version: glib_requirement)
 gio_unix_dep = dependency('gio-unix-2.0', version: glib_requirement)
 glib_dep = dependency('glib-2.0', version: glib_requirement)
 gsettings_desktop_schemas = dependency('gsettings-desktop-schemas')
-gtk_dep = dependency('gtk+-3.0', version: gtk_requirement)
-gtk_unix_print_dep = dependency('gtk+-unix-print-3.0', version: gtk_requirement)
+gtk_dep = dependency('gtk4', version: gtk_requirement)
+gtk_unix_print_dep = dependency('gtk4-unix-print', version: gtk_requirement)
 hogweed_dep = dependency('hogweed', version: nettle_requirement)
 iso_codes_dep = dependency('iso-codes', version: '>= 0.35')
 json_glib_dep = dependency('json-glib-1.0', version: '>= 1.6')
 libarchive_dep = dependency('libarchive')
-libdazzle_dep = dependency('libdazzle-1.0', version: '>= 3.37.1')
-libhandy_dep = dependency('libhandy-1', version: '>= 1.5.0')
+libadwaita_dep = dependency('libadwaita-1', version: '>= 1.0.0')
 libsecret_dep = dependency('libsecret-1', version: '>= 0.19.0')
 libxml_dep = dependency('libxml-2.0', version: '>= 2.6.12')
 nettle_dep = dependency('nettle', version: nettle_requirement)
-portal_dep = dependency('libportal-gtk3', version: '>= 0.6')
+portal_dep = dependency('libportal-gtk4', version: '>= 0.6')
 sqlite3_dep = dependency('sqlite3', version: '>= 3.22')
 
-if get_option('soup2').enabled()
-  libsoup_dep = dependency('libsoup-2.4', version: '>= 2.48.0')
-  webkit2gtk_dep = dependency('webkit2gtk-4.0', version: webkitgtk_requirement)
-  webkit2gtk_web_extension_dep = dependency('webkit2gtk-web-extension-4.0', version: webkitgtk_requirement)
-else
-  libsoup_dep = dependency('libsoup-3.0', version: '>= 2.99.4')
-  webkit2gtk_dep = dependency('webkit2gtk-4.1', version: webkitgtk_requirement)
-  webkit2gtk_web_extension_dep = dependency('webkit2gtk-web-extension-4.1', version: webkitgtk_requirement)
-endif
+libsoup_dep = dependency('libsoup-3.0', version: '>= 2.99.4')
+webkit2gtk_dep = dependency('webkit2gtk-5.0', version: webkitgtk_requirement)
+webkit2gtk_web_extension_dep = dependency('webkit2gtk-web-extension-5.0', version: webkitgtk_requirement)
 
 webkit_revision = webkit2gtk_dep.get_variable(pkgconfig : 'revision', default_value : '')
 if webkit_revision == 'tarball'
diff --git a/org.gnome.Epiphany.Canary.json.in b/org.gnome.Epiphany.Canary.json.in
index 299af7304..6e9d463b2 100644
--- a/org.gnome.Epiphany.Canary.json.in
+++ b/org.gnome.Epiphany.Canary.json.in
@@ -18,27 +18,15 @@
         "--socket=pulseaudio",
         "--socket=wayland",
         "--system-talk-name=org.freedesktop.GeoClue2",
-        "--own-name=org.gnome.Epiphany.WebAppProvider"
+        "--own-name=org.gnome.Epiphany.WebAppProvider",
+        "--env=WEBKIT_NICOSIA_PAINTING_THREADS=0"
     ],
     "modules" : [
-        {
-            "name" : "libdazzle",
-            "buildsystem" : "meson",
-            "config-opts" : [
-                "-Dwith_vapi=false"
-            ],
-            "sources" : [
-                {
-                    "type" : "git",
-                    "url" : "https://gitlab.gnome.org/GNOME/libdazzle.git";
-                }
-            ]
-        },
         {
             "name" : "libportal",
             "buildsystem" : "meson",
             "config-opts" : [
-                "-Dbackends=gtk3",
+                "-Dbackends=gtk4",
                 "-Dintrospection=false",
                 "-Ddocs=false"
             ],
@@ -76,19 +64,40 @@
             ]
         },
         {
-            "name": "libhandy",
+            "name" : "libsass",
+            "buildsystem" : "meson",
+            "sources" : [
+                {
+                    "type" : "git",
+                    "url" : "https://github.com/lazka/libsass.git";,
+                    "branch" : "meson"
+                }
+            ]
+        },
+        {
+            "name" : "sassc",
+            "buildsystem" : "meson",
+            "sources" : [
+                {
+                    "type" : "git",
+                    "url" : "https://github.com/lazka/sassc.git";,
+                    "branch" : "meson"
+                }
+            ]
+        },
+        {
+            "name": "libadwaita",
             "buildsystem" : "meson",
             "config-opts" : [
                 "-Dgtk_doc=false",
                 "-Dvapi=false",
                 "-Dtests=false",
-                "-Dexamples=false",
-                "-Dglade_catalog=disabled"
+                "-Dexamples=false"
             ],
             "sources" : [
                 {
                     "type" : "git",
-                    "url" : "https://gitlab.gnome.org/GNOME/libhandy.git";,
+                    "url" : "https://gitlab.gnome.org/GNOME/libadwaita.git";,
                     "branch" : "main"
                 }
             ]
diff --git a/org.gnome.Epiphany.json b/org.gnome.Epiphany.json
index c86058338..e8e4ba75b 100644
--- a/org.gnome.Epiphany.json
+++ b/org.gnome.Epiphany.json
@@ -18,7 +18,8 @@
         "--socket=pulseaudio",
         "--socket=wayland",
         "--system-talk-name=org.freedesktop.GeoClue2",
-        "--own-name=org.gnome.Epiphany.WebAppProvider"
+        "--own-name=org.gnome.Epiphany.WebAppProvider",
+        "--env=WEBKIT_NICOSIA_PAINTING_THREADS=0"
     ],
     "modules" : [
          {
@@ -90,21 +91,11 @@
                 }
             ]
         },
-        {
-            "name" : "libdazzle",
-            "buildsystem" : "meson",
-            "sources" : [
-                {
-                    "type" : "git",
-                    "url" : "https://gitlab.gnome.org/GNOME/libdazzle.git";
-                }
-            ]
-        },
         {
             "name" : "libportal",
             "buildsystem" : "meson",
             "config-opts" : [
-                "-Dbackends=gtk3",
+                "-Dbackends=gtk4",
                 "-Dintrospection=false",
                 "-Ddocs=false"
             ],
diff --git a/src/bookmarks/ephy-add-bookmark-popover.c b/src/bookmarks/ephy-add-bookmark-popover.c
index c5978d0d3..7ef84ceca 100644
--- a/src/bookmarks/ephy-add-bookmark-popover.c
+++ b/src/bookmarks/ephy-add-bookmark-popover.c
@@ -44,8 +44,8 @@ bookmark_removed_cb (EphyAddBookmarkPopover *self,
                      EphyBookmark           *bookmark,
                      EphyBookmarksManager   *manager)
 {
-  GtkWidget *relative_to;
-  GtkWidget *window;
+  GtkWidget *parent;
+  GtkRoot *window;
   EphyEmbed *embed;
   EphyWebView *view;
   const char *address;
@@ -54,12 +54,12 @@ bookmark_removed_cb (EphyAddBookmarkPopover *self,
   g_assert (EPHY_IS_BOOKMARK (bookmark));
   g_assert (EPHY_IS_BOOKMARKS_MANAGER (manager));
 
-  relative_to = gtk_popover_get_relative_to (GTK_POPOVER (self));
+  parent = gtk_widget_get_parent (GTK_WIDGET (self));
 
-  if (!relative_to)
+  if (!parent)
     return;
 
-  window = gtk_widget_get_toplevel (relative_to);
+  window = gtk_widget_get_root (parent);
   embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
   view = ephy_embed_get_web_view (embed);
 
@@ -79,19 +79,19 @@ bookmark_removed_cb (EphyAddBookmarkPopover *self,
 static void
 popover_shown (EphyAddBookmarkPopover *self)
 {
-  GtkWidget *relative_to;
-  GtkWidget *window;
+  GtkWidget *parent;
+  GtkRoot *window;
   EphyBookmarksManager *manager;
   EphyBookmark *bookmark;
   EphyEmbed *embed;
   const char *address;
 
-  relative_to = gtk_popover_get_relative_to (GTK_POPOVER (self));
+  parent = gtk_widget_get_parent (GTK_WIDGET (self));
 
-  if (!relative_to)
+  if (!parent)
     return;
 
-  window = gtk_widget_get_toplevel (relative_to);
+  window = gtk_widget_get_root (parent);
   manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
   embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
 
@@ -122,7 +122,7 @@ popover_shown (EphyAddBookmarkPopover *self)
   self->grid = ephy_bookmark_properties_new (bookmark,
                                              EPHY_BOOKMARK_PROPERTIES_TYPE_POPOVER,
                                              GTK_WIDGET (self));
-  gtk_container_add (GTK_CONTAINER (self), self->grid);
+  gtk_popover_set_child (GTK_POPOVER (self), self->grid);
   gtk_popover_set_default_widget (GTK_POPOVER (self),
                                   ephy_bookmark_properties_get_add_tag_button (EPHY_BOOKMARK_PROPERTIES 
(self->grid)));
 
@@ -144,7 +144,7 @@ popover_hidden (EphyAddBookmarkPopover *self)
 
   if (self->grid) {
     gtk_popover_set_default_widget (GTK_POPOVER (self), NULL);
-    gtk_container_remove (GTK_CONTAINER (self), self->grid);
+    gtk_popover_set_child (GTK_POPOVER (self), NULL);
     self->grid = NULL;
   }
 }
diff --git a/src/bookmarks/ephy-bookmark-properties.c b/src/bookmarks/ephy-bookmark-properties.c
index 238f27541..5db4e708b 100644
--- a/src/bookmarks/ephy-bookmark-properties.c
+++ b/src/bookmarks/ephy-bookmark-properties.c
@@ -80,8 +80,8 @@ flow_box_sort_func (GtkFlowBoxChild *child1,
   g_assert (GTK_IS_FLOW_BOX_CHILD (child1));
   g_assert (GTK_IS_FLOW_BOX_CHILD (child2));
 
-  box1 = gtk_bin_get_child (GTK_BIN (child1));
-  box2 = gtk_bin_get_child (GTK_BIN (child2));
+  box1 = gtk_flow_box_child_get_child (GTK_FLOW_BOX_CHILD (child1));
+  box2 = gtk_flow_box_child_get_child (GTK_FLOW_BOX_CHILD (child2));
 
   label1 = g_object_get_data (G_OBJECT (box1), "label");
   label2 = g_object_get_data (G_OBJECT (box2), "label");
@@ -111,7 +111,6 @@ ephy_bookmark_properties_tags_box_child_activated_cb (EphyBookmarkProperties *se
                                                       GtkFlowBoxChild        *child,
                                                       GtkFlowBox             *flow_box)
 {
-  GtkStyleContext *context;
   GtkWidget *box;
   GtkWidget *label;
 
@@ -119,18 +118,17 @@ ephy_bookmark_properties_tags_box_child_activated_cb (EphyBookmarkProperties *se
   g_assert (GTK_IS_FLOW_BOX_CHILD (child));
   g_assert (GTK_IS_FLOW_BOX (flow_box));
 
-  box = gtk_bin_get_child (GTK_BIN (child));
+  box = gtk_flow_box_child_get_child (GTK_FLOW_BOX_CHILD (child));
   label = g_object_get_data (G_OBJECT (box), "label");
 
-  context = gtk_widget_get_style_context (GTK_WIDGET (child));
-  if (gtk_style_context_has_class (context, "bookmark-tag-widget-selected")) {
+  if (gtk_widget_has_css_class (GTK_WIDGET (child), "bookmark-tag-widget-selected")) {
     ephy_bookmark_remove_tag (self->bookmark,
                               gtk_label_get_text (GTK_LABEL (label)));
-    gtk_style_context_remove_class (context, "bookmark-tag-widget-selected");
+    gtk_widget_remove_css_class (GTK_WIDGET (child), "bookmark-tag-widget-selected");
   } else {
     ephy_bookmark_add_tag (self->bookmark,
                            gtk_label_get_text (GTK_LABEL (label)));
-    gtk_style_context_add_class (context, "bookmark-tag-widget-selected");
+    gtk_widget_add_css_class (GTK_WIDGET (child), "bookmark-tag-widget-selected");
   }
 }
 
@@ -152,7 +150,7 @@ ephy_bookmark_properties_tag_widget_button_clicked_cb (EphyBookmarkProperties *s
   ephy_bookmarks_manager_delete_tag (self->manager, gtk_label_get_text (label));
 
   flow_box_child = gtk_widget_get_parent (box);
-  gtk_widget_destroy (flow_box_child);
+  gtk_flow_box_remove (GTK_FLOW_BOX (self->tags_box), flow_box_child);
   update_tags_scrollbar (self);
 }
 
@@ -164,7 +162,6 @@ ephy_bookmark_properties_create_tag_widget (EphyBookmarkProperties *self,
   GtkWidget *widget;
   GtkWidget *box;
   GtkWidget *label;
-  GtkStyleContext *context;
   gboolean default_tag;
   const char *label_text;
 
@@ -177,11 +174,10 @@ ephy_bookmark_properties_create_tag_widget (EphyBookmarkProperties *self,
   if (default_tag) {
     GtkWidget *image;
 
-    image = gtk_image_new_from_icon_name ("emblem-favorite-symbolic",
-                                          GTK_ICON_SIZE_BUTTON);
+    image = gtk_image_new_from_icon_name ("emblem-favorite-symbolic");
     gtk_widget_set_margin_bottom (image, 8);
     gtk_widget_set_margin_top (image, 8);
-    gtk_box_pack_start (GTK_BOX (box), image, FALSE, TRUE, 0);
+    gtk_box_append (GTK_BOX (box), image);
   }
 
   label_text = default_tag ? EPHY_BOOKMARKS_FAVORITES_TAG : tag;
@@ -189,18 +185,15 @@ ephy_bookmark_properties_create_tag_widget (EphyBookmarkProperties *self,
   gtk_widget_set_hexpand (label, TRUE);
   gtk_label_set_xalign (GTK_LABEL (label), 0);
   gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
-  gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (box), label);
 
   if (!default_tag) {
     GtkWidget *button;
 
-    button = gtk_button_new ();
-    gtk_button_set_image (GTK_BUTTON (button),
-                          gtk_image_new_from_icon_name ("window-close-symbolic",
-                                                        GTK_ICON_SIZE_MENU));
+    button = gtk_button_new_from_icon_name ("window-close-symbolic");
     gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
-    gtk_style_context_add_class (gtk_widget_get_style_context (button), "flat");
-    gtk_box_pack_start (GTK_BOX (box), button, FALSE, TRUE, 0);
+    gtk_widget_add_css_class (button, "flat");
+    gtk_box_append (GTK_BOX (box), button);
     g_signal_connect_object (button, "clicked",
                              G_CALLBACK (ephy_bookmark_properties_tag_widget_button_clicked_cb),
                              self,
@@ -209,30 +202,22 @@ ephy_bookmark_properties_create_tag_widget (EphyBookmarkProperties *self,
 
   g_object_set_data (G_OBJECT (box), "label", label);
 
-  gtk_container_add (GTK_CONTAINER (widget), box);
+  gtk_flow_box_child_set_child (GTK_FLOW_BOX_CHILD (widget), box);
 
-  context = gtk_widget_get_style_context (widget);
-  gtk_style_context_add_class (context, "bookmark-tag-widget");
+  gtk_widget_add_css_class (widget, "bookmark-tag-widget");
   if (selected)
-    gtk_style_context_add_class (context, "bookmark-tag-widget-selected");
-
-  gtk_widget_show_all (widget);
+    gtk_widget_add_css_class (widget, "bookmark-tag-widget-selected");
 
   return widget;
 }
 
 static void
-ephy_bookmark_properties_actions_add_tag (GSimpleAction *action,
-                                          GVariant      *value,
-                                          gpointer       user_data)
+ephy_bookmark_properties_actions_add_tag (EphyBookmarkProperties *self)
 {
-  EphyBookmarkProperties *self = user_data;
   GtkEntryBuffer *buffer;
   GtkWidget *widget;
   const char *text;
 
-  g_assert (EPHY_IS_BOOKMARK_PROPERTIES (self));
-
   buffer = gtk_entry_get_buffer (GTK_ENTRY (self->add_tag_entry));
   text = gtk_entry_buffer_get_text (buffer);
 
@@ -248,26 +233,20 @@ ephy_bookmark_properties_actions_add_tag (GSimpleAction *action,
   update_tags_scrollbar (self);
 
   /* Empty entry and disable button's action until new text is inserted */
-  gtk_entry_set_text (GTK_ENTRY (self->add_tag_entry), "");
-  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+  gtk_editable_set_text (GTK_EDITABLE (self->add_tag_entry), "");
+  gtk_widget_action_set_enabled (GTK_WIDGET (self), "bookmark-properties.add-tag", FALSE);
 
   gtk_widget_grab_focus (GTK_WIDGET (self->add_tag_entry));
 }
 
 static void
-ephy_bookmark_properties_actions_remove_bookmark (GSimpleAction *action,
-                                                  GVariant      *value,
-                                                  gpointer       user_data)
+ephy_bookmark_properties_actions_remove_bookmark (EphyBookmarkProperties *self)
 {
-  EphyBookmarkProperties *self = user_data;
-
-  g_assert (EPHY_IS_BOOKMARK_PROPERTIES (self));
-
   self->bookmark_is_removed = TRUE;
   ephy_bookmarks_manager_remove_bookmark (self->manager, self->bookmark);
 
   if (self->type == EPHY_BOOKMARK_PROPERTIES_TYPE_DIALOG)
-    gtk_widget_destroy (self->parent);
+    gtk_window_destroy (GTK_WINDOW (self->parent));
 }
 
 static void
@@ -275,20 +254,16 @@ ephy_bookmark_properties_buffer_text_changed_cb (EphyBookmarkProperties *self,
                                                  GParamSpec             *pspec,
                                                  GtkEntryBuffer         *buffer)
 {
-  GActionGroup *group;
-  GAction *action;
   const char *text;
 
   g_assert (EPHY_IS_BOOKMARK_PROPERTIES (self));
   g_assert (GTK_IS_ENTRY_BUFFER (buffer));
 
-  group = gtk_widget_get_action_group (GTK_WIDGET (self), "bookmark-properties");
-  action = g_action_map_lookup_action (G_ACTION_MAP (group), "add-tag");
   text = gtk_entry_buffer_get_text (buffer);
   if (ephy_bookmarks_manager_tag_exists (self->manager, text) || g_strcmp0 (text, "") == 0)
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+    gtk_widget_action_set_enabled (GTK_WIDGET (self), "bookmark-properties.add-tag", FALSE);
   else
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
+    gtk_widget_action_set_enabled (GTK_WIDGET (self), "bookmark-properties.add-tag", TRUE);
 }
 
 static void
@@ -378,15 +353,15 @@ ephy_bookmark_properties_constructed (GObject *object)
 
   /* Set appearance based on type */
   if (self->type == EPHY_BOOKMARK_PROPERTIES_TYPE_DIALOG) {
-    gtk_container_remove (GTK_CONTAINER (self), self->popover_bookmark_label);
+    gtk_box_remove (GTK_BOX (self), self->popover_bookmark_label);
   } else if (self->type == EPHY_BOOKMARK_PROPERTIES_TYPE_POPOVER) {
-    gtk_container_remove (GTK_CONTAINER (self), self->address_label);
-    gtk_container_remove (GTK_CONTAINER (self), self->address_entry);
+    gtk_box_remove (GTK_BOX (self), self->address_label);
+    gtk_box_remove (GTK_BOX (self), self->address_entry);
   }
 
   /* Set text for name entry */
-  gtk_entry_set_text (GTK_ENTRY (self->name_entry),
-                      ephy_bookmark_get_title (self->bookmark));
+  gtk_editable_set_text (GTK_EDITABLE (self->name_entry),
+                         ephy_bookmark_get_title (self->bookmark));
 
   g_object_bind_property (GTK_ENTRY (self->name_entry), "text",
                           self->bookmark, "title",
@@ -398,7 +373,7 @@ ephy_bookmark_properties_constructed (GObject *object)
 
     address = ephy_bookmark_get_url (self->bookmark);
     decoded_address = ephy_uri_decode (address);
-    gtk_entry_set_text (GTK_ENTRY (self->address_entry), decoded_address);
+    gtk_editable_set_text (GTK_EDITABLE (self->address_entry), decoded_address);
 
     g_object_bind_property (GTK_ENTRY (self->address_entry), "text",
                             self->bookmark, "bmkUri",
@@ -430,7 +405,6 @@ ephy_bookmark_properties_constructed (GObject *object)
                            G_CALLBACK (ephy_bookmark_properties_tags_box_child_activated_cb),
                            self,
                            G_CONNECT_SWAPPED);
-  gtk_widget_show_all (self->tags_box);
 }
 
 static void
@@ -449,6 +423,16 @@ ephy_bookmark_properties_finalize (GObject *object)
   G_OBJECT_CLASS (ephy_bookmark_properties_parent_class)->finalize (object);
 }
 
+static void
+ephy_bookmark_properties_realize (GtkWidget *widget)
+{
+  EphyBookmarkProperties *self = EPHY_BOOKMARK_PROPERTIES (widget);
+
+  GTK_WIDGET_CLASS (ephy_bookmark_properties_parent_class)->realize (widget);
+
+  gtk_widget_grab_focus (self->name_entry);
+}
+
 static void
 ephy_bookmark_properties_class_init (EphyBookmarkPropertiesClass *klass)
 {
@@ -459,6 +443,8 @@ ephy_bookmark_properties_class_init (EphyBookmarkPropertiesClass *klass)
   object_class->constructed = ephy_bookmark_properties_constructed;
   object_class->finalize = ephy_bookmark_properties_finalize;
 
+  widget_class->realize = ephy_bookmark_properties_realize;
+
   obj_properties[PROP_BOOKMARK] =
     g_param_spec_object ("bookmark",
                          "An EphyBookmark object",
@@ -493,19 +479,16 @@ ephy_bookmark_properties_class_init (EphyBookmarkPropertiesClass *klass)
   gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, tags_scrolled_window);
   gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, add_tag_entry);
   gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, add_tag_button);
-}
 
-static const GActionEntry entries[] = {
-  { "add-tag", ephy_bookmark_properties_actions_add_tag },
-  { "remove-bookmark", ephy_bookmark_properties_actions_remove_bookmark }
-};
+  gtk_widget_class_install_action (widget_class, "bookmark-properties.add-tag",
+                                   NULL, 
(GtkWidgetActionActivateFunc)ephy_bookmark_properties_actions_add_tag);
+  gtk_widget_class_install_action (widget_class, "bookmark-properties.remove-bookmark",
+                                   NULL, 
(GtkWidgetActionActivateFunc)ephy_bookmark_properties_actions_remove_bookmark);
+}
 
 static void
 ephy_bookmark_properties_init (EphyBookmarkProperties *self)
 {
-  g_autoptr (GSimpleActionGroup) group = NULL;
-  GAction *action;
-
   gtk_widget_init_template (GTK_WIDGET (self));
 
   self->manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
@@ -534,16 +517,9 @@ ephy_bookmark_properties_init (EphyBookmarkProperties *self)
                               (GtkFlowBoxSortFunc)flow_box_sort_func,
                               NULL, NULL);
 
-  group = g_simple_action_group_new ();
-  g_action_map_add_action_entries (G_ACTION_MAP (group), entries,
-                                   G_N_ELEMENTS (entries), self);
-  gtk_widget_insert_action_group (GTK_WIDGET (self), "bookmark-properties",
-                                  G_ACTION_GROUP (group));
-
   /* Disable the "add-tag" action until text is inserted in the corresponding
    * entry */
-  action = g_action_map_lookup_action (G_ACTION_MAP (group), "add-tag");
-  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+  gtk_widget_action_set_enabled (GTK_WIDGET (self), "bookmark-properties.add-tag", FALSE);
 
   g_signal_connect_object (gtk_entry_get_buffer (GTK_ENTRY (self->add_tag_entry)),
                            "notify::text",
diff --git a/src/bookmarks/ephy-bookmark-row.c b/src/bookmarks/ephy-bookmark-row.c
index 5caef810c..df99630c7 100644
--- a/src/bookmarks/ephy-bookmark-row.c
+++ b/src/bookmarks/ephy-bookmark-row.c
@@ -60,7 +60,7 @@ ephy_bookmark_row_button_clicked_cb (EphyBookmarkRow *row,
 
   dialog = g_object_new (GTK_TYPE_DIALOG,
                          "title", _("Bookmark Properties"),
-                         "transient-for", GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (row))),
+                         "transient-for", GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (row))),
                          "use-header-bar", TRUE,
                          "modal", TRUE,
                          NULL);
@@ -70,12 +70,12 @@ ephy_bookmark_row_button_clicked_cb (EphyBookmarkRow *row,
   grid = ephy_bookmark_properties_new (ephy_bookmark_row_get_bookmark (row),
                                        EPHY_BOOKMARK_PROPERTIES_TYPE_DIALOG,
                                        dialog);
-  gtk_window_set_default (GTK_WINDOW (dialog),
-                          ephy_bookmark_properties_get_add_tag_button (EPHY_BOOKMARK_PROPERTIES (grid)));
+  gtk_window_set_default_widget (GTK_WINDOW (dialog),
+                                 ephy_bookmark_properties_get_add_tag_button (EPHY_BOOKMARK_PROPERTIES 
(grid)));
 
-  gtk_container_add (GTK_CONTAINER (content_area), grid);
+  gtk_box_append (GTK_BOX (content_area), grid);
 
-  gtk_widget_show (dialog);
+  gtk_window_present (GTK_WINDOW (dialog));
 }
 
 static void
diff --git a/src/bookmarks/ephy-bookmarks-popover.c b/src/bookmarks/ephy-bookmarks-popover.c
index 5d7c0e993..bf6cd23ff 100644
--- a/src/bookmarks/ephy-bookmarks-popover.c
+++ b/src/bookmarks/ephy-bookmarks-popover.c
@@ -26,7 +26,6 @@
 #include "ephy-bookmark-row.h"
 #include "ephy-bookmarks-manager.h"
 #include "ephy-debug.h"
-#include "ephy-gui.h"
 #include "ephy-link.h"
 #include "ephy-shell.h"
 #include "ephy-window.h"
@@ -65,7 +64,7 @@ tag_detail_back (EphyBookmarksPopover *self)
                                     "default");
 
   while ((row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->tag_detail_list_box), 0)))
-    gtk_container_remove (GTK_CONTAINER (self->tag_detail_list_box), GTK_WIDGET (row));
+    gtk_list_box_remove (GTK_LIST_BOX (self->tag_detail_list_box), GTK_WIDGET (row));
 }
 
 static void
@@ -82,7 +81,7 @@ remove_bookmark_row (GtkListBox *list_box,
 
     if (g_strcmp0 (type, EPHY_LIST_BOX_ROW_TYPE_BOOKMARK) == 0 &&
         g_strcmp0 (ephy_bookmark_row_get_bookmark_url (EPHY_BOOKMARK_ROW (row)), url) == 0) {
-      gtk_container_remove (GTK_CONTAINER (list_box), GTK_WIDGET (row));
+      gtk_list_box_remove (list_box, GTK_WIDGET (row));
       break;
     }
   }
@@ -116,7 +115,7 @@ ephy_bookmarks_popover_bookmark_tag_added_cb (EphyBookmarksPopover *self,
     GtkWidget *bookmark_row;
 
     bookmark_row = create_bookmark_row (bookmark, self);
-    gtk_container_add (GTK_CONTAINER (self->tag_detail_list_box), bookmark_row);
+    gtk_list_box_append (GTK_LIST_BOX (self->tag_detail_list_box), bookmark_row);
   }
 
   exists = FALSE;
@@ -134,7 +133,7 @@ ephy_bookmarks_popover_bookmark_tag_added_cb (EphyBookmarksPopover *self,
 
   if (!exists) {
     GtkWidget *tag_row = create_tag_row (tag);
-    gtk_container_add (GTK_CONTAINER (self->tags_list_box), tag_row);
+    gtk_list_box_append (GTK_LIST_BOX (self->tags_list_box), tag_row);
   }
 }
 
@@ -172,7 +171,7 @@ ephy_bookmarks_popover_bookmark_tag_removed_cb (EphyBookmarksPopover *self,
 
     if (!exists) {
       GtkWidget *row = create_bookmark_row (bookmark, self);
-      gtk_container_add (GTK_CONTAINER (self->tags_list_box), row);
+      gtk_list_box_append (GTK_LIST_BOX (self->tags_list_box), row);
     }
   }
 
@@ -198,7 +197,7 @@ ephy_bookmarks_popover_bookmark_tag_removed_cb (EphyBookmarksPopover *self,
       const char *title = g_object_get_data (G_OBJECT (row), "title");
 
       if (g_strcmp0 (title, tag) == 0)
-        gtk_container_remove (GTK_CONTAINER (self->tags_list_box), GTK_WIDGET (row));
+        gtk_list_box_remove (GTK_LIST_BOX (self->tags_list_box), GTK_WIDGET (row));
     }
   }
 }
@@ -241,9 +240,9 @@ create_tag_row (const char *tag)
   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 
   if (g_strcmp0 (tag, EPHY_BOOKMARKS_FAVORITES_TAG) == 0) {
-    image = gtk_image_new_from_icon_name ("emblem-favorite-symbolic", GTK_ICON_SIZE_MENU);
+    image = gtk_image_new_from_icon_name ("emblem-favorite-symbolic");
   } else {
-    image = gtk_image_new_from_icon_name ("ephy-bookmark-tag-symbolic", GTK_ICON_SIZE_MENU);
+    image = gtk_image_new_from_icon_name ("ephy-bookmark-tag-symbolic");
   }
   label = gtk_label_new (tag);
 
@@ -251,11 +250,10 @@ create_tag_row (const char *tag)
   gtk_label_set_xalign (GTK_LABEL (label), 0);
   gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
 
-  gtk_box_pack_start (GTK_BOX (box), image, FALSE, TRUE, 0);
-  gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (box), image);
+  gtk_box_append (GTK_BOX (box), label);
 
-  gtk_container_add (GTK_CONTAINER (row), box);
-  gtk_widget_show_all (row);
+  gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box);
 
   return row;
 }
@@ -273,7 +271,7 @@ ephy_bookmarks_popover_bookmark_added_cb (EphyBookmarksPopover *self,
 
   if (g_sequence_is_empty (ephy_bookmark_get_tags (bookmark))) {
     row = create_bookmark_row (bookmark, self);
-    gtk_container_add (GTK_CONTAINER (self->tags_list_box), row);
+    gtk_list_box_append (GTK_LIST_BOX (self->tags_list_box), row);
   }
 
   if (strcmp (gtk_stack_get_visible_child_name (GTK_STACK (self->toplevel_stack)), "empty-state") == 0)
@@ -315,7 +313,7 @@ ephy_bookmarks_popover_tag_created_cb (EphyBookmarksPopover *self,
   g_assert (EPHY_IS_BOOKMARKS_MANAGER (manager));
 
   tag_row = create_tag_row (tag);
-  gtk_container_add (GTK_CONTAINER (self->tags_list_box), tag_row);
+  gtk_list_box_append (GTK_LIST_BOX (self->tags_list_box), tag_row);
 }
 
 static void
@@ -330,8 +328,8 @@ ephy_bookmarks_popover_tag_deleted_cb (EphyBookmarksPopover *self,
   g_assert (EPHY_IS_BOOKMARKS_MANAGER (manager));
 
   row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->tags_list_box), position);
-  gtk_container_remove (GTK_CONTAINER (self->tags_list_box),
-                        GTK_WIDGET (row));
+  gtk_list_box_remove (GTK_LIST_BOX (self->tags_list_box),
+                       GTK_WIDGET (row));
 
   if (g_strcmp0 (gtk_stack_get_visible_child_name (GTK_STACK (self->toplevel_stack)), "tag_detail") == 0 &&
       g_strcmp0 (self->tag_detail_tag, tag) == 0) {
@@ -369,16 +367,6 @@ tags_list_box_sort_func (GtkListBoxRow *row1,
   return g_strcmp0 (title1, title2);
 }
 
-static void
-ephy_bookmarks_popover_actions_tag_detail_back (GSimpleAction *action,
-                                                GVariant      *value,
-                                                gpointer       user_data)
-{
-  EphyBookmarksPopover *self = user_data;
-
-  tag_detail_back (self);
-}
-
 static void
 ephy_bookmarks_popover_show_tag_detail (EphyBookmarksPopover *self,
                                         const char           *tag)
@@ -394,7 +382,7 @@ ephy_bookmarks_popover_show_tag_detail (EphyBookmarksPopover *self,
     GtkWidget *row;
 
     row = create_bookmark_row (bookmark, self);
-    gtk_container_add (GTK_CONTAINER (self->tag_detail_list_box), row);
+    gtk_list_box_append (GTK_LIST_BOX (self->tag_detail_list_box), row);
   }
 
   gtk_label_set_label (GTK_LABEL (self->tag_detail_label), tag);
@@ -410,61 +398,64 @@ ephy_bookmarks_popover_show_tag_detail (EphyBookmarksPopover *self,
 
 static void
 ephy_bookmarks_popover_open_bookmark (EphyBookmarksPopover *self,
-                                      GtkListBoxRow        *row)
+                                      GtkListBoxRow        *row,
+                                      EphyLinkFlags         flags)
 {
   GtkWidget *window;
   const char *url;
-  GdkEventType type = GDK_NOTHING;
-  guint state = 0, button = (guint) - 1;
-  EphyLinkFlags flags;
 
   window = gtk_widget_get_ancestor (GTK_WIDGET (self), EPHY_TYPE_WINDOW);
   g_assert (EPHY_IS_WINDOW (window));
   url = ephy_bookmark_row_get_bookmark_url (EPHY_BOOKMARK_ROW (row));
 
-  ephy_gui_get_current_event (&type, &state, &button, NULL);
-  flags = ephy_link_flags_from_modifiers (state, button == GDK_BUTTON_MIDDLE &&
-                                          (type == GDK_BUTTON_PRESS ||
-                                           type == GDK_BUTTON_RELEASE));
-
   ephy_link_open (EPHY_LINK (window), url, NULL, flags | EPHY_LINK_BOOKMARK);
 }
 
 static void
-ephy_bookmarks_popover_list_box_row_activated_cb (EphyBookmarksPopover *self,
-                                                  GtkListBoxRow        *row,
-                                                  GtkListBox           *box)
+row_clicked_cb (GtkGesture           *gesture,
+                int                   n_click,
+                double                x,
+                double                y,
+                EphyBookmarksPopover *self)
 {
+  GtkWidget *list;
+  GtkListBoxRow *row;
+  guint button;
   const char *type;
-  const char *tag;
 
-  g_assert (EPHY_IS_BOOKMARKS_POPOVER (self));
-  g_assert (GTK_IS_LIST_BOX_ROW (row));
-  g_assert (GTK_IS_LIST_BOX (box));
+  button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+
+  if (button != GDK_BUTTON_PRIMARY && button != GDK_BUTTON_MIDDLE) {
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+    return;
+  }
+
+  list = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+
+  g_assert (GTK_IS_LIST_BOX (list));
+
+  row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (list), y);
+
+  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
 
   type = g_object_get_data (G_OBJECT (row), "type");
   if (g_strcmp0 (type, EPHY_LIST_BOX_ROW_TYPE_BOOKMARK) == 0) {
-    ephy_bookmarks_popover_open_bookmark (self, row);
-  } else {
-    tag = g_object_get_data (G_OBJECT (row), "title");
-    ephy_bookmarks_popover_show_tag_detail (self, tag);
-  }
-}
+    GdkModifierType modifiers;
+    EphyLinkFlags flags;
 
-static gboolean
-ephy_bookmarks_popover_list_box_button_release_event_cb (EphyBookmarksPopover *self,
-                                                         GdkEvent             *event,
-                                                         GtkListBox           *box)
-{
-  GdkEventButton *event_button = (GdkEventButton *)event;
-  GtkListBoxRow *row = gtk_list_box_get_row_at_y (box, event_button->y);
+    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
+    modifiers &= gtk_accelerator_get_default_mod_mask ();
 
-  if (event_button->button != GDK_BUTTON_MIDDLE)
-    return GDK_EVENT_PROPAGATE;
+    flags = ephy_link_flags_from_modifiers (modifiers, button == GDK_BUTTON_MIDDLE);
 
-  ephy_bookmarks_popover_open_bookmark (self, row);
+    ephy_bookmarks_popover_open_bookmark (self, row, flags);
 
-  return GDK_EVENT_STOP;
+    if (button != GDK_BUTTON_MIDDLE)
+      gtk_popover_popdown (GTK_POPOVER (self));
+  } else {
+    const char *tag = g_object_get_data (G_OBJECT (row), "title");
+    ephy_bookmarks_popover_show_tag_detail (self, tag);
+  }
 }
 
 static void
@@ -491,11 +482,10 @@ ephy_bookmarks_popover_class_init (EphyBookmarksPopoverClass *klass)
   gtk_widget_class_bind_template_child (widget_class, EphyBookmarksPopover, tags_list_box);
   gtk_widget_class_bind_template_child (widget_class, EphyBookmarksPopover, tag_detail_list_box);
   gtk_widget_class_bind_template_child (widget_class, EphyBookmarksPopover, tag_detail_label);
-}
 
-static const GActionEntry entries[] = {
-  { "tag-detail-back", ephy_bookmarks_popover_actions_tag_detail_back }
-};
+  gtk_widget_class_install_action (widget_class, "popover.tag-detail-back", NULL,
+                                   (GtkWidgetActionActivateFunc)tag_detail_back);
+}
 
 static void
 ephy_bookmarks_popover_init (EphyBookmarksPopover *self)
@@ -503,18 +493,12 @@ ephy_bookmarks_popover_init (EphyBookmarksPopover *self)
   GSequence *tags;
   GSequenceIter *iter;
   g_autoptr (GSequence) bookmarks = NULL;
-  g_autoptr (GSimpleActionGroup) group = NULL;
+  GtkGesture *gesture;
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
   self->manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
 
-  group = g_simple_action_group_new ();
-  g_action_map_add_action_entries (G_ACTION_MAP (group), entries,
-                                   G_N_ELEMENTS (entries), self);
-  gtk_widget_insert_action_group (GTK_WIDGET (self), "popover",
-                                  G_ACTION_GROUP (group));
-
   gtk_list_box_bind_model (GTK_LIST_BOX (self->bookmarks_list_box),
                            G_LIST_MODEL (self->manager),
                            create_bookmark_row,
@@ -539,7 +523,7 @@ ephy_bookmarks_popover_init (EphyBookmarksPopover *self)
 
     if (!ephy_bookmarks_manager_has_bookmarks_with_tag (self->manager, tag)) {
       tag_row = create_tag_row (tag);
-      gtk_container_add (GTK_CONTAINER (self->tags_list_box), tag_row);
+      gtk_list_box_append (GTK_LIST_BOX (self->tags_list_box), tag_row);
     }
   }
 
@@ -551,8 +535,7 @@ ephy_bookmarks_popover_init (EphyBookmarksPopover *self)
     GtkWidget *bookmark_row;
 
     bookmark_row = create_bookmark_row (bookmark, self);
-    gtk_widget_show_all (bookmark_row);
-    gtk_container_add (GTK_CONTAINER (self->tags_list_box), bookmark_row);
+    gtk_list_box_append (GTK_LIST_BOX (self->tags_list_box), bookmark_row);
   }
 
   g_signal_connect_object (self->manager, "bookmark-added",
@@ -574,18 +557,20 @@ ephy_bookmarks_popover_init (EphyBookmarksPopover *self)
                            G_CALLBACK (ephy_bookmarks_popover_bookmark_tag_removed_cb),
                            self, G_CONNECT_SWAPPED);
 
-  g_signal_connect_object (self->bookmarks_list_box, "row-activated",
-                           G_CALLBACK (ephy_bookmarks_popover_list_box_row_activated_cb),
-                           self, G_CONNECT_SWAPPED);
-  g_signal_connect_object (self->tags_list_box, "row-activated",
-                           G_CALLBACK (ephy_bookmarks_popover_list_box_row_activated_cb),
-                           self, G_CONNECT_SWAPPED);
-  g_signal_connect_object (self->tag_detail_list_box, "row-activated",
-                           G_CALLBACK (ephy_bookmarks_popover_list_box_row_activated_cb),
-                           self, G_CONNECT_SWAPPED);
-  g_signal_connect_object (self->bookmarks_list_box, "button-release-event",
-                           G_CALLBACK (ephy_bookmarks_popover_list_box_button_release_event_cb),
-                           self, G_CONNECT_SWAPPED);
+  gesture = gtk_gesture_click_new ();
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
+  g_signal_connect (gesture, "released", G_CALLBACK (row_clicked_cb), self);
+  gtk_widget_add_controller (self->bookmarks_list_box, GTK_EVENT_CONTROLLER (gesture));
+
+  gesture = gtk_gesture_click_new ();
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
+  g_signal_connect (gesture, "released", G_CALLBACK (row_clicked_cb), self);
+  gtk_widget_add_controller (self->tags_list_box, GTK_EVENT_CONTROLLER (gesture));
+
+  gesture = gtk_gesture_click_new ();
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
+  g_signal_connect (gesture, "released", G_CALLBACK (row_clicked_cb), self);
+  gtk_widget_add_controller (self->tag_detail_list_box, GTK_EVENT_CONTROLLER (gesture));
 }
 
 EphyBookmarksPopover *
diff --git a/src/ephy-action-bar-end.c b/src/ephy-action-bar-end.c
index 5af19cf7b..864eccc07 100644
--- a/src/ephy-action-bar-end.c
+++ b/src/ephy-action-bar-end.c
@@ -22,48 +22,35 @@
 #include "ephy-action-bar-end.h"
 #include "ephy-add-bookmark-popover.h"
 #include "ephy-desktop-utils.h"
+#include "ephy-downloads-paintable.h"
 #include "ephy-downloads-popover.h"
-#include "ephy-location-entry.h"
 #include "ephy-shell.h"
 #include "ephy-window.h"
 
 #define NEEDS_ATTENTION_ANIMATION_TIMEOUT 2000 /*ms */
-#define ANIMATION_X_GROW 30
-#define ANIMATION_Y_GROW 30
 
 struct _EphyActionBarEnd {
   GtkBox parent_instance;
 
   GtkWidget *bookmark_button;
-  GtkWidget *bookmark_image;
   GtkWidget *bookmarks_button;
   GtkWidget *downloads_revealer;
   GtkWidget *downloads_button;
   GtkWidget *downloads_popover;
   GtkWidget *downloads_icon;
-  GtkWidget *downloads_progress;
   GtkWidget *browser_action_box;
 
+  GdkPaintable *downloads_paintable;
+
   guint downloads_button_attention_timeout_id;
 };
 
 G_DEFINE_TYPE (EphyActionBarEnd, ephy_action_bar_end, GTK_TYPE_BOX)
 
-static void begin_complete_theatrics (EphyActionBarEnd *self);
-
-static void
-remove_downloads_button_attention_style (EphyActionBarEnd *self)
-{
-  GtkStyleContext *style_context;
-
-  style_context = gtk_widget_get_style_context (self->downloads_button);
-  gtk_style_context_remove_class (style_context, "epiphany-downloads-button-needs-attention");
-}
-
 static gboolean
-on_remove_downloads_button_attention_style_timeout_cb (EphyActionBarEnd *self)
+add_attention_timeout_cb (EphyActionBarEnd *self)
 {
-  remove_downloads_button_attention_style (self);
+  gtk_widget_remove_css_class (self->downloads_icon, "accent");
   self->downloads_button_attention_timeout_id = 0;
 
   return G_SOURCE_REMOVE;
@@ -72,16 +59,11 @@ on_remove_downloads_button_attention_style_timeout_cb (EphyActionBarEnd *self)
 static void
 add_attention (EphyActionBarEnd *self)
 {
-  GtkStyleContext *style_context;
-
-  style_context = gtk_widget_get_style_context (self->downloads_button);
-
   g_clear_handle_id (&self->downloads_button_attention_timeout_id, g_source_remove);
-  remove_downloads_button_attention_style (self);
 
-  gtk_style_context_add_class (style_context, "epiphany-downloads-button-needs-attention");
+  gtk_widget_add_css_class (self->downloads_icon, "accent");
   self->downloads_button_attention_timeout_id = g_timeout_add (NEEDS_ATTENTION_ANIMATION_TIMEOUT,
-                                                               
(GSourceFunc)on_remove_downloads_button_attention_style_timeout_cb,
+                                                               G_SOURCE_FUNC (add_attention_timeout_cb),
                                                                self);
 }
 
@@ -90,102 +72,14 @@ download_added_cb (EphyDownloadsManager *manager,
                    EphyDownload         *download,
                    EphyActionBarEnd     *action_bar_end)
 {
-  GtkAllocation rect;
-  DzlBoxTheatric *theatric;
-
   if (!action_bar_end->downloads_popover) {
-    action_bar_end->downloads_popover = ephy_downloads_popover_new (action_bar_end->downloads_button);
+    action_bar_end->downloads_popover = ephy_downloads_popover_new ();
     gtk_menu_button_set_popover (GTK_MENU_BUTTON (action_bar_end->downloads_button),
                                  action_bar_end->downloads_popover);
   }
 
   add_attention (action_bar_end);
   gtk_revealer_set_reveal_child (GTK_REVEALER (action_bar_end->downloads_revealer), TRUE);
-
-  if (gtk_widget_is_visible (GTK_WIDGET (action_bar_end))) {
-    gtk_widget_get_allocation (GTK_WIDGET (action_bar_end->downloads_button), &rect);
-    theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
-                             "alpha", 0.9,
-                             "background", "#fdfdfd",
-                             "target", action_bar_end->downloads_button,
-                             "height", rect.height,
-                             "width", rect.width,
-                             "x", rect.x,
-                             "y", rect.y,
-                             NULL);
-
-    dzl_object_animate_full (theatric,
-                             DZL_ANIMATION_EASE_IN_CUBIC,
-                             250,
-                             gtk_widget_get_frame_clock (GTK_WIDGET (action_bar_end->downloads_button)),
-                             g_object_unref,
-                             theatric,
-                             "x", rect.x - ANIMATION_X_GROW,
-                             "width", rect.width + (ANIMATION_X_GROW * 2),
-                             "y", rect.y - ANIMATION_Y_GROW,
-                             "height", rect.height + (ANIMATION_Y_GROW * 2),
-                             "alpha", 0.0,
-                             NULL);
-  }
-}
-
-static gboolean
-begin_complete_theatrics_from_main (gpointer user_data)
-{
-  EphyActionBarEnd *self = user_data;
-  GtkAllocation rect;
-
-  gtk_widget_get_allocation (GTK_WIDGET (self->downloads_button), &rect);
-  if (rect.x != -1 && rect.y != -1)
-    begin_complete_theatrics (self);
-
-  return G_SOURCE_REMOVE;
-}
-
-static void
-begin_complete_theatrics (EphyActionBarEnd *self)
-{
-  g_autoptr (GIcon) icon = NULL;
-  DzlBoxTheatric *theatric;
-  GtkAllocation rect;
-
-  gtk_widget_get_allocation (GTK_WIDGET (self->downloads_button), &rect);
-
-  if (rect.x == -1 && rect.y == -1) {
-    /* Delay this until our widget has been mapped/realized/displayed */
-    g_idle_add_full (G_PRIORITY_LOW,
-                     begin_complete_theatrics_from_main,
-                     g_object_ref (self), g_object_unref);
-    return;
-  }
-
-  rect.x = 0;
-  rect.y = 0;
-
-  icon = g_themed_icon_new ("folder-download-symbolic");
-
-  theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
-                           "alpha", 1.0,
-                           "height", rect.height,
-                           "icon", icon,
-                           "target", self,
-                           "width", rect.width,
-                           "x", rect.x,
-                           "y", rect.y,
-                           NULL);
-
-  dzl_object_animate_full (theatric,
-                           DZL_ANIMATION_EASE_OUT_CUBIC,
-                           750,
-                           gtk_widget_get_frame_clock (GTK_WIDGET (self)),
-                           g_object_unref,
-                           theatric,
-                           "x", rect.x - 60,
-                           "width", rect.width + 120,
-                           "y", rect.y,
-                           "height", rect.height + 120,
-                           "alpha", 0.0,
-                           NULL);
 }
 
 static void
@@ -193,8 +87,7 @@ download_completed_cb (EphyDownloadsManager *manager,
                        EphyDownload         *download,
                        EphyActionBarEnd     *action_bar_end)
 {
-  if (gtk_widget_get_mapped (GTK_WIDGET (action_bar_end)))
-    begin_complete_theatrics (action_bar_end);
+  ephy_downloads_paintable_animate_done (EPHY_DOWNLOADS_PAINTABLE (action_bar_end->downloads_paintable));
 }
 
 static void
@@ -212,7 +105,7 @@ downloads_estimated_progress_cb (EphyDownloadsManager *manager,
 {
   gdouble fraction = ephy_downloads_manager_get_estimated_progress (manager);
 
-  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (action_bar_end->downloads_progress), fraction);
+  g_object_set (action_bar_end->downloads_paintable, "progress", fraction, NULL);
 }
 
 static void
@@ -220,7 +113,7 @@ show_downloads_cb (EphyDownloadsManager *manager,
                    EphyActionBarEnd     *action_bar_end)
 {
   if (gtk_widget_get_mapped (GTK_WIDGET (action_bar_end)))
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (action_bar_end->downloads_button), TRUE);
+    gtk_menu_button_popup (GTK_MENU_BUTTON (action_bar_end->downloads_button));
 }
 
 static void
@@ -234,9 +127,6 @@ ephy_action_bar_end_class_init (EphyActionBarEndClass *klass)
   gtk_widget_class_bind_template_child (widget_class,
                                         EphyActionBarEnd,
                                         bookmark_button);
-  gtk_widget_class_bind_template_child (widget_class,
-                                        EphyActionBarEnd,
-                                        bookmark_image);
   gtk_widget_class_bind_template_child (widget_class,
                                         EphyActionBarEnd,
                                         bookmarks_button);
@@ -249,9 +139,6 @@ ephy_action_bar_end_class_init (EphyActionBarEndClass *klass)
   gtk_widget_class_bind_template_child (widget_class,
                                         EphyActionBarEnd,
                                         downloads_icon);
-  gtk_widget_class_bind_template_child (widget_class,
-                                        EphyActionBarEnd,
-                                        downloads_progress);
   gtk_widget_class_bind_template_child (widget_class,
                                         EphyActionBarEnd,
                                         browser_action_box);
@@ -273,19 +160,17 @@ ephy_action_bar_end_init (EphyActionBarEnd *action_bar_end)
                                  ephy_downloads_manager_get_downloads (downloads_manager) != NULL);
 
   if (ephy_downloads_manager_get_downloads (downloads_manager)) {
-    action_bar_end->downloads_popover = ephy_downloads_popover_new (action_bar_end->downloads_button);
+    action_bar_end->downloads_popover = ephy_downloads_popover_new ();
     gtk_menu_button_set_popover (GTK_MENU_BUTTON (action_bar_end->downloads_button), 
action_bar_end->downloads_popover);
   }
 
+  action_bar_end->downloads_paintable = ephy_downloads_paintable_new (action_bar_end->downloads_icon);
+  gtk_image_set_from_paintable (GTK_IMAGE (action_bar_end->downloads_icon),
+                                action_bar_end->downloads_paintable);
+
   if (is_desktop_pantheon ()) {
-    gtk_button_set_image (GTK_BUTTON (action_bar_end->bookmarks_button),
-                          gtk_image_new_from_icon_name ("user-bookmarks",
-                                                        GTK_ICON_SIZE_LARGE_TOOLBAR));
-    gtk_image_set_from_icon_name (GTK_IMAGE (action_bar_end->downloads_icon),
-                                  "browser-download",
-                                  GTK_ICON_SIZE_LARGE_TOOLBAR);
-    gtk_style_context_add_class (gtk_widget_get_style_context (action_bar_end->downloads_button),
-                                 "flat");
+    gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (action_bar_end->bookmarks_button),
+                                   "user-bookmarks");
   }
 
   g_signal_connect_object (downloads_manager, "download-added",
@@ -327,14 +212,14 @@ void
 ephy_action_bar_end_show_downloads (EphyActionBarEnd *action_bar_end)
 {
   if (gtk_widget_get_visible (action_bar_end->downloads_button))
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (action_bar_end->downloads_button), TRUE);
+    gtk_menu_button_popup (GTK_MENU_BUTTON (action_bar_end->downloads_button));
 }
 
 void
 ephy_action_bar_end_show_bookmarks (EphyActionBarEnd *action_bar_end)
 {
   if (gtk_widget_get_visible (action_bar_end->bookmarks_button))
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (action_bar_end->bookmarks_button), TRUE);
+    gtk_menu_button_popup (GTK_MENU_BUTTON (action_bar_end->bookmarks_button));
 }
 
 GtkWidget *
@@ -353,7 +238,7 @@ void
 ephy_action_bar_end_add_browser_action (EphyActionBarEnd *action_bar_end,
                                         GtkWidget        *action)
 {
-  gtk_container_add (GTK_CONTAINER (action_bar_end->browser_action_box), action);
+  gtk_box_append (GTK_BOX (action_bar_end->browser_action_box), action);
 }
 
 void
@@ -376,15 +261,13 @@ ephy_action_bar_end_set_bookmark_icon_state (EphyActionBarEnd      *action_bar_e
       break;
     case EPHY_BOOKMARK_ICON_EMPTY:
       gtk_widget_set_visible (action_bar_end->bookmark_button, TRUE);
-      gtk_image_set_from_icon_name (GTK_IMAGE (action_bar_end->bookmark_image),
-                                    "non-starred-symbolic",
-                                    GTK_ICON_SIZE_BUTTON);
+      gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (action_bar_end->bookmark_button),
+                                     "non-starred-symbolic");
       break;
     case EPHY_BOOKMARK_ICON_BOOKMARKED:
       gtk_widget_set_visible (action_bar_end->bookmark_button, TRUE);
-      gtk_image_set_from_icon_name (GTK_IMAGE (action_bar_end->bookmark_image),
-                                    "starred-symbolic",
-                                    GTK_ICON_SIZE_BUTTON);
+      gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (action_bar_end->bookmark_button),
+                                     "starred-symbolic");
       break;
     default:
       g_assert_not_reached ();
diff --git a/src/ephy-action-bar-start.c b/src/ephy-action-bar-start.c
index 913700590..2e426342d 100644
--- a/src/ephy-action-bar-start.c
+++ b/src/ephy-action-bar-start.c
@@ -43,12 +43,11 @@ struct _EphyActionBarStart {
   GtkWidget *navigation_back;
   GtkWidget *navigation_forward;
   GtkWidget *combined_stop_reload_button;
-  GtkWidget *combined_stop_reload_image;
   GtkWidget *homepage_button;
   GtkWidget *new_tab_button;
   GtkWidget *placeholder;
 
-  guint navigation_buttons_menu_timeout;
+  GtkWidget *history_menu;
 };
 
 G_DEFINE_TYPE (EphyActionBarStart, ephy_action_bar_start, GTK_TYPE_BOX)
@@ -63,105 +62,44 @@ typedef enum {
   WEBKIT_HISTORY_FORWARD
 } WebKitHistoryType;
 
-typedef struct {
-  GtkWidget *button;
-  EphyWindow *window;
-  EphyNavigationHistoryDirection direction;
-  GdkEventButton *event;
-} PopupData;
-
 #define MAX_LABEL_LENGTH 48
 #define HISTORY_ITEM_DATA_KEY "history-item-data-key"
 
-static gboolean
-item_enter_notify_event_cb (GtkWidget   *widget,
-                            GdkEvent    *event,
-                            EphyWebView *view)
+static void
+history_row_enter_cb (GtkEventController *controller,
+                      double              x,
+                      double              y,
+                      EphyActionBarStart *action_bar_start)
 {
-  char *text;
+  GtkWidget *widget;
+  const char *text;
+  GtkRoot *root;
+  EphyEmbed *embed;
+  WebKitWebView *web_view;
 
+  widget = gtk_event_controller_get_widget (controller);
   text = g_object_get_data (G_OBJECT (widget), "link-message");
-  ephy_web_view_set_link_message (view, text);
 
-  return GDK_EVENT_PROPAGATE;
-}
+  root = gtk_widget_get_root (GTK_WIDGET (action_bar_start));
+  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (root));
+  web_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed);
 
-static gboolean
-item_leave_notify_event_cb (GtkWidget   *widget,
-                            GdkEvent    *event,
-                            EphyWebView *view)
-{
-  ephy_web_view_set_link_message (view, NULL);
-  return GDK_EVENT_PROPAGATE;
+  ephy_web_view_set_link_message (EPHY_WEB_VIEW (web_view), text);
 }
 
 static void
-icon_loaded_cb (GObject      *source,
-                GAsyncResult *result,
-                GtkWidget    *image)
-{
-  WebKitFaviconDatabase *database = WEBKIT_FAVICON_DATABASE (source);
-  GdkPixbuf *favicon = NULL;
-  cairo_surface_t *icon_surface = webkit_favicon_database_get_favicon_finish (database, result, NULL);
-
-  if (icon_surface) {
-    gint scale = gtk_widget_get_scale_factor (image);
-
-    favicon = ephy_pixbuf_get_from_surface_scaled (icon_surface, FAVICON_SIZE * scale, FAVICON_SIZE * scale);
-    cairo_surface_destroy (icon_surface);
-  }
-
-  if (favicon)
-    gtk_image_set_from_gicon (GTK_IMAGE (image), G_ICON (favicon), GTK_ICON_SIZE_MENU);
-
-  g_object_unref (image);
-}
-
-static GtkWidget *
-new_history_menu_item (EphyWebView *view,
-                       const char  *origtext,
-                       const char  *address)
+history_row_leave_cb (GtkEventController *controller,
+                      EphyActionBarStart *action_bar_start)
 {
-  GtkWidget *menu_item;
-  GtkWidget *box;
-  GtkWidget *image;
-  GtkWidget *label;
-  WebKitFaviconDatabase *database;
-  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
-
-  g_assert (address != NULL && origtext != NULL);
-
-  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
-
-  image = gtk_image_new ();
-  gtk_box_pack_start (GTK_BOX (box), image, FALSE, TRUE, 0);
-
-  label = gtk_label_new (origtext);
-  gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
-  gtk_label_set_max_width_chars (GTK_LABEL (label), MAX_LABEL_LENGTH);
-  gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
-  gtk_widget_set_hexpand (label, TRUE);
-  gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
-
-  menu_item = gtk_menu_item_new ();
-  gtk_container_add (GTK_CONTAINER (menu_item), box);
-
-  database = webkit_web_context_get_favicon_database (ephy_embed_shell_get_web_context (shell));
-  webkit_favicon_database_get_favicon (database, address,
-                                       NULL,
-                                       (GAsyncReadyCallback)icon_loaded_cb,
-                                       g_object_ref (image));
-
-  g_object_set_data_full (G_OBJECT (menu_item), "link-message", g_strdup (address), (GDestroyNotify)g_free);
-
-  g_signal_connect (menu_item, "enter-notify-event",
-                    G_CALLBACK (item_enter_notify_event_cb), view);
-  g_signal_connect (menu_item, "leave-notify-event",
-                    G_CALLBACK (item_leave_notify_event_cb), view);
+  GtkRoot *root;
+  EphyEmbed *embed;
+  WebKitWebView *web_view;
 
-  gtk_widget_show_all (menu_item);
+  root = gtk_widget_get_root (GTK_WIDGET (action_bar_start));
+  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (root));
+  web_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed);
 
-  return menu_item;
+  ephy_web_view_set_link_message (EPHY_WEB_VIEW (web_view), NULL);
 }
 
 static void
@@ -172,7 +110,7 @@ middle_click_handle_on_history_menu_item (EphyEmbed                 *embed,
   const gchar *url;
 
   new_embed = ephy_shell_new_tab (ephy_shell_get_default (),
-                                  EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed))),
+                                  EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (embed))),
                                   embed,
                                   0);
   g_assert (new_embed != NULL);
@@ -182,29 +120,72 @@ middle_click_handle_on_history_menu_item (EphyEmbed                 *embed,
   ephy_web_view_load_url (ephy_embed_get_web_view (new_embed), url);
 }
 
-static gboolean
-navigation_menu_item_pressed_cb (GtkWidget *menuitem,
-                                 GdkEvent  *event,
-                                 gpointer   user_data)
+static void
+history_row_released_cb (GtkGesture         *gesture,
+                         int                 n_click,
+                         double              x,
+                         double              y,
+                         EphyActionBarStart *action_bar_start)
 {
-  EphyWindow *window = EPHY_WINDOW (user_data);
   WebKitBackForwardListItem *item;
+  GtkWidget *widget;
+  guint button;
+  GtkRoot *root;
   EphyEmbed *embed;
 
-  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
+  widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+
+  if (!gtk_widget_contains (widget, x, y)) {
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+    return;
+  }
+
+  button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+
+  if (button != GDK_BUTTON_PRIMARY && button != GDK_BUTTON_MIDDLE) {
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+    return;
+  }
+
+  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
 
-  item = (WebKitBackForwardListItem *)g_object_get_data (G_OBJECT (menuitem), HISTORY_ITEM_DATA_KEY);
+  root = gtk_widget_get_root (GTK_WIDGET (action_bar_start));
+  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (root));
 
-  if (((GdkEventButton *)event)->button == GDK_BUTTON_MIDDLE) {
+  item = WEBKIT_BACK_FORWARD_LIST_ITEM (g_object_get_data (G_OBJECT (widget), HISTORY_ITEM_DATA_KEY));
+
+  if (button == GDK_BUTTON_MIDDLE) {
     middle_click_handle_on_history_menu_item (embed, item);
   } else {
     WebKitWebView *web_view;
 
     web_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed);
     webkit_web_view_go_to_back_forward_list_item (web_view, item);
+
+    gtk_popover_popdown (GTK_POPOVER (action_bar_start->history_menu));
+  }
+}
+
+static void
+icon_loaded_cb (GObject      *source,
+                GAsyncResult *result,
+                GtkWidget    *image)
+{
+  WebKitFaviconDatabase *database = WEBKIT_FAVICON_DATABASE (source);
+  GdkPixbuf *favicon = NULL;
+  cairo_surface_t *icon_surface = webkit_favicon_database_get_favicon_finish (database, result, NULL);
+
+  if (icon_surface) {
+    int scale = gtk_widget_get_scale_factor (image);
+
+    favicon = ephy_pixbuf_get_from_surface_scaled (icon_surface, FAVICON_SIZE * scale, FAVICON_SIZE * scale);
+    cairo_surface_destroy (icon_surface);
   }
 
-  return GDK_EVENT_STOP;
+  if (favicon)
+    gtk_image_set_from_pixbuf (GTK_IMAGE (image), favicon);
+
+  g_object_unref (image);
 }
 
 static GList *
@@ -221,19 +202,81 @@ construct_webkit_history_list (WebKitWebView     *web_view,
 }
 
 static GtkWidget *
-build_dropdown_menu (EphyWindow                     *window,
-                     EphyNavigationHistoryDirection  direction)
+build_history_row (EphyActionBarStart        *action_bar_start,
+                   WebKitBackForwardListItem *item)
 {
-  GtkMenuShell *menu;
-  EphyEmbed *embed;
-  GList *list, *l;
-  WebKitWebView *web_view;
+  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+  const char *uri;
+  g_autofree char *title = NULL;
+  WebKitFaviconDatabase *database;
+  GtkWidget *row, *box, *icon, *label;
+  GtkEventController *controller;
+  GtkGesture *gesture;
+
+  uri = webkit_back_forward_list_item_get_uri (item);
+  title = g_strdup (webkit_back_forward_list_item_get_title (item));
+
+  row = gtk_list_box_row_new ();
+
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+  gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box);
+
+  icon = gtk_image_new ();
+  gtk_image_set_pixel_size (GTK_IMAGE (icon), 16);
+  gtk_box_append (GTK_BOX (box), icon);
+
+  label = gtk_label_new (NULL);
+  gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+  gtk_label_set_max_width_chars (GTK_LABEL (label), MAX_LABEL_LENGTH);
+  gtk_label_set_single_line_mode (GTK_LABEL (label), TRUE);
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_box_append (GTK_BOX (box), label);
+
+  g_object_set_data_full (G_OBJECT (row), HISTORY_ITEM_DATA_KEY,
+                          g_object_ref (item), g_object_unref);
+
+  if (title && *title)
+    gtk_label_set_label (GTK_LABEL (label), title);
+  else
+    gtk_label_set_label (GTK_LABEL (label), uri);
+
+  database = webkit_web_context_get_favicon_database (ephy_embed_shell_get_web_context (shell));
+  webkit_favicon_database_get_favicon (database, uri,
+                                       NULL,
+                                       (GAsyncReadyCallback)icon_loaded_cb,
+                                       g_object_ref (icon));
 
-  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
-  g_assert (embed != NULL);
+  g_object_set_data_full (G_OBJECT (row), "link-message",
+                          g_strdup (uri), (GDestroyNotify)g_free);
 
-  menu = GTK_MENU_SHELL (gtk_menu_new ());
+  controller = gtk_event_controller_motion_new ();
+  g_signal_connect (controller, "enter", G_CALLBACK (history_row_enter_cb), action_bar_start);
+  g_signal_connect (controller, "leave", G_CALLBACK (history_row_leave_cb), action_bar_start);
+  gtk_widget_add_controller (row, controller);
 
+  gesture = gtk_gesture_click_new ();
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
+  g_signal_connect (gesture, "released", G_CALLBACK (history_row_released_cb), action_bar_start);
+  gtk_widget_add_controller (row, GTK_EVENT_CONTROLLER (gesture));
+
+  return row;
+}
+
+static void
+build_history_menu (EphyActionBarStart             *action_bar_start,
+                    GtkWidget                      *parent,
+                    EphyNavigationHistoryDirection  direction)
+{
+  GtkWidget *popover, *listbox;
+  GtkRoot *root;
+  EphyEmbed *embed;
+  WebKitWebView *web_view;
+  g_autoptr (GList) list = NULL;
+  GList *l;
+
+  root = gtk_widget_get_root (GTK_WIDGET (action_bar_start));
+  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (root));
   web_view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed);
 
   if (direction == EPHY_NAVIGATION_HISTORY_DIRECTION_BACK)
@@ -243,227 +286,127 @@ build_dropdown_menu (EphyWindow                     *window,
     list = construct_webkit_history_list (web_view,
                                           WEBKIT_HISTORY_FORWARD, 10);
 
-  for (l = list; l != NULL; l = l->next) {
-    GtkWidget *item;
-    WebKitBackForwardListItem *hitem;
-    const char *uri;
-    char *title;
-
-    hitem = (WebKitBackForwardListItem *)l->data;
-    uri = webkit_back_forward_list_item_get_uri (hitem);
-    title = g_strdup (webkit_back_forward_list_item_get_title (hitem));
-
-    if (title == NULL || g_strstrip (title)[0] == '\0')
-      item = new_history_menu_item (EPHY_WEB_VIEW (web_view), uri, uri);
-    else
-      item = new_history_menu_item (EPHY_WEB_VIEW (web_view), title, uri);
+  popover = gtk_popover_new ();
+  gtk_popover_set_has_arrow (GTK_POPOVER (popover), FALSE);
+  gtk_widget_set_halign (popover, GTK_ALIGN_START);
+  gtk_widget_add_css_class (popover, "menu");
+  gtk_widget_set_parent (popover, parent);
 
-    g_free (title);
+  listbox = gtk_list_box_new ();
+  gtk_popover_set_child (GTK_POPOVER (popover), listbox);
 
-    g_object_set_data_full (G_OBJECT (item), HISTORY_ITEM_DATA_KEY,
-                            g_object_ref (hitem), g_object_unref);
+  for (l = list; l; l = l->next) {
+    GtkWidget *row = build_history_row (action_bar_start, l->data);
 
-    g_signal_connect (item, "button-release-event",
-                      G_CALLBACK (navigation_menu_item_pressed_cb), window);
-
-    gtk_menu_shell_append (menu, item);
-    gtk_widget_show_all (item);
+    gtk_list_box_append (GTK_LIST_BOX (listbox), row);
   }
 
-  g_list_free (list);
-
-  return GTK_WIDGET (menu);
+  action_bar_start->history_menu = popover;
 }
 
 static void
-popup_history_menu (GtkWidget                      *widget,
-                    EphyWindow                     *window,
-                    EphyNavigationHistoryDirection  direction,
-                    GdkEventButton                 *event)
+history_menu_closed_cb (EphyActionBarStart *action_bar_start)
 {
-  GtkWidget *menu;
-
-  menu = build_dropdown_menu (window, direction);
-  gtk_menu_popup_at_widget (GTK_MENU (menu),
-                            widget,
-                            GDK_GRAVITY_SOUTH_WEST,
-                            GDK_GRAVITY_NORTH_WEST,
-                            (GdkEvent *)event);
-}
+  GtkWidget *parent = gtk_widget_get_parent (action_bar_start->history_menu);
 
-static gboolean
-menu_timeout_cb (PopupData *data)
-{
-  if (data != NULL && data->window)
-    popup_history_menu (data->button, data->window, data->direction, data->event);
-
-  return G_SOURCE_REMOVE;
+  g_clear_pointer (&action_bar_start->history_menu, gtk_widget_unparent);
+  gtk_widget_unset_state_flags (parent, GTK_STATE_FLAG_CHECKED);
 }
 
-static gboolean
-navigation_button_press_event_cb (GtkButton *button,
-                                  GdkEvent  *event,
-                                  gpointer   user_data)
+static void
+handle_history_menu (EphyActionBarStart *action_bar_start,
+                     double              x,
+                     double              y,
+                     GtkGesture         *gesture)
 {
-  EphyActionBarStart *action_bar_start = EPHY_ACTION_BAR_START (user_data);
+  GtkWidget *widget;
   EphyNavigationHistoryDirection direction;
-  PopupData *data;
-  gboolean is_back = FALSE;
-
-  is_back = (GTK_WIDGET (button) == action_bar_start->navigation_back);
-
-  direction = is_back ? EPHY_NAVIGATION_HISTORY_DIRECTION_BACK
-                      : EPHY_NAVIGATION_HISTORY_DIRECTION_FORWARD;
-
-  switch (((GdkEventButton *)event)->button) {
-    case GDK_BUTTON_SECONDARY:
-      popup_history_menu (GTK_WIDGET (button), EPHY_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET 
(action_bar_start), EPHY_TYPE_WINDOW)),
-                          direction, (GdkEventButton *)event);
-      return GDK_EVENT_STOP;
-    case GDK_BUTTON_MIDDLE:
-      /* If a desktop-wide middle-click titlebar action is enabled, we want to
-       * prevent that action from occurring because we handle middle click in
-       * navigation_button_release_event_cb().
-       */
-      return GDK_EVENT_STOP;
-    default:
-      break;
-  }
 
-  data = g_new (PopupData, 1);
-  data->button = GTK_WIDGET (button);
-  data->window = EPHY_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (action_bar_start), EPHY_TYPE_WINDOW));
-  data->direction = direction;
-  data->event = (GdkEventButton *)event;
+  widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
 
-  action_bar_start->navigation_buttons_menu_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 500,
-                                                                          (GSourceFunc)menu_timeout_cb,
-                                                                          data,
-                                                                          (GDestroyNotify)g_free);
-  g_source_set_name_by_id (action_bar_start->navigation_buttons_menu_timeout, "[epiphany] menu_timeout_cb");
+  if (!gtk_widget_contains (widget, x, y)) {
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+    return;
+  }
 
-  return GDK_EVENT_PROPAGATE;
-}
+  if (widget == action_bar_start->navigation_back)
+    direction = EPHY_NAVIGATION_HISTORY_DIRECTION_BACK;
+  else if (widget == action_bar_start->navigation_forward)
+    direction = EPHY_NAVIGATION_HISTORY_DIRECTION_FORWARD;
+  else
+    g_assert_not_reached ();
 
-static gboolean
-navigation_button_release_event_cb (GtkButton *button,
-                                    GdkEvent  *event,
-                                    gpointer   user_data)
-{
-  EphyActionBarStart *action_bar_start = EPHY_ACTION_BAR_START (user_data);
-  GActionGroup *action_group;
-  GAction *action;
-  EphyNavigationHistoryDirection direction;
-  gboolean is_back = FALSE;
-  gboolean open_in_new_tab = FALSE;
-  gboolean open_in_current_tab = FALSE;
-  GdkEventType type = GDK_NOTHING;
-  guint state = 0, button_val = (guint) - 1, keyval = (guint) - 1;
-
-  ephy_gui_get_current_event (&type, &state, &button_val, &keyval);
-  is_back = (GTK_WIDGET (button) == action_bar_start->navigation_back);
-
-  g_clear_handle_id (&action_bar_start->navigation_buttons_menu_timeout, g_source_remove);
-
-  action_group = gtk_widget_get_action_group (gtk_widget_get_ancestor (GTK_WIDGET (action_bar_start), 
EPHY_TYPE_WINDOW), "toolbar");
-
-  direction = is_back ? EPHY_NAVIGATION_HISTORY_DIRECTION_BACK
-                      : EPHY_NAVIGATION_HISTORY_DIRECTION_FORWARD;
-
-  open_in_new_tab = (((GdkEventButton *)event)->button == GDK_BUTTON_MIDDLE) || (state == GDK_CONTROL_MASK);
-  open_in_current_tab = ((GdkEventButton *)event)->button == GDK_BUTTON_PRIMARY;
-
-  if (open_in_new_tab) {
-    if (direction == EPHY_NAVIGATION_HISTORY_DIRECTION_BACK) {
-      action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                           "navigation-back-new-tab");
-      g_action_activate (action, NULL);
-    } else if (direction == EPHY_NAVIGATION_HISTORY_DIRECTION_FORWARD) {
-      action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                           "navigation-forward-new-tab");
-      g_action_activate (action, NULL);
-    }
-
-    /* Don't propagate the event to avoid other middle-click actions. */
-    return GDK_EVENT_STOP;
-  }
+  build_history_menu (action_bar_start, widget, direction);
 
-  if (open_in_current_tab) {
-    if (direction == EPHY_NAVIGATION_HISTORY_DIRECTION_BACK) {
-      action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                           "navigation-back");
-      g_action_activate (action, NULL);
-    } else if (direction == EPHY_NAVIGATION_HISTORY_DIRECTION_FORWARD) {
-      action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                           "navigation-forward");
-      g_action_activate (action, NULL);
-    }
-
-    /* Propagate the event to allow the button to correctly reset its internal
-     * state. */
-    return GDK_EVENT_PROPAGATE;
-  }
+  gtk_popover_popup (GTK_POPOVER (action_bar_start->history_menu));
+  gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_CHECKED, FALSE);
+
+  g_signal_connect_swapped (action_bar_start->history_menu, "closed",
+                            G_CALLBACK (history_menu_closed_cb), action_bar_start);
 
-  /* Propagate other unhandled events. */
-  return GDK_EVENT_PROPAGATE;
+  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
 }
 
-static gboolean
-homepage_button_release_event_cb (GtkButton *button,
-                                  GdkEvent  *event,
-                                  gpointer   user_data)
+static void
+long_pressed_cb (GtkGesture         *gesture,
+                 double              x,
+                 double              y,
+                 EphyActionBarStart *action_bar_start)
 {
-  EphyActionBarStart *action_bar_start = EPHY_ACTION_BAR_START (user_data);
-  GActionGroup *action_group;
-  GAction *action;
-
-  action_group = gtk_widget_get_action_group (gtk_widget_get_ancestor (GTK_WIDGET (action_bar_start), 
EPHY_TYPE_WINDOW), "toolbar");
+  handle_history_menu (action_bar_start, x, y, gesture);
+}
 
-  switch (((GdkEventButton *)event)->button) {
-    case GDK_BUTTON_MIDDLE:
-      action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "homepage-new-tab");
-      g_action_activate (action, NULL);
-      return GDK_EVENT_STOP;
-    default:
-      break;
-  }
+static void
+right_click_pressed_cb (GtkGesture         *gesture,
+                        int                 n_click,
+                        double              x,
+                        double              y,
+                        EphyActionBarStart *action_bar_start)
+{
+  handle_history_menu (action_bar_start, x, y, gesture);
+}
 
-  return GDK_EVENT_PROPAGATE;
+static void
+middle_click_pressed_cb (GtkGesture *gesture)
+{
+  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
 }
 
-static gboolean
-new_tab_button_release_event_cb (GtkButton *button,
-                                 GdkEvent  *event,
-                                 gpointer   user_data)
+static void
+middle_click_released_cb (GtkGesture         *gesture,
+                          int                 n_click,
+                          double              x,
+                          double              y,
+                          EphyActionBarStart *action_bar_start)
 {
-  EphyActionBarStart *action_bar_start = EPHY_ACTION_BAR_START (user_data);
+  GtkWidget *widget;
+  EphyWindow *window;
   GActionGroup *action_group;
   GAction *action;
+  const char *action_name;
 
-  action_group = gtk_widget_get_action_group (gtk_widget_get_ancestor (GTK_WIDGET (action_bar_start), 
EPHY_TYPE_WINDOW), "toolbar");
+  widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
 
-  switch (((GdkEventButton *)event)->button) {
-    case GDK_BUTTON_MIDDLE:
-      action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "new-tab-from-clipboard");
-      g_action_activate (action, NULL);
-      return GDK_EVENT_STOP;
-    default:
-      break;
+  if (!gtk_widget_contains (widget, x, y)) {
+    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+    return;
   }
 
-  return GDK_EVENT_PROPAGATE;
-}
-
-static gboolean
-navigation_leave_notify_event_cb (GtkButton *button,
-                                  GdkEvent  *event,
-                                  gpointer   user_data)
-{
-  EphyActionBarStart *action_bar_start = EPHY_ACTION_BAR_START (user_data);
-
-  g_clear_handle_id (&action_bar_start->navigation_buttons_menu_timeout, g_source_remove);
+  if (widget == action_bar_start->navigation_back)
+    action_name = "navigation-back-new-tab";
+  else if (widget == action_bar_start->navigation_forward)
+    action_name = "navigation-forward-new-tab";
+  else if (widget == action_bar_start->homepage_button)
+    action_name = "homepage-new-tab";
+  else if (widget == action_bar_start->new_tab_button)
+    action_name = "new-tab-from-clipboard";
+  else
+    g_assert_not_reached ();
 
-  return GDK_EVENT_PROPAGATE;
+  window = EPHY_WINDOW (gtk_widget_get_root (widget));
+  action_group = ephy_window_get_action_group (window, "toolbar");
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group), action_name);
+  g_action_activate (action, NULL);
 }
 
 static void
@@ -489,7 +432,7 @@ ephy_action_bar_start_dispose (GObject *object)
 {
   EphyActionBarStart *action_bar_start = EPHY_ACTION_BAR_START (object);
 
-  g_clear_handle_id (&action_bar_start->navigation_buttons_menu_timeout, g_source_remove);
+  g_clear_pointer (&action_bar_start->history_menu, gtk_widget_unparent);
 
   G_OBJECT_CLASS (ephy_action_bar_start_parent_class)->dispose (object);
 }
@@ -516,22 +459,6 @@ ephy_action_bar_start_constructed (GObject *object)
 
   gtk_widget_init_template (GTK_WIDGET (action_bar_start));
 
-  /* Back */
-  g_signal_connect (action_bar_start->navigation_back, "button-press-event",
-                    G_CALLBACK (navigation_button_press_event_cb), action_bar_start);
-  g_signal_connect (action_bar_start->navigation_back, "button-release-event",
-                    G_CALLBACK (navigation_button_release_event_cb), action_bar_start);
-  g_signal_connect (action_bar_start->navigation_back, "leave-notify-event",
-                    G_CALLBACK (navigation_leave_notify_event_cb), action_bar_start);
-
-  /* Forward */
-  g_signal_connect (action_bar_start->navigation_forward, "button-press-event",
-                    G_CALLBACK (navigation_button_press_event_cb), action_bar_start);
-  g_signal_connect (action_bar_start->navigation_forward, "button-release-event",
-                    G_CALLBACK (navigation_button_release_event_cb), action_bar_start);
-  g_signal_connect (action_bar_start->navigation_forward, "leave-notify-event",
-                    G_CALLBACK (navigation_leave_notify_event_cb), action_bar_start);
-
   /* Combined_stop_reload */
   gtk_widget_set_tooltip_text (action_bar_start->combined_stop_reload_button, _(REFRESH_BUTTON_TOOLTIP));
 
@@ -547,27 +474,10 @@ ephy_action_bar_start_constructed (GObject *object)
   } else {
     gtk_widget_set_visible (action_bar_start->homepage_button, FALSE);
   }
-  g_signal_connect (action_bar_start->homepage_button, "button-release-event",
-                    G_CALLBACK (homepage_button_release_event_cb), action_bar_start);
 
   /* New Tab Button */
   update_new_tab_button_visibility (action_bar_start);
 
-  g_signal_connect (action_bar_start->new_tab_button, "button-release-event",
-                    G_CALLBACK (new_tab_button_release_event_cb), action_bar_start);
-
-  if (is_desktop_pantheon ()) {
-    gtk_button_set_image (GTK_BUTTON (action_bar_start->navigation_back),
-                          gtk_image_new_from_icon_name ("go-previous-symbolic",
-                                                        get_icon_size ()));
-    gtk_button_set_image (GTK_BUTTON (action_bar_start->navigation_forward),
-                          gtk_image_new_from_icon_name ("go-next-symbolic",
-                                                        get_icon_size ()));
-    gtk_button_set_image (GTK_BUTTON (action_bar_start->homepage_button),
-                          gtk_image_new_from_icon_name ("go-home-symbolic",
-                                                        get_icon_size ()));
-  }
-
   if (ephy_profile_dir_is_web_application ()) {
     GtkWidget *navigation_box = ephy_action_bar_start_get_navigation_box (action_bar_start);
 
@@ -599,9 +509,6 @@ ephy_action_bar_start_class_init (EphyActionBarStartClass *klass)
   gtk_widget_class_bind_template_child (widget_class,
                                         EphyActionBarStart,
                                         combined_stop_reload_button);
-  gtk_widget_class_bind_template_child (widget_class,
-                                        EphyActionBarStart,
-                                        combined_stop_reload_image);
   gtk_widget_class_bind_template_child (widget_class,
                                         EphyActionBarStart,
                                         homepage_button);
@@ -611,6 +518,15 @@ ephy_action_bar_start_class_init (EphyActionBarStartClass *klass)
   gtk_widget_class_bind_template_child (widget_class,
                                         EphyActionBarStart,
                                         placeholder);
+
+  gtk_widget_class_bind_template_callback (widget_class,
+                                           right_click_pressed_cb);
+  gtk_widget_class_bind_template_callback (widget_class,
+                                           long_pressed_cb);
+  gtk_widget_class_bind_template_callback (widget_class,
+                                           middle_click_pressed_cb);
+  gtk_widget_class_bind_template_callback (widget_class,
+                                           middle_click_released_cb);
 }
 
 static void
@@ -636,16 +552,14 @@ ephy_action_bar_start_change_combined_stop_reload_state (EphyActionBarStart *act
                                                          gboolean            loading)
 {
   if (loading) {
-    gtk_image_set_from_icon_name (GTK_IMAGE (action_bar_start->combined_stop_reload_image),
-                                  "process-stop-symbolic",
-                                  get_icon_size ());
+    gtk_button_set_icon_name (GTK_BUTTON (action_bar_start->combined_stop_reload_button),
+                              "process-stop-symbolic");
     /* Translators: tooltip for the stop button */
     gtk_widget_set_tooltip_text (action_bar_start->combined_stop_reload_button,
                                  _("Stop loading the current page"));
   } else {
-    gtk_image_set_from_icon_name (GTK_IMAGE (action_bar_start->combined_stop_reload_image),
-                                  "view-refresh-symbolic",
-                                  get_icon_size ());
+    gtk_button_set_icon_name (GTK_BUTTON (action_bar_start->combined_stop_reload_button),
+                              "view-refresh-symbolic");
     gtk_widget_set_tooltip_text (action_bar_start->combined_stop_reload_button,
                                  _(REFRESH_BUTTON_TOOLTIP));
   }
diff --git a/src/ephy-action-bar.c b/src/ephy-action-bar.c
index 0245b6dc5..a381a8756 100644
--- a/src/ephy-action-bar.c
+++ b/src/ephy-action-bar.c
@@ -21,7 +21,6 @@
 
 #include "ephy-action-bar.h"
 #include "ephy-add-bookmark-popover.h"
-#include "ephy-location-entry.h"
 #include "ephy-pages-button.h"
 #include "ephy-settings.h"
 #include "ephy-shell.h"
@@ -37,7 +36,7 @@ enum {
 static GParamSpec *object_properties[N_PROPERTIES] = { NULL, };
 
 struct _EphyActionBar {
-  GtkBin parent_instance;
+  AdwBin parent_instance;
 
   EphyWindow *window;
   GtkRevealer *revealer;
@@ -49,7 +48,7 @@ struct _EphyActionBar {
   gboolean can_reveal;
 };
 
-G_DEFINE_TYPE (EphyActionBar, ephy_action_bar, GTK_TYPE_BIN)
+G_DEFINE_TYPE (EphyActionBar, ephy_action_bar, ADW_TYPE_BIN)
 
 static void
 sync_chromes_visibility (EphyActionBar *action_bar)
diff --git a/src/ephy-action-bar.h b/src/ephy-action-bar.h
index bf0309b4a..b261fd1f2 100644
--- a/src/ephy-action-bar.h
+++ b/src/ephy-action-bar.h
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
+#include <adwaita.h>
 
 #include "ephy-action-bar-end.h"
 #include "ephy-action-bar-start.h"
@@ -32,7 +32,7 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_ACTION_BAR (ephy_action_bar_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyActionBar, ephy_action_bar, EPHY, ACTION_BAR, GtkBin);
+G_DECLARE_FINAL_TYPE (EphyActionBar, ephy_action_bar, EPHY, ACTION_BAR, AdwBin);
 
 EphyActionBar      *ephy_action_bar_new                  (EphyWindow *window);
 EphyActionBarStart *ephy_action_bar_get_action_bar_start (EphyActionBar *action_bar);
diff --git a/src/ephy-desktop-utils.c b/src/ephy-desktop-utils.c
index ec45135fa..90c7461a5 100644
--- a/src/ephy-desktop-utils.c
+++ b/src/ephy-desktop-utils.c
@@ -46,15 +46,6 @@ is_desktop_gnome (void)
   return strstr (xdg_current_desktop, "GNOME") != NULL;
 }
 
-GtkIconSize
-get_icon_size (void)
-{
-  if (is_desktop_pantheon ())
-    return GTK_ICON_SIZE_LARGE_TOOLBAR;
-
-  return GTK_ICON_SIZE_BUTTON;
-}
-
 const char *
 ephy_get_fallback_favicon_name (const char      *uri,
                                 EphyFaviconType  type)
diff --git a/src/ephy-desktop-utils.h b/src/ephy-desktop-utils.h
index c162f3f5a..f30134aac 100644
--- a/src/ephy-desktop-utils.h
+++ b/src/ephy-desktop-utils.h
@@ -33,8 +33,6 @@ typedef enum {
 gboolean is_desktop_pantheon (void);
 gboolean is_desktop_gnome (void);
 
-GtkIconSize get_icon_size (void);
-
 const char *ephy_get_fallback_favicon_name (const char *title, EphyFaviconType type);
 
 G_END_DECLS
diff --git a/src/ephy-encoding-dialog.c b/src/ephy-encoding-dialog.c
index e305bb14e..c20d4e461 100644
--- a/src/ephy-encoding-dialog.c
+++ b/src/ephy-encoding-dialog.c
@@ -87,7 +87,7 @@ select_encoding_row (GtkListBox   *list_box,
     EphyEncoding *ephy_encoding;
     const char *encoding_string = NULL;
 
-    ephy_encoding_row = EPHY_ENCODING_ROW (gtk_bin_get_child (GTK_BIN (row)));
+    ephy_encoding_row = EPHY_ENCODING_ROW (gtk_list_box_row_get_child (row));
     ephy_encoding = ephy_encoding_row_get_encoding (ephy_encoding_row);
     encoding_string = ephy_encoding_get_encoding (ephy_encoding);
 
@@ -216,7 +216,7 @@ ephy_encoding_dialog_response_cb (GtkWidget          *widget,
                                   int                 response,
                                   EphyEncodingDialog *dialog)
 {
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 }
 
 static void
@@ -227,7 +227,7 @@ clean_selected_list_box (GtkListBox *list_box)
 
   while ((row = gtk_list_box_get_row_at_index (list_box, i++))) {
     EphyEncodingRow *ephy_encoding_row =
-      EPHY_ENCODING_ROW (gtk_bin_get_child (GTK_BIN (row)));
+      EPHY_ENCODING_ROW (gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row)));
     ephy_encoding_row_set_selected (ephy_encoding_row, FALSE);
   }
 }
@@ -254,7 +254,7 @@ row_activated_cb (GtkListBox         *box,
 
   dialog->update_view_tag = TRUE;
 
-  ephy_encoding_row = EPHY_ENCODING_ROW (gtk_bin_get_child (GTK_BIN (row)));
+  ephy_encoding_row = EPHY_ENCODING_ROW (gtk_list_box_row_get_child (row));
   ephy_encoding = ephy_encoding_row_get_encoding (ephy_encoding_row);
   selected_encoding = ephy_encoding_get_encoding (ephy_encoding);
 
@@ -322,7 +322,7 @@ static void
 add_list_item (EphyEncoding *encoding,
                GtkListBox   *list_box)
 {
-  gtk_container_add (GTK_CONTAINER (list_box), GTK_WIDGET (ephy_encoding_row_new (encoding)));
+  gtk_list_box_append (GTK_LIST_BOX (list_box), GTK_WIDGET (ephy_encoding_row_new (encoding)));
 }
 
 static int
diff --git a/src/ephy-firefox-sync-dialog.c b/src/ephy-firefox-sync-dialog.c
index 51eff3114..b39a7bdd2 100644
--- a/src/ephy-firefox-sync-dialog.c
+++ b/src/ephy-firefox-sync-dialog.c
@@ -35,8 +35,39 @@
 
 #define FXA_IFRAME_URL "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v3";
 
+#define EPHY_TYPE_SYNC_FREQUENCY (ephy_sync_frequency_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphySyncFrequency, ephy_sync_frequency, EPHY, SYNC_FREQUENCY, GObject);
+
+struct _EphySyncFrequency {
+  GObject parent_instance;
+  guint frequency;
+};
+
+G_DEFINE_FINAL_TYPE (EphySyncFrequency, ephy_sync_frequency, G_TYPE_OBJECT)
+
+static void
+ephy_sync_frequency_class_init (EphySyncFrequencyClass *klass)
+{
+}
+
+static void
+ephy_sync_frequency_init (EphySyncFrequency *self)
+{
+}
+
+static EphySyncFrequency *
+ephy_sync_frequency_new (guint frequency)
+{
+  EphySyncFrequency *self = g_object_new (EPHY_TYPE_SYNC_FREQUENCY, NULL);
+
+  self->frequency = frequency;
+
+  return self;
+}
+
 struct _EphyFirefoxSyncDialog {
-  HdyWindow parent_instance;
+  GtkWindow parent_instance;
 
   GtkWidget *sync_page_group;
   GtkWidget *sync_firefox_iframe_box;
@@ -61,7 +92,7 @@ struct _EphyFirefoxSyncDialog {
   WebKitUserScript *fxa_script;
 };
 
-G_DEFINE_TYPE (EphyFirefoxSyncDialog, ephy_firefox_sync_dialog, HDY_TYPE_WINDOW)
+G_DEFINE_TYPE (EphyFirefoxSyncDialog, ephy_firefox_sync_dialog, GTK_TYPE_WINDOW)
 
 static const guint sync_frequency_minutes[] = { 5, 15, 30, 60 };
 
@@ -107,7 +138,7 @@ sync_set_last_sync_time (EphyFirefoxSyncDialog *sync_dialog)
      */
     char *text = g_strdup_printf (_("Last synchronized: %s"), time);
 
-    hdy_action_row_set_subtitle (HDY_ACTION_ROW (sync_dialog->sync_firefox_account_row), text);
+    adw_action_row_set_subtitle (ADW_ACTION_ROW (sync_dialog->sync_firefox_account_row), text);
 
     g_free (text);
     g_free (time);
@@ -162,7 +193,7 @@ sync_secrets_store_finished_cb (EphySyncService       *service,
   g_assert (EPHY_IS_FIREFOX_SYNC_DIALOG (sync_dialog));
 
   if (!error) {
-    hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (sync_dialog->sync_firefox_account_row),
+    adw_preferences_row_set_title (ADW_PREFERENCES_ROW (sync_dialog->sync_firefox_account_row),
                                    ephy_sync_utils_get_sync_user ());
     gtk_widget_hide (sync_dialog->sync_page_group);
     gtk_widget_show (sync_dialog->sync_firefox_account_group);
@@ -406,7 +437,7 @@ sync_open_webmail_clicked_cb (WebKitUserContentManager *manager,
   EphyShell *shell;
   EphyEmbed *embed;
   GtkWindow *window;
-  GtkWidget *prefs_dialog;
+  GtkRoot *prefs_dialog;
   char *url;
 
   url = jsc_value_to_string (webkit_javascript_result_get_js_value (result));
@@ -419,8 +450,8 @@ sync_open_webmail_clicked_cb (WebKitUserContentManager *manager,
     ephy_web_view_load_url (ephy_embed_get_web_view (embed), url);
 
     /* Close the preferences dialog. */
-    prefs_dialog = gtk_widget_get_toplevel (GTK_WIDGET (sync_page));
-    gtk_widget_destroy (GTK_WIDGET (prefs_dialog));
+    prefs_dialog = gtk_widget_get_root (GTK_WIDGET (sync_page));
+    gtk_window_destroy (GTK_WINDOW (prefs_dialog));
 
     g_free (url);
   }
@@ -433,7 +464,6 @@ sync_setup_firefox_iframe (EphyFirefoxSyncDialog *sync_dialog)
   WebKitWebsiteDataManager *manager;
   WebKitWebContext *embed_context;
   WebKitWebContext *sync_context;
-  GtkWidget *frame;
   const char *script;
 
   if (!sync_dialog->fxa_web_view) {
@@ -484,15 +514,12 @@ sync_setup_firefox_iframe (EphyFirefoxSyncDialog *sync_dialog)
                                                                "settings", ephy_embed_prefs_get_settings (),
                                                                "web-context", sync_context,
                                                                NULL));
+    gtk_widget_set_overflow (GTK_WIDGET (sync_dialog->fxa_web_view), GTK_OVERFLOW_HIDDEN);
+    gtk_widget_add_css_class (GTK_WIDGET (sync_dialog->fxa_web_view), "card");
     gtk_widget_set_vexpand (GTK_WIDGET (sync_dialog->fxa_web_view), TRUE);
     gtk_widget_set_visible (GTK_WIDGET (sync_dialog->fxa_web_view), TRUE);
-    frame = gtk_frame_new (NULL);
-    gtk_widget_set_visible (frame, TRUE);
-    gtk_container_add (GTK_CONTAINER (frame),
-                       GTK_WIDGET (sync_dialog->fxa_web_view));
-    gtk_box_pack_start (GTK_BOX (sync_dialog->sync_firefox_iframe_box),
-                        frame,
-                        FALSE, TRUE, 0);
+    gtk_box_append (GTK_BOX (sync_dialog->sync_firefox_iframe_box),
+                    GTK_WIDGET (sync_dialog->fxa_web_view));
 
     g_object_unref (sync_context);
   }
@@ -514,7 +541,7 @@ on_sync_sign_out_button_clicked (GtkWidget             *button,
   gtk_widget_hide (sync_dialog->sync_firefox_account_group);
   gtk_widget_hide (sync_dialog->sync_options_group);
   gtk_widget_show (sync_dialog->sync_page_group);
-  hdy_action_row_set_subtitle (HDY_ACTION_ROW (sync_dialog->sync_firefox_account_row), NULL);
+  adw_action_row_set_subtitle (ADW_ACTION_ROW (sync_dialog->sync_firefox_account_row), NULL);
 }
 
 static void
@@ -558,10 +585,10 @@ on_sync_device_name_save_button_clicked (GtkWidget             *button,
   EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
   const char *text;
 
-  text = gtk_entry_get_text (GTK_ENTRY (sync_dialog->sync_device_name_entry));
+  text = gtk_editable_get_text (GTK_EDITABLE (sync_dialog->sync_device_name_entry));
   if (!g_strcmp0 (text, "")) {
     char *name = ephy_sync_utils_get_device_name ();
-    gtk_entry_set_text (GTK_ENTRY (sync_dialog->sync_device_name_entry), name);
+    gtk_editable_set_text (GTK_EDITABLE (sync_dialog->sync_device_name_entry), name);
     g_free (name);
   } else {
     ephy_sync_service_update_device_name (service, text);
@@ -580,7 +607,7 @@ on_sync_device_name_cancel_button_clicked (GtkWidget             *button,
   char *name;
 
   name = ephy_sync_utils_get_device_name ();
-  gtk_entry_set_text (GTK_ENTRY (sync_dialog->sync_device_name_entry), name);
+  gtk_editable_set_text (GTK_EDITABLE (sync_dialog->sync_device_name_entry), name);
 
   gtk_widget_set_sensitive (GTK_WIDGET (sync_dialog->sync_device_name_entry), FALSE);
   gtk_widget_set_visible (GTK_WIDGET (sync_dialog->sync_device_name_change_button), TRUE);
@@ -590,6 +617,15 @@ on_sync_device_name_cancel_button_clicked (GtkWidget             *button,
   g_free (name);
 }
 
+static gchar *
+get_sync_frequency_minutes_name (EphySyncFrequency *value)
+{
+  guint n_minutes = value->frequency;
+  const char *minutes_text = ngettext ("%u min", "%u mins", n_minutes);
+
+  return g_strdup_printf (minutes_text, n_minutes);
+}
+
 static void
 prefs_sync_page_finalize (GObject *object)
 {
@@ -642,6 +678,7 @@ ephy_firefox_sync_dialog_class_init (EphyFirefoxSyncDialogClass *klass)
   gtk_widget_class_bind_template_callback (widget_class, on_sync_device_name_change_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_sync_device_name_save_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_sync_device_name_cancel_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, get_sync_frequency_minutes_name);
 }
 
 static gboolean
@@ -651,11 +688,11 @@ sync_frequency_get_mapping (GValue   *value,
 {
   uint minutes = g_variant_get_uint32 (variant);
 
-  for (gint i = 0; i < (gint)G_N_ELEMENTS (sync_frequency_minutes); i++) {
+  for (guint i = 0; i < (guint)G_N_ELEMENTS (sync_frequency_minutes); i++) {
     if (sync_frequency_minutes[i] != minutes)
       continue;
 
-    g_value_set_int (value, i);
+    g_value_set_uint (value, i);
 
     return TRUE;
   }
@@ -668,9 +705,9 @@ sync_frequency_set_mapping (const GValue       *value,
                             const GVariantType *expected_type,
                             gpointer            user_data)
 {
-  gint i = g_value_get_int (value);
+  guint i = g_value_get_uint (value);
 
-  if (i >= (gint)G_N_ELEMENTS (sync_frequency_minutes))
+  if (i >= (guint)G_N_ELEMENTS (sync_frequency_minutes))
     return NULL;
 
   return g_variant_new_uint32 (sync_frequency_minutes[i]);
@@ -679,32 +716,19 @@ sync_frequency_set_mapping (const GValue       *value,
 static GListModel *
 create_sync_frequency_minutes_model ()
 {
-  GListStore *list_store = g_list_store_new (HDY_TYPE_VALUE_OBJECT);
-  HdyValueObject *obj;
-  g_auto (GValue) value = G_VALUE_INIT;
+  GListStore *list_store = g_list_store_new (EPHY_TYPE_SYNC_FREQUENCY);
   guint i;
 
-  g_value_init (&value, G_TYPE_UINT);
-
   for (i = 0; i < G_N_ELEMENTS (sync_frequency_minutes); i++) {
-    g_value_set_uint (&value, sync_frequency_minutes[i]);
-    obj = hdy_value_object_new (&value);
-    g_list_store_insert (list_store, i, obj);
-    g_clear_object (&obj);
+    g_autoptr (EphySyncFrequency) frequency =
+      ephy_sync_frequency_new (sync_frequency_minutes[i]);
+
+    g_list_store_insert (list_store, i, frequency);
   }
 
   return G_LIST_MODEL (list_store);
 }
 
-static gchar *
-get_sync_frequency_minutes_name (HdyValueObject *value)
-{
-  guint n_minutes = g_value_get_uint (hdy_value_object_get_value (value));
-  const char *minutes_text = ngettext ("%u min", "%u mins", n_minutes);
-
-  return g_strdup_printf (minutes_text, n_minutes);
-}
-
 void
 ephy_firefox_sync_dialog_setup (EphyFirefoxSyncDialog *sync_dialog)
 {
@@ -714,7 +738,7 @@ ephy_firefox_sync_dialog_setup (EphyFirefoxSyncDialog *sync_dialog)
   char *name = ephy_sync_utils_get_device_name ();
   g_autoptr (GListModel) sync_frequency_minutes_model = create_sync_frequency_minutes_model ();
 
-  gtk_entry_set_text (GTK_ENTRY (sync_dialog->sync_device_name_entry), name);
+  gtk_editable_set_text (GTK_EDITABLE (sync_dialog->sync_device_name_entry), name);
 
   if (!user) {
     sync_setup_firefox_iframe (sync_dialog);
@@ -722,7 +746,7 @@ ephy_firefox_sync_dialog_setup (EphyFirefoxSyncDialog *sync_dialog)
     gtk_widget_hide (sync_dialog->sync_options_group);
   } else {
     sync_set_last_sync_time (sync_dialog);
-    hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (sync_dialog->sync_firefox_account_row), user);
+    adw_preferences_row_set_title (ADW_PREFERENCES_ROW (sync_dialog->sync_firefox_account_row), user);
     gtk_widget_hide (sync_dialog->sync_page_group);
   }
 
@@ -747,15 +771,12 @@ ephy_firefox_sync_dialog_setup (EphyFirefoxSyncDialog *sync_dialog)
                    "active",
                    G_SETTINGS_BIND_DEFAULT);
 
-  hdy_combo_row_bind_name_model (HDY_COMBO_ROW (sync_dialog->sync_frequency_row),
-                                 sync_frequency_minutes_model,
-                                 (HdyComboRowGetNameFunc)get_sync_frequency_minutes_name,
-                                 NULL,
-                                 NULL);
+  adw_combo_row_set_model (ADW_COMBO_ROW (sync_dialog->sync_frequency_row),
+                           sync_frequency_minutes_model);
   g_settings_bind_with_mapping (sync_settings,
                                 EPHY_PREFS_SYNC_FREQUENCY,
                                 sync_dialog->sync_frequency_row,
-                                "selected-index",
+                                "selected",
                                 G_SETTINGS_BIND_DEFAULT,
                                 sync_frequency_get_mapping,
                                 sync_frequency_set_mapping,
diff --git a/src/ephy-firefox-sync-dialog.h b/src/ephy-firefox-sync-dialog.h
index 9e20ed484..72d473702 100644
--- a/src/ephy-firefox-sync-dialog.h
+++ b/src/ephy-firefox-sync-dialog.h
@@ -21,13 +21,13 @@
 #pragma once
 
 #include <glib-object.h>
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_FIREFOX_SYNC_DIALOG (ephy_firefox_sync_dialog_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyFirefoxSyncDialog, ephy_firefox_sync_dialog, EPHY, FIREFOX_SYNC_DIALOG, HdyWindow)
+G_DECLARE_FINAL_TYPE (EphyFirefoxSyncDialog, ephy_firefox_sync_dialog, EPHY, FIREFOX_SYNC_DIALOG, GtkWindow)
 
 GtkWidget *ephy_firefox_sync_dialog_new ();
 
diff --git a/src/ephy-fullscreen-box.c b/src/ephy-fullscreen-box.c
index 5ace6fa49..a88f59192 100644
--- a/src/ephy-fullscreen-box.c
+++ b/src/ephy-fullscreen-box.c
@@ -21,17 +21,15 @@
 #include "config.h"
 #include "ephy-fullscreen-box.h"
 
-#include <handy.h>
+#include <adwaita.h>
 
 #define FULLSCREEN_HIDE_DELAY 300
 #define SHOW_HEADERBAR_DISTANCE_PX 5
 
 struct _EphyFullscreenBox {
-  GtkEventBox parent_instance;
+  GtkWidget parent_instance;
 
-  HdyFlap *flap;
-  GtkEventController *controller;
-  GtkGesture *gesture;
+  AdwFlap *flap;
 
   gboolean fullscreen;
   gboolean autohide;
@@ -45,7 +43,7 @@ struct _EphyFullscreenBox {
 
 static void ephy_fullscreen_box_buildable_init (GtkBuildableIface *iface);
 
-G_DEFINE_TYPE_WITH_CODE (EphyFullscreenBox, ephy_fullscreen_box, GTK_TYPE_EVENT_BOX,
+G_DEFINE_TYPE_WITH_CODE (EphyFullscreenBox, ephy_fullscreen_box, GTK_TYPE_WIDGET,
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                 ephy_fullscreen_box_buildable_init))
 
@@ -66,7 +64,7 @@ show_ui (EphyFullscreenBox *self)
 {
   g_clear_handle_id (&self->timeout_id, g_source_remove);
 
-  hdy_flap_set_reveal_flap (self->flap, TRUE);
+  adw_flap_set_reveal_flap (self->flap, TRUE);
 }
 
 static void
@@ -77,7 +75,7 @@ hide_ui (EphyFullscreenBox *self)
   if (!self->fullscreen)
     return;
 
-  hdy_flap_set_reveal_flap (self->flap, FALSE);
+  adw_flap_set_reveal_flap (self->flap, FALSE);
   gtk_widget_grab_focus (GTK_WIDGET (self->flap));
 }
 
@@ -94,7 +92,7 @@ hide_timeout_cb (EphyFullscreenBox *self)
 static void
 start_hide_timeout (EphyFullscreenBox *self)
 {
-  if (!hdy_flap_get_reveal_flap (self->flap))
+  if (!adw_flap_get_reveal_flap (self->flap))
     return;
 
   if (self->timeout_id)
@@ -119,12 +117,9 @@ is_descendant_of (GtkWidget *widget,
 
   parent = widget;
 
-  while (parent && parent != target && !GTK_IS_POPOVER (parent))
+  while (parent && parent != target)
     parent = gtk_widget_get_parent (parent);
 
-  if (GTK_IS_POPOVER (parent))
-    return is_descendant_of (gtk_popover_get_relative_to (GTK_POPOVER (parent)), target);
-
   return parent == target;
 }
 
@@ -133,8 +128,8 @@ get_titlebar_area_height (EphyFullscreenBox *self)
 {
   gdouble height;
 
-  height = gtk_widget_get_allocated_height (hdy_flap_get_flap (self->flap));
-  height *= hdy_flap_get_reveal_progress (self->flap);
+  height = gtk_widget_get_allocated_height (adw_flap_get_flap (self->flap));
+  height *= adw_flap_get_reveal_progress (self->flap);
   height = MAX (height, SHOW_HEADERBAR_DISTANCE_PX);
 
   return height;
@@ -154,7 +149,7 @@ update (EphyFullscreenBox *self,
   }
 
   if (self->last_focus && is_descendant_of (self->last_focus,
-                                            hdy_flap_get_flap (self->flap)))
+                                            adw_flap_get_flap (self->flap)))
     show_ui (self);
   else if (hide_immediately)
     hide_ui (self);
@@ -178,12 +173,6 @@ enter_cb (EphyFullscreenBox *self,
           double             x,
           double             y)
 {
-  g_autoptr (GdkEvent) event = gtk_get_current_event ();
-
-  if (event->crossing.window != gtk_widget_get_window (GTK_WIDGET (self)) ||
-      event->crossing.detail == GDK_NOTIFY_INFERIOR)
-    return;
-
   motion_cb (self, x, y);
 }
 
@@ -191,9 +180,10 @@ static void
 press_cb (EphyFullscreenBox *self,
           int                n_press,
           double             x,
-          double             y)
+          double             y,
+          GtkGesture        *gesture)
 {
-  gtk_gesture_set_state (self->gesture, GTK_EVENT_SEQUENCE_DENIED);
+  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
 
   self->is_touch = TRUE;
 
@@ -202,14 +192,22 @@ press_cb (EphyFullscreenBox *self,
 }
 
 static void
-set_focus_cb (EphyFullscreenBox *self,
-              GtkWidget         *widget)
+set_focus (EphyFullscreenBox *self,
+           GtkWidget         *widget)
 {
   self->last_focus = widget;
 
   update (self, TRUE);
 }
 
+static void
+notify_focus_cb (EphyFullscreenBox *self,
+                 GParamSpec        *pspec,
+                 GtkRoot           *root)
+{
+  set_focus (self, gtk_root_get_focus (root));
+}
+
 static void
 notify_reveal_cb (EphyFullscreenBox *self)
 {
@@ -217,72 +215,38 @@ notify_reveal_cb (EphyFullscreenBox *self)
 }
 
 static void
-ephy_fullscreen_box_hierarchy_changed (GtkWidget *widget,
-                                       GtkWidget *previous_toplevel)
+ephy_fullscreen_box_root (GtkWidget *widget)
 {
   EphyFullscreenBox *self = EPHY_FULLSCREEN_BOX (widget);
-  GtkWidget *toplevel;
+  GtkRoot *root;
 
-  if (previous_toplevel && GTK_IS_WINDOW (previous_toplevel))
-    g_signal_handlers_disconnect_by_func (previous_toplevel, set_focus_cb, widget);
+  GTK_WIDGET_CLASS (ephy_fullscreen_box_parent_class)->root (widget);
 
-  toplevel = gtk_widget_get_toplevel (widget);
+  root = gtk_widget_get_root (widget);
 
-  if (toplevel && GTK_IS_WINDOW (toplevel)) {
-    g_signal_connect_object (toplevel, "set-focus",
-                             G_CALLBACK (set_focus_cb), widget,
+  if (root && GTK_IS_WINDOW (root)) {
+    g_signal_connect_object (root, "notify::focus-widget",
+                             G_CALLBACK (notify_focus_cb), widget,
                              G_CONNECT_SWAPPED);
 
-    set_focus_cb (self, gtk_window_get_focus (GTK_WINDOW (toplevel)));
+    set_focus (self, gtk_window_get_focus (GTK_WINDOW (root)));
   } else {
-    set_focus_cb (self, NULL);
+    set_focus (self, NULL);
   }
 }
 
 static void
-ephy_fullscreen_box_add (GtkContainer *container,
-                         GtkWidget    *widget)
-{
-  EphyFullscreenBox *self = EPHY_FULLSCREEN_BOX (container);
-
-  if (!self->flap)
-    GTK_CONTAINER_CLASS (ephy_fullscreen_box_parent_class)->add (container, widget);
-  else
-    gtk_container_add (GTK_CONTAINER (self->flap), widget);
-}
-
-static void
-ephy_fullscreen_box_remove (GtkContainer *container,
-                            GtkWidget    *widget)
+ephy_fullscreen_box_unroot (GtkWidget *widget)
 {
-  EphyFullscreenBox *self = EPHY_FULLSCREEN_BOX (container);
+  EphyFullscreenBox *self = EPHY_FULLSCREEN_BOX (widget);
+  GtkRoot *root = gtk_widget_get_root (widget);
 
-  if (widget == GTK_WIDGET (self->flap)) {
-    GTK_CONTAINER_CLASS (ephy_fullscreen_box_parent_class)->remove (container, widget);
-    self->flap = NULL;
-  } else {
-    gtk_container_remove (GTK_CONTAINER (self->flap), widget);
-  }
-}
+  if (root && GTK_IS_WINDOW (root))
+    g_signal_handlers_disconnect_by_func (root, notify_focus_cb, widget);
 
-static void
-ephy_fullscreen_box_forall (GtkContainer *container,
-                            gboolean      include_internals,
-                            GtkCallback   callback,
-                            gpointer      callback_data)
-{
-  EphyFullscreenBox *self = EPHY_FULLSCREEN_BOX (container);
+  set_focus (self, NULL);
 
-  if (include_internals) {
-    GTK_CONTAINER_CLASS (ephy_fullscreen_box_parent_class)->forall (container,
-                                                                    include_internals,
-                                                                    callback,
-                                                                    callback_data);
-  } else {
-    gtk_container_foreach (GTK_CONTAINER (self->flap),
-                           callback,
-                           callback_data);
-  }
+  GTK_WIDGET_CLASS (ephy_fullscreen_box_parent_class)->unroot (widget);
 }
 
 static void
@@ -311,7 +275,7 @@ ephy_fullscreen_box_get_property (GObject    *object,
       break;
 
     case PROP_REVEALED:
-      g_value_set_boolean (value, hdy_flap_get_reveal_flap (self->flap));
+      g_value_set_boolean (value, adw_flap_get_reveal_flap (self->flap));
       break;
 
     default:
@@ -354,8 +318,10 @@ ephy_fullscreen_box_dispose (GObject *object)
 {
   EphyFullscreenBox *self = EPHY_FULLSCREEN_BOX (object);
 
-  g_clear_object (&self->controller);
-  g_clear_object (&self->gesture);
+  if (self->flap) {
+    gtk_widget_unparent (GTK_WIDGET (self->flap));
+    self->flap = NULL;
+  }
 
   G_OBJECT_CLASS (ephy_fullscreen_box_parent_class)->dispose (object);
 }
@@ -365,17 +331,13 @@ ephy_fullscreen_box_class_init (EphyFullscreenBoxClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 
   object_class->get_property = ephy_fullscreen_box_get_property;
   object_class->set_property = ephy_fullscreen_box_set_property;
   object_class->dispose = ephy_fullscreen_box_dispose;
 
-  widget_class->hierarchy_changed = ephy_fullscreen_box_hierarchy_changed;
-
-  container_class->add = ephy_fullscreen_box_add;
-  container_class->remove = ephy_fullscreen_box_remove;
-  container_class->forall = ephy_fullscreen_box_forall;
+  widget_class->root = ephy_fullscreen_box_root;
+  widget_class->unroot = ephy_fullscreen_box_unroot;
 
   props[PROP_FULLSCREEN] =
     g_param_spec_boolean ("fullscreen",
@@ -415,47 +377,49 @@ ephy_fullscreen_box_class_init (EphyFullscreenBoxClass *klass)
   g_object_class_install_properties (object_class, LAST_PROP, props);
 
   gtk_widget_class_set_css_name (widget_class, "fullscreenbox");
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
 }
 
 static void
 ephy_fullscreen_box_init (EphyFullscreenBox *self)
 {
-  HdyFlap *flap;
+  AdwFlap *flap;
+  GtkEventController *controller;
+  GtkGesture *gesture;
 
   self->autohide = TRUE;
 
-  gtk_widget_add_events (GTK_WIDGET (self), GDK_ALL_EVENTS_MASK);
-
-  flap = HDY_FLAP (hdy_flap_new ());
+  flap = ADW_FLAP (adw_flap_new ());
   gtk_orientable_set_orientation (GTK_ORIENTABLE (flap), GTK_ORIENTATION_VERTICAL);
-  hdy_flap_set_flap_position (flap, GTK_PACK_START);
-  hdy_flap_set_fold_policy (flap, HDY_FLAP_FOLD_POLICY_NEVER);
-  hdy_flap_set_locked (flap, TRUE);
-  hdy_flap_set_modal (flap, FALSE);
-  hdy_flap_set_swipe_to_open (flap, FALSE);
-  hdy_flap_set_swipe_to_close (flap, FALSE);
-  hdy_flap_set_transition_type (flap, HDY_FLAP_TRANSITION_TYPE_OVER);
-  gtk_widget_show (GTK_WIDGET (flap));
+  adw_flap_set_flap_position (flap, GTK_PACK_START);
+  adw_flap_set_fold_policy (flap, ADW_FLAP_FOLD_POLICY_NEVER);
+  adw_flap_set_locked (flap, TRUE);
+  adw_flap_set_modal (flap, FALSE);
+  adw_flap_set_swipe_to_open (flap, FALSE);
+  adw_flap_set_swipe_to_close (flap, FALSE);
+  adw_flap_set_transition_type (flap, ADW_FLAP_TRANSITION_TYPE_OVER);
 
   g_signal_connect_object (flap, "notify::reveal-flap",
                            G_CALLBACK (notify_reveal_cb), self, G_CONNECT_SWAPPED);
 
-  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (flap));
+  gtk_widget_set_parent (GTK_WIDGET (flap), GTK_WIDGET (self));
   self->flap = flap;
 
-  self->controller = gtk_event_controller_motion_new (GTK_WIDGET (self));
-  gtk_event_controller_set_propagation_phase (self->controller, GTK_PHASE_CAPTURE);
-  g_signal_connect_object (self->controller, "enter",
+  controller = gtk_event_controller_motion_new ();
+  gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
+  g_signal_connect_object (controller, "enter",
                            G_CALLBACK (enter_cb), self, G_CONNECT_SWAPPED);
-  g_signal_connect_object (self->controller, "motion",
+  g_signal_connect_object (controller, "motion",
                            G_CALLBACK (motion_cb), self, G_CONNECT_SWAPPED);
+  gtk_widget_add_controller (GTK_WIDGET (self), controller);
 
-  self->gesture = gtk_gesture_multi_press_new (GTK_WIDGET (self));
-  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->gesture),
+  gesture = gtk_gesture_click_new ();
+  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
                                               GTK_PHASE_CAPTURE);
-  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (self->gesture), TRUE);
-  g_signal_connect_object (self->gesture, "pressed",
+  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), TRUE);
+  g_signal_connect_object (gesture, "pressed",
                            G_CALLBACK (press_cb), self, G_CONNECT_SWAPPED);
+  gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
 }
 
 static void
@@ -509,10 +473,10 @@ ephy_fullscreen_box_set_fullscreen (EphyFullscreenBox *self,
     return;
 
   if (fullscreen) {
-    hdy_flap_set_fold_policy (self->flap, HDY_FLAP_FOLD_POLICY_ALWAYS);
+    adw_flap_set_fold_policy (self->flap, ADW_FLAP_FOLD_POLICY_ALWAYS);
     update (self, FALSE);
   } else {
-    hdy_flap_set_fold_policy (self->flap, HDY_FLAP_FOLD_POLICY_NEVER);
+    adw_flap_set_fold_policy (self->flap, ADW_FLAP_FOLD_POLICY_NEVER);
     show_ui (self);
   }
 
@@ -556,7 +520,7 @@ ephy_fullscreen_box_get_titlebar (EphyFullscreenBox *self)
 {
   g_return_val_if_fail (EPHY_IS_FULLSCREEN_BOX (self), NULL);
 
-  return hdy_flap_get_flap (self->flap);
+  return adw_flap_get_flap (self->flap);
 }
 
 void
@@ -564,12 +528,12 @@ ephy_fullscreen_box_set_titlebar (EphyFullscreenBox *self,
                                   GtkWidget         *titlebar)
 {
   g_return_if_fail (EPHY_IS_FULLSCREEN_BOX (self));
-  g_return_if_fail (GTK_IS_WIDGET (titlebar) || titlebar == NULL);
+  g_return_if_fail (titlebar == NULL || GTK_IS_WIDGET (titlebar));
 
-  if (hdy_flap_get_flap (self->flap) == titlebar)
+  if (adw_flap_get_flap (self->flap) == titlebar)
     return;
 
-  hdy_flap_set_flap (self->flap, titlebar);
+  adw_flap_set_flap (self->flap, titlebar);
 
   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TITLEBAR]);
 }
@@ -579,7 +543,7 @@ ephy_fullscreen_box_get_content (EphyFullscreenBox *self)
 {
   g_return_val_if_fail (EPHY_IS_FULLSCREEN_BOX (self), NULL);
 
-  return hdy_flap_get_content (self->flap);
+  return adw_flap_get_content (self->flap);
 }
 
 void
@@ -587,12 +551,12 @@ ephy_fullscreen_box_set_content (EphyFullscreenBox *self,
                                  GtkWidget         *content)
 {
   g_return_if_fail (EPHY_IS_FULLSCREEN_BOX (self));
-  g_return_if_fail (GTK_IS_WIDGET (content) || content == NULL);
+  g_return_if_fail (content == NULL || GTK_IS_WIDGET (content));
 
-  if (hdy_flap_get_content (self->flap) == content)
+  if (adw_flap_get_content (self->flap) == content)
     return;
 
-  hdy_flap_set_content (self->flap, content);
+  adw_flap_set_content (self->flap, content);
 
   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CONTENT]);
 }
diff --git a/src/ephy-fullscreen-box.h b/src/ephy-fullscreen-box.h
index ce85c8e42..fec2f31e2 100644
--- a/src/ephy-fullscreen-box.h
+++ b/src/ephy-fullscreen-box.h
@@ -26,7 +26,7 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_FULLSCREEN_BOX (ephy_fullscreen_box_get_type())
 
-G_DECLARE_FINAL_TYPE (EphyFullscreenBox, ephy_fullscreen_box, EPHY, FULLSCREEN_BOX, GtkEventBox)
+G_DECLARE_FINAL_TYPE (EphyFullscreenBox, ephy_fullscreen_box, EPHY, FULLSCREEN_BOX, GtkWidget)
 
 EphyFullscreenBox *ephy_fullscreen_box_new            (void);
 
diff --git a/src/ephy-header-bar.c b/src/ephy-header-bar.c
index d6132ba8b..d51615335 100644
--- a/src/ephy-header-bar.c
+++ b/src/ephy-header-bar.c
@@ -35,8 +35,8 @@
 #include "ephy-title-widget.h"
 #include "ephy-type-builtins.h"
 
+#include <adwaita.h>
 #include <glib/gi18n.h>
-#include <handy.h>
 
 #define POPOVER_HIDE_DELAY 300
 
@@ -52,7 +52,7 @@ static GParamSpec *object_properties[N_PROPERTIES] = { NULL, };
 static const char *REFRESH_BUTTON_TOOLTIP = N_("Reload the current page");
 
 struct _EphyHeaderBar {
-  GtkBin parent_instance;
+  AdwBin parent_instance;
 
   GtkWidget *header_bar;
   EphyWindow *window;
@@ -65,13 +65,12 @@ struct _EphyHeaderBar {
   GtkWidget *zoom_level_label;
   GtkWidget *restore_button;
   GtkWidget *combined_stop_reload_button;
-  GtkWidget *combined_stop_reload_image;
   GtkWidget *page_menu_popover;
 
   guint popover_hide_timeout_id;
 };
 
-G_DEFINE_TYPE (EphyHeaderBar, ephy_header_bar, GTK_TYPE_BIN)
+G_DEFINE_TYPE (EphyHeaderBar, ephy_header_bar, ADW_TYPE_BIN)
 
 static void
 ephy_header_bar_set_property (GObject      *object,
@@ -125,7 +124,7 @@ sync_chromes_visibility (EphyHeaderBar *header_bar)
 static gboolean
 hide_timeout_cb (EphyHeaderBar *header_bar)
 {
-  gtk_popover_popdown (GTK_POPOVER (header_bar->page_menu_popover));
+  gtk_menu_button_popdown (GTK_MENU_BUTTON (header_bar->page_menu_button));
 
   header_bar->popover_hide_timeout_id = 0;
 
@@ -137,9 +136,9 @@ fullscreen_changed_cb (EphyHeaderBar *header_bar)
 {
   gboolean fullscreen;
 
-  g_object_get (header_bar->window, "fullscreen", &fullscreen, NULL);
+  g_object_get (header_bar->window, "fullscreened", &fullscreen, NULL);
 
-  gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (header_bar->header_bar), !fullscreen);
+  gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header_bar->header_bar), !fullscreen);
   gtk_widget_set_visible (header_bar->restore_button, fullscreen);
 
   if (fullscreen) {
@@ -158,6 +157,57 @@ update_revealer_visibility (GtkRevealer *revealer)
                           gtk_revealer_get_child_revealed (revealer));
 }
 
+static gboolean
+remove_menu_item (GMenu      *menu,
+                  const char *action_name)
+{
+  int i, n;
+
+  n = g_menu_model_get_n_items (G_MENU_MODEL (menu));
+
+  for (i = 0; i < n; i++) {
+    g_autofree char *item_action = NULL;
+    g_autofree char *submenu_id = NULL;
+    g_autoptr (GMenuModel) section = NULL;
+
+    g_menu_model_get_item_attribute (G_MENU_MODEL (menu),
+                                     i,
+                                     G_MENU_ATTRIBUTE_ACTION,
+                                     "s",
+                                     &item_action);
+
+    if (!g_strcmp0 (action_name, item_action)) {
+      g_menu_remove (menu, i);
+      return TRUE;
+    }
+
+    /* FIXME: this isn't particularly great. Maybe we should have custom
+     * attributes for everything like show-in-app-mode etc? */
+    g_menu_model_get_item_attribute (G_MENU_MODEL (menu),
+                                     i,
+                                     "ephy-submenu-id",
+                                     "s",
+                                     &submenu_id);
+
+    if (!g_strcmp0 (action_name, submenu_id)) {
+      g_menu_remove (menu, i);
+      return TRUE;
+    }
+
+    section = g_menu_model_get_item_link (G_MENU_MODEL (menu),
+                                          i,
+                                          G_MENU_LINK_SECTION);
+
+    if (!G_IS_MENU (section))
+      continue;
+
+    if (remove_menu_item (G_MENU (section), action_name))
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
 static void
 ephy_header_bar_constructed (GObject *object)
 {
@@ -167,32 +217,30 @@ ephy_header_bar_constructed (GObject *object)
   GtkBuilder *builder;
   EphyEmbedShell *embed_shell;
   GtkSizeGroup *downloads_size_group;
+  GMenu *menu;
 
   G_OBJECT_CLASS (ephy_header_bar_parent_class)->constructed (object);
 
   g_signal_connect_object (header_bar->window, "notify::chrome",
                            G_CALLBACK (sync_chromes_visibility), header_bar,
                            G_CONNECT_SWAPPED);
-  g_signal_connect_object (header_bar->window, "notify::fullscreen",
+  g_signal_connect_object (header_bar->window, "notify::fullscreened",
                            G_CALLBACK (fullscreen_changed_cb), header_bar,
                            G_CONNECT_SWAPPED);
 
   /* Header bar */
   header_bar->header_bar = gtk_header_bar_new ();
-  gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (header_bar->header_bar), TRUE);
-  gtk_widget_show (header_bar->header_bar);
-  gtk_container_add (GTK_CONTAINER (header_bar), header_bar->header_bar);
+  adw_bin_set_child (ADW_BIN (header_bar), header_bar->header_bar);
 
   /* Start action elements */
   header_bar->action_bar_start = ephy_action_bar_start_new ();
-  gtk_widget_show (GTK_WIDGET (header_bar->action_bar_start));
   header_bar->start_revealer = GTK_REVEALER (gtk_revealer_new ());
   g_signal_connect (header_bar->start_revealer, "notify::child-revealed",
                     G_CALLBACK (update_revealer_visibility), NULL);
   g_signal_connect (header_bar->start_revealer, "notify::reveal-child",
                     G_CALLBACK (update_revealer_visibility), NULL);
   gtk_revealer_set_transition_type (GTK_REVEALER (header_bar->start_revealer), 
GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT);
-  gtk_container_add (GTK_CONTAINER (header_bar->start_revealer), GTK_WIDGET (header_bar->action_bar_start));
+  gtk_revealer_set_child (GTK_REVEALER (header_bar->start_revealer), GTK_WIDGET 
(header_bar->action_bar_start));
 
   gtk_header_bar_pack_start (GTK_HEADER_BAR (header_bar->header_bar),
                              GTK_WIDGET (header_bar->start_revealer));
@@ -202,14 +250,11 @@ ephy_header_bar_constructed (GObject *object)
   /* Title widget (location entry or title box) */
   if (ephy_embed_shell_get_mode (embed_shell) == EPHY_EMBED_SHELL_MODE_APPLICATION)
     header_bar->title_widget = EPHY_TITLE_WIDGET (ephy_title_box_new ());
-  else {
+  else
     header_bar->title_widget = EPHY_TITLE_WIDGET (ephy_location_entry_new ());
-  }
 
-  event_box = gtk_event_box_new ();
-  gtk_widget_add_events (event_box, GDK_ALL_EVENTS_MASK);
-  gtk_widget_show (event_box);
-  gtk_header_bar_set_custom_title (GTK_HEADER_BAR (header_bar->header_bar), event_box);
+  event_box = adw_bin_new ();
+  gtk_header_bar_set_title_widget (GTK_HEADER_BAR (header_bar->header_bar), event_box);
   gtk_widget_set_name (event_box, "title-box-container");
 
   if (is_desktop_pantheon ()) {
@@ -218,22 +263,19 @@ ephy_header_bar_constructed (GObject *object)
     gtk_widget_set_margin_start (GTK_WIDGET (header_bar->title_widget), 6);
     gtk_widget_set_margin_end (GTK_WIDGET (header_bar->title_widget), 6);
 
-    gtk_container_add (GTK_CONTAINER (event_box), GTK_WIDGET (header_bar->title_widget));
+    adw_bin_set_child (ADW_BIN (event_box), GTK_WIDGET (header_bar->title_widget));
   } else {
     GtkWidget *clamp;
 
-    clamp = hdy_clamp_new ();
+    clamp = adw_clamp_new ();
     gtk_widget_set_hexpand (GTK_WIDGET (clamp), TRUE);
-    gtk_widget_show (clamp);
-    hdy_clamp_set_maximum_size (HDY_CLAMP (clamp), 860);
-    hdy_clamp_set_tightening_threshold (HDY_CLAMP (clamp), 560);
-    gtk_container_add (GTK_CONTAINER (clamp), GTK_WIDGET (header_bar->title_widget));
+    adw_clamp_set_maximum_size (ADW_CLAMP (clamp), 860);
+    adw_clamp_set_tightening_threshold (ADW_CLAMP (clamp), 560);
+    adw_clamp_set_child (ADW_CLAMP (clamp), GTK_WIDGET (header_bar->title_widget));
 
-    gtk_container_add (GTK_CONTAINER (event_box), clamp);
+    adw_bin_set_child (ADW_BIN (event_box), clamp);
   }
 
-  gtk_widget_show (GTK_WIDGET (header_bar->title_widget));
-
   if (EPHY_IS_LOCATION_ENTRY (header_bar->title_widget)) {
     EphyLocationEntry *lentry = EPHY_LOCATION_ENTRY (header_bar->title_widget);
     GtkWidget *popover = ephy_add_bookmark_popover_new ();
@@ -242,8 +284,8 @@ ephy_header_bar_constructed (GObject *object)
   }
 
   /* Fullscreen restore button */
-  header_bar->restore_button = gtk_button_new_from_icon_name ("view-restore-symbolic",
-                                                              GTK_ICON_SIZE_BUTTON);
+  header_bar->restore_button = gtk_button_new_from_icon_name ("view-restore-symbolic");
+  gtk_widget_hide (header_bar->restore_button);
   gtk_actionable_set_action_name (GTK_ACTIONABLE (header_bar->restore_button),
                                   "win.fullscreen");
   gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar->header_bar),
@@ -252,56 +294,45 @@ ephy_header_bar_constructed (GObject *object)
   /* Page Menu */
   button = gtk_menu_button_new ();
   header_bar->page_menu_button = button;
-  gtk_button_set_image (GTK_BUTTON (button),
-                        gtk_image_new_from_icon_name ("open-menu-symbolic", GTK_ICON_SIZE_BUTTON));
-  g_type_ensure (G_TYPE_THEMED_ICON);
+  gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (button), "open-menu-symbolic");
   builder = gtk_builder_new_from_resource ("/org/gnome/epiphany/gtk/page-menu-popover.ui");
+  menu = G_MENU (gtk_builder_get_object (builder, "menu"));
   header_bar->page_menu_popover = GTK_WIDGET (gtk_builder_get_object (builder, "page-menu-popover"));
   header_bar->zoom_level_label = GTK_WIDGET (gtk_builder_get_object (builder, "zoom-level"));
+
   if (ephy_embed_shell_get_mode (embed_shell) == EPHY_EMBED_SHELL_MODE_APPLICATION) {
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "new-window-separator")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "new-window-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "new-incognito-window-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "reopen-closed-tab-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "save-as-application-separator")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "save-as-application-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "application-manager-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "override-text-encoding-separator")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "override-text-encoding-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "keyboard-shortcuts-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "help-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "firefox-sync-separator")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "firefox-sync-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "import-export-menu")));
+    remove_menu_item (menu, "app.new-window");
+    remove_menu_item (menu, "app.new-incognito");
+    remove_menu_item (menu, "app.reopen-closed-tab");
+    remove_menu_item (menu, "win.save-as-application");
+    remove_menu_item (menu, "win.open-application-manager");
+    remove_menu_item (menu, "win.encoding");
+    remove_menu_item (menu, "app.shortcuts");
+    remove_menu_item (menu, "app.help");
+    remove_menu_item (menu, "app.firefox-sync-dialog");
+    remove_menu_item (menu, "import-export");
   } else if (ephy_is_running_inside_sandbox ()) {
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "run-in-background-separator")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "run-in-background-button")));
+    remove_menu_item (menu, "app.run-in-background");
 
     if (is_desktop_pantheon ())
-      gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "help-button")));
+      remove_menu_item (menu, "app.help");
   } else {
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "run-in-background-separator")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "run-in-background-button")));
+    remove_menu_item (menu, "app.run-in-background");
   }
 
   if (!ephy_can_install_web_apps ()) {
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "save-as-application-separator")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "save-as-application-button")));
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "application-manager-button")));
+    remove_menu_item (menu, "win.save-as-application");
+    remove_menu_item (menu, "win.open-application-manager");
   }
 
   header_bar->combined_stop_reload_button = GTK_WIDGET (gtk_builder_get_object (builder, 
"combined_stop_reload_button"));
-  header_bar->combined_stop_reload_image = GTK_WIDGET (gtk_builder_get_object (builder, 
"combined_stop_reload_image"));
   gtk_widget_set_tooltip_text (header_bar->combined_stop_reload_button, _(REFRESH_BUTTON_TOOLTIP));
 
   if (is_desktop_pantheon ()) {
-    gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object (builder, "about-button")));
+    remove_menu_item (menu, "app.about");
 
-    gtk_button_set_image (GTK_BUTTON (button),
-                          gtk_image_new_from_icon_name ("open-menu",
-                                                        GTK_ICON_SIZE_LARGE_TOOLBAR));
+    gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (button), "open-menu");
   }
-  g_settings_bind (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_ENABLE_WEBEXTENSIONS, gtk_builder_get_object (builder, 
"extensions-button"), "visible", G_SETTINGS_BIND_DEFAULT);
 
   gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), header_bar->page_menu_popover);
   g_object_unref (builder);
@@ -310,14 +341,13 @@ ephy_header_bar_constructed (GObject *object)
 
   /* End action elements */
   header_bar->action_bar_end = ephy_action_bar_end_new ();
-  gtk_widget_show (GTK_WIDGET (header_bar->action_bar_end));
   header_bar->end_revealer = GTK_REVEALER (gtk_revealer_new ());
   g_signal_connect (header_bar->end_revealer, "notify::child-revealed",
                     G_CALLBACK (update_revealer_visibility), NULL);
   g_signal_connect (header_bar->end_revealer, "notify::reveal-child",
                     G_CALLBACK (update_revealer_visibility), NULL);
   gtk_revealer_set_transition_type (GTK_REVEALER (header_bar->end_revealer), 
GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT);
-  gtk_container_add (GTK_CONTAINER (header_bar->end_revealer), GTK_WIDGET (header_bar->action_bar_end));
+  gtk_revealer_set_child (GTK_REVEALER (header_bar->end_revealer), GTK_WIDGET (header_bar->action_bar_end));
 
   gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar->header_bar),
                            GTK_WIDGET (header_bar->end_revealer));
@@ -445,16 +475,14 @@ ephy_header_bar_start_change_combined_stop_reload_state (EphyHeaderBar *header_b
                                                          gboolean       loading)
 {
   if (loading) {
-    gtk_image_set_from_icon_name (GTK_IMAGE (header_bar->combined_stop_reload_image),
-                                  "process-stop-symbolic",
-                                  get_icon_size ());
+    gtk_button_set_icon_name (GTK_BUTTON (header_bar->combined_stop_reload_button),
+                              "process-stop-symbolic");
     /* Translators: tooltip for the stop button */
     gtk_widget_set_tooltip_text (header_bar->combined_stop_reload_button,
                                  _("Stop loading the current page"));
   } else {
-    gtk_image_set_from_icon_name (GTK_IMAGE (header_bar->combined_stop_reload_image),
-                                  "view-refresh-symbolic",
-                                  get_icon_size ());
+    gtk_button_set_icon_name (GTK_BUTTON (header_bar->combined_stop_reload_button),
+                              "view-refresh-symbolic");
     gtk_widget_set_tooltip_text (header_bar->combined_stop_reload_button,
                                  _(REFRESH_BUTTON_TOOLTIP));
   }
diff --git a/src/ephy-header-bar.h b/src/ephy-header-bar.h
index f066de16d..c3eff87f1 100644
--- a/src/ephy-header-bar.h
+++ b/src/ephy-header-bar.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
+#include <adwaita.h>
 
 #include "ephy-action-bar-end.h"
 #include "ephy-action-bar-start.h"
@@ -32,7 +32,7 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_HEADER_BAR (ephy_header_bar_get_type())
 
-G_DECLARE_FINAL_TYPE (EphyHeaderBar, ephy_header_bar, EPHY, HEADER_BAR, GtkBin)
+G_DECLARE_FINAL_TYPE (EphyHeaderBar, ephy_header_bar, EPHY, HEADER_BAR, AdwBin)
 
 GtkWidget          *ephy_header_bar_new                            (EphyWindow    *window);
 EphyTitleWidget    *ephy_header_bar_get_title_widget               (EphyHeaderBar *header_bar);
diff --git a/src/ephy-history-dialog.c b/src/ephy-history-dialog.c
index 64bf10e76..808a31f67 100644
--- a/src/ephy-history-dialog.c
+++ b/src/ephy-history-dialog.c
@@ -35,17 +35,17 @@
 #include "ephy-time-helpers.h"
 #include "ephy-window.h"
 
+#include <adwaita.h>
 #include <ctype.h>
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
-#include <handy.h>
 #include <string.h>
 #include <time.h>
 
 #define NUM_FETCH_LIMIT 15
 
 struct _EphyHistoryDialog {
-  HdyWindow parent_instance;
+  AdwWindow parent_instance;
 
   EphySnapshotService *snapshot_service;
   EphyHistoryService *history_service;
@@ -55,6 +55,7 @@ struct _EphyHistoryDialog {
   GtkWidget *header_bars_stack;
   GtkWidget *window_header_bar;
   GtkWidget *search_button;
+  GtkWidget *selection_button;
   GtkWidget *selection_header_bar;
   GtkWidget *search_bar;
   GtkWidget *search_entry;
@@ -88,7 +89,7 @@ struct _EphyHistoryDialog {
   gboolean has_search_results;
 };
 
-G_DEFINE_TYPE (EphyHistoryDialog, ephy_history_dialog, HDY_TYPE_WINDOW)
+G_DEFINE_TYPE (EphyHistoryDialog, ephy_history_dialog, ADW_TYPE_WINDOW)
 
 enum {
   PROP_0,
@@ -142,6 +143,7 @@ update_ui_state (EphyHistoryDialog *self)
   }
 
   gtk_widget_set_sensitive (self->search_button, has_data);
+  gtk_widget_set_sensitive (self->selection_button, has_data);
   gtk_widget_set_sensitive (self->clear_all_button, has_data && self->can_clear);
   gtk_widget_set_sensitive (self->selection_open_button, !self->is_selection_empty);
   gtk_widget_set_sensitive (self->selection_delete_button, !self->is_selection_empty && !incognito_mode);
@@ -205,7 +207,7 @@ set_selection_active (EphyHistoryDialog *self,
     GtkWidget *separator = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "separator"));
 
     /* Uncheck all rows when toggling selection mode */
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), FALSE);
+    gtk_check_button_set_active (GTK_CHECK_BUTTON (check_button), FALSE);
 
     /* Show/Hide row selection widgets (check_button + separator) */
     gtk_widget_set_visible (check_button, selection_active);
@@ -243,8 +245,8 @@ set_is_selection_empty (EphyHistoryDialog *self,
 static EphyHistoryURL *
 get_url_from_row (GtkListBoxRow *row)
 {
-  return ephy_history_url_new (hdy_action_row_get_subtitle (HDY_ACTION_ROW (row)),
-                               hdy_preferences_row_get_title (HDY_PREFERENCES_ROW (row)),
+  return ephy_history_url_new (adw_action_row_get_subtitle (ADW_ACTION_ROW (row)),
+                               adw_preferences_row_get_title (ADW_PREFERENCES_ROW (row)),
                                0,
                                0,
                                0);
@@ -256,7 +258,7 @@ clear_listbox (GtkWidget *listbox)
   GtkListBoxRow *row;
 
   while ((row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (listbox), 0)))
-    gtk_container_remove (GTK_CONTAINER (listbox), GTK_WIDGET (row));
+    gtk_list_box_remove (GTK_LIST_BOX (listbox), GTK_WIDGET (row));
 }
 
 static void
@@ -283,7 +285,7 @@ on_find_urls_cb (gpointer service,
 static GList *
 substrings_filter (EphyHistoryDialog *self)
 {
-  const gchar *search_text = gtk_entry_get_text (GTK_ENTRY (self->search_entry));
+  const gchar *search_text = gtk_editable_get_text (GTK_EDITABLE (self->search_entry));
   char **tokens, **p;
   GList *substrings = NULL;
 
@@ -347,7 +349,7 @@ get_checked_rows (EphyHistoryDialog *self)
     GtkCheckButton *check_button =
       GTK_CHECK_BUTTON (g_object_get_data (G_OBJECT (row), "check-button"));
 
-    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button)))
+    if (gtk_check_button_get_active (check_button))
       checked_rows = g_list_prepend (checked_rows, row);
   }
 
@@ -404,7 +406,7 @@ row_copy_url_button_clicked (GtkWidget *button,
   g_autoptr (EphyHistoryURL) url = get_url_from_row (row);
 
   if (url)
-    gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button), GDK_SELECTION_CLIPBOARD), 
url->url, -1);
+    gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)), url->url);
 }
 
 static void
@@ -427,11 +429,15 @@ create_row (EphyHistoryDialog *self,
   GtkWidget *separator;
   GtkWidget *check_button;
   GtkWidget *copy_url_button;
+  g_autofree char *title_escaped = g_markup_escape_text (url->title, -1);
+  g_autofree char *subtitle_escaped = g_markup_escape_text (url->url, -1);
 
   /* Row */
-  row = hdy_action_row_new ();
-  hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), url->title);
-  hdy_action_row_set_subtitle (HDY_ACTION_ROW (row), url->url);
+  row = adw_action_row_new ();
+  adw_action_row_set_title_lines (ADW_ACTION_ROW (row), 1);
+  adw_action_row_set_subtitle_lines (ADW_ACTION_ROW (row), 1);
+  adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), title_escaped);
+  adw_action_row_set_subtitle (ADW_ACTION_ROW (row), subtitle_escaped);
   gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE);
   gtk_widget_set_tooltip_text (row, url->url);
 
@@ -454,20 +460,18 @@ create_row (EphyHistoryDialog *self,
   g_signal_connect (check_button, "toggled", G_CALLBACK (row_check_button_toggled), self);
 
   /* Copy URL button */
-  copy_url_button = gtk_button_new_from_icon_name ("edit-copy-symbolic", GTK_ICON_SIZE_BUTTON);
+  copy_url_button = gtk_button_new_from_icon_name ("edit-copy-symbolic");
   gtk_widget_set_valign (copy_url_button, GTK_ALIGN_CENTER);
   gtk_widget_set_tooltip_text (copy_url_button, _("Copy URL"));
   g_signal_connect (copy_url_button, "clicked", G_CALLBACK (row_copy_url_button_clicked), row);
 
-  hdy_action_row_add_prefix (HDY_ACTION_ROW (row), check_button);
-  hdy_action_row_add_prefix (HDY_ACTION_ROW (row), separator);
-  gtk_container_add (GTK_CONTAINER (row), date);
-  gtk_container_add (GTK_CONTAINER (row), copy_url_button);
+  adw_action_row_add_prefix (ADW_ACTION_ROW (row), separator);
+  adw_action_row_add_prefix (ADW_ACTION_ROW (row), check_button);
+  adw_action_row_add_suffix (ADW_ACTION_ROW (row), date);
+  adw_action_row_add_suffix (ADW_ACTION_ROW (row), copy_url_button);
 
   gtk_widget_set_sensitive (check_button, ephy_embed_shell_get_mode (shell) != 
EPHY_EMBED_SHELL_MODE_INCOGNITO);
 
-  gtk_widget_show_all (row);
-
   /* Hide the Separator and CheckButton if selection isn't active */
   if (!self->selection_active) {
     gtk_widget_set_visible (separator, FALSE);
@@ -524,7 +528,7 @@ confirmation_dialog_response_cb (GtkWidget         *dialog,
                                  int                response,
                                  EphyHistoryDialog *self)
 {
-  gtk_widget_destroy (dialog);
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   if (response == GTK_RESPONSE_ACCEPT) {
     ephy_history_service_clear (self->history_service,
@@ -568,20 +572,6 @@ confirmation_dialog_construct (EphyHistoryDialog *self)
   return dialog;
 }
 
-static gboolean
-on_listbox_key_press_event (GtkWidget         *widget,
-                            GdkEventKey       *event,
-                            EphyHistoryDialog *self)
-{
-  if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_KP_Delete) {
-    delete_checked_rows (self);
-
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
 static void
 on_search_entry_changed (GtkSearchEntry    *search_entry,
                          EphyHistoryDialog *self)
@@ -599,76 +589,30 @@ load_further_data (EphyHistoryDialog *self)
 }
 
 static gboolean
-on_key_press_event (EphyHistoryDialog *self,
-                    GdkEvent          *event,
-                    gpointer           user_data)
+key_pressed_cb (EphyHistoryDialog *self,
+                guint              keyval,
+                guint              keycode,
+                GdkModifierType    state)
 {
-  GdkEventKey *key = (GdkEventKey *)event;
-  HdySearchBar *search_bar = HDY_SEARCH_BAR (self->search_bar);
-
   /* Keep track internally of the Shift modifier needed for the
    * interval selection logic */
-  if (key->keyval == GDK_KEY_Shift_L || key->keyval == GDK_KEY_Shift_R)
+  if (keyval == GDK_KEY_Shift_L || keyval == GDK_KEY_Shift_R)
     self->shift_modifier_active = TRUE;
 
-  /* Check if event can be handled by the search bar */
-  if (hdy_search_bar_handle_event (search_bar, event) == GDK_EVENT_STOP)
-    return GDK_EVENT_STOP;
-
-  if (key->keyval == GDK_KEY_Down || key->keyval == GDK_KEY_Page_Down) {
-    g_autoptr (GList) children = gtk_container_get_children (GTK_CONTAINER (self->listbox));
-    GtkWidget *last = g_list_last (children)->data;
-    GtkWidget *focus = gtk_container_get_focus_child (GTK_CONTAINER (self->listbox));
-
-    if (focus == last) {
-      load_further_data (self);
-
-      return GDK_EVENT_PROPAGATE;
-    }
-  }
-
-  if (key->keyval == GDK_KEY_Escape &&
-      !hdy_search_bar_get_search_mode (search_bar)) {
-    if (self->selection_active)
-      set_selection_active (self, FALSE);
-    else
-      gtk_window_close (GTK_WINDOW (self));
-
-    return GDK_EVENT_STOP;
-  }
-
-  /* Edge case: Shift + Enter in selection mode
-   * Pressing simply Enter without any modifiers activates the focused row,
-   * but pressing Enter with modifiers doesn't do anything.
-   * We want Shift + Enter to activate the row and trigger the
-   * row interval selecton logic */
-  if (key->keyval == GDK_KEY_Return && self->shift_modifier_active && self->selection_active) {
-    GtkWindow *dialog_window = GTK_WINDOW (self);
-    GtkWidget *focused_widget = gtk_window_get_focus (dialog_window);
-
-    if (GTK_IS_LIST_BOX_ROW (focused_widget)) {
-      g_signal_emit_by_name (self->listbox, "row-activated", focused_widget, self);
-
-      return GDK_EVENT_STOP;
-    }
-  }
-
   return GDK_EVENT_PROPAGATE;
 }
 
 static gboolean
-on_key_release_event (EphyHistoryDialog *self,
-                      GdkEvent          *event,
-                      gpointer           user_data)
+key_released_cb (EphyHistoryDialog *self,
+                 guint              keyval,
+                 guint              keycode,
+                 GdkModifierType    state)
 {
-  GdkEventKey *key = (GdkEventKey *)event;
-
   /* Keep track internally of the Shift modifier needed for the
    * interval selection logic */
-  if (key->keyval == GDK_KEY_Shift_L || key->keyval == GDK_KEY_Shift_R)
+  if (keyval == GDK_KEY_Shift_L || keyval == GDK_KEY_Shift_R)
     self->shift_modifier_active = FALSE;
 
-  /* Don't handle the event */
   return GDK_EVENT_PROPAGATE;
 }
 
@@ -693,7 +637,7 @@ check_rows_interval (GtkListBox *listbox,
     GtkListBoxRow *row = gtk_list_box_get_row_at_index (listbox, index);
     GtkCheckButton *check_button = GTK_CHECK_BUTTON (g_object_get_data (G_OBJECT (row), "check-button"));
 
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), TRUE);
+    gtk_check_button_set_active (check_button, TRUE);
   }
 }
 
@@ -703,11 +647,11 @@ handle_selection_row_activated_event (EphyHistoryDialog *self,
 {
   g_autoptr (GList) checked_rows = get_checked_rows (self);
   GtkCheckButton *check_button = GTK_CHECK_BUTTON (g_object_get_data (G_OBJECT (activated_row), 
"check-button"));
-  gboolean button_checked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check_button));
+  gboolean button_checked = gtk_check_button_get_active (check_button);
 
   /* If Shift modifier isn't active, event simply toggles the row's checkbox button */
   if (!self->shift_modifier_active) {
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), !button_checked);
+    gtk_check_button_set_active (check_button, !button_checked);
     return;
   }
 
@@ -729,10 +673,10 @@ handle_selection_row_activated_event (EphyHistoryDialog *self,
       GtkCheckButton *row_check_btn =
         GTK_CHECK_BUTTON (g_object_get_data (G_OBJECT (row), "check-button"));
 
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (row_check_btn), FALSE);
+      gtk_check_button_set_active (row_check_btn, FALSE);
     }
 
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), TRUE);
+    gtk_check_button_set_active (check_button, TRUE);
   }
 }
 
@@ -879,6 +823,79 @@ on_selection_open_button_clicked (GtkWidget         *open_button,
   }
 }
 
+static gboolean
+shift_activate_cb (EphyHistoryDialog *self)
+{
+  GtkWidget *focused_widget;
+
+  if (!self->selection_active)
+    return GDK_EVENT_PROPAGATE;
+
+  focused_widget = gtk_window_get_focus (GTK_WINDOW (self));
+
+  if (GTK_IS_LIST_BOX_ROW (focused_widget)) {
+    g_signal_emit_by_name (self->listbox, "row-activated", focused_widget, self);
+
+    return GDK_EVENT_STOP;
+  }
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+escape_cb (EphyHistoryDialog *self)
+{
+  if (self->selection_active)
+    set_selection_active (self, FALSE);
+  else
+    gtk_window_close (GTK_WINDOW (self));
+
+  return GDK_EVENT_STOP;
+}
+
+static gboolean
+delete_selected_cb (EphyHistoryDialog *self)
+{
+  if (self->selection_active && !self->is_selection_empty) {
+    delete_checked_rows (self);
+
+    return GDK_EVENT_STOP;
+  }
+
+  /* TODO: Delete the focused row if not in selection mode? */
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+find_shortuct_cb (EphyHistoryDialog *self)
+{
+  gboolean search;
+
+  search = gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (self->search_bar));
+
+  if (!search && !self->has_data)
+    return GDK_EVENT_PROPAGATE;
+
+  gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (self->search_bar), !search);
+
+  return GDK_EVENT_STOP;
+}
+
+static gboolean
+load_more_shortcut_cb (GtkWidget         *listbox,
+                       GVariant          *args,
+                       EphyHistoryDialog *self)
+{
+  GtkWidget *last = gtk_widget_get_last_child (listbox);
+  GtkWidget *focus = gtk_widget_get_focus_child (listbox);
+
+  if (focus == last)
+    load_further_data (self);
+
+  return GDK_EVENT_PROPAGATE;
+}
+
 static void
 ephy_history_dialog_class_init (EphyHistoryDialogClass *klass)
 {
@@ -905,6 +922,7 @@ ephy_history_dialog_class_init (EphyHistoryDialogClass *klass)
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, header_bars_stack);
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, window_header_bar);
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, search_button);
+  gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, selection_button);
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, selection_header_bar);
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, search_bar);
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, search_entry);
@@ -921,10 +939,10 @@ ephy_history_dialog_class_init (EphyHistoryDialogClass *klass)
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, selection_delete_button);
   gtk_widget_class_bind_template_child (widget_class, EphyHistoryDialog, selection_open_button);
 
-  gtk_widget_class_bind_template_callback (widget_class, on_listbox_key_press_event);
+  gtk_widget_class_bind_template_callback (widget_class, key_pressed_cb);
+  gtk_widget_class_bind_template_callback (widget_class, key_released_cb);
+  gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated);
   gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated);
-  gtk_widget_class_bind_template_callback (widget_class, on_key_press_event);
-  gtk_widget_class_bind_template_callback (widget_class, on_key_release_event);
   gtk_widget_class_bind_template_callback (widget_class, on_selection_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_selection_cancel_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_search_entry_changed);
@@ -932,6 +950,32 @@ ephy_history_dialog_class_init (EphyHistoryDialogClass *klass)
   gtk_widget_class_bind_template_callback (widget_class, on_clear_all_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_selection_delete_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_selection_open_button_clicked);
+
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_Return, GDK_SHIFT_MASK,
+                                (GtkShortcutFunc)shift_activate_cb,
+                                NULL);
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_ISO_Enter, GDK_SHIFT_MASK,
+                                (GtkShortcutFunc)shift_activate_cb,
+                                NULL);
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_KP_Enter, GDK_SHIFT_MASK,
+                                (GtkShortcutFunc)shift_activate_cb,
+                                NULL);
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_space, GDK_SHIFT_MASK,
+                                (GtkShortcutFunc)shift_activate_cb,
+                                NULL);
+
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_Escape, 0,
+                                (GtkShortcutFunc)escape_cb,
+                                NULL);
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_Delete, 0,
+                                (GtkShortcutFunc)delete_selected_cb,
+                                NULL);
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_KP_Delete, 0,
+                                (GtkShortcutFunc)delete_selected_cb,
+                                NULL);
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_F, GDK_CONTROL_MASK,
+                                (GtkShortcutFunc)find_shortuct_cb,
+                                NULL);
 }
 
 GtkWidget *
@@ -953,6 +997,8 @@ ephy_history_dialog_init (EphyHistoryDialog *self)
 {
   EphyEmbedShell *shell = ephy_embed_shell_get_default ();
   const char *tooltip;
+  GtkEventController *controller;
+  GtkShortcut *shortcut;
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
@@ -962,7 +1008,8 @@ ephy_history_dialog_init (EphyHistoryDialog *self)
   self->sorter_source = 0;
   self->is_selection_empty = TRUE;
 
-  hdy_search_bar_connect_entry (HDY_SEARCH_BAR (self->search_bar), GTK_ENTRY (self->search_entry));
+  gtk_search_bar_connect_entry (GTK_SEARCH_BAR (self->search_bar),
+                                GTK_EDITABLE (self->search_entry));
 
   ephy_gui_ensure_window_group (GTK_WINDOW (self));
 
@@ -977,6 +1024,14 @@ ephy_history_dialog_init (EphyHistoryDialog *self)
   gtk_widget_set_tooltip_text (self->clear_all_button, tooltip);
   set_is_loading (self, TRUE);
 
-  hdy_status_page_set_icon_name (HDY_STATUS_PAGE (self->empty_history_message),
+  adw_status_page_set_icon_name (ADW_STATUS_PAGE (self->empty_history_message),
                                  APPLICATION_ID "-symbolic");
+
+  shortcut = gtk_shortcut_new (gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_Down, 0),
+                                                            gtk_keyval_trigger_new (GDK_KEY_Page_Down, 0)),
+                               gtk_callback_action_new ((GtkShortcutFunc)load_more_shortcut_cb, self, NULL));
+
+  controller = gtk_shortcut_controller_new ();
+  gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
+  gtk_widget_add_controller (self->listbox, controller);
 }
diff --git a/src/ephy-history-dialog.h b/src/ephy-history-dialog.h
index d77e45d8d..74a4252d0 100644
--- a/src/ephy-history-dialog.h
+++ b/src/ephy-history-dialog.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include <handy.h>
+#include <adwaita.h>
 
 #include "ephy-history-service.h"
 
@@ -28,7 +28,7 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_HISTORY_DIALOG (ephy_history_dialog_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyHistoryDialog, ephy_history_dialog, EPHY, HISTORY_DIALOG, HdyWindow)
+G_DECLARE_FINAL_TYPE (EphyHistoryDialog, ephy_history_dialog, EPHY, HISTORY_DIALOG, AdwWindow)
 
 GtkWidget      *ephy_history_dialog_new        (EphyHistoryService *history_service);
 
diff --git a/src/ephy-link.c b/src/ephy-link.c
index 07ca9a26f..66cf6dec1 100644
--- a/src/ephy-link.c
+++ b/src/ephy-link.c
@@ -105,10 +105,10 @@ ephy_link_flags_from_modifiers (GdkModifierType modifiers,
       return EPHY_LINK_NEW_TAB | EPHY_LINK_NEW_TAB_APPEND_AFTER;
     }
   } else {
-    if ((modifiers == (GDK_MOD1_MASK | GDK_SHIFT_MASK)) ||
+    if ((modifiers == (GDK_ALT_MASK | GDK_SHIFT_MASK)) ||
         (modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) {
       return EPHY_LINK_NEW_WINDOW;
-    } else if ((modifiers == GDK_MOD1_MASK) || (modifiers == GDK_CONTROL_MASK)) {
+    } else if ((modifiers == GDK_ALT_MASK) || (modifiers == GDK_CONTROL_MASK)) {
       return EPHY_LINK_NEW_TAB | EPHY_LINK_NEW_TAB_APPEND_AFTER | EPHY_LINK_JUMP_TO;
     }
   }
diff --git a/src/ephy-location-controller.c b/src/ephy-location-controller.c
index 3f0db081d..dab4bf122 100644
--- a/src/ephy-location-controller.c
+++ b/src/ephy-location-controller.c
@@ -33,7 +33,6 @@
 #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>
@@ -50,7 +49,7 @@ struct _EphyLocationController {
 
   EphyWindow *window;
   EphyTitleWidget *title_widget;
-  GtkGesture *longpress_gesture;
+  GtkEventController *focus_controller;
   char *address;
   gboolean editable;
   gboolean sync_address_is_blocked;
@@ -58,8 +57,6 @@ struct _EphyLocationController {
 };
 
 static void ephy_location_controller_finalize (GObject *object);
-static void user_changed_cb (GtkWidget              *widget,
-                             EphyLocationController *controller);
 static void sync_address (EphyLocationController *controller,
                           GParamSpec             *pspec,
                           GtkWidget              *widget);
@@ -86,15 +83,13 @@ entry_activate_cb (EphyLocationEntry      *entry,
   const char *content;
   char *address;
   char *effective_address;
-  GtkWidget *inner_entry;
 
   if (controller->sync_address_is_blocked) {
     controller->sync_address_is_blocked = FALSE;
     g_signal_handlers_unblock_by_func (controller, G_CALLBACK (sync_address), entry);
   }
 
-  inner_entry = ephy_location_entry_get_entry (entry);
-  content = gtk_entry_get_text (GTK_ENTRY (inner_entry));
+  content = gtk_editable_get_text (GTK_EDITABLE (entry));
   if (content == NULL || content[0] == '\0')
     return;
 
@@ -162,17 +157,14 @@ entry_activate_cb (EphyLocationEntry      *entry,
 
 static void
 user_changed_cb (GtkWidget              *widget,
+                 const char             *address,
                  EphyLocationController *controller)
 {
-  const char *address;
-  DzlSuggestionEntry *entry = DZL_SUGGESTION_ENTRY (ephy_location_entry_get_entry (EPHY_LOCATION_ENTRY 
(widget)));
   GListModel *model;
 
-  address = dzl_suggestion_entry_get_typed_text (entry);
-
   LOG ("user_changed_cb, address %s", address);
 
-  model = dzl_suggestion_entry_get_model (entry);
+  model = ephy_location_entry_get_model (EPHY_LOCATION_ENTRY (controller->title_widget));
 
   ephy_suggestion_model_query_async (EPHY_SUGGESTION_MODEL (model), address, TRUE, NULL, NULL, NULL);
 }
@@ -214,10 +206,8 @@ get_title_cb (EphyLocationEntry      *entry,
   return g_strdup (ephy_embed_get_title (embed));
 }
 
-static gboolean
-focus_in_event_cb (GtkWidget              *entry,
-                   GdkEventFocus          *event,
-                   EphyLocationController *controller)
+static void
+focus_enter_cb (EphyLocationController *controller)
 {
   const char *address;
 
@@ -226,23 +216,17 @@ focus_in_event_cb (GtkWidget              *entry,
   address = ephy_title_widget_get_address (controller->title_widget);
   if (!controller->sync_address_is_blocked && address && *address) {
     controller->sync_address_is_blocked = TRUE;
-    g_signal_handlers_block_by_func (controller, G_CALLBACK (sync_address), entry);
+    g_signal_handlers_block_by_func (controller, G_CALLBACK (sync_address), controller->title_widget);
   }
-
-  return FALSE;
 }
 
-static gboolean
-focus_out_event_cb (GtkWidget              *entry,
-                    GdkEventFocus          *event,
-                    EphyLocationController *controller)
+static void
+focus_leave_cb (EphyLocationController *controller)
 {
   if (controller->sync_address_is_blocked) {
     controller->sync_address_is_blocked = FALSE;
-    g_signal_handlers_unblock_by_func (controller, G_CALLBACK (sync_address), entry);
+    g_signal_handlers_unblock_by_func (controller, G_CALLBACK (sync_address), controller->title_widget);
   }
-
-  return FALSE;
 }
 
 static void
@@ -254,17 +238,6 @@ notify_selected_index_cb (EphyLocationController *controller)
   }
 }
 
-static void
-longpress_gesture_cb (GtkGestureLongPress *gesture,
-                      gdouble              x,
-                      gdouble              y,
-                      gpointer             user_data)
-{
-  GtkWidget *entry = user_data;
-
-  gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
-}
-
 static void
 reader_mode_changed_cb (EphyLocationEntry *lentry,
                         gboolean           active,
@@ -286,7 +259,8 @@ ephy_location_controller_constructed (GObject *object)
   EphyBookmarksManager *bookmarks_manager;
   EphySuggestionModel *model;
   EphyTabView *tab_view;
-  GtkWidget *widget, *entry;
+  GtkWidget *widget;
+  GtkEventController *focus_controller;
 
   G_OBJECT_CLASS (ephy_location_controller_parent_class)->constructed (object);
 
@@ -304,24 +278,19 @@ ephy_location_controller_constructed (GObject *object)
   if (!EPHY_IS_LOCATION_ENTRY (controller->title_widget))
     return;
 
-  entry = ephy_location_entry_get_entry (EPHY_LOCATION_ENTRY (controller->title_widget));
   g_signal_connect (controller->title_widget, "user-changed", G_CALLBACK (user_changed_cb), controller);
 
-  controller->longpress_gesture = gtk_gesture_long_press_new (entry);
-  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller->longpress_gesture), TRUE);
-  g_signal_connect (controller->longpress_gesture, "pressed", G_CALLBACK (longpress_gesture_cb), entry);
-
   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_suggestion_model_new (history_service, bookmarks_manager);
-  dzl_suggestion_entry_set_model (DZL_SUGGESTION_ENTRY (entry), G_LIST_MODEL (model));
+  ephy_location_entry_set_model (EPHY_LOCATION_ENTRY (controller->title_widget), G_LIST_MODEL (model));
   g_object_unref (model);
 
   g_signal_connect (controller->title_widget, "reader-mode-changed",
                     G_CALLBACK (reader_mode_changed_cb), controller);
 
   g_object_bind_property (controller, "editable",
-                          entry, "editable",
+                          widget, "editable",
                           G_BINDING_SYNC_CREATE);
 
   g_signal_connect_object (widget, "activate",
@@ -331,10 +300,13 @@ ephy_location_controller_constructed (GObject *object)
                            G_CALLBACK (get_location_cb), controller, 0);
   g_signal_connect_object (widget, "get-title",
                            G_CALLBACK (get_title_cb), controller, 0);
-  g_signal_connect_object (widget, "focus-in-event",
-                           G_CALLBACK (focus_in_event_cb), controller, 0);
-  g_signal_connect_object (widget, "focus-out-event",
-                           G_CALLBACK (focus_out_event_cb), controller, 0);
+
+  focus_controller = gtk_event_controller_focus_new ();
+  g_signal_connect_object (focus_controller, "enter",
+                           G_CALLBACK (focus_enter_cb), controller, G_CONNECT_SWAPPED);
+  g_signal_connect_object (focus_controller, "leave",
+                           G_CALLBACK (focus_leave_cb), controller, G_CONNECT_SWAPPED);
+  gtk_widget_add_controller (widget, focus_controller);
 }
 
 static void
@@ -391,13 +363,15 @@ ephy_location_controller_dispose (GObject *object)
   if (!controller->title_widget)
     return;
 
-  g_clear_object (&controller->longpress_gesture);
-
   if (EPHY_IS_LOCATION_ENTRY (controller->title_widget)) {
     g_signal_handlers_disconnect_matched (controller, G_SIGNAL_MATCH_DATA,
                                           0, 0, NULL, NULL, controller->title_widget);
     g_signal_handlers_disconnect_matched (controller->title_widget, G_SIGNAL_MATCH_DATA,
                                           0, 0, NULL, NULL, controller);
+
+    gtk_widget_remove_controller (GTK_WIDGET (controller->title_widget),
+                                  controller->focus_controller);
+    controller->focus_controller = NULL;
   }
   controller->title_widget = NULL;
 
diff --git a/src/ephy-lockdown.c b/src/ephy-lockdown.c
index 19d35af5f..e5a289641 100644
--- a/src/ephy-lockdown.c
+++ b/src/ephy-lockdown.c
@@ -215,22 +215,19 @@ window_added_cb (GtkApplication *application,
                              action_group, app_mode_app_actions,
                              G_N_ELEMENTS (app_mode_app_actions));
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                              "win");
+  action_group = ephy_window_get_action_group (EPHY_WINDOW (window), "win");
   bind_settings_and_actions (EPHY_SETTINGS_LOCKDOWN,
                              action_group,
                              window_actions,
                              G_N_ELEMENTS (window_actions));
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                              "toolbar");
+  action_group = ephy_window_get_action_group (EPHY_WINDOW (window), "toolbar");
   bind_settings_and_actions (EPHY_SETTINGS_LOCKDOWN,
                              action_group,
                              toolbar_actions,
                              G_N_ELEMENTS (toolbar_actions));
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                              "popup");
+  action_group = ephy_window_get_action_group (EPHY_WINDOW (window), "popup");
   bind_settings_and_actions (EPHY_SETTINGS_LOCKDOWN,
                              action_group, popup_actions,
                              G_N_ELEMENTS (popup_actions));
diff --git a/src/ephy-mouse-gesture-controller.c b/src/ephy-mouse-gesture-controller.c
index f3a804d91..c0b96e24d 100644
--- a/src/ephy-mouse-gesture-controller.c
+++ b/src/ephy-mouse-gesture-controller.c
@@ -42,7 +42,7 @@ typedef enum {
 struct _EphyMouseGestureController {
   GObject parent_instance;
 
-  GtkEventController *controller;
+  GtkGesture *gesture;
   EphyWindow *window;
   WebKitWebView *web_view;
 
@@ -65,35 +65,69 @@ static GParamSpec *obj_properties[LAST_PROP];
 G_DEFINE_TYPE (EphyMouseGestureController, ephy_mouse_gesture_controller, G_TYPE_OBJECT)
 
 static void
-ephy_mouse_gesture_controller_motion_cb (GtkEventControllerMotion *controller,
-                                         gdouble                   x,
-                                         gdouble                   y,
-                                         gpointer                  user_data)
+ephy_mouse_gesture_controller_reset (EphyMouseGestureController *self)
 {
-  EphyMouseGestureController *self = EPHY_MOUSE_GESTURE_CONTROLLER (user_data);
-  MouseDirection direction;
-  gdouble offset_x, offset_y;
+  self->direction = MOUSE_DIRECTION_UNKNOWN;
+  self->sequence_pos = 0;
+  self->last_x = 0;
+  self->last_y = 0;
+  self->gesture_active = FALSE;
+}
 
-  if (!self->gesture_active || self->sequence_pos == NUM_SEQUENCES)
+static void
+drag_begin_cb (GtkGestureDrag             *gesture,
+               double                      start_x,
+               double                      start_y,
+               EphyMouseGestureController *self)
+{
+  GtkWidget *picked_widget;
+
+  if (!g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_ENABLE_MOUSE_GESTURES)) {
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
     return;
+  }
+
+  picked_widget = gtk_widget_pick (GTK_WIDGET (self->window),
+                                   start_x,
+                                   start_y,
+                                   GTK_PICK_DEFAULT);
 
-  if (isnan (self->last_x) || isnan (self->last_y)) {
-    self->last_x = x;
-    self->last_y = y;
+  if (picked_widget != GTK_WIDGET (self->web_view)) {
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
     return;
   }
+}
 
-  offset_x = x - self->last_x;
-  offset_y = y - self->last_y;
+static void
+drag_update_cb (GtkGestureDrag             *gesture,
+                double                      offset_x,
+                double                      offset_y,
+                EphyMouseGestureController *self)
+{
+  MouseDirection direction;
+  double delta_x = offset_x - self->last_x;
+  double delta_y = offset_y - self->last_y;
+
+  self->last_x = offset_x;
+  self->last_y = offset_y;
+
+  if (!self->gesture_active &&
+      gtk_drag_check_threshold (GTK_WIDGET (self->window),
+                                0, 0, offset_x, offset_y)) {
+    self->gesture_active = TRUE;
+  }
+
+  if (!self->gesture_active || self->sequence_pos == NUM_SEQUENCES)
+    return;
 
   /* Try to guess direction */
-  if (fabs (offset_x) > fabs (offset_y) * 2) {
-    if (offset_x > 0)
+  if (fabs (delta_x) > fabs (delta_y) * 2) {
+    if (delta_x > 0)
       direction = MOUSE_DIRECTION_RIGHT;
     else
       direction = MOUSE_DIRECTION_LEFT;
-  } else if (fabs (offset_y) > fabs (offset_x) * 2) {
-    if (offset_y > 0)
+  } else if (fabs (delta_y) > fabs (delta_x) * 2) {
+    if (delta_y > 0)
       direction = MOUSE_DIRECTION_DOWN;
     else
       direction = MOUSE_DIRECTION_UP;
@@ -101,9 +135,6 @@ ephy_mouse_gesture_controller_motion_cb (GtkEventControllerMotion *controller,
     return;
   }
 
-  self->last_x = x;
-  self->last_y = y;
-
   if (self->direction == direction)
     return;
 
@@ -113,75 +144,20 @@ ephy_mouse_gesture_controller_motion_cb (GtkEventControllerMotion *controller,
 }
 
 static void
-ephy_mouse_gesture_controller_set_property (GObject      *object,
-                                            guint         prop_id,
-                                            const GValue *value,
-                                            GParamSpec   *pspec)
-{
-  EphyMouseGestureController *self = EPHY_MOUSE_GESTURE_CONTROLLER (object);
-
-  switch (prop_id) {
-    case PROP_WINDOW:
-      self->window = g_value_get_object (value);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static void
-ephy_mouse_gesture_controller_get_property (GObject    *object,
-                                            guint       prop_id,
-                                            GValue     *value,
-                                            GParamSpec *pspec)
-{
-  EphyMouseGestureController *self = EPHY_MOUSE_GESTURE_CONTROLLER (object);
-
-  switch (prop_id) {
-    case PROP_WINDOW:
-      g_value_set_object (value, self->window);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static void
-ephy_mouse_gesture_controller_reset (EphyMouseGestureController *self)
+drag_end_cb (GtkGestureDrag             *gesture,
+             double                      offset_x,
+             double                      offset_y,
+             EphyMouseGestureController *self)
 {
-  self->direction = MOUSE_DIRECTION_UNKNOWN;
-  self->sequence_pos = 0;
-  self->last_x = NAN;
-  self->last_y = NAN;
-  self->gesture_active = FALSE;
-}
-
-static gboolean
-ephy_mouse_gesture_controller_button_press_cb (GtkWidget *widget,
-                                               GdkEvent  *event,
-                                               gpointer   user_data)
-{
-  EphyMouseGestureController *self = EPHY_MOUSE_GESTURE_CONTROLLER (user_data);
-  GdkEventButton *button_event = (GdkEventButton *)event;
-
-  if (button_event->button == GDK_BUTTON_MIDDLE)
-    self->gesture_active = TRUE;
-  else
-    self->gesture_active = FALSE;
+  GActionGroup *action_group_toolbar = ephy_window_get_action_group (self->window, "toolbar");
+  GActionGroup *action_group_win = ephy_window_get_action_group (self->window, "win");
+  GActionGroup *action_group_tab = ephy_window_get_action_group (self->window, "tab");
+  GAction *action;
 
-  return FALSE;
-}
+  if (!self->gesture_active)
+    return;
 
-static void
-handle_gesture (gpointer user_data)
-{
-  EphyMouseGestureController *self = EPHY_MOUSE_GESTURE_CONTROLLER (user_data);
-  GActionGroup *action_group_toolbar = gtk_widget_get_action_group (GTK_WIDGET (self->window), "toolbar");
-  GActionGroup *action_group_win = gtk_widget_get_action_group (GTK_WIDGET (self->window), "win");
-  GActionGroup *action_group_tab = gtk_widget_get_action_group (GTK_WIDGET (self->window), "tab");
-  GAction *action;
+  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
 
   switch (self->sequence_pos) {
     case 1:
@@ -217,41 +193,53 @@ handle_gesture (gpointer user_data)
   ephy_mouse_gesture_controller_reset (self);
 }
 
-static gboolean
-ephy_mouse_gesture_controller_button_release_cb (GtkWidget *widget,
-                                                 GdkEvent  *event,
-                                                 gpointer   user_data)
+static void
+cancel_cb (GtkGesture                 *gesture,
+           GdkEventSequence           *sequence,
+           EphyMouseGestureController *self)
 {
-  EphyMouseGestureController *self = EPHY_MOUSE_GESTURE_CONTROLLER (user_data);
-  GdkEventButton *button_event = (GdkEventButton *)event;
+  ephy_mouse_gesture_controller_reset (self);
+}
 
-  if (button_event->button == GDK_BUTTON_MIDDLE) {
-    if (self->gesture_active && g_settings_get_boolean (EPHY_SETTINGS_WEB, 
EPHY_PREFS_WEB_ENABLE_MOUSE_GESTURES))
-      handle_gesture (user_data);
+static void
+ephy_mouse_gesture_controller_set_property (GObject      *object,
+                                            guint         prop_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec)
+{
+  EphyMouseGestureController *self = EPHY_MOUSE_GESTURE_CONTROLLER (object);
 
-    self->gesture_active = FALSE;
+  switch (prop_id) {
+    case PROP_WINDOW:
+      self->window = g_value_get_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
   }
-
-  return FALSE;
 }
 
 static void
-ephy_mouse_gesture_controller_init (EphyMouseGestureController *self)
+ephy_mouse_gesture_controller_get_property (GObject    *object,
+                                            guint       prop_id,
+                                            GValue     *value,
+                                            GParamSpec *pspec)
 {
+  EphyMouseGestureController *self = EPHY_MOUSE_GESTURE_CONTROLLER (object);
+
+  switch (prop_id) {
+    case PROP_WINDOW:
+      g_value_set_object (value, self->window);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
 }
 
-void
-ephy_mouse_gesture_controller_unset_web_view (EphyMouseGestureController *self)
+static void
+ephy_mouse_gesture_controller_init (EphyMouseGestureController *self)
 {
-  if (self->web_view) {
-    g_signal_handlers_disconnect_by_func (self->web_view,
-                                          G_CALLBACK (ephy_mouse_gesture_controller_button_press_cb),
-                                          self);
-    g_signal_handlers_disconnect_by_func (self->web_view,
-                                          G_CALLBACK (ephy_mouse_gesture_controller_button_release_cb),
-                                          self);
-    g_clear_object (&self->web_view);
-  }
 }
 
 static void
@@ -259,7 +247,12 @@ ephy_mouse_gesture_controller_dispose (GObject *object)
 {
   EphyMouseGestureController *self = EPHY_MOUSE_GESTURE_CONTROLLER (object);
 
-  g_clear_object (&self->controller);
+  if (self->gesture) {
+    gtk_widget_remove_controller (GTK_WIDGET (self->window),
+                                  GTK_EVENT_CONTROLLER (self->gesture));
+    self->gesture = NULL;
+  }
+
   ephy_mouse_gesture_controller_unset_web_view (self);
 
   G_OBJECT_CLASS (ephy_mouse_gesture_controller_parent_class)->dispose (object);
@@ -272,8 +265,24 @@ ephy_mouse_gesture_controller_constructed (GObject *object)
 
   ephy_mouse_gesture_controller_reset (self);
 
-  self->controller = gtk_event_controller_motion_new (GTK_WIDGET (self->window));
-  g_signal_connect (self->controller, "motion", G_CALLBACK (ephy_mouse_gesture_controller_motion_cb), self);
+  self->gesture = gtk_gesture_drag_new ();
+  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->gesture),
+                                              GTK_PHASE_CAPTURE);
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->gesture),
+                                 GDK_BUTTON_MIDDLE);
+  gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (self->gesture), TRUE);
+
+  g_signal_connect (self->gesture, "drag-begin",
+                    G_CALLBACK (drag_begin_cb), self);
+  g_signal_connect (self->gesture, "drag-update",
+                    G_CALLBACK (drag_update_cb), self);
+  g_signal_connect (self->gesture, "drag-end",
+                    G_CALLBACK (drag_end_cb), self);
+  g_signal_connect (self->gesture, "cancel",
+                    G_CALLBACK (cancel_cb), self);
+
+  gtk_widget_add_controller (GTK_WIDGET (self->window),
+                             GTK_EVENT_CONTROLLER (self->gesture));
 }
 
 static void
@@ -312,8 +321,11 @@ ephy_mouse_gesture_controller_set_web_view (EphyMouseGestureController *self,
 {
   ephy_mouse_gesture_controller_unset_web_view (self);
 
-  g_signal_connect_object (web_view, "button-press-event", G_CALLBACK 
(ephy_mouse_gesture_controller_button_press_cb), self, 0);
-  g_signal_connect_object (web_view, "button-release-event", G_CALLBACK 
(ephy_mouse_gesture_controller_button_release_cb), self, 0);
-
   self->web_view = g_object_ref (web_view);
 }
+
+void
+ephy_mouse_gesture_controller_unset_web_view (EphyMouseGestureController *self)
+{
+  g_clear_object (&self->web_view);
+}
diff --git a/src/ephy-page-row.c b/src/ephy-page-row.c
index 932e8f37e..eb99c05b0 100644
--- a/src/ephy-page-row.c
+++ b/src/ephy-page-row.c
@@ -37,7 +37,7 @@ struct _EphyPageRow {
   GtkLabel *title;
   GtkButton *close_button;
 
-  HdyTabPage *page;
+  AdwTabPage *page;
   EphyTabView *tab_view;
 };
 
@@ -47,7 +47,7 @@ static void
 update_spinner (EphyPageRow *self)
 {
   if (gtk_widget_get_mapped (GTK_WIDGET (self)) &&
-      hdy_tab_page_get_loading (self->page))
+      adw_tab_page_get_loading (self->page))
     gtk_spinner_start (self->spinner);
   else
     gtk_spinner_stop (self->spinner);
@@ -56,23 +56,19 @@ update_spinner (EphyPageRow *self)
 static void
 close_clicked_cb (EphyPageRow *self)
 {
-  hdy_tab_view_close_page (ephy_tab_view_get_tab_view (self->tab_view), self->page);
+  adw_tab_view_close_page (ephy_tab_view_get_tab_view (self->tab_view), self->page);
 }
 
-static gboolean
-button_release_event (GtkWidget   *widget,
-                      GdkEvent    *event,
-                      EphyPageRow *self)
+static void
+released_cb (GtkGesture  *gesture,
+             int          n_press,
+             double       x,
+             double       y,
+             EphyPageRow *self)
 {
-  GdkEventButton *button_event = (GdkEventButton *)event;
-
-  if (button_event->button == GDK_BUTTON_MIDDLE) {
-    hdy_tab_view_close_page (ephy_tab_view_get_tab_view (self->tab_view), self->page);
-
-    return GDK_EVENT_STOP;
-  }
+  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
 
-  return GDK_EVENT_PROPAGATE;
+  adw_tab_view_close_page (ephy_tab_view_get_tab_view (self->tab_view), self->page);
 }
 
 static void
@@ -90,7 +86,7 @@ ephy_page_row_class_init (EphyPageRowClass *klass)
   gtk_widget_class_bind_template_child (widget_class, EphyPageRow, close_button);
   gtk_widget_class_bind_template_callback (widget_class, update_spinner);
   gtk_widget_class_bind_template_callback (widget_class, close_clicked_cb);
-  gtk_widget_class_bind_template_callback (widget_class, button_release_event);
+  gtk_widget_class_bind_template_callback (widget_class, released_cb);
 }
 
 static void
@@ -116,14 +112,14 @@ loading_to_visible_child (GBinding     *binding,
 static void
 update_icon_cb (EphyPageRow *self)
 {
-  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (self->page));
+  EphyEmbed *embed = EPHY_EMBED (adw_tab_page_get_child (self->page));
   EphyWebView *view = ephy_embed_get_web_view (embed);
   GIcon *icon = G_ICON (ephy_web_view_get_icon (view));
   const char *uri, *favicon_name;
-  HdyTabView *tab_view;
+  AdwTabView *tab_view;
 
   if (icon) {
-    gtk_image_set_from_gicon (self->icon, icon, GTK_ICON_SIZE_MENU);
+    gtk_image_set_from_gicon (self->icon, icon);
 
     return;
   }
@@ -134,25 +130,25 @@ update_icon_cb (EphyPageRow *self)
   if (favicon_name) {
     g_autoptr (GIcon) fallback_icon = g_themed_icon_new (favicon_name);
 
-    gtk_image_set_from_gicon (self->icon, fallback_icon, GTK_ICON_SIZE_MENU);
+    gtk_image_set_from_gicon (self->icon, fallback_icon);
 
     return;
   }
 
   tab_view = ephy_tab_view_get_tab_view (self->tab_view);
 
-  gtk_image_set_from_gicon (self->icon, hdy_tab_view_get_default_icon (tab_view), GTK_ICON_SIZE_MENU);
+  gtk_image_set_from_gicon (self->icon, adw_tab_view_get_default_icon (tab_view));
 }
 
 EphyPageRow *
 ephy_page_row_new (EphyTabView *tab_view,
-                   HdyTabPage  *page)
+                   AdwTabPage  *page)
 {
   EphyPageRow *self;
-  GtkWidget *embed = hdy_tab_page_get_child (page);
+  GtkWidget *embed = adw_tab_page_get_child (page);
   EphyWebView *view;
 
-  g_assert (HDY_IS_TAB_PAGE (page));
+  g_assert (ADW_IS_TAB_PAGE (page));
   g_assert (EPHY_IS_EMBED (embed));
 
   view = ephy_embed_get_web_view (EPHY_EMBED (embed));
@@ -214,7 +210,7 @@ ephy_page_row_set_adaptive_mode (EphyPageRow      *self,
   }
 }
 
-HdyTabPage *
+AdwTabPage *
 ephy_page_row_get_page (EphyPageRow *self)
 {
   g_assert (EPHY_IS_PAGE_ROW (self));
diff --git a/src/ephy-page-row.h b/src/ephy-page-row.h
index bff823af3..e74957220 100644
--- a/src/ephy-page-row.h
+++ b/src/ephy-page-row.h
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include <handy.h>
+#include <adwaita.h>
 #include "ephy-adaptive-mode.h"
 #include "ephy-tab-view.h"
 
@@ -32,11 +32,11 @@ G_BEGIN_DECLS
 G_DECLARE_FINAL_TYPE (EphyPageRow, ephy_page_row, EPHY, PAGE_ROW, GtkListBoxRow)
 
 EphyPageRow *ephy_page_row_new (EphyTabView *view,
-                                HdyTabPage  *page);
+                                AdwTabPage  *page);
 
 void ephy_page_row_set_adaptive_mode (EphyPageRow      *self,
                                       EphyAdaptiveMode  adaptive_mode);
 
-HdyTabPage *ephy_page_row_get_page (EphyPageRow *self);
+AdwTabPage *ephy_page_row_get_page (EphyPageRow *self);
 
 G_END_DECLS
diff --git a/src/ephy-pages-button.c b/src/ephy-pages-button.c
index 757a7f79e..4a4b683c6 100644
--- a/src/ephy-pages-button.c
+++ b/src/ephy-pages-button.c
@@ -92,7 +92,7 @@ update_icon (EphyPagesButton *self)
 
   gtk_widget_set_visible (GTK_WIDGET (self->pages_label), !is_overflow);
   gtk_label_set_text (self->pages_label, label_text);
-  gtk_image_set_from_icon_name (self->pages_icon, icon_name, GTK_ICON_SIZE_BUTTON);
+  gtk_image_set_from_icon_name (self->pages_icon, icon_name);
 }
 
 EphyPagesButton *
diff --git a/src/ephy-pages-popover.c b/src/ephy-pages-popover.c
index 04c190e40..f6c028e11 100644
--- a/src/ephy-pages-popover.c
+++ b/src/ephy-pages-popover.c
@@ -23,10 +23,6 @@
 
 #include "ephy-pages-popover.h"
 
-#ifdef GDK_WINDOWING_X11
-#include <gdk/gdkx.h>
-#endif
-
 #include "ephy-page-row.h"
 #include "ephy-window.h"
 
@@ -63,7 +59,7 @@ row_activated_cb (EphyPagesPopover *self,
 {
   EphyWindow *window;
   GApplication *application;
-  HdyTabPage *page;
+  AdwTabPage *page;
 
   g_assert (EPHY_IS_PAGES_POPOVER (self));
   g_assert (EPHY_IS_PAGE_ROW (row));
@@ -72,31 +68,29 @@ row_activated_cb (EphyPagesPopover *self,
   window = EPHY_WINDOW (gtk_application_get_active_window (GTK_APPLICATION (application)));
   page = ephy_page_row_get_page (EPHY_PAGE_ROW (row));
 
-  hdy_tab_view_set_selected_page (ephy_tab_view_get_tab_view (self->tab_view), page);
+  adw_tab_view_set_selected_page (ephy_tab_view_get_tab_view (self->tab_view), page);
   ephy_window_close_pages_view (window);
 
   gtk_popover_popdown (GTK_POPOVER (self));
 }
 
 static GtkWidget *
-create_row (HdyTabPage       *page,
+create_row (AdwTabPage       *page,
             EphyPagesPopover *self)
 {
   EphyPageRow *row = ephy_page_row_new (self->tab_view, page);
 
   ephy_page_row_set_adaptive_mode (row, EPHY_ADAPTIVE_MODE_NORMAL);
 
-  gtk_widget_show (GTK_WIDGET (row));
-
   return GTK_WIDGET (row);
 }
 
 static void
-selected_page_changed_cb (HdyTabView       *tab_view,
+selected_page_changed_cb (AdwTabView       *tab_view,
                           GParamSpec       *pspec,
                           EphyPagesPopover *self)
 {
-  HdyTabPage *page = hdy_tab_view_get_selected_page (tab_view);
+  AdwTabPage *page = adw_tab_view_get_selected_page (tab_view);
   gint position;
   GtkListBoxRow *row;
 
@@ -106,7 +100,7 @@ selected_page_changed_cb (HdyTabView       *tab_view,
     return;
   }
 
-  position = hdy_tab_view_get_page_position (tab_view, page);
+  position = adw_tab_view_get_page_position (tab_view, page);
   row = gtk_list_box_get_row_at_index (self->list_box, position);
   gtk_list_box_select_row (self->list_box, row);
 }
@@ -121,30 +115,6 @@ ephy_pages_popover_dispose (GObject *object)
   G_OBJECT_CLASS (ephy_pages_popover_parent_class)->dispose (object);
 }
 
-#ifdef GDK_WINDOWING_X11
-static void
-ephy_pages_popover_get_preferred_height (GtkWidget *widget,
-                                         gint      *minimum_height,
-                                         gint      *natural_height)
-{
-  EphyPagesPopover *self = EPHY_PAGES_POPOVER (widget);
-  int height;
-
-  GTK_WIDGET_CLASS (ephy_pages_popover_parent_class)->get_preferred_height (widget,
-                                                                            minimum_height,
-                                                                            natural_height);
-  /* Ensure that popover won't leave current window */
-  height = gtk_widget_get_allocated_height (GTK_WIDGET (self->tab_view));
-  gtk_scrolled_window_set_max_content_height (self->scrolled_window, height);
-}
-
-static GtkSizeRequestMode
-ephy_pages_popover_get_request_mode (GtkWidget *widget)
-{
-  return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
-}
-#endif
-
 static void
 ephy_pages_popover_class_init (EphyPagesPopoverClass *klass)
 {
@@ -153,13 +123,6 @@ ephy_pages_popover_class_init (EphyPagesPopoverClass *klass)
 
   object_class->dispose = ephy_pages_popover_dispose;
 
-#ifdef GDK_WINDOWING_X11
-  if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
-    widget_class->get_request_mode = ephy_pages_popover_get_request_mode;
-    widget_class->get_preferred_height = ephy_pages_popover_get_preferred_height;
-  }
-#endif
-
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/epiphany/gtk/pages-popover.ui");
   gtk_widget_class_bind_template_child (widget_class, EphyPagesPopover, list_box);
   gtk_widget_class_bind_template_child (widget_class, EphyPagesPopover, scrolled_window);
@@ -173,13 +136,9 @@ ephy_pages_popover_init (EphyPagesPopover *self)
 }
 
 EphyPagesPopover *
-ephy_pages_popover_new (GtkWidget *relative_to)
+ephy_pages_popover_new (void)
 {
-  g_assert (!relative_to || GTK_IS_WIDGET (relative_to));
-
-  return g_object_new (EPHY_TYPE_PAGES_POPOVER,
-                       "relative-to", relative_to,
-                       NULL);
+  return g_object_new (EPHY_TYPE_PAGES_POPOVER, NULL);
 }
 
 EphyTabView *
@@ -205,7 +164,7 @@ ephy_pages_popover_set_tab_view (EphyPagesPopover *self,
   g_object_weak_ref (G_OBJECT (tab_view), (GWeakNotify)drop_tab_view, self);
   self->tab_view = tab_view;
 
-  self->model = hdy_tab_view_get_pages (ephy_tab_view_get_tab_view (tab_view));
+  self->model = G_LIST_MODEL (adw_tab_view_get_pages (ephy_tab_view_get_tab_view (tab_view)));
 
   gtk_list_box_bind_model (self->list_box,
                            self->model,
diff --git a/src/ephy-pages-popover.h b/src/ephy-pages-popover.h
index 1c148d403..d0a9c5eba 100644
--- a/src/ephy-pages-popover.h
+++ b/src/ephy-pages-popover.h
@@ -31,7 +31,7 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (EphyPagesPopover, ephy_pages_popover, EPHY, PAGES_POPOVER, GtkPopover)
 
-EphyPagesPopover *ephy_pages_popover_new (GtkWidget *relative_to);
+EphyPagesPopover *ephy_pages_popover_new (void);
 
 EphyTabView *ephy_pages_popover_get_tab_view (EphyPagesPopover *self);
 void         ephy_pages_popover_set_tab_view (EphyPagesPopover *self,
diff --git a/src/ephy-pages-view.c b/src/ephy-pages-view.c
index 6b0f57184..4eb11ab56 100644
--- a/src/ephy-pages-view.c
+++ b/src/ephy-pages-view.c
@@ -44,7 +44,7 @@ row_activated_cb (EphyPagesView *self,
 {
   EphyWindow *window;
   GApplication *application;
-  HdyTabPage *page;
+  AdwTabPage *page;
 
   g_assert (EPHY_IS_PAGES_VIEW (self));
   g_assert (EPHY_IS_PAGE_ROW (row));
@@ -53,29 +53,27 @@ row_activated_cb (EphyPagesView *self,
   window = EPHY_WINDOW (gtk_application_get_active_window (GTK_APPLICATION (application)));
   page = ephy_page_row_get_page (EPHY_PAGE_ROW (row));
 
-  hdy_tab_view_set_selected_page (ephy_tab_view_get_tab_view (self->tab_view), page);
+  adw_tab_view_set_selected_page (ephy_tab_view_get_tab_view (self->tab_view), page);
   ephy_window_close_pages_view (window);
 }
 
 static GtkWidget *
-create_row (HdyTabPage    *page,
+create_row (AdwTabPage    *page,
             EphyPagesView *self)
 {
   EphyPageRow *row = ephy_page_row_new (self->tab_view, page);
 
   ephy_page_row_set_adaptive_mode (row, EPHY_ADAPTIVE_MODE_NARROW);
 
-  gtk_widget_show (GTK_WIDGET (row));
-
   return GTK_WIDGET (row);
 }
 
 static void
-selected_page_changed_cb (HdyTabView    *tab_view,
+selected_page_changed_cb (AdwTabView    *tab_view,
                           GParamSpec    *pspec,
                           EphyPagesView *self)
 {
-  HdyTabPage *page = hdy_tab_view_get_selected_page (tab_view);
+  AdwTabPage *page = adw_tab_view_get_selected_page (tab_view);
   gint position;
   GtkListBoxRow *row;
 
@@ -85,7 +83,7 @@ selected_page_changed_cb (HdyTabView    *tab_view,
     return;
   }
 
-  position = hdy_tab_view_get_page_position (tab_view, page);
+  position = adw_tab_view_get_page_position (tab_view, page);
   row = gtk_list_box_get_row_at_index (self->list_box, position);
   gtk_list_box_select_row (self->list_box, row);
 }
@@ -147,7 +145,7 @@ ephy_pages_view_set_tab_view (EphyPagesView *self,
   g_object_add_weak_pointer (G_OBJECT (tab_view), (gpointer *)&self->tab_view);
   self->tab_view = tab_view;
 
-  self->model = hdy_tab_view_get_pages (ephy_tab_view_get_tab_view (tab_view));
+  self->model = G_LIST_MODEL (adw_tab_view_get_pages (ephy_tab_view_get_tab_view (tab_view)));
 
   gtk_list_box_bind_model (self->list_box,
                            self->model,
diff --git a/src/ephy-session.c b/src/ephy-session.c
index 764ed1a19..566cf499e 100644
--- a/src/ephy-session.c
+++ b/src/ephy-session.c
@@ -255,7 +255,7 @@ ephy_session_undo_close_tab (EphySession *session)
       flags |= EPHY_NEW_TAB_FIRST;
     }
 
-    window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab_view)));
+    window = EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (tab_view)));
     new_tab = ephy_shell_new_tab (ephy_shell_get_default (),
                                   window, embed,
                                   flags);
@@ -325,24 +325,24 @@ ephy_session_get_can_undo_tab_closed (EphySession *session)
 }
 
 static void
-tab_view_page_attached_cb (HdyTabView  *tab_view,
-                           HdyTabPage  *page,
+tab_view_page_attached_cb (AdwTabView  *tab_view,
+                           AdwTabPage  *page,
                            guint        position,
                            EphySession *session)
 {
-  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyEmbed *embed = EPHY_EMBED (adw_tab_page_get_child (page));
 
   g_signal_connect (ephy_embed_get_web_view (embed), "load-changed",
                     G_CALLBACK (load_changed_cb), session);
 }
 
 static void
-tab_view_page_detached_cb (HdyTabView  *tab_view,
-                           HdyTabPage  *page,
+tab_view_page_detached_cb (AdwTabView  *tab_view,
+                           AdwTabPage  *page,
                            gint         position,
                            EphySession *session)
 {
-  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyEmbed *embed = EPHY_EMBED (adw_tab_page_get_child (page));
   EphyTabView *ephy_tab_view = EPHY_TAB_VIEW (g_object_get_data (G_OBJECT (tab_view), "ephy-tab-view"));
 
   ephy_session_save (session);
@@ -355,8 +355,8 @@ tab_view_page_detached_cb (HdyTabView  *tab_view,
 }
 
 static void
-tab_view_page_reordered_cb (HdyTabView  *tab_view,
-                            HdyTabPage  *page,
+tab_view_page_reordered_cb (AdwTabView  *tab_view,
+                            AdwTabPage  *page,
                             guint        position,
                             EphySession *session)
 {
@@ -364,7 +364,7 @@ tab_view_page_reordered_cb (HdyTabView  *tab_view,
 }
 
 static void
-tab_view_notify_selected_page_cb (HdyTabView  *tab_view,
+tab_view_notify_selected_page_cb (AdwTabView  *tab_view,
                                   GParamSpec  *pspec,
                                   EphySession *session)
 {
@@ -391,7 +391,7 @@ window_added_cb (GtkApplication *application,
                  EphySession    *session)
 {
   EphyWindow *ephy_window;
-  HdyTabView *tab_view;
+  AdwTabView *tab_view;
 
   ephy_session_save (session);
 
@@ -1133,6 +1133,14 @@ session_parser_context_free (SessionParserContext *context)
   g_free (context);
 }
 
+static void
+window_destroyed (GtkWidget  *widget,
+                  GtkWidget **widget_pointer)
+{
+  if (widget_pointer)
+    *widget_pointer = NULL;
+}
+
 static void
 session_parse_window (SessionParserContext  *context,
                       const gchar          **names,
@@ -1149,7 +1157,7 @@ session_parse_window (SessionParserContext  *context,
   }
 
   context->window = ephy_window_new ();
-  context->destroy_id = g_signal_connect (context->window, "destroy", G_CALLBACK (gtk_widget_destroyed), 
&context->window);
+  context->destroy_id = g_signal_connect (context->window, "destroy", G_CALLBACK (window_destroyed), 
&context->window);
 
   for (i = 0; names[i]; i++) {
     gulong int_value;
@@ -1195,7 +1203,7 @@ session_parse_embed (SessionParserContext  *context,
                      const gchar          **names,
                      const gchar          **values)
 {
-  HdyTabView *tab_view;
+  AdwTabView *tab_view;
   const char *url = NULL;
   const char *title = NULL;
   const char *history = NULL;
@@ -1262,8 +1270,8 @@ session_parse_embed (SessionParserContext  *context,
                                      context->window, NULL, flags,
                                      0);
 
-    hdy_tab_view_set_page_pinned (tab_view,
-                                  hdy_tab_view_get_page (tab_view, GTK_WIDGET (embed)),
+    adw_tab_view_set_page_pinned (tab_view,
+                                  adw_tab_view_get_page (tab_view, GTK_WIDGET (embed)),
                                   is_pin);
 
     web_view = ephy_embed_get_web_view (embed);
@@ -1794,7 +1802,7 @@ ephy_session_clear (EphySession *session)
   shell = ephy_shell_get_default ();
   windows = g_list_copy (gtk_application_get_windows (GTK_APPLICATION (shell)));
   for (p = windows; p; p = p->next)
-    gtk_widget_destroy (GTK_WIDGET (p->data));
+    gtk_window_destroy (GTK_WINDOW (p->data));
   g_list_free (windows);
   g_queue_foreach (session->closed_tabs,
                    (GFunc)closed_tab_free, NULL);
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index f016911aa..bc009182f 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -50,7 +50,6 @@
 
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
-#include <handy.h>
 
 struct _EphyShell {
   EphyEmbedShell parent_instance;
@@ -488,11 +487,6 @@ ephy_shell_startup (GApplication *application)
 
   G_APPLICATION_CLASS (ephy_shell_parent_class)->startup (application);
 
-  hdy_init ();
-
-  hdy_style_manager_set_color_scheme (hdy_style_manager_get_default (),
-                                      HDY_COLOR_SCHEME_PREFER_LIGHT);
-
   /* If we are under Pantheon set the icon-theme and cursor-theme accordingly. */
   if (is_desktop_pantheon ()) {
     GtkSettings *settings = gtk_settings_get_default ();
@@ -813,11 +807,6 @@ ephy_shell_constructed (GObject *object)
     g_application_set_flags (G_APPLICATION (object), flags);
   }
 
-  if (ephy_embed_shell_get_mode (EPHY_EMBED_SHELL (object)) == EPHY_EMBED_SHELL_MODE_APPLICATION) {
-    dzl_application_add_resources (DZL_APPLICATION (object),
-                                   "resource:///org/gnome/Epiphany");
-  }
-
   /* FIXME: not sure if this is the best place to put this stuff. */
   ephy_shell_get_lockdown (EPHY_SHELL (object));
 
@@ -869,9 +858,13 @@ ephy_shell_dispose (GObject *object)
 
   LOG ("EphyShell disposing");
 
+  if (shell->history_dialog) {
+    gtk_window_destroy (GTK_WINDOW (shell->history_dialog));
+    shell->history_dialog = NULL;
+  }
+
   g_clear_object (&shell->session);
   g_clear_object (&shell->lockdown);
-  g_clear_pointer (&shell->history_dialog, gtk_widget_destroy);
   g_clear_object (&shell->prefs_dialog);
   g_clear_object (&shell->network_monitor);
   g_clear_object (&shell->sync_service);
@@ -1164,7 +1157,6 @@ ephy_shell_get_bookmarks_manager (EphyShell *shell)
 
   return shell->bookmarks_manager;
 }
-
 /**
  * ephy_shell_get_history_manager:
  * @shell: the #EphyShell
@@ -1215,6 +1207,14 @@ ephy_shell_get_net_monitor (EphyShell *shell)
   return shell->network_monitor;
 }
 
+static void
+window_destroyed (GtkWidget  *widget,
+                  GtkWidget **widget_pointer)
+{
+  if (widget_pointer)
+    *widget_pointer = NULL;
+}
+
 /**
  * ephy_shell_get_history_dialog:
  *
@@ -1233,7 +1233,7 @@ ephy_shell_get_history_dialog (EphyShell *shell)
     shell->history_dialog = ephy_history_dialog_new (service);
     g_signal_connect (shell->history_dialog,
                       "destroy",
-                      G_CALLBACK (gtk_widget_destroyed),
+                      G_CALLBACK (window_destroyed),
                       &shell->history_dialog);
   }
 
@@ -1252,7 +1252,7 @@ ephy_shell_get_firefox_sync_dialog (EphyShell *shell)
     shell->firefox_sync_dialog = ephy_firefox_sync_dialog_new ();
     g_signal_connect (shell->firefox_sync_dialog,
                       "destroy",
-                      G_CALLBACK (gtk_widget_destroyed),
+                      G_CALLBACK (window_destroyed),
                       &shell->firefox_sync_dialog);
   }
 
@@ -1272,7 +1272,7 @@ ephy_shell_get_prefs_dialog (EphyShell *shell)
 
     g_signal_connect (shell->prefs_dialog,
                       "destroy",
-                      G_CALLBACK (gtk_widget_destroyed),
+                      G_CALLBACK (window_destroyed),
                       &shell->prefs_dialog);
   }
 
@@ -1354,7 +1354,7 @@ ephy_shell_close_all_windows (EphyShell *shell)
     windows = windows->next;
 
     if (ephy_window_close (window))
-      gtk_widget_destroy (GTK_WIDGET (window));
+      gtk_window_destroy (GTK_WINDOW (window));
     else
       retval = FALSE;
   }
diff --git a/src/ephy-suggestion-model.c b/src/ephy-suggestion-model.c
index 3299e2db6..ddfab9557 100644
--- a/src/ephy-suggestion-model.c
+++ b/src/ephy-suggestion-model.c
@@ -27,7 +27,7 @@
 #include "ephy-user-agent.h"
 #include "ephy-window.h"
 
-#include <dazzle.h>
+#include "dzl-fuzzy-mutable-index.h"
 #include <glib/gi18n.h>
 
 #define MAX_URL_ENTRIES             25
diff --git a/src/ephy-tab-view.c b/src/ephy-tab-view.c
index e2c785892..b69454613 100644
--- a/src/ephy-tab-view.c
+++ b/src/ephy-tab-view.c
@@ -22,7 +22,6 @@
 #include "ephy-tab-view.h"
 
 #include "ephy-desktop-utils.h"
-#include "ephy-dnd.h"
 #include "ephy-embed-utils.h"
 #include "ephy-link.h"
 #include "ephy-settings.h"
@@ -31,14 +30,14 @@
 #define MAX_NUMBER_OF_URLS 20
 
 struct _EphyTabView {
-  GtkBin parent_instance;
+  AdwBin parent_instance;
 
-  HdyTabView *tab_view;
-  HdyTabBar *tab_bar;
-  HdyTabPage *current_page;
+  AdwTabView *tab_view;
+  AdwTabBar *tab_bar;
+  AdwTabPage *current_page;
 };
 
-G_DEFINE_TYPE (EphyTabView, ephy_tab_view, GTK_TYPE_BIN)
+G_DEFINE_TYPE (EphyTabView, ephy_tab_view, ADW_TYPE_BIN)
 
 enum {
   PROP_0,
@@ -58,19 +57,19 @@ notify_n_pages_cb (EphyTabView *self)
 static void
 notify_selected_page_cb (EphyTabView *self)
 {
-  HdyTabPage *page = hdy_tab_view_get_selected_page (self->tab_view);
+  AdwTabPage *page = adw_tab_view_get_selected_page (self->tab_view);
 
   if (page)
-    hdy_tab_page_set_needs_attention (page, FALSE);
+    adw_tab_page_set_needs_attention (page, FALSE);
 
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_INDEX]);
 }
 
 static void
 indicator_activated_cb (EphyTabView *self,
-                        HdyTabPage  *page)
+                        AdwTabPage  *page)
 {
-  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyEmbed *embed = EPHY_EMBED (adw_tab_page_get_child (page));
   EphyWebView *view = ephy_embed_get_web_view (embed);
   gboolean muted = webkit_web_view_get_is_muted (WEBKIT_WEB_VIEW (view));
 
@@ -79,18 +78,18 @@ indicator_activated_cb (EphyTabView *self,
 
 static void
 setup_menu_cb (EphyTabView *self,
-               HdyTabPage  *page)
+               AdwTabPage  *page)
 {
   self->current_page = page;
 }
 
-static HdyTabPage *
+static AdwTabPage *
 get_current_page (EphyTabView *self)
 {
   if (self->current_page)
     return self->current_page;
 
-  return hdy_tab_view_get_selected_page (self->tab_view);
+  return adw_tab_view_get_selected_page (self->tab_view);
 }
 
 static void
@@ -150,12 +149,11 @@ ephy_tab_view_class_init (EphyTabViewClass *klass)
 static void
 ephy_tab_view_init (EphyTabView *self)
 {
-  self->tab_view = HDY_TAB_VIEW (hdy_tab_view_new ());
-  gtk_widget_show (GTK_WIDGET (self->tab_view));
+  self->tab_view = ADW_TAB_VIEW (adw_tab_view_new ());
 
   g_object_set_data (G_OBJECT (self->tab_view), "ephy-tab-view", self);
 
-  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->tab_view));
+  adw_bin_set_child (ADW_BIN (self), GTK_WIDGET (self->tab_view));
 
   g_signal_connect_object (self->tab_view,
                            "notify::n-pages",
@@ -191,52 +189,52 @@ ephy_tab_view_new (void)
 void
 ephy_tab_view_next (EphyTabView *self)
 {
-  hdy_tab_view_select_next_page (self->tab_view);
+  adw_tab_view_select_next_page (self->tab_view);
 }
 
 void
 ephy_tab_view_pin (EphyTabView *self)
 {
-  hdy_tab_view_set_page_pinned (self->tab_view, get_current_page (self), TRUE);
+  adw_tab_view_set_page_pinned (self->tab_view, get_current_page (self), TRUE);
 }
 
 void
 ephy_tab_view_unpin (EphyTabView *self)
 {
-  hdy_tab_view_set_page_pinned (self->tab_view, get_current_page (self), FALSE);
+  adw_tab_view_set_page_pinned (self->tab_view, get_current_page (self), FALSE);
 }
 
 void
 ephy_tab_view_close (EphyTabView *self,
                      GtkWidget   *widget)
 {
-  HdyTabPage *page = hdy_tab_view_get_page (self->tab_view, widget);
+  AdwTabPage *page = adw_tab_view_get_page (self->tab_view, widget);
 
-  hdy_tab_view_close_page (self->tab_view, page);
+  adw_tab_view_close_page (self->tab_view, page);
 }
 
 void
 ephy_tab_view_close_selected (EphyTabView *self)
 {
-  hdy_tab_view_close_page (self->tab_view, get_current_page (self));
+  adw_tab_view_close_page (self->tab_view, get_current_page (self));
 }
 
 void
 ephy_tab_view_close_left (EphyTabView *self)
 {
-  hdy_tab_view_close_pages_before (self->tab_view, get_current_page (self));
+  adw_tab_view_close_pages_before (self->tab_view, get_current_page (self));
 }
 
 void
 ephy_tab_view_close_right (EphyTabView *self)
 {
-  hdy_tab_view_close_pages_after (self->tab_view, get_current_page (self));
+  adw_tab_view_close_pages_after (self->tab_view, get_current_page (self));
 }
 
 void
 ephy_tab_view_close_other (EphyTabView *self)
 {
-  hdy_tab_view_close_other_pages (self->tab_view, get_current_page (self));
+  adw_tab_view_close_other_pages (self->tab_view, get_current_page (self));
 }
 
 void
@@ -246,67 +244,67 @@ ephy_tab_view_foreach (EphyTabView         *self,
 {
   int i, n;
 
-  n = hdy_tab_view_get_n_pages (self->tab_view);
+  n = adw_tab_view_get_n_pages (self->tab_view);
 
   for (i = 0; i < n; i++) {
-    HdyTabPage *page = hdy_tab_view_get_nth_page (self->tab_view, i);
+    AdwTabPage *page = adw_tab_view_get_nth_page (self->tab_view, i);
 
-    callback (hdy_tab_page_get_child (page), user_data);
+    callback (adw_tab_page_get_child (page), user_data);
   }
 }
 
 int
 ephy_tab_view_get_n_pages (EphyTabView *self)
 {
-  return hdy_tab_view_get_n_pages (self->tab_view);
+  return adw_tab_view_get_n_pages (self->tab_view);
 }
 
 int
 ephy_tab_view_get_selected_index (EphyTabView *self)
 {
-  HdyTabPage *page = hdy_tab_view_get_selected_page (self->tab_view);
+  AdwTabPage *page = adw_tab_view_get_selected_page (self->tab_view);
 
   if (!page)
     return -1;
 
-  return hdy_tab_view_get_page_position (self->tab_view, page);
+  return adw_tab_view_get_page_position (self->tab_view, page);
 }
 
 int
 ephy_tab_view_get_page_index (EphyTabView *self,
                               GtkWidget   *widget)
 {
-  HdyTabPage *page = hdy_tab_view_get_page (self->tab_view, widget);
+  AdwTabPage *page = adw_tab_view_get_page (self->tab_view, widget);
 
-  return hdy_tab_view_get_page_position (self->tab_view, page);
+  return adw_tab_view_get_page_position (self->tab_view, page);
 }
 
 GtkWidget *
 ephy_tab_view_get_nth_page (EphyTabView *self,
                             int          index)
 {
-  HdyTabPage *page = hdy_tab_view_get_nth_page (self->tab_view, index);
+  AdwTabPage *page = adw_tab_view_get_nth_page (self->tab_view, index);
 
-  return hdy_tab_page_get_child (page);
+  return adw_tab_page_get_child (page);
 }
 
 void
 ephy_tab_view_select_nth_page (EphyTabView *self,
                                int          index)
 {
-  HdyTabPage *page = hdy_tab_view_get_nth_page (self->tab_view, index);
+  AdwTabPage *page = adw_tab_view_get_nth_page (self->tab_view, index);
 
-  hdy_tab_view_set_selected_page (self->tab_view, page);
+  adw_tab_view_set_selected_page (self->tab_view, page);
 }
 
 gboolean
 ephy_tab_view_select_page (EphyTabView *self,
                            GtkWidget   *widget)
 {
-  HdyTabPage *page = hdy_tab_view_get_page (self->tab_view, widget);
+  AdwTabPage *page = adw_tab_view_get_page (self->tab_view, widget);
 
   if (page)
-    hdy_tab_view_set_selected_page (self->tab_view, page);
+    adw_tab_view_set_selected_page (self->tab_view, page);
 
   return !!page;
 }
@@ -314,15 +312,15 @@ ephy_tab_view_select_page (EphyTabView *self,
 GtkWidget *
 ephy_tab_view_get_selected_page (EphyTabView *self)
 {
-  HdyTabPage *page = hdy_tab_view_get_selected_page (self->tab_view);
+  AdwTabPage *page = adw_tab_view_get_selected_page (self->tab_view);
 
   if (!page)
     return NULL;
 
-  return hdy_tab_page_get_child (page);
+  return adw_tab_page_get_child (page);
 }
 
-HdyTabView *
+AdwTabView *
 ephy_tab_view_get_tab_view (EphyTabView *self)
 {
   return self->tab_view;
@@ -334,11 +332,11 @@ ephy_tab_view_get_pages (EphyTabView *self)
   GList *list = NULL;
   int i, n;
 
-  n = hdy_tab_view_get_n_pages (self->tab_view);
+  n = adw_tab_view_get_n_pages (self->tab_view);
 
   for (i = 0; i < n; i++) {
-    HdyTabPage *page = hdy_tab_view_get_nth_page (self->tab_view, i);
-    GtkWidget *content = hdy_tab_page_get_child (page);
+    AdwTabPage *page = adw_tab_view_get_nth_page (self->tab_view, i);
+    GtkWidget *content = adw_tab_page_get_child (page);
 
     list = g_list_prepend (list, content);
   }
@@ -350,26 +348,26 @@ gboolean
 ephy_tab_view_get_is_pinned (EphyTabView *self,
                              GtkWidget   *widget)
 {
-  HdyTabPage *page = hdy_tab_view_get_page (self->tab_view, widget);
+  AdwTabPage *page = adw_tab_view_get_page (self->tab_view, widget);
 
-  return hdy_tab_page_get_pinned (page);
+  return adw_tab_page_get_pinned (page);
 }
 
 static void
-update_title_cb (HdyTabPage *page)
+update_title_cb (AdwTabPage *page)
 {
-  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyEmbed *embed = EPHY_EMBED (adw_tab_page_get_child (page));
   EphyWebView *view = ephy_embed_get_web_view (embed);
   const char *title = ephy_embed_get_title (embed);
   const char *address;
 
   if (!ephy_embed_has_load_pending (embed) &&
-      !hdy_tab_page_get_selected (page) &&
-      hdy_tab_page_get_pinned (page))
-    hdy_tab_page_set_needs_attention (page, TRUE);
+      !adw_tab_page_get_selected (page) &&
+      adw_tab_page_get_pinned (page))
+    adw_tab_page_set_needs_attention (page, TRUE);
 
   if (title && strlen (title)) {
-    hdy_tab_page_set_title (page, title);
+    adw_tab_page_set_title (page, title);
     return;
   }
 
@@ -377,20 +375,20 @@ update_title_cb (HdyTabPage *page)
 
   if (ephy_web_view_is_loading (view) &&
       !ephy_embed_utils_is_no_show_address (address))
-    hdy_tab_page_set_title (page, address);
+    adw_tab_page_set_title (page, address);
 }
 
 static void
-update_icon_cb (HdyTabPage *page)
+update_icon_cb (AdwTabPage *page)
 {
-  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyEmbed *embed = EPHY_EMBED (adw_tab_page_get_child (page));
   EphyWebView *view = ephy_embed_get_web_view (embed);
   GIcon *icon = G_ICON (ephy_web_view_get_icon (view));
   g_autoptr (GIcon) placeholder_icon = NULL;
   const char *uri, *favicon_name;
 
   if (icon) {
-    hdy_tab_page_set_icon (page, icon);
+    adw_tab_page_set_icon (page, icon);
     return;
   }
 
@@ -400,13 +398,13 @@ update_icon_cb (HdyTabPage *page)
   if (favicon_name)
     placeholder_icon = g_themed_icon_new (favicon_name);
 
-  hdy_tab_page_set_icon (page, placeholder_icon);
+  adw_tab_page_set_icon (page, placeholder_icon);
 }
 
 static void
-update_indicator_cb (HdyTabPage *page)
+update_indicator_cb (AdwTabPage *page)
 {
-  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyEmbed *embed = EPHY_EMBED (adw_tab_page_get_child (page));
   EphyWebView *view = ephy_embed_get_web_view (embed);
   g_autoptr (GIcon) icon = NULL;
 
@@ -417,7 +415,7 @@ update_indicator_cb (HdyTabPage *page)
       icon = G_ICON (g_themed_icon_new ("ephy-audio-playing-symbolic"));
   }
 
-  hdy_tab_page_set_indicator_icon (page, icon);
+  adw_tab_page_set_indicator_icon (page, icon);
 }
 
 int
@@ -427,26 +425,26 @@ ephy_tab_view_add_tab (EphyTabView *self,
                        int          position,
                        gboolean     jump_to)
 {
-  HdyTabPage *page;
+  AdwTabPage *page;
   EphyWebView *view;
 
   if (parent) {
-    HdyTabPage *parent_page;
+    AdwTabPage *parent_page;
 
-    parent_page = hdy_tab_view_get_page (self->tab_view, GTK_WIDGET (parent));
-    page = hdy_tab_view_add_page (self->tab_view, GTK_WIDGET (embed), parent_page);
+    parent_page = adw_tab_view_get_page (self->tab_view, GTK_WIDGET (parent));
+    page = adw_tab_view_add_page (self->tab_view, GTK_WIDGET (embed), parent_page);
   } else if (position < 0) {
-    page = hdy_tab_view_append (self->tab_view, GTK_WIDGET (embed));
+    page = adw_tab_view_append (self->tab_view, GTK_WIDGET (embed));
   } else {
-    page = hdy_tab_view_insert (self->tab_view, GTK_WIDGET (embed), position);
+    page = adw_tab_view_insert (self->tab_view, GTK_WIDGET (embed), position);
   }
 
   if (jump_to)
-    hdy_tab_view_set_selected_page (self->tab_view, page);
+    adw_tab_view_set_selected_page (self->tab_view, page);
 
   view = ephy_embed_get_web_view (embed);
 
-  hdy_tab_page_set_indicator_activatable (page, TRUE);
+  adw_tab_page_set_indicator_activatable (page, TRUE);
 
   g_object_bind_property (view, "is-loading", page, "loading", G_BINDING_SYNC_CREATE);
 
@@ -473,77 +471,69 @@ ephy_tab_view_add_tab (EphyTabView *self,
   update_icon_cb (page);
   update_indicator_cb (page);
 
-  return hdy_tab_view_get_page_position (self->tab_view, page);
+  return adw_tab_view_get_page_position (self->tab_view, page);
 }
 
 GtkWidget *
 ephy_tab_view_get_current_page (EphyTabView *self)
 {
-  HdyTabPage *page = get_current_page (self);
+  AdwTabPage *page = get_current_page (self);
 
   if (!page)
     return NULL;
 
-  return hdy_tab_page_get_child (page);
+  return adw_tab_page_get_child (page);
 }
 
 static void
-drag_data_received_cb (EphyTabView      *self,
-                       HdyTabPage       *page,
-                       GdkDragContext   *context,
-                       GtkSelectionData *selection_data,
-                       guint             info,
-                       guint             time)
-{
-  GtkWidget *window;
+drag_drop_cb (EphyTabView  *self,
+              AdwTabPage   *page,
+              const GValue *value)
+{
+  EphyLink *window;
   EphyEmbed *embed;
-  GdkAtom target;
-  const guchar *data;
 
   if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
                               EPHY_PREFS_LOCKDOWN_ARBITRARY_URL))
     return;
 
-  data = gtk_selection_data_get_data (selection_data);
-  if (gtk_selection_data_get_length (selection_data) <= 0 || data)
-    return;
+  embed = EPHY_EMBED (adw_tab_page_get_child (page));
+  window = EPHY_LINK (gtk_widget_get_root (GTK_WIDGET (self)));
 
-  embed = EPHY_EMBED (hdy_tab_page_get_child (page));
-  target = gtk_selection_data_get_target (selection_data);
+  if (G_VALUE_HOLDS (value, G_TYPE_FILE)) {
+    GFile *file = g_value_get_object (value);
+    g_autofree char *uri = g_file_get_uri (file);
+    g_print ("1\n");
 
-  window = gtk_widget_get_toplevel (GTK_WIDGET (self));
+    ephy_link_open (window, uri, embed, embed ? 0 : EPHY_LINK_NEW_TAB);
+  } else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST)) {
+    GdkFileList *file_list = g_value_get_object (value);
+    g_autoptr (GSList) files = gdk_file_list_get_files (file_list);
+    GSList *l;
+    int i = 0;
+    g_print ("2\n");
 
-  if (target == gdk_atom_intern (EPHY_DND_URL_TYPE, FALSE)) {
-    /* URL_TYPE has format: url \n title */
-    g_auto (GStrv) split = g_strsplit ((const char *)data, "\n", 2);
+    for (l = files; l && i < MAX_NUMBER_OF_URLS; l = l->next) {
+      GFile *file = l->data;
+      g_autofree char *uri = g_file_get_uri (file);
 
-    if (split && split[0] && split[0][0] != '\0') {
-      ephy_link_open (EPHY_LINK (window), NULL, NULL, EPHY_LINK_NEW_TAB);
-      ephy_link_open (EPHY_LINK (window), split[0], embed,
-                      embed ? 0 : EPHY_LINK_NEW_TAB);
+      ephy_link_open (window, uri, embed, (embed && i == 0) ? 0 : EPHY_LINK_NEW_TAB);
+      i++;
     }
-  } else if (target == gdk_atom_intern (EPHY_DND_URI_LIST_TYPE, FALSE)) {
-    g_auto (GStrv) uris = gtk_selection_data_get_uris (selection_data);
+  } else if (G_VALUE_HOLDS (value, G_TYPE_STRING)) {
+    const char *text = g_value_get_string (value);
+    g_auto (GStrv) split = g_strsplit (text, "\n", MAX_NUMBER_OF_URLS);
     int i;
 
-    if (!uris)
-      return;
+    for (i = 0; *split[i]; i++) {
+      const char *uri = split[i];
+      g_autofree char *normalized =
+        ephy_embed_utils_normalize_or_autosearch_address (uri);
 
-    for (i = 0; i < MAX_NUMBER_OF_URLS && uris[i]; i++) {
-      embed = ephy_link_open (EPHY_LINK (window), uris[i], embed,
-                              (embed && i == 0) ? 0 : EPHY_LINK_NEW_TAB);
+      ephy_link_open (window, normalized, embed, (embed && i == 0) ? 0 : EPHY_LINK_NEW_TAB);
     }
   } else {
-    g_autofree char *text =
-      (char *)gtk_selection_data_get_text (selection_data);
-
-    if (text) {
-      g_autofree char *address =
-        ephy_embed_utils_normalize_or_autosearch_address (text);
-
-      ephy_link_open (EPHY_LINK (window), address, embed,
-                      embed ? 0 : EPHY_LINK_NEW_TAB);
-    }
+    g_assert_not_reached ();
   }
 }
 
@@ -561,7 +551,7 @@ visibility_policy_changed_cb (EphyTabView *self)
     policy = g_settings_get_enum (EPHY_SETTINGS_UI,
                                   EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY);
 
-  hdy_tab_bar_set_autohide (self->tab_bar,
+  adw_tab_bar_set_autohide (self->tab_bar,
                             policy != EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY_ALWAYS);
   gtk_widget_set_visible (GTK_WIDGET (self->tab_bar),
                           mode != EPHY_EMBED_SHELL_MODE_APPLICATION &&
@@ -574,7 +564,7 @@ expand_changed_cb (EphyTabView *self)
   gboolean expand = g_settings_get_boolean (EPHY_SETTINGS_UI,
                                             EPHY_PREFS_UI_EXPAND_TABS_BAR);
 
-  hdy_tab_bar_set_expand_tabs (self->tab_bar, expand);
+  adw_tab_bar_set_expand_tabs (self->tab_bar, expand);
 }
 
 static gboolean
@@ -599,46 +589,40 @@ is_layout_reversed (void)
 static void
 notify_decoration_layout_cb (EphyTabView *self)
 {
-  hdy_tab_bar_set_inverted (self->tab_bar, is_layout_reversed ());
+  adw_tab_bar_set_inverted (self->tab_bar, is_layout_reversed ());
 }
 
 void
 ephy_tab_view_set_tab_bar (EphyTabView *self,
-                           HdyTabBar   *tab_bar)
+                           AdwTabBar   *tab_bar)
 {
-  g_autoptr (GtkTargetList) target_list = NULL;
   GtkSettings *settings;
-  static const GtkTargetEntry url_drag_types [] = {
-    { (char *)EPHY_DND_URI_LIST_TYPE, 0, 0 },
-    { (char *)EPHY_DND_URL_TYPE, 0, 1 },
-  };
 
   self->tab_bar = tab_bar;
 
-  target_list = gtk_target_list_new (url_drag_types,
-                                     G_N_ELEMENTS (url_drag_types));
-  gtk_target_list_add_text_targets (target_list, 0);
-
-  hdy_tab_bar_set_extra_drag_dest_targets (self->tab_bar, target_list);
+  adw_tab_bar_setup_extra_drop_target (tab_bar, GDK_ACTION_COPY, (GType[3]) {
+    G_TYPE_STRING,
+    G_TYPE_FILE,
+    GDK_TYPE_FILE_LIST,
+  }, 3);
 
-  g_signal_connect_object (tab_bar, "extra-drag-data-received",
-                           G_CALLBACK (drag_data_received_cb), self,
+  g_signal_connect_object (tab_bar, "extra-drag-drop",
+                           G_CALLBACK (drag_drop_cb), self,
                            G_CONNECT_SWAPPED);
 
   if (is_desktop_pantheon ()) {
     GtkWidget *button;
 
-    hdy_tab_bar_set_autohide (tab_bar, FALSE);
-    hdy_tab_bar_set_expand_tabs (tab_bar, FALSE);
+    adw_tab_bar_set_autohide (tab_bar, FALSE);
+    adw_tab_bar_set_expand_tabs (tab_bar, FALSE);
 
-    button = gtk_button_new_from_icon_name ("list-add-symbolic", GTK_ICON_SIZE_MENU);
+    button = gtk_button_new_from_icon_name ("list-add-symbolic");
     /* Translators: tooltip for the new tab button */
     gtk_widget_set_tooltip_text (button, _("Open a new tab"));
     gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.new-tab");
     gtk_style_context_add_class (gtk_widget_get_style_context (button), "flat");
-    gtk_widget_show (button);
 
-    hdy_tab_bar_set_start_action_widget (tab_bar, button);
+    adw_tab_bar_set_start_action_widget (tab_bar, button);
   } else {
     g_signal_connect_object (EPHY_SETTINGS_UI,
                              "changed::" EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY,
diff --git a/src/ephy-tab-view.h b/src/ephy-tab-view.h
index b95d1c85f..fd6c04484 100644
--- a/src/ephy-tab-view.h
+++ b/src/ephy-tab-view.h
@@ -22,13 +22,13 @@
 
 #include "ephy-embed.h"
 
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_TAB_VIEW (ephy_tab_view_get_type())
 
-G_DECLARE_FINAL_TYPE (EphyTabView, ephy_tab_view, EPHY, TAB_VIEW, GtkBin)
+G_DECLARE_FINAL_TYPE (EphyTabView, ephy_tab_view, EPHY, TAB_VIEW, AdwBin)
 
 typedef void (*EphyTabViewCallback) (GtkWidget *widget,
                                      gpointer   data);
@@ -66,7 +66,7 @@ gboolean     ephy_tab_view_select_page        (EphyTabView *self,
 
 GtkWidget   *ephy_tab_view_get_selected_page  (EphyTabView *self);
 
-HdyTabView  *ephy_tab_view_get_tab_view       (EphyTabView *self);
+AdwTabView  *ephy_tab_view_get_tab_view       (EphyTabView *self);
 
 GList        *ephy_tab_view_get_pages         (EphyTabView *self);
 
@@ -82,6 +82,6 @@ gint          ephy_tab_view_add_tab           (EphyTabView *self,
 GtkWidget    *ephy_tab_view_get_current_page  (EphyTabView *self);
 
 void          ephy_tab_view_set_tab_bar       (EphyTabView *self,
-                                               HdyTabBar   *tab_bar);
+                                               AdwTabBar   *tab_bar);
 
 G_END_DECLS
diff --git a/src/ephy-web-extension-dialog.c b/src/ephy-web-extension-dialog.c
index d0344c950..0decb10df 100644
--- a/src/ephy-web-extension-dialog.c
+++ b/src/ephy-web-extension-dialog.c
@@ -26,10 +26,11 @@
 #include "ephy-web-extension-dialog.h"
 #include "ephy-web-extension-manager.h"
 
+#include <adwaita.h>
 #include <gtk/gtk.h>
 
 struct _EphyWebExtensionDialog {
-  HdyWindow parent_instance;
+  GtkWindow parent_instance;
 
   EphyWebExtensionManager *web_extension_manager;
 
@@ -37,7 +38,7 @@ struct _EphyWebExtensionDialog {
   GtkStack *stack;
 };
 
-G_DEFINE_TYPE (EphyWebExtensionDialog, ephy_web_extension_dialog, HDY_TYPE_WINDOW)
+G_DEFINE_TYPE (EphyWebExtensionDialog, ephy_web_extension_dialog, GTK_TYPE_WINDOW)
 
 static void
 clear_listbox (GtkWidget *listbox)
@@ -45,7 +46,7 @@ clear_listbox (GtkWidget *listbox)
   GtkListBoxRow *row;
 
   while ((row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (listbox), 0)))
-    gtk_container_remove (GTK_CONTAINER (listbox), GTK_WIDGET (row));
+    gtk_list_box_remove (GTK_LIST_BOX (listbox), GTK_WIDGET (row));
 }
 
 static void
@@ -55,9 +56,9 @@ on_remove_confirmed (GtkDialog       *dialog,
 {
   GtkListBoxRow *row = user_data;
   EphyWebExtensionDialog *self =
-    EPHY_WEB_EXTENSION_DIALOG (gtk_widget_get_toplevel (GTK_WIDGET (row)));
+    EPHY_WEB_EXTENSION_DIALOG (gtk_widget_get_root (GTK_WIDGET (row)));
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   if (response == GTK_RESPONSE_OK) {
     EphyWebExtension *web_extension = g_object_get_data (G_OBJECT (row), "web_extension");
@@ -111,20 +112,15 @@ toggle_state_set_cb (GtkSwitch *widget,
 }
 
 static void
-homepage_activated_cb (HdyActionRow *row,
+homepage_activated_cb (AdwActionRow *row,
                        gpointer      user_data)
 {
   EphyWebExtensionDialog *self = EPHY_WEB_EXTENSION_DIALOG (user_data);
   EphyWebExtension *web_extension = g_object_get_data (G_OBJECT (row), "web_extension");
-  g_autoptr (GError) error = NULL;
 
-  gtk_show_uri_on_window (GTK_WINDOW (self),
-                          ephy_web_extension_get_homepage_url (web_extension),
-                          GDK_CURRENT_TIME,
-                          &error);
-
-  if (error)
-    g_warning ("Couldn't to open homepage: %s", error->message);
+  gtk_show_uri (GTK_WINDOW (self),
+                ephy_web_extension_get_homepage_url (web_extension),
+                GDK_CURRENT_TIME);
 }
 
 static GtkWidget *
@@ -142,7 +138,7 @@ create_row (EphyWebExtensionDialog *self,
   g_autoptr (GdkPixbuf) icon = NULL;
   EphyWebExtensionManager *manager = ephy_shell_get_web_extension_manager (ephy_shell_get_default ());
 
-  row = hdy_expander_row_new ();
+  row = adw_expander_row_new ();
   g_object_set_data (G_OBJECT (row), "web_extension", web_extension);
 
   /* Tooltip */
@@ -150,66 +146,64 @@ create_row (EphyWebExtensionDialog *self,
 
   /* Icon */
   icon = ephy_web_extension_get_icon (web_extension, 32);
-  image = icon ? gtk_image_new_from_pixbuf (icon) : gtk_image_new_from_icon_name 
("application-x-addon-symbolic", GTK_ICON_SIZE_DND);
+  image = icon ? gtk_image_new_from_pixbuf (icon) : gtk_image_new_from_icon_name 
("application-x-addon-symbolic");
   gtk_image_set_pixel_size (GTK_IMAGE (image), 32);
-  hdy_expander_row_add_prefix (HDY_EXPANDER_ROW (row), image);
+  adw_expander_row_add_prefix (ADW_EXPANDER_ROW (row), image);
 
   /* Titles */
-  hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), ephy_web_extension_get_name (web_extension));
-  hdy_expander_row_set_subtitle (HDY_EXPANDER_ROW (row), ephy_web_extension_get_description (web_extension));
-  hdy_expander_row_set_show_enable_switch (HDY_EXPANDER_ROW (row), FALSE);
+  adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), ephy_web_extension_get_name (web_extension));
+  adw_expander_row_set_subtitle (ADW_EXPANDER_ROW (row), ephy_web_extension_get_description (web_extension));
+  adw_expander_row_set_show_enable_switch (ADW_EXPANDER_ROW (row), FALSE);
 
   toggle = gtk_switch_new ();
   gtk_switch_set_active (GTK_SWITCH (toggle), ephy_web_extension_manager_is_active (manager, web_extension));
   g_signal_connect (toggle, "state-set", G_CALLBACK (toggle_state_set_cb), web_extension);
   gtk_widget_set_valign (toggle, GTK_ALIGN_CENTER);
-  hdy_expander_row_add_action (HDY_EXPANDER_ROW (row), toggle);
+  adw_expander_row_add_action (ADW_EXPANDER_ROW (row), toggle);
 
   /* Author */
   if (ephy_web_extension_get_author (web_extension)) {
-    sub_row = hdy_action_row_new ();
-    gtk_container_add (GTK_CONTAINER (row), sub_row);
-    hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (sub_row), _("Author"));
+    sub_row = adw_action_row_new ();
+    adw_expander_row_add_row (ADW_EXPANDER_ROW (row), sub_row);
+    adw_preferences_row_set_title (ADW_PREFERENCES_ROW (sub_row), _("Author"));
     author = gtk_label_new (ephy_web_extension_get_author (web_extension));
-    gtk_label_set_line_wrap (GTK_LABEL (author), TRUE);
-    gtk_container_add (GTK_CONTAINER (sub_row), author);
+    gtk_label_set_wrap (GTK_LABEL (author), TRUE);
+    adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), author);
   }
 
   /* Version */
-  sub_row = hdy_action_row_new ();
-  gtk_container_add (GTK_CONTAINER (row), sub_row);
-  hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (sub_row), _("Version"));
+  sub_row = adw_action_row_new ();
+  adw_expander_row_add_row (ADW_EXPANDER_ROW (row), sub_row);
+  adw_preferences_row_set_title (ADW_PREFERENCES_ROW (sub_row), _("Version"));
   version = gtk_label_new (ephy_web_extension_get_version (web_extension));
-  dzl_gtk_widget_add_style_class (version, "dim-label");
-  gtk_container_add (GTK_CONTAINER (sub_row), version);
+  gtk_widget_add_css_class (version, "dim-label");
+  adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), version);
 
   /* Homepage url */
   if (ephy_web_extension_get_homepage_url (web_extension)) {
-    sub_row = hdy_action_row_new ();
-    gtk_container_add (GTK_CONTAINER (row), sub_row);
-    hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (sub_row), _("Homepage"));
+    sub_row = adw_action_row_new ();
+    adw_expander_row_add_row (ADW_EXPANDER_ROW (row), sub_row);
+    adw_preferences_row_set_title (ADW_PREFERENCES_ROW (sub_row), _("Homepage"));
     gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (sub_row), TRUE);
     g_signal_connect (sub_row, "activated", G_CALLBACK (homepage_activated_cb), self);
-    homepage_icon = gtk_image_new_from_icon_name ("ephy-open-link-symbolic", GTK_ICON_SIZE_BUTTON);
-    dzl_gtk_widget_add_style_class (homepage_icon, "dim-label");
-    gtk_container_add (GTK_CONTAINER (sub_row), homepage_icon);
+    homepage_icon = gtk_image_new_from_icon_name ("ephy-open-link-symbolic");
+    gtk_widget_add_css_class (homepage_icon, "dim-label");
+    adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), homepage_icon);
     g_object_set_data (G_OBJECT (sub_row), "web_extension", web_extension);
   }
 
   /* Remove button */
-  sub_row = hdy_action_row_new ();
-  gtk_container_add (GTK_CONTAINER (row), sub_row);
+  sub_row = adw_action_row_new ();
+  adw_expander_row_add_row (ADW_EXPANDER_ROW (row), sub_row);
 
   button = gtk_button_new_with_mnemonic (_("_Remove"));
   gtk_widget_set_valign (GTK_WIDGET (button), GTK_ALIGN_CENTER);
-  dzl_gtk_widget_add_style_class (button, "destructive-action");
+  gtk_widget_add_css_class (button, "destructive-action");
   g_signal_connect (button, "clicked", G_CALLBACK (on_remove_button_clicked), self);
   gtk_widget_set_tooltip_text (button, _("Remove selected WebExtension"));
-  gtk_container_add (GTK_CONTAINER (sub_row), button);
+  adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), button);
   g_object_set_data (G_OBJECT (button), "row", row);
 
-  gtk_widget_show_all (GTK_WIDGET (row));
-
   return GTK_WIDGET (row);
 }
 
diff --git a/src/ephy-web-extension-dialog.h b/src/ephy-web-extension-dialog.h
index b8418f0f8..8f3f4b3c6 100644
--- a/src/ephy-web-extension-dialog.h
+++ b/src/ephy-web-extension-dialog.h
@@ -28,7 +28,7 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_WEB_EXTENSION_DIALOG (ephy_web_extension_dialog_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyWebExtensionDialog, ephy_web_extension_dialog, EPHY, WEB_EXTENSION_DIALOG, 
HdyWindow)
+G_DECLARE_FINAL_TYPE (EphyWebExtensionDialog, ephy_web_extension_dialog, EPHY, WEB_EXTENSION_DIALOG, 
GtkWindow)
 
 GtkWidget *ephy_web_extension_dialog_new (void);
 
diff --git a/src/ephy-window.c b/src/ephy-window.c
index 4deeecafe..5c804171c 100644
--- a/src/ephy-window.c
+++ b/src/ephy-window.c
@@ -146,18 +146,17 @@ const struct {
 #define SETTINGS_CONNECTION_DATA_KEY    "EphyWindowSettings"
 
 struct _EphyWindow {
-  HdyApplicationWindow parent_instance;
+  AdwApplicationWindow parent_instance;
 
-  GtkWidget *main_deck;
+  GtkWidget *main_leaflet;
   EphyFullscreenBox *fullscreen_box;
-  GtkWidget *window_handle;
   GtkBox *titlebar_box;
   GtkWidget *header_bar;
   EphyPagesView *pages_view;
   EphyBookmarksManager *bookmarks_manager;
   GHashTable *action_labels;
   EphyTabView *tab_view;
-  HdyTabBar *tab_bar;
+  AdwTabBar *tab_bar;
   GtkRevealer *tab_bar_revealer;
   GtkRevealer *pages_menu_revealer;
   EphyPagesPopover *pages_popover;
@@ -193,6 +192,8 @@ struct _EphyWindow {
   guint checking_modified_forms : 1;
   guint confirmed_close_with_multiple_tabs : 1;
   guint present_on_insert : 1;
+
+  GHashTable *action_groups;
 };
 
 enum {
@@ -200,7 +201,6 @@ enum {
   PROP_ACTIVE_CHILD,
   PROP_CHROME,
   PROP_SINGLE_TAB_MODE,
-  PROP_FULLSCREEN
 };
 
 /* Make sure not to overlap with those in ephy-lockdown.c */
@@ -348,7 +348,7 @@ ephy_window_open_link (EphyLink      *link,
                EPHY_LINK_NEW_TAB |
                EPHY_LINK_NEW_WINDOW)) {
     EphyNewTabFlags ntflags = 0;
-    EphyWindow *target_window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed)));
+    EphyWindow *target_window = EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (embed)));
 
     if (flags & EPHY_LINK_JUMP_TO) {
       ntflags |= EPHY_NEW_TAB_JUMP;
@@ -394,7 +394,7 @@ ephy_window_link_iface_init (EphyLinkInterface *iface)
   iface->open_link = ephy_window_open_link;
 }
 
-G_DEFINE_TYPE_WITH_CODE (EphyWindow, ephy_window, HDY_TYPE_APPLICATION_WINDOW,
+G_DEFINE_TYPE_WITH_CODE (EphyWindow, ephy_window, ADW_TYPE_APPLICATION_WINDOW,
                          G_IMPLEMENT_INTERFACE (EPHY_TYPE_LINK,
                                                 ephy_window_link_iface_init)
                          G_IMPLEMENT_INTERFACE (EPHY_TYPE_EMBED_CONTAINER,
@@ -443,7 +443,7 @@ sync_tab_load_status (EphyWebView     *view,
 
   loading = ephy_web_view_is_loading (view);
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "win");
+  action_group = ephy_window_get_action_group (window, "win");
 
   /* disable print while loading, see bug #116344 */
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
@@ -451,7 +451,7 @@ sync_tab_load_status (EphyWebView     *view,
   ephy_action_change_sensitivity_flags (G_SIMPLE_ACTION (action),
                                         SENS_FLAG_LOADING, loading);
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "toolbar");
+  action_group = ephy_window_get_action_group (window, "toolbar");
 
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                        "stop");
@@ -488,21 +488,26 @@ update_adaptive_mode (EphyWindow *window)
   EphyAdaptiveMode adaptive_mode;
   gint width, height;
   GdkDisplay *display;
-  GdkWindow *surface;
+  GdkSurface *surface;
   GdkMonitor *monitor = NULL;
   GdkRectangle geometry = {};
 
-  gtk_window_get_size (GTK_WINDOW (window),
-                       &width,
-                       &height);
-
   /* Get the monitor to guess whether we are on a mobile or not. If not found,
    * fallback to the window size.
    */
   display = gtk_widget_get_display (GTK_WIDGET (window));
-  surface = gtk_widget_get_window (GTK_WIDGET (window));
+  surface = gtk_native_get_surface (GTK_NATIVE (window));
+
+  if (window->is_maximized || window->is_fullscreen) {
+    width = gtk_widget_get_width (GTK_WIDGET (window));
+    height = gtk_widget_get_height (GTK_WIDGET (window));
+  } else {
+    width = window->current_width;
+    height = window->current_height;
+  }
+
   if (display != NULL && surface != NULL)
-    monitor = gdk_display_get_monitor_at_window (display, surface);
+    monitor = gdk_display_get_monitor_at_surface (display, surface);
   if (monitor != NULL)
     gdk_monitor_get_geometry (monitor, &geometry);
   else
@@ -534,54 +539,52 @@ update_adaptive_mode (EphyWindow *window)
 }
 
 static void
-ephy_window_fullscreen (EphyWindow *window)
+notify_fullscreen_cb (EphyWindow *window)
 {
   EphyEmbed *embed;
+  gboolean fullscreen = gtk_window_is_fullscreen (GTK_WINDOW (window));
+  GAction *action;
+  GActionGroup *action_group;
 
-  window->is_fullscreen = TRUE;
-  g_object_notify (G_OBJECT (window), "fullscreen");
+  window->is_fullscreen = fullscreen;
 
-  /* sync status */
   embed = window->active_embed;
-  sync_tab_load_status (ephy_embed_get_web_view (embed), WEBKIT_LOAD_STARTED, window);
-  sync_tab_security (ephy_embed_get_web_view (embed), NULL, window);
+
+  if (embed && fullscreen) {
+    /* sync status */
+    sync_tab_load_status (ephy_embed_get_web_view (embed), WEBKIT_LOAD_STARTED, window);
+    sync_tab_security (ephy_embed_get_web_view (embed), NULL, window);
+  }
 
   update_adaptive_mode (window);
-  ephy_embed_entering_fullscreen (embed);
-}
 
-static void
-ephy_window_unfullscreen (EphyWindow *window)
-{
-  window->is_fullscreen = FALSE;
-  g_object_notify (G_OBJECT (window), "fullscreen");
+  if (embed) {
+    if (fullscreen)
+      ephy_embed_entering_fullscreen (embed);
+    else
+      ephy_embed_leaving_fullscreen (embed);
+  }
 
-  update_adaptive_mode (window);
-  ephy_embed_leaving_fullscreen (window->active_embed);
+  ephy_fullscreen_box_set_fullscreen (window->fullscreen_box,
+                                      fullscreen && window->show_fullscreen_header_bar);
+  gtk_widget_set_visible (GTK_WIDGET (window->titlebar_box),
+                          !fullscreen || window->show_fullscreen_header_bar);
+
+  window->show_fullscreen_header_bar = FALSE;
+
+  action_group = ephy_window_get_action_group (window, "win");
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "fullscreen");
+
+  g_simple_action_set_state (G_SIMPLE_ACTION (action),
+                             g_variant_new_boolean (fullscreen));
 }
 
 static gboolean
-ephy_window_should_view_receive_key_press_event (EphyWindow  *window,
-                                                 GdkEventKey *event)
+ephy_window_should_view_receive_key_press_event (EphyWindow      *window,
+                                                 guint            keyval,
+                                                 GdkModifierType  state)
 {
-  GdkDisplay *display;
-  GdkKeymap *keymap;
-  guint keyval;
-  GdkModifierType consumed;
-  GdkModifierType state_mask = GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK;
-
-  display = gtk_widget_get_display (GTK_WIDGET (window));
-  keymap = gdk_keymap_get_for_display (display);
-
-  gdk_keymap_translate_keyboard_state (keymap,
-                                       event->hardware_keycode,
-                                       event->state,
-                                       event->group,
-                                       &keyval,
-                                       NULL,
-                                       NULL,
-                                       &consumed);
-  state_mask &= ~consumed;
+  state &= (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_ALT_MASK);
 
   /* Focus location entry */
   if (keyval == GDK_KEY_F6)
@@ -589,7 +592,7 @@ ephy_window_should_view_receive_key_press_event (EphyWindow  *window,
 
   /* Websites are allowed to override most Epiphany accelerators, but not
    * window or tab management accelerators. */
-  if ((event->state & state_mask) == GDK_CONTROL_MASK)
+  if (state == GDK_CONTROL_MASK)
     return keyval != GDK_KEY_n &&            /* New Window */
            keyval != GDK_KEY_q &&            /* Quit */
            keyval != GDK_KEY_T &&            /* Reopen Closed Tab */
@@ -603,14 +606,14 @@ ephy_window_should_view_receive_key_press_event (EphyWindow  *window,
            keyval != GDK_KEY_KP_Tab &&       /* Next Tab */
            keyval != GDK_KEY_ISO_Left_Tab;   /* Previous Tab (Shift+Tab -> ISO Left Tab) */
 
-  if ((event->state & state_mask) == (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
+  if (state == (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
     return keyval != GDK_KEY_n &&            /* New Incognito Window */
            keyval != GDK_KEY_Page_Up &&      /* Move Tab Left */
            keyval != GDK_KEY_KP_Page_Up &&   /* Move Tab Left */
            keyval != GDK_KEY_Page_Down &&    /* Move Tab Right */
            keyval != GDK_KEY_KP_Page_Down;   /* Move Tab Right */
 
-  if ((event->state & state_mask) == GDK_MOD1_MASK)
+  if (state == GDK_ALT_MASK)
     return keyval != GDK_KEY_Left &&      /* Back */
            keyval != GDK_KEY_Right &&     /* Forward */
            keyval != GDK_KEY_Home &&      /* Homepage */
@@ -629,14 +632,17 @@ ephy_window_should_view_receive_key_press_event (EphyWindow  *window,
 }
 
 static gboolean
-ephy_window_key_press_event (GtkWidget   *widget,
-                             GdkEventKey *event)
+key_pressed_cb (EphyWindow            *window,
+                guint                  keyval,
+                guint                  keycode,
+                GdkModifierType        state,
+                GtkEventControllerKey *controller)
 {
   EphyWebView *view;
 
-  view = ephy_embed_get_web_view (EPHY_WINDOW (widget)->active_embed);
-  if (gtk_window_get_focus (GTK_WINDOW (widget)) != GTK_WIDGET (view))
-    return GTK_WIDGET_CLASS (ephy_window_parent_class)->key_press_event (widget, event);
+  view = ephy_embed_get_web_view (window->active_embed);
+  if (gtk_window_get_focus (GTK_WINDOW (window)) != GTK_WIDGET (view))
+    return GDK_EVENT_PROPAGATE;
 
   /* GtkWindow's key press handler first calls gtk_window_activate_key,
    * then gtk_window_propagate_key_event. We want to do the opposite,
@@ -648,9 +654,8 @@ ephy_window_key_press_event (GtkWidget   *widget,
    * short-circuit the event propagation if it's a special keybinding
    * that is reserved for Epiphany not allowed to be seen by webpages.
    */
-  if (!ephy_window_should_view_receive_key_press_event (EPHY_WINDOW (widget), event) ||
-      !gtk_window_propagate_key_event (GTK_WINDOW (widget), event)) {
-    gtk_window_activate_key (GTK_WINDOW (widget), event);
+  if (ephy_window_should_view_receive_key_press_event (window, keyval, state)) {
+    gtk_event_controller_key_forward (controller, GTK_WIDGET (view));
 
     return GDK_EVENT_STOP;
   }
@@ -659,22 +664,16 @@ ephy_window_key_press_event (GtkWidget   *widget,
 }
 
 static gboolean
-ephy_window_delete_event (GtkWidget   *widget,
-                          GdkEventAny *event)
+ephy_window_close_request (GtkWindow *window)
 {
   if ((ephy_embed_shell_get_mode (ephy_embed_shell_get_default ()) == EPHY_EMBED_SHELL_MODE_APPLICATION) && 
g_settings_get_boolean (EPHY_SETTINGS_WEB_APP, EPHY_PREFS_WEB_APP_RUN_IN_BACKGROUND)) {
-    gtk_widget_hide (widget);
+    gtk_widget_hide (GTK_WIDGET (window));
     return TRUE;
   }
 
-  if (!ephy_window_close (EPHY_WINDOW (widget)))
+  if (!ephy_window_close (EPHY_WINDOW (window)))
     return TRUE;
 
-  /* proceed with window close */
-  if (GTK_WIDGET_CLASS (ephy_window_parent_class)->delete_event) {
-    return GTK_WIDGET_CLASS (ephy_window_parent_class)->delete_event (widget, event);
-  }
-
   return FALSE;
 }
 
@@ -687,7 +686,7 @@ update_link_actions_sensitivity (EphyWindow *window,
   GAction *action;
   GActionGroup *action_group;
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "popup");
+  action_group = ephy_window_get_action_group (window, "popup");
 
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                        "open-link-in-new-window");
@@ -711,8 +710,7 @@ update_edit_action_sensitivity (EphyWindow *window,
   GActionGroup *action_group;
   GAction *action;
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                              "win");
+  action_group = ephy_window_get_action_group (window, "win");
 
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                        action_name);
@@ -773,8 +771,7 @@ enable_edit_actions_sensitivity (EphyWindow *window)
   GActionGroup *action_group;
   GAction *action;
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                              "win");
+  action_group = ephy_window_get_action_group (window, "win");
 
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "cut");
   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
@@ -1014,7 +1011,7 @@ _ephy_window_set_default_actions_sensitive (EphyWindow *window,
     NULL
   };
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "win");
+  action_group = ephy_window_get_action_group (window, "win");
 
   /* Page menu */
   for (i = 0; action_group_actions[i] != NULL; i++) {
@@ -1025,14 +1022,14 @@ _ephy_window_set_default_actions_sensitive (EphyWindow *window,
   }
 
   /* Page context popup */
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "popup");
+  action_group = ephy_window_get_action_group (window, "popup");
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                        "context-bookmark-page");
   ephy_action_change_sensitivity_flags (G_SIMPLE_ACTION (action),
                                         flags, set);
 
   /* Toolbar */
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "toolbar");
+  action_group = ephy_window_get_action_group (window, "toolbar");
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                        "combined-stop-reload");
   ephy_action_change_sensitivity_flags (G_SIMPLE_ACTION (action),
@@ -1098,8 +1095,7 @@ sync_tab_zoom (WebKitWebView *web_view,
     can_zoom_normal = TRUE;
   }
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                              "win");
+  action_group = ephy_window_get_action_group (window, "win");
 
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "zoom-in");
   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_zoom_in);
@@ -1109,6 +1105,21 @@ sync_tab_zoom (WebKitWebView *web_view,
   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_zoom_normal);
 }
 
+static void
+sync_extensions (EphyWindow *window)
+{
+  gboolean enable_extensions;
+  GActionGroup *action_group;
+  GAction *action;
+
+  enable_extensions = g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_ENABLE_WEBEXTENSIONS);
+
+  action_group = ephy_window_get_action_group (window, "win");
+
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "extensions");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enable_extensions);
+}
+
 static void
 sync_tab_document_type (EphyWebView *view,
                         GParamSpec  *pspec,
@@ -1130,8 +1141,7 @@ sync_tab_document_type (EphyWebView *view,
   is_image = type == EPHY_WEB_VIEW_DOCUMENT_IMAGE;
   disable = (type != EPHY_WEB_VIEW_DOCUMENT_HTML);
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                              "win");
+  action_group = ephy_window_get_action_group (window, "win");
 
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "encoding");
   ephy_action_change_sensitivity_flags (G_SIMPLE_ACTION (action), SENS_FLAG_DOCUMENT, disable);
@@ -1156,7 +1166,7 @@ _ephy_window_set_navigation_flags (EphyWindow                 *window,
   GActionGroup *action_group;
   GAction *action;
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "toolbar");
+  action_group = ephy_window_get_action_group (window, "toolbar");
 
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "navigation-back");
   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), flags & EPHY_WEB_VIEW_NAV_BACK);
@@ -1517,12 +1527,9 @@ populate_context_menu (WebKitWebView       *web_view,
                               EPHY_PREFS_LOCKDOWN_CONTEXT_MENU))
     return GDK_EVENT_STOP;
 
-  window_action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                                     "win");
-  toolbar_action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                                      "toolbar");
-  popup_action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                                    "popup");
+  window_action_group = ephy_window_get_action_group (window, "win");
+  toolbar_action_group = ephy_window_get_action_group (window, "toolbar");
+  popup_action_group = ephy_window_get_action_group (window, "popup");
 
   if (webkit_hit_test_result_context_is_image (hit_test_result)) {
     is_image = TRUE;
@@ -1890,7 +1897,7 @@ window_properties_geometry_changed (WebKitWindowProperties *properties,
   webkit_window_properties_get_geometry (properties, &geometry);
 
   if (geometry.width > 0 && geometry.height > 0)
-    gtk_window_resize (GTK_WINDOW (window), geometry.width, geometry.height);
+    gtk_window_set_default_size (GTK_WINDOW (window), geometry.width, geometry.height);
 }
 
 static void
@@ -1912,7 +1919,7 @@ ephy_window_configure_for_view (EphyWindow    *window,
 
     title_widget = GTK_WIDGET (ephy_header_bar_get_title_widget (EPHY_HEADER_BAR (window->header_bar)));
     lentry = EPHY_LOCATION_ENTRY (title_widget);
-    gtk_editable_set_editable (GTK_EDITABLE (ephy_location_entry_get_entry (lentry)), FALSE);
+    gtk_editable_set_editable (GTK_EDITABLE (lentry), FALSE);
 
     if (webkit_window_properties_get_menubar_visible (properties))
       chrome |= EPHY_WINDOW_CHROME_MENU;
@@ -1941,8 +1948,8 @@ web_view_ready_cb (WebKitWebView *web_view,
   EphyWindow *window, *parent_view_window;
   gboolean using_new_window;
 
-  window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (web_view)));
-  parent_view_window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (parent_web_view)));
+  window = EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (web_view)));
+  parent_view_window = EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (parent_web_view)));
 
   using_new_window = window != parent_view_window;
 
@@ -2092,12 +2099,7 @@ decide_navigation_policy (WebKitWebView            *web_view,
   uri = webkit_uri_request_get_uri (request);
 
   if (!ephy_embed_utils_address_has_web_scheme (uri) && webkit_navigation_action_is_user_gesture 
(navigation_action)) {
-    g_autoptr (GError) error = NULL;
-    gtk_show_uri_on_window (GTK_WINDOW (window), uri, GDK_CURRENT_TIME, &error);
-    if (error) {
-      LOG ("failed to handle non-web scheme: %s", error->message);
-      return accept_navigation_policy_decision (window, decision, uri);
-    }
+    gtk_show_uri (GTK_WINDOW (window), uri, GDK_CURRENT_TIME);
 
     webkit_policy_decision_ignore (decision);
     return TRUE;
@@ -2117,10 +2119,11 @@ decide_navigation_policy (WebKitWebView            *web_view,
       if (ephy_web_application_is_uri_allowed (uri)) {
         gtk_widget_show (GTK_WIDGET (window));
       } else {
-        ephy_file_open_uri_in_default_browser (uri, gtk_window_get_screen (GTK_WINDOW (window)));
+        ephy_file_open_uri_in_default_browser (uri,
+                                               gdk_surface_get_display (gtk_native_get_surface (GTK_NATIVE 
(window))));
         webkit_policy_decision_ignore (decision);
 
-        gtk_widget_destroy (GTK_WIDGET (window));
+        gtk_window_destroy (GTK_WINDOW (window));
 
         return TRUE;
       }
@@ -2131,7 +2134,8 @@ decide_navigation_policy (WebKitWebView            *web_view,
       if (ephy_web_application_is_uri_allowed (uri))
         return accept_navigation_policy_decision (window, decision, uri);
 
-      ephy_file_open_uri_in_default_browser (uri, gtk_window_get_screen (GTK_WINDOW (window)));
+      ephy_file_open_uri_in_default_browser (uri,
+                                             gdk_surface_get_display (gtk_native_get_surface (GTK_NATIVE 
(window))));
       webkit_policy_decision_ignore (decision);
 
       return TRUE;
@@ -2171,7 +2175,7 @@ decide_navigation_policy (WebKitWebView            *web_view,
       inherit_session = TRUE;
     }
     /* Alt+click means download URI */
-    else if (button == GDK_BUTTON_PRIMARY && state == GDK_MOD1_MASK) {
+    else if (button == GDK_BUTTON_PRIMARY && state == GDK_ALT_MASK) {
       if (save_target_uri (window, web_view)) {
         webkit_policy_decision_ignore (decision);
         return TRUE;
@@ -2439,9 +2443,9 @@ ephy_window_connect_active_embed (EphyWindow *window)
   g_signal_connect_object (view, "notify::is-blank",
                            G_CALLBACK (sync_tab_is_blank),
                            window, 0);
-  g_signal_connect_object (view, "context-menu",
-                           G_CALLBACK (populate_context_menu),
-                           window, 0);
+//  g_signal_connect_object (view, "context-menu", FIXME
+//                           G_CALLBACK (populate_context_menu),
+//                           window, 0);
   g_signal_connect_object (view, "mouse-target-changed",
                            G_CALLBACK (ephy_window_mouse_target_changed_cb),
                            window, 0);
@@ -2522,7 +2526,7 @@ ephy_window_set_active_tab (EphyWindow *window,
   EphyEmbed *old_embed;
 
   g_assert (EPHY_IS_WINDOW (window));
-  g_assert (gtk_widget_get_toplevel (GTK_WIDGET (new_embed)) == GTK_WIDGET (window));
+  g_assert (gtk_widget_get_root (GTK_WIDGET (new_embed)) == GTK_ROOT (window));
 
   old_embed = window->active_embed;
 
@@ -2539,8 +2543,8 @@ ephy_window_set_active_tab (EphyWindow *window,
 }
 
 static void
-tab_view_setup_menu_cb (HdyTabView *tab_view,
-                        HdyTabPage *page,
+tab_view_setup_menu_cb (AdwTabView *tab_view,
+                        AdwTabPage *page,
                         EphyWindow *window)
 {
   EphyWebView *view;
@@ -2554,17 +2558,17 @@ tab_view_setup_menu_cb (HdyTabView *tab_view,
   gboolean muted;
 
   if (page) {
-    n_pages = hdy_tab_view_get_n_pages (tab_view);
-    n_pinned_pages = hdy_tab_view_get_n_pinned_pages (tab_view);
-    position = hdy_tab_view_get_page_position (tab_view, page);
-    pinned = hdy_tab_page_get_pinned (page);
+    n_pages = adw_tab_view_get_n_pages (tab_view);
+    n_pinned_pages = adw_tab_view_get_n_pinned_pages (tab_view);
+    position = adw_tab_view_get_page_position (tab_view, page);
+    pinned = adw_tab_page_get_pinned (page);
 
-    view = ephy_embed_get_web_view (EPHY_EMBED (hdy_tab_page_get_child (page)));
+    view = ephy_embed_get_web_view (EPHY_EMBED (adw_tab_page_get_child (page)));
     audio_playing = webkit_web_view_is_playing_audio (WEBKIT_WEB_VIEW (view));
     muted = webkit_web_view_get_is_muted (WEBKIT_WEB_VIEW (view));
   }
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "tab");
+  action_group = ephy_window_get_action_group (window, "tab");
 
   /* enable/disable close others/left/right */
   /* If there's no page, enable all actions so that we don't interfere with hotkeys */
@@ -2615,7 +2619,7 @@ static gboolean
 delayed_remove_child (gpointer data)
 {
   GtkWidget *widget = GTK_WIDGET (data);
-  EphyEmbedContainer *container = EPHY_EMBED_CONTAINER (gtk_widget_get_toplevel (widget));
+  EphyEmbedContainer *container = EPHY_EMBED_CONTAINER (gtk_widget_get_root (widget));
 
   ephy_embed_container_remove_child (container, EPHY_EMBED (widget));
 
@@ -2669,12 +2673,12 @@ reader_mode_cb (EphyWebView *view,
 }
 
 static void
-tab_view_page_attached_cb (HdyTabView *tab_view,
-                           HdyTabPage *page,
+tab_view_page_attached_cb (AdwTabView *tab_view,
+                           AdwTabPage *page,
                            gint        position,
                            EphyWindow *window)
 {
-  GtkWidget *content = hdy_tab_page_get_child (page);
+  GtkWidget *content = adw_tab_page_get_child (page);
   EphyEmbed *embed;
 
   g_assert (EPHY_IS_EMBED (content));
@@ -2696,12 +2700,12 @@ tab_view_page_attached_cb (HdyTabView *tab_view,
 }
 
 static void
-tab_view_page_detached_cb (HdyTabView *tab_view,
-                           HdyTabPage *page,
+tab_view_page_detached_cb (AdwTabView *tab_view,
+                           AdwTabPage *page,
                            gint        position,
                            EphyWindow *window)
 {
-  GtkWidget *content = hdy_tab_page_get_child (page);
+  GtkWidget *content = adw_tab_page_get_child (page);
 
   LOG ("page-detached tab view %p embed %p position %d\n", tab_view, content, position);
 
@@ -2764,19 +2768,19 @@ ephy_window_close_tab (EphyWindow *window,
    * tab, even if it wasn't at the start of this function.
    */
   if (!window->closing && ephy_tab_view_get_n_pages (window->tab_view) == 0)
-    gtk_widget_destroy (GTK_WIDGET (window));
+    gtk_window_destroy (GTK_WINDOW (window));
 }
 
 typedef struct {
   EphyWindow *window;
   EphyEmbed *embed;
-  HdyTabPage *page;
+  AdwTabPage *page;
 } TabHasModifiedFormsData;
 
 static TabHasModifiedFormsData *
 tab_has_modified_forms_data_new (EphyWindow *window,
                                  EphyEmbed  *embed,
-                                 HdyTabPage *page)
+                                 AdwTabPage *page)
 {
   TabHasModifiedFormsData *data = g_new (TabHasModifiedFormsData, 1);
   data->window = window;
@@ -2801,9 +2805,9 @@ tab_has_modified_forms_dialog_cb (GtkDialog               *dialog,
                                   GtkResponseType          response,
                                   TabHasModifiedFormsData *data)
 {
-  HdyTabView *tab_view = ephy_tab_view_get_tab_view (data->window->tab_view);
+  AdwTabView *tab_view = ephy_tab_view_get_tab_view (data->window->tab_view);
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   if (response == GTK_RESPONSE_ACCEPT) {
     /* It's safe to close the tab immediately because we are only checking a
@@ -2811,10 +2815,10 @@ tab_has_modified_forms_dialog_cb (GtkDialog               *dialog,
      * codepath for checking modified forms when closing the whole window,
      * see ephy_window_check_modified_forms().
      */
-    hdy_tab_view_close_page_finish (tab_view, data->page, TRUE);
+    adw_tab_view_close_page_finish (tab_view, data->page, TRUE);
     ephy_window_close_tab (data->window, data->embed);
   } else
-    hdy_tab_view_close_page_finish (tab_view, data->page, FALSE);
+    adw_tab_view_close_page_finish (tab_view, data->page, FALSE);
 
   tab_has_modified_forms_data_free (data);
 }
@@ -2831,10 +2835,10 @@ tab_has_modified_forms_cb (EphyWebView             *view,
   if (data->window != NULL &&
       data->embed != NULL &&
       data->page != NULL) {
-    HdyTabView *tab_view = ephy_tab_view_get_tab_view (data->window->tab_view);
+    AdwTabView *tab_view = ephy_tab_view_get_tab_view (data->window->tab_view);
 
     if (!has_modified_forms) {
-      hdy_tab_view_close_page_finish (tab_view, data->page, TRUE);
+      adw_tab_view_close_page_finish (tab_view, data->page, TRUE);
       ephy_window_close_tab (data->window, data->embed);
     } else {
       GtkWidget *dialog;
@@ -2878,19 +2882,19 @@ run_downloads_in_background (EphyWindow *window,
 }
 
 static gboolean
-tab_view_close_page_cb (HdyTabView *tab_view,
-                        HdyTabPage *page,
+tab_view_close_page_cb (AdwTabView *tab_view,
+                        AdwTabPage *page,
                         EphyWindow *window)
 {
-  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyEmbed *embed = EPHY_EMBED (adw_tab_page_get_child (page));
 
-  if (hdy_tab_page_get_pinned (page))
+  if (adw_tab_page_get_pinned (page))
     return GDK_EVENT_PROPAGATE;
 
   if (ephy_tab_view_get_n_pages (window->tab_view) == 1) {
     if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
                                 EPHY_PREFS_LOCKDOWN_QUIT)) {
-      hdy_tab_view_close_page_finish (tab_view, page, FALSE);
+      adw_tab_view_close_page_finish (tab_view, page, FALSE);
       return GDK_EVENT_STOP;
     }
 
@@ -2906,7 +2910,7 @@ tab_view_close_page_cb (HdyTabView *tab_view,
       if (ephy_downloads_manager_has_active_downloads (manager)) {
         GList *list = ephy_downloads_manager_get_downloads (manager);
         run_downloads_in_background (window, g_list_length (list));
-        hdy_tab_view_close_page_finish (tab_view, page, FALSE);
+        adw_tab_view_close_page_finish (tab_view, page, FALSE);
         return GDK_EVENT_STOP;
       }
     }
@@ -2935,8 +2939,8 @@ tab_view_close_page_cb (HdyTabView *tab_view,
   return GDK_EVENT_PROPAGATE;
 }
 
-static HdyTabView *
-tab_view_create_window_cb (HdyTabView *tab_view,
+static AdwTabView *
+tab_view_create_window_cb (AdwTabView *tab_view,
                            EphyWindow *window)
 {
   EphyWindow *new_window;
@@ -2953,7 +2957,6 @@ ephy_window_update_entry_focus (EphyWindow  *window,
                                 EphyWebView *view)
 {
   GtkWidget *title_widget;
-  GtkWidget *entry;
   const char *address = NULL;
 
   address = ephy_web_view_get_address (view);
@@ -2963,10 +2966,9 @@ ephy_window_update_entry_focus (EphyWindow  *window,
     return;
 
   title_widget = GTK_WIDGET (ephy_header_bar_get_title_widget (EPHY_HEADER_BAR (window->header_bar)));
-  if (EPHY_IS_LOCATION_ENTRY (title_widget)) {
-    entry = ephy_location_entry_get_entry (EPHY_LOCATION_ENTRY (title_widget));
-    gtk_entry_grab_focus_without_selecting (GTK_ENTRY (entry));
-  }
+
+  if (EPHY_IS_LOCATION_ENTRY (title_widget))
+    ephy_location_entry_grab_focus_without_selecting (EPHY_LOCATION_ENTRY (title_widget));
 }
 
 static void
@@ -3007,15 +3009,14 @@ static EphyTabView *
 setup_tab_view (EphyWindow *window)
 {
   EphyTabView *tab_view = ephy_tab_view_new ();
-  HdyTabView *view = ephy_tab_view_get_tab_view (tab_view);
+  AdwTabView *view = ephy_tab_view_get_tab_view (tab_view);
   g_autoptr (GtkBuilder) builder = NULL;
 
   gtk_widget_set_vexpand (GTK_WIDGET (tab_view), TRUE);
 
   builder = gtk_builder_new_from_resource ("/org/gnome/epiphany/gtk/notebook-context-menu.ui");
 
-  hdy_tab_view_set_menu_model (view, G_MENU_MODEL (gtk_builder_get_object (builder, "notebook-menu")));
-  hdy_tab_view_set_shortcut_widget (view, GTK_WIDGET (window));
+  adw_tab_view_set_menu_model (view, G_MENU_MODEL (gtk_builder_get_object (builder, "notebook-menu")));
 
   g_signal_connect_object (view, "notify::selected-page",
                            G_CALLBACK (tab_view_notify_selected_page_cb),
@@ -3122,57 +3123,12 @@ ephy_window_get_property (GObject    *object,
     case PROP_SINGLE_TAB_MODE:
       g_value_set_boolean (value, window->is_popup);
       break;
-    case PROP_FULLSCREEN:
-      g_value_set_boolean (value, window->is_fullscreen);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
 }
 
-static gboolean
-ephy_window_state_event (GtkWidget           *widget,
-                         GdkEventWindowState *event)
-{
-  EphyWindow *window = EPHY_WINDOW (widget);
-  gboolean result = GDK_EVENT_PROPAGATE;
-
-  if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
-    GActionGroup *action_group;
-    GAction *action;
-    gboolean fullscreen;
-
-    fullscreen = !!(event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
-
-    if (fullscreen) {
-      ephy_window_fullscreen (window);
-    } else {
-      ephy_window_unfullscreen (window);
-    }
-
-    ephy_fullscreen_box_set_fullscreen (window->fullscreen_box, fullscreen && 
window->show_fullscreen_header_bar);
-    gtk_widget_set_visible (GTK_WIDGET (window->titlebar_box), !fullscreen || 
window->show_fullscreen_header_bar);
-
-    window->show_fullscreen_header_bar = FALSE;
-
-    action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "win");
-    action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "fullscreen");
-
-    g_simple_action_set_state (G_SIMPLE_ACTION (action),
-                               g_variant_new_boolean (fullscreen));
-  } else if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
-    window->is_maximized = !!(event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED);
-  }
-
-  update_adaptive_mode (window);
-
-  if (GTK_WIDGET_CLASS (ephy_window_parent_class)->window_state_event)
-    result = GTK_WIDGET_CLASS (ephy_window_parent_class)->window_state_event (widget, event);
-
-  return result;
-}
-
 void
 ephy_window_set_default_size (EphyWindow *window,
                               gint        width,
@@ -3203,9 +3159,9 @@ ephy_window_show (GtkWidget *widget)
                       &window->current_height);
 
       if (window->current_width > 0 && window->current_height > 0) {
-        gtk_window_resize (GTK_WINDOW (window),
-                           window->current_width,
-                           window->current_height);
+        gtk_window_set_default_size (GTK_WINDOW (window),
+                                     window->current_width,
+                                     window->current_height);
       }
 
       window->has_default_size = TRUE;
@@ -3230,7 +3186,7 @@ ephy_window_should_save_state (EphyWindow *window)
 }
 
 static void
-ephy_window_destroy (GtkWidget *widget)
+ephy_window_hide (GtkWidget *widget)
 {
   EphyWindow *window = EPHY_WINDOW (widget);
 
@@ -3242,14 +3198,70 @@ ephy_window_destroy (GtkWidget *widget)
     g_settings_set_boolean (EPHY_SETTINGS_STATE, "is-maximized", window->is_maximized);
   }
 
-  GTK_WIDGET_CLASS (ephy_window_parent_class)->destroy (widget);
+  GTK_WIDGET_CLASS (ephy_window_parent_class)->hide (widget);
+}
+
+static void
+compute_size_cb (EphyWindow      *window,
+                 GdkToplevelSize *size)
+{
+  GdkSurface *surface = gtk_native_get_surface (GTK_NATIVE (window));
+  GdkToplevelState state = gdk_toplevel_get_state (GDK_TOPLEVEL (surface));
+
+  window->is_maximized = gtk_window_is_maximized (GTK_WINDOW (window));
+
+  if (state & (GDK_TOPLEVEL_STATE_FULLSCREEN |
+               GDK_TOPLEVEL_STATE_MAXIMIZED |
+               GDK_TOPLEVEL_STATE_TILED |
+               GDK_TOPLEVEL_STATE_TOP_TILED |
+               GDK_TOPLEVEL_STATE_RIGHT_TILED |
+               GDK_TOPLEVEL_STATE_BOTTOM_TILED |
+               GDK_TOPLEVEL_STATE_LEFT_TILED |
+               GDK_TOPLEVEL_STATE_MINIMIZED)) {
+    window->current_width = gdk_surface_get_width (surface);
+    window->current_height = gdk_surface_get_height (surface);
+  } else {
+    gtk_window_get_default_size (GTK_WINDOW (window),
+                                 &window->current_width,
+                                 &window->current_height);
+  }
+
+  update_adaptive_mode (window);
+}
+
+static void
+ephy_window_realize (GtkWidget *widget)
+{
+  EphyWindow *window = EPHY_WINDOW (widget);
+  GdkSurface *surface;
+
+  GTK_WIDGET_CLASS (ephy_window_parent_class)->realize (widget);
+
+  surface = gtk_native_get_surface (GTK_NATIVE (window));
+
+  g_signal_connect_swapped (surface, "compute-size", G_CALLBACK (compute_size_cb), window);
+}
+
+static void
+ephy_window_unrealize (GtkWidget *widget)
+{
+  EphyWindow *window = EPHY_WINDOW (widget);
+  GdkSurface *surface = gtk_native_get_surface (GTK_NATIVE (window));
+
+  g_signal_handlers_disconnect_by_func (surface, G_CALLBACK (compute_size_cb), window);
+
+  GTK_WIDGET_CLASS (ephy_window_parent_class)->unrealize (widget);
 }
 
 static void
 ephy_window_finalize (GObject *object)
 {
+  EphyWindow *window = EPHY_WINDOW (object);
+
   G_OBJECT_CLASS (ephy_window_parent_class)->finalize (object);
 
+  g_hash_table_unref (window->action_groups);
+
   LOG ("EphyWindow finalised %p", object);
 }
 
@@ -3277,17 +3289,16 @@ sync_user_input_cb (EphyLocationController *action,
 }
 
 static void
-security_popover_notify_visible_cb (GtkWidget  *widget,
-                                    GParamSpec *param,
-                                    gpointer    user_data)
+security_popover_closed_cb (GtkPopover    *popover,
+                            GtkMenuButton *button)
 {
-  if (!gtk_widget_get_visible (widget))
-    gtk_widget_destroy (widget);
+  gtk_menu_button_popdown (button);
+  gtk_menu_button_set_popover (button, NULL);
 }
 
 static void
 title_widget_lock_clicked_cb (EphyTitleWidget *title_widget,
-                              GdkRectangle    *lock_position,
+                              GtkMenuButton   *menu_button,
                               gpointer         user_data)
 {
   EphyWindow *window = EPHY_WINDOW (user_data);
@@ -3301,17 +3312,15 @@ title_widget_lock_clicked_cb (EphyTitleWidget *title_widget,
   view = ephy_embed_get_web_view (window->active_embed);
   ephy_web_view_get_security_level (view, &security_level, &address, &certificate, &tls_errors);
 
-  security_popover = ephy_security_popover_new (GTK_WIDGET (title_widget),
-                                                address,
+  security_popover = ephy_security_popover_new (address,
                                                 certificate,
                                                 tls_errors,
                                                 security_level);
 
-  g_signal_connect (security_popover, "notify::visible",
-                    G_CALLBACK (security_popover_notify_visible_cb), NULL);
-  gtk_popover_set_pointing_to (GTK_POPOVER (security_popover), lock_position);
-  gtk_popover_set_position (GTK_POPOVER (security_popover), GTK_POS_BOTTOM);
-  gtk_popover_popup (GTK_POPOVER (security_popover));
+  g_signal_connect (security_popover, "closed",
+                    G_CALLBACK (security_popover_closed_cb), menu_button);
+  gtk_menu_button_set_popover (menu_button, security_popover);
+  gtk_menu_button_popup (menu_button);
 }
 
 static GtkWidget *
@@ -3320,16 +3329,8 @@ setup_header_bar (EphyWindow *window)
   GtkWidget *header_bar;
   EphyTitleWidget *title_widget;
 
-  window->window_handle = hdy_window_handle_new ();
   header_bar = ephy_header_bar_new (window);
 
-  gtk_container_add (GTK_CONTAINER (window->window_handle), header_bar);
-
-  gtk_widget_show (window->window_handle);
-  gtk_widget_show (header_bar);
-
-  gtk_style_context_add_class (gtk_widget_get_style_context (header_bar), "titlebar");
-
   title_widget = ephy_header_bar_get_title_widget (EPHY_HEADER_BAR (header_bar));
   g_signal_connect (title_widget, "lock-clicked",
                     G_CALLBACK (title_widget_lock_clicked_cb), window);
@@ -3341,7 +3342,7 @@ static void
 update_pages_menu_revealer (EphyWindow *window)
 {
   gtk_revealer_set_reveal_child (window->pages_menu_revealer,
-                                 hdy_tab_bar_get_is_overflowing (window->tab_bar) ||
+                                 adw_tab_bar_get_is_overflowing (window->tab_bar) ||
                                  gtk_widget_get_visible (GTK_WIDGET (window->pages_popover)));
 }
 
@@ -3355,17 +3356,17 @@ setup_tabs_menu (EphyWindow *window)
   revealer = GTK_REVEALER (gtk_revealer_new ());
   gtk_revealer_set_transition_type (revealer,
                                     GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT);
-  hdy_tab_bar_set_end_action_widget (window->tab_bar, GTK_WIDGET (revealer));
+  adw_tab_bar_set_end_action_widget (window->tab_bar, GTK_WIDGET (revealer));
   window->pages_menu_revealer = revealer;
 
   menu_button = gtk_menu_button_new ();
-  gtk_button_set_relief (GTK_BUTTON (menu_button), GTK_RELIEF_NONE);
+  gtk_widget_add_css_class (menu_button, "flat");
   /* Translators: tooltip for the tab switcher menu button */
   gtk_widget_set_tooltip_text (menu_button, _("View open tabs"));
   gtk_widget_set_margin_start (menu_button, 1);
-  gtk_container_add (GTK_CONTAINER (revealer), menu_button);
+  gtk_revealer_set_child (revealer, menu_button);
 
-  popover = ephy_pages_popover_new (menu_button);
+  popover = ephy_pages_popover_new ();
   ephy_pages_popover_set_tab_view (popover, window->tab_view);
   gtk_menu_button_set_popover (GTK_MENU_BUTTON (menu_button),
                                GTK_WIDGET (popover));
@@ -3377,8 +3378,6 @@ setup_tabs_menu (EphyWindow *window)
   g_signal_connect_object (window->pages_popover, "notify::visible",
                            G_CALLBACK (update_pages_menu_revealer), window,
                            G_CONNECT_SWAPPED);
-
-  gtk_widget_show_all (GTK_WIDGET (revealer));
 }
 
 static EphyLocationController *
@@ -3406,7 +3405,6 @@ setup_action_bar (EphyWindow *window)
   GtkWidget *action_bar;
 
   action_bar = GTK_WIDGET (ephy_action_bar_new (window));
-  gtk_widget_show (action_bar);
 
   g_object_bind_property (window->fullscreen_box, "revealed",
                           action_bar, "can-reveal",
@@ -3475,12 +3473,18 @@ on_default_browser_question_response (GtkInfoBar *info_bar,
                                       gint        response_id,
                                       gpointer    user_data)
 {
+  GtkWidget *parent;
+
   if (response_id == GTK_RESPONSE_YES)
     set_as_default_browser ();
   else if (response_id == GTK_RESPONSE_NO)
     g_settings_set_boolean (EPHY_SETTINGS_MAIN, EPHY_PREFS_ASK_FOR_DEFAULT, FALSE);
 
-  gtk_widget_destroy (GTK_WIDGET (info_bar));
+  parent = gtk_widget_get_parent (GTK_WIDGET (info_bar));
+
+  g_assert (GTK_IS_BOX (parent));
+
+  gtk_box_remove (GTK_BOX (parent), GTK_WIDGET (info_bar));
 }
 
 static void
@@ -3488,31 +3492,27 @@ add_default_browser_question (GtkBox *box)
 {
   GtkWidget *label;
   GtkWidget *info_bar;
-  GtkWidget *content_area;
 
 #if !TECH_PREVIEW
   label = gtk_label_new (_("Set Web as your default browser?"));
 #else
   label = gtk_label_new (_("Set Epiphany Technology Preview as your default browser?"));
 #endif
-  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_label_set_wrap (GTK_LABEL (label), TRUE);
   gtk_widget_show (label);
 
   info_bar = gtk_info_bar_new ();
   gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_QUESTION);
   gtk_info_bar_set_show_close_button (GTK_INFO_BAR (info_bar), TRUE);
 
-  content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
-  gtk_container_add (GTK_CONTAINER (content_area), label);
+  gtk_info_bar_add_child (GTK_INFO_BAR (info_bar), label);
 
   gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), _("_Yes"), GTK_RESPONSE_YES);
   gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), _("_No"), GTK_RESPONSE_NO);
 
   g_signal_connect (info_bar, "response", G_CALLBACK (on_default_browser_question_response), NULL);
 
-  gtk_box_pack_start (box, info_bar, FALSE, TRUE, 0);
-
-  gtk_widget_show (info_bar);
+  gtk_box_append (box, info_bar);
 }
 
 static gboolean
@@ -3549,14 +3549,14 @@ download_completed_cb (EphyDownload *download,
 }
 
 static void
-notify_deck_child_cb (EphyWindow *window)
+notify_leaflet_child_cb (EphyWindow *window)
 {
   GActionGroup *action_group;
   GAction *action;
   gboolean pages_open;
 
-  pages_open = hdy_deck_get_visible_child (HDY_DECK (window->main_deck)) == GTK_WIDGET (window->pages_view);
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "win");
+  pages_open = adw_leaflet_get_visible_child (ADW_LEAFLET (window->main_leaflet)) == GTK_WIDGET 
(window->pages_view);
+  action_group = ephy_window_get_action_group (window, "win");
 
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "content");
   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), pages_open);
@@ -3566,17 +3566,11 @@ notify_deck_child_cb (EphyWindow *window)
 }
 
 static void
-ephy_window_size_allocate (GtkWidget     *widget,
-                           GtkAllocation *allocation)
+insert_action_group (const char   *prefix,
+                     GActionGroup *group,
+                     GtkWidget    *widget)
 {
-  EphyWindow *window = EPHY_WINDOW (widget);
-
-  GTK_WIDGET_CLASS (ephy_window_parent_class)->size_allocate (widget, allocation);
-
-  if (!(window->is_maximized || window->is_fullscreen))
-    gtk_window_get_size (GTK_WINDOW (widget), &window->current_width, &window->current_height);
-
-  update_adaptive_mode (window);
+  gtk_widget_insert_action_group (widget, prefix, group);
 }
 
 static void
@@ -3592,46 +3586,48 @@ ephy_window_constructed (GObject *object)
   EphyEmbedShellMode mode;
   EphyWindowChrome chrome = EPHY_WINDOW_CHROME_DEFAULT;
   GApplication *app;
+  GtkEventController *controller;
 
   G_OBJECT_CLASS (ephy_window_parent_class)->constructed (object);
 
   window = EPHY_WINDOW (object);
 
+  window->action_groups = g_hash_table_new_full (g_str_hash,
+                                                 g_str_equal,
+                                                 g_free,
+                                                 NULL);
+
   /* Add actions */
   simple_action_group = g_simple_action_group_new ();
   g_action_map_add_action_entries (G_ACTION_MAP (simple_action_group),
                                    window_entries,
                                    G_N_ELEMENTS (window_entries),
                                    window);
-  gtk_widget_insert_action_group (GTK_WIDGET (window),
-                                  "win",
-                                  G_ACTION_GROUP (simple_action_group));
+  g_hash_table_insert (window->action_groups, g_strdup ("win"), simple_action_group);
 
   simple_action_group = g_simple_action_group_new ();
   g_action_map_add_action_entries (G_ACTION_MAP (simple_action_group),
                                    tab_entries,
                                    G_N_ELEMENTS (tab_entries),
                                    window);
-  gtk_widget_insert_action_group (GTK_WIDGET (window), "tab",
-                                  G_ACTION_GROUP (simple_action_group));
+  g_hash_table_insert (window->action_groups, g_strdup ("tab"), simple_action_group);
 
   simple_action_group = g_simple_action_group_new ();
   g_action_map_add_action_entries (G_ACTION_MAP (simple_action_group),
                                    toolbar_entries,
                                    G_N_ELEMENTS (toolbar_entries),
                                    window);
-  gtk_widget_insert_action_group (GTK_WIDGET (window),
-                                  "toolbar",
-                                  G_ACTION_GROUP (simple_action_group));
+  g_hash_table_insert (window->action_groups, g_strdup ("toolbar"), simple_action_group);
 
   simple_action_group = g_simple_action_group_new ();
   g_action_map_add_action_entries (G_ACTION_MAP (simple_action_group),
                                    popup_entries,
                                    G_N_ELEMENTS (popup_entries),
                                    window);
-  gtk_widget_insert_action_group (GTK_WIDGET (window),
-                                  "popup",
-                                  G_ACTION_GROUP (simple_action_group));
+  g_hash_table_insert (window->action_groups, g_strdup ("popup"), simple_action_group);
+
+  g_hash_table_foreach (window->action_groups,
+                        (GHFunc)insert_action_group, window);
 
   window->action_labels = g_hash_table_new_full (g_str_hash,
                                                  g_str_equal,
@@ -3660,20 +3656,24 @@ ephy_window_constructed (GObject *object)
                                            accels_navigation_ltr_rtl[i].accelerators);
   }
 
+  g_signal_connect (window, "notify::fullscreened",
+                    G_CALLBACK (notify_fullscreen_cb), NULL);
+
   ephy_gui_ensure_window_group (GTK_WINDOW (window));
 
   window->tab_view = setup_tab_view (window);
-  window->tab_bar = hdy_tab_bar_new ();
+  window->tab_bar = adw_tab_bar_new ();
   window->tab_bar_revealer = GTK_REVEALER (gtk_revealer_new ());
-  window->main_deck = hdy_deck_new ();
+  window->main_leaflet = adw_leaflet_new ();
   window->fullscreen_box = ephy_fullscreen_box_new ();
   window->pages_view = ephy_pages_view_new ();
 
-  g_signal_connect_swapped (window->main_deck, "notify::visible-child",
-                            G_CALLBACK (notify_deck_child_cb), window);
+  adw_leaflet_set_can_unfold (ADW_LEAFLET (window->main_leaflet), FALSE);
+  g_signal_connect_swapped (window->main_leaflet, "notify::visible-child",
+                            G_CALLBACK (notify_leaflet_child_cb), window);
 
   gtk_revealer_set_transition_type (window->tab_bar_revealer, GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
-  hdy_tab_bar_set_view (window->tab_bar, ephy_tab_view_get_tab_view (window->tab_view));
+  adw_tab_bar_set_view (window->tab_bar, ephy_tab_view_get_tab_view (window->tab_view));
   ephy_pages_view_set_tab_view (window->pages_view, window->tab_view);
 
   setup_tabs_menu (window);
@@ -3699,33 +3699,25 @@ ephy_window_constructed (GObject *object)
       !ephy_profile_dir_is_web_application ())
     add_default_browser_question (box);
 
-  gtk_container_add (GTK_CONTAINER (window->tab_bar_revealer), GTK_WIDGET (window->tab_bar));
-  gtk_box_pack_start (window->titlebar_box, GTK_WIDGET (window->window_handle), FALSE, TRUE, 0);
-  gtk_box_pack_start (window->titlebar_box, GTK_WIDGET (window->tab_bar_revealer), FALSE, TRUE, 0);
-  gtk_box_pack_start (box, GTK_WIDGET (window->tab_view), FALSE, TRUE, 0);
-  gtk_box_pack_start (box, GTK_WIDGET (window->action_bar), FALSE, TRUE, 0);
+  gtk_revealer_set_child (window->tab_bar_revealer, GTK_WIDGET (window->tab_bar));
+  gtk_box_append (window->titlebar_box, GTK_WIDGET (window->header_bar));
+  gtk_box_append (window->titlebar_box, GTK_WIDGET (window->tab_bar_revealer));
+  gtk_box_append (box, GTK_WIDGET (window->tab_view));
+  gtk_box_append (box, GTK_WIDGET (window->action_bar));
   ephy_fullscreen_box_set_content (window->fullscreen_box, GTK_WIDGET (box));
   ephy_fullscreen_box_set_titlebar (window->fullscreen_box, GTK_WIDGET (window->titlebar_box));
 
-  gtk_container_add (GTK_CONTAINER (window->main_deck), GTK_WIDGET (window->fullscreen_box));
-  gtk_container_add (GTK_CONTAINER (window->main_deck), GTK_WIDGET (window->pages_view));
-  gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (window->main_deck));
-  gtk_widget_show (GTK_WIDGET (window->main_deck));
-  gtk_widget_show (GTK_WIDGET (window->pages_view));
-  gtk_widget_show (GTK_WIDGET (window->fullscreen_box));
-  gtk_widget_show (GTK_WIDGET (window->titlebar_box));
-  gtk_widget_show (GTK_WIDGET (box));
-  gtk_widget_show (GTK_WIDGET (window->tab_view));
-  gtk_widget_show (GTK_WIDGET (window->tab_bar));
-  gtk_widget_show (GTK_WIDGET (window->tab_bar_revealer));
+  adw_leaflet_append (ADW_LEAFLET (window->main_leaflet), GTK_WIDGET (window->fullscreen_box));
+  adw_leaflet_append (ADW_LEAFLET (window->main_leaflet), GTK_WIDGET (window->pages_view));
+  adw_application_window_set_content (ADW_APPLICATION_WINDOW (window), GTK_WIDGET (window->main_leaflet));
 
   ephy_tab_view_set_tab_bar (window->tab_view, window->tab_bar);
 
-  hdy_deck_set_visible_child (HDY_DECK (window->main_deck), GTK_WIDGET (window->fullscreen_box));
-  hdy_deck_set_can_swipe_back (HDY_DECK (window->main_deck), TRUE);
+  adw_leaflet_set_visible_child (ADW_LEAFLET (window->main_leaflet), GTK_WIDGET (window->fullscreen_box));
+  adw_leaflet_set_can_navigate_back (ADW_LEAFLET (window->main_leaflet), TRUE);
 
   /* other notifiers */
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "win");
+  action_group = ephy_window_get_action_group (window, "win");
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                        "browse-with-caret");
 
@@ -3737,8 +3729,7 @@ ephy_window_constructed (GObject *object)
                                 NULL,
                                 action, NULL);
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window),
-                                              "win");
+  action_group = ephy_window_get_action_group (window, "win");
 
   /* Disable actions not needed for popup mode. */
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "new-tab");
@@ -3746,7 +3737,7 @@ ephy_window_constructed (GObject *object)
                                         SENS_FLAG_CHROME,
                                         window->is_popup);
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "popup");
+  action_group = ephy_window_get_action_group (window, "popup");
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                        "open-link-in-new-tab");
   ephy_action_change_sensitivity_flags (G_SIMPLE_ACTION (action),
@@ -3757,11 +3748,11 @@ ephy_window_constructed (GObject *object)
   if (mode == EPHY_EMBED_SHELL_MODE_APPLICATION) {
     g_object_set (window->location_controller, "editable", FALSE, NULL);
 
-    action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "popup");
+    action_group = ephy_window_get_action_group (window, "popup");
     action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "context-bookmark-page");
     ephy_action_change_sensitivity_flags (G_SIMPLE_ACTION (action), SENS_FLAG_CHROME, TRUE);
 
-    action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "win");
+    action_group = ephy_window_get_action_group (window, "win");
     for (i = 0; i < G_N_ELEMENTS (disabled_actions_for_app_mode); i++) {
       action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                            disabled_actions_for_app_mode[i]);
@@ -3770,12 +3761,12 @@ ephy_window_constructed (GObject *object)
     }
     chrome &= ~(EPHY_WINDOW_CHROME_LOCATION | EPHY_WINDOW_CHROME_TABSBAR | EPHY_WINDOW_CHROME_BOOKMARKS);
   } else if (mode == EPHY_EMBED_SHELL_MODE_INCOGNITO) {
-    action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "win");
+    action_group = ephy_window_get_action_group (window, "win");
     action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "bookmark-page");
     ephy_action_change_sensitivity_flags (G_SIMPLE_ACTION (action),
                                           SENS_FLAG_CHROME, TRUE);
 
-    action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "popup");
+    action_group = ephy_window_get_action_group (window, "popup");
     action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "context-bookmark-page");
     ephy_action_change_sensitivity_flags (G_SIMPLE_ACTION (action),
                                           SENS_FLAG_CHROME, TRUE);
@@ -3788,6 +3779,18 @@ ephy_window_constructed (GObject *object)
   ephy_window_set_chrome (window, chrome);
 
   ephy_web_extension_manager_install_actions (ephy_shell_get_web_extension_manager (ephy_shell_get_default 
()), window);
+
+  sync_extensions (window);
+  g_signal_connect_object (EPHY_SETTINGS_WEB,
+                           "changed::" EPHY_PREFS_WEB_ENABLE_WEBEXTENSIONS,
+                           G_CALLBACK (sync_extensions),
+                           window,
+                           G_CONNECT_SWAPPED);
+
+  controller = gtk_event_controller_key_new ();
+  gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
+  g_signal_connect_swapped (controller, "key-pressed", G_CALLBACK (key_pressed_cb), window);
+  gtk_widget_add_controller (GTK_WIDGET (window), controller);
 }
 
 static void
@@ -3795,6 +3798,7 @@ ephy_window_class_init (EphyWindowClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkWindowClass *window_class = GTK_WINDOW_CLASS (klass);
   EphyDownloadsManager *manager;
 
   object_class->constructed = ephy_window_constructed;
@@ -3803,12 +3807,12 @@ ephy_window_class_init (EphyWindowClass *klass)
   object_class->get_property = ephy_window_get_property;
   object_class->set_property = ephy_window_set_property;
 
-  widget_class->key_press_event = ephy_window_key_press_event;
-  widget_class->window_state_event = ephy_window_state_event;
   widget_class->show = ephy_window_show;
-  widget_class->destroy = ephy_window_destroy;
-  widget_class->delete_event = ephy_window_delete_event;
-  widget_class->size_allocate = ephy_window_size_allocate;
+  widget_class->hide = ephy_window_hide;
+  widget_class->realize = ephy_window_realize;
+  widget_class->unrealize = ephy_window_unrealize;
+
+  window_class->close_request = ephy_window_close_request;
 
   g_object_class_override_property (object_class,
                                     PROP_ACTIVE_CHILD,
@@ -3828,15 +3832,6 @@ ephy_window_class_init (EphyWindowClass *klass)
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_STATIC_STRINGS));
 
-  g_object_class_install_property (object_class,
-                                   PROP_FULLSCREEN,
-                                   g_param_spec_boolean ("fullscreen",
-                                                         NULL,
-                                                         NULL,
-                                                         FALSE,
-                                                         G_PARAM_READABLE |
-                                                         G_PARAM_STATIC_STRINGS));
-
   manager = ephy_embed_shell_get_downloads_manager (EPHY_EMBED_SHELL (ephy_shell_get_default ()));
   g_signal_connect (manager, "download-completed", G_CALLBACK (download_completed_cb), NULL);
 }
@@ -3893,7 +3888,7 @@ ephy_window_open_pages_view (EphyWindow *window)
 {
   g_assert (EPHY_IS_WINDOW (window));
 
-  hdy_deck_navigate (HDY_DECK (window->main_deck), HDY_NAVIGATION_DIRECTION_FORWARD);
+  adw_leaflet_navigate (ADW_LEAFLET (window->main_leaflet), ADW_NAVIGATION_DIRECTION_FORWARD);
 }
 
 /**
@@ -3907,7 +3902,7 @@ ephy_window_close_pages_view (EphyWindow *window)
 {
   g_assert (EPHY_IS_WINDOW (window));
 
-  hdy_deck_navigate (HDY_DECK (window->main_deck), HDY_NAVIGATION_DIRECTION_BACK);
+  adw_leaflet_navigate (ADW_LEAFLET (window->main_leaflet), ADW_NAVIGATION_DIRECTION_BACK);
 }
 
 /**
@@ -3977,7 +3972,6 @@ ephy_window_location_search (EphyWindow *window)
 {
   EphyTitleWidget *title_widget = ephy_header_bar_get_title_widget (EPHY_HEADER_BAR (window->header_bar));
   EphyLocationEntry *location_entry = EPHY_LOCATION_ENTRY (title_widget);
-  GtkEntry *location_gtk_entry = GTK_ENTRY (ephy_location_entry_get_entry (location_entry));
   GtkApplication *gtk_application = gtk_window_get_application (GTK_WINDOW (window));
   EphyEmbedShell *embed_shell = EPHY_EMBED_SHELL (gtk_application);
   EphySearchEngineManager *search_engine_manager = ephy_embed_shell_get_search_engine_manager (embed_shell);
@@ -3985,9 +3979,9 @@ ephy_window_location_search (EphyWindow *window)
   const char *bang = ephy_search_engine_get_bang (default_engine);
   char *entry_text = g_strconcat (bang, " ", NULL);
 
-  gtk_window_set_focus (GTK_WINDOW (window), GTK_WIDGET (location_gtk_entry));
-  gtk_entry_set_text (location_gtk_entry, entry_text);
-  gtk_editable_set_position (GTK_EDITABLE (location_gtk_entry), strlen (entry_text));
+  gtk_window_set_focus (GTK_WINDOW (window), GTK_WIDGET (location_entry));
+  gtk_editable_set_text (GTK_EDITABLE (location_entry), entry_text);
+  gtk_editable_set_position (GTK_EDITABLE (location_entry), strlen (entry_text));
 
   g_free (entry_text);
 }
@@ -4135,7 +4129,7 @@ finish_window_close_after_modified_forms_check (WindowHasModifiedFormsData *data
   should_close = ephy_window_close (data->window);
   data->window->force_close = FALSE;
   if (should_close)
-    gtk_widget_destroy (GTK_WIDGET (data->window));
+    gtk_window_destroy (GTK_WINDOW (data->window));
 
   window_has_modified_forms_data_free (data);
 }
@@ -4145,7 +4139,7 @@ confirm_close_window_with_modified_forms_cb (GtkDialog                  *dialog,
                                              GtkResponseType             response,
                                              WindowHasModifiedFormsData *data)
 {
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   if (response == GTK_RESPONSE_ACCEPT)
     finish_window_close_after_modified_forms_check (data);
@@ -4250,7 +4244,7 @@ window_close_with_multiple_tabs_cb (GtkDialog       *dialog,
                                     GtkResponseType  response,
                                     EphyWindow      *window)
 {
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   if (response == GTK_RESPONSE_ACCEPT) {
     window->confirmed_close_with_multiple_tabs = TRUE;
@@ -4373,3 +4367,10 @@ ephy_window_get_geometry (EphyWindow   *window,
   rectangle->width = window->current_width;
   rectangle->height = window->current_height;
 }
+
+GActionGroup *
+ephy_window_get_action_group (EphyWindow *window,
+                              const char *prefix)
+{
+  return g_hash_table_lookup (window->action_groups, prefix);
+}
diff --git a/src/ephy-window.h b/src/ephy-window.h
index c34b0d474..7249934be 100644
--- a/src/ephy-window.h
+++ b/src/ephy-window.h
@@ -27,13 +27,13 @@
 #include "ephy-tab-view.h"
 #include "ephy-web-view.h"
 
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_WINDOW (ephy_window_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyWindow, ephy_window, EPHY, WINDOW, HdyApplicationWindow)
+G_DECLARE_FINAL_TYPE (EphyWindow, ephy_window, EPHY, WINDOW, AdwApplicationWindow)
 
 typedef enum
 {
@@ -92,6 +92,9 @@ gboolean          ephy_window_is_fullscreen              (EphyWindow *window);
 void              ephy_window_get_geometry               (EphyWindow   *window,
                                                           GdkRectangle *rectangle);
 
+GActionGroup     *ephy_window_get_action_group           (EphyWindow  *window,
+                                                          const char  *prefix);
+
 void              ephy_window_sync_bookmark_state        (EphyWindow            *window,
                                                           EphyBookmarkIconState  state);
 
diff --git a/src/meson.build b/src/meson.build
index 54964d150..71e6a2798 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -73,7 +73,7 @@ libephymain_deps = [
   ephywidgets_dep,
   gvdb_dep,
   libarchive_dep,
-  libhandy_dep,
+  libadwaita_dep,
   portal_dep
 ]
 
diff --git a/src/popup-commands.c b/src/popup-commands.c
index 516f9c5c0..6769c2d7d 100644
--- a/src/popup-commands.c
+++ b/src/popup-commands.c
@@ -107,8 +107,9 @@ static void
 popup_cmd_copy_to_clipboard (EphyWindow *window,
                              const char *text)
 {
-  gtk_clipboard_set_text (gtk_clipboard_get_default (gdk_display_get_default ()),
-                          text, -1);
+  GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window));
+
+  gdk_clipboard_set_text (clipboard, text);
 }
 
 void
@@ -173,7 +174,7 @@ filename_confirmed_cb (GtkFileChooser      *dialog,
     ephy_downloads_manager_add_download (ephy_embed_shell_get_downloads_manager 
(ephy_embed_shell_get_default ()),
                                          data->download);
 
-    current_folder = gtk_file_chooser_get_current_folder_file (dialog);
+    current_folder = gtk_file_chooser_get_current_folder (dialog);
     current_folder_path = g_file_get_path (current_folder);
     g_settings_set_string (EPHY_SETTINGS_WEB,
                            EPHY_PREFS_WEB_LAST_DOWNLOAD_DIRECTORY,
@@ -204,7 +205,6 @@ filename_suggested_cb (EphyDownload        *download,
                                      GTK_WIDGET (data->window),
                                      GTK_FILE_CHOOSER_ACTION_SAVE,
                                      EPHY_FILE_FILTER_NONE);
-  gtk_file_chooser_set_do_overwrite_confirmation (dialog, TRUE);
 
   last_directory_path = g_settings_get_string (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_LAST_DOWNLOAD_DIRECTORY);
 
@@ -213,7 +213,7 @@ filename_suggested_cb (EphyDownload        *download,
     g_autoptr (GError) error = NULL;
 
     last_directory = g_file_new_for_path (last_directory_path);
-    gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog), last_directory, &error);
+    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), last_directory, &error);
 
     if (error)
       g_warning ("Failed to set current folder %s: %s", last_directory_path, error->message);
diff --git a/src/preferences/ephy-data-view.c b/src/preferences/ephy-data-view.c
index 0e863c869..a869f37f1 100644
--- a/src/preferences/ephy-data-view.c
+++ b/src/preferences/ephy-data-view.c
@@ -21,13 +21,13 @@
 #include "config.h"
 #include "ephy-data-view.h"
 
+#include <adwaita.h>
 #include <ctype.h>
-#include <handy.h>
 
 typedef struct {
   GtkWidget *box;
   GtkWidget *child;
-  GtkWidget *header_bar;
+  GtkWidget *window_title;
   GtkWidget *back_button;
   GtkWidget *clear_button;
   GtkWidget *search_bar;
@@ -42,11 +42,12 @@ typedef struct {
   gboolean has_search_results : 1;
   gboolean can_clear : 1;
   char *search_text;
+  char *search_description;
 } EphyDataViewPrivate;
 
 static void ephy_data_view_buildable_init (GtkBuildableIface *iface);
 
-G_DEFINE_TYPE_WITH_CODE (EphyDataView, ephy_data_view, GTK_TYPE_BIN,
+G_DEFINE_TYPE_WITH_CODE (EphyDataView, ephy_data_view, GTK_TYPE_WIDGET,
                          G_ADD_PRIVATE (EphyDataView)
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                 ephy_data_view_buildable_init))
@@ -129,55 +130,13 @@ on_search_entry_changed (GtkSearchEntry *entry,
   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
   const char *text;
 
-  text = gtk_entry_get_text (GTK_ENTRY (entry));
+  text = gtk_editable_get_text (GTK_EDITABLE (entry));
   g_free (priv->search_text);
   priv->search_text = g_strdup (text);
 
   g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_SEARCH_TEXT]);
 }
 
-gboolean
-ephy_data_view_handle_event (EphyDataView *self,
-                             GdkEvent     *event)
-{
-  EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
-  GdkEventKey *key = (GdkEventKey *)event;
-  gint result;
-
-  /* Firstly, we check if the HdySearchBar can handle the event */
-  result = hdy_search_bar_handle_event (HDY_SEARCH_BAR (priv->search_bar), event);
-
-  if (result == GDK_EVENT_STOP)
-    return result;
-
-  /* Secondly, we check for the shortcuts implemented by the data views */
-  /* Ctrl + F */
-  if ((key->state & GDK_CONTROL_MASK) && key->keyval == GDK_KEY_f) {
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->search_button), TRUE);
-    return GDK_EVENT_STOP;
-  }
-
-  /* Shift + Delete */
-  if ((key->state & GDK_SHIFT_MASK) && key->keyval == GDK_KEY_Delete) {
-    gtk_button_clicked (GTK_BUTTON (priv->clear_button));
-    return GDK_EVENT_STOP;
-  }
-
-  /* Finally, we check for the Escape key */
-  if (key->keyval == GDK_KEY_Escape) {
-    if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->search_button)))
-      /* If the user presses the Escape key and the search bar is hidden,
-       * we want the event to have the same effect as clicking the back button*/
-      gtk_button_clicked (GTK_BUTTON (priv->back_button));
-    else
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->search_button), FALSE);
-
-    return GDK_EVENT_STOP;
-  }
-
-  return GDK_EVENT_PROPAGATE;
-}
-
 static void
 ephy_data_view_set_property (GObject      *object,
                              guint         prop_id,
@@ -189,7 +148,7 @@ ephy_data_view_set_property (GObject      *object,
 
   switch (prop_id) {
     case PROP_TITLE:
-      gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header_bar), g_value_get_string (value));
+      adw_window_title_set_title (ADW_WINDOW_TITLE (priv->window_title), g_value_get_string (value));
       break;
     case PROP_CLEAR_ACTION_NAME:
       gtk_actionable_set_action_name (GTK_ACTIONABLE (priv->clear_button), g_value_get_string (value));
@@ -204,14 +163,18 @@ ephy_data_view_set_property (GObject      *object,
       ephy_data_view_set_clear_button_tooltip (self, g_value_get_string (value));
       break;
     case PROP_SEARCH_DESCRIPTION:
-      gtk_entry_set_placeholder_text (GTK_ENTRY (priv->search_entry), g_value_get_string (value));
-      atk_object_set_description (gtk_widget_get_accessible (GTK_WIDGET (self)), g_value_get_string (value));
+      g_clear_pointer (&priv->search_description, g_free);
+      priv->search_description = g_value_dup_string (value);
+      gtk_accessible_update_property (GTK_ACCESSIBLE (self),
+                                      GTK_ACCESSIBLE_PROPERTY_DESCRIPTION,
+                                      priv->search_description,
+                                      -1);
       break;
     case PROP_EMPTY_TITLE:
-      hdy_status_page_set_title (HDY_STATUS_PAGE (priv->empty_page), g_value_get_string (value));
+      adw_status_page_set_title (ADW_STATUS_PAGE (priv->empty_page), g_value_get_string (value));
       break;
     case PROP_EMPTY_DESCRIPTION:
-      hdy_status_page_set_description (HDY_STATUS_PAGE (priv->empty_page), g_value_get_string (value));
+      adw_status_page_set_description (ADW_STATUS_PAGE (priv->empty_page), g_value_get_string (value));
       break;
     case PROP_IS_LOADING:
       ephy_data_view_set_is_loading (self, g_value_get_boolean (value));
@@ -242,7 +205,7 @@ ephy_data_view_get_property (GObject    *object,
 
   switch (prop_id) {
     case PROP_TITLE:
-      g_value_set_string (value, gtk_header_bar_get_title (GTK_HEADER_BAR (priv->header_bar)));
+      g_value_set_string (value, adw_window_title_get_title (ADW_WINDOW_TITLE (priv->window_title)));
       break;
     case PROP_CLEAR_ACTION_NAME:
       g_value_set_string (value, gtk_actionable_get_action_name (GTK_ACTIONABLE (priv->clear_button)));
@@ -257,13 +220,13 @@ ephy_data_view_get_property (GObject    *object,
       g_value_set_string (value, ephy_data_view_get_clear_button_tooltip (self));
       break;
     case PROP_SEARCH_DESCRIPTION:
-      g_value_set_string (value, gtk_entry_get_placeholder_text (GTK_ENTRY (priv->search_entry)));
+      g_value_set_string (value, priv->search_description);
       break;
     case PROP_EMPTY_TITLE:
-      g_value_set_string (value, hdy_status_page_get_title (HDY_STATUS_PAGE (priv->empty_page)));
+      g_value_set_string (value, adw_status_page_get_title (ADW_STATUS_PAGE (priv->empty_page)));
       break;
     case PROP_EMPTY_DESCRIPTION:
-      g_value_set_string (value, hdy_status_page_get_description (HDY_STATUS_PAGE (priv->empty_page)));
+      g_value_set_string (value, adw_status_page_get_description (ADW_STATUS_PAGE (priv->empty_page)));
       break;
     case PROP_SEARCH_TEXT:
       g_value_set_string (value, ephy_data_view_get_search_text (self));
@@ -286,6 +249,17 @@ ephy_data_view_get_property (GObject    *object,
   }
 }
 
+static void
+ephy_data_view_dispose (GObject *object)
+{
+  EphyDataView *self = EPHY_DATA_VIEW (object);
+  EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
+
+  g_clear_pointer (&priv->box, gtk_widget_unparent);
+
+  G_OBJECT_CLASS (ephy_data_view_parent_class)->dispose (object);
+}
+
 static void
 ephy_data_view_finalize (GObject *object)
 {
@@ -293,10 +267,24 @@ ephy_data_view_finalize (GObject *object)
   EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
 
   g_free (priv->search_text);
+  g_free (priv->search_description);
 
   G_OBJECT_CLASS (ephy_data_view_parent_class)->finalize (object);
 }
 
+static gboolean
+find_shortuct_cb (EphyDataView *self)
+{
+  EphyDataViewPrivate *priv = ephy_data_view_get_instance_private (self);
+  gboolean search;
+
+  search = gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (priv->search_bar));
+
+  gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->search_bar), !search);
+
+  return GDK_EVENT_STOP;
+}
+
 static void
 ephy_data_view_class_init (EphyDataViewClass *klass)
 {
@@ -305,6 +293,7 @@ ephy_data_view_class_init (EphyDataViewClass *klass)
 
   object_class->set_property = ephy_data_view_set_property;
   object_class->get_property = ephy_data_view_get_property;
+  object_class->dispose = ephy_data_view_dispose;
   object_class->finalize = ephy_data_view_finalize;
 
   obj_properties[PROP_TITLE] =
@@ -427,7 +416,7 @@ ephy_data_view_class_init (EphyDataViewClass *klass)
                                                "/org/gnome/epiphany/gtk/data-view.ui");
 
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, box);
-  gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, header_bar);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, window_title);
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, back_button);
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, clear_button);
   gtk_widget_class_bind_template_child_private (widget_class, EphyDataView, empty_page);
@@ -440,6 +429,13 @@ ephy_data_view_class_init (EphyDataViewClass *klass)
   gtk_widget_class_bind_template_callback (widget_class, on_back_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_clear_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, on_search_entry_changed);
+
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+
+  gtk_widget_class_add_binding (widget_class, GDK_KEY_F, GDK_CONTROL_MASK,
+                                (GtkShortcutFunc)find_shortuct_cb, NULL);
+  gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Delete, GDK_SHIFT_MASK,
+                                       "clear-button-clicked", NULL);
 }
 
 static void
@@ -449,9 +445,10 @@ ephy_data_view_init (EphyDataView *self)
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  hdy_search_bar_connect_entry (HDY_SEARCH_BAR (priv->search_bar), GTK_ENTRY (priv->search_entry));
+  gtk_search_bar_connect_entry (GTK_SEARCH_BAR (priv->search_bar),
+                                GTK_EDITABLE (priv->search_entry));
 
-  hdy_status_page_set_icon_name (HDY_STATUS_PAGE (priv->empty_page),
+  adw_status_page_set_icon_name (ADW_STATUS_PAGE (priv->empty_page),
                                  APPLICATION_ID "-symbolic");
 
   update (self);
@@ -474,7 +471,7 @@ ephy_data_view_add_child (GtkBuildable *buildable,
   g_assert (!priv->child);
 
   priv->child = GTK_WIDGET (child);
-  gtk_container_add (GTK_CONTAINER (priv->stack), priv->child);
+  gtk_stack_add_child (GTK_STACK (priv->stack), priv->child);
 
   update (self);
 }
diff --git a/src/preferences/ephy-data-view.h b/src/preferences/ephy-data-view.h
index a7c71185d..942009e03 100644
--- a/src/preferences/ephy-data-view.h
+++ b/src/preferences/ephy-data-view.h
@@ -26,11 +26,11 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_DATA_VIEW (ephy_data_view_get_type ())
 
-G_DECLARE_DERIVABLE_TYPE (EphyDataView, ephy_data_view, EPHY, DATA_VIEW, GtkBin)
+G_DECLARE_DERIVABLE_TYPE (EphyDataView, ephy_data_view, EPHY, DATA_VIEW, GtkWidget)
 
 struct _EphyDataViewClass
 {
-  GtkBinClass parent_class;
+  GtkWidgetClass parent_class;
 };
 
 const gchar *ephy_data_view_get_clear_button_label (EphyDataView *self);
@@ -59,7 +59,4 @@ void     ephy_data_view_set_can_clear (EphyDataView *self,
 
 const gchar *ephy_data_view_get_search_text (EphyDataView *self);
 
-gboolean ephy_data_view_handle_event (EphyDataView *self,
-                                      GdkEvent     *event);
-
 G_END_DECLS
diff --git a/src/preferences/ephy-lang-row.c b/src/preferences/ephy-lang-row.c
index d2899b3fc..d7f5d9446 100644
--- a/src/preferences/ephy-lang-row.c
+++ b/src/preferences/ephy-lang-row.c
@@ -22,24 +22,22 @@
 
 enum {
   DELETE_BUTTON_CLICKED,
+  MOVE_ROW,
 
   LAST_SIGNAL
 };
 
 struct _EphyLangRow {
-  GtkListBoxRow parent_instance;
+  AdwActionRow parent_instance;
 
-  GtkWidget *dnd_top_revealer;
-  GtkWidget *action_row;
-  GtkWidget *drag_event_box;
+  GtkWidget *drag_handle;
   GtkWidget *delete_button;
-  GtkWidget *dnd_bottom_revealer;
   char *code;
 };
 
 static guint signals[LAST_SIGNAL];
 
-G_DEFINE_TYPE (EphyLangRow, ephy_lang_row, GTK_TYPE_LIST_BOX_ROW)
+G_DEFINE_TYPE (EphyLangRow, ephy_lang_row, ADW_TYPE_ACTION_ROW)
 
 static void
 ephy_lang_row_dispose (GObject *object)
@@ -51,6 +49,61 @@ ephy_lang_row_dispose (GObject *object)
   G_OBJECT_CLASS (ephy_lang_row_parent_class)->dispose (object);
 }
 
+static GdkContentProvider *
+drag_prepare_cb (EphyLangRow *self,
+                 double       x,
+                 double       y)
+{
+  return gdk_content_provider_new_typed (EPHY_TYPE_LANG_ROW, self);
+}
+
+static void
+drag_begin_cb (EphyLangRow *self,
+               GdkDrag     *drag)
+{
+  GtkAllocation alloc;
+  GtkWidget *drag_list;
+  GtkWidget *drag_row;
+  GtkWidget *drag_icon;
+  const char *title;
+
+  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+  drag_list = gtk_list_box_new ();
+  gtk_widget_set_size_request (drag_list, alloc.width, alloc.height);
+  gtk_widget_add_css_class (drag_list, "boxed-list");
+
+  title = adw_preferences_row_get_title (ADW_PREFERENCES_ROW (self));
+
+  drag_row = ephy_lang_row_new ();
+  ephy_lang_row_set_code (EPHY_LANG_ROW (drag_row), self->code);
+  adw_preferences_row_set_title (ADW_PREFERENCES_ROW (drag_row), title);
+
+  gtk_list_box_append (GTK_LIST_BOX (drag_list), drag_row);
+
+  drag_icon = gtk_drag_icon_get_for_drag (drag);
+  gtk_widget_add_css_class (drag_icon, "boxed-list");
+  gtk_drag_icon_set_child (GTK_DRAG_ICON (drag_icon), drag_list);
+}
+
+static gboolean
+drop_cb (EphyLangRow  *self,
+         const GValue *value,
+         double        x,
+         double        y)
+{
+  EphyLangRow *source;
+
+  if (!G_VALUE_HOLDS (value, EPHY_TYPE_LANG_ROW))
+    return FALSE;
+
+  source = g_value_get_object (value);
+
+  g_signal_emit (source, signals[MOVE_ROW], 0, self);
+
+  return TRUE;
+}
+
 static void
 on_delete_button_clicked (GtkWidget   *button,
                           EphyLangRow *self)
@@ -76,19 +129,33 @@ ephy_lang_row_class_init (EphyLangRowClass *klass)
                   0, NULL, NULL, NULL,
                   G_TYPE_NONE, 0);
 
-  gtk_widget_class_bind_template_child (widget_class, EphyLangRow, dnd_top_revealer);
-  gtk_widget_class_bind_template_child (widget_class, EphyLangRow, action_row);
-  gtk_widget_class_bind_template_child (widget_class, EphyLangRow, drag_event_box);
+  signals[MOVE_ROW] =
+    g_signal_new ("move-row",
+                  EPHY_TYPE_LANG_ROW,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  1, EPHY_TYPE_LANG_ROW);
+
+  gtk_widget_class_bind_template_child (widget_class, EphyLangRow, drag_handle);
   gtk_widget_class_bind_template_child (widget_class, EphyLangRow, delete_button);
-  gtk_widget_class_bind_template_child (widget_class, EphyLangRow, dnd_bottom_revealer);
 
+  gtk_widget_class_bind_template_callback (widget_class, drag_prepare_cb);
+  gtk_widget_class_bind_template_callback (widget_class, drag_begin_cb);
   gtk_widget_class_bind_template_callback (widget_class, on_delete_button_clicked);
 }
 
 static void
 ephy_lang_row_init (EphyLangRow *self)
 {
+  GtkDropTarget *target;
+
   gtk_widget_init_template (GTK_WIDGET (self));
+
+  target = gtk_drop_target_new (EPHY_TYPE_LANG_ROW, GDK_ACTION_MOVE);
+  gtk_drop_target_set_preload (target, TRUE);
+  g_signal_connect_swapped (target, "drop", G_CALLBACK (drop_cb), self);
+  gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (target));
 }
 
 GtkWidget *
@@ -97,13 +164,6 @@ ephy_lang_row_new ()
   return g_object_new (EPHY_TYPE_LANG_ROW, NULL);
 }
 
-void
-ephy_lang_row_set_title (EphyLangRow *self,
-                         const char  *title)
-{
-  hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (self->action_row), title);
-}
-
 void
 ephy_lang_row_set_code (EphyLangRow *self,
                         const char  *code)
@@ -120,27 +180,9 @@ ephy_lang_row_get_code (EphyLangRow *self)
   return self->code;
 }
 
-GtkWidget *
-ephy_lang_row_get_drag_event_box (EphyLangRow *self)
-{
-  return self->drag_event_box;
-}
-
 void
 ephy_lang_row_set_delete_sensitive (EphyLangRow *self,
                                     gboolean     sensitive)
 {
   gtk_widget_set_sensitive (self->delete_button, sensitive);
 }
-
-GtkWidget *
-ephy_lang_row_get_dnd_top_revealer (EphyLangRow *self)
-{
-  return self->dnd_top_revealer;
-}
-
-GtkWidget *
-ephy_lang_row_get_dnd_bottom_revealer (EphyLangRow *self)
-{
-  return self->dnd_bottom_revealer;
-}
diff --git a/src/preferences/ephy-lang-row.h b/src/preferences/ephy-lang-row.h
index 612bc4f32..ac945bda9 100644
--- a/src/preferences/ephy-lang-row.h
+++ b/src/preferences/ephy-lang-row.h
@@ -20,29 +20,21 @@
 
 #pragma once
 
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_LANG_ROW (ephy_lang_row_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyLangRow, ephy_lang_row, EPHY, LANG_ROW, GtkListBoxRow)
+G_DECLARE_FINAL_TYPE (EphyLangRow, ephy_lang_row, EPHY, LANG_ROW, AdwActionRow)
 
 GtkWidget      *ephy_lang_row_new                     ();
 
-void            ephy_lang_row_set_title               (EphyLangRow *self,
-                                                       const char  *title);
-
 const char     *ephy_lang_row_get_code                (EphyLangRow *self);
 void            ephy_lang_row_set_code                (EphyLangRow *self,
                                                        const char  *code);
 
-GtkWidget      *ephy_lang_row_get_drag_event_box      (EphyLangRow *self);
-
 void            ephy_lang_row_set_delete_sensitive    (EphyLangRow *self,
                                                        gboolean     sensitive);
 
-GtkWidget      *ephy_lang_row_get_dnd_top_revealer    (EphyLangRow *self);
-GtkWidget      *ephy_lang_row_get_dnd_bottom_revealer (EphyLangRow *self);
-
 G_END_DECLS
diff --git a/src/preferences/ephy-prefs-dialog.c b/src/preferences/ephy-prefs-dialog.c
index 29addb886..6d8094d97 100644
--- a/src/preferences/ephy-prefs-dialog.c
+++ b/src/preferences/ephy-prefs-dialog.c
@@ -33,47 +33,33 @@
 #include "prefs-general-page.h"
 
 struct _EphyPrefsDialog {
-  HdyPreferencesWindow parent_instance;
+  AdwPreferencesWindow parent_instance;
 
   PrefsGeneralPage *general_page;
 
   GtkWidget *active_data_view;
 };
 
-G_DEFINE_TYPE (EphyPrefsDialog, ephy_prefs_dialog, HDY_TYPE_PREFERENCES_WINDOW)
+G_DEFINE_TYPE (EphyPrefsDialog, ephy_prefs_dialog, ADW_TYPE_PREFERENCES_WINDOW)
 
 static gboolean
-on_key_press_event (EphyPrefsDialog *prefs_dialog,
-                    GdkEvent        *event,
-                    gpointer         user_data)
+on_close_request (EphyPrefsDialog *prefs_dialog)
 {
-  EphyDataView *active_data_view = EPHY_DATA_VIEW (prefs_dialog->active_data_view);
-
-  /* If the user is currently viewing one the data views,
-   * then we want to redirect any key events there */
-  if (active_data_view)
-    return ephy_data_view_handle_event (active_data_view, event);
-
-  return GDK_EVENT_PROPAGATE;
-}
-
-static void
-on_delete_event (EphyPrefsDialog *prefs_dialog)
-{
-  prefs_general_page_on_pd_delete_event (prefs_dialog->general_page);
-  gtk_widget_destroy (GTK_WIDGET (prefs_dialog));
+  prefs_general_page_on_pd_close_request (prefs_dialog->general_page);
 
   /* To avoid any unnecessary IO when typing changes in the search engine
    * list row's entries, only save when closing the prefs dialog.
    */
   ephy_search_engine_manager_save_to_settings (ephy_embed_shell_get_search_engine_manager 
(ephy_embed_shell_get_default ()));
+
+  return GDK_EVENT_PROPAGATE;
 }
 
 static void
 on_any_data_view_back_button_clicked (GtkWidget       *data_view,
                                       EphyPrefsDialog *prefs_dialog)
 {
-  hdy_preferences_window_close_subpage (HDY_PREFERENCES_WINDOW (prefs_dialog));
+  adw_preferences_window_close_subpage (ADW_PREFERENCES_WINDOW (prefs_dialog));
 
   prefs_dialog->active_data_view = NULL;
 }
@@ -86,7 +72,7 @@ present_data_view (EphyPrefsDialog *prefs_dialog,
                            G_CALLBACK (on_any_data_view_back_button_clicked),
                            prefs_dialog, 0);
 
-  hdy_preferences_window_present_subpage (HDY_PREFERENCES_WINDOW (prefs_dialog),
+  adw_preferences_window_present_subpage (ADW_PREFERENCES_WINDOW (prefs_dialog),
                                           presented_view);
 
   prefs_dialog->active_data_view = presented_view;
@@ -96,9 +82,7 @@ static void
 on_passwords_row_activated (GtkWidget       *privacy_page,
                             EphyPrefsDialog *prefs_dialog)
 {
-  GtkWidget *view = g_object_new (EPHY_TYPE_PASSWORDS_VIEW,
-                                  "visible", TRUE,
-                                  NULL);
+  GtkWidget *view = g_object_new (EPHY_TYPE_PASSWORDS_VIEW, NULL);
 
   present_data_view (prefs_dialog, view);
 }
@@ -107,9 +91,7 @@ static void
 on_clear_data_row_activated (GtkWidget       *privacy_page,
                              EphyPrefsDialog *prefs_dialog)
 {
-  GtkWidget *view = g_object_new (EPHY_TYPE_CLEAR_DATA_VIEW,
-                                  "visible", TRUE,
-                                  NULL);
+  GtkWidget *view = g_object_new (EPHY_TYPE_CLEAR_DATA_VIEW, NULL);
 
   present_data_view (prefs_dialog, view);
 }
@@ -125,8 +107,7 @@ ephy_prefs_dialog_class_init (EphyPrefsDialogClass *klass)
   gtk_widget_class_bind_template_child (widget_class, EphyPrefsDialog, general_page);
 
   /* Template file callbacks */
-  gtk_widget_class_bind_template_callback (widget_class, on_key_press_event);
-  gtk_widget_class_bind_template_callback (widget_class, on_delete_event);
+  gtk_widget_class_bind_template_callback (widget_class, on_close_request);
   gtk_widget_class_bind_template_callback (widget_class, on_passwords_row_activated);
   gtk_widget_class_bind_template_callback (widget_class, on_clear_data_row_activated);
 }
diff --git a/src/preferences/ephy-prefs-dialog.h b/src/preferences/ephy-prefs-dialog.h
index d5252ef83..bff692bd0 100644
--- a/src/preferences/ephy-prefs-dialog.h
+++ b/src/preferences/ephy-prefs-dialog.h
@@ -21,12 +21,12 @@
 
 #pragma once
 
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_PREFS_DIALOG (ephy_prefs_dialog_get_type ())
 
-G_DECLARE_FINAL_TYPE (EphyPrefsDialog, ephy_prefs_dialog, EPHY, PREFS_DIALOG, HdyPreferencesWindow)
+G_DECLARE_FINAL_TYPE (EphyPrefsDialog, ephy_prefs_dialog, EPHY, PREFS_DIALOG, AdwPreferencesWindow)
 
 G_END_DECLS
diff --git a/src/preferences/ephy-search-engine-listbox.c b/src/preferences/ephy-search-engine-listbox.c
index 2e5cb2184..3dc6b1501 100644
--- a/src/preferences/ephy-search-engine-listbox.c
+++ b/src/preferences/ephy-search-engine-listbox.c
@@ -168,7 +168,7 @@ ephy_add_engine_button_merged_model_init (EphyAddEngineButtonMergedModel *self)
 }
 
 struct _EphySearchEngineListBox {
-  GtkBin parent_instance;
+  AdwBin parent_instance;
 
   GtkWidget *list;
 
@@ -189,7 +189,7 @@ struct _EphySearchEngineListBox {
   gboolean is_model_initially_loaded;
 };
 
-G_DEFINE_TYPE (EphySearchEngineListBox, ephy_search_engine_list_box, GTK_TYPE_BIN)
+G_DEFINE_TYPE (EphySearchEngineListBox, ephy_search_engine_list_box, ADW_TYPE_BIN)
 
 GtkWidget *
 ephy_search_engine_list_box_new (void)
@@ -261,7 +261,7 @@ on_row_expand_state_changed_cb (EphySearchEngineRow     *expanded_row,
   int i = 0;
 
   /* We only unexpand other rows if this is a notify signal for an expanded row. */
-  if (!hdy_expander_row_get_expanded (HDY_EXPANDER_ROW (expanded_row)))
+  if (!adw_expander_row_get_expanded (ADW_EXPANDER_ROW (expanded_row)))
     return;
 
   while ((row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->list), i++))) {
@@ -273,7 +273,7 @@ on_row_expand_state_changed_cb (EphySearchEngineRow     *expanded_row,
     if (row == GTK_LIST_BOX_ROW (expanded_row))
       continue;
 
-    hdy_expander_row_set_expanded (HDY_EXPANDER_ROW (row), FALSE);
+    adw_expander_row_set_expanded (ADW_EXPANDER_ROW (row), FALSE);
   }
 }
 
@@ -305,10 +305,8 @@ create_add_search_engine_row ()
 
   gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), true);
   gtk_widget_set_size_request (row, -1, 50);
-  gtk_widget_show (row);
 
-  gtk_widget_show (label);
-  gtk_container_add (GTK_CONTAINER (row), label);
+  gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), label);
 
   return row;
 }
@@ -322,7 +320,7 @@ create_search_engine_row (EphySearchEngine        *engine,
   g_signal_connect_object (engine, "notify::name", G_CALLBACK (on_search_engine_name_changed_cb), self, 0);
 
   ephy_search_engine_row_set_radio_button_group (row,
-                                                 GTK_RADIO_BUTTON (self->radio_buttons_group));
+                                                 GTK_CHECK_BUTTON (self->radio_buttons_group));
   g_signal_connect (row,
                     "notify::expanded",
                     G_CALLBACK (on_row_expand_state_changed_cb),
@@ -335,7 +333,7 @@ create_search_engine_row (EphySearchEngine        *engine,
     /* This will also unexpand all other rows, to make the new one stand out,
      * in on_row_expand_state_changed_cb().
      */
-    hdy_expander_row_set_expanded (HDY_EXPANDER_ROW (row), TRUE);
+    adw_expander_row_set_expanded (ADW_EXPANDER_ROW (row), TRUE);
   }
 
   return GTK_WIDGET (row);
@@ -392,7 +390,7 @@ ephy_search_engine_list_box_init (EphySearchEngineListBox *self)
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  self->radio_buttons_group = gtk_radio_button_new (NULL);
+  self->radio_buttons_group = gtk_check_button_new ();
   /* Ref the radio buttons group and remove the floating reference because we
    * don't hook this widget to any part of the UI (for example gtk_container_add
    * would remove the floating reference and ref it).
diff --git a/src/preferences/ephy-search-engine-listbox.h b/src/preferences/ephy-search-engine-listbox.h
index 6750972ac..c65cfb42a 100644
--- a/src/preferences/ephy-search-engine-listbox.h
+++ b/src/preferences/ephy-search-engine-listbox.h
@@ -20,14 +20,13 @@
 
 #pragma once
 
-#include <glib-object.h>
-#include <gtk/gtk.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_SEARCH_ENGINE_LIST_BOX (ephy_search_engine_list_box_get_type())
 
-G_DECLARE_FINAL_TYPE (EphySearchEngineListBox, ephy_search_engine_list_box, EPHY, SEARCH_ENGINE_LIST_BOX, 
GtkBin)
+G_DECLARE_FINAL_TYPE (EphySearchEngineListBox, ephy_search_engine_list_box, EPHY, SEARCH_ENGINE_LIST_BOX, 
AdwBin)
 
 GtkWidget *ephy_search_engine_list_box_new                (void);
 
diff --git a/src/preferences/ephy-search-engine-row.c b/src/preferences/ephy-search-engine-row.c
index 9b78d436e..eba051b89 100644
--- a/src/preferences/ephy-search-engine-row.c
+++ b/src/preferences/ephy-search-engine-row.c
@@ -29,7 +29,7 @@
 #include "ephy-embed-shell.h"
 
 struct _EphySearchEngineRow {
-  HdyExpanderRow parent_instance;
+  AdwExpanderRow parent_instance;
 
   /* Widgets */
   GtkWidget *name_entry;
@@ -42,7 +42,7 @@ struct _EphySearchEngineRow {
   EphySearchEngineManager *manager;
 };
 
-G_DEFINE_TYPE (EphySearchEngineRow, ephy_search_engine_row, HDY_TYPE_EXPANDER_ROW)
+G_DEFINE_TYPE (EphySearchEngineRow, ephy_search_engine_row, ADW_TYPE_EXPANDER_ROW)
 
 enum {
   PROP_0,
@@ -86,10 +86,10 @@ ephy_search_engine_row_new (EphySearchEngine        *engine,
  */
 void
 ephy_search_engine_row_set_radio_button_group (EphySearchEngineRow *self,
-                                               GtkRadioButton      *radio_button_group)
+                                               GtkCheckButton      *radio_button_group)
 {
-  gtk_radio_button_set_group (GTK_RADIO_BUTTON (self->radio_button),
-                              gtk_radio_button_get_group (radio_button_group));
+  gtk_check_button_set_group (GTK_CHECK_BUTTON (self->radio_button),
+                              radio_button_group);
 }
 
 /***** Private implementation *****/
@@ -182,7 +182,7 @@ on_bang_entry_text_changed_cb (EphySearchEngineRow *row,
                                GParamSpec          *pspec,
                                GtkEntry            *bang_entry)
 {
-  const char *bang = gtk_entry_get_text (bang_entry);
+  const char *bang = gtk_editable_get_text (GTK_EDITABLE (bang_entry));
 
   /* Checks if the bang already exists */
   if (g_strcmp0 (bang, ephy_search_engine_get_bang (row->engine)) != 0 &&
@@ -210,7 +210,7 @@ on_address_entry_text_changed_cb (EphySearchEngineRow *row,
                                   GtkEntry            *address_entry)
 {
   const char *validation_message = NULL;
-  const char *url = gtk_entry_get_text (address_entry);
+  const char *url = gtk_editable_get_text (GTK_EDITABLE (address_entry));
 
   /* Address in invalid. */
   if (!validate_search_engine_address (url, &validation_message)) {
@@ -309,7 +309,7 @@ update_bang_for_name (EphySearchEngineRow *row,
   }
   lowercase_acronym = g_utf8_strdown (acronym, -1); /* Bangs are usually lowercase */
   final_bang = g_strconcat ("!", lowercase_acronym, NULL); /* "!" is the prefix for the bang */
-  gtk_entry_set_text (GTK_ENTRY (row->bang_entry), final_bang);
+  gtk_editable_set_text (GTK_EDITABLE (row->bang_entry), final_bang);
   ephy_search_engine_set_bang (row->engine, final_bang);
 }
 
@@ -318,7 +318,7 @@ on_name_entry_text_changed_cb (EphySearchEngineRow *row,
                                GParamSpec          *pspec,
                                GtkEntry            *name_entry)
 {
-  const char *new_name = gtk_entry_get_text (name_entry);
+  const char *new_name = gtk_editable_get_text (GTK_EDITABLE (name_entry));
 
   /* This is an edge case when you copy the whole name then paste it again in
    * place of the whole current name. GtkEntry will record a notify signal even
@@ -342,7 +342,7 @@ on_name_entry_text_changed_cb (EphySearchEngineRow *row,
      * "Wikipedia (en)". That's just annoying, so only do it when there hasn't
      * been any bang added yet.
      */
-    if (g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (row->bang_entry)), "") == 0)
+    if (g_strcmp0 (gtk_editable_get_text (GTK_EDITABLE (row->bang_entry)), "") == 0)
       update_bang_for_name (row, new_name);
 
     ephy_search_engine_set_name (row->engine, new_name);
@@ -354,7 +354,7 @@ on_radio_button_active_changed_cb (EphySearchEngineRow *self,
                                    GParamSpec          *pspec,
                                    GtkButton           *button)
 {
-  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) &&
+  if (gtk_check_button_get_active (GTK_CHECK_BUTTON (button)) &&
       /* Avoid infinite loop between this callback and on_default_engine_changed_cb() */
       ephy_search_engine_manager_get_default_engine (self->manager) != self->engine)
     ephy_search_engine_manager_set_default_engine (self->manager, self->engine);
@@ -366,19 +366,13 @@ on_default_engine_changed_cb (EphySearchEngineManager *manager,
                               EphySearchEngineRow     *self)
 {
   if (ephy_search_engine_manager_get_default_engine (manager) == self->engine)
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->radio_button), TRUE);
+    gtk_check_button_set_active (GTK_CHECK_BUTTON (self->radio_button), TRUE);
 }
 
 static void
 on_remove_button_clicked_cb (EphySearchEngineRow *row,
                              GtkButton           *button)
 {
-  /* FIXME: this should be fixed in libhandy
-   * Unexpand the row before removing it so the styling isn't broken.
-   * See the checked-expander-row-previous-sibling style class in HdyExpanderRow documentation.
-   */
-  hdy_expander_row_set_expanded (HDY_EXPANDER_ROW (row), FALSE);
-
   ephy_search_engine_manager_delete_engine (row->manager, row->engine);
 }
 
@@ -426,7 +420,8 @@ on_ephy_search_engine_row_constructed (GObject *object)
   g_assert (self->engine != NULL);
   g_assert (self->manager != NULL);
 
-  gtk_entry_set_text (GTK_ENTRY (self->name_entry), ephy_search_engine_get_name (self->engine));
+  gtk_editable_set_text (GTK_EDITABLE (self->name_entry),
+                         ephy_search_engine_get_name (self->engine));
 
   /* We can't directly bind that in the UI file because there's issues with
    * properties bindings that involve the root widget (the <template> root one).
@@ -435,10 +430,10 @@ on_ephy_search_engine_row_constructed (GObject *object)
                           self, "title",
                           G_BINDING_SYNC_CREATE);
 
-  gtk_entry_set_text (GTK_ENTRY (self->address_entry),
-                      ephy_search_engine_get_url (self->engine));
-  gtk_entry_set_text (GTK_ENTRY (self->bang_entry),
-                      ephy_search_engine_get_bang (self->engine));
+  gtk_editable_set_text (GTK_EDITABLE (self->address_entry),
+                         ephy_search_engine_get_url (self->engine));
+  gtk_editable_set_text (GTK_EDITABLE (self->bang_entry),
+                         ephy_search_engine_get_bang (self->engine));
 
   g_signal_connect_object (self->name_entry, "notify::text", G_CALLBACK (on_name_entry_text_changed_cb), 
self, G_CONNECT_SWAPPED);
   g_signal_connect_object (self->address_entry, "notify::text", G_CALLBACK 
(on_address_entry_text_changed_cb), self, G_CONNECT_SWAPPED);
diff --git a/src/preferences/ephy-search-engine-row.h b/src/preferences/ephy-search-engine-row.h
index f94a8288d..4a0a7c557 100644
--- a/src/preferences/ephy-search-engine-row.h
+++ b/src/preferences/ephy-search-engine-row.h
@@ -21,20 +21,20 @@
 
 #pragma once
 
-#include <handy.h>
+#include <adwaita.h>
 #include "ephy-search-engine-manager.h"
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_SEARCH_ENGINE_ROW (ephy_search_engine_row_get_type())
 
-G_DECLARE_FINAL_TYPE (EphySearchEngineRow, ephy_search_engine_row, EPHY, SEARCH_ENGINE_ROW, HdyExpanderRow)
+G_DECLARE_FINAL_TYPE (EphySearchEngineRow, ephy_search_engine_row, EPHY, SEARCH_ENGINE_ROW, AdwExpanderRow)
 
 EphySearchEngineRow *ephy_search_engine_row_new                    (EphySearchEngine        *engine,
                                                                     EphySearchEngineManager *manager);
 void                 ephy_search_engine_row_set_can_remove         (EphySearchEngineRow *self,
                                                                     gboolean             can_remove);
 void                 ephy_search_engine_row_set_radio_button_group (EphySearchEngineRow *self,
-                                                                    GtkRadioButton      *radio_button_group);
+                                                                    GtkCheckButton      *radio_button_group);
 
 G_END_DECLS
diff --git a/src/preferences/passwords-view.c b/src/preferences/passwords-view.c
index cd19acf7a..d994cd55c 100644
--- a/src/preferences/passwords-view.c
+++ b/src/preferences/passwords-view.c
@@ -20,10 +20,9 @@
 
 #include "config.h"
 
-#include <dazzle.h>
+#include <adwaita.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
-#include <handy.h>
 #include <string.h>
 
 #define SECRET_API_SUBJECT_TO_CHANGE
@@ -70,7 +69,7 @@ clear_listbox (GtkWidget *listbox)
   GtkListBoxRow *row;
 
   while ((row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (listbox), 0)))
-    gtk_container_remove (GTK_CONTAINER (listbox), GTK_WIDGET (row));
+    gtk_list_box_remove (GTK_LIST_BOX (listbox), GTK_WIDGET (row));
 }
 
 static void
@@ -129,7 +128,7 @@ copy_password_clicked (GtkWidget *button,
   const char *password = user_data;
 
   if (password)
-    gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button), GDK_SELECTION_CLIPBOARD), 
password, -1);
+    gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)), password);
 }
 
 static void
@@ -139,7 +138,7 @@ copy_username_clicked (GtkWidget *button,
   const char *username = user_data;
 
   if (username)
-    gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button), GDK_SELECTION_CLIPBOARD), 
username, -1);
+    gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (button)), username);
 }
 
 static void
@@ -162,7 +161,7 @@ confirmation_dialog_response_cb (GtkWidget         *dialog,
                                  int                response,
                                  EphyPasswordsView *self)
 {
-  gtk_widget_destroy (dialog);
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   if (response == GTK_RESPONSE_ACCEPT) {
     ephy_password_manager_forget_all (self->manager);
@@ -180,9 +179,9 @@ confirmation_dialog_construct (EphyPasswordsView *self)
 {
   GtkWidget *dialog;
   GtkWidget *button;
-  GtkWidget *window;
+  GtkRoot *window;
 
-  window = gtk_widget_get_toplevel (GTK_WIDGET (self));
+  window = gtk_widget_get_root (GTK_WIDGET (self));
 
   dialog = gtk_message_dialog_new (GTK_WINDOW (window),
                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -242,26 +241,25 @@ populate_model_cb (GList    *records,
     GtkWidget *sub_row;
     GtkWidget *separator;
     GtkWidget *button;
-    GtkWidget *image;
     GtkWidget *entry;
     const char *text;
 
-    row = hdy_expander_row_new ();
+    row = adw_expander_row_new ();
     g_object_set_data (G_OBJECT (row), "record", record);
-    hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (row), ephy_password_record_get_origin (record));
-    hdy_expander_row_set_subtitle (HDY_EXPANDER_ROW (row), ephy_password_record_get_username (record));
-    hdy_expander_row_set_show_enable_switch (HDY_EXPANDER_ROW (row), FALSE);
+    adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), ephy_password_record_get_origin (record));
+    adw_expander_row_set_subtitle (ADW_EXPANDER_ROW (row), ephy_password_record_get_username (record));
+    adw_expander_row_set_show_enable_switch (ADW_EXPANDER_ROW (row), FALSE);
 
-    button = gtk_button_new_from_icon_name ("edit-copy-symbolic", GTK_ICON_SIZE_BUTTON);
+    button = gtk_button_new_from_icon_name ("edit-copy-symbolic");
     gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
     gtk_widget_set_tooltip_text (button, _("Copy password"));
-    hdy_expander_row_add_action (HDY_EXPANDER_ROW (row), button);
+    adw_expander_row_add_action (ADW_EXPANDER_ROW (row), button);
     g_signal_connect (button, "clicked", G_CALLBACK (copy_password_clicked), (void 
*)(ephy_password_record_get_password (record)));
 
     /* Username */
-    sub_row = hdy_action_row_new ();
-    hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (sub_row), _("Username"));
-    gtk_container_add (GTK_CONTAINER (row), sub_row);
+    sub_row = adw_action_row_new ();
+    adw_preferences_row_set_title (ADW_PREFERENCES_ROW (sub_row), _("Username"));
+    adw_expander_row_add_row (ADW_EXPANDER_ROW (row), sub_row);
 
     entry = gtk_entry_new ();
     gtk_widget_set_hexpand (entry, TRUE);
@@ -272,73 +270,69 @@ populate_model_cb (GList    *records,
 
     text = ephy_password_record_get_username (record);
     if (text)
-      gtk_entry_set_text (GTK_ENTRY (entry), text);
+      gtk_editable_set_text (GTK_EDITABLE (entry), text);
 
-    gtk_container_add (GTK_CONTAINER (sub_row), entry);
+    adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), entry);
 
     separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
     gtk_widget_set_margin_top (separator, 8);
     gtk_widget_set_margin_bottom (separator, 8);
-    gtk_container_add (GTK_CONTAINER (sub_row), separator);
+    adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), separator);
 
-    button = gtk_button_new_from_icon_name ("edit-copy-symbolic", GTK_ICON_SIZE_BUTTON);
+    button = gtk_button_new_from_icon_name ("edit-copy-symbolic");
     g_signal_connect (button, "clicked", G_CALLBACK (copy_username_clicked), (void 
*)(ephy_password_record_get_username (record)));
     gtk_widget_set_tooltip_text (button, _("Copy username"));
     gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
-    gtk_container_add (GTK_CONTAINER (sub_row), button);
+    adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), button);
 
     /* Password */
-    sub_row = hdy_action_row_new ();
-    hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (sub_row), _("Password"));
-    gtk_container_add (GTK_CONTAINER (row), sub_row);
+    sub_row = adw_action_row_new ();
+    adw_preferences_row_set_title (ADW_PREFERENCES_ROW (sub_row), _("Password"));
+    adw_expander_row_add_row (ADW_EXPANDER_ROW (row), sub_row);
 
-    entry = gtk_entry_new ();
+    entry = gtk_password_entry_new ();
     gtk_widget_set_hexpand (entry, TRUE);
     gtk_widget_set_valign (entry, GTK_ALIGN_CENTER);
     gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE);
     gtk_entry_set_alignment (GTK_ENTRY (entry), 1.0f);
     gtk_entry_set_has_frame (GTK_ENTRY (entry), FALSE);
-    gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
 
     text = ephy_password_record_get_password (record);
     if (text)
-      gtk_entry_set_text (GTK_ENTRY (entry), text);
+      gtk_editable_set_text (GTK_EDITABLE (entry), text);
 
-    gtk_container_add (GTK_CONTAINER (sub_row), entry);
+    adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), entry);
 
     separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
     gtk_widget_set_margin_top (separator, 8);
     gtk_widget_set_margin_bottom (separator, 8);
-    gtk_container_add (GTK_CONTAINER (sub_row), separator);
+    adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), separator);
 
     button = gtk_toggle_button_new ();
-    image = gtk_image_new_from_icon_name ("dialog-password-symbolic", GTK_ICON_SIZE_BUTTON);
-    gtk_button_set_image (GTK_BUTTON (button), image);
+    gtk_button_set_icon_name (GTK_BUTTON (button), "dialog-password-symbolic");
     gtk_widget_set_tooltip_text (button, _("Reveal password"));
     gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
 
     g_object_bind_property (G_OBJECT (button), "active", G_OBJECT (entry), "visibility", G_BINDING_DEFAULT);
-    gtk_container_add (GTK_CONTAINER (sub_row), button);
+    adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), button);
 
     /* Remove button */
-    sub_row = hdy_action_row_new ();
-    gtk_container_add (GTK_CONTAINER (row), sub_row);
+    sub_row = adw_action_row_new ();
+    adw_expander_row_add_row (ADW_EXPANDER_ROW (row), sub_row);
 
     button = gtk_button_new_with_label (_("Remove Password"));
     gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
-    dzl_gtk_widget_add_style_class (button, "destructive-action");
+    gtk_widget_add_css_class (button, "destructive-action");
     g_signal_connect (button, "clicked", G_CALLBACK (forget_clicked), record);
-    gtk_container_add (GTK_CONTAINER (sub_row), button);
+    adw_action_row_add_suffix (ADW_ACTION_ROW (sub_row), button);
 
     g_object_set_data (G_OBJECT (record), "passwords-view", passwords_view);
 
-    gtk_list_box_insert (GTK_LIST_BOX (passwords_view->listbox), row, -1);
+    gtk_list_box_append (GTK_LIST_BOX (passwords_view->listbox), row);
   }
 
-  if (g_list_length (records)) {
+  if (g_list_length (records))
     ephy_data_view_set_has_data (EPHY_DATA_VIEW (passwords_view), TRUE);
-    gtk_widget_show_all (passwords_view->listbox);
-  }
 
   g_assert (!passwords_view->records);
   passwords_view->records = g_list_copy_deep (records, (GCopyFunc)g_object_ref, NULL);
diff --git a/src/preferences/prefs-appearance-page.c b/src/preferences/prefs-appearance-page.c
index cef02e813..bd62957fb 100644
--- a/src/preferences/prefs-appearance-page.c
+++ b/src/preferences/prefs-appearance-page.c
@@ -32,7 +32,7 @@
 #include <math.h>
 
 struct _PrefsAppearancePage {
-  HdyPreferencesPage parent_instance;
+  AdwPreferencesPage parent_instance;
 
   /* Fonts */
   GtkWidget *use_gnome_fonts_row;
@@ -53,58 +53,22 @@ struct _PrefsAppearancePage {
   GtkWidget *default_zoom_spin_button;
 };
 
-G_DEFINE_TYPE (PrefsAppearancePage, prefs_appearance_page, HDY_TYPE_PREFERENCES_PAGE)
+G_DEFINE_TYPE (PrefsAppearancePage, prefs_appearance_page, ADW_TYPE_PREFERENCES_PAGE)
 
 static gchar *
-reader_font_style_get_name (HdyEnumValueObject *value,
-                            gpointer            user_data)
+reader_font_style_get_name (gpointer                 user_data,
+                            EphyPrefsReaderFontStyle style)
 {
-  g_assert (HDY_IS_ENUM_VALUE_OBJECT (value));
-
-  switch (hdy_enum_value_object_get_value (value)) {
+  switch (style) {
     case EPHY_PREFS_READER_FONT_STYLE_SANS:
-      return g_strdup_printf ("<span font-family=\"%s\">%s</span>", "sans", _("Sans"));
+      return g_strdup (_("Sans"));
     case EPHY_PREFS_READER_FONT_STYLE_SERIF:
-      return g_strdup_printf ("<span font-family=\"%s\">%s</span>", "serif", _("Serif"));
+      return g_strdup (_("Serif"));
     default:
-      return NULL;
+      g_assert_not_reached ();
   }
 }
 
-static GtkWidget *
-reader_font_style_create_list_widget (gpointer item,
-                                      gpointer user_data)
-{
-  g_autofree gchar *name = reader_font_style_get_name (item, NULL);
-
-  return g_object_new (GTK_TYPE_LABEL,
-                       "ellipsize", PANGO_ELLIPSIZE_END,
-                       "label", name,
-                       "use-markup", TRUE,
-                       "max-width-chars", 20,
-                       "valign", GTK_ALIGN_CENTER,
-                       "visible", TRUE,
-                       "xalign", 0.0,
-                       NULL);
-}
-
-static GtkWidget *
-reader_font_style_create_current_widget (gpointer item,
-                                         gpointer user_data)
-{
-  g_autofree gchar *name = reader_font_style_get_name (item, NULL);
-
-  return g_object_new (GTK_TYPE_LABEL,
-                       "ellipsize", PANGO_ELLIPSIZE_END,
-                       "halign", GTK_ALIGN_END,
-                       "label", name,
-                       "use-markup", TRUE,
-                       "valign", GTK_ALIGN_CENTER,
-                       "visible", TRUE,
-                       "xalign", 0.0,
-                       NULL);
-}
-
 static gboolean
 reader_font_style_get_mapping (GValue   *value,
                                GVariant *variant,
@@ -113,9 +77,9 @@ reader_font_style_get_mapping (GValue   *value,
   const char *reader_colors = g_variant_get_string (variant, NULL);
 
   if (g_strcmp0 (reader_colors, "sans") == 0)
-    g_value_set_int (value, EPHY_PREFS_READER_FONT_STYLE_SANS);
+    g_value_set_uint (value, EPHY_PREFS_READER_FONT_STYLE_SANS);
   else if (g_strcmp0 (reader_colors, "serif") == 0)
-    g_value_set_int (value, EPHY_PREFS_READER_FONT_STYLE_SERIF);
+    g_value_set_uint (value, EPHY_PREFS_READER_FONT_STYLE_SERIF);
 
   return TRUE;
 }
@@ -125,7 +89,7 @@ reader_font_style_set_mapping (const GValue       *value,
                                const GVariantType *expected_type,
                                gpointer            user_data)
 {
-  switch (g_value_get_int (value)) {
+  switch (g_value_get_uint (value)) {
     case EPHY_PREFS_READER_FONT_STYLE_SANS:
       return g_variant_new_string ("sans");
     case EPHY_PREFS_READER_FONT_STYLE_SERIF:
@@ -136,18 +100,16 @@ reader_font_style_set_mapping (const GValue       *value,
 }
 
 static gchar *
-reader_color_scheme_get_name (HdyEnumValueObject *value,
-                              gpointer            user_data)
+reader_color_scheme_get_name (gpointer                   user_data,
+                              EphyPrefsReaderColorScheme scheme)
 {
-  g_assert (HDY_IS_ENUM_VALUE_OBJECT (value));
-
-  switch (hdy_enum_value_object_get_value (value)) {
+  switch (scheme) {
     case EPHY_PREFS_READER_COLORS_LIGHT:
       return g_strdup (_("Light"));
     case EPHY_PREFS_READER_COLORS_DARK:
       return g_strdup (_("Dark"));
     default:
-      return NULL;
+      g_assert_not_reached ();
   }
 }
 
@@ -159,9 +121,9 @@ reader_color_scheme_get_mapping (GValue   *value,
   const char *reader_colors = g_variant_get_string (variant, NULL);
 
   if (g_strcmp0 (reader_colors, "light") == 0)
-    g_value_set_int (value, EPHY_PREFS_READER_COLORS_LIGHT);
+    g_value_set_uint (value, EPHY_PREFS_READER_COLORS_LIGHT);
   else if (g_strcmp0 (reader_colors, "dark") == 0)
-    g_value_set_int (value, EPHY_PREFS_READER_COLORS_DARK);
+    g_value_set_uint (value, EPHY_PREFS_READER_COLORS_DARK);
 
   return TRUE;
 }
@@ -171,7 +133,7 @@ reader_color_scheme_set_mapping (const GValue       *value,
                                  const GVariantType *expected_type,
                                  gpointer            user_data)
 {
-  switch (g_value_get_int (value)) {
+  switch (g_value_get_uint (value)) {
     case EPHY_PREFS_READER_COLORS_LIGHT:
       return g_variant_new_string ("light");
     case EPHY_PREFS_READER_COLORS_DARK:
@@ -262,7 +224,7 @@ on_default_zoom_spin_button_output (GtkSpinButton *spin,
   adjustment = gtk_spin_button_get_adjustment (spin);
   value = (int)gtk_adjustment_get_value (adjustment);
   text = g_strdup_printf ("%.f%%", value);
-  gtk_entry_set_text (GTK_ENTRY (spin), text);
+  gtk_editable_set_text (GTK_EDITABLE (spin), text);
 
   return TRUE;
 }
@@ -280,26 +242,6 @@ on_default_zoom_spin_button_value_changed (GtkSpinButton *spin,
   g_settings_set_double (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_DEFAULT_ZOOM_LEVEL, value);
 }
 
-static void
-setup_font_row (PrefsAppearancePage *appearance_page)
-{
-  g_autoptr (GListStore) store = g_list_store_new (HDY_TYPE_ENUM_VALUE_OBJECT);
-  g_autoptr (GEnumClass) enum_class = g_type_class_ref (EPHY_TYPE_PREFS_READER_FONT_STYLE);
-
-  for (guint i = 0; i < enum_class->n_values; i++) {
-    g_autoptr (HdyEnumValueObject) obj = hdy_enum_value_object_new (&enum_class->values[i]);
-
-    g_list_store_append (store, obj);
-  }
-
-  hdy_combo_row_bind_model (HDY_COMBO_ROW (appearance_page->reader_mode_font_style),
-                            G_LIST_MODEL (store),
-                            (GtkListBoxCreateWidgetFunc)reader_font_style_create_list_widget,
-                            (GtkListBoxCreateWidgetFunc)reader_font_style_create_current_widget,
-                            NULL,
-                            NULL);
-}
-
 static void
 setup_appearance_page (PrefsAppearancePage *appearance_page)
 {
@@ -318,48 +260,41 @@ setup_appearance_page (PrefsAppearancePage *appearance_page)
   g_settings_bind (web_settings,
                    EPHY_PREFS_WEB_SANS_SERIF_FONT,
                    appearance_page->sans_fontbutton,
-                   "font-name",
+                   "font",
                    G_SETTINGS_BIND_DEFAULT);
 
   g_settings_bind (web_settings,
                    EPHY_PREFS_WEB_SERIF_FONT,
                    appearance_page->serif_fontbutton,
-                   "font-name",
+                   "font",
                    G_SETTINGS_BIND_DEFAULT);
 
   g_settings_bind (web_settings,
                    EPHY_PREFS_WEB_MONOSPACE_FONT,
                    appearance_page->mono_fontbutton,
-                   "font-name",
+                   "font",
                    G_SETTINGS_BIND_DEFAULT);
 
   /* ======================================================================== */
   /* ========================== Reader Mode ================================= */
   /* ======================================================================== */
-
-  setup_font_row (appearance_page);
-
   g_settings_bind_with_mapping (reader_settings,
                                 EPHY_PREFS_READER_FONT_STYLE,
                                 appearance_page->reader_mode_font_style,
-                                "selected-index",
+                                "selected",
                                 G_SETTINGS_BIND_DEFAULT,
                                 reader_font_style_get_mapping,
                                 reader_font_style_set_mapping,
                                 NULL, NULL);
 
-  g_object_bind_property (hdy_style_manager_get_default (), "system-supports-color-schemes",
+  g_object_bind_property (adw_style_manager_get_default (), "system-supports-color-schemes",
                           appearance_page->reader_mode_color_scheme, "visible",
                           G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
 
-  hdy_combo_row_set_for_enum (HDY_COMBO_ROW (appearance_page->reader_mode_color_scheme),
-                              EPHY_TYPE_PREFS_READER_COLOR_SCHEME,
-                              reader_color_scheme_get_name, NULL, NULL);
-
   g_settings_bind_with_mapping (reader_settings,
                                 EPHY_PREFS_READER_COLOR_SCHEME,
                                 appearance_page->reader_mode_color_scheme,
-                                "selected-index",
+                                "selected",
                                 G_SETTINGS_BIND_DEFAULT,
                                 reader_color_scheme_get_mapping,
                                 reader_color_scheme_set_mapping,
@@ -433,6 +368,8 @@ prefs_appearance_page_class_init (PrefsAppearancePageClass *klass)
   gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, default_zoom_spin_button);
 
   /* Signals */
+  gtk_widget_class_bind_template_callback (widget_class, reader_font_style_get_name);
+  gtk_widget_class_bind_template_callback (widget_class, reader_color_scheme_get_name);
   gtk_widget_class_bind_template_callback (widget_class, on_default_zoom_spin_button_output);
   gtk_widget_class_bind_template_callback (widget_class, on_default_zoom_spin_button_value_changed);
 }
diff --git a/src/preferences/prefs-appearance-page.h b/src/preferences/prefs-appearance-page.h
index 6489ba8cf..052e403d8 100644
--- a/src/preferences/prefs-appearance-page.h
+++ b/src/preferences/prefs-appearance-page.h
@@ -21,12 +21,12 @@
 #pragma once
 
 #include <glib-object.h>
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_PREFS_APPEARANCE_PAGE (prefs_appearance_page_get_type ())
 
-G_DECLARE_FINAL_TYPE (PrefsAppearancePage, prefs_appearance_page, EPHY, PREFS_APPEARANCE_PAGE, 
HdyPreferencesPage)
+G_DECLARE_FINAL_TYPE (PrefsAppearancePage, prefs_appearance_page, EPHY, PREFS_APPEARANCE_PAGE, 
AdwPreferencesPage)
 
 G_END_DECLS
diff --git a/src/preferences/prefs-general-page.c b/src/preferences/prefs-general-page.c
index 42ee99969..7bb2f1639 100644
--- a/src/preferences/prefs-general-page.c
+++ b/src/preferences/prefs-general-page.c
@@ -42,7 +42,7 @@ enum {
 };
 
 struct _PrefsGeneralPage {
-  HdyPreferencesPage parent_instance;
+  AdwPreferencesPage parent_instance;
 
   /* Web Application */
   EphyWebApplication *webapp;
@@ -87,7 +87,7 @@ struct _PrefsGeneralPage {
   GtkWidget *enable_switch_to_new_tab;
 
   /* Languages */
-  HdyPreferencesGroup *lang_group;
+  AdwPreferencesGroup *lang_group;
   GtkWidget *lang_listbox;
   GtkWidget *enable_spell_checking_switch;
 
@@ -95,7 +95,7 @@ struct _PrefsGeneralPage {
   GtkTreeView *add_lang_treeview;
 };
 
-G_DEFINE_TYPE (PrefsGeneralPage, prefs_general_page, HDY_TYPE_PREFERENCES_PAGE)
+G_DEFINE_TYPE (PrefsGeneralPage, prefs_general_page, ADW_TYPE_PREFERENCES_PAGE)
 
 static void
 prefs_general_page_finalize (GObject *object)
@@ -154,130 +154,25 @@ language_editor_update_pref (PrefsGeneralPage *general_page)
                   "as", &builder);
 }
 
+static GtkDialog *setup_add_language_dialog (PrefsGeneralPage *general_page);
+
 static void
-drag_data_received (GtkWidget        *widget,
-                    GdkDragContext   *context,
-                    gint              x,
-                    gint              y,
-                    GtkSelectionData *selection_data,
-                    guint             info,
-                    guint32           time,
-                    gpointer          data)
+language_editor_add_activated (GtkWidget *listbox,
+                               GtkWidget *activated_row,
+                               GtkWidget *add_row)
 {
-  PrefsGeneralPage *general_page = data;
-  GtkListBox *lang_listbox = GTK_LIST_BOX (general_page->lang_listbox);
-  GtkWidget *hovered_row;
-  GtkWidget *dragged_row;
-  int length;
-  int hovered_row_pos;
-  int dragged_row_pos;
-  int insert_pos;
-
-  hovered_row = GTK_WIDGET (gtk_list_box_get_row_at_y (lang_listbox, y));
-  dragged_row = (gpointer) * (gpointer *)gtk_selection_data_get_data (selection_data);
-
-  length = get_list_box_length (GTK_LIST_BOX (widget));
-  hovered_row_pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (hovered_row));
-  dragged_row_pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (dragged_row));
-
-  /* If the drag ended while hovering over the dragged row then we return */
-  if (hovered_row == dragged_row)
-    return;
+  PrefsGeneralPage *general_page;
 
-  /* If the drag ended while hovering over the "Add Language" row then we return */
-  if (hovered_row_pos == length - 1)
+  if (add_row != activated_row)
     return;
 
-  g_object_ref (dragged_row);
-  gtk_container_remove (GTK_CONTAINER (lang_listbox), dragged_row);
-
-  /* Calculation logic for insert_pos */
-  if (y < 20) {
-    insert_pos = 0;
-  } else if (dragged_row_pos < hovered_row_pos) {
-    insert_pos = hovered_row_pos;
-  } else { /* dragged_row_pos > hovered_row_pos is true here */
-    insert_pos = hovered_row_pos + 1;
-  }
-
-  gtk_list_box_insert (lang_listbox, dragged_row, insert_pos);
-  g_object_unref (dragged_row);
-
-  language_editor_update_pref (general_page);
-}
-
-static gboolean
-drag_motion (GtkWidget      *widget,
-             GdkDragContext *context,
-             int             x,
-             int             y,
-             guint           time)
-{
-  GtkListBoxRow *first_lang_row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (widget), 0);
-
-  GtkWidget *dragged_row;
-  GtkWidget *hovered_row;
-  GtkWidget *expanded_dnd_revealer;
-  GtkWidget *hovered_row_dnd_revealer = NULL;
-
-  dragged_row = g_object_get_data (G_OBJECT (widget), "dragged-row");
-  hovered_row = GTK_WIDGET (gtk_list_box_get_row_at_y (GTK_LIST_BOX (widget), y));
-  expanded_dnd_revealer = g_object_get_data (G_OBJECT (widget), "dnd-expanded-revealer");
-
-  if (EPHY_IS_LANG_ROW (hovered_row))
-    hovered_row_dnd_revealer = ephy_lang_row_get_dnd_bottom_revealer (EPHY_LANG_ROW (hovered_row));
-
-  /* Edge case: If the user is hovering with the dragged row close to the
-   * top of the list, then we reveal the top empty row of EphyLangRow */
-  if (y < 20 && dragged_row != GTK_WIDGET (first_lang_row)) {
-    GtkWidget *top_revealer = ephy_lang_row_get_dnd_top_revealer (EPHY_LANG_ROW (hovered_row));
-
-    if (expanded_dnd_revealer)
-      gtk_revealer_set_reveal_child (GTK_REVEALER (expanded_dnd_revealer), FALSE);
-
-    gtk_revealer_set_reveal_child (GTK_REVEALER (top_revealer), TRUE);
-    g_object_set_data (G_OBJECT (widget), "dnd-expanded-revealer", top_revealer);
-
-    return TRUE;
-  }
-
-  if (dragged_row == hovered_row) {
-    if (expanded_dnd_revealer)
-      gtk_revealer_set_reveal_child (GTK_REVEALER (expanded_dnd_revealer), FALSE);
-
-    g_object_set_data (G_OBJECT (widget), "dnd-expanded-revealer", NULL);
-
-    return TRUE;
-  }
-
-  if (EPHY_IS_LANG_ROW (hovered_row)) {
-    if (expanded_dnd_revealer)
-      gtk_revealer_set_reveal_child (GTK_REVEALER (expanded_dnd_revealer), FALSE);
-
-    gtk_revealer_set_reveal_child (GTK_REVEALER (hovered_row_dnd_revealer), TRUE);
-    g_object_set_data (G_OBJECT (widget), "dnd-expanded-revealer", hovered_row_dnd_revealer);
-    return TRUE;
-  } else {
-    if (expanded_dnd_revealer)
-      gtk_revealer_set_reveal_child (GTK_REVEALER (expanded_dnd_revealer), FALSE);
-
-    g_object_set_data (G_OBJECT (widget), "dnd-expanded-revealer", NULL);
-    return FALSE;
-  }
-}
-
-static GtkDialog *setup_add_language_dialog (PrefsGeneralPage *general_page);
+  general_page = EPHY_PREFS_GENERAL_PAGE (gtk_widget_get_ancestor (listbox, EPHY_TYPE_PREFS_GENERAL_PAGE));
 
-static void
-language_editor_add_button_release_event (GtkWidget        *button,
-                                          GdkEvent         *event,
-                                          PrefsGeneralPage *general_page)
-{
   if (general_page->add_lang_dialog == NULL) {
     GtkWindow *prefs_dialog;
     GtkDialog **add_lang_dialog;
 
-    prefs_dialog = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (general_page)));
+    prefs_dialog = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (general_page)));
     general_page->add_lang_dialog = setup_add_language_dialog (general_page);
     gtk_window_set_transient_for (GTK_WINDOW (general_page->add_lang_dialog), prefs_dialog);
 
@@ -294,17 +189,18 @@ language_editor_add_button_release_event (GtkWidget        *button,
 static void
 language_editor_add_function_buttons (PrefsGeneralPage *general_page)
 {
-  GtkWidget *box;
+  GtkWidget *row;
   GtkWidget *label;
 
-  box = gtk_event_box_new ();
+  row = gtk_list_box_row_new ();
   label = gtk_label_new (_("Add Language"));
-  g_signal_connect (box, "button-release-event", G_CALLBACK (language_editor_add_button_release_event), 
general_page);
-  gtk_container_add (GTK_CONTAINER (box), label);
-  gtk_widget_set_size_request (box, -1, 50);
-  gtk_widget_show_all (GTK_WIDGET (box));
+  gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), label);
+  gtk_widget_set_size_request (row, -1, 50);
 
-  gtk_list_box_insert (GTK_LIST_BOX (general_page->lang_listbox), box, -1);
+  gtk_list_box_append (GTK_LIST_BOX (general_page->lang_listbox), row);
+
+  g_signal_connect_object (general_page->lang_listbox, "row-activated",
+                           G_CALLBACK (language_editor_add_activated), row, 0);
 }
 
 static void
@@ -334,94 +230,34 @@ static void
 language_editor_delete_button_clicked_cb (EphyLangRow      *row,
                                           PrefsGeneralPage *general_page)
 {
-  gtk_container_remove (GTK_CONTAINER (general_page->lang_listbox), GTK_WIDGET (row));
+  gtk_list_box_remove (GTK_LIST_BOX (general_page->lang_listbox), GTK_WIDGET (row));
   language_editor_update_pref (general_page);
   language_editor_update_state (general_page);
 }
 
-void
-drag_data_get (GtkWidget        *widget,
-               GdkDragContext   *context,
-               GtkSelectionData *selection_data,
-               guint             info,
-               guint             time,
-               gpointer          data)
-{
-  GtkWidget *row = gtk_widget_get_ancestor (widget, EPHY_TYPE_LANG_ROW);
-
-  gtk_selection_data_set (selection_data,
-                          gdk_atom_intern_static_string ("EPHY_LANG_ROW"),
-                          32,
-                          (const guchar *)&row,
-                          sizeof (gpointer));
-}
-
 static void
-drag_begin (GtkWidget      *widget,
-            GdkDragContext *context,
-            gpointer        data)
-{
-  GtkWidget *row;
-  GtkAllocation alloc;
-  cairo_surface_t *surface;
-  cairo_t *cr;
-  int x, y;
-  double sx, sy;
-
-  row = gtk_widget_get_ancestor (widget, EPHY_TYPE_LANG_ROW);
-  gtk_widget_get_allocation (row, &alloc);
-  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, alloc.width, alloc.height);
-  cr = cairo_create (surface);
-
-  gtk_style_context_add_class (gtk_widget_get_style_context (row), "drag-icon");
-  gtk_widget_draw (row, cr);
-  gtk_style_context_remove_class (gtk_widget_get_style_context (row), "drag-icon");
-
-  gtk_widget_translate_coordinates (widget, row, 0, 0, &x, &y);
-  cairo_surface_get_device_scale (surface, &sx, &sy);
-  cairo_surface_set_device_offset (surface, -x * sy, -y * sy);
-  gtk_drag_set_icon_surface (context, surface);
-
-  cairo_destroy (cr);
-  cairo_surface_destroy (surface);
-
-  g_object_set_data (G_OBJECT (gtk_widget_get_parent (row)), "dragged-row", row);
-  gtk_style_context_add_class (gtk_widget_get_style_context (row), "drag-row");
-}
-
-static void
-drag_end (GtkWidget      *widget,
-          GdkDragContext *context,
-          gpointer        data)
+language_editor_move_row_cb (EphyLangRow      *row,
+                             EphyLangRow      *dest_row,
+                             PrefsGeneralPage *general_page)
 {
-  GtkWidget *lang_row;
-  GtkWidget *lang_listbox;
-  GtkWidget *dnd_expanded_revealer;
-
-  lang_row = gtk_widget_get_ancestor (widget, EPHY_TYPE_LANG_ROW);
-  lang_listbox = gtk_widget_get_parent (lang_row);
-  dnd_expanded_revealer = g_object_get_data (G_OBJECT (lang_listbox), "dnd-expanded-revealer");
+  int index = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (dest_row));
 
-  g_object_set_data (G_OBJECT (lang_listbox), "dragged-row", NULL);
-  gtk_style_context_remove_class (gtk_widget_get_style_context (lang_row), "drag-row");
+  g_object_ref (row);
+  gtk_list_box_remove (GTK_LIST_BOX (general_page->lang_listbox),
+                       GTK_WIDGET (row));
+  gtk_list_box_insert (GTK_LIST_BOX (general_page->lang_listbox),
+                       GTK_WIDGET (row), index);
+  g_object_unref (row);
 
-  if (dnd_expanded_revealer) {
-    gtk_revealer_set_reveal_child (GTK_REVEALER (dnd_expanded_revealer), FALSE);
-    g_object_set_data (G_OBJECT (lang_listbox), "dnd-expanded-revealer", NULL);
-  }
+  language_editor_update_pref (general_page);
 }
 
-static GtkTargetEntry entries[] = {
-  { "GTK_LIST_BOX_ROW", GTK_TARGET_SAME_APP, 0 }
-};
-
 static void
 language_editor_add (PrefsGeneralPage *general_page,
                      const char       *code,
                      const char       *desc)
 {
   GtkWidget *row;
-  GtkWidget *event_box;
   int len;
   int index;
 
@@ -443,16 +279,10 @@ language_editor_add (PrefsGeneralPage *general_page,
   row = ephy_lang_row_new ();
 
   ephy_lang_row_set_code (EPHY_LANG_ROW (row), code);
-  ephy_lang_row_set_title (EPHY_LANG_ROW (row), desc);
-  gtk_style_context_add_class (gtk_widget_get_style_context (row), "row");
-
-  event_box = ephy_lang_row_get_drag_event_box (EPHY_LANG_ROW (row));
-  gtk_drag_source_set (event_box, GDK_BUTTON1_MASK, entries, 1, GDK_ACTION_MOVE);
-  g_signal_connect (event_box, "drag-begin", G_CALLBACK (drag_begin), general_page);
-  g_signal_connect (event_box, "drag-end", G_CALLBACK (drag_end), general_page);
-  g_signal_connect (event_box, "drag-data-get", G_CALLBACK (drag_data_get), general_page);
+  adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), desc);
 
   g_signal_connect (row, "delete-button-clicked", G_CALLBACK (language_editor_delete_button_clicked_cb), 
general_page);
+  g_signal_connect (row, "move-row", G_CALLBACK (language_editor_move_row_cb), general_page);
 
   gtk_list_box_insert (GTK_LIST_BOX (general_page->lang_listbox), row, len - 1);
 }
@@ -500,7 +330,8 @@ add_lang_dialog_response_cb (GtkWidget        *widget,
     language_editor_update_state (general_page);
   }
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
+  general_page->add_lang_dialog = NULL;
 }
 
 static void
@@ -555,11 +386,11 @@ setup_add_language_dialog (PrefsGeneralPage *general_page)
   GtkTreeIter iter;
   guint i, n;
   GtkBuilder *builder;
-  GtkWidget *prefs_dialog;
+  GtkWindow *prefs_dialog;
   g_auto (GStrv) locales;
 
   builder = gtk_builder_new_from_resource ("/org/gnome/epiphany/gtk/prefs-lang-dialog.ui");
-  prefs_dialog = gtk_widget_get_toplevel (GTK_WIDGET (general_page));
+  prefs_dialog = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (general_page)));
   ad = GTK_WIDGET (gtk_builder_get_object (builder, "add_language_dialog"));
   add_button = GTK_WIDGET (gtk_builder_get_object (builder, "add_button"));
   treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "languages_treeview"));
@@ -603,7 +434,7 @@ setup_add_language_dialog (PrefsGeneralPage *general_page)
   gtk_tree_sortable_set_sort_column_id
     (GTK_TREE_SORTABLE (sortmodel), COL_LANG_NAME, GTK_SORT_ASCENDING);
 
-  gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (prefs_dialog)),
+  gtk_window_group_add_window (gtk_window_get_group (prefs_dialog),
                                GTK_WINDOW (ad));
   gtk_window_set_modal (GTK_WINDOW (ad), TRUE);
 
@@ -715,13 +546,13 @@ download_folder_file_chooser_cb (GtkNativeDialog  *chooser,
 static void
 download_folder_row_activated_cb (PrefsGeneralPage *general_page)
 {
-  GtkWidget *parent;
+  GtkRoot *root;
   g_autofree char *downloads_path = NULL;
   GtkFileChooserNative *chooser;
 
-  parent = gtk_widget_get_toplevel (GTK_WIDGET (general_page));
+  root = gtk_widget_get_root (GTK_WIDGET (general_page));
   chooser = gtk_file_chooser_native_new (_("Select a Directory"),
-                                         GTK_WINDOW (parent),
+                                         GTK_WINDOW (root),
                                          GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
                                          _("_Select"),
                                          _("_Cancel"));
@@ -735,9 +566,9 @@ download_folder_row_activated_cb (PrefsGeneralPage *general_page)
 
     downloads_dir = g_file_new_for_path (downloads_path);
 
-    gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (chooser),
-                                              downloads_dir,
-                                              &error);
+    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
+                                         downloads_dir,
+                                         &error);
 
     if (error)
       g_warning ("Failed to set current folder %s: %s", downloads_path, error->message);
@@ -794,14 +625,14 @@ save_web_application (PrefsGeneralPage *general_page)
   if (!general_page->webapp)
     return G_SOURCE_REMOVE;
 
-  text = gtk_entry_get_text (GTK_ENTRY (general_page->webapp_url));
+  text = gtk_editable_get_text (GTK_EDITABLE (general_page->webapp_url));
   if (g_strcmp0 (general_page->webapp->url, text) != 0) {
     g_free (general_page->webapp->url);
     general_page->webapp->url = g_strdup (text);
     changed = TRUE;
   }
 
-  text = gtk_entry_get_text (GTK_ENTRY (general_page->webapp_title));
+  text = gtk_editable_get_text (GTK_EDITABLE (general_page->webapp_title));
   if (g_strcmp0 (general_page->webapp->name, text) != 0) {
     g_free (general_page->webapp->name);
     general_page->webapp->name = g_strdup (text);
@@ -840,9 +671,7 @@ prefs_general_page_update_webapp_icon (PrefsGeneralPage *general_page,
   if (!icon)
     return;
 
-  gtk_image_set_from_gicon (GTK_IMAGE (general_page->webapp_icon),
-                            G_ICON (icon),
-                            GTK_ICON_SIZE_DND);
+  gtk_image_set_from_gicon (GTK_IMAGE (general_page->webapp_icon), G_ICON (icon));
   gtk_image_set_pixel_size (GTK_IMAGE (general_page->webapp_icon), 32);
 
   g_object_set_data_full (G_OBJECT (general_page->webapp_icon), "ephy-webapp-icon-path",
@@ -875,7 +704,7 @@ on_webapp_entry_changed (GtkEditable      *editable,
 }
 
 void
-prefs_general_page_on_pd_delete_event (PrefsGeneralPage *general_page)
+prefs_general_page_on_pd_close_request (PrefsGeneralPage *general_page)
 {
   if (general_page->webapp_save_id) {
     g_source_remove (general_page->webapp_save_id);
@@ -935,15 +764,15 @@ on_webapp_icon_button_clicked (GtkWidget        *button,
 }
 
 static void
-custom_homepage_entry_changed (GtkEntry         *entry,
+custom_homepage_entry_changed (GtkEditable      *editable,
                                PrefsGeneralPage *general_page)
 {
-  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (general_page->custom_homepage_radiobutton))) {
+  if (gtk_check_button_get_active (GTK_CHECK_BUTTON (general_page->custom_homepage_radiobutton))) {
     g_settings_set_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL,
-                           gtk_entry_get_text (entry));
-  } else if ((gtk_entry_get_text (entry) != NULL) &&
-             gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (general_page->new_tab_homepage_radiobutton))) {
-    g_settings_set_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL, gtk_entry_get_text (entry));
+                           gtk_editable_get_text (editable));
+  } else if ((gtk_editable_get_text (editable) != NULL) &&
+             gtk_check_button_get_active (GTK_CHECK_BUTTON (general_page->new_tab_homepage_radiobutton))) {
+    g_settings_set_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL, gtk_editable_get_text (editable));
     gtk_widget_set_sensitive (general_page->custom_homepage_entry, TRUE);
     gtk_widget_grab_focus (general_page->custom_homepage_entry);
   }
@@ -954,7 +783,7 @@ custom_homepage_entry_icon_released (GtkEntry             *entry,
                                      GtkEntryIconPosition  icon_pos)
 {
   if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
-    gtk_entry_set_text (entry, "");
+    gtk_editable_set_text (GTK_EDITABLE (entry), "");
 }
 
 static gboolean
@@ -982,7 +811,7 @@ new_tab_homepage_set_mapping (const GValue       *value,
     return NULL;
 
   /* In case the new tab button is pressed while there's text in the custom homepage entry */
-  gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+  gtk_editable_set_text (GTK_EDITABLE (general_page->custom_homepage_entry), "");
   gtk_widget_set_sensitive (general_page->custom_homepage_entry, FALSE);
 
   return g_variant_new_string ("");
@@ -1012,7 +841,7 @@ blank_homepage_set_mapping (const GValue       *value,
   if (!g_value_get_boolean (value))
     return NULL;
 
-  gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+  gtk_editable_set_text (GTK_EDITABLE (general_page->custom_homepage_entry), "");
 
   return g_variant_new_string ("about:newtab");
 }
@@ -1040,17 +869,17 @@ custom_homepage_set_mapping (const GValue       *value,
 
   if (!g_value_get_boolean (value)) {
     gtk_widget_set_sensitive (general_page->custom_homepage_entry, FALSE);
-    gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+    gtk_editable_set_text (GTK_EDITABLE (general_page->custom_homepage_entry), "");
     return NULL;
   }
 
   gtk_widget_set_sensitive (general_page->custom_homepage_entry, TRUE);
   gtk_widget_grab_focus (general_page->custom_homepage_entry);
-  setting = gtk_entry_get_text (GTK_ENTRY (general_page->custom_homepage_entry));
+  setting = gtk_editable_get_text (GTK_EDITABLE (general_page->custom_homepage_entry));
   if (!setting || setting[0] == '\0')
     return NULL;
 
-  gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), setting);
+  gtk_editable_set_text (GTK_EDITABLE (general_page->custom_homepage_entry), setting);
 
   return g_variant_new_string (setting);
 }
@@ -1063,7 +892,7 @@ on_manage_webapp_additional_urls_button_clicked (GtkWidget        *button,
   GtkWindow *prefs_dialog;
 
   urls_dialog = ephy_webapp_additional_urls_dialog_new ();
-  prefs_dialog = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (general_page)));
+  prefs_dialog = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (general_page)));
 
   gtk_window_set_transient_for (GTK_WINDOW (urls_dialog), prefs_dialog);
   gtk_window_set_modal (GTK_WINDOW (urls_dialog), TRUE);
@@ -1139,10 +968,6 @@ init_lang_listbox (PrefsGeneralPage *general_page)
   char **list = NULL;
   int i;
 
-  gtk_drag_dest_set (general_page->lang_listbox, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, entries, 
1, GDK_ACTION_MOVE);
-  g_signal_connect (general_page->lang_listbox, "drag-data-received", G_CALLBACK (drag_data_received), 
general_page);
-  g_signal_connect (general_page->lang_listbox, "drag-motion", G_CALLBACK (drag_motion), NULL);
-
   list = g_settings_get_strv (EPHY_SETTINGS_WEB,
                               EPHY_PREFS_WEB_LANGUAGE);
 
@@ -1182,8 +1007,8 @@ setup_general_page (PrefsGeneralPage *general_page)
 
     if (!g_settings_get_boolean (EPHY_SETTINGS_WEB_APP, EPHY_PREFS_WEB_APP_SYSTEM)) {
       prefs_general_page_update_webapp_icon (general_page, general_page->webapp->icon_path);
-      gtk_entry_set_text (GTK_ENTRY (general_page->webapp_url), general_page->webapp->url);
-      gtk_entry_set_text (GTK_ENTRY (general_page->webapp_title), general_page->webapp->name);
+      gtk_editable_set_text (GTK_EDITABLE (general_page->webapp_url), general_page->webapp->url);
+      gtk_editable_set_text (GTK_EDITABLE (general_page->webapp_title), general_page->webapp->name);
     }
   }
 
@@ -1235,13 +1060,13 @@ setup_general_page (PrefsGeneralPage *general_page)
                                 general_page,
                                 NULL);
 
-  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (general_page->custom_homepage_radiobutton))) {
+  if (gtk_check_button_get_active (GTK_CHECK_BUTTON (general_page->custom_homepage_radiobutton))) {
     gtk_widget_set_sensitive (general_page->custom_homepage_entry, TRUE);
-    gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry),
-                        g_settings_get_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL));
+    gtk_editable_set_text (GTK_EDITABLE (general_page->custom_homepage_entry),
+                           g_settings_get_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL));
   } else {
     gtk_widget_set_sensitive (general_page->custom_homepage_entry, FALSE);
-    gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+    gtk_editable_set_text (GTK_EDITABLE (general_page->custom_homepage_entry), "");
   }
 
   g_signal_connect (general_page->custom_homepage_entry, "changed",
diff --git a/src/preferences/prefs-general-page.h b/src/preferences/prefs-general-page.h
index c56fbf452..040f2c15d 100644
--- a/src/preferences/prefs-general-page.h
+++ b/src/preferences/prefs-general-page.h
@@ -20,8 +20,7 @@
 
 #pragma once
 
-#include <glib-object.h>
-#include <handy.h>
+#include <adwaita.h>
 
 #include "ephy-prefs-dialog.h"
 
@@ -29,8 +28,8 @@ G_BEGIN_DECLS
 
 #define EPHY_TYPE_PREFS_GENERAL_PAGE (prefs_general_page_get_type ())
 
-G_DECLARE_FINAL_TYPE (PrefsGeneralPage, prefs_general_page, EPHY, PREFS_GENERAL_PAGE, HdyPreferencesPage)
+G_DECLARE_FINAL_TYPE (PrefsGeneralPage, prefs_general_page, EPHY, PREFS_GENERAL_PAGE, AdwPreferencesPage)
 
-void prefs_general_page_on_pd_delete_event (PrefsGeneralPage *general_page);
+void prefs_general_page_on_pd_close_request (PrefsGeneralPage *general_page);
 
 G_END_DECLS
diff --git a/src/preferences/prefs-privacy-page.c b/src/preferences/prefs-privacy-page.c
index d8caea0e9..1c85c5d8e 100644
--- a/src/preferences/prefs-privacy-page.c
+++ b/src/preferences/prefs-privacy-page.c
@@ -34,7 +34,7 @@ enum {
 };
 
 struct _PrefsPrivacyPage {
-  HdyPreferencesPage parent_instance;
+  AdwPreferencesPage parent_instance;
 
   /* Web Safety */
   GtkWidget *safe_browsing_group;
@@ -53,7 +53,7 @@ struct _PrefsPrivacyPage {
 
 static guint signals[LAST_SIGNAL];
 
-G_DEFINE_TYPE (PrefsPrivacyPage, prefs_privacy_page, HDY_TYPE_PREFERENCES_PAGE)
+G_DEFINE_TYPE (PrefsPrivacyPage, prefs_privacy_page, ADW_TYPE_PREFERENCES_PAGE)
 
 static void
 on_passwords_row_activated (GtkWidget        *row,
diff --git a/src/preferences/prefs-privacy-page.h b/src/preferences/prefs-privacy-page.h
index 2a07a2234..7a0c71057 100644
--- a/src/preferences/prefs-privacy-page.h
+++ b/src/preferences/prefs-privacy-page.h
@@ -20,13 +20,12 @@
 
 #pragma once
 
-#include <glib-object.h>
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define EPHY_TYPE_PREFS_PRIVACY_PAGE (prefs_privacy_page_get_type ())
 
-G_DECLARE_FINAL_TYPE (PrefsPrivacyPage, prefs_privacy_page, EPHY, PREFS_PRIVACY_PAGE, HdyPreferencesPage)
+G_DECLARE_FINAL_TYPE (PrefsPrivacyPage, prefs_privacy_page, EPHY, PREFS_PRIVACY_PAGE, AdwPreferencesPage)
 
 G_END_DECLS
diff --git a/src/resources/about.css b/src/resources/about.css
index e3138b92b..24bacc0be 100644
--- a/src/resources/about.css
+++ b/src/resources/about.css
@@ -2,8 +2,8 @@
 
 html {
     /* Adwaita colors */
-    --bg-color: #f6f5f4;
-    --fg-color: #2e3436;
+    --bg-color: #fafafa;
+    --fg-color: rgba(0, 0, 0, 0.8);
     --base-color: #fff;
     --text-color: #000;
     --borders: #d3d7cf;
@@ -20,9 +20,9 @@ html {
 @media (prefers-color-scheme: dark) {
     html {
         /* Adwaita colors */
-        --bg-color: #353535;
-        --fg-color: #eeeeec;
-        --base-color: #2d2d2d;
+        --bg-color: #242424;
+        --fg-color: #fff;
+        --base-color: rgba(255, 255, 255, 0.08);
         --text-color: #fff;
         --borders: #1b1b1b;
         --icon-invert: 0.93; /* average(0xee, 0xee, 0xec) / 0xff */
@@ -47,8 +47,8 @@ html {
 }
 
 h1 {
-    font-size: 24pt;
-    font-weight: 300;
+    font-size: 20pt;
+    font-weight: 800;
     margin-bottom: 0;
 }
 
@@ -70,8 +70,10 @@ h1 {
     .dialog {
         padding: 36px 42px;
         background-color: var(--base-color);
-        border-radius: 1em;
-        border: 2px solid var(--borders);
+        border-radius: 12px;
+        box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.03),
+                    0 1px 3px 1px rgba(0, 0, 0, 0.07),
+                    0 2px 6px 2px rgba(0, 0, 0, 0.03);
     }
 }
 
@@ -89,7 +91,6 @@ h1 {
 
 #about-subtitle {
     opacity: 0.55; /* Adwaita's dim-label */
-    text-shadow: 0 1px 0 var(--base-color);
 }
 
 #about-icon {
@@ -149,7 +150,6 @@ html.epiphany-html {
     font-size: 1rem;
     font-weight: bold;
     color: var(--fg-color);
-    text-shadow: 0 1px 0 var(--base-color);
 }
 
 @media (min-width: 600px) {
@@ -289,7 +289,6 @@ html.epiphany-html {
 
 .overview-item {
     position: relative;
-    overflow: hidden;
     outline: 0;
     transition: 0;
     text-align: center;
@@ -326,10 +325,13 @@ html.epiphany-html {
 .overview-thumbnail {
     height: 160px;
     display: block;
-    border: 1px solid rgba(0, 0, 0, .2);
     background: url(ephy-resource:///org/gnome/epiphany/page-icons/web-watermark.svg) center no-repeat, 
var(--base-color);
     background-repeat: no-repeat;
-    border-radius: 5px;
+    border-radius: 12px;
+    border-color: 1px solid transparent;
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.03),
+                0 1px 3px 1px rgba(0, 0, 0, 0.07);
+    transition: transform 150ms ease, box-shadow 150ms ease;
 }
 
 /* In order to preserve a wide aspect ratio as the layout changes, we need to reduce the height */
@@ -348,13 +350,15 @@ html.epiphany-html {
 
 .overview-thumbnail:hover,
 :focus .overview-thumbnail {
-    border: 1px solid rgba(0, 0, 0, .4);
-    box-shadow: 0 1px 1px rgba(0,0,0,0.3);
+    transform: translateY(-1px);
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.07),
+                0 1px 3px 1px rgba(0, 0, 0, 0.11),
+                0 2px 6px 2px rgba(0, 0, 0, 0.07);
 }
 
 .overview-thumbnail:active {
-    border: 1px solid rgba(0, 0, 0, .4);
-    box-shadow: inset 0 1px 1px rgba(0,0,0,0.3);
+    box-shadow: inset 0 1px 1px 0 rgba(0,0,0,0.07),
+                inset 0 1px 2px 1px rgba(0,0,0,0.11);
 }
 
 .overview-close-button {
diff --git a/src/resources/ephy-download-done-symbolic.svg b/src/resources/ephy-download-done-symbolic.svg
new file mode 100644
index 000000000..ebb9aa256
--- /dev/null
+++ b/src/resources/ephy-download-done-symbolic.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg";><path d="M12.754 
6.66c.176-.2.262-.46.246-.726A1.006 1.006 0 0 0 11.934 5c-.266.02-.512.14-.688.34l-4.297 5.195-2.242-2.242a1 
1 0 1 0-1.414 1.414l3 3a1.005 1.005 0 0 0 1.46-.047z" fill="#2e3436"/></svg>
\ No newline at end of file
diff --git a/src/resources/ephy-download-symbolic.svg b/src/resources/ephy-download-symbolic.svg
index d4a5a1259..edca8058f 100644
--- a/src/resources/ephy-download-symbolic.svg
+++ b/src/resources/ephy-download-symbolic.svg
@@ -1,3 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg"; height="16" width="16">
-    <path overflow="visible" font-family="Bitstream Vera Sans" font-weight="400" 
style="line-height:normal;-inkscape-font-specification:'Bitstream Vera 
Sans';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-transform:none;marker:none"
 d="M7 1v7.563L5.719 7.28A1.015 1.015 0 0 0 5 7H4v1c0 .265.093.53.281.719l3 3 .282.281h.875l.28-.281 3-3A1.01 
1.01 0 0 0 12 8V7h-1c-.265 0-.53.093-.719.281L9 8.563V1Z" transform="translate(0 -1)" fill="#2e3436" 
color="#000"/>
-</svg>
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg";><path 
style="color:#000;fill:#000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none" d="M8 2.8a1 1 0 
0 0-1 1v5.786L5.207 7.793a1 1 0 1 0-1.414 1.414l3.5 3.5a1 1 0 0 0 1.414 0l3.5-3.5a1 1 0 0 0-1.414-1.414L9 
9.586V3.8a1 1 0 0 0-1-1Z"/></svg>
\ No newline at end of file
diff --git a/src/resources/epiphany.gresource.xml b/src/resources/epiphany.gresource.xml
index d72cdcc7b..7b0e8b499 100644
--- a/src/resources/epiphany.gresource.xml
+++ b/src/resources/epiphany.gresource.xml
@@ -15,6 +15,7 @@
     <file preprocess="xml-stripblanks" compressed="true">gtk/firefox-sync-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/history-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/lang-row.ui</file>
+    <file preprocess="xml-stripblanks" compressed="true">gtk/location-entry.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/notebook-context-menu.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/page-menu-popover.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/page-row.ui</file>
@@ -50,6 +51,7 @@
   </gresource>
   <gresource prefix="/org/gnome/Epiphany/icons">
     <file compressed="true" 
alias="scalable/actions/ephy-download-symbolic.svg">ephy-download-symbolic.svg</file>
+    <file compressed="true" 
alias="scalable/actions/ephy-download-done-symbolic.svg">ephy-download-done-symbolic.svg</file>
     <file compressed="true" 
alias="scalable/actions/ephy-bookmark-tag-symbolic.svg">ephy-bookmark-tag-symbolic.svg</file>
     <file compressed="true" 
alias="scalable/actions/ephy-reader-mode-symbolic.svg">ephy-reader-mode-symbolic.svg</file>
     <file compressed="true" 
alias="scalable/status/ephy-audio-muted-symbolic.svg">ephy-audio-muted-symbolic.svg</file>
@@ -61,11 +63,8 @@
     <file compressed="true" 
alias="scalable/status/ephy-webpage-symbolic.svg">ephy-webpage-symbolic.svg</file>
   </gresource>
   <gresource prefix="/org/gnome/Epiphany">
-    <file compressed="true">themes/shared.css</file>
-    <file compressed="true">themes/Adwaita.css</file>
-    <file compressed="true">themes/Adwaita-dark.css</file>
-    <file compressed="true">themes/elementary.css</file>
-    <file compressed="true">themes/HighContrast.css</file>
-    <file compressed="true">themes/HighContrastInverse.css</file>
+    <file compressed="true">style.css</file>
+    <file compressed="true">style-dark.css</file>
+    <file compressed="true">style-hc.css</file>
   </gresource>
 </gresources>
diff --git a/src/resources/error.css b/src/resources/error.css
index 1e6a77866..454874805 100644
--- a/src/resources/error.css
+++ b/src/resources/error.css
@@ -2,8 +2,8 @@
 
 html {
     /* Adwaita colors */
-    --bg-color: #f6f5f4;
-    --fg-color: #2e3436;
+    --bg-color: #fafafa;
+    --fg-color: rgba(0, 0, 0, 0.8);
     --base-color: #fff;
     --text-color: #000;
     --borders: #d3d7cf;
@@ -24,9 +24,9 @@ html {
 @media (prefers-color-scheme: dark) {
     html {
         /* Adwaita colors */
-        --bg-color: #353535;
-        --fg-color: #eeeeec;
-        --base-color: #2d2d2d;
+        --bg-color: #242424;
+        --fg-color: #fff;
+        --base-color: rgba(255, 255, 255, 0.08);
         --text-color: #fff;
         --borders: #1b1b1b;
         --error-color: #f66151;
@@ -81,8 +81,8 @@ html {
 
 #msg-title {
     text-align: center;
-    font-size: 24pt;
-    font-weight: 300;
+    font-size: 20pt;
+    font-weight: 800;
 }
 
 #msg-icon {
diff --git a/src/resources/gtk/action-bar-end.ui b/src/resources/gtk/action-bar-end.ui
index c11e194b3..0f68d7f7a 100644
--- a/src/resources/gtk/action-bar-end.ui
+++ b/src/resources/gtk/action-bar-end.ui
@@ -4,45 +4,23 @@
     <property name="spacing">6</property>
     <child>
       <object class="GtkBox" id="browser_action_box">
-        <property name="visible">True</property>
         <property name="spacing">6</property>
       </object>
     </child>
     <child>
       <object class="GtkRevealer" id="downloads_revealer">
-        <property name="visible">True</property>
         <property name="transition-type">crossfade</property>
         <child>
           <object class="GtkMenuButton" id="downloads_button">
             <!-- Translators: tooltip for the downloads button -->
             <property name="tooltip_text" translatable="yes">View downloads</property>
-            <property name="visible">True</property>
             <property name="receives_default">True</property>
+            <style>
+              <class name="image-button"/>
+            </style>
             <child>
-              <object class="GtkOverlay">
-                <property name="visible">True</property>
+              <object class="GtkImage" id="downloads_icon">
                 <property name="valign">center</property>
-                <child>
-                  <object class="GtkImage" id="downloads_icon">
-                    <property name="visible">True</property>
-                    <property name="icon_name">ephy-download-symbolic</property>
-                    <style>
-                      <class name="download_icon"/>
-                    </style>
-                  </object>
-                </child>
-                <child type="overlay">
-                  <object class="GtkProgressBar" id="downloads_progress">
-                    <property name="visible">True</property>
-                    <property name="valign">end</property>
-                    <style>
-                      <class name="download_progress"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="pass-through">True</property>
-                  </packing>
-                </child>
               </object>
             </child>
           </object>
@@ -51,41 +29,20 @@
     </child>
     <child>
       <object class="GtkMenuButton" id="bookmark_button">
-        <property name="visible">True</property>
         <!-- Translators: tooltip for the bookmark button -->
         <property name="tooltip_text" translatable="yes">Bookmark page</property>
-        <child>
-          <object class="GtkImage" id="bookmark_image">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="icon_name">non-starred-symbolic</property>
-          </object>
-        </child>
-        <style>
-          <class name="image-button"/>
-        </style>
+        <property name="icon-name">non-starred-symbolic</property>
       </object>
     </child>
     <child>
       <object class="GtkMenuButton" id="bookmarks_button">
-        <property name="visible">True</property>
         <!-- Translators: tooltip for the bookmarks popover button -->
         <property name="tooltip_text" translatable="yes">View and manage your bookmarks</property>
-        <property name="popover">bookmarks_popover</property>
-        <style>
-          <class name="image-button"/>
-        </style>
-        <child>
-          <object class="GtkImage" id="bookmarks_image">
-            <property name="visible">True</property>
-            <property name="icon-name">ephy-library-symbolic</property>
-            <property name="icon-size">1</property>
-          </object>
-        </child>
+        <property name="icon-name">ephy-library-symbolic</property>
+        <property name="popover">
+          <object class="EphyBookmarksPopover" id="bookmarks_popover"/>
+        </property>
       </object>
     </child>
   </template>
-  <object class="EphyBookmarksPopover" id="bookmarks_popover">
-    <property name="visible">True</property>
-  </object>
 </interface>
diff --git a/src/resources/gtk/action-bar-start.ui b/src/resources/gtk/action-bar-start.ui
index 3c8b22a10..d7e638296 100644
--- a/src/resources/gtk/action-bar-start.ui
+++ b/src/resources/gtk/action-bar-start.ui
@@ -4,44 +4,63 @@
     <property name="spacing">6</property>
     <child>
       <object class="GtkBox" id="navigation_box">
-        <property name="visible">True</property>
         <property name="orientation">horizontal</property>
+        <property name="spacing">6</property>
         <style>
-          <class name="linked"/>
           <class name="navigation-box"/>
         </style>
         <child>
           <object class="GtkButton" id="navigation_back">
-            <property name="visible">True</property>
             <property name="action-name">toolbar.navigation-back</property>
+            <property name="icon-name">go-previous-symbolic</property>
             <!-- Translators: tooltip for the back button -->
             <property name="tooltip_text" translatable="yes">Go back to the previous page</property>
-            <style>
-              <class name="image-button"/>
-            </style>
             <child>
-              <object class="GtkImage" id="back_image">
-                <property name="visible">True</property>
-                <property name="icon-name">go-previous-symbolic</property>
-                <property name="icon-size">1</property>
+              <object class="GtkGestureClick">
+                <!-- GDK_BUTTON_MIDDLE -->
+                <property name="button">2</property>
+                <signal name="pressed" handler="middle_click_pressed_cb"/>
+                <signal name="released" handler="middle_click_released_cb"/>
+              </object>
+            </child>
+            <child>
+              <object class="GtkGestureClick">
+                <!-- GDK_BUTTON_SECONDARY -->
+                <property name="button">3</property>
+                <signal name="pressed" handler="right_click_pressed_cb"/>
+              </object>
+            </child>
+            <child>
+              <object class="GtkGestureLongPress">
+                <signal name="pressed" handler="long_pressed_cb"/>
               </object>
             </child>
           </object>
         </child>
         <child>
           <object class="GtkButton" id="navigation_forward">
-            <property name="visible">True</property>
             <property name="action-name">toolbar.navigation-forward</property>
+            <property name="icon-name">go-next-symbolic</property>
             <!-- Translators: tooltip for the forward button -->
             <property name="tooltip_text" translatable="yes">Go forward to the next page</property>
-            <style>
-              <class name="image-button"/>
-            </style>
             <child>
-              <object class="GtkImage" id="forward_image">
-                <property name="visible">True</property>
-                <property name="icon-name">go-next-symbolic</property>
-                <property name="icon-size">1</property>
+              <object class="GtkGestureClick">
+                <!-- GDK_BUTTON_MIDDLE -->
+                <property name="button">2</property>
+                <signal name="pressed" handler="middle_click_pressed_cb"/>
+                <signal name="released" handler="middle_click_released_cb"/>
+              </object>
+            </child>
+            <child>
+              <object class="GtkGestureClick">
+                <!-- GDK_BUTTON_SECONDARY -->
+                <property name="button">3</property>
+                <signal name="pressed" handler="right_click_pressed_cb"/>
+              </object>
+            </child>
+            <child>
+              <object class="GtkGestureLongPress">
+                <signal name="pressed" handler="long_pressed_cb"/>
               </object>
             </child>
           </object>
@@ -50,60 +69,44 @@
     </child>
     <child>
       <object class="GtkButton" id="combined_stop_reload_button">
-        <property name="visible">True</property>
         <property name="action-name">toolbar.combined-stop-reload</property>
-        <style>
-          <class name="image-button"/>
-        </style>
-        <child>
-          <object class="GtkImage" id="combined_stop_reload_image">
-            <property name="visible">True</property>
-            <property name="icon-name">view-refresh-symbolic</property>
-            <property name="icon-size">1</property>
-          </object>
-        </child>
+        <property name="icon-name">view-refresh-symbolic</property>
       </object>
     </child>
     <child>
       <object class="GtkButton" id="homepage_button">
-        <property name="visible">True</property>
         <property name="action-name">win.home</property>
+        <property name="icon-name">go-home-symbolic</property>
         <!-- Translators: tooltip for the secret homepage button -->
         <property name="tooltip_text" translatable="yes">Go to your homepage</property>
-        <style>
-          <class name="image-button"/>
-        </style>
         <child>
-          <object class="GtkImage" id="homepage_image">
-            <property name="visible">True</property>
-            <property name="icon-name">go-home-symbolic</property>
-            <property name="icon-size">1</property>
+          <object class="GtkGestureClick">
+            <!-- GDK_BUTTON_MIDDLE -->
+            <property name="button">2</property>
+            <signal name="pressed" handler="middle_click_pressed_cb"/>
+            <signal name="released" handler="middle_click_released_cb"/>
           </object>
         </child>
       </object>
     </child>
     <child>
       <object class="GtkButton" id="new_tab_button">
-        <property name="visible">True</property>
         <property name="action-name">win.new-tab</property>
+        <property name="icon-name">tab-new-symbolic</property>
         <!-- Translators: tooltip for the new tab button -->
         <property name="tooltip_text" translatable="yes">Open a new tab</property>
-        <style>
-          <class name="image-button"/>
-        </style>
         <child>
-          <object class="GtkImage" id="new_tab_image">
-            <property name="visible">True</property>
-            <property name="icon-name">tab-new-symbolic</property>
-            <property name="icon-size">1</property>
+          <object class="GtkGestureClick">
+            <!-- GDK_BUTTON_MIDDLE -->
+            <property name="button">2</property>
+            <signal name="pressed" handler="middle_click_pressed_cb"/>
+            <signal name="released" handler="middle_click_released_cb"/>
           </object>
         </child>
       </object>
     </child>
     <child>
-      <object class="GtkBox" id="placeholder">
-        <property name="visible">True</property>
-      </object>
+      <object class="GtkBox" id="placeholder"/>
     </child>
   </template>
 </interface>
diff --git a/src/resources/gtk/action-bar.ui b/src/resources/gtk/action-bar.ui
index 102cd5f71..dc1851c20 100644
--- a/src/resources/gtk/action-bar.ui
+++ b/src/resources/gtk/action-bar.ui
@@ -1,42 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <template class="EphyActionBar" parent="GtkBin">
+  <template class="EphyActionBar" parent="AdwBin">
     <child>
       <object class="GtkRevealer" id="revealer">
-        <property name="visible">True</property>
         <property name="transition-type">slide-up</property>
         <child>
           <object class="GtkActionBar" id="action_bar">
-            <property name="visible">True</property>
-            <child>
-              <object class="EphyActionBarStart" id="action_bar_start">
-                <property name="visible">True</property>
-              </object>
-              <packing>
-                <property name="pack-type">start</property>
-              </packing>
+            <child type="start">
+              <object class="EphyActionBarStart" id="action_bar_start"/>
             </child>
-            <child>
+            <child type="end">
               <object class="EphyPagesButton" id="pages_button">
-                <property name="visible">True</property>
                 <!-- Translators: tooltip for the page switcher button -->
                 <property name="tooltip_text" translatable="yes">View open pages</property>
                 <property name="action_name">win.tabs-view</property>
-                <style>
-                  <class name="image-button"/>
-                </style>
               </object>
-              <packing>
-                <property name="pack-type">end</property>
-              </packing>
             </child>
-            <child>
-              <object class="EphyActionBarEnd" id="action_bar_end">
-                <property name="visible">True</property>
-              </object>
-              <packing>
-                <property name="pack-type">end</property>
-              </packing>
+            <child type="end">
+              <object class="EphyActionBarEnd" id="action_bar_end"/>
             </child>
           </object>
         </child>
diff --git a/src/resources/gtk/bookmark-properties.ui b/src/resources/gtk/bookmark-properties.ui
index a89a90514..e5e6ee92a 100644
--- a/src/resources/gtk/bookmark-properties.ui
+++ b/src/resources/gtk/bookmark-properties.ui
@@ -3,7 +3,6 @@
 <interface>
   <requires lib="gtk+" version="3.18"/>
   <template class="EphyBookmarkProperties" parent="GtkBox">
-    <property name="visible">True</property>
     <property name="margin-top">12</property>
     <property name="margin-bottom">12</property>
     <property name="margin-start">12</property>
@@ -12,7 +11,6 @@
     <property name="spacing">6</property>
     <child>
       <object class="GtkLabel" id="popover_bookmark_label">
-        <property name="visible">True</property>
         <property name="label" translatable="yes">Bookmark</property>
         <attributes>
           <attribute name="weight" value="bold"/>
@@ -21,7 +19,6 @@
     </child>
     <child>
       <object class="GtkLabel">
-        <property name="visible">True</property>
         <property name="xalign">0</property>
         <property name="label" translatable="yes">_Name</property>
         <property name="use-underline">True</property>
@@ -33,13 +30,11 @@
     </child>
     <child>
       <object class="GtkEntry" id="name_entry">
-        <property name="visible">True</property>
         <property name="max_width_chars">32</property>
       </object>
     </child>
     <child>
       <object class="GtkLabel" id="address_label">
-        <property name="visible">True</property>
         <property name="xalign">0</property>
         <property name="margin_top">6</property>
         <property name="label" translatable="yes">_Address</property>
@@ -51,13 +46,10 @@
       </object>
     </child>
     <child>
-      <object class="GtkEntry" id="address_entry">
-        <property name="visible">True</property>
-      </object>
+      <object class="GtkEntry" id="address_entry"/>
     </child>
     <child>
       <object class="GtkLabel" id="popover_tags_label">
-        <property name="visible">True</property>
         <property name="xalign">0</property>
         <property name="margin_top">12</property>
         <property name="label" translatable="yes">Tags</property>
@@ -68,7 +60,6 @@
     </child>
     <child>
       <object class="GtkScrolledWindow" id="tags_scrolled_window">
-        <property name="visible">True</property>
         <property name="vexpand">True</property>
         <property name="min_content_height">38</property>
         <property name="max_content_width">270</property>
@@ -77,7 +68,6 @@
         <property name="propagate_natural_height">True</property>
         <child>
           <object class="GtkFlowBox" id="tags_box">
-            <property name="visible">True</property>
             <property name="homogeneous">True</property>
             <property name="valign">start</property>
             <property name="column_spacing">6</property>
@@ -91,10 +81,8 @@
     </child>
     <child>
       <object class="GtkBox">
-        <property name="visible">True</property>
         <child>
           <object class="GtkEntry" id="add_tag_entry">
-            <property name="visible">True</property>
             <property name="hexpand">True</property>
             <property name="activates_default">True</property>
             <property name="placeholder_text" translatable="yes">Add Tag…</property>
@@ -103,8 +91,6 @@
         <child>
           <object class="GtkButton" id="add_tag_button">
             <property name="label" translatable="yes">_Add</property>
-            <property name="visible">True</property>
-            <property name="can_default">True</property>
             <property name="receives_default">False</property>
             <property name="action_name">bookmark-properties.add-tag</property>
             <property name="use_underline">True</property>
@@ -118,7 +104,6 @@
     <child>
       <object class="GtkButton" id="remove_bookmark_button">
         <property name="label" translatable="yes">_Remove</property>
-        <property name="visible">True</property>
         <property name="receives_default">False</property>
         <property name="halign">end</property>
         <property name="action_name">bookmark-properties.remove-bookmark</property>
diff --git a/src/resources/gtk/bookmark-row.ui b/src/resources/gtk/bookmark-row.ui
index b3f29d8de..506dfbd5d 100644
--- a/src/resources/gtk/bookmark-row.ui
+++ b/src/resources/gtk/bookmark-row.ui
@@ -3,20 +3,16 @@
 <interface>
   <requires lib="gtk+" version="3.18"/>
   <template class="EphyBookmarkRow" parent="GtkListBoxRow">
-    <property name="visible">True</property>
     <child>
       <object class="GtkBox">
-        <property name="visible">True</property>
         <property name="spacing">6</property>
         <child>
           <object class="GtkImage" id="favicon_image">
-            <property name="visible">True</property>
             <property name="margin-start">6</property>
           </object>
         </child>
         <child>
           <object class="GtkLabel" id="title_label">
-            <property name="visible">True</property>
             <property name="hexpand">True</property>
             <property name="ellipsize">end</property>
             <property name="max_width_chars">1</property>
@@ -26,13 +22,9 @@
         <child>
           <object class="GtkButton" id="properties_button">
             <property name="visible">True</property>
+            <property name="icon_name">emblem-system-symbolic</property>
             <property name="receives_default">False</property>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="icon_name">emblem-system-symbolic</property>
-              </object>
-            </child>
+            <property name="valign">center</property>
             <style>
               <class name="flat"/>
             </style>
diff --git a/src/resources/gtk/bookmarks-popover.ui b/src/resources/gtk/bookmarks-popover.ui
index 5925e0a47..e1f5a22ac 100644
--- a/src/resources/gtk/bookmarks-popover.ui
+++ b/src/resources/gtk/bookmarks-popover.ui
@@ -4,193 +4,177 @@
   <template class="EphyBookmarksPopover" parent="GtkPopover">
     <child>
       <object class="GtkBox">
-        <property name="margin-top">12</property>
-        <property name="margin-bottom">12</property>
-        <property name="margin-start">12</property>
-        <property name="margin-end">12</property>
+        <property name="margin-top">6</property>
         <property name="orientation">vertical</property>
         <property name="spacing">6</property>
-        <property name="visible">true</property>
         <child>
           <object class="GtkStack" id="toplevel_stack">
             <property name="vhomogeneous">false</property>
             <property name="interpolate-size">true</property>
-            <property name="transition-type">GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT</property>
-            <property name="visible">true</property>
+            <property name="transition-type">slide-left-right</property>
             <child>
-              <object class="GtkBox">
-                <property name="orientation">vertical</property>
-                <property name="spacing">6</property>
-                <property name="visible">true</property>
-                <child>
-                  <object class="GtkStackSwitcher">
-                    <property name="halign">center</property>
-                    <property name="stack">stack</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkStack" id="stack">
-                    <property name="vhomogeneous">false</property>
-                    <property name="interpolate-size">true</property>
-                    <property name="transition-type">GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT</property>
-                    <property name="visible">true</property>
+              <object class="GtkStackPage">
+                <property name="name">default</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
                     <child>
-                      <object class="GtkScrolledWindow">
-                        <property name="propagate-natural-height">True</property>
-                        <property name="max-content-height">400</property>
-                        <property name="hscrollbar-policy">never</property>
-                        <property name="visible">true</property>
+                      <object class="GtkStackSwitcher">
+                        <property name="halign">center</property>
+                        <property name="stack">stack</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkStack" id="stack">
+                        <property name="vhomogeneous">false</property>
+                        <property name="interpolate-size">true</property>
+                        <property name="transition-type">slide-left-right</property>
                         <child>
-                          <object class="GtkListBox" id="bookmarks_list_box">
-                            <property name="selection-mode">none</property>
-                            <property name="visible">true</property>
+                          <object class="GtkStackPage">
+                            <property name="name">all</property>
+                            <property name="title" translatable="yes">All</property>
+                            <property name="child">
+                              <object class="GtkScrolledWindow">
+                                <property name="propagate-natural-height">True</property>
+                                <property name="max-content-height">400</property>
+                                <property name="hscrollbar-policy">never</property>
+                                <child>
+                                  <object class="GtkListBox" id="bookmarks_list_box">
+                                    <property name="selection-mode">none</property>
+                                    <style>
+                                      <class name="navigation-sidebar"/>
+                                    </style>
+                                  </object>
+                                </child>
+                              </object>
+                            </property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkStackPage">
+                            <property name="name">tags</property>
+                            <property name="title" translatable="yes">Tags</property>
+                            <property name="child">
+                              <object class="GtkScrolledWindow">
+                                <property name="propagate-natural-height">True</property>
+                                <property name="max-content-height">400</property>
+                                <property name="hscrollbar-policy">never</property>
+                                <child>
+                                  <object class="GtkListBox" id="tags_list_box">
+                                    <property name="selection-mode">none</property>
+                                    <style>
+                                      <class name="navigation-sidebar"/>
+                                    </style>
+                                  </object>
+                                </child>
+                              </object>
+                            </property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">tag_detail</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkCenterBox">
+                        <child type="start">
+                          <object class="GtkButton" id="tag_detail_back_button">
+                            <property name="action-name">popover.tag-detail-back</property>
+                            <property name="icon-name">go-previous-symbolic</property>
+                            <property name="margin-start">6</property>
+                            <property name="margin-end">6</property>
                             <style>
-                              <class name="background"/>
+                              <class name="flat"/>
                             </style>
                           </object>
                         </child>
+                        <child type="center">
+                          <object class="GtkLabel" id="tag_detail_label">
+                            <property name="ellipsize">end</property>
+                            <property name="max-width-chars">0</property>
+                            <property name="hexpand">True</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                        </child>
                       </object>
-                      <packing>
-                        <property name="name">all</property>
-                        <property name="title" translatable="yes">All</property>
-                      </packing>
                     </child>
                     <child>
                       <object class="GtkScrolledWindow">
                         <property name="propagate-natural-height">True</property>
                         <property name="max-content-height">400</property>
                         <property name="hscrollbar-policy">never</property>
-                        <property name="visible">true</property>
                         <child>
-                          <object class="GtkListBox" id="tags_list_box">
+                          <object class="GtkListBox" id="tag_detail_list_box">
                             <property name="selection-mode">none</property>
-                            <property name="visible">true</property>
                             <style>
-                              <class name="background"/>
+                              <class name="navigation-sidebar"/>
                             </style>
                           </object>
                         </child>
                       </object>
-                      <packing>
-                        <property name="name">tags</property>
-                        <property name="title" translatable="yes">Tags</property>
-                      </packing>
                     </child>
                   </object>
-                </child>
+                </property>
               </object>
-              <packing>
-                <property name="name">default</property>
-              </packing>
             </child>
             <child>
-              <object class="GtkBox">
-                <property name="orientation">vertical</property>
-                <property name="spacing">6</property>
-                <property name="visible">true</property>
-                <child>
+              <object class="GtkStackPage">
+                <property name="name">empty-state</property>
+                <property name="child">
                   <object class="GtkBox">
-                    <property name="spacing">6</property>
-                    <property name="visible">true</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
                     <child>
-                      <object class="GtkButton" id="tag_detail_back_button">
-                        <property name="action-name">popover.tag-detail-back</property>
-                        <property name="visible">true</property>
+                      <object class="GtkImage">
+                        <property name="icon-name">non-starred-symbolic</property>
+                        <property name="pixel-size">64</property>
                         <style>
-                          <class name="image-button"/>
+                          <class name="dim-label"/>
                         </style>
-                        <child>
-                          <object class="GtkImage">
-                            <property name="icon-name">go-previous-symbolic</property>
-                            <property name="visible">true</property>
-                          </object>
-                        </child>
                       </object>
                     </child>
                     <child>
-                      <object class="GtkLabel" id="tag_detail_label">
-                        <property name="visible">true</property>
-                        <property name="ellipsize">end</property>
-                        <property name="max-width-chars">0</property>
-                        <property name="hexpand">True</property>
+                      <object class="GtkLabel" id="title">
+                        <property name="label" translatable="yes">No bookmarks yet?</property>
+                        <property name="max-width-chars">40</property>
+                        <property name="wrap">true</property>
+                        <property name="justify">center</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
                         <attributes>
+                          <attribute name="scale" value="2"/>
                           <attribute name="weight" value="bold"/>
                         </attributes>
                       </object>
                     </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkScrolledWindow">
-                    <property name="propagate-natural-height">True</property>
-                    <property name="max-content-height">400</property>
-                    <property name="hscrollbar-policy">never</property>
-                    <property name="visible">true</property>
                     <child>
-                      <object class="GtkListBox" id="tag_detail_list_box">
-                        <property name="selection-mode">none</property>
-                        <property name="visible">true</property>
+                      <object class="GtkLabel" id="subtitle">
+                        <property name="label" translatable="yes">Bookmark some webpages to view them 
here.</property>
+                        <property name="use-markup">true</property>
+                        <property name="max-width-chars">40</property>
+                        <property name="wrap">true</property>
+                        <property name="justify">center</property>
                         <style>
-                          <class name="background"/>
+                          <class name="dim-label"/>
                         </style>
                       </object>
                     </child>
                   </object>
-                </child>
-              </object>
-              <packing>
-                <property name="name">tag_detail</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkBox">
-                <property name="orientation">vertical</property>
-                <property name="spacing">12</property>
-                <property name="visible">true</property>
-                <child>
-                  <object class="GtkImage">
-                    <property name="icon-name">non-starred-symbolic</property>
-                    <property name="pixel-size">64</property>
-                    <property name="visible">true</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="title">
-                    <property name="label" translatable="yes">No bookmarks yet?</property>
-                    <property name="visible">true</property>
-                    <property name="max-width-chars">40</property>
-                    <property name="wrap">true</property>
-                    <property name="justify">center</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
-                    <attributes>
-                      <attribute name="scale" value="2"/>
-                      <attribute name="weight" value="bold"/>
-                    </attributes>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="subtitle">
-                    <property name="label" translatable="yes">Bookmark some webpages to view them 
here.</property>
-                    <property name="use-markup">true</property>
-                    <property name="visible">true</property>
-                    <property name="max-width-chars">40</property>
-                    <property name="wrap">true</property>
-                    <property name="justify">center</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
-                  </object>
-                </child>
+                </property>
               </object>
-              <packing>
-                <property name="name">empty-state</property>
-              </packing>
             </child>
           </object>
         </child>
diff --git a/src/resources/gtk/clear-data-view.ui b/src/resources/gtk/clear-data-view.ui
index 16d94f47f..319706436 100644
--- a/src/resources/gtk/clear-data-view.ui
+++ b/src/resources/gtk/clear-data-view.ui
@@ -31,27 +31,22 @@
     <signal name="notify::search-text" handler="search_text_changed_cb" swapped="yes"/>
     <child>
       <object class="GtkScrolledWindow" id="scrolled_window">
-        <property name="visible">True</property>
         <child>
-          <object class="HdyClamp">
-            <property name="visible">True</property>
+          <object class="AdwClamp">
             <property name="margin_start">6</property>
             <property name="margin_end">6</property>
             <child>
               <object class="GtkBox">
-                <property name="visible">True</property>
                 <property name="orientation">vertical</property>
                 <property name="margin_top">6</property>
                 <property name="margin_bottom">6</property>
                 <property name="spacing">12</property>
                 <child>
                   <object class="GtkBox">
-                    <property name="visible">True</property>
                     <property name="orientation">vertical</property>
                     <property name="spacing">6</property>
                     <child>
                       <object class="GtkLabel">
-                        <property name="visible">True</property>
                         <property name="label" translatable="yes">Clear selected personal data:</property>
                         <property name="use-underline">True</property>
                         <property name="xalign">0</property>
@@ -61,7 +56,6 @@
                     </child>
                     <child>
                       <object class="GtkScrolledWindow">
-                        <property name="visible">True</property>
                         <property name="vexpand">True</property>
                         <property name="min_content_height">100</property>
                         <style>
@@ -69,7 +63,6 @@
                         </style>
                         <child>
                           <object class="GtkTreeView" id="treeview">
-                            <property name="visible">True</property>
                             <property name="model">treemodelfilter</property>
                             <property name="headers-visible">False</property>
                             <child internal-child="selection">
@@ -109,7 +102,6 @@
                 </child>
                 <child>
                   <object class="GtkLabel">
-                    <property name="visible">True</property>
                     <property name="label" translatable="yes">You cannot undo this action. The data you are 
choosing to clear will be removed forever.</property>
                     <property name="justify">left</property>
                     <property name="xalign">0</property>
diff --git a/src/resources/gtk/data-view.ui b/src/resources/gtk/data-view.ui
index d46584ff7..ba9ac0a65 100644
--- a/src/resources/gtk/data-view.ui
+++ b/src/resources/gtk/data-view.ui
@@ -2,70 +2,40 @@
 <interface>
   <requires lib="gtk+" version="3.20"/>
   <requires lib="libhandy" version="0.0"/>
-  <object class="GtkImage" id="search_image">
-    <property name="visible">True</property>
-    <property name="icon_name">edit-find-symbolic</property>
-  </object>
-  <object class="GtkImage" id="back_image">
-    <property name="visible">True</property>
-    <property name="icon_name">go-previous-symbolic</property>
-  </object>
-  <template class="EphyDataView" parent="GtkBin">
+  <template class="EphyDataView" parent="GtkWidget">
     <child>
       <object class="GtkBox" id="box">
-        <property name="visible">True</property>
         <property name="orientation">vertical</property>
         <child>
-          <object class="HdyWindowHandle">
-            <property name="visible">True</property>
-            <child>
-              <object class="GtkHeaderBar" id="header_bar">
-                <property name="visible">True</property>
-                <property name="show_close_button">True</property>
-                <child>
-                  <object class="GtkButton" id="back_button">
-                    <property name="visible">True</property>
-                    <property name="image">back_image</property>
-                    <signal name="clicked" handler="on_back_button_clicked"/>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkToggleButton" id="search_button">
-                    <property name="visible">True</property>
-                    <property name="receives_default">True</property>
-                    <property name="image">search_image</property>
-                    <property name="always_show_image">True</property>
-                    <property name="active" bind-source="search_bar" bind-property="search-mode-enabled" 
bind-flags="sync-create|bidirectional"/>
-                    <style>
-                      <class name="image-button"/>
-                    </style>
-                    <child internal-child="accessible">
-                      <object class="AtkObject">
-                        <property name="AtkObject::accessible-name" translatable="yes">Search</property>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="pack_type">end</property>
-                  </packing>
-                </child>
+          <object class="GtkHeaderBar" id="header_bar">
+            <property name="title-widget">
+              <object class="AdwWindowTitle" id="window_title"/>
+            </property>
+            <child type="start">
+              <object class="GtkButton" id="back_button">
+                <property name="icon-name">go-previous-symbolic</property>
+                <signal name="clicked" handler="on_back_button_clicked"/>
+              </object>
+            </child>
+            <child type="end">
+              <object class="GtkToggleButton" id="search_button">
+                <property name="icon-name">edit-find-symbolic</property>
+                <property name="active" bind-source="search_bar" bind-property="search-mode-enabled" 
bind-flags="sync-create|bidirectional"/>
+                <property name="tooltip-text" translatable="yes">Search</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdySearchBar" id="search_bar">
-            <property name="visible">True</property>
+          <object class="GtkSearchBar" id="search_bar">
             <property name="search_mode_enabled">False</property>
+            <property name="key-capture-widget">EphyDataView</property>
             <child>
-              <object class="HdyClamp">
-                <property name="visible">True</property>
+              <object class="AdwClamp">
+                <property name="hexpand">True</property>
                 <child>
                   <object class="GtkSearchEntry" id="search_entry">
-                    <property name="visible">True</property>
-                    <property name="primary_icon_name">edit-find-symbolic</property>
-                    <property name="primary_icon_activatable">False</property>
-                    <property name="primary_icon_sensitive">False</property>
+                    <property name="placeholder-text" bind-source="EphyDataView" 
bind-property="search-description" bind-flags="sync-create"/>
                     <signal name="search-changed" handler="on_search_entry_changed" swapped="no"/>
                   </object>
                 </child>
@@ -76,52 +46,51 @@
         <child>
           <object class="GtkStack" id="stack">
             <property name="vexpand">True</property>
-            <property name="visible">True</property>
             <child>
-              <object class="GtkSpinner" id="spinner">
-                <property name="visible">True</property>
-              </object>
-              <packing>
+              <object class="GtkStackPage">
                 <property name="name">loading</property>
-              </packing>
+                <property name="child">
+                  <object class="GtkSpinner" id="spinner">
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                    <property name="width-request">32</property>
+                    <property name="height-request">32</property>
+                  </object>
+                </property>
+              </object>
             </child>
             <child>
-              <object class="HdyStatusPage" id="empty_page">
-                <property name="visible">True</property>
-              </object>
-              <packing>
+              <object class="GtkStackPage">
                 <property name="name">empty</property>
-              </packing>
+                <property name="child">
+                  <object class="AdwStatusPage" id="empty_page"/>
+                </property>
+              </object>
             </child>
             <child>
-              <object class="HdyStatusPage">
-                <property name="visible">True</property>
-                <property name="icon_name">edit-find-symbolic</property>
-                <property name="title" translatable="yes">No Results Found</property>
-                <property name="description" translatable="yes">Try a different search</property>
-              </object>
-              <packing>
+              <object class="GtkStackPage">
                 <property name="name">no-results</property>
-              </packing>
+                <property name="child">
+                  <object class="AdwStatusPage">
+                    <property name="icon_name">edit-find-symbolic</property>
+                    <property name="title" translatable="yes">No Results Found</property>
+                    <property name="description" translatable="yes">Try a different search</property>
+                  </object>
+                </property>
+              </object>
             </child>
           </object>
         </child>
         <child>
           <object class="GtkActionBar">
-            <property name="visible">True</property>
-            <child>
+            <child type="end">
               <object class="GtkButton" id="clear_button">
-                <property name="visible">True</property>
-                <property name="receives_default">True</property>
                 <property name="use_underline">True</property>
                 <signal name="clicked" handler="on_clear_button_clicked" swapped="yes"/>
                 <style>
                   <class name="destructive-action"/>
                 </style>
               </object>
-              <packing>
-                <property name="pack-type">end</property>
-              </packing>
             </child>
           </object>
         </child>
diff --git a/src/resources/gtk/encoding-dialog.ui b/src/resources/gtk/encoding-dialog.ui
index 01796c782..19c51680b 100644
--- a/src/resources/gtk/encoding-dialog.ui
+++ b/src/resources/gtk/encoding-dialog.ui
@@ -4,16 +4,11 @@
   <requires lib="gtk+" version="3.14"/>
   <template class="EphyEncodingDialog" parent="GtkDialog">
     <property name="title" translatable="yes">Text Encoding</property>
-    <property name="role">epiphany-encoding-chooser</property>
     <property name="modal">True</property>
     <property name="default_width">500</property>
     <property name="default_height">420</property>
-    <property name="type_hint">dialog</property>
     <signal name="response" handler="ephy_encoding_dialog_response_cb" swapped="no"/>
-    <child>
-      <placeholder/>
-    </child>
-    <child internal-child="vbox">
+    <child internal-child="content_area">
       <object class="GtkBox">
         <property name="orientation">vertical</property>
         <property name="spacing">6</property>
@@ -23,11 +18,9 @@
         <property name="margin-end">6</property>
         <child>
           <object class="GtkBox">
-            <property name="visible">True</property>
             <property name="spacing">6</property>
             <child>
               <object class="GtkLabel">
-                <property name="visible">True</property>
                 <property name="xalign">0</property>
                 <property name="margin-start">6</property>
                 <property name="hexpand">True</property>
@@ -37,7 +30,6 @@
             </child>
             <child>
               <object class="GtkSwitch" id="default_switch">
-                <property name="visible">True</property>
                 <property name="valign">start</property>
                 <signal name="state-set" handler="default_switch_toggled_cb" swapped="no"/>
               </object>
@@ -46,108 +38,100 @@
         </child>
         <child>
           <object class="GtkStack" id="type_stack">
-            <property name="visible">True</property>
             <property name="sensitive">False</property>
             <property name="vexpand">True</property>
             <property name="interpolate_size">True</property>
             <child>
-              <object class="GtkBox" id="suggested_box">
-                <property name="visible">True</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">16</property>
-                <child>
-                  <object class="GtkBox" id="recent_box">
-                    <property name="visible">True</property>
+              <object class="GtkStackPage">
+                <property name="name">suggested_box</property>
+                <property name="child">
+                  <object class="GtkBox" id="suggested_box">
                     <property name="orientation">vertical</property>
-                    <property name="spacing">8</property>
+                    <property name="spacing">16</property>
                     <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="margin_start">8</property>
-                        <property name="label" translatable="yes">Recent encodings</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
+                      <object class="GtkBox" id="recent_box">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">8</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="xalign">0</property>
+                            <property name="margin_start">8</property>
+                            <property name="label" translatable="yes">Recent encodings</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkFrame">
+                            <property name="vexpand">True</property>
+                            <child>
+                              <object class="GtkListBox" id="recent_list_box">
+                                <signal name="row-activated" handler="row_activated_cb" swapped="no"/>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
                       </object>
                     </child>
                     <child>
-                      <object class="GtkFrame">
-                        <property name="visible">True</property>
-                        <property name="vexpand">True</property>
+                      <object class="GtkBox" id="related_box">
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">8</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="xalign">0</property>
+                            <property name="margin_start">8</property>
+                            <property name="label" translatable="yes">Related encodings</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                        </child>
                         <child>
-                          <object class="GtkListBox" id="recent_list_box">
-                            <property name="visible">True</property>
-                            <signal name="row-activated" handler="row_activated_cb" swapped="no"/>
+                          <object class="GtkFrame">
+                            <property name="margin-top">6</property>
+                            <property name="margin-bottom">6</property>
+                            <property name="margin-start">6</property>
+                            <property name="margin-end">6</property>
+                            <child>
+                              <object class="GtkListBox" id="related_list_box">
+                                <signal name="row-activated" handler="row_activated_cb" swapped="no"/>
+                              </object>
+                            </child>
                           </object>
                         </child>
                       </object>
                     </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkBox" id="related_box">
-                    <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
-                    <property name="spacing">8</property>
                     <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="margin_start">8</property>
-                        <property name="label" translatable="yes">Related encodings</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
+                      <object class="GtkButton">
+                        <property name="label" translatable="yes">Show all…</property>
+                        <property name="receives_default">False</property>
+                        <signal name="clicked" handler="show_all_button_clicked_cb" swapped="no"/>
                       </object>
                     </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">scrolled-window</property>
+                <property name="child">
+                  <object class="GtkFrame">
                     <child>
-                      <object class="GtkFrame">
-                        <property name="visible">True</property>
-                        <property name="margin-top">6</property>
-                        <property name="margin-bottom">6</property>
-                        <property name="margin-start">6</property>
-                        <property name="margin-end">6</property>
+                      <object class="GtkScrolledWindow">
+                        <property name="hscrollbar_policy">never</property>
                         <child>
-                          <object class="GtkListBox" id="related_list_box">
-                            <property name="visible">True</property>
-                            <signal name="row-activated" handler="row_activated_cb" swapped="no"/>
+                          <object class="GtkListBox" id="list_box">
+                            <signal name="row-activated" handler="row_activated_cb"/>
                           </object>
                         </child>
                       </object>
                     </child>
                   </object>
-                </child>
-                <child>
-                  <object class="GtkButton">
-                    <property name="label" translatable="yes">Show all…</property>
-                    <property name="visible">True</property>
-                    <property name="receives_default">False</property>
-                    <signal name="clicked" handler="show_all_button_clicked_cb" swapped="no"/>
-                  </object>
-                </child>
+                </property>
               </object>
-              <packing>
-                <property name="name">suggested_box</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkScrolledWindow">
-                <property name="visible">True</property>
-                <property name="hscrollbar_policy">never</property>
-                <style>
-                  <class name="frame"/>
-                </style>
-                <child>
-                  <object class="GtkListBox" id="list_box">
-                    <property name="visible">True</property>
-                    <signal name="row-activated" handler="row_activated_cb"/>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="name">scrolled-window</property>
-              </packing>
             </child>
           </object>
         </child>
diff --git a/src/resources/gtk/encoding-row.ui b/src/resources/gtk/encoding-row.ui
index 966a1017f..d22827a8d 100644
--- a/src/resources/gtk/encoding-row.ui
+++ b/src/resources/gtk/encoding-row.ui
@@ -2,17 +2,15 @@
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
   <template class="EphyEncodingRow" parent="GtkBox">
-    <property name="visible">True</property> <!-- TODO remove when never called manually -->
     <property name="halign">center</property>
     <property name="height-request">30</property>
     <property name="spacing">8</property>
     <child>
-      <object class="GtkLabel" id="encoding_label">
-        <property name="visible">True</property>
-      </object>
+      <object class="GtkLabel" id="encoding_label"/>
     </child>
     <child>
       <object class="GtkImage" id="selected_image">
+        <property name="visible">False</property>
         <property name="icon-name">object-select-symbolic</property>
       </object>
     </child>
diff --git a/src/resources/gtk/firefox-sync-dialog.ui b/src/resources/gtk/firefox-sync-dialog.ui
index 7011a4264..277f740b4 100644
--- a/src/resources/gtk/firefox-sync-dialog.ui
+++ b/src/resources/gtk/firefox-sync-dialog.ui
@@ -1,203 +1,172 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.0"/>
-  <template class="EphyFirefoxSyncDialog" parent="HdyWindow">
-    <property name="visible">True</property>
+  <template class="EphyFirefoxSyncDialog" parent="GtkWindow">
     <property name="modal">False</property>
-    <property name="window-position">center-on-parent</property>
     <property name="default-width">640</property>
     <property name="default-height">800</property>
     <property name="destroy-with-parent">True</property>
-    <property name="type-hint">dialog</property>
+    <property name="title" translatable="yes">Firefox Sync</property>
+    <child type="titlebar">
+      <object class="GtkHeaderBar"/>
+    </child>
     <child>
-      <object class="GtkBox">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+      <object class="AdwPreferencesPage">
         <child>
-          <object class="HdyHeaderBar">
-            <property name="visible">True</property>
-            <property name="show-close-button">True</property>
-            <property name="title" translatable="yes">Firefox Sync</property>
+          <object class="AdwPreferencesGroup" id="sync_page_group">
+            <property name="description" translatable="yes">Sign in with your Firefox account to sync your 
data with GNOME Web and Firefox on other computers. GNOME Web is not Firefox and is not produced or endorsed 
by Mozilla.</property>
+            <child>
+              <object class="GtkBox" id="sync_firefox_iframe_box">
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="sync_firefox_iframe_label">
+                    <property name="visible">False</property>
+                    <property name="xalign">0</property>
+                  </object>
+                </child>
+              </object>
+            </child>
           </object>
         </child>
         <child>
-          <object class="HdyPreferencesPage">
-            <property name="visible">True</property>
+          <object class="AdwPreferencesGroup" id="sync_firefox_account_group">
+            <property name="title" translatable="yes">Firefox Account</property>
+            <child>
+              <object class="AdwActionRow" id="sync_firefox_account_row">
+                <property name="subtitle" translatable="yes">Logged in</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <object class="GtkButton" id="sync_sign_out_button">
+                    <property name="label" translatable="yes">Sign _out</property>
+                    <property name="use-underline">True</property>
+                    <property name="valign">center</property>
+                    <signal name="clicked" handler="on_sync_sign_out_button_clicked"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="AdwPreferencesGroup" id="sync_options_group">
+            <property name="title" translatable="yes">Sync Options</property>
+            <child>
+              <object class="AdwActionRow">
+                <property name="activatable_widget">sync_bookmarks_switch</property>
+                <property name="title" translatable="yes">Sync _Bookmarks</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <object class="GtkSwitch" id="sync_bookmarks_switch">
+                    <property name="valign">center</property>
+                  </object>
+                </child>
+              </object>
+            </child>
             <child>
-              <object class="HdyPreferencesGroup" id="sync_page_group">
-                <property name="description" translatable="yes">Sign in with your Firefox account to sync 
your data with GNOME Web and Firefox on other computers. GNOME Web is not Firefox and is not produced or 
endorsed by Mozilla.</property>
-                <property name="visible">True</property>
+              <object class="AdwActionRow">
+                <property name="activatable_widget">sync_passwords_switch</property>
+                <property name="title" translatable="yes">Sync _Passwords</property>
+                <property name="use_underline">True</property>
                 <child>
-                  <object class="GtkBox" id="sync_firefox_iframe_box">
-                    <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
-                    <property name="spacing">6</property>
-                    <child>
-                      <object class="GtkLabel" id="sync_firefox_iframe_label">
-                        <property name="visible">False</property>
-                        <property name="xalign">0</property>
-                      </object>
-                    </child>
+                  <object class="GtkSwitch" id="sync_passwords_switch">
+                    <property name="valign">center</property>
                   </object>
                 </child>
               </object>
             </child>
             <child>
-              <object class="HdyPreferencesGroup" id="sync_firefox_account_group">
-                <property name="title" translatable="yes">Firefox Account</property>
-                <property name="visible">True</property>
+              <object class="AdwActionRow">
+                <property name="activatable_widget">sync_history_switch</property>
+                <property name="title" translatable="yes">Sync _History</property>
+                <property name="use_underline">True</property>
                 <child>
-                  <object class="HdyActionRow" id="sync_firefox_account_row">
-                    <property name="subtitle" translatable="yes">Logged in</property>
-                    <property name="use_underline">True</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="GtkButton" id="sync_sign_out_button">
-                        <property name="label" translatable="yes">Sign _out</property>
-                        <property name="use-underline">True</property>
-                        <property name="valign">center</property>
-                        <property name="visible">True</property>
-                        <signal name="clicked" handler="on_sync_sign_out_button_clicked"/>
-                      </object>
-                    </child>
+                  <object class="GtkSwitch" id="sync_history_switch">
+                    <property name="valign">center</property>
                   </object>
                 </child>
               </object>
             </child>
             <child>
-              <object class="HdyPreferencesGroup" id="sync_options_group">
-                <property name="title" translatable="yes">Sync Options</property>
-                <property name="visible">True</property>
+              <object class="AdwActionRow">
+                <property name="activatable_widget">sync_open_tabs_switch</property>
+                <property name="title" translatable="yes">Sync Open _Tabs</property>
+                <property name="use_underline">True</property>
                 <child>
-                  <object class="HdyActionRow">
-                    <property name="activatable_widget">sync_bookmarks_switch</property>
-                    <property name="title" translatable="yes">Sync _Bookmarks</property>
-                    <property name="use_underline">True</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="GtkSwitch" id="sync_bookmarks_switch">
-                        <property name="valign">center</property>
-                        <property name="visible">True</property>
-                      </object>
-                    </child>
+                  <object class="GtkSwitch" id="sync_open_tabs_switch">
+                    <property name="valign">center</property>
                   </object>
                 </child>
                 <child>
-                  <object class="HdyActionRow">
-                    <property name="activatable_widget">sync_passwords_switch</property>
-                    <property name="title" translatable="yes">Sync _Passwords</property>
-                    <property name="use_underline">True</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="GtkSwitch" id="sync_passwords_switch">
-                        <property name="valign">center</property>
-                        <property name="visible">True</property>
-                      </object>
-                    </child>
+                  <object class="GtkButton" id="synced_tabs_button">
+                    <property name="label" translatable="yes">S_ynced tabs</property>
+                    <property name="use-underline">True</property>
+                    <property name="valign">center</property>
+                    <signal name="clicked" handler="on_sync_synced_tabs_button_clicked"/>
                   </object>
                 </child>
+              </object>
+            </child>
+            <child>
+              <object class="AdwComboRow" id="sync_frequency_row">
+                <property name="title" translatable="yes">Frequency</property>
+                <property name="expression">
+                  <closure type="gchararray" function="get_sync_frequency_minutes_name"/>
+                </property>
+                <child>
+                  <object class="GtkSeparator">
+                    <property name="margin_bottom">8</property>
+                    <property name="margin_top">8</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton" id="sync_now_button">
+                    <property name="label" translatable="yes">Sync _now</property>
+                    <property name="use-underline">True</property>
+                    <property name="valign">center</property>
+                    <signal name="clicked" handler="on_sync_sync_now_button_clicked"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="AdwActionRow">
+                <property name="activatable">False</property>
+                <property name="title" translatable="yes">Device name</property>
+                <property name="use_underline">True</property>
                 <child>
-                  <object class="HdyActionRow">
-                    <property name="activatable_widget">sync_history_switch</property>
-                    <property name="title" translatable="yes">Sync _History</property>
-                    <property name="use_underline">True</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="GtkSwitch" id="sync_history_switch">
-                        <property name="valign">center</property>
-                        <property name="visible">True</property>
-                      </object>
-                    </child>
+                  <object class="GtkButton" id="sync_device_name_cancel_button">
+                    <property name="label" translatable="yes">_Cancel</property>
+                    <property name="use-underline">True</property>
+                    <property name="valign">center</property>
+                    <property name="visible">False</property>
+                    <signal name="clicked" handler="on_sync_device_name_cancel_button_clicked"/>
                   </object>
                 </child>
                 <child>
-                  <object class="HdyActionRow">
-                    <property name="activatable_widget">sync_open_tabs_switch</property>
-                    <property name="title" translatable="yes">Sync Open _Tabs</property>
-                    <property name="use_underline">True</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="GtkSwitch" id="sync_open_tabs_switch">
-                        <property name="valign">center</property>
-                        <property name="visible">True</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkButton" id="synced_tabs_button">
-                        <property name="label" translatable="yes">S_ynced tabs</property>
-                        <property name="use-underline">True</property>
-                        <property name="valign">center</property>
-                        <property name="visible">True</property>
-                        <signal name="clicked" handler="on_sync_synced_tabs_button_clicked"/>
-                      </object>
-                    </child>
+                  <object class="GtkButton" id="sync_device_name_save_button">
+                    <property name="label" translatable="yes">_Save</property>
+                    <property name="use-underline">True</property>
+                    <property name="valign">center</property>
+                    <property name="visible">False</property>
+                    <signal name="clicked" handler="on_sync_device_name_save_button_clicked"/>
                   </object>
                 </child>
                 <child>
-                  <object class="HdyComboRow" id="sync_frequency_row">
-                    <property name="title" translatable="yes">Frequency</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="GtkSeparator">
-                        <property name="margin_bottom">8</property>
-                        <property name="margin_top">8</property>
-                        <property name="visible">True</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkButton" id="sync_now_button">
-                        <property name="label" translatable="yes">Sync _now</property>
-                        <property name="use-underline">True</property>
-                        <property name="valign">center</property>
-                        <property name="visible">True</property>
-                        <signal name="clicked" handler="on_sync_sync_now_button_clicked"/>
-                      </object>
-                    </child>
+                  <object class="GtkButton" id="sync_device_name_change_button">
+                    <property name="label" translatable="yes">_Change</property>
+                    <property name="use-underline">True</property>
+                    <property name="valign">center</property>
+                    <signal name="clicked" handler="on_sync_device_name_change_button_clicked"/>
                   </object>
                 </child>
                 <child>
-                  <object class="HdyActionRow">
-                    <property name="activatable">False</property>
-                    <property name="title" translatable="yes">Device name</property>
-                    <property name="use_underline">True</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="GtkButton" id="sync_device_name_cancel_button">
-                        <property name="label" translatable="yes">_Cancel</property>
-                        <property name="use-underline">True</property>
-                        <property name="valign">center</property>
-                        <property name="visible">False</property>
-                        <signal name="clicked" handler="on_sync_device_name_cancel_button_clicked"/>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkButton" id="sync_device_name_save_button">
-                        <property name="label" translatable="yes">_Save</property>
-                        <property name="use-underline">True</property>
-                        <property name="valign">center</property>
-                        <property name="visible">False</property>
-                        <signal name="clicked" handler="on_sync_device_name_save_button_clicked"/>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkButton" id="sync_device_name_change_button">
-                        <property name="label" translatable="yes">_Change</property>
-                        <property name="use-underline">True</property>
-                        <property name="valign">center</property>
-                        <property name="visible">True</property>
-                        <signal name="clicked" handler="on_sync_device_name_change_button_clicked"/>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkEntry" id="sync_device_name_entry">
-                        <property name="sensitive">False</property>
-                        <property name="margin-start">12</property>
-                        <property name="margin-end">12</property>
-                        <property name="margin-top">8</property>
-                        <property name="margin-bottom">8</property>
-                        <property name="visible">True</property>
-                      </object>
-                    </child>
+                  <object class="GtkEntry" id="sync_device_name_entry">
+                    <property name="sensitive">False</property>
+                    <property name="margin-start">12</property>
+                    <property name="margin-end">12</property>
+                    <property name="margin-top">8</property>
+                    <property name="margin-bottom">8</property>
                   </object>
                 </child>
               </object>
diff --git a/src/resources/gtk/history-dialog.ui b/src/resources/gtk/history-dialog.ui
index 89830749b..524b0e7db 100644
--- a/src/resources/gtk/history-dialog.ui
+++ b/src/resources/gtk/history-dialog.ui
@@ -2,143 +2,70 @@
 <interface>
   <requires lib="gtk+" version="3.20"/>
   <requires lib="libhandy" version="0.0"/>
-  <template class="EphyHistoryDialog" parent="HdyWindow">
-    <!--property name="search_description" translatable="yes">Search history</property-->
+  <template class="EphyHistoryDialog" parent="AdwWindow">
     <property name="modal">True</property>
-    <property name="window-position">center-on-parent</property>
     <property name="default-width">640</property>
     <property name="default-height">800</property>
     <property name="destroy-with_parent">True</property>
-    <property name="type-hint">dialog</property>
-    <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
-    <signal name="key-release-event" handler="on_key_release_event"/>
+    <property name="title" translatable="yes">History</property>
     <child>
       <object class="GtkBox">
-        <property name="visible">True</property>
         <property name="orientation">vertical</property>
         <child>
           <object class="GtkStack" id="header_bars_stack">
-            <property name="visible">True</property>
-            <property name="transition-type">GTK_STACK_TRANSITION_TYPE_CROSSFADE</property>
+            <property name="transition-type">crossfade</property>
             <child>
-              <object class="HdyHeaderBar" id="window_header_bar">
-                <property name="visible">True</property>
-                <property name="show_close_button">True</property>
-                <property name="title" translatable="yes">History</property>
-                <child>
+              <object class="GtkHeaderBar" id="window_header_bar">
+                <child type="end">
                   <object class="GtkButton" id="selection_button">
-                    <property name="visible">True</property>
-                    <property name="valign">center</property>
                     <property name="use-underline">True</property>
+                    <property name="icon-name">selection-mode-symbolic</property>
+                    <property name="tooltip-text" translatable="yes">Select Items</property>
                     <signal name="clicked" handler="on_selection_button_clicked"/>
-                    <style>
-                      <class name="image-button"/>
-                    </style>
-                    <child internal-child="accessible">
-                      <object class="AtkObject" id="a11y-button3">
-                        <property name="accessible-name" translatable="yes">Select Items</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkImage">
-                        <property name="visible">True</property>
-                        <property name="icon-name">object-select-symbolic</property>
-                        <property name="icon-size">1</property>
-                      </object>
-                    </child>
                   </object>
-                  <packing>
-                    <property name="pack-type">end</property>
-                  </packing>
                 </child>
-                <child>
+                <child type="end">
                   <object class="GtkToggleButton" id="search_button">
-                    <property name="visible">True</property>
-                    <property name="receives_default">True</property>
-                    <property name="always_show_image">True</property>
+                    <property name="icon-name">edit-find-symbolic</property>
+                    <property name="tooltip-text" translatable="yes">Search</property>
                     <property name="active" bind-source="search_bar" bind-property="search-mode-enabled" 
bind-flags="sync-create|bidirectional"/>
-                    <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
-                    <child>
-                      <object class="GtkImage">
-                        <property name="visible">True</property>
-                        <property name="icon_name">edit-find-symbolic</property>
-                      </object>
-                    </child>
-                    <child internal-child="accessible">
-                      <object class="AtkObject">
-                        <property name="AtkObject::accessible-name" translatable="yes">Search</property>
-                      </object>
-                    </child>
                   </object>
-                  <packing>
-                    <property name="pack_type">end</property>
-                  </packing>
                 </child>
               </object>
             </child>
             <child>
-              <object class="HdyHeaderBar" id="selection_header_bar">
-                <property name="visible">True</property>
-                <property name="title" translatable="yes">History</property>
-                <style>
-                  <class name="selection-mode"/>
-                </style>
-                <child>
+              <object class="GtkHeaderBar" id="selection_header_bar">
+                <property name="show-title-buttons">False</property>
+                <child type="end">
                   <object class="GtkButton" id="selection_cancel_button">
-                    <property name="visible">True</property>
-                    <property name="valign">center</property>
                     <property name="use-underline">True</property>
                     <property name="label" translatable="yes">_Cancel</property>
                     <signal name="clicked" handler="on_selection_cancel_button_clicked"/>
-                    <style>
-                      <class name="text-button"/>
-                    </style>
                   </object>
-                  <packing>
-                    <property name="pack-type">end</property>
-                  </packing>
                 </child>
-                <child>
+                <child type="end">
                   <object class="GtkToggleButton" id="selection_search_button">
-                    <property name="visible">True</property>
+                    <property name="icon-name">edit-find-symbolic</property>
+                    <property name="tooltip-text" translatable="yes">Search</property>
                     <property name="active" bind-source="search_bar" bind-property="search-mode-enabled" 
bind-flags="sync-create|bidirectional"/>
-                    <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
-                    <child>
-                      <object class="GtkImage">
-                        <property name="visible">True</property>
-                        <property name="icon_name">edit-find-symbolic</property>
-                      </object>
-                    </child>
-                    <child internal-child="accessible">
-                      <object class="AtkObject">
-                        <property name="AtkObject::accessible-name" translatable="yes">Search</property>
-                      </object>
-                    </child>
                   </object>
-                  <packing>
-                    <property name="pack_type">end</property>
-                  </packing>
                 </child>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdySearchBar" id="search_bar">
-            <property name="visible">True</property>
+          <object class="GtkSearchBar" id="search_bar">
             <property name="search_mode_enabled">False</property>
+            <property name="key-capture-widget">EphyHistoryDialog</property>
             <child>
-              <object class="HdyClamp">
-                <property name="visible">True</property>
+              <object class="AdwClamp">
+                <property name="hexpand">True</property>
                 <property name="maximum_size">400</property>
                 <property name="tightening_threshold">300</property>
                 <child>
                   <object class="GtkSearchEntry" id="search_entry">
-                    <property name="visible">True</property>
                     <property name="placeholder-text" translatable="yes">Search history</property>
-                    <property name="primary_icon_name">edit-find-symbolic</property>
-                    <property name="primary_icon_activatable">False</property>
-                    <property name="primary_icon_sensitive">False</property>
                     <signal name="search-changed" handler="on_search_entry_changed"/>
                   </object>
                 </child>
@@ -148,31 +75,31 @@
         </child>
         <child>
           <object class="GtkStack" id="history_presentation_stack">
-            <property name="visible">True</property>
             <property name="vexpand">True</property>
             <child>
               <object class="GtkScrolledWindow" id="history_scrolled_window">
-                <property name="visible">True</property>
                 <signal name="edge-reached" handler="on_edge_reached" object="EphyHistoryDialog" 
swapped="no"/>
                 <child>
-                  <object class="HdyClamp">
-                    <property name="visible">True</property>
-                    <property name="margin_start">6</property>
-                    <property name="margin_end">6</property>
-                    <property name="maximum_size">1024</property>
+                  <object class="GtkViewport">
+                    <property name="scroll-to-focus">True</property>
                     <child>
-                      <object class="GtkListBox" id="listbox">
-                        <property name="visible">True</property>
-                        <property name="margin_top">6</property>
-                        <property name="margin_bottom">6</property>
-                        <property name="valign">start</property>
-                        <property name="selection_mode">none</property>
-                        <property name="activate_on_single_click">True</property>
-                        <signal name="key-press-event" handler="on_listbox_key_press_event" swapped="no"/>
-                        <signal name="row-activated" handler="on_listbox_row_activated"/>
-                        <style>
-                          <class name="content"/>
-                        </style>
+                      <object class="AdwClamp">
+                        <property name="margin_start">6</property>
+                        <property name="margin_end">6</property>
+                        <property name="maximum_size">1024</property>
+                        <child>
+                          <object class="GtkListBox" id="listbox">
+                            <property name="margin_top">6</property>
+                            <property name="margin_bottom">6</property>
+                            <property name="valign">start</property>
+                            <property name="selection_mode">none</property>
+                            <property name="activate_on_single_click">True</property>
+                            <signal name="row-activated" handler="on_listbox_row_activated"/>
+                            <style>
+                              <class name="boxed-list"/>
+                            </style>
+                          </object>
+                        </child>
                       </object>
                     </child>
                   </object>
@@ -181,19 +108,20 @@
             </child>
             <child>
               <object class="GtkSpinner" id="loading_spinner">
-                <property name="visible">True</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="width-request">32</property>
+                <property name="height-request">32</property>
               </object>
             </child>
             <child>
-              <object class="HdyStatusPage" id="empty_history_message">
-                <property name="visible">True</property>
+              <object class="AdwStatusPage" id="empty_history_message">
                 <property name="title" translatable="yes">The History is Empty</property>
                 <property name="description" translatable="yes">Visited pages will be listed here</property>
               </object>
             </child>
             <child>
-              <object class="HdyStatusPage" id="no_search_results_message">
-                <property name="visible">True</property>
+              <object class="AdwStatusPage" id="no_search_results_message">
                 <property name="icon_name">edit-find-symbolic</property>
                 <property name="title" translatable="yes">No Results Found</property>
                 <property name="description" translatable="yes">Try a different search</property>
@@ -203,37 +131,25 @@
         </child>
         <child>
           <object class="GtkStack" id="action_bars_stack">
-            <property name="visible">True</property>
-            <property name="transition-type">GTK_STACK_TRANSITION_TYPE_CROSSFADE</property>
+            <property name="transition-type">crossfade</property>
             <child>
               <object class="GtkActionBar" id="regular_action_bar">
-                <property name="visible">True</property>
-                <child>
+                <child type="end">
                   <object class="GtkButton" id="clear_all_button">
-                    <property name="visible">True</property>
-                    <property name="receives_default">True</property>
                     <property name="use-underline">True</property>
                     <property name="label" translatable="yes">_Clear All</property>
                     <signal name="clicked" handler="on_clear_all_button_clicked"/>
-                    <accelerator key="Delete" modifiers="GDK_SHIFT_MASK" signal="clicked"/>
                     <style>
                       <class name="destructive-action"/>
-                      <class name="image-button"/>
                     </style>
                   </object>
-                  <packing>
-                    <property name="pack-type">end</property>
-                  </packing>
                 </child>
               </object>
             </child>
             <child>
               <object class="GtkActionBar" id="selection_action_bar">
-                <property name="visible">True</property>
-                <child>
+                <child type="end">
                   <object class="GtkButton" id="selection_delete_button">
-                    <property name="visible">True</property>
-                    <property name="receives_default">True</property>
                     <property name="sensitive">False</property>
                     <property name="use_underline">True</property>
                     <property name="label" translatable="yes">_Delete</property>
@@ -242,14 +158,9 @@
                       <class name="destructive-action"/>
                     </style>
                   </object>
-                  <packing>
-                    <property name="pack-type">end</property>
-                  </packing>
                 </child>
                 <child>
                   <object class="GtkButton" id="selection_open_button">
-                    <property name="visible">True</property>
-                    <property name="receives_default">True</property>
                     <property name="sensitive">False</property>
                     <property name="use_underline">True</property>
                     <property name="label" translatable="yes">_Open</property>
@@ -262,5 +173,11 @@
         </child>
       </object>
     </child>
+    <child>
+      <object class="GtkEventControllerKey">
+        <signal name="key-pressed" handler="key_pressed_cb" swapped="yes"/>
+        <signal name="key-released" handler="key_released_cb" swapped="yes"/>
+      </object>
+    </child>
   </template>
 </interface>
diff --git a/src/resources/gtk/lang-row.ui b/src/resources/gtk/lang-row.ui
index 09318894c..4ccc181ee 100644
--- a/src/resources/gtk/lang-row.ui
+++ b/src/resources/gtk/lang-row.ui
@@ -1,68 +1,26 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.20"/>
-  <template class="EphyLangRow" parent="GtkListBoxRow">
-    <property name="visible">True</property>
-    <child>
-      <object class="GtkBox">
-        <property name="visible">True</property>
-        <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
+  <template class="EphyLangRow" parent="AdwActionRow">
+    <child type="prefix">
+      <object class="GtkImage" id="drag_handle">
+        <property name="icon-name">list-drag-handle-symbolic</property>
         <child>
-          <object class="GtkRevealer" id="dnd_top_revealer">
-            <property name="visible">True</property>
-            <child>
-              <object class="HdyActionRow">
-                <property name="visible">True</property>
-              </object>
-            </child>
-          </object>
-        </child>
-        <child>
-          <object class="HdyActionRow" id="action_row">
-            <property name="visible">True</property>
-            <child type="prefix">
-              <object class="GtkEventBox" id="drag_event_box">
-                <property name="visible">True</property>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon-name">list-drag-handle-symbolic</property>
-                    <property name="icon-size">1</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton" id="delete_button">
-                <property name="visible">True</property>
-                <property name="valign">center</property>
-                <property name="tooltip-text" translatable="yes">Delete language</property>
-                <signal name="clicked" handler="on_delete_button_clicked"/>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon-name">edit-delete-symbolic</property>
-                    <property name="icon-size">2</property>
-                    <style>
-                      <class name="image-button"/>
-                    </style>
-                  </object>
-                </child>
-              </object>
-            </child>
-          </object>
-        </child>
-        <child>
-          <object class="GtkRevealer" id="dnd_bottom_revealer">
-            <property name="visible">True</property>
-            <child>
-              <object class="HdyActionRow">
-                <property name="visible">True</property>
-              </object>
-            </child>
+          <object class="GtkDragSource">
+            <property name="actions">move</property>
+            <signal name="prepare" handler="drag_prepare_cb" swapped="yes"/>
+            <signal name="drag-begin" handler="drag_begin_cb" swapped="yes"/>
           </object>
         </child>
       </object>
     </child>
+    <child type="suffix">
+      <object class="GtkButton" id="delete_button">
+        <property name="valign">center</property>
+        <property name="icon-name">edit-delete-symbolic</property>
+        <property name="tooltip-text" translatable="yes">Delete language</property>
+        <signal name="clicked" handler="on_delete_button_clicked"/>
+      </object>
+    </child>
   </template>
 </interface>
diff --git a/src/resources/gtk/location-entry.ui b/src/resources/gtk/location-entry.ui
new file mode 100644
index 000000000..6c68c3714
--- /dev/null
+++ b/src/resources/gtk/location-entry.ui
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <template class="EphyLocationEntry" parent="GtkWidget">
+    <style>
+      <class name="location-entry"/>
+    </style>
+    <signal name="changed" handler="editable_changed_cb" swapped="yes"/>
+    <child>
+      <object class="GtkText" id="text">
+        <property name="placeholder-text" translatable="yes">Search for websites, bookmarks, and open 
tabs</property>
+        <property name="input-hints">no-emoji</property>
+        <property name="max-width-chars">200</property>
+        <signal name="activate" handler="activate_cb" swapped="yes"/>
+        <signal name="cut-clipboard" handler="cut_clipboard_cb" swapped="yes"/>
+        <signal name="copy-clipboard" handler="copy_clipboard_cb" swapped="yes"/>
+        <child>
+          <object class="GtkEventControllerKey">
+            <signal name="key-pressed" handler="key_pressed_cb" swapped="yes"/>
+          </object>
+        </child>
+        <child>
+          <object class="GtkGestureClick">
+            <property name="button">0</property>
+            <property name="exclusive">True</property>
+            <property name="propagation-phase">capture</property>
+            <signal name="pressed" handler="text_pressed_cb" swapped="yes"/>
+          </object>
+        </child>
+        <child>
+          <object class="GtkShortcutController">
+            <property name="scope">local</property>
+            <child>
+              <object class="GtkShortcut">
+                <property name="trigger">Menu</property>
+                <property name="action">action(menu.popup-extra)</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkShortcut">
+                <property name="trigger">&lt;Shift&gt;F10</property>
+                <property name="action">action(menu.popup-extra)</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkPopoverMenu" id="context_menu">
+            <property name="menu-model">context_menu_model</property>
+            <property name="halign">start</property>
+            <property name="has-arrow">False</property>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkProgressBar" id="progress">
+        <property name="css-name">progress</property>
+        <property name="can-target">False</property>
+        <property name="valign">end</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuButton" id="security_button">
+        <property name="tooltip-text" translatable="yes">Show website security status and 
permissions</property>
+        <property name="valign">center</property>
+        <style>
+          <class name="entry-icon"/>
+          <class name="start"/>
+        </style>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuButton" id="bookmark_button">
+        <property name="tooltip-text" translatable="yes">Bookmark this page</property>
+        <property name="valign">center</property>
+        <style>
+          <class name="entry-icon"/>
+          <class name="end"/>
+        </style>
+      </object>
+    </child>
+    <child>
+      <object class="GtkToggleButton" id="reader_mode_button">
+        <property name="tooltip-text" translatable="yes">Toggle reader mode</property>
+        <property name="valign">center</property>
+        <property name="visible">False</property>
+        <signal name="clicked" handler="reader_mode_clicked_cb" swapped="yes"/>
+        <style>
+          <class name="entry-icon"/>
+          <class name="end"/>
+        </style>
+      </object>
+    </child>
+    <child>
+      <object class="GtkPopover" id="suggestions_popover">
+        <property name="has-arrow">False</property>
+        <property name="autohide">False</property>
+        <property name="can-focus">False</property>
+        <property name="child">
+          <object class="GtkScrolledWindow">
+            <property name="hscrollbar-policy">never</property>
+            <property name="max-content-height">400</property>
+            <property name="propagate-natural-height">True</property>
+            <property name="child">
+              <object class="GtkListView">
+                <signal name="activate" handler="suggestion_activated_cb" swapped="yes"/>
+                <property name="single-click-activate">True</property>
+                <property name="model">
+                  <object class="GtkSingleSelection" id="suggestions_model">
+                    <property name="autoselect">False</property>
+                    <signal name="items-changed" handler="update_suggestions_popover" swapped="yes"/>
+                  </object>
+                </property>
+                <property name="factory">
+                  <object class="GtkBuilderListItemFactory">
+                    <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GtkListItem">
+    <property name="child">
+      <object class="GtkGrid">
+        <property name="column-spacing">6</property>
+        <property name="row-spacing">3</property>
+        <child>
+          <object class="GtkImage">
+            <property name="pixel-size">16</property>
+            <binding name="gicon">
+              <closure type="GIcon" function="get_suggestion_icon">
+                <lookup name="icon" type="EphySuggestion">
+                  <lookup name="item">GtkListItem</lookup>
+                </lookup>
+              </closure>
+            </binding>
+            <layout>
+              <property name="column">0</property>
+              <property name="row">0</property>
+            </layout>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="use-markup">True</property>
+            <property name="ellipsize">end</property>
+            <property name="xalign">0</property>
+            <property name="single-line-mode">True</property>
+            <property name="hexpand">True</property>
+            <binding name="label">
+              <lookup name="title" type="EphySuggestion">
+                <lookup name="item">GtkListItem</lookup>
+              </lookup>
+            </binding>
+            <layout>
+              <property name="column">1</property>
+              <property name="row">0</property>
+            </layout>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="use-markup">True</property>
+            <property name="ellipsize">end</property>
+            <property name="xalign">0</property>
+            <property name="single-line-mode">True</property>
+            <property name="hexpand">True</property>
+            <binding name="label">
+              <lookup name="subtitle" type="EphySuggestion">
+                <lookup name="item">GtkListItem</lookup>
+              </lookup>
+            </binding>
+            <layout>
+              <property name="column">1</property>
+              <property name="row">1</property>
+            </layout>
+            <style>
+              <class name="dim-label"/>
+              <class name="caption"/>
+            </style>
+          </object>
+        </child>
+        <child>
+          <object class="GtkImage">
+            <property name="pixel-size">16</property>
+            <binding name="gicon">
+              <closure type="GIcon" function="get_suggestion_secondary_icon">
+                <lookup name="secondary-icon" type="EphySuggestion">
+                  <lookup name="item">GtkListItem</lookup>
+                </lookup>
+              </closure>
+            </binding>
+            <layout>
+              <property name="column">2</property>
+              <property name="row">0</property>
+            </layout>
+          </object>
+        </child>
+      </object>
+    </property>
+  </template>
+</interface>
+                    ]]></property>
+                  </object>
+                </property>
+              </object>
+            </property>
+          </object>
+        </property>
+        <style>
+          <class name="menu"/>
+          <class name="suggestions"/>
+        </style>
+      </object>
+    </child>
+    <child>
+      <object class="GtkEventControllerFocus">
+        <signal name="enter" handler="focus_enter_cb" swapped="yes"/>
+        <signal name="leave" handler="focus_leave_cb" swapped="yes"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkGestureClick">
+        <property name="propagation-phase">capture</property>
+        <!-- GDK_BUTTON_PRIMARY -->
+        <property name="button">1</property>
+        <property name="exclusive">True</property>
+        <signal name="pressed" handler="click_pressed_cb" swapped="yes"/>
+        <signal name="released" handler="click_released_cb" swapped="yes"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkGestureLongPress">
+        <property name="propagation-phase">capture</property>
+        <property name="touch-only">True</property>
+        <property name="exclusive">True</property>
+        <signal name="pressed" handler="long_press_cb" swapped="yes"/>
+      </object>
+    </child>
+  </template>
+  <menu id="context_menu_model">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Undo</attribute>
+        <attribute name="action">edit.undo-extra</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Redo</attribute>
+        <attribute name="action">edit.redo-extra</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Cu_t</attribute>
+        <attribute name="action">clipboard.cut</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Copy</attribute>
+        <attribute name="action">clipboard.copy</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Paste</attribute>
+        <attribute name="action">clipboard.paste</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Paste and _Go</attribute>
+        <attribute name="action">clipboard.paste-and-go</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Delete</attribute>
+        <attribute name="action">selection.delete</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Cl_ear</attribute>
+        <attribute name="action">edit.clear</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Select _All</attribute>
+        <attribute name="action">selection.select-all</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/src/resources/gtk/page-menu-popover.ui b/src/resources/gtk/page-menu-popover.ui
index 0e1b5b786..e34d2f54e 100644
--- a/src/resources/gtk/page-menu-popover.ui
+++ b/src/resources/gtk/page-menu-popover.ui
@@ -1,374 +1,180 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
+  <menu id="menu">
+    <section>
+      <item>
+        <attribute name="custom">zoom</attribute>
+      </item>
+      <item>
+        <attribute name="custom">header</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Run in Background</attribute>
+        <attribute name="action">app.run-in-background</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_New Window</attribute>
+        <attribute name="action">app.new-window</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">New _Incognito Window</attribute>
+        <attribute name="action">app.new-incognito</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Reopen Closed _Tab</attribute>
+        <attribute name="action">app.reopen-closed-tab</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_History</attribute>
+        <attribute name="action">app.history</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Firefox _Sync</attribute>
+        <attribute name="action">app.firefox-sync-dialog</attribute>
+      </item>
+      <submenu>
+        <attribute name="label" translatable="yes">I_mport and Export</attribute>
+        <attribute name="ephy-submenu-id">import-export</attribute>
+        <section>
+          <item>
+            <attribute name="label" translatable="yes">I_mport Bookmarks…</attribute>
+            <attribute name="action">app.import-bookmarks</attribute>
+          </item>
+          <item>
+            <attribute name="label" translatable="yes">E_xport Bookmarks…</attribute>
+            <attribute name="action">app.export-bookmarks</attribute>
+          </item>
+        </section>
+        <section>
+          <item>
+            <attribute name="label" translatable="yes">Import _Passwords…</attribute>
+            <attribute name="action">app.import-passwords</attribute>
+          </item>
+        </section>
+      </submenu>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Install Site as Web _Application…</attribute>
+        <attribute name="action">win.save-as-application</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Open Appli_cation Manager</attribute>
+        <attribute name="action">win.open-application-manager</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">E_xtensions</attribute>
+        <attribute name="action">win.extensions</attribute>
+        <attribute name="hidden-when">action-disabled</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Override Text Encoding…</attribute>
+        <attribute name="action">win.encoding</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">Pr_eferences</attribute>
+        <attribute name="action">app.preferences</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
+        <attribute name="action">app.shortcuts</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Help</attribute>
+        <attribute name="action">app.help</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_About Web</attribute>
+        <attribute name="action">app.about</attribute>
+      </item>
+    </section>
+  </menu>
   <object class="GtkPopoverMenu" id="page-menu-popover">
-    <child>
-      <object class="GtkBox">
-        <property name="margin-top">12</property>
-        <property name="orientation">vertical</property>
-        <property name="visible">True</property>
-        <property name="spacing">6</property>
+    <property name="menu-model">menu</property>
+    <child type="zoom">
+      <object class="GtkBox" id="zoom-box">
+        <property name="orientation">horizontal</property>
+        <property name="homogeneous">True</property>
+        <style>
+          <class name="linked"/>
+          <class name="page-menu-zoom-box"/>
+        </style>
         <child>
-          <object class="GtkBox" id="zoom-box">
-            <property name="visible">True</property>
-            <property name="orientation">horizontal</property>
-            <property name="homogeneous">True</property>
-            <property name="margin-start">12</property>
-            <property name="margin-end">12</property>
-            <style>
-              <class name="linked"/>
-              <class name="page-menu-zoom-box"/>
-            </style>
-            <child>
-              <object class="GtkButton">
-                <property name="tooltip_text" translatable="yes" context="tooltip">Zoom Out</property>
-                <property name="action-name">win.zoom-out</property>
-                <property name="visible">True</property>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon_name">zoom-out-symbolic</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton">
-                <property name="tooltip_text" translatable="yes">Restore Zoom</property>
-                <property name="action-name">win.zoom-normal</property>
-                <property name="visible">True</property>
-                <child>
-                  <object class="GtkLabel" id="zoom-level">
-                    <property name="visible">True</property>
-                    <property name="width-chars">5</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton">
-                <property name="tooltip_text" translatable="yes" context="tooltip">Zoom In</property>
-                <property name="action-name">win.zoom-in</property>
-                <property name="visible">True</property>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon_name">zoom-in-symbolic</property>
-                  </object>
-                </child>
-              </object>
-            </child>
+          <object class="GtkButton">
+            <property name="tooltip_text" translatable="yes" context="tooltip">Zoom Out</property>
+            <property name="icon_name">zoom-out-symbolic</property>
+            <property name="action-name">win.zoom-out</property>
           </object>
         </child>
         <child>
-          <object class="GtkBox">
-            <property name="visible">True</property>
-            <property name="orientation">horizontal</property>
-            <property name="spacing">6</property>
-            <property name="homogeneous">True</property>
-            <property name="margin-start">12</property>
-            <property name="margin-end">12</property>
+          <object class="GtkButton">
+            <property name="tooltip_text" translatable="yes">Restore Zoom</property>
+            <property name="action-name">win.zoom-normal</property>
             <child>
-              <object class="GtkModelButton">
-                <property name="tooltip_text" translatable="yes">Print…</property>
-                <property name="action-name">win.print</property>
-                <property name="iconic">True</property>
-                <property name="centered">True</property>
-                <property name="icon">print</property>
+              <object class="GtkLabel" id="zoom-level">
                 <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton">
-                <property name="tooltip_text" translatable="yes">Find…</property>
-                <property name="action-name">win.find</property>
-                <property name="iconic">True</property>
-                <property name="centered">True</property>
-                <property name="icon">find</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton">
-                <property name="tooltip_text" translatable="yes">Fullscreen</property>
-                <property name="action-name">win.fullscreen</property>
-                <property name="iconic">True</property>
-                <property name="centered">True</property>
-                <property name="icon">fullscreen</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton" id="combined_stop_reload_button">
-                <property name="tooltip_text" translatable="yes">Reload</property>
-                <property name="action-name">toolbar.combined-stop-reload</property>
-                <property name="visible">True</property>
-                <style>
-                  <class name="image-button"/>
-                </style>
-                <child>
-                  <object class="GtkImage" id="combined_stop_reload_image">
-                    <property name="visible">True</property>
-                    <property name="icon-name">view-refresh-symbolic</property>
-                    <property name="icon-size">1</property>
-                  </object>
-                </child>
+                <property name="width-chars">5</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="GtkBox">
-            <property name="orientation">vertical</property>
-            <property name="visible">True</property>
-            <style>
-              <class name="page-menu-contents"/>
-            </style>
-
-            <!-- FRAGILE: These buttons are manually removed for non app mode in ephy-header-bar.c. -->
-            <child>
-              <object class="GtkSeparator" id="run-in-background-separator">
-                <property name="orientation">horizontal</property>
-                <property name="margin-top">6</property>
-                <property name="margin-bottom">6</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton" id="run-in-background-button">
-                <property name="text" translatable="yes">_Run in Background</property>
-                <property name="action-name">app.run-in-background</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-
-            <!-- FRAGILE: These buttons are manually removed for app mode in ephy-header-bar.c. -->
-            <child>
-              <object class="GtkSeparator" id="new-window-separator">
-                <property name="orientation">horizontal</property>
-                <property name="margin-top">6</property>
-                <property name="margin-bottom">6</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton" id="new-window-button">
-                <property name="text" translatable="yes">_New Window</property>
-                <property name="action-name">app.new-window</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton" id="new-incognito-window-button">
-                <property name="text" translatable="yes">New _Incognito Window</property>
-                <property name="action-name">app.new-incognito</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkSeparator">
-                <property name="orientation">horizontal</property>
-                <property name="margin-top">6</property>
-                <property name="margin-bottom">6</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <!-- FRAGILE: This button is manually removed for app mode in ephy-header-bar.c. -->
-            <child>
-              <object class="GtkModelButton" id="reopen-closed-tab-button">
-                <property name="text" translatable="yes">Reopen Closed _Tab</property>
-                <property name="action-name">app.reopen-closed-tab</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton">
-                <property name="text" translatable="yes">_History</property>
-                <property name="action-name">app.history</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <!-- FRAGILE: These buttons are manually removed for app mode in ephy-header-bar.c. -->
-            <child>
-              <object class="GtkSeparator" id="firefox-sync-separator">
-                <property name="orientation">horizontal</property>
-                <property name="margin-top">6</property>
-                <property name="margin-bottom">6</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton" id="firefox-sync-button">
-                <property name="text" translatable="yes">Firefox _Sync</property>
-                <property name="action-name">app.firefox-sync-dialog</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton" id="import-export-menu">
-                <property name="text" translatable="yes">I_mport and Export</property>
-                <property name="visible">True</property>
-                <property name="menu-name">import_export</property>
-              </object>
-            </child>
-            <!-- FRAGILE: These buttons are manually removed for app mode in ephy-header-bar.c. -->
-            <child>
-              <object class="GtkSeparator" id="save-as-application-separator">
-                <property name="orientation">horizontal</property>
-                <property name="margin-top">6</property>
-                <property name="margin-bottom">6</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton" id="save-as-application-button">
-                <property name="text" translatable="yes">Install Site as Web _Application…</property>
-                <property name="action-name">win.save-as-application</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton" id="application-manager-button">
-                <property name="text" translatable="yes">Open Appli_cation Manager</property>
-                <property name="action-name">win.open-application-manager</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton" id="extensions-button">
-                <property name="text" translatable="yes">E_xtensions</property>
-                <property name="action-name">win.extensions</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <!-- FRAGILE: These buttons are manually removed for app mode in ephy-header-bar.c. -->
-            <child>
-              <object class="GtkSeparator" id="override-text-encoding-separator">
-                <property name="orientation">horizontal</property>
-                <property name="margin-top">6</property>
-                <property name="margin-bottom">6</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton" id="override-text-encoding-button">
-                <property name="text" translatable="yes">_Override Text Encoding…</property>
-                <property name="action-name">win.encoding</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkSeparator">
-                <property name="orientation">horizontal</property>
-                <property name="margin-top">6</property>
-                <property name="margin-bottom">6</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkModelButton">
-                <property name="text" translatable="yes">Pr_eferences</property>
-                <property name="action-name">app.preferences</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <!-- FRAGILE: This button is manually removed for app mode in ephy-header-bar.c. -->
-            <child>
-              <object class="GtkModelButton" id="keyboard-shortcuts-button">
-                <property name="text" translatable="yes">_Keyboard Shortcuts</property>
-                <property name="action-name">app.shortcuts</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <!-- FRAGILE: This button is manually removed for app mode/Pantheon in ephy-header-bar.c. -->
-            <child>
-              <object class="GtkModelButton" id="help-button">
-                <property name="text" translatable="yes">_Help</property>
-                <property name="action-name">app.help</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-            <!-- FRAGILE: This button is manually removed for Pantheon in ephy-header-bar.c. -->
-            <child>
-              <object class="GtkModelButton" id="about-button">
-                <property name="text" translatable="yes">_About Web</property>
-                <property name="action-name">app.about</property>
-                <property name="visible">True</property>
-              </object>
-            </child>
-
+          <object class="GtkButton">
+            <property name="tooltip_text" translatable="yes" context="tooltip">Zoom In</property>
+            <property name="icon_name">zoom-in-symbolic</property>
+            <property name="action-name">win.zoom-in</property>
           </object>
         </child>
       </object>
-      <packing>
-        <property name="submenu">main</property>
-      </packing>
     </child>
-    <child>
+    <child type="header">
       <object class="GtkBox">
-        <property name="margin-top">12</property>
-        <property name="orientation">vertical</property>
-        <property name="visible">True</property>
-        <style>
-          <class name="page-menu-contents"/>
-        </style>
-        <child>
-          <object class="GtkModelButton">
-            <property name="text" translatable="yes">Import and Export</property>
-            <property name="visible">True</property>
-            <property name="centered">True</property>
-            <property name="menu-name">main</property>
-            <property name="inverted">True</property>
-            <property name="margin-bottom">6</property>
-          </object>
-        </child>
+        <property name="orientation">horizontal</property>
+        <property name="spacing">6</property>
+        <property name="homogeneous">True</property>
+        <property name="margin-top">6</property>
         <child>
-          <object class="GtkModelButton">
-            <property name="text" translatable="yes">I_mport Bookmarks…</property>
-            <property name="action-name">app.import-bookmarks</property>
-            <property name="visible">True</property>
+          <object class="GtkButton">
+            <property name="tooltip_text" translatable="yes">Print…</property>
+            <property name="icon-name">document-print-symbolic</property>
+            <property name="action-name">win.print</property>
           </object>
         </child>
         <child>
-          <object class="GtkModelButton">
-            <property name="text" translatable="yes">E_xport Bookmarks…</property>
-            <property name="action-name">app.export-bookmarks</property>
-            <property name="visible">True</property>
+          <object class="GtkButton">
+            <property name="tooltip_text" translatable="yes">Find…</property>
+            <property name="icon-name">edit-find-symbolic</property>
+            <property name="action-name">win.find</property>
           </object>
         </child>
         <child>
-          <object class="GtkSeparator">
-            <property name="orientation">horizontal</property>
-            <property name="margin-top">6</property>
-            <property name="margin-bottom">6</property>
-            <property name="visible">True</property>
+          <object class="GtkButton">
+            <property name="tooltip_text" translatable="yes">Fullscreen</property>
+            <property name="icon-name">view-fullscreen-symbolic</property>
+            <property name="action-name">win.fullscreen</property>
           </object>
         </child>
         <child>
-          <object class="GtkModelButton">
-            <property name="text" translatable="yes">Import _Passwords…</property>
-            <property name="action-name">app.import-passwords</property>
-            <property name="visible">True</property>
+          <object class="GtkButton" id="combined_stop_reload_button">
+            <property name="tooltip_text" translatable="yes">Reload</property>
+            <property name="icon-name">view-refresh-symbolic</property>
+            <property name="action-name">toolbar.combined-stop-reload</property>
           </object>
         </child>
       </object>
-      <packing>
-        <property name="submenu">import_export</property>
-      </packing>
     </child>
   </object>
-    <object class="GThemedIcon" id="zoom_out">
-    <property name="name">zoom-out-symbolic</property>
-  </object>
-  <object class="GThemedIcon" id="zoom_in">
-    <property name="name">zoom-in-symbolic</property>
-  </object>
-  <object class="GThemedIcon" id="print">
-    <property name="name">document-print-symbolic</property>
-  </object>
-  <object class="GThemedIcon" id="find">
-    <property name="name">system-search-symbolic</property>
-  </object>
-  <object class="GThemedIcon" id="fullscreen">
-    <property name="name">view-fullscreen-symbolic</property>
-  </object>
 </interface>
diff --git a/src/resources/gtk/page-row.ui b/src/resources/gtk/page-row.ui
index 0cb1a93e5..e3a301fe8 100644
--- a/src/resources/gtk/page-row.ui
+++ b/src/resources/gtk/page-row.ui
@@ -6,77 +6,59 @@
     <signal name="map" handler="update_spinner" swapped="true"/>
     <signal name="unmap" handler="update_spinner" swapped="true"/>
     <child>
-      <object class="GtkEventBox">
-        <property name="visible">True</property>
-        <signal name="button-release-event" handler="button_release_event" swapped="no"/>
+      <object class="GtkGestureClick">
+        <property name="button">2</property>
+        <signal name="released" handler="released_cb"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="margin-start">3</property>
+        <property name="margin-end">1</property>
         <child>
-          <object class="GtkBox" id="box">
-            <property name="visible">True</property>
-            <property name="margin-start">3</property>
-            <property name="margin-end">1</property>
-            <child>
-              <object class="GtkStack" id="icon_stack">
-                <property name="visible">True</property>
-                <property name="margin_start">4</property>
-                <property name="margin_end">4</property>
-                <child>
-                  <object class="GtkImage" id="icon">
-                    <property name="visible">True</property>
-                    <property name="icon_size">1</property>
-                    <property name="pixel_size">16</property>
-                    <property name="margin_top">6</property>
-                    <property name="margin_bottom">6</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkSpinner" id="spinner">
-                    <property name="visible">True</property>
-                  </object>
-                </child>
-              </object>
-            </child>
+          <object class="GtkStack" id="icon_stack">
+            <property name="margin_start">4</property>
+            <property name="margin_end">4</property>
             <child>
-              <object class="GtkLabel" id="title">
-                <property name="visible">True</property>
-                <property name="margin_start">4</property>
-                <property name="hexpand">True</property>
-                <property name="ellipsize">end</property>
-                <property name="single_line_mode">True</property>
-                <property name="xalign">0</property>
+              <object class="GtkImage" id="icon">
+                <property name="pixel_size">16</property>
+                <property name="margin_top">6</property>
+                <property name="margin_bottom">6</property>
               </object>
             </child>
             <child>
-              <object class="GtkImage" id="speaker_icon">
-                <property name="visible">True</property>
-                <property name="margin_start">8</property>
-                <property name="icon_name">audio-volume-high-symbolic</property>
-                <property name="icon_size">1</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton" id="close_button">
-                <property name="visible">True</property>
-                <property name="focus_on_click">False</property>
-                <property name="receives_default">False</property>
-                <property name="tooltip_text" translatable="yes">Close page</property>
-                <property name="valign">center</property>
-                <signal name="clicked" handler="close_clicked_cb" swapped="yes"/>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon_name">window-close-symbolic</property>
-                    <property name="icon_size">1</property>
-                  </object>
-                </child>
-                <style>
-                  <class name="flat"/>
-                  <class name="image-button"/>
-                  <class name="close-button"/>
-                </style>
-              </object>
+              <object class="GtkSpinner" id="spinner"/>
             </child>
           </object>
         </child>
+        <child>
+          <object class="GtkLabel" id="title">
+            <property name="margin_start">4</property>
+            <property name="hexpand">True</property>
+            <property name="ellipsize">end</property>
+            <property name="single_line_mode">True</property>
+            <property name="xalign">0</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkImage" id="speaker_icon">
+            <property name="margin_start">8</property>
+            <property name="icon_name">audio-volume-high-symbolic</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="close_button">
+            <property name="focus_on_click">False</property>
+            <property name="tooltip_text" translatable="yes">Close page</property>
+            <property name="valign">center</property>
+            <property name="icon_name">window-close-symbolic</property>
+            <signal name="clicked" handler="close_clicked_cb" swapped="yes"/>
+            <style>
+              <class name="flat"/>
+              <class name="close-button"/>
+            </style>
+          </object>
+        </child>
       </object>
     </child>
   </template>
diff --git a/src/resources/gtk/pages-button.ui b/src/resources/gtk/pages-button.ui
index b08d4c320..c42512a02 100644
--- a/src/resources/gtk/pages-button.ui
+++ b/src/resources/gtk/pages-button.ui
@@ -2,17 +2,17 @@
 <interface>
   <!-- interface-requires gtk+ 3.16 -->
   <template class="EphyPagesButton" parent="GtkButton">
+    <style>
+    <class name="image-button"/>
+    </style>
     <child>
       <object class="GtkOverlay">
-        <property name="visible">True</property>
+        <property name="can-focus">False</property>
         <child>
-          <object class="GtkImage" id="pages_icon">
-            <property name="visible">True</property>
-          </object>
+          <object class="GtkImage" id="pages_icon"/>
         </child>
         <child type="overlay">
           <object class="GtkLabel" id="pages_label">
-            <property name="visible">True</property>
             <property name="width-chars">2</property>
             <attributes>
               <attribute name="font-features" value="tnum=1"/>
@@ -20,9 +20,6 @@
               <attribute name="weight" value="ultrabold"/>
             </attributes>
           </object>
-          <packing>
-            <property name="pass_through">True</property>
-          </packing>
         </child>
       </object>
     </child>
diff --git a/src/resources/gtk/pages-popover.ui b/src/resources/gtk/pages-popover.ui
index 9557d511a..e17212aa9 100644
--- a/src/resources/gtk/pages-popover.ui
+++ b/src/resources/gtk/pages-popover.ui
@@ -3,18 +3,16 @@
   <!-- interface-requires gtk+ 3.16 -->
   <template class="EphyPagesPopover" parent="GtkPopover">
     <style>
-      <class name="combo"/>
+      <class name="menu"/>
     </style>
     <child>
       <object class="GtkScrolledWindow" id="scrolled_window">
         <property name="hscrollbar_policy">never</property>
         <property name="propagate_natural_height">True</property>
         <property name="max-content-height">700</property>
-        <property name="visible">True</property>
         <child>
           <object class="GtkListBox" id="list_box">
             <property name="selection_mode">single</property>
-            <property name="visible">True</property>
             <property name="width_request">300</property>
             <signal name="row-activated" handler="row_activated_cb" swapped="true"/>
             <style>
diff --git a/src/resources/gtk/pages-view.ui b/src/resources/gtk/pages-view.ui
index 9ee40daa3..1077a53db 100644
--- a/src/resources/gtk/pages-view.ui
+++ b/src/resources/gtk/pages-view.ui
@@ -5,53 +5,38 @@
     <property name="name">pages-view</property>
     <property name="orientation">vertical</property>
     <child>
-      <object class="HdyHeaderBar">
-        <property name="visible">True</property>
-        <property name="show-close-button">True</property>
-        <property name="title" translatable="yes">Tabs</property>
-        <property name="has-subtitle">False</property>
-        <child>
+      <object class="GtkHeaderBar">
+        <property name="title-widget">
+          <object class="AdwWindowTitle">
+            <property name="title" translatable="yes">Tabs</property>
+          </object>
+        </property>
+        <child type="start">
           <object class="GtkButton">
-            <property name="visible">True</property>
             <property name="action-name">win.content</property>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="icon-name">go-previous-symbolic</property>
-              </object>
-            </child>
+            <property name="icon-name">go-previous-symbolic</property>
           </object>
         </child>
-        <child>
+        <child type="end">
           <object class="GtkButton">
-            <property name="visible">True</property>
             <property name="action-name">win.new-tab</property>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="icon-name">tab-new-symbolic</property>
-              </object>
-            </child>
+            <property name="icon-name">tab-new-symbolic</property>
           </object>
-          <packing>
-            <property name="pack-type">end</property>
-          </packing>
         </child>
       </object>
     </child>
     <child>
       <object class="GtkScrolledWindow">
-        <property name="visible">True</property>
         <property name="hscrollbar_policy">never</property>
         <property name="vexpand">True</property>
         <child>
           <object class="GtkListBox" id="list_box">
             <property name="selection_mode">single</property>
-            <property name="visible">True</property>
             <property name="width_request">300</property>
             <signal name="row-activated" handler="row_activated_cb" swapped="true"/>
             <style>
               <class name="pages-list"/>
+              <class name="navigation-sidebar"/>
             </style>
           </object>
         </child>
diff --git a/src/resources/gtk/passwords-view.ui b/src/resources/gtk/passwords-view.ui
index ebd15e246..d5f3bd6fe 100644
--- a/src/resources/gtk/passwords-view.ui
+++ b/src/resources/gtk/passwords-view.ui
@@ -33,31 +33,27 @@
     <signal name="notify::search-text" handler="on_search_text_changed" swapped="yes"/>
     <child>
       <object class="GtkBox">
-        <property name="visible">True</property>
         <property name="orientation">vertical</property>
         <child>
           <object class="GtkScrolledWindow">
             <property name="width_request">300</property>
             <property name="height_request">300</property>
-            <property name="visible">True</property>
             <property name="vexpand">True</property>
             <property name="min_content_width">300</property>
             <property name="min_content_height">300</property>
             <child>
-              <object class="HdyClamp">
-                <property name="visible">True</property>
+              <object class="AdwClamp">
                 <property name="margin_start">6</property>
                 <property name="margin_end">6</property>
                 <child>
                   <object class="GtkListBox" id="listbox">
-                    <property name="visible">True</property>
                     <property name="margin_top">6</property>
                     <property name="margin_bottom">6</property>
                     <property name="valign">start</property>
                     <property name="selection_mode">multiple</property>
                     <property name="activate_on_single_click">False</property>
                     <style>
-                      <class name="content"/>
+                      <class name="boxed-list"/>
                     </style>
                   </object>
                 </child>
diff --git a/src/resources/gtk/prefs-appearance-page.ui b/src/resources/gtk/prefs-appearance-page.ui
index 3c1987985..b7180bb2d 100644
--- a/src/resources/gtk/prefs-appearance-page.ui
+++ b/src/resources/gtk/prefs-appearance-page.ui
@@ -1,60 +1,51 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.0"/>
-  <template class="PrefsAppearancePage" parent="HdyPreferencesPage">
+  <template class="PrefsAppearancePage" parent="AdwPreferencesPage">
     <property name="icon_name">document-edit-symbolic</property>
     <property name="title" translatable="yes">Appearance</property>
-    <property name="visible">True</property>
     <child>
-      <object class="HdyPreferencesGroup">
+      <object class="AdwPreferencesGroup">
         <property name="title" translatable="yes">Fonts</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyExpanderRow" id="use_gnome_fonts_row">
+          <object class="AdwExpanderRow" id="use_gnome_fonts_row">
             <property name="show_enable_switch">True</property>
             <property name="title" translatable="yes">Use Custom Fonts</property>
-            <property name="visible">True</property>
             <child>
-              <object class="HdyActionRow">
+              <object class="AdwActionRow">
                 <property name="activatable">False</property>
                 <property name="title" translatable="yes">Sans serif font</property>
-                <property name="visible">True</property>
                 <child>
                   <object class="GtkFontButton" id="sans_fontbutton">
                     <property name="font">Sans 12</property>
                     <property name="use-font">True</property>
                     <property name="valign">center</property>
-                    <property name="visible">True</property>
                   </object>
                 </child>
               </object>
             </child>
             <child>
-              <object class="HdyActionRow">
+              <object class="AdwActionRow">
                 <property name="activatable">False</property>
                 <property name="title" translatable="yes">Serif font</property>
-                <property name="visible">True</property>
                 <child>
                   <object class="GtkFontButton" id="serif_fontbutton">
                     <property name="font">Sans 12</property>
                     <property name="use-font">True</property>
                     <property name="valign">center</property>
-                    <property name="visible">True</property>
                   </object>
                 </child>
               </object>
             </child>
             <child>
-              <object class="HdyActionRow">
+              <object class="AdwActionRow">
                 <property name="activatable">False</property>
                 <property name="title" translatable="yes">Monospace font</property>
-                <property name="visible">True</property>
                 <child>
                   <object class="GtkFontButton" id="mono_fontbutton">
                     <property name="font">Sans 12</property>
                     <property name="use-font">True</property>
                     <property name="valign">center</property>
-                    <property name="visible">True</property>
                   </object>
                 </child>
               </object>
@@ -64,110 +55,106 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup" id="reader_mode_box">
+      <object class="AdwPreferencesGroup" id="reader_mode_box">
         <property name="title" translatable="yes">Reader Mode</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyComboRow" id="reader_mode_font_style">
+          <object class="AdwComboRow" id="reader_mode_font_style">
             <property name="title" translatable="yes">Font Style</property>
-            <property name="visible">True</property>
+            <property name="expression">
+              <closure type="gchararray" function="reader_font_style_get_name">
+                <lookup name="value" type="AdwEnumListItem"/>
+              </closure>
+            </property>
+            <property name="model">
+              <object class="AdwEnumListModel">
+                <property name="enum-type">EphyPrefsReaderFontStyle</property>
+              </object>
+            </property>
           </object>
         </child>
         <child>
-          <object class="HdyComboRow" id="reader_mode_color_scheme">
+          <object class="AdwComboRow" id="reader_mode_color_scheme">
             <property name="title" translatable="yes">Color Scheme</property>
-            <property name="visible">True</property>
+            <property name="expression">
+              <closure type="gchararray" function="reader_color_scheme_get_name">
+                <lookup name="value" type="AdwEnumListItem"/>
+              </closure>
+            </property>
+            <property name="model">
+              <object class="AdwEnumListModel">
+                <property name="enum-type">EphyPrefsReaderColorScheme</property>
+              </object>
+            </property>
           </object>
         </child>
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup">
+      <object class="AdwPreferencesGroup">
         <property name="title" translatable="yes">Style</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">css_switch</property>
             <property name="title" translatable="yes">Use Custom Stylesheet</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="css_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
             <child>
               <object class="GtkSeparator">
                 <property name="margin_bottom">8</property>
                 <property name="margin_top">8</property>
-                <property name="visible">True</property>
               </object>
             </child>
             <child>
               <object class="GtkButton" id="css_edit_button">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
-                <style>
-                  <class name="image-button"/>
-                </style>
-                <child>
-                  <object class="GtkImage">
-                    <property name="icon_name">document-edit-symbolic</property>
-                    <property name="visible">True</property>
-                  </object>
-                </child>
+                <property name="icon_name">document-edit-symbolic</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">js_switch</property>
             <property name="title" translatable="yes">Use Custom JavaScript</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="js_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
             <child>
               <object class="GtkSeparator">
                 <property name="margin_bottom">8</property>
                 <property name="margin_top">8</property>
-                <property name="visible">True</property>
               </object>
             </child>
             <child>
               <object class="GtkButton" id="js_edit_button">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
-                <style>
-                  <class name="image-button"/>
-                </style>
-                <child>
-                  <object class="GtkImage">
-                    <property name="icon_name">document-edit-symbolic</property>
-                    <property name="visible">True</property>
-                  </object>
-                </child>
+                <property name="icon_name">document-edit-symbolic</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable">False</property>
             <property name="title" translatable="yes">Default Zoom Level</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSpinButton" id="default_zoom_spin_button">
-                <property name="adjustment">zoom_adjustment</property>
-                <property name="input_purpose">number</property>
+                <property name="adjustment">
+                  <object class="GtkAdjustment" id="zoom_adjustment">
+                    <property name="lower">33</property>
+                    <property name="upper">300</property>
+                    <property name="step_increment">1</property>
+                    <property name="page_increment">10</property>
+                  </object>
+                </property>
                 <property name="max_width_chars">5</property>
                 <property name="valign">center</property>
                 <property name="value">100</property>
-                <property name="visible">True</property>
                 <signal name="output" handler="on_default_zoom_spin_button_output"/>
                 <signal name="value-changed" handler="on_default_zoom_spin_button_value_changed"/>
               </object>
@@ -185,10 +172,4 @@
       <widget name="mono_fontbutton"/>
     </widgets>
   </object>
-  <object class="GtkAdjustment" id="zoom_adjustment">
-    <property name="lower">33</property>
-    <property name="upper">300</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
 </interface>
diff --git a/src/resources/gtk/prefs-dialog.ui b/src/resources/gtk/prefs-dialog.ui
index 9556fbbe5..1588dc91c 100644
--- a/src/resources/gtk/prefs-dialog.ui
+++ b/src/resources/gtk/prefs-dialog.ui
@@ -1,17 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.0"/>
-  <template class="EphyPrefsDialog" parent="HdyPreferencesWindow">
-    <property name="role">epiphany-preferences</property>
+  <template class="EphyPrefsDialog" parent="AdwPreferencesWindow">
     <property name="modal">True</property>
-    <property name="window-position">center</property>
     <property name="destroy-with-parent">True</property>
     <property name="icon-name">gtk-preferences</property>
     <property name="default-width">640</property>
     <property name="default-height">800</property>
-    <property name="can-swipe-back">True</property>
-    <signal name="key-press-event" handler="on_key_press_event"/>
-    <signal name="delete-event" handler="on_delete_event"/>
+    <property name="can-navigate-back">True</property>
+    <signal name="close-request" handler="on_close_request"/>
     <child>
       <object class="PrefsGeneralPage" id="general_page"/>
     </child>
diff --git a/src/resources/gtk/prefs-general-page.ui b/src/resources/gtk/prefs-general-page.ui
index 6c5541324..def26707e 100644
--- a/src/resources/gtk/prefs-general-page.ui
+++ b/src/resources/gtk/prefs-general-page.ui
@@ -1,46 +1,38 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.0"/>
-  <template class="PrefsGeneralPage" parent="HdyPreferencesPage">
+  <template class="PrefsGeneralPage" parent="AdwPreferencesPage">
     <property name="icon_name">applications-system-symbolic</property>
     <property name="title" translatable="yes">General</property>
-    <property name="visible">True</property>
     <child>
-      <object class="HdyPreferencesGroup" id="webapp_box">
+      <object class="AdwPreferencesGroup" id="webapp_box">
         <property name="title" translatable="yes">Web Application</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow" id="webapp_icon_row">
+          <object class="AdwActionRow" id="webapp_icon_row">
             <property name="activatable">False</property>
             <property name="title" translatable="yes">_Icon</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkButton" id="webapp_icon_button">
-                <property name="visible">true</property>
                 <property name="valign">center</property>
                 <signal name="clicked" handler="on_webapp_icon_button_clicked"/>
                 <style>
                   <class name="image-button"/>
                 </style>
                 <child>
-                  <object class="GtkImage" id="webapp_icon">
-                    <property name="visible">True</property>
-                  </object>
+                  <object class="GtkImage" id="webapp_icon"/>
                </child>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow" id="webapp_url_row">
+          <object class="AdwActionRow" id="webapp_url_row">
             <property name="activatable">False</property>
             <property name="title" translatable="yes">_Homepage</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkEntry" id="webapp_url">
-                <property name="visible">True</property>
                 <property name="valign">center</property>
                 <signal name="changed" handler="on_webapp_entry_changed"/>
               </object>
@@ -48,14 +40,12 @@
           </object>
         </child>
         <child>
-          <object class="HdyActionRow" id="webapp_title_row">
+          <object class="AdwActionRow" id="webapp_title_row">
             <property name="activatable">False</property>
             <property name="title" translatable="yes">_Title</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkEntry" id="webapp_title">
-                <property name="visible">True</property>
                 <property name="valign">center</property>
                 <signal name="changed" handler="on_webapp_entry_changed"/>
               </object>
@@ -63,15 +53,13 @@
           </object>
         </child>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable">False</property>
             <property name="title" translatable="yes">_Manage Additional URLs</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkButton" id="webapp_additional_urls_dialog_button">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
                 <signal name="clicked" handler="on_manage_webapp_additional_urls_button_clicked"/>
                 <style>
                   <class name="image-button"/>
@@ -79,7 +67,6 @@
                 <child>
                   <object class="GtkImage">
                     <property name="icon_name">emblem-system-symbolic</property>
-                    <property name="visible">True</property>
                   </object>
                 </child>
               </object>
@@ -89,33 +76,28 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup">
+      <object class="AdwPreferencesGroup">
         <property name="title" translatable="yes">Web Content</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow" id="adblock_allow_row">
+          <object class="AdwActionRow" id="adblock_allow_row">
             <property name="activatable_widget">adblock_allow_switch</property>
             <property name="title" translatable="yes">Block _Advertisements</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="adblock_allow_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">popups_allow_switch</property>
             <property name="title" translatable="yes">Block _Popup Windows</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="popups_allow_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
@@ -123,49 +105,42 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup" id="homepage_box">
+      <object class="AdwPreferencesGroup" id="homepage_box">
         <property name="title" translatable="yes">Homepage</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">new_tab_homepage_radiobutton</property>
             <property name="title" translatable="yes">Most _Visited Pages</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child type="prefix">
-              <object class="GtkRadioButton" id="new_tab_homepage_radiobutton">
+              <object class="GtkCheckButton" id="new_tab_homepage_radiobutton">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">blank_homepage_radiobutton</property>
             <property name="title" translatable="yes">_Blank Page</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child type="prefix">
-              <object class="GtkRadioButton" id="blank_homepage_radiobutton">
+              <object class="GtkCheckButton" id="blank_homepage_radiobutton">
                 <property name="valign">center</property>
                 <property name="group">new_tab_homepage_radiobutton</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">custom_homepage_radiobutton</property>
             <property name="title" translatable="yes">_Custom</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child type="prefix">
-              <object class="GtkRadioButton" id="custom_homepage_radiobutton">
+              <object class="GtkCheckButton" id="custom_homepage_radiobutton">
                 <property name="valign">center</property>
                 <property name="group">new_tab_homepage_radiobutton</property>
-                <property name="visible">True</property>
               </object>
             </child>
             <child>
@@ -173,7 +148,6 @@
                 <property name="hexpand">True</property>
                 <property name="secondary-icon-name">edit-clear-symbolic</property>
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
@@ -181,39 +155,33 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup" id="download_box">
+      <object class="AdwPreferencesGroup" id="download_box">
         <property name="title" translatable="yes">Downloads</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">ask_on_download_switch</property>
             <property name="title" translatable="yes">Ask o_n Download</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="ask_on_download_switch">
                 <property name="margin-start">12</property>
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow" id="download_folder_row">
+          <object class="AdwActionRow" id="download_folder_row">
             <property name="sensitive" bind-source="ask_on_download_switch" bind-property="active" 
bind-flags="sync-create|invert-boolean"/>
             <property name="title" translatable="yes">_Download Folder</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <property name="activatable">True</property>
             <signal name="activated" handler="download_folder_row_activated_cb" swapped="yes"/>
             <child>
               <object class="GtkBox">
-                <property name="visible">True</property>
                 <property name="spacing">6</property>
                 <child>
                   <object class="GtkImage">
-                    <property name="visible">True</property>
                     <property name="icon-name">folder-symbolic</property>
                     <style>
                       <class name="dim-label"/>
@@ -222,7 +190,6 @@
                 </child>
                 <child>
                   <object class="GtkLabel" id="download_folder_label">
-                    <property name="visible">True</property>
                     <property name="ellipsize">end</property>
                     <property name="xalign">1</property>
                     <style>
@@ -234,7 +201,6 @@
             </child>
             <child>
               <object class="GtkImage">
-                <property name="visible">True</property>
                 <property name="icon-name">go-next-symbolic</property>
                 <style>
                   <class name="dim-label"/>
@@ -246,44 +212,36 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup" id="search_engine_group">
+      <object class="AdwPreferencesGroup" id="search_engine_group">
         <property name="title" translatable="yes">Search Engines</property>
-        <property name="visible">True</property>
         <child>
-          <object class="EphySearchEngineListBox">
-            <property name="visible">True</property>
-          </object>
+          <object class="EphySearchEngineListBox"/>
         </child>
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup" id="session_box">
+      <object class="AdwPreferencesGroup" id="session_box">
         <property name="title" translatable="yes">Session</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">start_in_incognito_mode_switch</property>
             <property name="title" translatable="yes">Start in _Incognito Mode</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="start_in_incognito_mode_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow" id="restore_session_row">
+          <object class="AdwActionRow" id="restore_session_row">
             <property name="activatable_widget">restore_session_switch</property>
             <property name="title" translatable="yes">_Restore Tabs on Startup</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="restore_session_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
@@ -291,33 +249,28 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup" id="browsing_box">
+      <object class="AdwPreferencesGroup" id="browsing_box">
         <property name="title" translatable="yes">Browsing</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">enable_mouse_gesture_switch</property>
             <property name="title" translatable="yes">Mouse _Gestures</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="enable_mouse_gesture_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">enable_switch_to_new_tab</property>
             <property name="title" translatable="yes">S_witch Immediately to New Tabs</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="enable_switch_to_new_tab">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
@@ -325,34 +278,28 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup" id="lang_group">
+      <object class="AdwPreferencesGroup" id="lang_group">
         <property name="title" translatable="yes">Languages</property>
-        <property name="visible">True</property>
         <child>
           <object class="GtkListBox" id="lang_listbox">
             <property name="selection_mode">none</property>
-            <property name="visible">True</property>
             <style>
-              <class name="content"/>
+              <class name="boxed-list"/>
             </style>
           </object>
         </child>
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup">
-        <property name="title" translatable="yes"></property>
-        <property name="visible">True</property>
+      <object class="AdwPreferencesGroup">
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">enable_spell_checking_switch</property>
             <property name="title" translatable="yes">_Spell Checking</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="enable_spell_checking_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
diff --git a/src/resources/gtk/prefs-lang-dialog.ui b/src/resources/gtk/prefs-lang-dialog.ui
index 81f7c3fbe..01e6769f8 100644
--- a/src/resources/gtk/prefs-lang-dialog.ui
+++ b/src/resources/gtk/prefs-lang-dialog.ui
@@ -2,9 +2,7 @@
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
   <object class="GtkDialog" id="add_language_dialog">
-    <property name="role">epiphany-preferences-add-language</property>
     <property name="destroy_with_parent">True</property>
-    <property name="type_hint">dialog</property>
     <property name="default_width">300</property>
     <property name="default_height">420</property>
     <property name="use_header_bar">1</property>
@@ -12,8 +10,6 @@
     <child type="action">
       <object class="GtkButton" id="cancelbutton1">
         <property name="label" translatable="yes">_Cancel</property>
-        <property name="visible">True</property>
-        <property name="can_default">True</property>
         <property name="receives_default">False</property>
         <property name="use_underline">True</property>
       </object>
@@ -21,20 +17,16 @@
     <child type="action">
       <object class="GtkButton" id="add_button">
         <property name="label" translatable="yes">_Add</property>
-        <property name="visible">True</property>
-        <property name="can_default">True</property>
         <property name="receives_default">False</property>
         <property name="use_underline">True</property>
       </object>
     </child>
-    <child internal-child="vbox">
+    <child internal-child="content_area">
       <object class="GtkBox" id="dialog-vbox3">
-        <property name="visible">True</property>
         <property name="orientation">vertical</property>
         <property name="spacing">2</property>
         <child>
           <object class="GtkBox" id="vbox198">
-            <property name="visible">True</property>
             <property name="orientation">vertical</property>
             <property name="margin-top">10</property>
             <property name="margin-bottom">10</property>
@@ -43,7 +35,6 @@
             <property name="spacing">6</property>
             <child>
               <object class="GtkLabel" id="label1309">
-                <property name="visible">True</property>
                 <property name="xalign">0</property>
                 <property name="label" translatable="yes">Choose a language:</property>
                 <property name="use_underline">True</property>
@@ -52,7 +43,6 @@
             </child>
             <child>
               <object class="GtkScrolledWindow" id="scrolledwindow3">
-                <property name="visible">True</property>
                 <property name="hscrollbar_policy">never</property>
                 <property name="vexpand">True</property>
                 <style>
@@ -60,7 +50,6 @@
                 </style>
                 <child>
                   <object class="GtkTreeView" id="languages_treeview">
-                    <property name="visible">True</property>
                     <property name="headers_visible">False</property>
                     <child internal-child="selection">
                       <object class="GtkTreeSelection" id="treeview-selection2"/>
diff --git a/src/resources/gtk/prefs-privacy-page.ui b/src/resources/gtk/prefs-privacy-page.ui
index bf61d6b91..9ebf61acf 100644
--- a/src/resources/gtk/prefs-privacy-page.ui
+++ b/src/resources/gtk/prefs-privacy-page.ui
@@ -1,24 +1,20 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.0"/>
-  <template class="PrefsPrivacyPage" parent="HdyPreferencesPage">
+  <template class="PrefsPrivacyPage" parent="AdwPreferencesPage">
     <property name="icon_name">preferences-system-privacy-symbolic</property>
     <property name="title" translatable="yes">Privacy</property>
-    <property name="visible">True</property>
     <child>
-      <object class="HdyPreferencesGroup" id="safe_browsing_group">
+      <object class="AdwPreferencesGroup" id="safe_browsing_group">
         <property name="title" translatable="yes">Web Safety</property>
-        <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">enable_safe_browsing_switch</property>
             <property name="title" translatable="yes">Block Dangerous Web_sites</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="enable_safe_browsing_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
@@ -26,25 +22,23 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup">
+      <object class="AdwPreferencesGroup">
         <property name="title" translatable="yes">Web Tracking</property>
         <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">enable_itp_switch</property>
             <property name="title" translatable="yes">Intelligent _Tracking Prevention</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="enable_itp_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
         </child>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">enable_website_data_storage_switch</property>
             <property name="subtitle" translatable="yes">Allow websites to store cookies, databases, and 
local storage data.</property>
             <property name="title" translatable="yes">_Website Data Storage</property>
@@ -61,11 +55,11 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup">
+      <object class="AdwPreferencesGroup">
         <property name="title" translatable="yes">Search Suggestions</property>
         <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <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>
@@ -82,11 +76,11 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup">
+      <object class="AdwPreferencesGroup">
         <property name="title" translatable="yes">Personal Data</property>
         <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable">True</property>
             <property name="subtitle" translatable="yes">You can clear stored personal data.</property>
             <property name="title" translatable="yes">Clear Personal _Data</property>
@@ -104,11 +98,11 @@
       </object>
     </child>
     <child>
-      <object class="HdyPreferencesGroup">
+      <object class="AdwPreferencesGroup">
         <property name="title" translatable="yes">Passwords</property>
         <property name="visible">True</property>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable">True</property>
             <property name="title" translatable="yes">_Passwords</property>
             <property name="use_underline">True</property>
@@ -123,15 +117,13 @@
           </object>
         </child>
         <child>
-          <object class="HdyActionRow">
+          <object class="AdwActionRow">
             <property name="activatable_widget">remember_passwords_switch</property>
             <property name="title" translatable="yes">_Remember Passwords</property>
             <property name="use_underline">True</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkSwitch" id="remember_passwords_switch">
                 <property name="valign">center</property>
-                <property name="visible">True</property>
               </object>
             </child>
           </object>
diff --git a/src/resources/gtk/search-engine-listbox.ui b/src/resources/gtk/search-engine-listbox.ui
index 50a82fd47..a8e662df1 100644
--- a/src/resources/gtk/search-engine-listbox.ui
+++ b/src/resources/gtk/search-engine-listbox.ui
@@ -1,17 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.20"/>
-  <template class="EphySearchEngineListBox" parent="GtkBin">
-    <property name="visible">True</property>
-    <child>
+  <template class="EphySearchEngineListBox" parent="AdwBin">
+    <property name="child">
       <object class="GtkListBox" id="list">
-        <property name="visible">True</property>
         <property name="selection-mode">none</property>
         <signal name="row-activated" handler="on_add_search_engine_row_clicked_cb" swapped="yes"/>
         <style>
-          <class name="content"/>
+          <class name="boxed-list"/>
         </style>
       </object>
-    </child>
+    </property>
   </template>
 </interface>
diff --git a/src/resources/gtk/search-engine-row.ui b/src/resources/gtk/search-engine-row.ui
index fa41f5f7b..0638ad0b6 100644
--- a/src/resources/gtk/search-engine-row.ui
+++ b/src/resources/gtk/search-engine-row.ui
@@ -1,11 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.20"/>
-  <template class="EphySearchEngineRow" parent="HdyExpanderRow">
-    <property name="visible">True</property>
+  <template class="EphySearchEngineRow" parent="AdwExpanderRow">
     <child type="prefix">
-      <object class="GtkRadioButton" id="radio_button">
-        <property name="visible">True</property>
+      <object class="GtkCheckButton" id="radio_button">
         <property name="valign">center</property>
         <property name="tooltip-text" translatable="yes">Selects the default search engine</property>
         <signal name="notify::active" handler="on_radio_button_active_changed_cb" 
object="EphySearchEngineRow" swapped="yes"/>
@@ -14,10 +12,8 @@
     <child>
       <object class="GtkListBoxRow">
         <property name="activatable">False</property>
-        <property name="visible">True</property>
         <child>
           <object class="GtkGrid">
-            <property name="visible">True</property>
             <property name="margin-top">12</property>
             <property name="margin-bottom">12</property>
             <property name="margin-start">12</property>
@@ -27,115 +23,103 @@
             <!-- Name field -->
             <child>
               <object class="GtkLabel">
-                <property name="visible">True</property>
                 <property name="label" translatable="yes">Name</property>
                 <property name="xalign">1</property>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">0</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">0</property>
-              </packing>
             </child>
             <child>
               <object class="GtkEntry" id="name_entry">
-                <property name="visible">True</property>
                 <property name="hexpand">True</property>
+                <layout>
+                  <property name="column">1</property>
+                  <property name="row">0</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">0</property>
-              </packing>
             </child>
             <!-- Address field -->
             <child>
               <object class="GtkLabel">
-                <property name="visible">True</property>
                 <property name="label" translatable="yes">Address</property>
                 <property name="xalign">1</property>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">1</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">1</property>
-              </packing>
             </child>
             <child>
               <object class="GtkEntry" id="address_entry">
-                <property name="visible">True</property>
                 <property name="hexpand">True</property>
-
                 <property name="placeholder-text">https://www.example.com/search?q=%s</property>
                 <property name="input-purpose">url</property>
                 <property name="secondary-icon-sensitive">False</property>
+                <layout>
+                  <property name="column">1</property>
+                  <property name="row">1</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">1</property>
-              </packing>
             </child>
             <!-- Bang field -->
             <child>
               <object class="GtkLabel">
-                <property name="visible">True</property>
                 <property name="label" translatable="yes">Shortcut</property>
                 <property name="xalign">1</property>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">2</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">2</property>
-              </packing>
             </child>
             <child>
               <object class="GtkEntry" id="bang_entry">
-                <property name="visible">True</property>
                 <property name="hexpand">True</property>
-
                 <property name="placeholder-text">!e</property>
                 <property name="input-purpose">url</property>
                 <property name="secondary-icon-sensitive">False</property>
+                <layout>
+                  <property name="column">1</property>
+                  <property name="row">2</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">2</property>
-              </packing>
             </child>
             <!-- Search engine address insight -->
             <child>
               <object class="GtkLabel">
-                <property name="visible">True</property>
-
                 <property name="label" translatable="yes">To determine the search address, perform a search 
using the search engine that you want to add and replace the search term with %s.</property>
                 <property name="xalign">0</property>
                 <property name="wrap">True</property>
                 <style>
                   <class name="dim-label"/>
                 </style>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">3</property>
+                  <!-- Span the whole parent grid width -->
+                  <property name="column-span">2</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">3</property>
-                <!-- Span the whole parent grid width -->
-                <property name="width">2</property>
-              </packing>
             </child>
 
             <child>
               <object class="GtkButton" id="remove_button">
-                <property name="visible">True</property>
                 <property name="halign">end</property>
-
                 <property name="label" translatable="yes">R_emove Search Engine</property>
                 <property name="use-underline">True</property>
                 <style>
                   <class name="destructive-action"/>
                 </style>
                 <signal name="clicked" handler="on_remove_button_clicked_cb" object="EphySearchEngineRow" 
swapped="yes"/>
+                <layout>
+                  <property name="column">0</property>
+                  <property name="row">4</property>
+                  <!-- Span the whole parent grid width -->
+                  <property name="column-span">2</property>
+                </layout>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">4</property>
-                <!-- Span the whole parent grid width -->
-                <property name="width">2</property>
-              </packing>
             </child>
           </object>
         </child>
diff --git a/src/resources/gtk/synced-tabs-dialog.ui b/src/resources/gtk/synced-tabs-dialog.ui
index 0b9baed57..b3262a509 100644
--- a/src/resources/gtk/synced-tabs-dialog.ui
+++ b/src/resources/gtk/synced-tabs-dialog.ui
@@ -14,18 +14,13 @@
   <template class="SyncedTabsDialog" parent="GtkDialog">
     <property name="height_request">500</property>
     <property name="modal">True</property>
-    <property name="window_position">center</property>
     <property name="destroy_with_parent">True</property>
-    <property name="type_hint">dialog</property>
+    <property name="title" translatable="yes">Synced Tabs</property>
     <child internal-child="headerbar">
-      <object class="GtkHeaderBar">
-        <property name="title" translatable="yes">Synced Tabs</property>
-        <property name="show-close-button">True</property>
-      </object>
+      <object class="GtkHeaderBar"/>
     </child>
-    <child internal-child="vbox">
+    <child internal-child="content_area">
       <object class="GtkBox">
-        <property name="visible">True</property>
         <property name="orientation">vertical</property>
         <property name="margin-top">15</property>
         <property name="margin-bottom">15</property>
@@ -34,7 +29,6 @@
         <property name="spacing">12</property>
         <child>
           <object class="GtkLabel">
-            <property name="visible">True</property>
             <property name="xalign">0</property>
             <property name="wrap">True</property>
             <property name="max-width-chars">50</property>
@@ -43,16 +37,13 @@
         </child>
         <child>
           <object class="GtkBox">
-            <property name="visible">True</property>
             <property name="orientation">vertical</property>
             <property name="spacing">6</property>
             <child>
               <object class="GtkScrolledWindow">
-                <property name="visible">True</property>
                 <property name="vexpand">True</property>
                 <child>
                   <object class="GtkTreeView" id="treeview">
-                    <property name="visible">True</property>
                     <property name="model">treestore</property>
                     <property name="headers-visible">False</property>
                     <signal name="row-activated" handler="treeview_row_activated_cb"/>
diff --git a/src/resources/gtk/web-extensions-dialog.ui b/src/resources/gtk/web-extensions-dialog.ui
index 67dd0b025..85087bdb6 100644
--- a/src/resources/gtk/web-extensions-dialog.ui
+++ b/src/resources/gtk/web-extensions-dialog.ui
@@ -2,70 +2,56 @@
 <!-- Generated with glade 3.38.0 -->
 <interface>
   <requires lib="gtk+" version="3.20"/>
-  <template class="EphyWebExtensionDialog" parent="HdyWindow">
+  <template class="EphyWebExtensionDialog" parent="GtkWindow">
     <property name="modal">True</property>
-    <property name="window-position">center-on-parent</property>
     <property name="default-width">640</property>
     <property name="default-height">400</property>
     <property name="destroy-with_parent">True</property>
-    <property name="type-hint">dialog</property>
     <property name="title" translatable="yes">Extensions</property>
-    <child>
-      <object class="GtkBox">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+    <child type="titlebar">
+      <object class="GtkHeaderBar">
         <child>
-          <object class="HdyHeaderBar">
-            <property name="visible">True</property>
-            <property name="show-close-button">True</property>
-            <property name="title" translatable="yes">Extensions</property>
-            <child>
-              <object class="GtkButton" id="add_button">
-                <property name="visible">True</property>
-                <property name="use-underline">True</property>
-                <property name="label" translatable="yes">_Add…</property>
-                <signal name="clicked" handler="on_add_button_clicked"/>
-              </object>
-            </child>
+          <object class="GtkButton" id="add_button">
+            <property name="icon-name">list-add-symbolic</property>
+            <property name="tooltip-text" translatable="yes">Add an Extension</property>
+            <signal name="clicked" handler="on_add_button_clicked"/>
           </object>
         </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkStack" id="stack">
         <child>
-          <object class="GtkStack" id="stack">
-            <property name="visible">True</property>
-            <property name="vexpand">True</property>
-            <child>
-              <object class="HdyStatusPage">
-                <property name="visible">True</property>
+          <object class="GtkStackPage">
+            <property name="name">empty</property>
+            <property name="child">
+              <object class="AdwStatusPage">
                 <property name="icon-name">application-x-addon-symbolic</property>
                 <property name="title" translatable="yes">No Extensions Installed</property>
                 <property name="description" translatable="yes">Add some extensions to display them 
here.</property>
               </object>
-              <packing>
-                <property name="name">empty</property>
-              </packing>
-            </child>
-            <child>
-              <object class="HdyPreferencesPage">
-                <property name="visible">True</property>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">list</property>
+            <property name="child">
+              <object class="AdwPreferencesPage">
                 <child>
-                  <object class="HdyPreferencesGroup">
-                    <property name="visible">True</property>
+                  <object class="AdwPreferencesGroup">
                     <child>
                       <object class="GtkListBox" id="listbox">
-                        <property name="visible">True</property>
                         <property name="selection-mode">none</property>
                         <style>
-                          <class name="content"/>
+                          <class name="boxed-list"/>
                         </style>
                       </object>
                     </child>
                   </object>
                 </child>
               </object>
-              <packing>
-                <property name="name">list</property>
-              </packing>
-            </child>
+            </property>
           </object>
         </child>
       </object>
diff --git a/src/resources/gtk/webapp-additional-urls-dialog.ui 
b/src/resources/gtk/webapp-additional-urls-dialog.ui
index a0aaa7649..f3b0c11a7 100644
--- a/src/resources/gtk/webapp-additional-urls-dialog.ui
+++ b/src/resources/gtk/webapp-additional-urls-dialog.ui
@@ -9,17 +9,14 @@
   </object>
   <template class="EphyWebappAdditionalURLsDialog" parent="GtkDialog">
     <property name="modal">True</property>
-    <property name="window_position">center-on-parent</property>
     <property name="default_width">300</property>
     <property name="default_height">400</property>
     <property name="destroy_with_parent">True</property>
-    <property name="type_hint">dialog</property>
     <property name="title" translatable="yes">Additional URLs</property>
-    <child internal-child="vbox">
+    <child internal-child="content_area">
       <object class="GtkBox">
         <child>
           <object class="GtkLabel">
-            <property name="visible">True</property>
             <property name="xalign">0</property>
             <property name="margin-top">12</property>
             <property name="margin-bottom">12</property>
@@ -31,12 +28,10 @@
         </child>
         <child>
           <object class="GtkScrolledWindow">
-            <property name="visible">True</property>
             <property name="vexpand">True</property>
             <property name="hscrollbar_policy">never</property>
             <child>
               <object class="GtkTreeView" id="treeview">
-                <property name="visible">True</property>
                 <property name="model">liststore</property>
                 <property name="enable_search">False</property>
                 <property name="search_column">0</property>
@@ -70,50 +65,32 @@
         </child>
         <child>
           <object class="GtkActionBar">
-            <property name="visible">True</property>
             <child>
-              <object class="GtkButton">
-                <property name="visible">True</property>
+              <object class="GtkButton" type="start">
+                <property name="icon_name">list-add-symbolic</property>
                 <property name="tooltip_text" translatable="yes">Add new URL</property>
                 <property name="action-name">webapp-additional-urls.new</property>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon_name">list-add-symbolic</property>
-                  </object>
-                </child>
               </object>
             </child>
-            <child>
+            <child type="start">
               <object class="GtkButton">
-                <property name="visible">True</property>
                 <property name="tooltip_text" translatable="yes">Remove the selected URLs</property>
+                <property name="icon_name">list-remove-symbolic</property>
                 <property name="action-name">webapp-additional-urls.forget</property>
                 <accelerator key="Delete" signal="clicked"/>
                 <accelerator key="KP_Delete" signal="clicked"/>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon_name">list-remove-symbolic</property>
-                  </object>
-                </child>
               </object>
             </child>
-            <child>
+            <child type="end">
               <object class="GtkButton">
                 <property name="label" translatable="yes">C_lear All</property>
-                <property name="visible">True</property>
                 <property name="use_underline">True</property>
                 <property name="action-name">webapp-additional-urls.forget-all</property>
                 <accelerator key="Delete" modifiers="GDK_SHIFT_MASK" signal="clicked"/>
                 <style>
                   <class name="destructive-action"/>
-                  <class name="text-button"/>
                 </style>
               </object>
-              <packing>
-                <property name="pack-type">end</property>
-              </packing>
             </child>
           </object>
         </child>
diff --git a/src/resources/style-dark.css b/src/resources/style-dark.css
new file mode 100644
index 000000000..9d2457912
--- /dev/null
+++ b/src/resources/style-dark.css
@@ -0,0 +1,27 @@
+/* incognito mode */
+.incognito-mode headerbar,
+.incognito-mode tabbar > revealer > box {
+  background-color: #252f49;
+}
+
+.incognito-mode headerbar:backdrop,
+.incognito-mode tabbar > revealer > box:backdrop,
+.incognito-mode actionbar {
+  background-color: #1c2438;
+}
+
+/* automation mode */
+.automation-mode headerbar,
+.automation-mode tabbar > revealer > box {
+  background-color: #733100;
+}
+
+.automation-mode headerbar:backdrop,
+.automation-mode tabbar > revealer > box:backdrop,
+.automation-mode actionbar {
+  background-color: #632300;
+}
+
+.entry-icon.starred {
+  color: @yellow_1;
+}
diff --git a/src/resources/style-hc.css b/src/resources/style-hc.css
new file mode 100644
index 000000000..b27fbd5d1
--- /dev/null
+++ b/src/resources/style-hc.css
@@ -0,0 +1,11 @@
+.entry-icon {
+  opacity: .85;
+}
+
+.entry-icon:hover {
+  opacity: 1;
+}
+
+.entry-icon:active {
+  opacity: .9;
+}
diff --git a/src/resources/style.css b/src/resources/style.css
new file mode 100644
index 000000000..295500b80
--- /dev/null
+++ b/src/resources/style.css
@@ -0,0 +1,194 @@
+/* floating status bar */
+.floating-bar {
+  background-color: @view_bg_color;
+  color: @view_fg_color;
+  box-shadow: 0 0 0 1px @borders;
+}
+
+.floating-bar:dir(ltr),
+.floating-bar.end:dir(rtl) {
+  border-radius: 0 6px 0 0;
+}
+
+.floating-bar:dir(rtl),
+.floating-bar.end:dir(ltr) {
+  border-radius: 6px 0 0 0;
+}
+
+#title-box-container {
+  transition: background 200ms ease-out;
+}
+
+/* incognito mode */
+.incognito-mode headerbar,
+.incognito-mode tabbar > revealer > box {
+  background-color: #d7e3f0;
+}
+
+.incognito-mode headerbar:backdrop,
+.incognito-mode tabbar > revealer > box:backdrop,
+.incognito-mode actionbar {
+  background-color: #eaf0f7;
+}
+
+.incognito-mode #title-box-container {
+  background: cross-fade(10% -gtk-icontheme('user-not-tracked-symbolic'), image(transparent)) 16px 0 / 64px 
64px no-repeat;
+}
+
+/* automation mode */
+.automation-mode headerbar,
+.automation-mode tabbar > revealer > box {
+  background-color: #ffa348;
+}
+
+.automation-mode headerbar:backdrop,
+.automation-mode tabbar > revealer > box:backdrop,
+.automation-mode actionbar {
+  background-color: #ffbe6f;
+}
+
+/* location entry */
+.location-entry.narrow {
+  font-size: small;
+}
+
+.entry-icon,
+.entry-icon > button {
+  margin: 0;
+  padding: 0;
+  min-width: 0;
+  min-height: 0;
+  background: none;
+}
+
+.entry-icon {
+  /* FIXME hc .85 */
+  opacity: .7;
+}
+
+.entry-icon:hover {
+  opacity: 1;
+}
+
+.entry-icon:active {
+  /* FIXME hc .9 */
+  opacity: .8;
+}
+
+.entry-icon.start:dir(ltr),
+.entry-icon.end:dir(rtl) {
+  margin-right: 6px;
+}
+
+.entry-icon.start:dir(rtl),
+.entry-icon.end:dir(ltr) {
+  margin-left: 6px;
+}
+
+.entry-icon:checked {
+  color: @accent_color;
+}
+
+.entry-icon.starred {
+  color: @yellow_5;
+}
+
+popover.suggestions {
+  padding-top: 6px;
+}
+
+popover.suggestions listview > row {
+  background: none;
+}
+
+popover.suggestions listview > row:selected {
+  background-color: alpha(currentColor, .07);
+}
+
+popover.suggestions listview > row:selected:active {
+  background-color: alpha(currentColor, .16);
+}
+
+/* bookmarks */
+.bookmarks-row button {
+  border-radius: 100%;
+  min-width: 28px;
+  min-height: 28px;
+  padding: 0;
+  margin: 2px;
+  opacity: 0.25;
+}
+
+.bookmarks-row:hover button {
+  opacity: 1;
+}
+
+.bookmark-tag-widget {
+  padding-left: 8px;
+  background-color: alpha(currentColor, .1);
+  border-radius: 6px;
+}
+
+.bookmark-tag-widget label {
+  padding-left: 8px;
+  padding-right: 8px;
+}
+
+.bookmark-tag-widget-selected {
+  background-color: @accent_bg_color;
+  color: @accent_fg_color;
+}
+
+.bookmark-tag-widget button {
+  border-radius: 100%;
+  min-width: 24px;
+  min-height: 24px;
+  padding: 0;
+  margin: 2px;
+}
+
+
+/* "Press $KEY to exit fullscreen" overlay text */
+#fullscreen-popup {
+  padding: 12px;
+  border-radius: 18px;
+  background: rgba(0, 0, 0, 0.65);
+  color: white;
+}
+
+fullscreenbox > flap > dimming,
+fullscreenbox > flap > outline,
+fullscreenbox > flap > border {
+  min-height: 0;
+  min-width: 0;
+  background: none;
+}
+
+fullscreenbox > flap > shadow {
+  min-height: 9px;
+  min-width: 9px;
+  background: linear-gradient(to bottom, alpha(black, .1), alpha(black, .0));
+}
+
+.pages-list row {
+  min-height: 30px;
+  padding: 3px;
+}
+
+.pages-list .close-button {
+  border-radius: 100%;
+  padding: 0;
+  min-width: 28px;
+  min-height: 28px;
+}
+
+#pages-view .close-button {
+  min-width: 36px;
+  min-height: 36px;
+}
+
+dnd > .boxed-list {
+  margin: 6px;
+  background-color: @window_bg_color;
+  background-image: image(@card_bg_color);
+}
diff --git a/src/synced-tabs-dialog.c b/src/synced-tabs-dialog.c
index db6d63edc..ae8b17dc3 100644
--- a/src/synced-tabs-dialog.c
+++ b/src/synced-tabs-dialog.c
@@ -153,7 +153,7 @@ synced_tabs_dialog_favicon_loaded_cb (GObject      *source,
     const char *icon_name = ephy_get_fallback_favicon_name (data->url, 
EPHY_FAVICON_TYPE_SHOW_MISSING_PLACEHOLDER);
 
     if (!icon_name)
-      icon_name = "hdy-tab-icon-missing-symbolic";
+      icon_name = "adw-tab-icon-missing-symbolic";
 
     favicon = g_themed_icon_new (icon_name);
   }
diff --git a/src/webextension/api/pageaction.c b/src/webextension/api/pageaction.c
index 1f2d53ff4..7ee94ec90 100644
--- a/src/webextension/api/pageaction.c
+++ b/src/webextension/api/pageaction.c
@@ -57,6 +57,7 @@ pageaction_handler_seticon (EphyWebExtension *self,
   GtkWidget *action;
   g_autoptr (JSCValue) path = NULL;
   g_autoptr (GdkPixbuf) pixbuf = NULL;
+  GtkWidget *child;
 
   action = pageaction_get_action (self, args);
   if (!action)
@@ -65,7 +66,10 @@ pageaction_handler_seticon (EphyWebExtension *self,
   path = jsc_value_object_get_property (args, "path");
   pixbuf = ephy_web_extension_load_pixbuf (self, jsc_value_to_string (path));
 
-  gtk_image_set_from_pixbuf (GTK_IMAGE (gtk_bin_get_child (GTK_BIN (action))), pixbuf);
+  /* action can be a GtkButton or GtkMenuButton. They both have a "child" property */
+  g_object_get (action, "child", &child, NULL);
+
+  gtk_image_set_from_pixbuf (GTK_IMAGE (child), pixbuf);
 
   return NULL;
 }
@@ -94,7 +98,7 @@ pageaction_handler_gettitle (EphyWebExtension *self,
                              JSCValue         *args)
 {
   GtkWidget *action;
-  g_autofree char *title = NULL;
+  const char *title = NULL;
 
   action = pageaction_get_action (self, args);
   if (!action)
diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c
index 4aaa9f9fe..3c8f178af 100644
--- a/src/webextension/ephy-web-extension-manager.c
+++ b/src/webextension/ephy-web-extension-manager.c
@@ -40,6 +40,7 @@
 #include "api/runtime.h"
 #include "api/tabs.h"
 
+#include <adwaita.h>
 #include <json-glib/json-glib.h>
 
 struct _EphyWebExtensionManager {
@@ -149,6 +150,16 @@ ephy_web_extension_manager_scan_directory (EphyWebExtensionManager *self,
   }
 }
 
+static void
+destroy_action (GtkWidget *action)
+{
+  GtkWidget *parent = gtk_widget_get_parent (action);
+
+  g_assert (GTK_IS_BOX (parent));
+
+  gtk_box_remove (GTK_BOX (parent), action);
+}
+
 static void
 ephy_web_extension_manager_constructed (GObject *object)
 {
@@ -157,7 +168,7 @@ ephy_web_extension_manager_constructed (GObject *object)
 
   self->background_web_views = g_hash_table_new (NULL, NULL);
   self->page_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_hash_table_destroy);
-  self->browser_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)gtk_widget_destroy);
+  self->browser_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)destroy_action);
   self->web_extensions = NULL;
 
   ephy_web_extension_manager_scan_directory (self, dir);
@@ -329,10 +340,9 @@ ephy_web_extension_manager_set_background_web_view (EphyWebExtensionManager *sel
   g_hash_table_insert (self->background_web_views, web_extension, web_view);
 }
 
-static gboolean
-page_action_clicked (GtkWidget      *event_box,
-                     GdkEventButton *event,
-                     gpointer        user_data)
+static void
+page_action_clicked (GtkButton *button,
+                     gpointer   user_data)
 {
   EphyWebExtension *web_extension = EPHY_WEB_EXTENSION (user_data);
   EphyShell *shell = ephy_shell_get_default ();
@@ -362,8 +372,6 @@ page_action_clicked (GtkWidget      *event_box,
                                            NULL,
                                            NULL,
                                            NULL);
-
-  return GDK_EVENT_STOP;
 }
 
 static GtkWidget *
@@ -371,20 +379,23 @@ create_page_action_widget (EphyWebExtensionManager *self,
                            EphyWebExtension        *web_extension)
 {
   GtkWidget *image;
-  GtkWidget *event_box;
-  GtkStyleContext *context;
+  GtkWidget *button;
+
+  button = gtk_button_new ();
+  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
 
-  /* Create new event box with page action */
-  event_box = gtk_event_box_new ();
   image = gtk_image_new ();
-  gtk_container_add (GTK_CONTAINER (event_box), image);
-  g_signal_connect_object (event_box, "button_press_event", G_CALLBACK (page_action_clicked), web_extension, 
0);
-  gtk_widget_show_all (event_box);
+  gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
+  gtk_button_set_child (GTK_BUTTON (button), image);
+
+  gtk_widget_add_css_class (button, "image-button");
+  gtk_widget_add_css_class (button, "entry-icon");
+  gtk_widget_add_css_class (button, "end");
 
-  context = gtk_widget_get_style_context (image);
-  gtk_style_context_add_class (context, "entry_icon");
+  g_signal_connect_object (button, "clicked",
+                           G_CALLBACK (page_action_clicked), web_extension, 0);
 
-  return g_object_ref (event_box);
+  return g_object_ref (button);
 }
 
 static void
@@ -530,6 +541,16 @@ update_translations (EphyWebExtension *web_extension)
                                                                               g_variant_new ("(sst)", 
ephy_web_extension_get_name (web_extension), data ? (char *)data : "", length)));
 }
 
+static void
+destroy_page_action (GtkWidget *action)
+{
+  EphyLocationEntry *entry;
+
+  entry = EPHY_LOCATION_ENTRY (gtk_widget_get_ancestor (action, EPHY_TYPE_LOCATION_ENTRY));
+
+  ephy_location_entry_page_action_remove (entry, action);
+}
+
 static void
 ephy_web_extension_manager_add_web_extension_to_webview (EphyWebExtensionManager *self,
                                                          EphyWebExtension        *web_extension,
@@ -548,7 +569,7 @@ ephy_web_extension_manager_add_web_extension_to_webview (EphyWebExtensionManager
 
       table = g_hash_table_lookup (self->page_action_map, web_extension);
       if (!table) {
-        table = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)gtk_widget_destroy);
+        table = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)destroy_page_action);
         g_hash_table_insert (self->page_action_map, web_extension, table);
       }
 
@@ -561,15 +582,15 @@ ephy_web_extension_manager_add_web_extension_to_webview (EphyWebExtensionManager
 }
 
 static void
-page_attached_cb (HdyTabView *tab_view,
-                  HdyTabPage *page,
+page_attached_cb (AdwTabView *tab_view,
+                  AdwTabPage *page,
                   gint        position,
                   gpointer    user_data)
 {
   EphyWebExtension *web_extension = EPHY_WEB_EXTENSION (user_data);
-  GtkWidget *child = hdy_tab_page_get_child (page);
+  GtkWidget *child = adw_tab_page_get_child (page);
   EphyWebView *web_view = ephy_embed_get_web_view (EPHY_EMBED (child));
-  EphyWindow *window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab_view)));
+  EphyWindow *window = EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (tab_view)));
   EphyWebExtensionManager *self = ephy_shell_get_web_extension_manager (ephy_shell_get_default ());
 
   ephy_web_extension_manager_add_web_extension_to_webview (self, web_extension, window, web_view);
@@ -671,7 +692,7 @@ create_browser_popup (EphyWebExtension *web_extension)
   g_autofree char *dir_name = NULL;
   const char *popup;
 
-  popover = gtk_popover_new (NULL);
+  popover = gtk_popover_new ();
 
   web_view = create_web_extensions_webview (web_extension, TRUE);
 
@@ -680,14 +701,13 @@ create_browser_popup (EphyWebExtension *web_extension)
   base_uri = g_strdup_printf ("ephy-webextension:///%s/", dir_name);
   data = ephy_web_extension_get_resource_as_string (web_extension, popup);
   webkit_web_view_load_html (WEBKIT_WEB_VIEW (web_view), (char *)data, base_uri);
-  gtk_container_add (GTK_CONTAINER (popover), web_view);
-  gtk_widget_show_all (web_view);
+  gtk_popover_set_child (GTK_POPOVER (popover), web_view);
 
   return popover;
 }
 
-static gboolean
-on_browser_action_clicked (GtkWidget *event_box,
+static void
+on_browser_action_clicked (GtkWidget *button,
                            gpointer   user_data)
 {
   EphyShell *shell = ephy_shell_get_default ();
@@ -710,8 +730,6 @@ on_browser_action_clicked (GtkWidget *event_box,
                                            NULL,
                                            NULL,
                                            NULL);
-
-  return GDK_EVENT_STOP;
 }
 
 
@@ -721,28 +739,32 @@ create_browser_action (EphyWebExtension *web_extension)
   GtkWidget *button;
   GtkWidget *image;
   GtkWidget *popover;
+  GdkPixbuf *pixbuf = ephy_web_extension_browser_action_get_icon (web_extension, 16);
 
   if (ephy_web_extension_get_browser_popup (web_extension)) {
     button = gtk_menu_button_new ();
-    image = gtk_image_new_from_pixbuf (ephy_web_extension_browser_action_get_icon (web_extension, 16));
+
+    if (pixbuf)
+      image = gtk_image_new_from_pixbuf (pixbuf);
+    else
+      image = gtk_image_new_from_icon_name ("application-x-addon-symbolic");
+
+    gtk_menu_button_set_child (GTK_MENU_BUTTON (button), image);
+    gtk_widget_add_css_class (button, "image-button");
+
     popover = create_browser_popup (web_extension);
     gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), popover);
-
-    gtk_button_set_image (GTK_BUTTON (button), image);
-    gtk_widget_set_visible (button, TRUE);
   } else {
-    GdkPixbuf *pixbuf = ephy_web_extension_browser_action_get_icon (web_extension, 16);
-
     button = gtk_button_new ();
 
     if (pixbuf)
       image = gtk_image_new_from_pixbuf (pixbuf);
     else
-      image = gtk_image_new_from_icon_name ("application-x-addon-symbolic", GTK_ICON_SIZE_BUTTON);
+      image = gtk_image_new_from_icon_name ("application-x-addon-symbolic");
 
     g_signal_connect_object (button, "clicked", G_CALLBACK (on_browser_action_clicked), web_extension, 0);
-    gtk_button_set_image (GTK_BUTTON (button), image);
-    gtk_widget_set_visible (button, TRUE);
+    gtk_button_set_child (GTK_BUTTON (button), image);
+    gtk_widget_add_css_class (button, "image-button");
   }
 
   return button;
@@ -754,7 +776,7 @@ ephy_web_extension_manager_add_web_extension_to_window (EphyWebExtensionManager
                                                         EphyWindow              *window)
 {
   EphyTabView *tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
-  HdyTabView *view = ephy_tab_view_get_tab_view (tab_view);
+  AdwTabView *view = ephy_tab_view_get_tab_view (tab_view);
 
   if (!ephy_web_extension_manager_is_active (self, web_extension))
     return;
@@ -821,7 +843,7 @@ ephy_web_extension_manager_remove_web_extension_from_window (EphyWebExtensionMan
                                                              EphyWindow              *window)
 {
   EphyTabView *tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
-  HdyTabView *view = ephy_tab_view_get_tab_view (tab_view);
+  AdwTabView *view = ephy_tab_view_get_tab_view (tab_view);
   GtkWidget *browser_action_widget;
 
   if (ephy_web_extension_manager_is_active (self, web_extension))
diff --git a/src/window-commands.c b/src/window-commands.c
index 8a7e52b91..336b70e30 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -63,7 +63,7 @@
 #include <gtk/gtk.h>
 #include <string.h>
 #include <webkit2/webkit2.h>
-#include <libportal-gtk3/portal-gtk3.h>
+#include <libportal-gtk4/portal-gtk4.h>
 
 #define DEFAULT_ICON_SIZE 192
 #define FAVICON_SIZE 16
@@ -252,10 +252,10 @@ show_import_export_result (GtkWindow  *parent,
 
   if (destroy_parent)
     g_signal_connect_swapped (info_dialog, "response",
-                              G_CALLBACK (gtk_widget_destroy), parent);
+                              G_CALLBACK (gtk_window_destroy), parent);
 
   g_signal_connect (info_dialog, "response",
-                    G_CALLBACK (gtk_widget_destroy), NULL);
+                    G_CALLBACK (gtk_window_destroy), NULL);
 
   gtk_window_present (GTK_WINDOW (info_dialog));
 }
@@ -275,11 +275,11 @@ show_firefox_profile_selector_cb (GtkDialog       *selector,
 
     list_box = g_object_get_data (G_OBJECT (selector), "list_box");
     row = gtk_list_box_get_selected_row (GTK_LIST_BOX (list_box));
-    row_widget = gtk_bin_get_child (GTK_BIN (row));
+    row_widget = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row));
     selected_profile = g_object_steal_data (G_OBJECT (row_widget), "profile_path");
   }
 
-  gtk_widget_destroy (GTK_WIDGET (selector));
+  gtk_window_destroy (GTK_WINDOW (selector));
 
   /* If there are multiple profiles, but the user didn't select one in
    * the profile (he pressed Cancel), don't display the import info dialog
@@ -332,11 +332,9 @@ show_firefox_profile_selector (GtkWindow *parent,
     gtk_widget_set_margin_bottom (label, 6);
     gtk_list_box_insert (GTK_LIST_BOX (list_box), label, -1);
   }
-  gtk_container_add (GTK_CONTAINER (content_area), list_box);
+  gtk_box_append (GTK_BOX (content_area), list_box);
   g_object_set_data (G_OBJECT (selector), "list_box", list_box);
 
-  gtk_widget_show_all (content_area);
-
   g_signal_connect (selector, "response",
                     G_CALLBACK (show_firefox_profile_selector_cb),
                     parent);
@@ -526,7 +524,7 @@ dialog_bookmarks_import_cb (GtkWindow       *parent,
         g_assert_not_reached ();
     }
   } else if (response == GTK_RESPONSE_CANCEL) {
-    gtk_widget_destroy (GTK_WIDGET (parent));
+    gtk_window_destroy (GTK_WINDOW (parent));
   }
 }
 
@@ -566,9 +564,10 @@ window_cmd_import_bookmarks (GSimpleAction *action,
   gtk_widget_set_margin_end (content_area, 30);
 
   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+  gtk_widget_set_vexpand (hbox, TRUE);
 
   label = gtk_label_new (_("From:"));
-  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (hbox), label);
 
   tree_model = create_tree_model ();
   combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (tree_model));
@@ -584,16 +583,16 @@ window_cmd_import_bookmarks (GSimpleAction *action,
   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell_renderer, TRUE);
   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell_renderer,
                                   "text", 0, NULL);
-  gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (hbox), combo_box);
 
-  gtk_container_add (GTK_CONTAINER (content_area), hbox);
+  gtk_box_append (GTK_BOX (content_area), hbox);
 
   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
   g_signal_connect (dialog, "response",
                     G_CALLBACK (dialog_bookmarks_import_cb),
                     GTK_COMBO_BOX (combo_box));
 
-  gtk_widget_show_all (dialog);
+  gtk_window_present (GTK_WINDOW (dialog));
 }
 
 static void
@@ -652,7 +651,6 @@ window_cmd_export_bookmarks (GSimpleAction *action,
                                                           GTK_FILE_CHOOSER_ACTION_SAVE,
                                                           _("_Save"),
                                                           _("_Cancel")));
-  gtk_file_chooser_set_show_hidden (dialog, TRUE);
 
   /* Translators: Only translate the part before ".html" (e.g. "bookmarks") */
   gtk_file_chooser_set_current_name (dialog, _("bookmarks.html"));
@@ -754,7 +752,7 @@ dialog_passwords_import_cb (GtkDialog   *dialog,
         g_assert_not_reached ();
     }
   } else {
-    gtk_widget_destroy (GTK_WIDGET (dialog));
+    gtk_window_destroy (GTK_WINDOW (dialog));
   }
 }
 
@@ -810,9 +808,10 @@ window_cmd_import_passwords (GSimpleAction *action,
   gtk_widget_set_margin_end (content_area, 30);
 
   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+  gtk_widget_set_vexpand (hbox, TRUE);
 
   label = gtk_label_new (_("From:"));
-  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (hbox), label);
 
   tree_model = create_import_passwords_tree_model ();
 
@@ -835,19 +834,18 @@ window_cmd_import_passwords (GSimpleAction *action,
   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell_renderer, TRUE);
   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell_renderer,
                                   "text", 0, NULL);
-  gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, TRUE, 0);
+  gtk_box_append (GTK_BOX (hbox), combo_box);
 
-  gtk_container_add (GTK_CONTAINER (content_area), hbox);
+  gtk_box_append (GTK_BOX (content_area), hbox);
 
   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
   g_signal_connect (dialog, "response",
                     G_CALLBACK (dialog_passwords_import_cb),
                     GTK_COMBO_BOX (combo_box));
 
-  gtk_widget_show_all (dialog);
+  gtk_window_present (GTK_WINDOW (dialog));
 }
 
-
 void
 window_cmd_show_history (GSimpleAction *action,
                          GVariant      *parameter,
@@ -894,6 +892,14 @@ window_cmd_show_preferences (GSimpleAction *action,
   gtk_window_present (dialog);
 }
 
+static void
+window_destroyed (GtkWidget  *widget,
+                  GtkWidget **widget_pointer)
+{
+  if (widget_pointer)
+    *widget_pointer = NULL;
+}
+
 void
 window_cmd_show_shortcuts (GSimpleAction *action,
                            GVariant      *parameter,
@@ -928,7 +934,7 @@ window_cmd_show_shortcuts (GSimpleAction *action,
 
     g_signal_connect (shortcuts_window,
                       "destroy",
-                      G_CALLBACK (gtk_widget_destroyed),
+                      G_CALLBACK (window_destroyed),
                       &shortcuts_window);
 
     g_object_unref (builder);
@@ -1051,7 +1057,7 @@ window_cmd_show_about (GSimpleAction *action,
   gtk_about_dialog_set_translator_credits (dialog, _("translator-credits"));
 
   g_signal_connect (dialog, "response",
-                    G_CALLBACK (gtk_widget_destroy), NULL);
+                    G_CALLBACK (gtk_window_destroy), NULL);
   gtk_window_present (GTK_WINDOW (dialog));
 
   g_free (comments);
@@ -1129,7 +1135,7 @@ window_cmd_navigation_new_tab (GSimpleAction *action,
     back_uri = webkit_back_forward_list_item_get_original_uri (back_item);
 
     embed = ephy_shell_new_tab (ephy_shell_get_default (),
-                                EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed))),
+                                EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (embed))),
                                 NULL,
                                 0);
 
@@ -1149,7 +1155,7 @@ window_cmd_navigation_new_tab (GSimpleAction *action,
     forward_uri = webkit_back_forward_list_item_get_original_uri (forward_item);
 
     embed = ephy_shell_new_tab (ephy_shell_get_default (),
-                                EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed))),
+                                EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (embed))),
                                 embed,
                                 0);
 
@@ -1182,7 +1188,7 @@ check_tab_has_modified_forms_confirm_cb (GtkDialog       *dialog,
 {
   WebKitWebView *view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed);
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   if (response == GTK_RESPONSE_ACCEPT) {
     gtk_widget_grab_focus (GTK_WIDGET (embed));
@@ -1197,7 +1203,7 @@ check_tab_has_modified_forms_and_reload_cb (EphyWebView  *view,
                                             GAsyncResult *result,
                                             EphyEmbed    *embed)
 {
-  EphyWindow *window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)));
+  EphyWindow *window = EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (view)));
   GtkWidget *dialog;
   GtkWidget *button;
   gboolean has_modified_forms;
@@ -1278,7 +1284,7 @@ window_cmd_combined_stop_reload (GSimpleAction *action,
   GAction *gaction;
   GVariant *state;
 
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (user_data), "toolbar");
+  action_group = ephy_window_get_action_group (EPHY_WINDOW (user_data), "toolbar");
 
   state = g_action_get_state (G_ACTION (action));
   /* If loading */
@@ -1301,12 +1307,10 @@ window_cmd_page_menu (GSimpleAction *action,
   EphyWindow *window = EPHY_WINDOW (user_data);
   EphyHeaderBar *header_bar;
   GtkMenuButton *button;
-  GtkPopover *popover;
 
   header_bar = EPHY_HEADER_BAR (ephy_window_get_header_bar (window));
   button = GTK_MENU_BUTTON (ephy_header_bar_get_page_menu_button (header_bar));
-  popover = gtk_menu_button_get_popover (button);
-  gtk_popover_popup (popover);
+  gtk_menu_button_popup (button);
 }
 
 void
@@ -1840,7 +1844,7 @@ dialog_save_as_application_confirmation_cb (GtkDialog                 *dialog,
                                             GtkResponseType            response,
                                             EphyApplicationDialogData *data)
 {
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   if (response == GTK_RESPONSE_OK) {
     ephy_web_application_delete (data->app_id, NULL);
@@ -2090,7 +2094,7 @@ save_response_cb (GtkNativeDialog *dialog,
       }
     }
 
-    current_file = gtk_file_chooser_get_current_folder_file (GTK_FILE_CHOOSER (dialog));
+    current_file = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
     current_path = g_file_get_path (current_file);
     g_settings_set_string (EPHY_SETTINGS_WEB,
                            EPHY_PREFS_WEB_LAST_DOWNLOAD_DIRECTORY,
@@ -2120,8 +2124,6 @@ window_cmd_save_as (GSimpleAction *action,
                                      GTK_FILE_CHOOSER_ACTION_SAVE,
                                      EPHY_FILE_FILTER_NONE);
 
-  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
-
   last_directory_path = g_settings_get_string (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_LAST_DOWNLOAD_DIRECTORY);
 
   if (last_directory_path && last_directory_path[0]) {
@@ -2129,7 +2131,7 @@ window_cmd_save_as (GSimpleAction *action,
     g_autoptr (GError) error = NULL;
 
     last_directory = g_file_new_for_path (last_directory_path);
-    gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog), last_directory, &error);
+    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), last_directory, &error);
 
     if (error)
       g_warning ("Failed to set current folder %s: %s", last_directory_path, error->message);
@@ -2207,6 +2209,7 @@ window_cmd_redo (GSimpleAction *action,
     }
   }
 }
+
 void
 window_cmd_cut (GSimpleAction *action,
                 GVariant      *parameter,
@@ -2216,7 +2219,7 @@ window_cmd_cut (GSimpleAction *action,
   GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
 
   if (GTK_IS_EDITABLE (widget)) {
-    gtk_editable_cut_clipboard (GTK_EDITABLE (widget));
+    gtk_widget_activate_action (widget, "clipboard.cut", NULL);
   } else {
     EphyEmbed *embed;
     embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
@@ -2235,7 +2238,7 @@ window_cmd_copy (GSimpleAction *action,
   GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
 
   if (GTK_IS_EDITABLE (widget)) {
-    gtk_editable_copy_clipboard (GTK_EDITABLE (widget));
+    gtk_widget_activate_action (widget, "clipboard.copy", NULL);
   } else {
     EphyEmbed *embed;
 
@@ -2255,7 +2258,7 @@ window_cmd_paste (GSimpleAction *action,
   GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
 
   if (GTK_IS_EDITABLE (widget)) {
-    gtk_editable_paste_clipboard (GTK_EDITABLE (widget));
+    gtk_widget_activate_action (widget, "clipboard.paste", NULL);
   } else {
     EphyEmbed *embed;
 
@@ -2275,7 +2278,7 @@ window_cmd_paste_as_plain_text (GSimpleAction *action,
   GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
 
   if (GTK_IS_EDITABLE (widget)) {
-    gtk_editable_paste_clipboard (GTK_EDITABLE (widget));
+    gtk_widget_activate_action (widget, "clipboard.paste", NULL);
   } else {
     EphyEmbed *embed;
 
@@ -2491,7 +2494,7 @@ window_cmd_page_source (GSimpleAction *action,
 
   new_embed = ephy_shell_new_tab
                 (ephy_shell_get_default (),
-                EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed))),
+                EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (embed))),
                 embed,
                 EPHY_NEW_TAB_JUMP | EPHY_NEW_TAB_APPEND_AFTER);
 
@@ -2558,7 +2561,6 @@ window_cmd_send_to (GSimpleAction *action,
   EphyEmbed *embed;
   char *command, *subject, *body;
   const char *location, *title;
-  GError *error = NULL;
 
   embed = ephy_embed_container_get_active_child
             (EPHY_EMBED_CONTAINER (window));
@@ -2577,10 +2579,7 @@ window_cmd_send_to (GSimpleAction *action,
   g_free (subject);
   g_free (body);
 
-  if (!gtk_show_uri_on_window (GTK_WINDOW (window), command, GDK_CURRENT_TIME, &error)) {
-    g_warning ("Unable to send link by email: %s\n", error->message);
-    g_error_free (error);
-  }
+  gtk_show_uri (GTK_WINDOW (window), command, GDK_CURRENT_TIME);
 
   g_free (command);
 }
@@ -2632,10 +2631,10 @@ enable_browse_with_caret_state_cb (GtkMessageDialog *dialog,
                                    GtkResponseType   response,
                                    EphyWindow       *window)
 {
-  GActionGroup *action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "win");
+  GActionGroup *action_group = ephy_window_get_action_group (window, "win");
   GAction *action;
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 
   action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
                                        "browse-with-caret");
@@ -2877,7 +2876,7 @@ window_cmd_homepage_new_tab (GSimpleAction *action,
   g_assert (embed != NULL);
 
   embed = ephy_shell_new_tab (ephy_shell_get_default (),
-                              EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed))),
+                              EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (embed))),
                               NULL,
                               0);
 
@@ -2890,18 +2889,26 @@ window_cmd_homepage_new_tab (GSimpleAction *action,
 }
 
 static void
-clipboard_text_received_cb (GtkClipboard *clipboard,
-                            const gchar  *text,
+clipboard_text_received_cb (GdkClipboard *clipboard,
+                            GAsyncResult *res,
                             EphyWindow   *window)
 {
   EphyEmbed *embed;
   EphyWebView *web_view;
+  g_autoptr (GError) error = NULL;
+  g_autofree char *text = NULL;
+
+  text = gdk_clipboard_read_text_finish (clipboard, res, &error);
+  if (error) {
+    g_warning ("Failed to the URL from clipboard: %s", error->message);
+    return;
+  }
 
   embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
   g_assert (embed != NULL);
 
   embed = ephy_shell_new_tab (ephy_shell_get_default (),
-                              EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed))),
+                              EPHY_WINDOW (gtk_widget_get_root (GTK_WIDGET (embed))),
                               NULL,
                               0);
 
@@ -2920,12 +2927,13 @@ window_cmd_new_tab_from_clipboard (GSimpleAction *action,
                                    gpointer       user_data)
 {
   EphyWindow *ephy_window = EPHY_WINDOW (user_data);
-  GtkClipboard *clipboard;
+  GdkClipboard *clipboard;
 
-  clipboard = gtk_clipboard_get_default (gdk_display_get_default ());
-  gtk_clipboard_request_text (clipboard,
-                              (GtkClipboardTextReceivedFunc)clipboard_text_received_cb,
-                              g_object_ref (ephy_window));
+  clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ephy_window));
+  gdk_clipboard_read_text_async (clipboard,
+                                 NULL,
+                                 (GAsyncReadyCallback)clipboard_text_received_cb,
+                                 g_object_ref (ephy_window));
 }
 
 void
@@ -2983,5 +2991,5 @@ window_cmd_extensions (GSimpleAction *action,
 
   dialog = ephy_web_extension_dialog_new ();
   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window));
-  gtk_widget_show_all (dialog);
+  gtk_window_present (GTK_WINDOW (dialog));
 }
diff --git a/tests/ephy-embed-shell-test.c b/tests/ephy-embed-shell-test.c
index a3107dac9..335bab82a 100644
--- a/tests/ephy-embed-shell-test.c
+++ b/tests/ephy-embed-shell-test.c
@@ -50,9 +50,9 @@ test_ephy_embed_shell_web_view_created (void)
   g_signal_connect (embed_shell, "web-view-created",
                     G_CALLBACK (web_view_created_cb), &web_view_created);
 
-  view = ephy_web_view_new ();
+  view = g_object_ref_sink (ephy_web_view_new ());
   g_assert_true (web_view_created);
-  gtk_widget_destroy (view);
+  g_object_unref (view);
 }
 
 int
diff --git a/tests/ephy-location-entry-test.c b/tests/ephy-location-entry-test.c
index db412847c..d92132065 100644
--- a/tests/ephy-location-entry-test.c
+++ b/tests/ephy-location-entry-test.c
@@ -36,18 +36,6 @@ test_entry_new (void)
   g_assert_true (EPHY_IS_LOCATION_ENTRY (entry));
 }
 
-static void
-test_entry_get_entry (void)
-{
-  EphyLocationEntry *lentry;
-  GtkWidget *entry;
-
-  lentry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
-  entry = ephy_location_entry_get_entry (lentry);
-
-  g_assert_true (GTK_IS_ENTRY (entry));
-}
-
 static void
 test_entry_set_location (void)
 {
@@ -108,7 +96,6 @@ test_entry_can_undo (void)
   const char *test = "test";
 
   EphyLocationEntry *lentry;
-  GtkWidget *entry;
 
   lentry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
 
@@ -117,8 +104,7 @@ test_entry_can_undo (void)
   /* Use gtk_* function or otherwise user_changed won't be correctly handled
    * internally by the location entry (see editable_changed_cb and
    * block_update) */
-  entry = ephy_location_entry_get_entry (lentry);
-  gtk_entry_set_text (GTK_ENTRY (entry), test);
+  gtk_editable_set_text (GTK_EDITABLE (lentry), test);
   g_assert_cmpint (ephy_location_entry_get_can_undo (lentry), ==, TRUE);
 }
 
@@ -156,8 +142,6 @@ main (int   argc,
 
   g_test_add_func ("/lib/widgets/ephy-location-entry/new",
                    test_entry_new);
-  g_test_add_func ("/lib/widgets/ephy-location-entry/get_entry",
-                   test_entry_get_entry);
   g_test_add_func ("/lib/widgets/ephy-location-entry/set_location",
                    test_entry_set_location);
   g_test_add_func ("/lib/widgets/ephy-location-entry/get_location",
diff --git a/tests/ephy-web-view-test.c b/tests/ephy-web-view-test.c
index 1cc18770b..77526363e 100644
--- a/tests/ephy-web-view-test.c
+++ b/tests/ephy-web-view-test.c
@@ -168,10 +168,10 @@ test_ephy_web_view_load_url (void)
     URLTest test;
     GMainLoop *loop;
     EphyWebView *view;
-    GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    GtkWidget *window = gtk_window_new ();
 
     view = EPHY_WEB_VIEW (ephy_web_view_new ());
-    gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (view));
+    gtk_window_set_child (GTK_WINDOW (window), GTK_WIDGET (view));
     test = test_load_url[i];
     loop = g_main_loop_new (NULL, FALSE);
 
@@ -187,7 +187,7 @@ test_ephy_web_view_load_url (void)
 
     g_main_loop_run (loop);
     g_main_loop_unref (loop);
-    g_object_unref (g_object_ref_sink (view));
+    g_object_unref (window);
   }
 }
 
diff --git a/tests/meson.build b/tests/meson.build
index d120d1391..c4e887732 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -84,6 +84,7 @@ if get_option('unit_tests').enabled()
 
   location_entry_test = executable('test-location-entry',
     'ephy-location-entry-test.c',
+    resources,
     dependencies: ephymain_dep,
     c_args: test_cargs,
   )


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