[epiphany/wip/sync] prefs-dialog: Add sync settings



commit 7df11794792116a5f60de01e0c1ce5296357978f
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date:   Fri Mar 31 00:29:55 2017 +0300

    prefs-dialog: Add sync settings
    
    Add option to enable/disable bookmarks sync.
    Add option to enable/disable sync with Firefox.
    Add option to configure sync frequency.
    Add 'sync now' functionality.

 data/org.gnome.epiphany.gschema.xml    |   19 ++-
 lib/ephy-prefs.h                       |   11 +-
 src/bookmarks/ephy-bookmarks-manager.c |   14 +-
 src/ephy-shell.c                       |   18 ++-
 src/ephy-shell.h                       |    2 +
 src/prefs-dialog.c                     |  335 ++++++++++++++++++++++----------
 src/resources/gtk/prefs-dialog.ui      |  181 ++++++++++++++---
 src/sync/ephy-sync-service.c           |  171 +++++++++++------
 src/sync/ephy-sync-service.h           |    8 +-
 9 files changed, 539 insertions(+), 220 deletions(-)
---
diff --git a/data/org.gnome.epiphany.gschema.xml b/data/org.gnome.epiphany.gschema.xml
index 73a55ac..7d697c5 100644
--- a/data/org.gnome.epiphany.gschema.xml
+++ b/data/org.gnome.epiphany.gschema.xml
@@ -272,12 +272,27 @@
                        <summary>Currently signed in sync user</summary>
                        <description>The email linked to the Firefox Account used to sync data with Mozilla’s 
servers.</description>
                </key>
-               <key type="d" name="bookmarks-sync-time">
+               <key type="u" name="sync-frequency">
+                       <default>30</default>
+                       <summary>The sync frequency in minutes</summary>
+                       <description>The number of minutes between two consecutive syncs.</description>
+               </key>
+               <key type="b" name="sync-with-firefox">
+                       <default>false</default>
+                       <summary>Sync data with Firefox</summary>
+                       <description>TRUE if Ephy collections should be synced with Firefox collections, 
FALSE otherwise.</description>
+               </key>
+               <key type="b" name="sync-bookmarks-enabled">
+                       <default>false</default>
+                       <summary>Enable bookmarks sync</summary>
+                       <description>TRUE if bookmarks collection should be synced, FALSE 
otherwise.</description>
+               </key>
+               <key type="d" name="sync-bookmarks-time">
                        <default>0</default>
                        <summary>Bookmarks sync timestamp</summary>
                        <description>The timestamp at which last bookmarks sync was made.</description>
                </key>
-               <key type="b" name="bookmarks-initial-sync">
+               <key type="b" name="sync-bookmarks-initial">
                        <default>true</default>
                        <summary>Initial sync or normal sync</summary>
                        <description>TRUE if bookmarks collection needs to be synced for the first time, 
FALSE otherwise.</description>
diff --git a/lib/ephy-prefs.h b/lib/ephy-prefs.h
index 904540a..7e53dbe 100644
--- a/lib/ephy-prefs.h
+++ b/lib/ephy-prefs.h
@@ -149,10 +149,13 @@ static const char * const ephy_prefs_web_schema[] = {
 #define EPHY_PREFS_LOCKDOWN_PRINTING          "disable-printing"
 #define EPHY_PREFS_LOCKDOWN_QUIT              "disable-quit"
 
-#define EPHY_PREFS_SYNC_SCHEMA                  "org.gnome.Epiphany.sync"
-#define EPHY_PREFS_SYNC_USER                    "sync-user"
-#define EPHY_PREFS_SYNC_BOOKMARKS_SYNC_TIME     "bookmarks-sync-time"
-#define EPHY_PREFS_SYNC_BOOKMARKS_INITIAL_SYNC  "bookmarks-initial-sync"
+#define EPHY_PREFS_SYNC_SCHEMA            "org.gnome.Epiphany.sync"
+#define EPHY_PREFS_SYNC_USER              "sync-user"
+#define EPHY_PREFS_SYNC_FREQUENCY         "sync-frequency"
+#define EPHY_PREFS_SYNC_WITH_FIREFOX      "sync-with-firefox"
+#define EPHY_PREFS_SYNC_BOOKMARKS_ENABLED "sync-bookmarks-enabled"
+#define EPHY_PREFS_SYNC_BOOKMARKS_TIME    "sync-bookmarks-time"
+#define EPHY_PREFS_SYNC_BOOKMARKS_INITIAL "sync-bookmarks-initial"
 
 static struct {
   const char *schema;
diff --git a/src/bookmarks/ephy-bookmarks-manager.c b/src/bookmarks/ephy-bookmarks-manager.c
index 4363c97..8aa995d 100644
--- a/src/bookmarks/ephy-bookmarks-manager.c
+++ b/src/bookmarks/ephy-bookmarks-manager.c
@@ -245,7 +245,10 @@ list_model_iface_init (GListModelInterface *iface)
 static const char *
 synchronizable_manager_get_collection_name (EphySynchronizableManager *manager)
 {
-  return "bookmarks";
+  gboolean sync_with_firefox = g_settings_get_boolean (EPHY_SETTINGS_SYNC,
+                                                       EPHY_PREFS_SYNC_WITH_FIREFOX);
+
+  return sync_with_firefox ? "bookmarks" : "ephy-bookmarks";
 }
 
 static GType
@@ -258,7 +261,7 @@ static gboolean
 synchronizable_manager_is_initial_sync (EphySynchronizableManager *manager)
 {
   return g_settings_get_boolean (EPHY_SETTINGS_SYNC,
-                                 EPHY_PREFS_SYNC_BOOKMARKS_INITIAL_SYNC);
+                                 EPHY_PREFS_SYNC_BOOKMARKS_INITIAL);
 }
 
 static void
@@ -266,20 +269,21 @@ synchronizable_manager_set_is_initial_sync (EphySynchronizableManager *manager,
                                             gboolean                   is_initial)
 {
   g_settings_set_boolean (EPHY_SETTINGS_SYNC,
-                          EPHY_PREFS_SYNC_BOOKMARKS_INITIAL_SYNC, is_initial);
+                          EPHY_PREFS_SYNC_BOOKMARKS_INITIAL,
+                          is_initial);
 }
 
 static double
 synchronizable_manager_get_sync_time (EphySynchronizableManager *manager)
 {
-  return g_settings_get_double (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_BOOKMARKS_SYNC_TIME);
+  return g_settings_get_double (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_BOOKMARKS_TIME);
 }
 
 static void
 synchronizable_manager_set_sync_time (EphySynchronizableManager *manager,
                                       double                     sync_time)
 {
-  g_settings_set_double (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_BOOKMARKS_SYNC_TIME, sync_time);
+  g_settings_set_double (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_BOOKMARKS_TIME, sync_time);
 }
 
 static void
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index b067195..0a91cc9 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -372,10 +372,6 @@ ephy_shell_startup (GApplication *application)
                       "sync-tokens-load-finished",
                       G_CALLBACK (sync_tokens_load_finished_cb), NULL);
 
