[epiphany] Split the Preferences Dialog



commit 95052545c8c4a4bcfdd57f89e2ed738f5def0252
Author: Yetizone <andreii lisita gmail com>
Date:   Fri May 8 14:50:35 2020 +0300

    Split the Preferences Dialog

 src/meson.build                                    |   19 +-
 src/{ => preferences}/clear-data-dialog.c          |    0
 src/{ => preferences}/clear-data-dialog.h          |    0
 src/{ => preferences}/cookies-dialog.c             |    0
 src/{ => preferences}/cookies-dialog.h             |    0
 src/{ => preferences}/ephy-search-engine-dialog.c  |    0
 src/{ => preferences}/ephy-search-engine-dialog.h  |    0
 src/{ => preferences}/passwords-dialog.c           |    0
 src/{ => preferences}/passwords-dialog.h           |    0
 src/preferences/prefs-appearance-page.c            |  426 ++++
 src/preferences/prefs-appearance-page.h            |   32 +
 src/preferences/prefs-dialog.c                     |   73 +
 src/{ => preferences}/prefs-dialog.h               |    0
 src/preferences/prefs-general-page.c               | 1378 ++++++++++
 src/preferences/prefs-general-page.h               |   37 +
 src/preferences/prefs-privacy-page.c               |  243 ++
 src/preferences/prefs-privacy-page.h               |   32 +
 src/preferences/prefs-sync-page.c                  |  790 ++++++
 src/preferences/prefs-sync-page.h                  |   34 +
 src/{ => preferences}/synced-tabs-dialog.c         |    0
 src/{ => preferences}/synced-tabs-dialog.h         |    0
 .../webapp-additional-urls-dialog.c                |    0
 .../webapp-additional-urls-dialog.h                |    0
 src/prefs-dialog.c                                 | 2640 --------------------
 src/resources/epiphany.gresource.xml               |    4 +
 src/resources/gtk/prefs-appearance-page.ui         |  216 ++
 src/resources/gtk/prefs-dialog.ui                  |  951 +------
 src/resources/gtk/prefs-general-page.ui            |  337 +++
 src/resources/gtk/prefs-privacy-page.ui            |  209 ++
 src/resources/gtk/prefs-sync-page.ui               |  205 ++
 30 files changed, 4032 insertions(+), 3594 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index 2a97287dd..718b278d6 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -19,8 +19,6 @@ libephymain_sources = [
   'bookmarks/ephy-bookmarks-import.c',
   'bookmarks/ephy-bookmarks-manager.c',
   'bookmarks/ephy-bookmarks-popover.c',
-  'clear-data-dialog.c',
-  'cookies-dialog.c',
   'ephy-action-bar.c',
   'ephy-action-bar-end.c',
   'ephy-action-bar-start.c',
@@ -40,18 +38,24 @@ libephymain_sources = [
   'ephy-pages-button.c',
   'ephy-pages-popover.c',
   'ephy-pages-view.c',
-  'ephy-search-engine-dialog.c',
   'ephy-session.c',
   'ephy-shell.c',
   'ephy-suggestion-model.c',
   'ephy-tab-header-bar.c',
   'ephy-tab-label.c',
   'ephy-window.c',
-  'passwords-dialog.c',
   'popup-commands.c',
-  'prefs-dialog.c',
-  'synced-tabs-dialog.c',
-  'webapp-additional-urls-dialog.c',
+  'preferences/clear-data-dialog.c',
+  'preferences/cookies-dialog.c',
+  'preferences/ephy-search-engine-dialog.c',
+  'preferences/passwords-dialog.c',
+  'preferences/prefs-appearance-page.c',
+  'preferences/prefs-dialog.c',
+  'preferences/prefs-general-page.c',
+  'preferences/prefs-privacy-page.c',
+  'preferences/prefs-sync-page.c',
+  'preferences/synced-tabs-dialog.c',
+  'preferences/webapp-additional-urls-dialog.c',
   'window-commands.c',
   compile_schemas,
   enums
@@ -71,6 +75,7 @@ libephymain_deps = [
 libephymain_includes = include_directories(
   '.',
   'bookmarks',
+  'preferences',
 )
 
 libephymain = shared_library('ephymain',
diff --git a/src/clear-data-dialog.c b/src/preferences/clear-data-dialog.c
similarity index 100%
rename from src/clear-data-dialog.c
rename to src/preferences/clear-data-dialog.c
diff --git a/src/clear-data-dialog.h b/src/preferences/clear-data-dialog.h
similarity index 100%
rename from src/clear-data-dialog.h
rename to src/preferences/clear-data-dialog.h
diff --git a/src/cookies-dialog.c b/src/preferences/cookies-dialog.c
similarity index 100%
rename from src/cookies-dialog.c
rename to src/preferences/cookies-dialog.c
diff --git a/src/cookies-dialog.h b/src/preferences/cookies-dialog.h
similarity index 100%
rename from src/cookies-dialog.h
rename to src/preferences/cookies-dialog.h
diff --git a/src/ephy-search-engine-dialog.c b/src/preferences/ephy-search-engine-dialog.c
similarity index 100%
rename from src/ephy-search-engine-dialog.c
rename to src/preferences/ephy-search-engine-dialog.c
diff --git a/src/ephy-search-engine-dialog.h b/src/preferences/ephy-search-engine-dialog.h
similarity index 100%
rename from src/ephy-search-engine-dialog.h
rename to src/preferences/ephy-search-engine-dialog.h
diff --git a/src/passwords-dialog.c b/src/preferences/passwords-dialog.c
similarity index 100%
rename from src/passwords-dialog.c
rename to src/preferences/passwords-dialog.c
diff --git a/src/passwords-dialog.h b/src/preferences/passwords-dialog.h
similarity index 100%
rename from src/passwords-dialog.h
rename to src/preferences/passwords-dialog.h
diff --git a/src/preferences/prefs-appearance-page.c b/src/preferences/prefs-appearance-page.c
new file mode 100644
index 000000000..dc14a6344
--- /dev/null
+++ b/src/preferences/prefs-appearance-page.c
@@ -0,0 +1,426 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  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 "prefs-appearance-page.h"
+
+#include "ephy-embed-prefs.h"
+#include "ephy-file-helpers.h"
+#include "ephy-flatpak-utils.h"
+#include "ephy-lib-type-builtins.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+
+#include <math.h>
+
+struct _PrefsAppearancePage {
+  HdyPreferencesPage parent_instance;
+
+  /* Fonts */
+  GtkWidget *use_gnome_fonts_row;
+  GtkWidget *use_custom_fonts_list;
+  GtkWidget *sans_fontbutton;
+  GtkWidget *serif_fontbutton;
+  GtkWidget *mono_fontbutton;
+
+  /* Reader Mode */
+  GtkWidget *reader_mode_box;
+  GtkWidget *reader_mode_font_style;
+  GtkWidget *reader_mode_color_scheme;
+
+  /* Style */
+  GtkWidget *css_switch;
+  GtkWidget *css_edit_button;
+  GtkWidget *js_switch;
+  GtkWidget *js_edit_button;
+  GtkWidget *default_zoom_spin_button;
+};
+
+G_DEFINE_TYPE (PrefsAppearancePage, prefs_appearance_page, HDY_TYPE_PREFERENCES_PAGE)
+
+static gchar *
+reader_font_style_get_name (HdyEnumValueObject *value,
+                            gpointer            user_data)
+{
+  g_assert (HDY_IS_ENUM_VALUE_OBJECT (value));
+
+  switch (hdy_enum_value_object_get_value (value)) {
+    case EPHY_PREFS_READER_FONT_STYLE_SANS:
+      return g_strdup (_("Sans"));
+    case EPHY_PREFS_READER_FONT_STYLE_SERIF:
+      return g_strdup (_("Serif"));
+    default:
+      return NULL;
+  }
+}
+
+static gboolean
+reader_font_style_get_mapping (GValue   *value,
+                               GVariant *variant,
+                               gpointer  user_data)
+{
+  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);
+  else if (g_strcmp0 (reader_colors, "serif") == 0)
+    g_value_set_int (value, EPHY_PREFS_READER_FONT_STYLE_SERIF);
+
+  return TRUE;
+}
+
+static GVariant *
+reader_font_style_set_mapping (const GValue       *value,
+                               const GVariantType *expected_type,
+                               gpointer            user_data)
+{
+  switch (g_value_get_int (value)) {
+    case EPHY_PREFS_READER_FONT_STYLE_SANS:
+      return g_variant_new_string ("sans");
+    case EPHY_PREFS_READER_FONT_STYLE_SERIF:
+      return g_variant_new_string ("serif");
+    default:
+      return g_variant_new_string ("crashed");
+  }
+}
+
+static gchar *
+reader_color_scheme_get_name (HdyEnumValueObject *value,
+                              gpointer            user_data)
+{
+  g_assert (HDY_IS_ENUM_VALUE_OBJECT (value));
+
+  switch (hdy_enum_value_object_get_value (value)) {
+    case EPHY_PREFS_READER_COLORS_LIGHT:
+      return g_strdup (_("Light"));
+    case EPHY_PREFS_READER_COLORS_DARK:
+      return g_strdup (_("Dark"));
+    default:
+      return NULL;
+  }
+}
+
+static gboolean
+reader_color_scheme_get_mapping (GValue   *value,
+                                 GVariant *variant,
+                                 gpointer  user_data)
+{
+  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);
+  else if (g_strcmp0 (reader_colors, "dark") == 0)
+    g_value_set_int (value, EPHY_PREFS_READER_COLORS_DARK);
+
+  return TRUE;
+}
+
+static GVariant *
+reader_color_scheme_set_mapping (const GValue       *value,
+                                 const GVariantType *expected_type,
+                                 gpointer            user_data)
+{
+  switch (g_value_get_int (value)) {
+    case EPHY_PREFS_READER_COLORS_LIGHT:
+      return g_variant_new_string ("light");
+    case EPHY_PREFS_READER_COLORS_DARK:
+      return g_variant_new_string ("dark");
+    default:
+      return g_variant_new_string ("crashed");
+  }
+}
+
+static void
+css_file_opened_cb (GObject      *source,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  gboolean ret;
+  GError *error = NULL;
+
+  ret = ephy_open_file_via_flatpak_portal_finish (result, &error);
+  if (!ret) {
+    if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+      g_warning ("Failed to open CSS file: %s", error->message);
+    g_error_free (error);
+  }
+}
+
+static void
+css_file_created_cb (GObject      *source,
+                     GAsyncResult *result,
+                     gpointer      user_data)
+{
+  GFile *file = G_FILE (source);
+  GFileOutputStream *stream;
+  GError *error = NULL;
+
+  stream = g_file_create_finish (file, result, &error);
+  if (stream == NULL && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+    g_warning ("Failed to create %s: %s", g_file_get_path (file), error->message);
+  else {
+    if (ephy_is_running_inside_flatpak ())
+      ephy_open_file_via_flatpak_portal (g_file_get_path (file), NULL, css_file_opened_cb, NULL);
+    else
+      ephy_file_launch_handler (file, gtk_get_current_event_time ());
+  }
+
+  if (error != NULL)
+    g_error_free (error);
+  if (stream != NULL)
+    g_object_unref (stream);
+  g_object_unref (file);
+}
+
+static void
+css_edit_button_clicked_cb (GtkWidget           *button,
+                            PrefsAppearancePage *appearance_page)
+{
+  GFile *css_file;
+
+  css_file = g_file_new_for_path (g_build_filename (ephy_profile_dir (),
+                                                    USER_STYLESHEET_FILENAME,
+                                                    NULL));
+
+  g_file_create_async (css_file, G_FILE_CREATE_NONE, G_PRIORITY_DEFAULT, NULL, css_file_created_cb, NULL);
+}
+
+static void
+js_file_opened_cb (GObject      *source,
+                   GAsyncResult *result,
+                   gpointer      user_data)
+{
+  gboolean ret;
+  g_autoptr (GError) error = NULL;
+
+  ret = ephy_open_file_via_flatpak_portal_finish (result, &error);
+  if (!ret && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+    g_warning ("Failed to open JS file: %s", error->message);
+}
+
+static void
+js_file_created_cb (GObject      *source,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  g_autoptr (GFile) file = G_FILE (source);
+  g_autoptr (GFileOutputStream) stream = NULL;
+  g_autoptr (GError) error = NULL;
+
+  stream = g_file_create_finish (file, result, &error);
+  if (stream == NULL && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+    g_warning ("Failed to create %s: %s", g_file_get_path (file), error->message);
+  else {
+    if (ephy_is_running_inside_flatpak ())
+      ephy_open_file_via_flatpak_portal (g_file_get_path (file), NULL, js_file_opened_cb, NULL);
+    else
+      ephy_file_launch_handler (file, gtk_get_current_event_time ());
+  }
+}
+
+static void
+js_edit_button_clicked_cb (GtkWidget           *button,
+                           PrefsAppearancePage *appearance_page)
+{
+  GFile *js_file;
+
+  js_file = g_file_new_for_path (g_build_filename (ephy_profile_dir (),
+                                                   USER_JAVASCRIPT_FILENAME,
+                                                   NULL));
+
+  g_file_create_async (g_steal_pointer (&js_file), G_FILE_CREATE_NONE, G_PRIORITY_DEFAULT, NULL, 
js_file_created_cb, NULL);
+}
+
+static gboolean
+on_default_zoom_spin_button_output (GtkSpinButton *spin,
+                                    gpointer       user_data)
+{
+  GtkAdjustment *adjustment;
+  g_autofree gchar *text = NULL;
+  gdouble value;
+
+  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);
+
+  return TRUE;
+}
+
+static void
+on_default_zoom_spin_button_value_changed (GtkSpinButton *spin,
+                                           gpointer       user_data)
+{
+  GtkAdjustment *adjustment;
+  gdouble value;
+
+  adjustment = gtk_spin_button_get_adjustment (spin);
+  value = gtk_adjustment_get_value (adjustment);
+  value = roundf (value) / 100;
+  g_settings_set_double (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_DEFAULT_ZOOM_LEVEL, value);
+}
+
+static void
+setup_appearance_page (PrefsAppearancePage *appearance_page)
+{
+  GSettings *web_settings = ephy_settings_get (EPHY_PREFS_WEB_SCHEMA);
+  GSettings *reader_settings = ephy_settings_get (EPHY_PREFS_READER_SCHEMA);
+
+  /* ======================================================================== */
+  /* ========================== Fonts ======================================= */
+  /* ======================================================================== */
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_USE_GNOME_FONTS,
+                   appearance_page->use_gnome_fonts_row,
+                   "enable-expansion",
+                   G_SETTINGS_BIND_INVERT_BOOLEAN);
+
+  gtk_list_box_set_header_func (GTK_LIST_BOX (appearance_page->use_custom_fonts_list),
+                                hdy_list_box_separator_header, NULL, NULL);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_SANS_SERIF_FONT,
+                   appearance_page->sans_fontbutton,
+                   "font-name",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_SERIF_FONT,
+                   appearance_page->serif_fontbutton,
+                   "font-name",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_MONOSPACE_FONT,
+                   appearance_page->mono_fontbutton,
+                   "font-name",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  /* ======================================================================== */
+  /* ========================== Reader Mode ================================= */
+  /* ======================================================================== */
+  hdy_combo_row_set_for_enum (HDY_COMBO_ROW (appearance_page->reader_mode_font_style),
+                              EPHY_TYPE_PREFS_READER_FONT_STYLE,
+                              reader_font_style_get_name, NULL, NULL);
+
+  g_settings_bind_with_mapping (reader_settings,
+                                EPHY_PREFS_READER_FONT_STYLE,
+                                appearance_page->reader_mode_font_style,
+                                "selected-index",
+                                G_SETTINGS_BIND_DEFAULT,
+                                reader_font_style_get_mapping,
+                                reader_font_style_set_mapping,
+                                NULL, NULL);
+
+  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",
+                                G_SETTINGS_BIND_DEFAULT,
+                                reader_color_scheme_get_mapping,
+                                reader_color_scheme_set_mapping,
+                                NULL, NULL);
+
+  /* ======================================================================== */
+  /* ========================== Style ======================================= */
+  /* ======================================================================== */
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_USER_CSS,
+                   appearance_page->css_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_USER_CSS,
+                   appearance_page->css_edit_button,
+                   "sensitive",
+                   G_SETTINGS_BIND_GET);
+
+  g_signal_connect (appearance_page->css_edit_button,
+                    "clicked",
+                    G_CALLBACK (css_edit_button_clicked_cb),
+                    appearance_page);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_USER_JS,
+                   appearance_page->js_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_USER_JS,
+                   appearance_page->js_edit_button,
+                   "sensitive",
+                   G_SETTINGS_BIND_GET);
+
+  g_signal_connect (appearance_page->js_edit_button,
+                    "clicked",
+                    G_CALLBACK (js_edit_button_clicked_cb),
+                    appearance_page);
+
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (appearance_page->default_zoom_spin_button),
+                             g_settings_get_double (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_DEFAULT_ZOOM_LEVEL) * 
100);
+}
+
+static void
+prefs_appearance_page_class_init (PrefsAppearancePageClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/epiphany/gtk/prefs-appearance-page.ui");
+
+  /* Fonts */
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, use_gnome_fonts_row);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, use_custom_fonts_list);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, sans_fontbutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, serif_fontbutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, mono_fontbutton);
+
+  /* Reader Mode */
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, reader_mode_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, reader_mode_font_style);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, reader_mode_color_scheme);
+
+  /* Style */
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, css_switch);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, css_edit_button);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, js_switch);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, js_edit_button);
+  gtk_widget_class_bind_template_child (widget_class, PrefsAppearancePage, default_zoom_spin_button);
+
+  /* Signals */
+  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);
+}
+
+static void
+prefs_appearance_page_init (PrefsAppearancePage *appearance_page)
+{
+  EphyEmbedShellMode mode = ephy_embed_shell_get_mode (ephy_embed_shell_get_default ());
+
+  gtk_widget_init_template (GTK_WIDGET (appearance_page));
+
+  gtk_widget_set_visible (appearance_page->reader_mode_box,
+                          mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+
+  setup_appearance_page (appearance_page);
+}
diff --git a/src/preferences/prefs-appearance-page.h b/src/preferences/prefs-appearance-page.h
new file mode 100644
index 000000000..ba511b4db
--- /dev/null
+++ b/src/preferences/prefs-appearance-page.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  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 <glib-object.h>
+
+#define HANDY_USE_UNSTABLE_API
+#include <handy.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_END_DECLS
diff --git a/src/preferences/prefs-dialog.c b/src/preferences/prefs-dialog.c
new file mode 100644
index 000000000..7755674c0
--- /dev/null
+++ b/src/preferences/prefs-dialog.c
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 200-2003 Marco Pesenti Gritti
+ *  Copyright © 2003, 2004, 2005 Christian Persch
+ *  Copyright © 2010, 2017 Igalia S.L.
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "prefs-dialog.h"
+
+#include "ephy-embed-utils.h"
+#include "ephy-gui.h"
+#include "prefs-general-page.h"
+#include "prefs-sync-page.h"
+
+#include <gtk/gtk.h>
+
+struct _PrefsDialog {
+  GtkDialog parent_instance;
+
+  GtkWidget *notebook;
+
+  PrefsGeneralPage *general_page;
+  PrefsSyncPage *sync_page;
+};
+
+G_DEFINE_TYPE (PrefsDialog, prefs_dialog, GTK_TYPE_DIALOG)
+
+static void
+prefs_dialog_class_init (PrefsDialogClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/epiphany/gtk/prefs-dialog.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, notebook);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, general_page);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_page);
+}
+
+static void
+prefs_dialog_init (PrefsDialog *dialog)
+{
+  EphyEmbedShellMode mode = ephy_embed_shell_get_mode (ephy_embed_shell_get_default ());
+
+  gtk_widget_init_template (GTK_WIDGET (dialog));
+  gtk_window_set_icon_name (GTK_WINDOW (dialog), APPLICATION_ID);
+
+  prefs_general_page_connect_pd_response (dialog->general_page, dialog);
+
+  if (mode == EPHY_EMBED_SHELL_MODE_BROWSER)
+    prefs_sync_page_setup (dialog->sync_page);
+  else
+    gtk_notebook_remove_page (GTK_NOTEBOOK (dialog->notebook), -1);
+
+  ephy_gui_ensure_window_group (GTK_WINDOW (dialog));
+}
diff --git a/src/prefs-dialog.h b/src/preferences/prefs-dialog.h
similarity index 100%
rename from src/prefs-dialog.h
rename to src/preferences/prefs-dialog.h
diff --git a/src/preferences/prefs-general-page.c b/src/preferences/prefs-general-page.c
new file mode 100644
index 000000000..682496be3
--- /dev/null
+++ b/src/preferences/prefs-general-page.c
@@ -0,0 +1,1378 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  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 "prefs-general-page.h"
+
+#include "ephy-embed-shell.h"
+#include "ephy-file-chooser.h"
+#include "ephy-file-helpers.h"
+#include "ephy-flatpak-utils.h"
+#include "ephy-langs.h"
+#include "ephy-settings.h"
+#include "ephy-search-engine-dialog.h"
+#include "ephy-web-app-utils.h"
+#include "webapp-additional-urls-dialog.h"
+
+#include "gnome-languages.h"
+#include <glib/gi18n.h>
+
+#define DOWNLOAD_BUTTON_WIDTH   8
+
+enum {
+  COL_LANG_NAME,
+  COL_LANG_CODE
+};
+
+struct _PrefsGeneralPage {
+  HdyPreferencesPage parent_instance;
+
+  /* Web Application */
+  EphyWebApplication *webapp;
+  guint webapp_save_id;
+  GtkWidget *webapp_box;
+  GtkWidget *webapp_icon;
+  GtkWidget *webapp_url;
+  GtkWidget *webapp_title;
+
+  /* Homepage */
+  GtkWidget *homepage_box;
+  GtkWidget *new_tab_homepage_radiobutton;
+  GtkWidget *blank_homepage_radiobutton;
+  GtkWidget *custom_homepage_radiobutton;
+  GtkWidget *custom_homepage_entry;
+
+  /* Downloads */
+  GtkWidget *download_box;
+  GtkWidget *ask_on_download_switch;
+  GtkWidget *download_folder_row;
+
+  /* Search Engines */
+  GtkWidget *search_box;
+
+  /* Session */
+  GtkWidget *session_box;
+  GtkWidget *start_in_incognito_mode_switch;
+  GtkWidget *restore_session_row;
+  GtkWidget *restore_session_switch;
+
+  /* Browsing */
+  GtkWidget *browsing_box;
+  GtkWidget *enable_smooth_scrolling_switch;
+  GtkWidget *enable_mouse_gesture_switch;
+
+  /* Languages */
+  HdyPreferencesGroup *lang_group;
+  GtkWidget *lang_listbox;
+  GtkWidget *enable_spell_checking_switch;
+
+  GtkDialog *add_lang_dialog;
+  GtkTreeView *add_lang_treeview;
+};
+
+G_DEFINE_TYPE (PrefsGeneralPage, prefs_general_page, HDY_TYPE_PREFERENCES_PAGE)
+
+static void
+prefs_general_page_finalize (GObject *object)
+{
+  PrefsGeneralPage *general_page = EPHY_PREFS_GENERAL_PAGE (object);
+
+  if (general_page->add_lang_dialog != NULL) {
+    GtkDialog **add_lang_dialog = &general_page->add_lang_dialog;
+
+    g_object_remove_weak_pointer (G_OBJECT (general_page->add_lang_dialog),
+                                  (gpointer *)add_lang_dialog);
+    g_object_unref (general_page->add_lang_dialog);
+  }
+
+  g_clear_pointer (&general_page->webapp, ephy_web_application_free);
+  G_OBJECT_CLASS (prefs_general_page_parent_class)->finalize (object);
+}
+
+static int
+get_list_box_length (GtkWidget *list_box)
+{
+  GList *children = gtk_container_get_children (GTK_CONTAINER (list_box));
+
+  return g_list_length (children);
+}
+
+static void
+language_editor_update_pref (PrefsGeneralPage *general_page)
+{
+  GVariantBuilder builder;
+  GtkListBoxRow *row;
+  int index = 0;
+
+  if (get_list_box_length (general_page->lang_listbox) <= 1) {
+    g_settings_set (EPHY_SETTINGS_WEB,
+                    EPHY_PREFS_WEB_LANGUAGE,
+                    "as", NULL);
+    return;
+  }
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY);
+
+  while ((row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (general_page->lang_listbox), index++))) {
+    char *code;
+
+    code = g_object_get_data (G_OBJECT (row), "code");
+    if (code)
+      g_variant_builder_add (&builder, "s", code);
+  }
+
+  g_settings_set (EPHY_SETTINGS_WEB,
+                  EPHY_PREFS_WEB_LANGUAGE,
+                  "as", &builder);
+}
+
+static void
+drag_data_received (GtkWidget        *widget,
+                    GdkDragContext   *context,
+                    gint              x,
+                    gint              y,
+                    GtkSelectionData *selection_data,
+                    guint             info,
+                    guint32           time,
+                    gpointer          data)
+{
+  GtkWidget *row_before;
+  GtkWidget *row_after;
+  GtkWidget *row;
+  GtkWidget *source;
+  PrefsGeneralPage *general_page = data;
+  int len;
+  int pos;
+
+  row_before = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-before"));
+  row_after = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-after"));
+
+  g_object_set_data (G_OBJECT (widget), "row-before", NULL);
+  g_object_set_data (G_OBJECT (widget), "row-after", NULL);
+
+  if (row_before)
+    gtk_style_context_remove_class (gtk_widget_get_style_context (row_before), "drag-hover-bottom");
+  if (row_after)
+    gtk_style_context_remove_class (gtk_widget_get_style_context (row_after), "drag-hover-top");
+
+  row = (gpointer) * (gpointer *)gtk_selection_data_get_data (selection_data);
+  source = gtk_widget_get_ancestor (row, GTK_TYPE_LIST_BOX_ROW);
+
+  if (source == row_after)
+    return;
+
+  len = get_list_box_length (general_page->lang_listbox);
+  g_object_ref (source);
+  gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (source)), source);
+
+  if (row_after)
+    pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (row_after));
+  else
+    pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (row_before)) + 1;
+
+  if (pos + 1 == len)
+    pos--;
+
+  gtk_list_box_insert (GTK_LIST_BOX (widget), source, pos);
+  g_object_unref (source);
+
+  language_editor_update_pref (general_page);
+}
+
+static GtkListBoxRow *
+get_row_before (GtkListBox    *list,
+                GtkListBoxRow *row)
+{
+  int pos = gtk_list_box_row_get_index (row);
+  return gtk_list_box_get_row_at_index (list, pos - 1);
+}
+
+static GtkListBoxRow *
+get_row_after (GtkListBox    *list,
+               GtkListBoxRow *row)
+{
+  int pos = gtk_list_box_row_get_index (row);
+  return gtk_list_box_get_row_at_index (list, pos + 1);
+}
+
+static GtkListBoxRow *
+get_last_row (GtkListBox *list)
+{
+  int i;
+
+  for (i = 0; ; i++) {
+    GtkListBoxRow *tmp;
+    tmp = gtk_list_box_get_row_at_index (list, i);
+    if (tmp == NULL)
+      break;
+  }
+
+  return i > 0 ? gtk_list_box_get_row_at_index (list, i - 1) : NULL;
+}
+
+static gboolean
+drag_motion (GtkWidget      *widget,
+             GdkDragContext *context,
+             int             x,
+             int             y,
+             guint           time)
+{
+  GtkAllocation alloc;
+  GtkWidget *row;
+  int hover_row_y;
+  int hover_row_height;
+  GtkWidget *drag_row;
+  GtkWidget *row_before;
+  GtkWidget *row_after;
+
+  row = GTK_WIDGET (gtk_list_box_get_row_at_y (GTK_LIST_BOX (widget), y));
+
+  drag_row = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "drag-row"));
+  row_before = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-before"));
+  row_after = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-after"));
+
+  gtk_style_context_remove_class (gtk_widget_get_style_context (drag_row), "drag-hover");
+  if (row_before)
+    gtk_style_context_remove_class (gtk_widget_get_style_context (row_before), "drag-hover-bottom");
+  if (row_after)
+    gtk_style_context_remove_class (gtk_widget_get_style_context (row_after), "drag-hover-top");
+
+  if (row) {
+    gtk_widget_get_allocation (row, &alloc);
+    hover_row_y = alloc.y;
+    hover_row_height = alloc.height;
+
+    if (y < hover_row_y + hover_row_height / 2) {
+      row_after = row;
+      row_before = GTK_WIDGET (get_row_before (GTK_LIST_BOX (widget), GTK_LIST_BOX_ROW (row)));
+    } else {
+      row_before = row;
+      row_after = GTK_WIDGET (get_row_after (GTK_LIST_BOX (widget), GTK_LIST_BOX_ROW (row)));
+    }
+  } else {
+    row_before = GTK_WIDGET (get_last_row (GTK_LIST_BOX (widget)));
+    row_after = NULL;
+  }
+
+  g_object_set_data (G_OBJECT (widget), "row-before", row_before);
+  g_object_set_data (G_OBJECT (widget), "row-after", row_after);
+
+  if (drag_row == row_before || drag_row == row_after) {
+    gtk_style_context_add_class (gtk_widget_get_style_context (drag_row), "drag-hover");
+    return FALSE;
+  }
+
+  if (row_before)
+    gtk_style_context_add_class (gtk_widget_get_style_context (row_before), "drag-hover-bottom");
+  if (row_after)
+    gtk_style_context_add_class (gtk_widget_get_style_context (row_after), "drag-hover-top");
+
+  return TRUE;
+}
+
+static void
+drag_leave (GtkWidget      *widget,
+            GdkDragContext *context,
+            guint           time)
+{
+  GtkWidget *drag_row;
+  GtkWidget *row_before;
+  GtkWidget *row_after;
+
+  drag_row = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "drag-row"));
+  row_before = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-before"));
+  row_after = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "row-after"));
+
+  gtk_style_context_remove_class (gtk_widget_get_style_context (drag_row), "drag-hover");
+  if (row_before)
+    gtk_style_context_remove_class (gtk_widget_get_style_context (row_before), "drag-hover-bottom");
+  if (row_after)
+    gtk_style_context_remove_class (gtk_widget_get_style_context (row_after), "drag-hover-top");
+}
+
+static GtkDialog *setup_add_language_dialog (PrefsGeneralPage *general_page);
+
+static void
+language_editor_add_button_release_event (GtkWidget        *button,
+                                          GdkEvent         *event,
+                                          PrefsGeneralPage *general_page)
+{
+  if (general_page->add_lang_dialog == NULL) {
+    GtkDialog **add_lang_dialog;
+
+    general_page->add_lang_dialog = setup_add_language_dialog (general_page);
+    gtk_window_set_transient_for (GTK_WINDOW (general_page->add_lang_dialog), GTK_WINDOW (general_page));
+
+    add_lang_dialog = &general_page->add_lang_dialog;
+
+    g_object_add_weak_pointer
+      (G_OBJECT (general_page->add_lang_dialog),
+      (gpointer *)add_lang_dialog);
+  }
+
+  gtk_window_present_with_time (GTK_WINDOW (general_page->add_lang_dialog), gtk_get_current_event_time ());
+}
+
+static void
+language_editor_add_function_buttons (PrefsGeneralPage *general_page)
+{
+  GtkWidget *box;
+  GtkWidget *label;
+
+  box = gtk_event_box_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_insert (GTK_LIST_BOX (general_page->lang_listbox), box, -1);
+}
+
+static void
+language_editor_update_state (PrefsGeneralPage *general_page)
+{
+  int length = get_list_box_length (general_page->lang_listbox);
+  int index;
+
+  if (length == 2) {
+    GtkListBoxRow *row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (general_page->lang_listbox), 0);
+    GtkWidget *action = g_object_get_data (G_OBJECT (row), "action");
+
+    gtk_widget_set_sensitive (action, FALSE);
+    return;
+  }
+
+  for (index = 0; index < length - 1; index++) {
+    GtkListBoxRow *row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (general_page->lang_listbox), index);
+    GtkWidget *action = g_object_get_data (G_OBJECT (row), "action");
+
+    gtk_widget_set_sensitive (action, TRUE);
+  }
+}
+
+static void
+language_editor_remove_button_clicked_cb (GtkWidget        *button,
+                                          PrefsGeneralPage *general_page)
+{
+  GtkWidget *row = g_object_get_data (G_OBJECT (button), "row");
+
+  if (row) {
+    gtk_container_remove (GTK_CONTAINER (general_page->lang_listbox), 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)
+{
+  gtk_selection_data_set (selection_data,
+                          gdk_atom_intern_static_string ("GTK_LIST_BOX_ROW"),
+                          32,
+                          (const guchar *)&widget,
+                          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, GTK_TYPE_LIST_BOX_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)), "drag-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)
+{
+  GtkWidget *row;
+
+  row = gtk_widget_get_ancestor (widget, GTK_TYPE_LIST_BOX_ROW);
+  g_object_set_data (G_OBJECT (gtk_widget_get_parent (row)), "drag-row", NULL);
+  gtk_style_context_remove_class (gtk_widget_get_style_context (row), "drag-row");
+  gtk_style_context_remove_class (gtk_widget_get_style_context (row), "drag-hover");
+}
+
+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 *event_box;
+  HdyActionRow *row;
+  GtkWidget *prefix;
+  GtkWidget *action;
+  int len;
+  int index;
+
+  g_assert (code != NULL && desc != NULL);
+
+  len = get_list_box_length (general_page->lang_listbox);
+
+  for (index = 0; index < len; index++) {
+    GtkListBoxRow *widget;
+    char *row_code;
+
+    widget = gtk_list_box_get_row_at_index (GTK_LIST_BOX (general_page->lang_listbox), index++);
+
+    row_code = g_object_get_data (G_OBJECT (widget), "code");
+    if (row_code && strcmp (row_code, code) == 0)
+      return;
+  }
+
+  row = hdy_action_row_new ();
+  hdy_action_row_set_title (row, desc);
+  g_object_set_data (G_OBJECT (row), "code", g_strdup (code));
+  gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (row)), "row");
+
+  event_box = gtk_event_box_new ();
+  gtk_drag_source_set (GTK_WIDGET (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);
+  prefix = gtk_image_new_from_icon_name ("list-drag-handle-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
+  gtk_container_add (GTK_CONTAINER (event_box), prefix);
+  hdy_action_row_add_prefix (row, event_box);
+
+  action = gtk_button_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
+  gtk_widget_set_tooltip_text (action, _("Delete language"));
+  g_object_set_data (G_OBJECT (row), "action", action);
+  g_object_set_data (G_OBJECT (action), "row", row);
+  g_signal_connect (action, "clicked", G_CALLBACK (language_editor_remove_button_clicked_cb), general_page);
+  gtk_widget_set_valign (action, GTK_ALIGN_CENTER);
+  hdy_action_row_add_action (row, action);
+
+  gtk_widget_show_all (GTK_WIDGET (row));
+
+  gtk_list_box_insert (GTK_LIST_BOX (general_page->lang_listbox), GTK_WIDGET (row), len - 1);
+}
+
+static void
+add_lang_dialog_response_cb (GtkWidget        *widget,
+                             int               response,
+                             PrefsGeneralPage *general_page)
+{
+  GtkDialog *dialog = general_page->add_lang_dialog;
+  GtkTreeModel *model;
+  GtkTreeSelection *selection;
+  GtkTreeIter iter;
+  GList *rows, *r;
+
+  g_assert (dialog != NULL);
+
+  if (response == GTK_RESPONSE_ACCEPT) {
+    selection = gtk_tree_view_get_selection (general_page->add_lang_treeview);
+
+    rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
+    for (r = rows; r != NULL; r = r->next) {
+      GtkTreePath *path = (GtkTreePath *)r->data;
+
+      if (gtk_tree_model_get_iter (model, &iter, path)) {
+        char *code, *desc;
+
+        gtk_tree_model_get (model, &iter,
+                            COL_LANG_NAME, &desc,
+                            COL_LANG_CODE, &code,
+                            -1);
+
+        language_editor_add (general_page, code, desc);
+
+        g_free (desc);
+        g_free (code);
+      }
+    }
+
+    g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
+    g_list_free (rows);
+
+    language_editor_update_pref (general_page);
+    language_editor_update_state (general_page);
+  }
+
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+add_lang_dialog_selection_changed (GtkTreeSelection *selection,
+                                   GtkWidget        *button)
+{
+  int n_selected;
+
+  n_selected = gtk_tree_selection_count_selected_rows (selection);
+  gtk_widget_set_sensitive (button, n_selected > 0);
+}
+
+static void
+add_language_add_system_language_entry (GtkListStore *store)
+{
+  GtkTreeIter iter;
+  char **sys_langs;
+  char *system, *text;
+  int n_sys_langs;
+
+  sys_langs = ephy_langs_get_languages ();
+  n_sys_langs = g_strv_length (sys_langs);
+
+  system = g_strjoinv (", ", sys_langs);
+
+  text = g_strdup_printf
+           (ngettext ("System language (%s)",
+                      "System languages (%s)", n_sys_langs), system);
+
+  gtk_list_store_append (store, &iter);
+  gtk_list_store_set (store, &iter,
+                      COL_LANG_NAME, text,
+                      COL_LANG_CODE, "system",
+                      -1);
+
+  g_strfreev (sys_langs);
+  g_free (system);
+  g_free (text);
+}
+
+static GtkDialog *
+setup_add_language_dialog (PrefsGeneralPage *general_page)
+{
+  GtkWidget *ad;
+  GtkWidget *add_button;
+  GtkListStore *store;
+  GtkTreeModel *sortmodel;
+  GtkTreeView *treeview;
+  GtkCellRenderer *renderer;
+  GtkTreeViewColumn *column;
+  GtkTreeSelection *selection;
+  GtkTreeIter iter;
+  guint i, n;
+  GtkBuilder *builder;
+  g_auto (GStrv) locales;
+
+  builder = gtk_builder_new_from_resource ("/org/gnome/epiphany/gtk/prefs-lang-dialog.ui");
+  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"));
+  general_page->add_lang_treeview = treeview;
+
+  store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+  locales = gnome_get_all_locales ();
+  n = g_strv_length (locales);
+
+  for (i = 0; i < n; i++) {
+    const char *locale = locales[i];
+    g_autofree char *language_code = NULL;
+    g_autofree char *country_code = NULL;
+    g_autofree char *language_name = NULL;
+    g_autofree char *shortened_locale = NULL;
+
+    if (!gnome_parse_locale (locale, &language_code, &country_code, NULL, NULL))
+      break;
+
+    if (language_code == NULL)
+      break;
+
+    language_name = gnome_get_language_from_locale (locale, locale);
+
+    if (country_code != NULL)
+      shortened_locale = g_strdup_printf ("%s-%s", language_code, country_code);
+    else
+      shortened_locale = g_strdup (language_code);
+
+    gtk_list_store_append (store, &iter);
+    gtk_list_store_set (store, &iter,
+                        COL_LANG_NAME, language_name,
+                        COL_LANG_CODE, shortened_locale,
+                        -1);
+  }
+
+  add_language_add_system_language_entry (store);
+
+  sortmodel = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
+  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 (general_page)),
+                               GTK_WINDOW (ad));
+  gtk_window_set_modal (GTK_WINDOW (ad), TRUE);
+
+  gtk_tree_view_set_reorderable (GTK_TREE_VIEW (treeview), FALSE);
+
+  gtk_tree_view_set_model (treeview, sortmodel);
+
+  gtk_tree_view_set_headers_visible (treeview, FALSE);
+
+  renderer = gtk_cell_renderer_text_new ();
+
+  gtk_tree_view_insert_column_with_attributes (treeview,
+                                               0, "Language",
+                                               renderer,
+                                               "text", 0,
+                                               NULL);
+  column = gtk_tree_view_get_column (treeview, 0);
+  gtk_tree_view_column_set_resizable (column, TRUE);
+  gtk_tree_view_column_set_sort_column_id (column, COL_LANG_NAME);
+
+  selection = gtk_tree_view_get_selection (treeview);
+  gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+
+  add_lang_dialog_selection_changed (GTK_TREE_SELECTION (selection), add_button);
+  g_signal_connect (selection, "changed",
+                    G_CALLBACK (add_lang_dialog_selection_changed), add_button);
+
+  g_signal_connect (ad, "response",
+                    G_CALLBACK (add_lang_dialog_response_cb), general_page);
+
+  g_object_unref (store);
+  g_object_unref (sortmodel);
+
+  return GTK_DIALOG (ad);
+}
+
+static char *
+language_for_locale (const char *locale)
+{
+  g_autoptr (GString) string = g_string_new (locale);
+
+  /* Before calling gnome_get_language_from_locale() we have to convert
+   * from web locales (e.g. es-ES) to UNIX (e.g. es_ES.UTF-8).
+   */
+  g_strdelimit (string->str, "-", '_');
+  g_string_append (string, ".UTF-8");
+
+  return gnome_get_language_from_locale (string->str, string->str);
+}
+
+static char *
+normalize_locale (const char *locale)
+{
+  char *result = g_strdup (locale);
+
+  /* The result we store in prefs looks like es-ES or en-US. We don't
+   * store codeset (not used in Accept-Langs) and we store with hyphen
+   * instead of underscore (ditto). So here we just uppercase the
+   * country code, converting e.g. es-es to es-ES. We have to do this
+   * because older versions of Epiphany stored locales as entirely
+   * lowercase.
+   */
+  for (char *p = strchr (result, '-'); p != NULL && *p != '\0'; p++)
+    *p = g_ascii_toupper (*p);
+
+  return result;
+}
+
+static void
+add_system_language_entry (PrefsGeneralPage *general_page)
+{
+  g_auto (GStrv) sys_langs = NULL;
+  g_autofree char *system = NULL;
+  g_autofree char *text = NULL;
+  int n_sys_langs;
+
+  sys_langs = ephy_langs_get_languages ();
+  n_sys_langs = g_strv_length (sys_langs);
+
+  system = g_strjoinv (", ", sys_langs);
+
+  text = g_strdup_printf
+           (ngettext ("System language (%s)",
+                      "System languages (%s)", n_sys_langs), system);
+
+  language_editor_add (general_page, "system", text);
+}
+
+static void
+download_path_changed_cb (GtkFileChooser *button)
+{
+  char *dir;
+
+  dir = gtk_file_chooser_get_filename (button);
+  if (dir == NULL)
+    return;
+
+  g_settings_set_string (EPHY_SETTINGS_STATE,
+                         EPHY_PREFS_STATE_DOWNLOAD_DIR, dir);
+  g_free (dir);
+}
+
+static void
+create_download_path_button (PrefsGeneralPage *general_page)
+{
+  GtkWidget *button;
+  char *dir;
+
+  dir = ephy_file_get_downloads_dir ();
+
+  button = gtk_file_chooser_button_new (_("Select a directory"),
+                                        GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+
+  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (button), dir);
+  gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (button),
+                                           DOWNLOAD_BUTTON_WIDTH);
+  g_signal_connect (button, "selection-changed",
+                    G_CALLBACK (download_path_changed_cb), general_page);
+  hdy_action_row_add_action (HDY_ACTION_ROW (general_page->download_folder_row), button);
+  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
+  gtk_widget_show (button);
+
+  g_settings_bind_writable (EPHY_SETTINGS_STATE,
+                            EPHY_PREFS_STATE_DOWNLOAD_DIR,
+                            button, "sensitive", FALSE);
+  g_free (dir);
+}
+
+static gboolean
+restore_session_get_mapping (GValue   *value,
+                             GVariant *variant,
+                             gpointer  user_data)
+{
+  const char *policy = g_variant_get_string (variant, NULL);
+  /* FIXME: Is it possible to somehow use EPHY_PREFS_RESTORE_SESSION_POLICY_ALWAYS here? */
+  g_value_set_boolean (value, !strcmp (policy, "always"));
+  return TRUE;
+}
+
+static GVariant *
+restore_session_set_mapping (const GValue       *value,
+                             const GVariantType *expected_type,
+                             gpointer            user_data)
+{
+  /* FIXME: Is it possible to somehow use EphyPrefsRestoreSessionPolicy here? */
+  if (g_value_get_boolean (value))
+    return g_variant_new_string ("always");
+  return g_variant_new_string ("crashed");
+}
+
+static gboolean
+save_web_application (PrefsGeneralPage *general_page)
+{
+  gboolean changed = FALSE;
+  const char *text;
+
+  if (!general_page->webapp)
+    return G_SOURCE_REMOVE;
+
+  text = gtk_entry_get_text (GTK_ENTRY (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));
+  if (g_strcmp0 (general_page->webapp->name, text) != 0) {
+    g_free (general_page->webapp->name);
+    general_page->webapp->name = g_strdup (text);
+    changed = TRUE;
+  }
+
+  text = (const char *)g_object_get_data (G_OBJECT (general_page->webapp_icon), "ephy-webapp-icon-url");
+  if (g_strcmp0 (general_page->webapp->icon_url, text) != 0) {
+    g_free (general_page->webapp->icon_url);
+    general_page->webapp->icon_url = g_strdup (text);
+    changed = TRUE;
+  }
+
+  if (changed)
+    ephy_web_application_save (general_page->webapp);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+prefs_general_page_save_web_application (PrefsGeneralPage *general_page)
+{
+  if (!general_page->webapp)
+    return;
+
+  g_clear_handle_id (&general_page->webapp_save_id, g_source_remove);
+  general_page->webapp_save_id = g_timeout_add_seconds (1, (GSourceFunc)save_web_application, general_page);
+}
+
+static void
+prefs_general_page_update_webapp_icon (PrefsGeneralPage *general_page,
+                                       const char       *icon_url)
+{
+  GdkPixbuf *icon, *scaled_icon;
+  int icon_width, icon_height;
+  double scale;
+
+  icon = gdk_pixbuf_new_from_file (icon_url, NULL);
+  if (!icon)
+    return;
+
+  icon_width = gdk_pixbuf_get_width (icon);
+  icon_height = gdk_pixbuf_get_height (icon);
+  scale = MIN ((double)64 / icon_width, (double)64 / icon_height);
+  scaled_icon = gdk_pixbuf_scale_simple (icon, icon_width * scale, icon_height * scale, GDK_INTERP_NEAREST);
+  g_object_unref (icon);
+  gtk_image_set_from_pixbuf (GTK_IMAGE (general_page->webapp_icon), scaled_icon);
+  g_object_set_data_full (G_OBJECT (general_page->webapp_icon), "ephy-webapp-icon-url",
+                          g_strdup (icon_url), g_free);
+  g_object_unref (scaled_icon);
+}
+
+static void
+webapp_icon_chooser_response_cb (GtkNativeDialog  *file_chooser,
+                                 int               response,
+                                 PrefsGeneralPage *general_page)
+{
+  if (response == GTK_RESPONSE_ACCEPT) {
+    char *icon_url;
+
+    icon_url = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_chooser));
+    prefs_general_page_update_webapp_icon (general_page, icon_url);
+    g_free (icon_url);
+    prefs_general_page_save_web_application (general_page);
+  }
+
+  g_object_unref (file_chooser);
+}
+
+static void
+on_webapp_entry_changed (GtkEditable      *editable,
+                         PrefsGeneralPage *dialog)
+{
+  prefs_general_page_save_web_application (dialog);
+}
+
+static void
+prefs_dialog_response_cb (GtkWidget        *widget,
+                          int               response,
+                          PrefsGeneralPage *general_page)
+{
+  if (general_page->webapp_save_id) {
+    g_source_remove (general_page->webapp_save_id);
+    general_page->webapp_save_id = 0;
+    save_web_application (general_page);
+  }
+
+  gtk_widget_destroy (widget);
+}
+
+static void
+on_webapp_icon_button_clicked (GtkWidget        *button,
+                               PrefsGeneralPage *general_page)
+{
+  GtkFileChooser *file_chooser;
+  GSList *pixbuf_formats, *l;
+  GtkFileFilter *images_filter;
+
+  file_chooser = ephy_create_file_chooser (_("Web Application Icon"),
+                                           GTK_WIDGET (general_page),
+                                           GTK_FILE_CHOOSER_ACTION_OPEN,
+                                           EPHY_FILE_FILTER_NONE);
+  images_filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (images_filter, _("Supported Image Files"));
+  gtk_file_chooser_add_filter (file_chooser, images_filter);
+
+  pixbuf_formats = gdk_pixbuf_get_formats ();
+  for (l = pixbuf_formats; l; l = g_slist_next (l)) {
+    GdkPixbufFormat *format = (GdkPixbufFormat *)l->data;
+    GtkFileFilter *filter;
+    gchar *name;
+    gchar **mime_types;
+    guint i;
+
+    if (gdk_pixbuf_format_is_disabled (format) || !gdk_pixbuf_format_is_writable (format))
+      continue;
+
+    filter = gtk_file_filter_new ();
+    name = gdk_pixbuf_format_get_description (format);
+    gtk_file_filter_set_name (filter, name);
+    g_free (name);
+
+    mime_types = gdk_pixbuf_format_get_mime_types (format);
+    for (i = 0; mime_types[i] != 0; i++) {
+      gtk_file_filter_add_mime_type (images_filter, mime_types[i]);
+      gtk_file_filter_add_mime_type (filter, mime_types[i]);
+    }
+    g_strfreev (mime_types);
+
+    gtk_file_chooser_add_filter (file_chooser, filter);
+  }
+  g_slist_free (pixbuf_formats);
+
+  g_signal_connect (file_chooser, "response",
+                    G_CALLBACK (webapp_icon_chooser_response_cb),
+                    general_page);
+
+  gtk_native_dialog_show (GTK_NATIVE_DIALOG (file_chooser));
+}
+
+static void
+custom_homepage_entry_changed (GtkEntry         *entry,
+                               PrefsGeneralPage *general_page)
+{
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_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_widget_set_sensitive (general_page->custom_homepage_entry, TRUE);
+    gtk_widget_grab_focus (general_page->custom_homepage_entry);
+  }
+}
+
+static void
+custom_homepage_entry_icon_released (GtkEntry             *entry,
+                                     GtkEntryIconPosition  icon_pos)
+{
+  if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
+    gtk_entry_set_text (entry, "");
+}
+
+static gboolean
+new_tab_homepage_get_mapping (GValue   *value,
+                              GVariant *variant,
+                              gpointer  user_data)
+{
+  const char *setting;
+
+  setting = g_variant_get_string (variant, NULL);
+  if (!setting || setting[0] == '\0')
+    g_value_set_boolean (value, TRUE);
+
+  return TRUE;
+}
+
+static GVariant *
+new_tab_homepage_set_mapping (const GValue       *value,
+                              const GVariantType *expected_type,
+                              gpointer            user_data)
+{
+  PrefsGeneralPage *general_page = EPHY_PREFS_GENERAL_PAGE (user_data);
+
+  if (!g_value_get_boolean (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_widget_set_sensitive (general_page->custom_homepage_entry, FALSE);
+
+  return g_variant_new_string ("");
+}
+
+static gboolean
+blank_homepage_get_mapping (GValue   *value,
+                            GVariant *variant,
+                            gpointer  user_data)
+{
+  const char *setting;
+
+  setting = g_variant_get_string (variant, NULL);
+  if (g_strcmp0 (setting, "about:blank") == 0)
+    g_value_set_boolean (value, TRUE);
+
+  return TRUE;
+}
+
+static GVariant *
+blank_homepage_set_mapping (const GValue       *value,
+                            const GVariantType *expected_type,
+                            gpointer            user_data)
+{
+  PrefsGeneralPage *general_page = EPHY_PREFS_GENERAL_PAGE (user_data);
+
+  if (!g_value_get_boolean (value))
+    return NULL;
+
+  gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+
+  return g_variant_new_string ("about:blank");
+}
+
+static gboolean
+custom_homepage_get_mapping (GValue   *value,
+                             GVariant *variant,
+                             gpointer  user_data)
+{
+  const char *setting;
+
+  setting = g_variant_get_string (variant, NULL);
+  if (setting && setting[0] != '\0' && g_strcmp0 (setting, "about:blank") != 0)
+    g_value_set_boolean (value, TRUE);
+  return TRUE;
+}
+
+static GVariant *
+custom_homepage_set_mapping (const GValue       *value,
+                             const GVariantType *expected_type,
+                             gpointer            user_data)
+{
+  PrefsGeneralPage *general_page = EPHY_PREFS_GENERAL_PAGE (user_data);
+  const char *setting;
+
+  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), "");
+    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));
+  if (!setting || setting[0] == '\0')
+    return NULL;
+
+  gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), setting);
+
+  return g_variant_new_string (setting);
+}
+
+static void
+on_search_engine_dialog_button_clicked (GtkWidget        *button,
+                                        PrefsGeneralPage *general_page)
+{
+  GtkWindow *search_engine_dialog;
+
+  search_engine_dialog = GTK_WINDOW (ephy_search_engine_dialog_new ());
+
+  gtk_window_set_transient_for (search_engine_dialog, GTK_WINDOW (general_page));
+  gtk_window_set_modal (search_engine_dialog, TRUE);
+  gtk_window_present_with_time (search_engine_dialog, gtk_get_current_event_time ());
+}
+
+static void
+on_manage_webapp_additional_urls_button_clicked (GtkWidget        *button,
+                                                 PrefsGeneralPage *general_page)
+{
+  EphyWebappAdditionalURLsDialog *urls_dialog;
+
+  urls_dialog = ephy_webapp_additional_urls_dialog_new ();
+  gtk_window_set_transient_for (GTK_WINDOW (urls_dialog), GTK_WINDOW (general_page));
+  gtk_window_set_modal (GTK_WINDOW (urls_dialog), TRUE);
+  gtk_window_present_with_time (GTK_WINDOW (urls_dialog), gtk_get_current_event_time ());
+}
+
+static void
+prefs_general_page_class_init (PrefsGeneralPageClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = prefs_general_page_finalize;
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/epiphany/gtk/prefs-general-page.ui");
+
+  /* Web Application */
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, webapp_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, webapp_icon);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, webapp_url);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, webapp_title);
+
+  /* Homepage */
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, homepage_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, new_tab_homepage_radiobutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, blank_homepage_radiobutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, custom_homepage_radiobutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, custom_homepage_entry);
+
+  /* Downloads */
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, download_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, ask_on_download_switch);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, download_folder_row);
+
+  /* Search Engines */
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, search_box);
+
+  /* Session */
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, session_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, start_in_incognito_mode_switch);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, restore_session_row);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, restore_session_switch);
+
+  /* Browsing */
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, browsing_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, enable_smooth_scrolling_switch);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, enable_mouse_gesture_switch);
+
+  /* Languages */
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, lang_group);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, lang_listbox);
+  gtk_widget_class_bind_template_child (widget_class, PrefsGeneralPage, enable_spell_checking_switch);
+
+  /* Signals */
+  gtk_widget_class_bind_template_callback (widget_class, on_webapp_icon_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_webapp_entry_changed);
+  gtk_widget_class_bind_template_callback (widget_class, on_manage_webapp_additional_urls_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_search_engine_dialog_button_clicked);
+}
+
+static const char *css =
+  ".row.drag-icon { "
+  "  background: white; "
+  "  border: 1px solid black; "
+  "}"
+  ".row.drag-row { "
+  "  color: gray; "
+  "  background: alpha(gray,0.2); "
+  "}"
+  ".row.drag-row.drag-hover { "
+  "  border-top: 1px solid #4e9a06; "
+  "  border-bottom: 1px solid #4e9a06; "
+  "}"
+  ".row.drag-hover image, "
+  ".row.drag-hover label { "
+  "  color: #4e9a06; "
+  "}"
+  ".row.drag-hover-top {"
+  "  border-top: 1px solid #4e9a06; "
+  "}"
+  ".row.drag-hover-bottom {"
+  "  border-bottom: 1px solid #4e9a06; "
+  "}"
+;
+
+static void
+init_lang_listbox (PrefsGeneralPage *general_page)
+{
+  char **list = NULL;
+  int i;
+  GtkCssProvider *provider;
+  GtkStyleContext *style_context;
+
+  style_context = gtk_widget_get_style_context (general_page->lang_listbox);
+  gtk_style_context_add_class (style_context, "frame");
+  gtk_list_box_set_header_func (GTK_LIST_BOX (general_page->lang_listbox), hdy_list_box_separator_header, 
NULL, NULL);
+
+  provider = gtk_css_provider_new ();
+  gtk_css_provider_load_from_data (provider, css, -1, NULL);
+  gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), 800);
+
+  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);
+  g_signal_connect (general_page->lang_listbox, "drag-leave", G_CALLBACK (drag_leave), NULL);
+
+  list = g_settings_get_strv (EPHY_SETTINGS_WEB,
+                              EPHY_PREFS_WEB_LANGUAGE);
+
+  language_editor_add_function_buttons (general_page);
+
+  /* Fill languages editor */
+  for (i = 0; list[i]; i++) {
+    const char *code = list[i];
+    if (strcmp (code, "system") == 0) {
+      add_system_language_entry (general_page);
+    } else if (code[0] != '\0') {
+      g_autofree char *normalized_locale = normalize_locale (code);
+      if (normalized_locale != NULL) {
+        g_autofree char *language_name = language_for_locale (normalized_locale);
+        if (language_name == NULL)
+          language_name = g_strdup (normalized_locale);
+        language_editor_add (general_page, normalized_locale, language_name);
+      }
+    }
+  }
+}
+
+void
+prefs_general_page_connect_pd_response (PrefsGeneralPage *general_page,
+                                        PrefsDialog      *pd)
+{
+  g_signal_connect (pd, "response", G_CALLBACK (prefs_dialog_response_cb), general_page);
+}
+
+static void
+setup_general_page (PrefsGeneralPage *general_page)
+{
+  GSettings *settings = ephy_settings_get (EPHY_PREFS_SCHEMA);
+  GSettings *web_settings = ephy_settings_get (EPHY_PREFS_WEB_SCHEMA);
+
+  /* ======================================================================== */
+  /* ========================== Web Application ============================= */
+  /* ======================================================================== */
+  if (ephy_embed_shell_get_mode (ephy_embed_shell_get_default ()) == EPHY_EMBED_SHELL_MODE_APPLICATION) {
+    general_page->webapp = ephy_web_application_for_profile_directory (ephy_profile_dir ());
+    g_assert (general_page->webapp);
+    prefs_general_page_update_webapp_icon (general_page, general_page->webapp->icon_url);
+    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);
+  }
+
+  /* ======================================================================== */
+  /* ========================== Homepage ==================================== */
+  /* ======================================================================== */
+  g_settings_bind_with_mapping (settings,
+                                EPHY_PREFS_HOMEPAGE_URL,
+                                general_page->new_tab_homepage_radiobutton,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                new_tab_homepage_get_mapping,
+                                new_tab_homepage_set_mapping,
+                                general_page,
+                                NULL);
+
+  g_settings_bind_with_mapping (settings,
+                                EPHY_PREFS_HOMEPAGE_URL,
+                                general_page->blank_homepage_radiobutton,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                blank_homepage_get_mapping,
+                                blank_homepage_set_mapping,
+                                general_page,
+                                NULL);
+
+  g_settings_bind_with_mapping (settings,
+                                EPHY_PREFS_HOMEPAGE_URL,
+                                general_page->custom_homepage_radiobutton,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                custom_homepage_get_mapping,
+                                custom_homepage_set_mapping,
+                                general_page,
+                                NULL);
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_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));
+  } else {
+    gtk_widget_set_sensitive (general_page->custom_homepage_entry, FALSE);
+    gtk_entry_set_text (GTK_ENTRY (general_page->custom_homepage_entry), "");
+  }
+
+  g_signal_connect (general_page->custom_homepage_entry, "changed",
+                    G_CALLBACK (custom_homepage_entry_changed),
+                    general_page);
+  g_signal_connect (general_page->custom_homepage_entry, "icon-release",
+                    G_CALLBACK (custom_homepage_entry_icon_released),
+                    NULL);
+
+  /* ======================================================================== */
+  /* ========================== Downloads =================================== */
+  /* ======================================================================== */
+  if (ephy_is_running_inside_flatpak ())
+    gtk_widget_hide (general_page->download_box);
+  else
+    create_download_path_button (general_page);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ASK_ON_DOWNLOAD,
+                   general_page->ask_on_download_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  /* ======================================================================== */
+  /* ========================== Session ===================================== */
+  /* ======================================================================== */
+  g_settings_bind (settings,
+                   EPHY_PREFS_START_IN_INCOGNITO_MODE,
+                   general_page->start_in_incognito_mode_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  g_settings_bind (settings,
+                   EPHY_PREFS_START_IN_INCOGNITO_MODE,
+                   general_page->restore_session_row,
+                   "sensitive",
+                   G_SETTINGS_BIND_INVERT_BOOLEAN);
+
+  g_settings_bind_with_mapping (settings,
+                                EPHY_PREFS_RESTORE_SESSION_POLICY,
+                                general_page->restore_session_switch,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                restore_session_get_mapping,
+                                restore_session_set_mapping,
+                                NULL, NULL);
+
+  /* ======================================================================== */
+  /* ========================== Browsing ==================================== */
+  /* ======================================================================== */
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_SMOOTH_SCROLLING,
+                   general_page->enable_smooth_scrolling_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_MOUSE_GESTURES,
+                   general_page->enable_mouse_gesture_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  /* ======================================================================== */
+  /* ========================== Languages =================================== */
+  /* ======================================================================== */
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_SPELL_CHECKING,
+                   general_page->enable_spell_checking_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  init_lang_listbox (general_page);
+}
+
+static void
+prefs_general_page_init (PrefsGeneralPage *general_page)
+{
+  EphyEmbedShellMode mode = ephy_embed_shell_get_mode (ephy_embed_shell_get_default ());
+
+  gtk_widget_init_template (GTK_WIDGET (general_page));
+
+  gtk_widget_set_visible (general_page->webapp_box,
+                          mode == EPHY_EMBED_SHELL_MODE_APPLICATION);
+  gtk_widget_set_visible (general_page->homepage_box,
+                          mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+  gtk_widget_set_visible (general_page->search_box,
+                          mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+  gtk_widget_set_visible (general_page->session_box,
+                          mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+  gtk_widget_set_visible (general_page->browsing_box,
+                          mode != EPHY_EMBED_SHELL_MODE_APPLICATION);
+
+  setup_general_page (general_page);
+}
diff --git a/src/preferences/prefs-general-page.h b/src/preferences/prefs-general-page.h
new file mode 100644
index 000000000..a235c46b6
--- /dev/null
+++ b/src/preferences/prefs-general-page.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  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 <glib-object.h>
+
+#define HANDY_USE_UNSTABLE_API
+#include <handy.h>
+
+#include "prefs-dialog.h"
+
+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)
+
+void prefs_general_page_connect_pd_response (PrefsGeneralPage *general_page,
+                                             PrefsDialog      *pd);
+
+G_END_DECLS
diff --git a/src/preferences/prefs-privacy-page.c b/src/preferences/prefs-privacy-page.c
new file mode 100644
index 000000000..53714d5d6
--- /dev/null
+++ b/src/preferences/prefs-privacy-page.c
@@ -0,0 +1,243 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  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 "prefs-privacy-page.h"
+
+#include "cookies-dialog.h"
+#include "clear-data-dialog.h"
+#include "passwords-dialog.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+
+struct _PrefsPrivacyPage {
+  HdyPreferencesPage parent_instance;
+
+  /* Web Content */
+  GtkWidget *popups_allow_switch;
+  GtkWidget *adblock_allow_switch;
+  GtkWidget *enable_safe_browsing_switch;
+
+  /* Cookies */
+  GtkWidget *always;
+  GtkWidget *no_third_party;
+  GtkWidget *never;
+
+  /* Passwords */
+  GtkWidget *remember_passwords_switch;
+
+  /* Personal Data */
+  GtkWidget *clear_personal_data_button;
+};
+
+G_DEFINE_TYPE (PrefsPrivacyPage, prefs_privacy_page, HDY_TYPE_PREFERENCES_PAGE)
+
+static void
+on_manage_cookies_button_clicked (GtkWidget        *button,
+                                  PrefsPrivacyPage *privacy_page)
+{
+  EphyCookiesDialog *cookies_dialog;
+
+  cookies_dialog = ephy_cookies_dialog_new ();
+
+  gtk_window_set_transient_for (GTK_WINDOW (cookies_dialog), GTK_WINDOW (privacy_page));
+  gtk_window_set_modal (GTK_WINDOW (cookies_dialog), TRUE);
+  gtk_window_present_with_time (GTK_WINDOW (cookies_dialog), gtk_get_current_event_time ());
+}
+
+static void
+on_manage_passwords_button_clicked (GtkWidget        *button,
+                                    PrefsPrivacyPage *privacy_page)
+{
+  EphyPasswordsDialog *passwords_dialog;
+  EphyPasswordManager *password_manager;
+
+  password_manager = ephy_embed_shell_get_password_manager (EPHY_EMBED_SHELL (ephy_shell_get_default ()));
+  passwords_dialog = ephy_passwords_dialog_new (password_manager);
+
+  gtk_window_set_transient_for (GTK_WINDOW (passwords_dialog), GTK_WINDOW (privacy_page));
+  gtk_window_set_modal (GTK_WINDOW (passwords_dialog), TRUE);
+  gtk_window_present_with_time (GTK_WINDOW (passwords_dialog), gtk_get_current_event_time ());
+}
+
+static gboolean
+cookies_get_mapping (GValue   *value,
+                     GVariant *variant,
+                     gpointer  user_data)
+{
+  const char *setting;
+  const char *name;
+
+  setting = g_variant_get_string (variant, NULL);
+  name = gtk_buildable_get_name (GTK_BUILDABLE (user_data));
+
+  if (g_strcmp0 (name, "no_third_party") == 0)
+    name = "no-third-party";
+
+  /* If the button name matches the setting, it should be active. */
+  if (g_strcmp0 (name, setting) == 0)
+    g_value_set_boolean (value, TRUE);
+
+  return TRUE;
+}
+
+static GVariant *
+cookies_set_mapping (const GValue       *value,
+                     const GVariantType *expected_type,
+                     gpointer            user_data)
+{
+  GVariant *variant = NULL;
+  const char *name;
+
+  /* Don't act unless the button has been activated (turned ON). */
+  if (!g_value_get_boolean (value))
+    return NULL;
+
+  name = gtk_buildable_get_name (GTK_BUILDABLE (user_data));
+  if (g_strcmp0 (name, "no_third_party") == 0)
+    variant = g_variant_new_string ("no-third-party");
+  else
+    variant = g_variant_new_string (name);
+
+  return variant;
+}
+
+static void
+clear_personal_data_button_clicked_cb (GtkWidget        *button,
+                                       PrefsPrivacyPage *privacy_page)
+{
+  ClearDataDialog *clear_dialog;
+
+  clear_dialog = g_object_new (EPHY_TYPE_CLEAR_DATA_DIALOG, NULL);
+  gtk_window_set_transient_for (GTK_WINDOW (clear_dialog), GTK_WINDOW (privacy_page));
+  gtk_window_set_modal (GTK_WINDOW (clear_dialog), TRUE);
+  gtk_window_present_with_time (GTK_WINDOW (clear_dialog), gtk_get_current_event_time ());
+}
+
+static void
+setup_privacy_page (PrefsPrivacyPage *privacy_page)
+{
+  GSettings *web_settings = ephy_settings_get (EPHY_PREFS_WEB_SCHEMA);
+
+  /* ======================================================================== */
+  /* ========================== Web Content ================================= */
+  /* ======================================================================== */
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_ADBLOCK,
+                   privacy_page->adblock_allow_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_POPUPS,
+                   privacy_page->popups_allow_switch,
+                   "active",
+                   G_SETTINGS_BIND_INVERT_BOOLEAN);
+
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_ENABLE_SAFE_BROWSING,
+                   privacy_page->enable_safe_browsing_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  /* ======================================================================== */
+  /* ========================== Cookies ===================================== */
+  /* ======================================================================== */
+  g_settings_bind_with_mapping (web_settings,
+                                EPHY_PREFS_WEB_COOKIES_POLICY,
+                                privacy_page->always,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                cookies_get_mapping,
+                                cookies_set_mapping,
+                                privacy_page->always,
+                                NULL);
+
+  g_settings_bind_with_mapping (web_settings,
+                                EPHY_PREFS_WEB_COOKIES_POLICY,
+                                privacy_page->no_third_party,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                cookies_get_mapping,
+                                cookies_set_mapping,
+                                privacy_page->no_third_party,
+                                NULL);
+
+  g_settings_bind_with_mapping (web_settings,
+                                EPHY_PREFS_WEB_COOKIES_POLICY,
+                                privacy_page->never,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                cookies_get_mapping,
+                                cookies_set_mapping,
+                                privacy_page->never,
+                                NULL);
+
+  /* ======================================================================== */
+  /* ========================== Passwords =================================== */
+  /* ======================================================================== */
+  g_settings_bind (web_settings,
+                   EPHY_PREFS_WEB_REMEMBER_PASSWORDS,
+                   privacy_page->remember_passwords_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  /* ======================================================================== */
+  /* ========================== Personal Data =============================== */
+  /* ======================================================================== */
+  g_signal_connect (privacy_page->clear_personal_data_button,
+                    "clicked",
+                    G_CALLBACK (clear_personal_data_button_clicked_cb),
+                    privacy_page);
+}
+
+static void
+prefs_privacy_page_class_init (PrefsPrivacyPageClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/epiphany/gtk/prefs-privacy-page.ui");
+
+  /* Web Content */
+  gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, popups_allow_switch);
+  gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, adblock_allow_switch);
+  gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, enable_safe_browsing_switch);
+
+  /* Cookies */
+  gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, always);
+  gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, no_third_party);
+  gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, never);
+
+  /* Passwords */
+  gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, remember_passwords_switch);
+
+  /* Personal Data */
+  gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, clear_personal_data_button);
+
+  /* Signals */
+  gtk_widget_class_bind_template_callback (widget_class, on_manage_cookies_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_manage_passwords_button_clicked);
+}
+
+static void
+prefs_privacy_page_init (PrefsPrivacyPage *privacy_page)
+{
+  gtk_widget_init_template (GTK_WIDGET (privacy_page));
+
+  setup_privacy_page (privacy_page);
+}
diff --git a/src/preferences/prefs-privacy-page.h b/src/preferences/prefs-privacy-page.h
new file mode 100644
index 000000000..5e99d603c
--- /dev/null
+++ b/src/preferences/prefs-privacy-page.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  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 <glib-object.h>
+
+#define HANDY_USE_UNSTABLE_API
+#include <handy.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_END_DECLS
diff --git a/src/preferences/prefs-sync-page.c b/src/preferences/prefs-sync-page.c
new file mode 100644
index 000000000..99de12ace
--- /dev/null
+++ b/src/preferences/prefs-sync-page.c
@@ -0,0 +1,790 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  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 "prefs-sync-page.h"
+
+#include "ephy-debug.h"
+#include "ephy-embed-prefs.h"
+#include "ephy-embed-shell.h"
+#include "ephy-prefs.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+#include "ephy-sync-service.h"
+#include "ephy-sync-utils.h"
+#include "ephy-time-helpers.h"
+#include "synced-tabs-dialog.h"
+
+#define FXA_IFRAME_URL "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v3";
+
+struct _PrefsSyncPage {
+  HdyPreferencesPage parent_instance;
+
+  GtkWidget *sync_page_box;
+  GtkWidget *sync_firefox_iframe_box;
+  GtkWidget *sync_firefox_iframe_label;
+  GtkWidget *sync_firefox_account_box;
+  GtkWidget *sync_firefox_account_row;
+  GtkWidget *sync_options_box;
+  GtkWidget *sync_bookmarks_checkbutton;
+  GtkWidget *sync_passwords_checkbutton;
+  GtkWidget *sync_history_checkbutton;
+  GtkWidget *sync_open_tabs_checkbutton;
+  GtkWidget *sync_frequency_row;
+  GtkWidget *sync_now_button;
+  GtkWidget *synced_tabs_button;
+  GtkWidget *sync_device_name_entry;
+  GtkWidget *sync_device_name_change_button;
+  GtkWidget *sync_device_name_save_button;
+  GtkWidget *sync_device_name_cancel_button;
+
+  WebKitWebView *fxa_web_view;
+  WebKitUserContentManager *fxa_manager;
+  WebKitUserScript *fxa_script;
+};
+
+G_DEFINE_TYPE (PrefsSyncPage, prefs_sync_page, HDY_TYPE_PREFERENCES_PAGE)
+
+static const guint sync_frequency_minutes[] = { 5, 15, 30, 60 };
+
+static void
+sync_collection_toggled_cb (GtkToggleButton *button,
+                            PrefsSyncPage   *sync_page)
+{
+  EphySynchronizableManager *manager = NULL;
+  EphyShell *shell = ephy_shell_get_default ();
+  EphySyncService *service = ephy_shell_get_sync_service (shell);
+
+  if (GTK_WIDGET (button) == sync_page->sync_bookmarks_checkbutton) {
+    manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_bookmarks_manager (shell));
+  } else if (GTK_WIDGET (button) == sync_page->sync_passwords_checkbutton) {
+    manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_embed_shell_get_password_manager (EPHY_EMBED_SHELL (shell)));
+  } else if (GTK_WIDGET (button) == sync_page->sync_history_checkbutton) {
+    manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_history_manager (shell));
+  } else if (GTK_WIDGET (button) == sync_page->sync_open_tabs_checkbutton) {
+    manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_open_tabs_manager (shell));
+    ephy_open_tabs_manager_clear_cache (EPHY_OPEN_TABS_MANAGER (manager));
+  } else {
+    g_assert_not_reached ();
+  }
+
+  if (gtk_toggle_button_get_active (button)) {
+    ephy_sync_service_register_manager (service, manager);
+  } else {
+    ephy_sync_service_unregister_manager (service, manager);
+    ephy_synchronizable_manager_set_is_initial_sync (manager, TRUE);
+  }
+}
+
+static void
+sync_set_last_sync_time (PrefsSyncPage *sync_page)
+{
+  gint64 sync_time = ephy_sync_utils_get_sync_time ();
+
+  if (sync_time) {
+    char *time = ephy_time_helpers_utf_friendly_time (sync_time);
+    /* Translators: the %s refers to the time at which the last sync was made.
+     * For example: Today 04:34 PM, Sun 11:25 AM, May 31 06:41 PM.
+     */
+    char *text = g_strdup_printf (_("Last synchronized: %s"), time);
+
+    hdy_action_row_set_subtitle (HDY_ACTION_ROW (sync_page->sync_firefox_account_row), text);
+
+    g_free (text);
+    g_free (time);
+  }
+}
+
+static void
+sync_finished_cb (EphySyncService *service,
+                  PrefsSyncPage   *sync_page)
+{
+  g_assert (EPHY_IS_SYNC_SERVICE (service));
+  g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+
+  gtk_widget_set_sensitive (sync_page->sync_now_button, TRUE);
+  sync_set_last_sync_time (sync_page);
+}
+
+static void
+sync_sign_in_details_show (PrefsSyncPage *sync_page,
+                           const char    *text)
+{
+  char *message;
+
+  g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+
+  message = g_strdup_printf ("<span fgcolor='#e6780b'>%s</span>", text);
+  gtk_label_set_markup (GTK_LABEL (sync_page->sync_firefox_iframe_label), message);
+  gtk_widget_set_visible (sync_page->sync_firefox_iframe_label, TRUE);
+
+  g_free (message);
+}
+
+static void
+sync_sign_in_error_cb (EphySyncService *service,
+                       const char      *error,
+                       PrefsSyncPage   *sync_page)
+{
+  g_assert (EPHY_IS_SYNC_SERVICE (service));
+  g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+
+  /* Display the error message and reload the iframe. */
+  sync_sign_in_details_show (sync_page, error);
+  webkit_web_view_load_uri (sync_page->fxa_web_view, FXA_IFRAME_URL);
+}
+
+static void
+sync_secrets_store_finished_cb (EphySyncService *service,
+                                GError          *error,
+                                PrefsSyncPage   *sync_page)
+{
+  g_assert (EPHY_IS_SYNC_SERVICE (service));
+  g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+
+  if (!error) {
+    hdy_action_row_set_title (HDY_ACTION_ROW (sync_page->sync_firefox_account_row),
+                              ephy_sync_utils_get_sync_user ());
+    gtk_widget_hide (sync_page->sync_page_box);
+    gtk_widget_show (sync_page->sync_firefox_account_box);
+    gtk_widget_show (sync_page->sync_options_box);
+  } else {
+    /* Display the error message and reload the iframe. */
+    sync_sign_in_details_show (sync_page, error->message);
+    webkit_web_view_load_uri (sync_page->fxa_web_view, FXA_IFRAME_URL);
+  }
+}
+
+static void
+sync_message_to_fxa_content (PrefsSyncPage *sync_page,
+                             const char    *web_channel_id,
+                             const char    *command,
+                             const char    *message_id,
+                             JsonObject    *data)
+{
+  JsonNode *node;
+  JsonObject *detail;
+  JsonObject *message;
+  char *detail_str;
+  char *script;
+  const char *type;
+
+  g_assert (EPHY_IS_PREFS_SYNC_PAGE (sync_page));
+  g_assert (web_channel_id);
+  g_assert (command);
+  g_assert (message_id);
+  g_assert (data);
+
+  message = json_object_new ();
+  json_object_set_string_member (message, "command", command);
+  json_object_set_string_member (message, "messageId", message_id);
+  json_object_set_object_member (message, "data", json_object_ref (data));
+  detail = json_object_new ();
+  json_object_set_string_member (detail, "id", web_channel_id);
+  json_object_set_object_member (detail, "message", message);
+  node = json_node_new (JSON_NODE_OBJECT);
+  json_node_set_object (node, detail);
+
+  type = "WebChannelMessageToContent";
+  detail_str = json_to_string (node, FALSE);
+  script = g_strdup_printf ("let e = new window.CustomEvent(\"%s\", {detail: %s});"
+                            "window.dispatchEvent(e);",
+                            type, detail_str);
+
+  /* We don't expect any response from the server. */
+  webkit_web_view_run_javascript (sync_page->fxa_web_view, script, NULL, NULL, NULL);
+
+  g_free (script);
+  g_free (detail_str);
+  json_object_unref (detail);
+  json_node_unref (node);
+}
+
+static gboolean
+sync_parse_message_from_fxa_content (const char  *message,
+                                     char       **web_channel_id,
+                                     char       **message_id,
+                                     char       **command,
+                                     JsonObject **data,
+                                     char       **error_msg)
+{
+  JsonNode *node;
+  JsonObject *object;
+  JsonObject *detail;
+  JsonObject *msg;
+  JsonObject *msg_data = NULL;
+  const char *type;
+  const char *channel_id;
+  const char *cmd;
+  const char *error = NULL;
+  gboolean success = FALSE;
+
+  g_assert (message);
+  g_assert (web_channel_id);
+  g_assert (message_id);
+  g_assert (command);
+  g_assert (data);
+  g_assert (error_msg);
+
+  /* Expected message format is:
+   * {
+   *   type: "WebChannelMessageToChrome",
+   *   detail: {
+   *     id: <id> (string, the id of the WebChannel),
+   *     message: {
+   *       messageId: <messageId> (optional string, the message id),
+   *       command: <command> (string, the message command),
+   *       data: <data> (optional JSON object, the message data)
+   *     }
+   *   }
+   * }
+   */
+
+  node = json_from_string (message, NULL);
+  if (!node) {
+    error = "Message is not a valid JSON";
+    goto out_error;
+  }
+  object = json_node_get_object (node);
+  if (!object) {
+    error = "Message is not a JSON object";
+    goto out_error;
+  }
+  type = json_object_get_string_member (object, "type");
+  if (!type) {
+    error = "Message has missing or invalid 'type' member";
+    goto out_error;
+  } else if (strcmp (type, "WebChannelMessageToChrome")) {
+    error = "Message type is not WebChannelMessageToChrome";
+    goto out_error;
+  }
+  detail = json_object_get_object_member (object, "detail");
+  if (!detail) {
+    error = "Message has missing or invalid 'detail' member";
+    goto out_error;
+  }
+  channel_id = json_object_get_string_member (detail, "id");
+  if (!channel_id) {
+    error = "'Detail' object has missing or invalid 'id' member";
+    goto out_error;
+  }
+  msg = json_object_get_object_member (detail, "message");
+  if (!msg) {
+    error = "'Detail' object has missing or invalid 'message' member";
+    goto out_error;
+  }
+  cmd = json_object_get_string_member (msg, "command");
+  if (!cmd) {
+    error = "'Message' object has missing or invalid 'command' member";
+    goto out_error;
+  }
+
+  *web_channel_id = g_strdup (channel_id);
+  *command = g_strdup (cmd);
+  *message_id = json_object_has_member (msg, "messageId") ?
+                g_strdup (json_object_get_string_member (msg, "messageId")) :
+                NULL;
+  if (json_object_has_member (msg, "data"))
+    msg_data = json_object_get_object_member (msg, "data");
+  *data = msg_data ? json_object_ref (msg_data) : NULL;
+
+  success = TRUE;
+  *error_msg = NULL;
+  goto out_no_error;
+
+out_error:
+  *web_channel_id = NULL;
+  *command = NULL;
+  *message_id = NULL;
+  *error_msg = g_strdup (error);
+
+out_no_error:
+  json_node_unref (node);
+
+  return success;
+}
+
+static void
+sync_message_from_fxa_content_cb (WebKitUserContentManager *manager,
+                                  WebKitJavascriptResult   *result,
+                                  PrefsSyncPage            *sync_page)
+{
+  JsonObject *data = NULL;
+  char *message = NULL;
+  char *web_channel_id = NULL;
+  char *message_id = NULL;
+  char *command = NULL;
+  char *error_msg = NULL;
+  gboolean is_error = FALSE;
+
+  message = jsc_value_to_string (webkit_javascript_result_get_js_value (result));
+  if (!message) {
+    g_warning ("Failed to get JavaScript result as string");
+    is_error = TRUE;
+    goto out;
+  }
+
+  if (!sync_parse_message_from_fxa_content (message, &web_channel_id,
+                                            &message_id, &command,
+                                            &data, &error_msg)) {
+    g_warning ("Failed to parse message from FxA Content Server: %s", error_msg);
+    is_error = TRUE;
+    goto out;
+  }
+
+  LOG ("WebChannelMessageToChrome: received %s command", command);
+
+  if (!g_strcmp0 (command, "fxaccounts:can_link_account")) {
+    /* Confirm a relink. Respond with {ok: true}. */
+    JsonObject *response = json_object_new ();
+    json_object_set_boolean_member (response, "ok", TRUE);
+    sync_message_to_fxa_content (sync_page, web_channel_id, command, message_id, response);
+    json_object_unref (response);
+  } else if (!g_strcmp0 (command, "fxaccounts:login")) {
+    /* Extract sync tokens and pass them to the sync service. */
+    const char *email = json_object_get_string_member (data, "email");
+    const char *uid = json_object_get_string_member (data, "uid");
+    const char *session_token = json_object_get_string_member (data, "sessionToken");
+    const char *key_fetch_token = json_object_get_string_member (data, "keyFetchToken");
+    const char *unwrap_kb = json_object_get_string_member (data, "unwrapBKey");
+
+    if (!email || !uid || !session_token || !key_fetch_token || !unwrap_kb) {
+      g_warning ("Message data has missing or invalid members");
+      is_error = TRUE;
+      goto out;
+    }
+    if (!json_object_has_member (data, "verified") ||
+        !JSON_NODE_HOLDS_VALUE (json_object_get_member (data, "verified"))) {
+      g_warning ("Message data has missing or invalid 'verified' member");
+      is_error = TRUE;
+      goto out;
+    }
+
+    ephy_sync_service_sign_in (ephy_shell_get_sync_service (ephy_shell_get_default ()),
+                               email, uid, session_token, key_fetch_token, unwrap_kb);
+  }
+
+out:
+  if (data)
+    json_object_unref (data);
+  g_free (message);
+  g_free (web_channel_id);
+  g_free (message_id);
+  g_free (command);
+  g_free (error_msg);
+
+  if (is_error) {
+    sync_sign_in_details_show (sync_page, _("Something went wrong, please try again later."));
+    webkit_web_view_load_uri (sync_page->fxa_web_view, FXA_IFRAME_URL);
+  }
+}
+
+static void
+sync_open_webmail_clicked_cb (WebKitUserContentManager *manager,
+                              WebKitJavascriptResult   *result,
+                              PrefsSyncPage            *sync_page)
+{
+  EphyShell *shell;
+  EphyEmbed *embed;
+  GtkWindow *window;
+  GtkWidget *prefs_dialog;
+  char *url;
+
+  url = jsc_value_to_string (webkit_javascript_result_get_js_value (result));
+  if (url) {
+    /* Open a new tab to the webmail URL. */
+    shell = ephy_shell_get_default ();
+    window = gtk_application_get_active_window (GTK_APPLICATION (shell));
+    embed = ephy_shell_new_tab (shell, EPHY_WINDOW (window),
+                                NULL, EPHY_NEW_TAB_JUMP);
+    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));
+
+    g_free (url);
+  }
+}
+
+static void
+sync_setup_firefox_iframe (PrefsSyncPage *sync_page)
+{
+  EphyEmbedShell *shell;
+  WebKitWebsiteDataManager *manager;
+  WebKitWebContext *embed_context;
+  WebKitWebContext *sync_context;
+  GtkWidget *frame;
+  const char *script;
+
+  if (!sync_page->fxa_web_view) {
+    script =
+      /* Handle sign-in messages from the FxA content server. */
+      "function handleToChromeMessage(event) {"
+      "  let e = JSON.stringify({type: event.type, detail: event.detail});"
+      "  window.webkit.messageHandlers.toChromeMessageHandler.postMessage(e);"
+      "};"
+      "window.addEventListener('WebChannelMessageToChrome', handleToChromeMessage);"
+      /* Handle open-webmail click event. */
+      "function handleOpenWebmailClick(event) {"
+      "  if (event.target.id == 'open-webmail' && event.target.hasAttribute('href'))"
+      "    
window.webkit.messageHandlers.openWebmailClickHandler.postMessage(event.target.getAttribute('href'));"
+      "};"
+      "var stage = document.getElementById('stage');"
+      "if (stage)"
+      "  stage.addEventListener('click', handleOpenWebmailClick);";
+
+    sync_page->fxa_script = webkit_user_script_new (script,
+                                                    WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
+                                                    WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END,
+                                                    NULL, NULL);
+    sync_page->fxa_manager = webkit_user_content_manager_new ();
+    webkit_user_content_manager_add_script (sync_page->fxa_manager, sync_page->fxa_script);
+    g_signal_connect (sync_page->fxa_manager,
+                      "script-message-received::toChromeMessageHandler",
+                      G_CALLBACK (sync_message_from_fxa_content_cb),
+                      sync_page);
+    g_signal_connect (sync_page->fxa_manager,
+                      "script-message-received::openWebmailClickHandler",
+                      G_CALLBACK (sync_open_webmail_clicked_cb),
+                      sync_page);
+    webkit_user_content_manager_register_script_message_handler (sync_page->fxa_manager,
+                                                                 "toChromeMessageHandler");
+    webkit_user_content_manager_register_script_message_handler (sync_page->fxa_manager,
+                                                                 "openWebmailClickHandler");
+
+    shell = ephy_embed_shell_get_default ();
+    embed_context = ephy_embed_shell_get_web_context (shell);
+    manager = webkit_web_context_get_website_data_manager (embed_context);
+    sync_context = webkit_web_context_new_with_website_data_manager (manager);
+    webkit_web_context_set_preferred_languages (sync_context,
+                                                g_object_get_data (G_OBJECT (embed_context), 
"preferred-languages"));
+
+    sync_page->fxa_web_view = WEBKIT_WEB_VIEW (g_object_new (WEBKIT_TYPE_WEB_VIEW,
+                                                             "user-content-manager", sync_page->fxa_manager,
+                                                             "settings", ephy_embed_prefs_get_settings (),
+                                                             "web-context", sync_context,
+                                                             NULL));
+    gtk_widget_set_vexpand (GTK_WIDGET (sync_page->fxa_web_view), TRUE);
+    gtk_widget_set_visible (GTK_WIDGET (sync_page->fxa_web_view), TRUE);
+    frame = gtk_frame_new (NULL);
+    gtk_widget_set_visible (frame, TRUE);
+    gtk_container_add (GTK_CONTAINER (frame),
+                       GTK_WIDGET (sync_page->fxa_web_view));
+    gtk_box_pack_start (GTK_BOX (sync_page->sync_firefox_iframe_box),
+                        frame,
+                        TRUE, TRUE, 0);
+
+    g_object_unref (sync_context);
+  }
+
+  webkit_web_view_load_uri (sync_page->fxa_web_view, FXA_IFRAME_URL);
+  gtk_widget_set_visible (sync_page->sync_firefox_iframe_label, FALSE);
+}
+
+static void
+on_sync_sign_out_button_clicked (GtkWidget     *button,
+                                 PrefsSyncPage *sync_page)
+{
+  EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+
+  ephy_sync_service_sign_out (service);
+
+  /* Show Firefox Accounts iframe. */
+  sync_setup_firefox_iframe (sync_page);
+  gtk_widget_hide (sync_page->sync_firefox_account_box);
+  gtk_widget_hide (sync_page->sync_options_box);
+  gtk_widget_show (sync_page->sync_page_box);
+  hdy_action_row_set_subtitle (HDY_ACTION_ROW (sync_page->sync_firefox_account_row), NULL);
+}
+
+static void
+on_sync_sync_now_button_clicked (GtkWidget     *button,
+                                 PrefsSyncPage *sync_page)
+{
+  EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+
+  gtk_widget_set_sensitive (button, FALSE);
+  ephy_sync_service_sync (service);
+}
+
+static void
+on_sync_synced_tabs_button_clicked (GtkWidget     *button,
+                                    PrefsSyncPage *sync_page)
+{
+  EphyOpenTabsManager *manager;
+  SyncedTabsDialog *synced_tabs_dialog;
+
+  manager = ephy_shell_get_open_tabs_manager (ephy_shell_get_default ());
+  synced_tabs_dialog = synced_tabs_dialog_new (manager);
+  gtk_window_set_transient_for (GTK_WINDOW (synced_tabs_dialog), GTK_WINDOW (sync_page));
+  gtk_window_set_modal (GTK_WINDOW (synced_tabs_dialog), TRUE);
+  gtk_window_present_with_time (GTK_WINDOW (synced_tabs_dialog), gtk_get_current_event_time ());
+}
+
+static void
+on_sync_device_name_change_button_clicked (GtkWidget     *button,
+                                           PrefsSyncPage *sync_page)
+{
+  gtk_widget_set_sensitive (GTK_WIDGET (sync_page->sync_device_name_entry), TRUE);
+  gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_change_button), FALSE);
+  gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_save_button), TRUE);
+  gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_cancel_button), TRUE);
+}
+
+static void
+on_sync_device_name_save_button_clicked (GtkWidget     *button,
+                                         PrefsSyncPage *sync_page)
+{
+  EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+  const char *text;
+
+  text = gtk_entry_get_text (GTK_ENTRY (sync_page->sync_device_name_entry));
+  if (!g_strcmp0 (text, "")) {
+    char *name = ephy_sync_utils_get_device_name ();
+    gtk_entry_set_text (GTK_ENTRY (sync_page->sync_device_name_entry), name);
+    g_free (name);
+  } else {
+    ephy_sync_service_update_device_name (service, text);
+  }
+
+  gtk_widget_set_sensitive (GTK_WIDGET (sync_page->sync_device_name_entry), FALSE);
+  gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_change_button), TRUE);
+  gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_save_button), FALSE);
+  gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_cancel_button), FALSE);
+}
+
+static void
+on_sync_device_name_cancel_button_clicked (GtkWidget     *button,
+                                           PrefsSyncPage *sync_page)
+{
+  char *name;
+
+  name = ephy_sync_utils_get_device_name ();
+  gtk_entry_set_text (GTK_ENTRY (sync_page->sync_device_name_entry), name);
+
+  gtk_widget_set_sensitive (GTK_WIDGET (sync_page->sync_device_name_entry), FALSE);
+  gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_change_button), TRUE);
+  gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_save_button), FALSE);
+  gtk_widget_set_visible (GTK_WIDGET (sync_page->sync_device_name_cancel_button), FALSE);
+
+  g_free (name);
+}
+
+static void
+prefs_sync_page_finalize (GObject *object)
+{
+  PrefsSyncPage *sync_page = EPHY_PREFS_SYNC_PAGE (object);
+
+  if (sync_page->fxa_web_view != NULL) {
+    webkit_user_content_manager_unregister_script_message_handler (sync_page->fxa_manager,
+                                                                   "toChromeMessageHandler");
+    webkit_user_content_manager_unregister_script_message_handler (sync_page->fxa_manager,
+                                                                   "openWebmailClickHandler");
+    webkit_user_script_unref (sync_page->fxa_script);
+    g_object_unref (sync_page->fxa_manager);
+  }
+
+  G_OBJECT_CLASS (prefs_sync_page_parent_class)->finalize (object);
+}
+
+static void
+prefs_sync_page_class_init (PrefsSyncPageClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/epiphany/gtk/prefs-sync-page.ui");
+
+  object_class->finalize = prefs_sync_page_finalize;
+
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_page_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_firefox_iframe_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_firefox_iframe_label);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_firefox_account_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_firefox_account_row);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_options_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_bookmarks_checkbutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_passwords_checkbutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_history_checkbutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_open_tabs_checkbutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_frequency_row);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_now_button);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, synced_tabs_button);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_device_name_entry);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_device_name_change_button);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_device_name_save_button);
+  gtk_widget_class_bind_template_child (widget_class, PrefsSyncPage, sync_device_name_cancel_button);
+
+  gtk_widget_class_bind_template_callback (widget_class, on_sync_sign_out_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_sync_sync_now_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_sync_synced_tabs_button_clicked);
+  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);
+}
+
+static gboolean
+sync_frequency_get_mapping (GValue   *value,
+                            GVariant *variant,
+                            gpointer  user_data)
+{
+  uint minutes = g_variant_get_uint32 (variant);
+
+  for (gint i = 0; i < (gint)G_N_ELEMENTS (sync_frequency_minutes); i++) {
+    if (sync_frequency_minutes[i] != minutes)
+      continue;
+
+    g_value_set_int (value, i);
+
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static GVariant *
+sync_frequency_set_mapping (const GValue       *value,
+                            const GVariantType *expected_type,
+                            gpointer            user_data)
+{
+  gint i = g_value_get_int (value);
+
+  if (i >= (gint)G_N_ELEMENTS (sync_frequency_minutes))
+    return NULL;
+
+  return g_variant_new_uint32 (sync_frequency_minutes[i]);
+}
+
+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;
+  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);
+  }
+
+  return G_LIST_MODEL (list_store);
+}
+
+static gchar *
+get_sync_frequency_minutes_name (HdyValueObject *value)
+{
+  return g_strdup_printf ("%u min", g_value_get_uint (hdy_value_object_get_value (value)));
+}
+
+void
+prefs_sync_page_setup (PrefsSyncPage *sync_page)
+{
+  EphySyncService *service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+  GSettings *sync_settings = ephy_settings_get (EPHY_PREFS_SYNC_SCHEMA);
+  char *user = ephy_sync_utils_get_sync_user ();
+  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_page->sync_device_name_entry), name);
+
+  if (!user) {
+    sync_setup_firefox_iframe (sync_page);
+    gtk_widget_hide (sync_page->sync_firefox_account_box);
+    gtk_widget_hide (sync_page->sync_options_box);
+  } else {
+    sync_set_last_sync_time (sync_page);
+    hdy_action_row_set_title (HDY_ACTION_ROW (sync_page->sync_firefox_account_row), user);
+    gtk_widget_hide (sync_page->sync_page_box);
+  }
+
+  g_settings_bind (sync_settings,
+                   EPHY_PREFS_SYNC_BOOKMARKS_ENABLED,
+                   sync_page->sync_bookmarks_checkbutton,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+  g_settings_bind (sync_settings,
+                   EPHY_PREFS_SYNC_PASSWORDS_ENABLED,
+                   sync_page->sync_passwords_checkbutton,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+  g_settings_bind (sync_settings,
+                   EPHY_PREFS_SYNC_HISTORY_ENABLED,
+                   sync_page->sync_history_checkbutton,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+  g_settings_bind (sync_settings,
+                   EPHY_PREFS_SYNC_OPEN_TABS_ENABLED,
+                   sync_page->sync_open_tabs_checkbutton,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+
+  hdy_combo_row_bind_name_model (HDY_COMBO_ROW (sync_page->sync_frequency_row),
+                                 sync_frequency_minutes_model,
+                                 (HdyComboRowGetNameFunc)get_sync_frequency_minutes_name,
+                                 NULL,
+                                 NULL);
+  g_settings_bind_with_mapping (sync_settings,
+                                EPHY_PREFS_SYNC_FREQUENCY,
+                                sync_page->sync_frequency_row,
+                                "selected-index",
+                                G_SETTINGS_BIND_DEFAULT,
+                                sync_frequency_get_mapping,
+                                sync_frequency_set_mapping,
+                                NULL, NULL);
+
+  g_object_bind_property (sync_page->sync_open_tabs_checkbutton, "active",
+                          sync_page->synced_tabs_button, "sensitive",
+                          G_BINDING_SYNC_CREATE);
+
+  g_signal_connect_object (service, "sync-secrets-store-finished",
+                           G_CALLBACK (sync_secrets_store_finished_cb),
+                           sync_page, 0);
+  g_signal_connect_object (service, "sync-sign-in-error",
+                           G_CALLBACK (sync_sign_in_error_cb),
+                           sync_page, 0);
+  g_signal_connect_object (service, "sync-finished",
+                           G_CALLBACK (sync_finished_cb),
+                           sync_page, 0);
+  g_signal_connect_object (sync_page->sync_bookmarks_checkbutton, "toggled",
+                           G_CALLBACK (sync_collection_toggled_cb),
+                           sync_page, 0);
+  g_signal_connect_object (sync_page->sync_passwords_checkbutton, "toggled",
+                           G_CALLBACK (sync_collection_toggled_cb),
+                           sync_page, 0);
+  g_signal_connect_object (sync_page->sync_history_checkbutton, "toggled",
+                           G_CALLBACK (sync_collection_toggled_cb),
+                           sync_page, 0);
+  g_signal_connect_object (sync_page->sync_open_tabs_checkbutton, "toggled",
+                           G_CALLBACK (sync_collection_toggled_cb),
+                           sync_page, 0);
+
+  g_free (user);
+  g_free (name);
+}
+
+static void
+prefs_sync_page_init (PrefsSyncPage *sync_page)
+{
+  gtk_widget_init_template (GTK_WIDGET (sync_page));
+}
diff --git a/src/preferences/prefs-sync-page.h b/src/preferences/prefs-sync-page.h
new file mode 100644
index 000000000..b7c5c2846
--- /dev/null
+++ b/src/preferences/prefs-sync-page.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  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 <glib-object.h>
+
+#define HANDY_USE_UNSTABLE_API
+#include <handy.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_PREFS_SYNC_PAGE (prefs_sync_page_get_type ())
+
+G_DECLARE_FINAL_TYPE (PrefsSyncPage, prefs_sync_page, EPHY, PREFS_SYNC_PAGE, HdyPreferencesPage)
+
+void prefs_sync_page_setup (PrefsSyncPage *sync_page);
+
+G_END_DECLS
diff --git a/src/synced-tabs-dialog.c b/src/preferences/synced-tabs-dialog.c
similarity index 100%
rename from src/synced-tabs-dialog.c
rename to src/preferences/synced-tabs-dialog.c
diff --git a/src/synced-tabs-dialog.h b/src/preferences/synced-tabs-dialog.h
similarity index 100%
rename from src/synced-tabs-dialog.h
rename to src/preferences/synced-tabs-dialog.h
diff --git a/src/webapp-additional-urls-dialog.c b/src/preferences/webapp-additional-urls-dialog.c
similarity index 100%
rename from src/webapp-additional-urls-dialog.c
rename to src/preferences/webapp-additional-urls-dialog.c
diff --git a/src/webapp-additional-urls-dialog.h b/src/preferences/webapp-additional-urls-dialog.h
similarity index 100%
rename from src/webapp-additional-urls-dialog.h
rename to src/preferences/webapp-additional-urls-dialog.h
diff --git a/src/resources/epiphany.gresource.xml b/src/resources/epiphany.gresource.xml
index 12da60dd5..de5e2b5dc 100644
--- a/src/resources/epiphany.gresource.xml
+++ b/src/resources/epiphany.gresource.xml
@@ -31,8 +31,12 @@
     <file preprocess="xml-stripblanks" compressed="true">gtk/pages-popover.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/pages-view.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/passwords-dialog.ui</file>
+    <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-appearance-page.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-dialog.ui</file>
+    <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-general-page.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-lang-dialog.ui</file>
+    <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-privacy-page.ui</file>
+    <file preprocess="xml-stripblanks" compressed="true">gtk/prefs-sync-page.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/search-engine-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/synced-tabs-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/shortcuts-dialog.ui</file>
diff --git a/src/resources/gtk/prefs-appearance-page.ui b/src/resources/gtk/prefs-appearance-page.ui
new file mode 100644
index 000000000..d4b57d1d8
--- /dev/null
+++ b/src/resources/gtk/prefs-appearance-page.ui
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <template class="PrefsAppearancePage" parent="HdyPreferencesPage">
+    <property name="icon_name">document-edit-symbolic</property>
+    <property name="title" translatable="yes">Appearance</property>
+    <property name="visible">True</property>
+    <style>
+      <class name="background"/>
+    </style>
+    <child>
+      <object class="HdyPreferencesGroup">
+        <property name="title" translatable="yes">Fonts</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyExpanderRow" 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="GtkSeparator">
+                <property name="margin-start">24</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBox" id="use_custom_fonts_list">
+                <property name="margin-start">24</property>
+                <property name="selection_mode">none</property>
+                <property name="visible">True</property>
+                <child>
+                  <object class="HdyActionRow">
+                    <property name="activatable">False</property>
+                    <property name="title" translatable="yes">Sans serif font</property>
+                    <property name="visible">True</property>
+                    <child type="action">
+                      <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">
+                    <property name="activatable">False</property>
+                    <property name="title" translatable="yes">Serif font</property>
+                    <property name="visible">True</property>
+                    <child type="action">
+                      <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">
+                    <property name="activatable">False</property>
+                    <property name="title" translatable="yes">Monospace font</property>
+                    <property name="visible">True</property>
+                    <child type="action">
+                      <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>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup" 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">
+            <property name="title" translatable="yes">Font Style</property>
+            <property name="visible">True</property>
+          </object>
+        </child>
+        <child>
+          <object class="HdyComboRow" id="reader_mode_color_scheme">
+            <property name="title" translatable="yes">Color Scheme</property>
+            <property name="visible">True</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">3</property>
+      </packing>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup">
+        <property name="title" translatable="yes">Style</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">css_switch</property>
+            <property name="title" translatable="yes">Use Custom Stylesheet</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <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>
+              </object>
+            </child>
+            <child type="action">
+              <object class="GtkSeparator">
+                <property name="margin_bottom">8</property>
+                <property name="margin_top">8</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+            <child type="action">
+              <object class="GtkSwitch" id="css_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">js_switch</property>
+            <property name="title" translatable="yes">Use Custom JavaScript</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <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>
+              </object>
+            </child>
+            <child type="action">
+              <object class="GtkSeparator">
+                <property name="margin_bottom">8</property>
+                <property name="margin_top">8</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+            <child type="action">
+              <object class="GtkSwitch" id="js_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable">False</property>
+            <property name="title" translatable="yes">Default zoom level</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <object class="GtkSpinButton" id="default_zoom_spin_button">
+                <property name="adjustment">zoom_adjustment</property>
+                <property name="can_focus">True</property>
+                <property name="input_purpose">number</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>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkSizeGroup">
+    <property name="mode">horizontal</property>
+    <widgets>
+      <widget name="sans_fontbutton"/>
+      <widget name="serif_fontbutton"/>
+      <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 fbfeea744..09f50ad2a 100644
--- a/src/resources/gtk/prefs-dialog.ui
+++ b/src/resources/gtk/prefs-dialog.ui
@@ -28,338 +28,7 @@
             <property name="scrollable">True</property>
             <property name="show_border">False</property>
             <child>
-              <object class="HdyPreferencesPage">
-                <property name="icon_name">applications-system-symbolic</property>
-                <property name="title" translatable="yes">General</property>
-                <property name="visible">True</property>
-                <style>
-                  <class name="background"/>
-                </style>
-                <child>
-                  <object class="HdyPreferencesGroup" id="webapp_box">
-                    <property name="title" translatable="yes">Web Application</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyPreferencesRow">
-                        <property name="activatable">False</property>
-                        <property name="visible">True</property>
-                        <child>
-                          <object class="GtkGrid">
-                            <property name="column-spacing">12</property>
-                            <property name="margin-bottom">8</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-top">8</property>
-                            <property name="row-spacing">6</property>
-                            <property name="visible">True</property>
-                            <child>
-                              <object class="GtkButton" id="webapp_icon_button">
-                                <property name="visible">true</property>
-                                <property name="halign">center</property>
-                                <signal name="clicked" handler="on_webapp_icon_button_clicked"/>
-                                <child>
-                                  <object class="GtkImage" id="webapp_icon">
-                                    <property name="visible">True</property>
-                                  </object>
-                                </child>
-                              </object>
-                              <packing>
-                                <property name="left-attach">0</property>
-                                <property name="top-attach">0</property>
-                                <property name="height">2</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkLabel">
-                                <property name="visible">True</property>
-                                <property name="halign">start</property>
-                                <property name="label" translatable="yes">Homepage:</property>
-                              </object>
-                              <packing>
-                                <property name="left-attach">1</property>
-                                <property name="top-attach">0</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkEntry" id="webapp_url">
-                                <property name="visible">True</property>
-                                <property name="hexpand">True</property>
-                                <signal name="changed" handler="on_webapp_entry_changed"/>
-                              </object>
-                              <packing>
-                                <property name="left-attach">2</property>
-                                <property name="top-attach">0</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkLabel">
-                                <property name="visible">True</property>
-                                <property name="halign">start</property>
-                                <property name="label" translatable="yes">Title:</property>
-                              </object>
-                              <packing>
-                                <property name="left-attach">1</property>
-                                <property name="top-attach">1</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <object class="GtkEntry" id="webapp_title">
-                                <property name="visible">True</property>
-                                <property name="hexpand">True</property>
-                                <signal name="changed" handler="on_webapp_entry_changed"/>
-                              </object>
-                              <packing>
-                                <property name="left-attach">2</property>
-                                <property name="top-attach">1</property>
-                              </packing>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <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 type="action">
-                          <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"/>
-                            </style>
-                            <child>
-                              <object class="GtkImage">
-                                <property name="icon_name">emblem-system-symbolic</property>
-                                <property name="visible">True</property>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup" id="homepage_box">
-                    <property name="title" translatable="yes">Homepage</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <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">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <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">
-                            <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">
-                        <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">
-                            <property name="valign">center</property>
-                            <property name="group">new_tab_homepage_radiobutton</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                        <child type="action">
-                          <object class="GtkEntry" id="custom_homepage_entry">
-                            <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>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup" id="download_box">
-                    <property name="title" translatable="yes">Downloads</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <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 type="action">
-                          <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">
-                        <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>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup" id="search_box">
-                    <property name="title" translatable="yes">Search Engines</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable">False</property>
-                        <property name="title" translatable="yes">_Manage Search Engines</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <object class="GtkButton" id="search_engine_dialog_button">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                            <signal name="clicked" handler="on_search_engine_dialog_button_clicked"/>
-                            <style>
-                              <class name="image-button"/>
-                            </style>
-                            <child>
-                              <object class="GtkImage">
-                                <property name="icon_name">emblem-system-symbolic</property>
-                                <property name="visible">True</property>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup" id="session_box">
-                    <property name="title" translatable="yes">Session</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <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 type="action">
-                          <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">
-                        <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 type="action">
-                          <object class="GtkSwitch" id="restore_session_switch">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup" id="browsing_box">
-                    <property name="title" translatable="yes">Browsing</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">enable_smooth_scrolling_switch</property>
-                        <property name="title" translatable="yes">Sm_ooth Scrolling</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <object class="GtkSwitch" id="enable_smooth_scrolling_switch">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <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 type="action">
-                          <object class="GtkSwitch" id="enable_mouse_gesture_switch">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup" 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>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup">
-                    <property name="title" translatable="yes"></property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <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 type="action">
-                          <object class="GtkSwitch" id="enable_spell_checking_switch">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
+              <object class="PrefsGeneralPage" id="general_page">
               </object>
             </child>
             <child type="tab">
@@ -372,210 +41,7 @@
               </packing>
             </child>
             <child>
-              <object class="HdyPreferencesPage">
-                <property name="icon_name">preferences-system-privacy-symbolic</property>
-                <property name="title" translatable="yes">Privacy</property>
-                <property name="visible">True</property>
-                <style>
-                  <class name="background"/>
-                </style>
-                <child>
-                  <object class="HdyPreferencesGroup">
-                    <property name="title" translatable="yes">Web Content</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow" id="adblock_allow_row">
-                        <property name="activatable_widget">adblock_allow_switch</property>
-                        <property name="title" translatable="yes">Try to Block _Advertisements</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <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">
-                        <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 type="action">
-                          <object class="GtkSwitch" id="popups_allow_switch">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">enable_safe_browsing_switch</property>
-                        <property name="title" translatable="yes">Try to Block Dangerous Web_sites</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <object class="GtkSwitch" id="enable_safe_browsing_switch">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup">
-                    <property name="title" translatable="yes">Cookies</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable">False</property>
-                        <property name="title" translatable="yes">Clear _Cookies</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <object class="GtkButton">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                            <signal name="clicked" handler="on_manage_cookies_button_clicked"/>
-                            <style>
-                              <class name="image-button"/>
-                            </style>
-                            <child>
-                              <object class="GtkImage">
-                                <property name="icon_name">emblem-system-symbolic</property>
-                                <property name="visible">True</property>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">always</property>
-                        <property name="title" translatable="yes">_Always accept</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="prefix">
-                          <object class="GtkRadioButton" id="always">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">no_third_party</property>
-                        <property name="subtitle" translatable="yes" comments="Refers to &quot;Only from 
sites you visit&quot; option under Cookies.">For example, not from advertisers on these sites.</property>
-                        <property name="title" translatable="yes">Only _from sites you visit</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="prefix">
-                          <object class="GtkRadioButton" id="no_third_party">
-                            <property name="valign">center</property>
-                            <property name="group">always</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">never</property>
-                        <property name="title" translatable="yes">_Never accept</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="prefix">
-                          <object class="GtkRadioButton" id="never">
-                            <property name="valign">center</property>
-                            <property name="group">always</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup">
-                    <property name="title" translatable="yes">Passwords</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable">False</property>
-                        <property name="title" translatable="yes">_Passwords</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <object class="GtkButton">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                            <signal name="clicked" handler="on_manage_passwords_button_clicked"/>
-                            <style>
-                              <class name="image-button"/>
-                            </style>
-                            <child>
-                              <object class="GtkImage">
-                                <property name="icon_name">emblem-system-symbolic</property>
-                                <property name="visible">True</property>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <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 type="action">
-                          <object class="GtkSwitch" id="remember_passwords_switch">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup">
-                    <property name="title" translatable="yes">Personal Data</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable">False</property>
-                        <property name="subtitle" translatable="yes">You can clear stored personal 
data.</property>
-                        <property name="title" translatable="yes">Clear Personal _Data</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <object class="GtkButton" id="clear_personal_data_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">emblem-system-symbolic</property>
-                                <property name="visible">True</property>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
+              <object class="PrefsPrivacyPage">
               </object>
               <packing>
                 <property name="position">1</property>
@@ -592,203 +58,7 @@
               </packing>
             </child>
             <child>
-              <object class="HdyPreferencesPage">
-                <property name="icon_name">document-edit-symbolic</property>
-                <property name="title" translatable="yes">Appearance</property>
-                <property name="visible">True</property>
-                <style>
-                  <class name="background"/>
-                </style>
-                <child>
-                  <object class="HdyPreferencesGroup">
-                    <property name="title" translatable="yes">Fonts</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyExpanderRow" 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="GtkSeparator">
-                            <property name="margin-start">24</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkListBox" id="use_custom_fonts_list">
-                            <property name="margin-start">24</property>
-                            <property name="selection_mode">none</property>
-                            <property name="visible">True</property>
-                            <child>
-                              <object class="HdyActionRow">
-                                <property name="activatable">False</property>
-                                <property name="title" translatable="yes">Sans serif font</property>
-                                <property name="visible">True</property>
-                                <child type="action">
-                                  <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">
-                                <property name="activatable">False</property>
-                                <property name="title" translatable="yes">Serif font</property>
-                                <property name="visible">True</property>
-                                <child type="action">
-                                  <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">
-                                <property name="activatable">False</property>
-                                <property name="title" translatable="yes">Monospace font</property>
-                                <property name="visible">True</property>
-                                <child type="action">
-                                  <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>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup" 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">
-                        <property name="title" translatable="yes">Font Style</property>
-                        <property name="visible">True</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyComboRow" id="reader_mode_color_scheme">
-                        <property name="title" translatable="yes">Color Scheme</property>
-                        <property name="visible">True</property>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">3</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup">
-                    <property name="title" translatable="yes">Style</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">css_switch</property>
-                        <property name="title" translatable="yes">Use Custom Stylesheet</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <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>
-                          </object>
-                        </child>
-                        <child type="action">
-                          <object class="GtkSeparator">
-                            <property name="margin_bottom">8</property>
-                            <property name="margin_top">8</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                        <child type="action">
-                          <object class="GtkSwitch" id="css_switch">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">js_switch</property>
-                        <property name="title" translatable="yes">Use Custom JavaScript</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <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>
-                          </object>
-                        </child>
-                        <child type="action">
-                          <object class="GtkSeparator">
-                            <property name="margin_bottom">8</property>
-                            <property name="margin_top">8</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                        <child type="action">
-                          <object class="GtkSwitch" id="js_switch">
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable">False</property>
-                        <property name="title" translatable="yes">Default zoom level</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <object class="GtkSpinButton" id="default_zoom_spin_button">
-                            <property name="adjustment">zoom_adjustment</property>
-                            <property name="can_focus">True</property>
-                            <property name="input_purpose">number</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>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
+              <object class="PrefsAppearancePage">
               </object>
               <packing>
                 <property name="position">2</property>
@@ -805,197 +75,7 @@
               </packing>
             </child>
             <child>
-              <object class="HdyPreferencesPage" id="sync_page">
-                <property name="icon_name">emblem-synchronizing-symbolic</property>
-                <property name="title" translatable="yes">Sync</property>
-                <property name="visible">True</property>
-                <style>
-                  <class name="background"/>
-                </style>
-                <child>
-                  <object class="HdyPreferencesGroup" id="sync_page_box">
-                    <property name="description" translatable="yes">Sign in with your Firefox account to 
sync your data with Web and Firefox on other computers. Web is not Firefox and is not produced or endorsed by 
Mozilla.</property>
-                    <property name="title" translatable="yes">Firefox Sync</property>
-                    <property name="visible">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="halign">start</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup" id="sync_firefox_account_box">
-                    <property name="title" translatable="yes">Firefox Account</property>
-                    <property name="visible">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 type="action">
-                          <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>
-                    </child>
-                  </object>
-                </child>
-                <child>
-                  <object class="HdyPreferencesGroup" id="sync_options_box">
-                    <property name="title" translatable="yes">Sync Options</property>
-                    <property name="visible">True</property>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">sync_bookmarks_checkbutton</property>
-                        <property name="title" translatable="yes">Sync _Bookmarks</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="prefix">
-                          <object class="GtkCheckButton" id="sync_bookmarks_checkbutton">
-                            <property name="use-underline">True</property>
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">sync_passwords_checkbutton</property>
-                        <property name="title" translatable="yes">Sync _Passwords</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="prefix">
-                          <object class="GtkCheckButton" id="sync_passwords_checkbutton">
-                            <property name="use-underline">True</property>
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">sync_history_checkbutton</property>
-                        <property name="title" translatable="yes">Sync _History</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="prefix">
-                          <object class="GtkCheckButton" id="sync_history_checkbutton">
-                            <property name="use-underline">True</property>
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="HdyActionRow">
-                        <property name="activatable_widget">sync_open_tabs_checkbutton</property>
-                        <property name="title" translatable="yes">Sync Open _Tabs</property>
-                        <property name="use_underline">True</property>
-                        <property name="visible">True</property>
-                        <child type="prefix">
-                          <object class="GtkCheckButton" id="sync_open_tabs_checkbutton">
-                            <property name="use-underline">True</property>
-                            <property name="valign">center</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                        <child type="action">
-                          <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>
-                    </child>
-                    <child>
-                      <object class="HdyComboRow" id="sync_frequency_row">
-                        <property name="title" translatable="yes">Frequency</property>
-                        <property name="visible">True</property>
-                        <child type="action">
-                          <object class="GtkSeparator">
-                            <property name="margin_bottom">8</property>
-                            <property name="margin_top">8</property>
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                        <child type="action">
-                          <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>
-                    </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 type="action">
-                          <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 type="action">
-                          <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 type="action">
-                          <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="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>
-                    </child>
-                  </object>
-                </child>
+              <object class="PrefsSyncPage" id="sync_page">
               </object>
             </child>
             <child type="tab">
@@ -1015,27 +95,4 @@
       </object>
     </child>
   </template>
-  <object class="GtkSizeGroup">
-    <property name="mode">horizontal</property>
-    <widgets>
-      <widget name="sans_fontbutton"/>
-      <widget name="serif_fontbutton"/>
-      <widget name="mono_fontbutton"/>
-    </widgets>
-  </object>
-  <object class="GtkSizeGroup">
-    <property name="mode">horizontal</property>
-    <widgets>
-      <widget name="sync_sign_out_button"/>
-      <widget name="synced_tabs_button"/>
-      <widget name="sync_now_button"/>
-      <widget name="sync_device_name_change_button"/>
-    </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-general-page.ui b/src/resources/gtk/prefs-general-page.ui
new file mode 100644
index 000000000..e13489aab
--- /dev/null
+++ b/src/resources/gtk/prefs-general-page.ui
@@ -0,0 +1,337 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <template class="PrefsGeneralPage" parent="HdyPreferencesPage">
+    <property name="icon_name">applications-system-symbolic</property>
+    <property name="title" translatable="yes">General</property>
+    <property name="visible">True</property>
+    <style>
+      <class name="background"/>
+    </style>
+    <child>
+      <object class="HdyPreferencesGroup" id="webapp_box">
+        <property name="title" translatable="yes">Web Application</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyPreferencesRow">
+            <property name="activatable">False</property>
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkGrid">
+                <property name="column-spacing">12</property>
+                <property name="margin-bottom">8</property>
+                <property name="margin-end">12</property>
+                <property name="margin-start">12</property>
+                <property name="margin-top">8</property>
+                <property name="row-spacing">6</property>
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkButton" id="webapp_icon_button">
+                    <property name="visible">true</property>
+                    <property name="halign">center</property>
+                    <signal name="clicked" handler="on_webapp_icon_button_clicked"/>
+                    <child>
+                      <object class="GtkImage" id="webapp_icon">
+                        <property name="visible">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="left-attach">0</property>
+                    <property name="top-attach">0</property>
+                    <property name="height">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="halign">start</property>
+                    <property name="label" translatable="yes">Homepage:</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="webapp_url">
+                    <property name="visible">True</property>
+                    <property name="hexpand">True</property>
+                    <signal name="changed" handler="on_webapp_entry_changed"/>
+                  </object>
+                  <packing>
+                    <property name="left-attach">2</property>
+                    <property name="top-attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="halign">start</property>
+                    <property name="label" translatable="yes">Title:</property>
+                  </object>
+                  <packing>
+                    <property name="left-attach">1</property>
+                    <property name="top-attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="webapp_title">
+                    <property name="visible">True</property>
+                    <property name="hexpand">True</property>
+                    <signal name="changed" handler="on_webapp_entry_changed"/>
+                  </object>
+                  <packing>
+                    <property name="left-attach">2</property>
+                    <property name="top-attach">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <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 type="action">
+              <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"/>
+                </style>
+                <child>
+                  <object class="GtkImage">
+                    <property name="icon_name">emblem-system-symbolic</property>
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup" id="homepage_box">
+        <property name="title" translatable="yes">Homepage</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <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">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <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">
+                <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">
+            <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">
+                <property name="valign">center</property>
+                <property name="group">new_tab_homepage_radiobutton</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+            <child type="action">
+              <object class="GtkEntry" id="custom_homepage_entry">
+                <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>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup" id="download_box">
+        <property name="title" translatable="yes">Downloads</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <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 type="action">
+              <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">
+            <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>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup" id="search_box">
+        <property name="title" translatable="yes">Search Engines</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable">False</property>
+            <property name="title" translatable="yes">_Manage Search Engines</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <object class="GtkButton" id="search_engine_dialog_button">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+                <signal name="clicked" handler="on_search_engine_dialog_button_clicked"/>
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child>
+                  <object class="GtkImage">
+                    <property name="icon_name">emblem-system-symbolic</property>
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup" id="session_box">
+        <property name="title" translatable="yes">Session</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <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 type="action">
+              <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">
+            <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 type="action">
+              <object class="GtkSwitch" id="restore_session_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup" id="browsing_box">
+        <property name="title" translatable="yes">Browsing</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">enable_smooth_scrolling_switch</property>
+            <property name="title" translatable="yes">Sm_ooth Scrolling</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <object class="GtkSwitch" id="enable_smooth_scrolling_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <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 type="action">
+              <object class="GtkSwitch" id="enable_mouse_gesture_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup" 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>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup">
+        <property name="title" translatable="yes"></property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <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 type="action">
+              <object class="GtkSwitch" id="enable_spell_checking_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/resources/gtk/prefs-privacy-page.ui b/src/resources/gtk/prefs-privacy-page.ui
new file mode 100644
index 000000000..c3b5d5107
--- /dev/null
+++ b/src/resources/gtk/prefs-privacy-page.ui
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <template class="PrefsPrivacyPage" parent="HdyPreferencesPage">
+    <property name="icon_name">preferences-system-privacy-symbolic</property>
+    <property name="title" translatable="yes">Privacy</property>
+    <property name="visible">True</property>
+    <style>
+      <class name="background"/>
+    </style>
+    <child>
+      <object class="HdyPreferencesGroup">
+        <property name="title" translatable="yes">Web Content</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow" id="adblock_allow_row">
+            <property name="activatable_widget">adblock_allow_switch</property>
+            <property name="title" translatable="yes">Try to Block _Advertisements</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <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">
+            <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 type="action">
+              <object class="GtkSwitch" id="popups_allow_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">enable_safe_browsing_switch</property>
+            <property name="title" translatable="yes">Try to Block Dangerous Web_sites</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <object class="GtkSwitch" id="enable_safe_browsing_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup">
+        <property name="title" translatable="yes">Cookies</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable">False</property>
+            <property name="title" translatable="yes">Clear _Cookies</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <object class="GtkButton">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+                <signal name="clicked" handler="on_manage_cookies_button_clicked"/>
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child>
+                  <object class="GtkImage">
+                    <property name="icon_name">emblem-system-symbolic</property>
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">always</property>
+            <property name="title" translatable="yes">_Always accept</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="prefix">
+              <object class="GtkRadioButton" id="always">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">no_third_party</property>
+            <property name="subtitle" translatable="yes" comments="Refers to &quot;Only from sites you 
visit&quot; option under Cookies.">For example, not from advertisers on these sites.</property>
+            <property name="title" translatable="yes">Only _from sites you visit</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="prefix">
+              <object class="GtkRadioButton" id="no_third_party">
+                <property name="valign">center</property>
+                <property name="group">always</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">never</property>
+            <property name="title" translatable="yes">_Never accept</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="prefix">
+              <object class="GtkRadioButton" id="never">
+                <property name="valign">center</property>
+                <property name="group">always</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup">
+        <property name="title" translatable="yes">Passwords</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable">False</property>
+            <property name="title" translatable="yes">_Passwords</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <object class="GtkButton">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+                <signal name="clicked" handler="on_manage_passwords_button_clicked"/>
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child>
+                  <object class="GtkImage">
+                    <property name="icon_name">emblem-system-symbolic</property>
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <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 type="action">
+              <object class="GtkSwitch" id="remember_passwords_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup">
+        <property name="title" translatable="yes">Personal Data</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable">False</property>
+            <property name="subtitle" translatable="yes">You can clear stored personal data.</property>
+            <property name="title" translatable="yes">Clear Personal _Data</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <object class="GtkButton" id="clear_personal_data_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">emblem-system-symbolic</property>
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/resources/gtk/prefs-sync-page.ui b/src/resources/gtk/prefs-sync-page.ui
new file mode 100644
index 000000000..f79f5bcc4
--- /dev/null
+++ b/src/resources/gtk/prefs-sync-page.ui
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <template class="PrefsSyncPage" parent="HdyPreferencesPage">
+    <property name="icon_name">emblem-synchronizing-symbolic</property>
+    <property name="title" translatable="yes">Sync</property>
+    <property name="visible">True</property>
+    <style>
+      <class name="background"/>
+    </style>
+    <child>
+      <object class="HdyPreferencesGroup" id="sync_page_box">
+        <property name="description" translatable="yes">Sign in with your Firefox account to sync your data 
with Web and Firefox on other computers. Web is not Firefox and is not produced or endorsed by 
Mozilla.</property>
+        <property name="title" translatable="yes">Firefox Sync</property>
+        <property name="visible">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="halign">start</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup" id="sync_firefox_account_box">
+        <property name="title" translatable="yes">Firefox Account</property>
+        <property name="visible">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 type="action">
+              <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>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="HdyPreferencesGroup" id="sync_options_box">
+        <property name="title" translatable="yes">Sync Options</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">sync_bookmarks_checkbutton</property>
+            <property name="title" translatable="yes">Sync _Bookmarks</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="prefix">
+              <object class="GtkCheckButton" id="sync_bookmarks_checkbutton">
+                <property name="use-underline">True</property>
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">sync_passwords_checkbutton</property>
+            <property name="title" translatable="yes">Sync _Passwords</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="prefix">
+              <object class="GtkCheckButton" id="sync_passwords_checkbutton">
+                <property name="use-underline">True</property>
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">sync_history_checkbutton</property>
+            <property name="title" translatable="yes">Sync _History</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="prefix">
+              <object class="GtkCheckButton" id="sync_history_checkbutton">
+                <property name="use-underline">True</property>
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">sync_open_tabs_checkbutton</property>
+            <property name="title" translatable="yes">Sync Open _Tabs</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child type="prefix">
+              <object class="GtkCheckButton" id="sync_open_tabs_checkbutton">
+                <property name="use-underline">True</property>
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+            <child type="action">
+              <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>
+        </child>
+        <child>
+          <object class="HdyComboRow" id="sync_frequency_row">
+            <property name="title" translatable="yes">Frequency</property>
+            <property name="visible">True</property>
+            <child type="action">
+              <object class="GtkSeparator">
+                <property name="margin_bottom">8</property>
+                <property name="margin_top">8</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+            <child type="action">
+              <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>
+        </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 type="action">
+              <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 type="action">
+              <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 type="action">
+              <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="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>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkSizeGroup">
+    <property name="mode">horizontal</property>
+    <widgets>
+      <widget name="sync_sign_out_button"/>
+      <widget name="synced_tabs_button"/>
+      <widget name="sync_now_button"/>
+      <widget name="sync_device_name_change_button"/>
+    </widgets>
+  </object>
+</interface>


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