-    /* Register the bookmarks collection. */
-    ephy_sync_service_register_manager (ephy_shell->sync_service,
-                                        EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_bookmarks_manager 
(ephy_shell)));
-
     gtk_application_set_app_menu (GTK_APPLICATION (application),
                                   G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
   } else {
@@ -890,6 +886,20 @@ ephy_shell_get_prefs_dialog (EphyShell *shell)
   return shell->prefs_dialog;
 }
 
+GList *
+ephy_shell_get_synchronizable_managers (EphyShell *shell)
+{
+  GList *managers = NULL;
+
+  g_return_val_if_fail (EPHY_IS_SHELL (shell), NULL);
+
+  if (g_settings_get_boolean (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_BOOKMARKS_ENABLED))
+    managers = g_list_prepend (managers,
+                              EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_bookmarks_manager (shell)));
+
+  return managers;
+}
+
 void
 _ephy_shell_create_instance (EphyEmbedShellMode mode)
 {
diff --git a/src/ephy-shell.h b/src/ephy-shell.h
index bd79449..f3fa42a 100644
--- a/src/ephy-shell.h
+++ b/src/ephy-shell.h
@@ -108,6 +108,8 @@ GtkWidget       *ephy_shell_get_history_dialog           (EphyShell *shell);
 
 GObject         *ephy_shell_get_prefs_dialog             (EphyShell *shell);
 
+GList           *ephy_shell_get_synchronizable_managers  (EphyShell *shell);
+
 guint           ephy_shell_get_n_windows                (EphyShell *shell);
 
 gboolean        ephy_shell_close_all_windows            (EphyShell *shell);
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index 35c316a..665245a 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -111,12 +111,23 @@ struct _PrefsDialog {
   GHashTable *iso_3166_table;
 
   /* sync */
-  GtkWidget *sync_authenticate_box;
-  GtkWidget *sync_sign_in_box;
-  GtkWidget *sync_sign_in_details;
-  GtkWidget *sync_sign_out_box;
-  GtkWidget *sync_sign_out_details;
+  EphySyncService *sync_service;
+  GtkWidget *sync_page_box;
+  GtkWidget *sync_firefox_iframe_box;
+  GtkWidget *sync_firefox_iframe_label;
+  GtkWidget *sync_firefox_account_box;
+  GtkWidget *sync_firefox_account_label;
   GtkWidget *sync_sign_out_button;
+  GtkWidget *sync_options_box;
+  GtkWidget *sync_with_firefox_checkbutton;
+  GtkWidget *sync_bookmarks_checkbutton;
+  GtkWidget *sync_frequency_5_min_radiobutton;
+  GtkWidget *sync_frequency_15_min_radiobutton;
+  GtkWidget *sync_frequency_30_min_radiobutton;
+  GtkWidget *sync_frequency_60_min_radiobutton;
+  GtkWidget *sync_now_button;
+  gboolean sync_bookmarks_enabled;
+  guint32 sync_frequency;
 
   WebKitWebView *fxa_web_view;
   WebKitUserContentManager *fxa_manager;
@@ -154,10 +165,36 @@ prefs_dialog_finalize (GObject *object)
     g_object_unref (dialog->fxa_manager);
   }
 
+  if (dialog->sync_frequency != g_settings_get_uint (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_FREQUENCY))
+    g_signal_emit_by_name (dialog->sync_service, "sync-frequency-changed");
+
   G_OBJECT_CLASS (prefs_dialog_parent_class)->finalize (object);
 }
 
 static void
+prefs_dialog_dispose (GObject *object)
+{
+  PrefsDialog *dialog = EPHY_PREFS_DIALOG (object);
+
+  if (dialog->sync_bookmarks_checkbutton &&
+      gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->sync_bookmarks_checkbutton)) != 
dialog->sync_bookmarks_enabled) {
+    g_settings_set_boolean (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_BOOKMARKS_INITIAL, TRUE);
+  }
+
+  G_OBJECT_CLASS (prefs_dialog_parent_class)->dispose (object);
+}
+
+static void
+sync_finished_cb (EphySyncService *service,
+                  PrefsDialog     *dialog)
+{
+  g_assert (EPHY_IS_SYNC_SERVICE (service));
+  g_assert (EPHY_IS_PREFS_DIALOG (dialog));
+
+  gtk_widget_set_sensitive (dialog->sync_now_button, TRUE);
+}
+
+static void
 sync_sign_in_details_show (PrefsDialog *dialog,
                            const char  *text)
 {
@@ -166,8 +203,8 @@ sync_sign_in_details_show (PrefsDialog *dialog,
   g_assert (EPHY_IS_PREFS_DIALOG (dialog));
 
   message = g_strdup_printf ("<span fgcolor='#e6780b'>%s</span>", text);
-  gtk_label_set_markup (GTK_LABEL (dialog->sync_sign_in_details), message);
-  gtk_widget_set_visible (dialog->sync_sign_in_details, TRUE);
+  gtk_label_set_markup (GTK_LABEL (dialog->sync_firefox_iframe_label), message);
+  gtk_widget_set_visible (dialog->sync_firefox_iframe_label, TRUE);
 
   g_free (message);
 }
@@ -186,28 +223,6 @@ sync_sign_in_error_cb (EphySyncService *service,
 }
 
 static void
-sync_hide_fxa_iframe (PrefsDialog *dialog,
-                      const char  *email)
-{
-  char *text;
-  char *account;
-
-  account = g_strdup_printf ("<b>%s</b>", email);
-  /* Translators: the %s refers to the email of the currently logged in user. */
-  text = g_strdup_printf (_("Currently logged in as %s"), account);
-  gtk_label_set_markup (GTK_LABEL (dialog->sync_sign_out_details), text);
-
-  gtk_container_remove (GTK_CONTAINER (dialog->sync_authenticate_box),
-                        dialog->sync_sign_in_box);
-  gtk_box_pack_start (GTK_BOX (dialog->sync_authenticate_box),
-                      dialog->sync_sign_out_box,
-                      TRUE, TRUE, 0);
-
-  g_free (text);
-  g_free (account);
-}
-
-static void
 sync_tokens_store_finished_cb (EphySyncService *service,
                                GError          *error,
                                PrefsDialog     *dialog)
@@ -216,19 +231,32 @@ sync_tokens_store_finished_cb (EphySyncService *service,
   g_assert (EPHY_IS_PREFS_DIALOG (dialog));
 
   if (error == NULL) {
-    /* Show the 'Signed in' panel. */
-    sync_hide_fxa_iframe (dialog, ephy_sync_service_get_user_email (service));
+    char *text;
+    char *account;
+
+    /* Show sync options panel. */
+    account = g_strdup_printf ("<b>%s</b>", ephy_sync_service_get_user_email (service));
+    /* Translators: the %s refers to the email of the currently logged in user. */
+    text = g_strdup_printf (_("Currently logged in as %s"), account);
+    gtk_label_set_markup (GTK_LABEL (dialog->sync_firefox_account_label), text);
+    gtk_container_remove (GTK_CONTAINER (dialog->sync_page_box),
+                          dialog->sync_firefox_iframe_box);
+    gtk_box_pack_start (GTK_BOX (dialog->sync_page_box),
+                        dialog->sync_firefox_account_box,
+                        FALSE, FALSE, 0);
+    gtk_box_pack_start (GTK_BOX (dialog->sync_page_box),
+                        dialog->sync_options_box,
+                        FALSE, FALSE, 0);
 
     g_settings_set_string (EPHY_SETTINGS_SYNC,
                            EPHY_PREFS_SYNC_USER,
                            ephy_sync_service_get_user_email (service));
-    g_settings_set_boolean (EPHY_SETTINGS_SYNC,
-                            EPHY_PREFS_SYNC_BOOKMARKS_INITIAL_SYNC,
-                            TRUE);
 
     /* Start syncing. */
-    /* TODO: Register the synchronizable managers before calling this. */
     ephy_sync_service_start_periodical_sync (service);
+
+    g_free (text);
+    g_free (account);
   } else {
     /* Destroy the current session. */
     ephy_sync_service_destroy_session (service, NULL);
@@ -275,7 +303,6 @@ sync_fxa_server_message_cb (WebKitUserContentManager *manager,
                             WebKitJavascriptResult   *result,
                             PrefsDialog              *dialog)
 {
-  EphySyncService *service;
   JsonParser *parser;
   JsonObject *object;
   JsonObject *detail;
@@ -283,7 +310,6 @@ sync_fxa_server_message_cb (WebKitUserContentManager *manager,
   const char *type;
   const char *command;
 
-  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
   json_string = ephy_embed_utils_get_js_result_as_string (result);
   parser = json_parser_new ();
   json_parser_load_from_data (parser, json_string, -1, NULL);
@@ -300,7 +326,7 @@ sync_fxa_server_message_cb (WebKitUserContentManager *manager,
   command = json_object_get_string_member (detail, "command");
 
   if (g_strcmp0 (command, "loaded") == 0) {
-    LOG ("Loaded Firefox Sign In iframe");
+    LOG ("Firefox Accounts iframe loaded");
   } else if (g_strcmp0 (command, "can_link_account") == 0) {
     /* We need to confirm a relink. */
     sync_send_data_to_fxa_server (dialog, "message", "can_link_account", "{'ok': true}");
@@ -312,7 +338,7 @@ sync_fxa_server_message_cb (WebKitUserContentManager *manager,
     const char *keyFetchToken = json_object_get_string_member (data, "keyFetchToken");
     const char *unwrapBKey = json_object_get_string_member (data, "unwrapBKey");
 
-    gtk_widget_set_visible (dialog->sync_sign_in_details, FALSE);
+    gtk_widget_set_visible (dialog->sync_firefox_iframe_label, FALSE);
     sync_send_data_to_fxa_server (dialog, "message", "login", NULL);
 
     /* Cannot retrieve the sync keys without keyFetchToken or unwrapBKey. */
@@ -320,7 +346,7 @@ sync_fxa_server_message_cb (WebKitUserContentManager *manager,
       g_warning ("Ignoring login with keyFetchToken or unwrapBKey missing!"
                  "Cannot retrieve sync keys with one of them missing.");
 
-      ephy_sync_service_destroy_session (service, sessionToken);
+      ephy_sync_service_destroy_session (dialog->sync_service, sessionToken);
       sync_sign_in_details_show (dialog, _("Something went wrong, please try again."));
       webkit_web_view_load_uri (dialog->fxa_web_view, FXA_IFRAME_URL);
 
@@ -331,7 +357,7 @@ sync_fxa_server_message_cb (WebKitUserContentManager *manager,
       sync_sign_in_details_show (dialog, _("Please don’t leave this page until "
                                            "you have completed the verification."));
 
-    ephy_sync_service_do_sign_in (service, email, uid,
+    ephy_sync_service_do_sign_in (dialog->sync_service, email, uid,
                                   sessionToken, keyFetchToken, unwrapBKey);
   } else if (g_strcmp0 (command, "session_status") == 0) {
     /* We are not signed in at this time, which we signal by returning an error. */
@@ -347,7 +373,7 @@ out:
 }
 
 static void
-sync_setup_sign_in_view (PrefsDialog *dialog)
+sync_setup_firefox_iframe (PrefsDialog *dialog)
 {
   EphyEmbedShell *shell;
   WebKitWebContext *embed_context;
@@ -359,62 +385,68 @@ sync_setup_sign_in_view (PrefsDialog *dialog)
                    "};"
                    "window.addEventListener(\"FirefoxAccountsCommand\", handleAccountsCommand);";
 
-  dialog->fxa_script = webkit_user_script_new (js,
-                                               WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
-                                               WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END,
-                                               NULL, NULL);
-  dialog->fxa_manager = webkit_user_content_manager_new ();
-  webkit_user_content_manager_add_script (dialog->fxa_manager, dialog->fxa_script);
-  g_signal_connect (dialog->fxa_manager,
-                    "script-message-received::accountsCommandHandler",
-                    G_CALLBACK (sync_fxa_server_message_cb),
-                    dialog);
-  webkit_user_content_manager_register_script_message_handler (dialog->fxa_manager,
-                                                               "accountsCommandHandler");
-
-  shell = ephy_embed_shell_get_default ();
-  embed_context = ephy_embed_shell_get_web_context (shell);
-
-  sync_context = webkit_web_context_new ();
-  webkit_web_context_set_preferred_languages (sync_context,
-                                              g_object_get_data (G_OBJECT (embed_context), 
"preferred-languages"));
-  dialog->fxa_web_view = WEBKIT_WEB_VIEW (g_object_new (WEBKIT_TYPE_WEB_VIEW,
-                                                        "user-content-manager", dialog->fxa_manager,
-                                                        "settings", ephy_embed_prefs_get_settings (),
-                                                        "web-context", sync_context,
-                                                        NULL));
-  g_object_unref (sync_context);
-
-  gtk_widget_set_visible (GTK_WIDGET (dialog->fxa_web_view), TRUE);
-  gtk_widget_set_size_request (GTK_WIDGET (dialog->fxa_web_view), 450, 450);
-  webkit_web_view_load_uri (dialog->fxa_web_view, FXA_IFRAME_URL);
+  if (dialog->fxa_web_view == NULL) {
+    dialog->fxa_script = webkit_user_script_new (js,
+                                                 WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
+                                                 WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END,
+                                                 NULL, NULL);
+    dialog->fxa_manager = webkit_user_content_manager_new ();
+    webkit_user_content_manager_add_script (dialog->fxa_manager, dialog->fxa_script);
+    g_signal_connect (dialog->fxa_manager,
+                      "script-message-received::accountsCommandHandler",
+                      G_CALLBACK (sync_fxa_server_message_cb),
+                      dialog);
+    webkit_user_content_manager_register_script_message_handler (dialog->fxa_manager,
+                                                                 "accountsCommandHandler");
+
+    shell = ephy_embed_shell_get_default ();
+    embed_context = ephy_embed_shell_get_web_context (shell);
+    sync_context = webkit_web_context_new ();
+    webkit_web_context_set_preferred_languages (sync_context,
+                                                g_object_get_data (G_OBJECT (embed_context), 
"preferred-languages"));
+
+    dialog->fxa_web_view = WEBKIT_WEB_VIEW (g_object_new (WEBKIT_TYPE_WEB_VIEW,
+                                                          "user-content-manager", dialog->fxa_manager,
+                                                          "settings", ephy_embed_prefs_get_settings (),
+                                                          "web-context", sync_context,
+                                                          NULL));
+    gtk_widget_set_visible (GTK_WIDGET (dialog->fxa_web_view), TRUE);
+    gtk_widget_set_size_request (GTK_WIDGET (dialog->fxa_web_view), 450, 450);
+    gtk_box_pack_start (GTK_BOX (dialog->sync_firefox_iframe_box),
+                      GTK_WIDGET (dialog->fxa_web_view),
+                      FALSE, FALSE, 0);
+
+    g_object_unref (sync_context);
+  }
 
-  gtk_widget_set_visible (dialog->sync_sign_in_details, FALSE);
-  gtk_container_add (GTK_CONTAINER (dialog->sync_sign_in_box),
-                     GTK_WIDGET (dialog->fxa_web_view));
+  webkit_web_view_load_uri (dialog->fxa_web_view, FXA_IFRAME_URL);
+  gtk_widget_set_visible (dialog->sync_firefox_iframe_label, FALSE);
 }
 
 static void
 on_sync_sign_out_button_clicked (GtkWidget   *button,
                                  PrefsDialog *dialog)
 {
-  EphySyncService *service;
-
-  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
-  ephy_sync_service_do_sign_out (service);
 
-  /* Show sign in box. */
-  if (dialog->fxa_web_view == NULL)
-    sync_setup_sign_in_view (dialog);
-  else
-    webkit_web_view_load_uri (dialog->fxa_web_view, FXA_IFRAME_URL);
+  ephy_sync_service_do_sign_out (dialog->sync_service);
+
+  /* Show Firefox Accounts iframe. */
+  sync_setup_firefox_iframe (dialog);
+  gtk_container_remove (GTK_CONTAINER (dialog->sync_page_box),
+                        dialog->sync_firefox_account_box);
+  gtk_container_remove (GTK_CONTAINER (dialog->sync_page_box),
+                        dialog->sync_options_box);
+  gtk_box_pack_start (GTK_BOX (dialog->sync_page_box),
+                      dialog->sync_firefox_iframe_box,
+                      FALSE, FALSE, 0);
+}
 
-  gtk_container_remove (GTK_CONTAINER (dialog->sync_authenticate_box),
-                        dialog->sync_sign_out_box);
-  gtk_box_pack_start (GTK_BOX (dialog->sync_authenticate_box),
-                      dialog->sync_sign_in_box,
-                      TRUE, TRUE, 0);
-  gtk_widget_set_visible (dialog->sync_sign_in_details, FALSE);
+static void
+on_sync_sync_now_button_clicked (GtkWidget   *button,
+                                 PrefsDialog *dialog)
+{
+  ephy_sync_service_do_sync (dialog->sync_service);
+  gtk_widget_set_sensitive (button, FALSE);
 }
 
 static void
@@ -462,6 +494,7 @@ prefs_dialog_class_init (PrefsDialogClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+  object_class->dispose = prefs_dialog_dispose;
   object_class->finalize = prefs_dialog_finalize;
 
   gtk_widget_class_set_template_from_resource (widget_class,
@@ -508,17 +541,26 @@ prefs_dialog_class_init (PrefsDialogClass *klass)
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, enable_spell_checking_checkbutton);
 
   /* sync */
-  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_authenticate_box);
-  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_sign_in_box);
-  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_sign_in_details);
-  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_sign_out_box);
-  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_sign_out_details);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_page_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_firefox_iframe_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_firefox_iframe_label);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_firefox_account_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_firefox_account_label);
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_sign_out_button);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_options_box);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_with_firefox_checkbutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_bookmarks_checkbutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_frequency_5_min_radiobutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_frequency_15_min_radiobutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_frequency_30_min_radiobutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_frequency_60_min_radiobutton);
+  gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_now_button);
 
   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);
   gtk_widget_class_bind_template_callback (widget_class, on_search_engine_dialog_button_clicked);
   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);
 }
 
 static void
@@ -1124,6 +1166,28 @@ clear_personal_data_button_clicked_cb (GtkWidget   *button,
 }
 
 static gboolean
+sync_frequency_get_mapping (GValue   *value,
+                            GVariant *variant,
+                            gpointer  user_data)
+{
+  if (GPOINTER_TO_UINT (user_data) == g_variant_get_uint32 (variant))
+    g_value_set_boolean (value, TRUE);
+
+  return TRUE;
+}
+
+static GVariant *
+sync_frequency_set_mapping (const GValue       *value,
+                            const GVariantType *expected_type,
+                            gpointer            user_data)
+{
+  if (!g_value_get_boolean (value))
+    return NULL;
+
+  return g_variant_new_uint32 (GPOINTER_TO_UINT (user_data));
+}
+
+static gboolean
 cookies_get_mapping (GValue   *value,
                      GVariant *variant,
                      gpointer  user_data)
@@ -1502,35 +1566,94 @@ setup_language_page (PrefsDialog *dialog)
 static void
 setup_sync_page (PrefsDialog *dialog)
 {
-  EphySyncService *service;
+  GSettings *sync_settings;
   char *account;
   char *text;
 
-  service = ephy_shell_get_sync_service (ephy_shell_get_default ());
+  sync_settings = ephy_settings_get (EPHY_PREFS_SYNC_SCHEMA);
+  dialog->sync_service = ephy_shell_get_sync_service (ephy_shell_get_default ());
 
-  if (ephy_sync_service_is_signed_in (service) == FALSE) {
-    sync_setup_sign_in_view (dialog);
-    gtk_container_remove (GTK_CONTAINER (dialog->sync_authenticate_box),
-                          dialog->sync_sign_out_box);
+  if (!ephy_sync_service_is_signed_in (dialog->sync_service)) {
+    sync_setup_firefox_iframe (dialog);
+    gtk_container_remove (GTK_CONTAINER (dialog->sync_page_box),
+                          dialog->sync_firefox_account_box);
+    gtk_container_remove (GTK_CONTAINER (dialog->sync_page_box),
+                          dialog->sync_options_box);
   } else {
-    gtk_container_remove (GTK_CONTAINER (dialog->sync_authenticate_box),
-                          dialog->sync_sign_in_box);
+    gtk_container_remove (GTK_CONTAINER (dialog->sync_page_box),
+                          dialog->sync_firefox_iframe_box);
 
-    account = g_strdup_printf ("<b>%s</b>", ephy_sync_service_get_user_email (service));
+    account = g_strdup_printf ("<b>%s</b>",
+                               ephy_sync_service_get_user_email (dialog->sync_service));
     /* Translators: the %s refers to the email of the currently logged in user. */
     text = g_strdup_printf (_("Currently logged in as %s"), account);
-    gtk_label_set_markup (GTK_LABEL (dialog->sync_sign_out_details), text);
+    gtk_label_set_markup (GTK_LABEL (dialog->sync_firefox_account_label), text);
 
     g_free (text);
     g_free (account);
   }
 
-  g_signal_connect_object (service, "sync-tokens-store-finished",
+  g_signal_connect_object (dialog->sync_service, "sync-tokens-store-finished",
                            G_CALLBACK (sync_tokens_store_finished_cb),
                            dialog, 0);
-  g_signal_connect_object (service, "sync-sign-in-error",
+  g_signal_connect_object (dialog->sync_service, "sync-sign-in-error",
                            G_CALLBACK (sync_sign_in_error_cb),
                            dialog, 0);
+  g_signal_connect_object (dialog->sync_service, "sync-finished",
+                           G_CALLBACK (sync_finished_cb),
+                           dialog, 0);
+
+  g_settings_bind (sync_settings,
+                   EPHY_PREFS_SYNC_WITH_FIREFOX,
+                   dialog->sync_with_firefox_checkbutton,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+  g_settings_bind (sync_settings,
+                   EPHY_PREFS_SYNC_BOOKMARKS_ENABLED,
+                   dialog->sync_bookmarks_checkbutton,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
+  g_settings_bind_with_mapping (sync_settings,
+                                EPHY_PREFS_SYNC_FREQUENCY,
+                                dialog->sync_frequency_5_min_radiobutton,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                sync_frequency_get_mapping,
+                                sync_frequency_set_mapping,
+                                GINT_TO_POINTER (5),
+                                NULL);
+  g_settings_bind_with_mapping (sync_settings,
+                                EPHY_PREFS_SYNC_FREQUENCY,
+                                dialog->sync_frequency_15_min_radiobutton,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                sync_frequency_get_mapping,
+                                sync_frequency_set_mapping,
+                                GINT_TO_POINTER (15),
+                                NULL);
+  g_settings_bind_with_mapping (sync_settings,
+                                EPHY_PREFS_SYNC_FREQUENCY,
+                                dialog->sync_frequency_30_min_radiobutton,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                sync_frequency_get_mapping,
+                                sync_frequency_set_mapping,
+                                GINT_TO_POINTER (30),
+                                NULL);
+  g_settings_bind_with_mapping (sync_settings,
+                                EPHY_PREFS_SYNC_FREQUENCY,
+                                dialog->sync_frequency_60_min_radiobutton,
+                                "active",
+                                G_SETTINGS_BIND_DEFAULT,
+                                sync_frequency_get_mapping,
+                                sync_frequency_set_mapping,
+                                GINT_TO_POINTER (60),
+                                NULL);
+
+  dialog->sync_bookmarks_enabled = g_settings_get_boolean (EPHY_SETTINGS_SYNC,
+                                                           EPHY_PREFS_SYNC_BOOKMARKS_ENABLED);
+  dialog->sync_frequency = g_settings_get_uint (EPHY_SETTINGS_SYNC,
+                                                EPHY_PREFS_SYNC_FREQUENCY);
 }
 
 static void
diff --git a/src/resources/gtk/prefs-dialog.ui b/src/resources/gtk/prefs-dialog.ui
index c22cb67..31ec162 100644
--- a/src/resources/gtk/prefs-dialog.ui
+++ b/src/resources/gtk/prefs-dialog.ui
@@ -770,11 +770,11 @@
               </packing>
             </child>
             <child>
-              <object class="GtkBox">
+              <object class="GtkBox" id="sync_page_box">
                 <property name="visible">True</property>
                 <property name="border-width">12</property>
                 <property name="orientation">vertical</property>
-                <property name="spacing">8</property>
+                <property name="spacing">18</property>
                 <child>
                   <object class="GtkBox">
                     <property name="visible">True</property>
@@ -794,69 +794,188 @@
                       <object class="GtkBox">
                         <property name="visible">True</property>
                         <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
                         <property name="margin-start">12</property>
-                        <property name="spacing">8</property>
                         <child>
                           <object class="GtkLabel">
                             <property name="visible">True</property>
                             <property name="halign">start</property>
-                            <property name="use-markup">True</property>
                             <property name="max-width-chars">60</property>
                             <property name="wrap">True</property>
-                            <property name="label" translatable="yes">Sign in with your Firefox account to 
sync your data with Web on other computers. Web is not Firefox and cannot sync with Firefox. Web is not 
produced or endorsed by Mozilla.</property>
+                            <property name="label" translatable="yes">Sign in with your Firefox account to 
sync your data with Web and Firefox on other computers. Web is not produced or endorsed by Mozilla.</property>
                           </object>
                         </child>
                       </object>
                     </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkBox" id="sync_firefox_iframe_box">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
+                    <property name="halign">center</property>
+                    <child>
+                      <object class="GtkLabel" id="sync_firefox_iframe_label">
+                        <property name="visible">False</property>
+                        <property name="halign">start</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkBox" id="sync_firefox_account_box">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes">Firefox Account</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                        </attributes>
+                      </object>
+                    </child>
                     <child>
-                      <object class="GtkBox" id="sync_authenticate_box">
+                      <object class="GtkBox">
                         <property name="visible">True</property>
                         <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
                         <property name="margin-start">12</property>
                         <child>
-                          <object class="GtkBox" id="sync_sign_in_box">
+                          <object class="GtkLabel" id="sync_firefox_account_label">
                             <property name="visible">True</property>
-                            <property name="orientation">vertical</property>
-                            <property name="halign">center</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="sync_sign_in_details">
-                                <property name="visible">False</property>
-                                <property name="halign">start</property>
-                              </object>
-                            </child>
+                            <property name="halign">start</property>
+                            <property name="label" translatable="yes"/>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="sync_sign_out_button">
+                            <property name="label" translatable="yes">Sign _out</property>
+                            <property name="visible">True</property>
+                            <property name="use-underline">True</property>
+                            <property name="halign">start</property>
+                            <signal name="clicked" handler="on_sync_sign_out_button_clicked"/>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkBox" id="sync_options_box">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes">Sync Options</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                        </attributes>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <property name="margin-start">12</property>
+                        <child>
+                          <object class="GtkCheckButton" id="sync_with_firefox_checkbutton">
+                            <property name="label" translatable="yes">Sync with _Firefox</property>
+                            <property name="visible">True</property>
+                            <property name="use-underline">True</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="halign">start</property>
+                            <property name="label" translatable="yes">Collections</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkBox" id="sync_sign_out_box">
+                          <object class="GtkBox">
                             <property name="visible">True</property>
                             <property name="orientation">vertical</property>
-                            <property name="spacing">8</property>
+                            <property name="spacing">6</property>
+                            <property name="margin-start">12</property>
                             <child>
-                              <object class="GtkLabel" id="sync_sign_out_details">
+                              <object class="GtkCheckButton" id="sync_bookmarks_checkbutton">
+                                <property name="label" translatable="yes">_Bookmarks</property>
                                 <property name="visible">True</property>
-                                <property name="halign">start</property>
-                                <property name="label" translatable="yes"></property>
+                                <property name="use-underline">True</property>
                               </object>
                             </child>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="halign">start</property>
+                            <property name="label" translatable="yes">Frequency</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">6</property>
+                            <property name="margin-start">12</property>
                             <child>
-                              <object class="GtkLabel">
+                              <object class="GtkBox">
                                 <property name="visible">True</property>
-                                <property name="halign">start</property>
-                                <property name="label" translatable="yes">Sign out if you wish to stop 
syncing.</property>
+                                <child>
+                                  <object class="GtkRadioButton" id="sync_frequency_5_min_radiobutton">
+                                    <property name="label" translatable="yes">_5 min</property>
+                                    <property name="visible">True</property>
+                                    <property name="use-underline">True</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkRadioButton" id="sync_frequency_15_min_radiobutton">
+                                    <property name="label" translatable="yes">_15 min</property>
+                                    <property name="visible">True</property>
+                                    <property name="use-underline">True</property>
+                                    <property name="group">sync_frequency_5_min_radiobutton</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkRadioButton" id="sync_frequency_30_min_radiobutton">
+                                    <property name="label" translatable="yes">_30 min</property>
+                                    <property name="visible">True</property>
+                                    <property name="use-underline">True</property>
+                                    <property name="group">sync_frequency_5_min_radiobutton</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkRadioButton" id="sync_frequency_60_min_radiobutton">
+                                    <property name="label" translatable="yes">_60 min</property>
+                                    <property name="visible">True</property>
+                                    <property name="use-underline">True</property>
+                                    <property name="group">sync_frequency_5_min_radiobutton</property>
+                                  </object>
+                                </child>
                               </object>
                             </child>
                             <child>
-                              <object class="GtkButton" id="sync_sign_out_button">
-                                <property name="label" translatable="yes">Sign _out</property>
+                              <object class="GtkButton" id="sync_now_button">
+                                <property name="label" translatable="yes">Sync _now</property>
                                 <property name="visible">True</property>
                                 <property name="use-underline">True</property>
                                 <property name="halign">start</property>
-                                <property name="width-request">100</property>
-                                <style>
-                                  <class name="destructive-action"/>
-                                  <class name="text-button"/>
-                                </style>
+                                <signal name="clicked" handler="on_sync_sync_now_button_clicked"/>
                               </object>
                             </child>
                           </object>
diff --git a/src/sync/ephy-sync-service.c b/src/sync/ephy-sync-service.c
index 3a69690..40799ed 100644
--- a/src/sync/ephy-sync-service.c
+++ b/src/sync/ephy-sync-service.c
@@ -35,7 +35,6 @@
 
 #define MOZILLA_TOKEN_SERVER_URL  "https://token.services.mozilla.com/1.0/sync/1.5";
 #define MOZILLA_FXA_SERVER_URL    "https://api.accounts.firefox.com/v1/";
-#define SYNC_FREQUENCY            (15 * 60)        /* seconds */
 #define CERTIFICATE_DURATION      (60 * 60 * 1000) /* milliseconds, limited to 24 hours */
 #define ASSERTION_DURATION        (5 * 60)         /* seconds */
 #define STORAGE_VERSION           5
@@ -51,8 +50,6 @@ struct _EphySyncService {
   char        *kB;
   GHashTable  *key_bundles;
 
-  GHashTable  *managers;
-
   char        *user_email;
   double       sync_time;
   gint64       auth_at;
@@ -74,6 +71,8 @@ enum {
   STORE_FINISHED,
   LOAD_FINISHED,
   SIGN_IN_ERROR,
+  SYNC_FREQUENCY_CHANGED,
+  SYNC_FINISHED,
   LAST_SIGNAL
 };
 
@@ -103,6 +102,8 @@ typedef struct {
 typedef struct {
   EphySynchronizableManager *manager;
   gboolean                   is_initial;
+  guint                      collection_index;
+  guint                      num_collections;
 } SyncCollectionAsyncData;
 
 typedef struct {
@@ -111,6 +112,7 @@ typedef struct {
 } SyncAsyncData;
 
 static void ephy_sync_service_send_next_storage_request (EphySyncService *self);
+static void ephy_sync_service_obtain_sync_key_bundles (EphySyncService *self);
 
 static StorageRequestAsyncData *
 storage_request_async_data_new (const char          *endpoint,
@@ -193,13 +195,17 @@ sign_in_async_data_free (SignInAsyncData *data)
 
 static SyncCollectionAsyncData *
 sync_collection_async_data_new (EphySynchronizableManager *manager,
-                                gboolean                   is_initial)
+                                gboolean                   is_initial,
+                                guint                      collection_index,
+                                guint                      num_collections)
 {
   SyncCollectionAsyncData *data;
 
   data = g_slice_new (SyncCollectionAsyncData);
   data->manager = g_object_ref (manager);
   data->is_initial = is_initial;
+  data->collection_index = collection_index;
+  data->num_collections = num_collections;
 
   return data;
 }
@@ -252,6 +258,56 @@ ephy_sync_service_storage_credentials_is_expired (EphySyncService *self)
 }
 
 static void
+ephy_sync_service_stop_periodical_sync (EphySyncService *self)
+{
+  g_assert (EPHY_IS_SYNC_SERVICE (self));
+
+  if (self->source_id != 0) {
+    g_source_remove (self->source_id);
+    self->source_id = 0;
+  }
+}
+
+static gboolean
+ephy_sync_service_sync (gpointer user_data)
+{
+  EphySyncService *service = EPHY_SYNC_SERVICE (user_data);
+  GList *managers = NULL;
+
+  managers = ephy_shell_get_synchronizable_managers (ephy_shell_get_default ());
+  if (managers) {
+    ephy_sync_service_obtain_sync_key_bundles (service);
+    g_list_free (managers);
+  } else {
+    g_signal_emit (service, signals[SYNC_FINISHED], 0);
+  }
+
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+ephy_sync_service_schedule_periodical_sync (EphySyncService *self)
+{
+  g_assert (EPHY_IS_SYNC_SERVICE (self));
+
+  self->source_id = g_timeout_add_seconds (g_settings_get_uint (EPHY_SETTINGS_SYNC,
+                                                                EPHY_PREFS_SYNC_FREQUENCY) * 60,
+                                           ephy_sync_service_sync,
+                                           self);
+  LOG ("Scheduled new sync with frequency %u mins",
+       g_settings_get_uint (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_FREQUENCY));
+}
+
+static void
+ephy_sync_service_sync_frequency_changed_cb (EphySyncService *self)
+{
+  g_assert (EPHY_IS_SYNC_SERVICE (self));
+
+  ephy_sync_service_stop_periodical_sync (self);
+  ephy_sync_service_schedule_periodical_sync (self);
+}
+
+static void
 ephy_sync_service_fxa_hawk_post_async (EphySyncService     *self,
                                        const char          *endpoint,
                                        const char          *id,
@@ -668,7 +724,6 @@ ephy_sync_service_finalize (GObject *object)
 
   g_queue_free_full (self->storage_queue, (GDestroyNotify) storage_request_async_data_free);
   g_hash_table_destroy (self->key_bundles);
-  g_hash_table_destroy (self->managers);
 
   G_OBJECT_CLASS (ephy_sync_service_parent_class)->finalize (object);
 }
@@ -720,6 +775,20 @@ ephy_sync_service_class_init (EphySyncServiceClass *klass)
                   0, NULL, NULL, NULL,
                   G_TYPE_NONE, 1,
                   G_TYPE_STRING);
+
+  signals[SYNC_FREQUENCY_CHANGED] =
+    g_signal_new ("sync-frequency-changed",
+                  EPHY_TYPE_SYNC_SERVICE,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
+  signals[SYNC_FINISHED] =
+    g_signal_new ("sync-finished",
+                  EPHY_TYPE_SYNC_SERVICE,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
 }
 
 static void
@@ -733,7 +802,6 @@ ephy_sync_service_init (EphySyncService *self)
   self->storage_queue = g_queue_new ();
   self->key_bundles = g_hash_table_new_full (g_str_hash, g_str_equal,
                                              NULL, (GDestroyNotify)ephy_sync_crypto_key_bundle_free);
-  self->managers = g_hash_table_new (g_str_hash, g_str_equal);
 
   settings = ephy_embed_prefs_get_settings ();
   user_agent = webkit_settings_get_user_agent (settings);
@@ -746,6 +814,10 @@ ephy_sync_service_init (EphySyncService *self)
     ephy_sync_secret_load_tokens (self);
   }
 
+  g_signal_connect (self, "sync-frequency-changed",
+                    G_CALLBACK (ephy_sync_service_sync_frequency_changed_cb),
+                    NULL);
+
   g_free (email);
 }
 
@@ -842,32 +914,6 @@ ephy_sync_service_get_key_bundle (EphySyncService *self,
 }
 
 void
-ephy_sync_service_register_manager (EphySyncService           *self,
-                                    EphySynchronizableManager *manager)
-{
-  const char *collection;
-
-  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
-  g_return_if_fail (EPHY_IS_SYNCHRONIZABLE_MANAGER (manager));
-
-  collection = ephy_synchronizable_manager_get_collection_name (manager);
-  g_hash_table_insert (self->managers, (char *)collection, manager);
-}
-
-void
-ephy_sync_service_unregister_manager (EphySyncService           *self,
-                                      EphySynchronizableManager *manager)
-{
-  const char *collection;
-
-  g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
-  g_return_if_fail (EPHY_IS_SYNCHRONIZABLE_MANAGER (manager));
-
-  collection = ephy_synchronizable_manager_get_collection_name (manager);
-  g_hash_table_remove (self->managers, collection);
-}
-
-void
 ephy_sync_service_clear_storage_credentials (EphySyncService *self)
 {
   g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
@@ -1461,13 +1507,18 @@ free_parser:
   if (parser)
     g_object_unref (parser);
 out:
+  if (data->collection_index == data->num_collections)
+    g_signal_emit (service, signals[SYNC_FINISHED], 0);
+
   sync_collection_async_data_free (data);
   ephy_sync_service_send_next_storage_request (service);
 }
 
 static void
 ephy_sync_service_sync_collection (EphySyncService           *self,
-                                   EphySynchronizableManager *manager)
+                                   EphySynchronizableManager *manager,
+                                   guint                      collection_index,
+                                   guint                      num_collections)
 {
   SyncCollectionAsyncData *data;
   const char *collection;
@@ -1480,7 +1531,7 @@ ephy_sync_service_sync_collection (EphySyncService           *self,
   collection = ephy_synchronizable_manager_get_collection_name (manager);
   endpoint = g_strdup_printf ("storage/%s?full=true", collection);
   is_initial = ephy_synchronizable_manager_is_initial_sync (manager);
-  data = sync_collection_async_data_new (manager, is_initial);
+  data = sync_collection_async_data_new (manager, is_initial, collection_index, num_collections);
 
   LOG ("Syncing %s collection...", collection);
   ephy_sync_service_queue_storage_request (self, endpoint, SOUP_METHOD_GET, NULL,
@@ -1490,22 +1541,6 @@ ephy_sync_service_sync_collection (EphySyncService           *self,
   g_free (endpoint);
 }
 
-static gboolean
-ephy_sync_service_do_sync (gpointer user_data)
-{
-  EphySyncService *service;
-  GHashTableIter it;
-  gpointer key;
-  gpointer value;
-
-  service = EPHY_SYNC_SERVICE (user_data);
-  g_hash_table_iter_init (&it, service->managers);
-  while (g_hash_table_iter_next (&it, &key, &value))
-    ephy_sync_service_sync_collection (service, EPHY_SYNCHRONIZABLE_MANAGER (value));
-
-  return G_SOURCE_CONTINUE;
-}
-
 static void
 obtain_sync_key_bundles_cb (SoupSession *session,
                             SoupMessage *msg,
@@ -1520,10 +1555,12 @@ obtain_sync_key_bundles_cb (SoupSession *session,
   JsonArray *array;
   JsonObjectIter iter;
   GError *error = NULL;
+  GList *managers = NULL;
   const char *member;
   const char *payload;
   char *record;
   guint8 *kB;
+  gboolean sync_finished = TRUE;
 
   service = ephy_shell_get_sync_service (ephy_shell_get_default ());
 
@@ -1615,11 +1652,20 @@ obtain_sync_key_bundles_cb (SoupSession *session,
     }
   }
 
-  /* Successfully retrieved key bundles, do sync. */
-  ephy_sync_service_do_sync (service);
-  service->source_id = g_timeout_add_seconds (SYNC_FREQUENCY,
-                                              ephy_sync_service_do_sync,
-                                              service);
+  /* Successfully retrieved key bundles, sync collections. */
+  managers = ephy_shell_get_synchronizable_managers (ephy_shell_get_default ());
+  if (managers) {
+    guint num_managers = g_list_length (managers);
+    guint index = 1;
+
+    for (GList *l = managers; l && l->data; l = l->next, index++)
+      ephy_sync_service_sync_collection (service,
+                                         EPHY_SYNCHRONIZABLE_MANAGER (l->data),
+                                         index, num_managers);
+
+    g_list_free (managers);
+    sync_finished = FALSE;
+  }
 
 free_record:
   g_free (record);
@@ -1629,6 +1675,9 @@ free_bundle:
 free_parser:
   g_object_unref (parser);
 out:
+  if (sync_finished)
+    g_signal_emit (service, signals[SYNC_FINISHED], 0);
+
   ephy_sync_service_send_next_storage_request (service);
 }
 
@@ -1644,22 +1693,20 @@ ephy_sync_service_obtain_sync_key_bundles (EphySyncService *self)
 }
 
 void
-ephy_sync_service_start_periodical_sync (EphySyncService *self)
+ephy_sync_service_do_sync (EphySyncService *self)
 {
   g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
   g_return_if_fail (ephy_sync_service_is_signed_in (self));
 
-  ephy_sync_service_obtain_sync_key_bundles (self);
+  ephy_sync_service_sync (self);
 }
 
 void
-ephy_sync_service_stop_periodical_sync (EphySyncService *self)
+ephy_sync_service_start_periodical_sync (EphySyncService *self)
 {
   g_return_if_fail (EPHY_IS_SYNC_SERVICE (self));
   g_return_if_fail (ephy_sync_service_is_signed_in (self));
 
-  if (self->source_id != 0) {
-    g_source_remove (self->source_id);
-    self->source_id = 0;
-  }
+  ephy_sync_service_sync (self);
+  ephy_sync_service_schedule_periodical_sync (self);
 }
diff --git a/src/sync/ephy-sync-service.h b/src/sync/ephy-sync-service.h
index f677ba4..1988457 100644
--- a/src/sync/ephy-sync-service.h
+++ b/src/sync/ephy-sync-service.h
@@ -45,10 +45,6 @@ void                 ephy_sync_service_set_token                  (EphySyncServi
                                                                    EphySyncTokenType          type);
 SyncCryptoKeyBundle *ephy_sync_service_get_key_bundle             (EphySyncService           *self,
                                                                    const char                *collection);
-void                 ephy_sync_service_register_manager           (EphySyncService           *self,
-                                                                   EphySynchronizableManager *manager);
-void                 ephy_sync_service_unregister_manager         (EphySyncService           *self,
-                                                                   EphySynchronizableManager *manager);
 void                 ephy_sync_service_clear_storage_credentials  (EphySyncService           *self);
 void                 ephy_sync_service_clear_tokens               (EphySyncService           *self);
 void                 ephy_sync_service_destroy_session            (EphySyncService           *self,
@@ -60,10 +56,10 @@ void                 ephy_sync_service_do_sign_in                 (EphySyncServi
                                                                    const char                *keyFetchToken,
                                                                    const char                *unwrapBKey);
 void                 ephy_sync_service_do_sign_out                (EphySyncService           *self);
+void                 ephy_sync_service_do_sync                    (EphySyncService           *self);
+void                 ephy_sync_service_start_periodical_sync      (EphySyncService           *self);
 void                 ephy_sync_service_delete_synchronizable      (EphySyncService           *self,
                                                                    EphySynchronizableManager *manager,
                                                                    EphySynchronizable        
*synchronizable);
-void                 ephy_sync_service_start_periodical_sync      (EphySyncService           *self);
-void                 ephy_sync_service_stop_periodical_sync       (EphySyncService           *self);
 
 G_END_DECLS


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