[evolution/gnome-3-26] [Composer] Better care of autosave snapshot after WebKitWebProcess crash



commit 7086967f836969b5c06e5a9b1fd9d689643dfda1
Author: Milan Crha <mcrha redhat com>
Date:   Thu Dec 7 18:41:39 2017 +0100

    [Composer] Better care of autosave snapshot after WebKitWebProcess crash
    
    This is related to bug 791291, but doesn't fix it, it rather makes sure that:
    a) any autosave snapshot is not overwritten with empty body of the message
    b) the snapshot is not deleted after "broken" composer close
    c) it's offered to recover the last snapshot (if exists) on close of the composer.
    
    This all makes sure that users will not lose all their work, only up
    to one minute of time, which is the interval of the autosave.

 src/composer/e-composer-actions.c                  |    4 +-
 src/composer/e-composer-private.h                  |    1 +
 src/composer/e-msg-composer.c                      |   22 +++++-
 src/e-util/e-content-editor.c                      |   28 ++++++
 src/e-util/e-content-editor.h                      |    1 +
 src/modules/composer-autosave/e-autosave-utils.c   |   28 ++++++
 src/modules/composer-autosave/e-autosave-utils.h   |    4 +
 .../composer-autosave/e-composer-autosave.c        |   91 +++++++++++++++++++-
 .../composer-autosave/e-composer-registry.c        |    2 +-
 src/modules/webkit-editor/e-webkit-editor.c        |   22 +++++
 10 files changed, 199 insertions(+), 4 deletions(-)
---
diff --git a/src/composer/e-composer-actions.c b/src/composer/e-composer-actions.c
index 0620237..501811c 100644
--- a/src/composer/e-composer-actions.c
+++ b/src/composer/e-composer-actions.c
@@ -58,8 +58,10 @@ static void
 action_close_cb (GtkAction *action,
                  EMsgComposer *composer)
 {
-       if (e_msg_composer_can_close (composer, TRUE))
+       if (e_msg_composer_can_close (composer, TRUE)) {
+               e_composer_emit_before_destroy (composer);
                gtk_widget_destroy (GTK_WIDGET (composer));
+       }
 }
 
 static void
diff --git a/src/composer/e-composer-private.h b/src/composer/e-composer-private.h
index f8a06b6..c590aae 100644
--- a/src/composer/e-composer-private.h
+++ b/src/composer/e-composer-private.h
@@ -142,6 +142,7 @@ gboolean    e_composer_selection_is_image_uris
                                                (EMsgComposer *composer,
                                                 GtkSelectionData *selection);
 void           e_composer_update_signature     (EMsgComposer *composer);
+void           e_composer_emit_before_destroy  (EMsgComposer *composer);
 
 G_END_DECLS
 
diff --git a/src/composer/e-msg-composer.c b/src/composer/e-msg-composer.c
index a71a7e1..5ac890f 100644
--- a/src/composer/e-msg-composer.c
+++ b/src/composer/e-msg-composer.c
@@ -101,6 +101,7 @@ enum {
        SAVE_TO_DRAFTS,
        SAVE_TO_OUTBOX,
        PRINT,
+       BEFORE_DESTROY,
        LAST_SIGNAL
 };
 
@@ -2746,6 +2747,23 @@ e_msg_composer_class_init (EMsgComposerClass *class)
                GTK_TYPE_PRINT_OPERATION_ACTION,
                CAMEL_TYPE_MIME_MESSAGE,
                E_TYPE_ACTIVITY);
+
+       signals[BEFORE_DESTROY] = g_signal_new (
+               "before-destroy",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_LAST,
+               0, NULL, NULL,
+               g_cclosure_marshal_VOID__VOID,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+}
+
+void
+e_composer_emit_before_destroy (EMsgComposer *composer)
+{
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+       g_signal_emit (composer, signals[BEFORE_DESTROY], 0);
 }
 
 static void
@@ -4016,6 +4034,7 @@ msg_composer_save_to_drafts_done_cb (gpointer user_data,
 
        if (e_msg_composer_is_exiting (composer) &&
            !e_content_editor_get_changed (cnt_editor)) {
+               e_composer_emit_before_destroy (composer);
                gtk_widget_destroy (GTK_WIDGET (composer));
        } else if (e_msg_composer_is_exiting (composer)) {
                gtk_widget_set_sensitive (GTK_WIDGET (composer), TRUE);
@@ -5366,7 +5385,8 @@ e_msg_composer_can_close (EMsgComposer *composer,
        if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
                return FALSE;
 
-       if (!e_content_editor_get_changed (cnt_editor))
+       if (!e_content_editor_get_changed (cnt_editor) ||
+           e_content_editor_is_malfunction (cnt_editor))
                return TRUE;
 
        window = gtk_widget_get_window (widget);
diff --git a/src/e-util/e-content-editor.c b/src/e-util/e-content-editor.c
index 7627aaa..d3df453 100644
--- a/src/e-util/e-content-editor.c
+++ b/src/e-util/e-content-editor.c
@@ -45,6 +45,22 @@ static void
 e_content_editor_default_init (EContentEditorInterface *iface)
 {
        /**
+        * EContentEditor:is-malfunction
+        *
+        * Determines whether the composer is malfunction. If it does, then
+        * the result of calling functions like get_content() is undefined.
+        */
+       g_object_interface_install_property (
+               iface,
+               g_param_spec_boolean (
+                       "is-malfunction",
+                       "Is Malfunction",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
         * EContentEditor:can-copy
         *
         * Determines whether it's possible to copy to clipboard. The action
@@ -595,6 +611,18 @@ e_content_editor_ref_spell_checker (EContentEditor *editor)
 }
 
 gboolean
+e_content_editor_is_malfunction (EContentEditor *editor)
+{
+       gboolean value = FALSE;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       g_object_get (G_OBJECT (editor), "is-malfunction", &value, NULL);
+
+       return value;
+}
+
+gboolean
 e_content_editor_can_cut (EContentEditor *editor)
 {
        gboolean value = FALSE;
diff --git a/src/e-util/e-content-editor.h b/src/e-util/e-content-editor.h
index e641190..11004e7 100644
--- a/src/e-util/e-content-editor.h
+++ b/src/e-util/e-content-editor.h
@@ -446,6 +446,7 @@ struct _EContentEditorInterface {
 
 ESpellChecker *        e_content_editor_ref_spell_checker
                                                (EContentEditor *editor);
+gboolean       e_content_editor_is_malfunction (EContentEditor *editor);
 gboolean       e_content_editor_can_cut        (EContentEditor *editor);
 gboolean       e_content_editor_can_copy       (EContentEditor *editor);
 gboolean       e_content_editor_can_paste      (EContentEditor *editor);
diff --git a/src/modules/composer-autosave/e-autosave-utils.c 
b/src/modules/composer-autosave/e-autosave-utils.c
index e4e307a..bd7035a 100644
--- a/src/modules/composer-autosave/e-autosave-utils.c
+++ b/src/modules/composer-autosave/e-autosave-utils.c
@@ -556,3 +556,31 @@ e_composer_get_snapshot_file (EMsgComposer *composer)
 
        return g_object_get_data (G_OBJECT (composer), SNAPSHOT_FILE_KEY);
 }
+
+void
+e_composer_prevent_snapshot_file_delete (EMsgComposer *composer)
+{
+       GFile *snapshot_file;
+
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+       snapshot_file = g_object_steal_data (G_OBJECT (composer), SNAPSHOT_FILE_KEY);
+       if (snapshot_file) {
+               g_object_set_data_full (G_OBJECT (composer), SNAPSHOT_FILE_KEY,
+                       snapshot_file, g_object_unref);
+       }
+}
+
+void
+e_composer_allow_snapshot_file_delete (EMsgComposer *composer)
+{
+       GFile *snapshot_file;
+
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+
+       snapshot_file = g_object_steal_data (G_OBJECT (composer), SNAPSHOT_FILE_KEY);
+       if (snapshot_file) {
+               g_object_set_data_full (G_OBJECT (composer), SNAPSHOT_FILE_KEY,
+                       snapshot_file, (GDestroyNotify) delete_snapshot_file);
+       }
+}
diff --git a/src/modules/composer-autosave/e-autosave-utils.h 
b/src/modules/composer-autosave/e-autosave-utils.h
index a27a877..8acd490 100644
--- a/src/modules/composer-autosave/e-autosave-utils.h
+++ b/src/modules/composer-autosave/e-autosave-utils.h
@@ -41,6 +41,10 @@ gboolean     e_composer_save_snapshot_finish (EMsgComposer *composer,
                                                 GAsyncResult *result,
                                                 GError **error);
 GFile *                e_composer_get_snapshot_file    (EMsgComposer *composer);
+void           e_composer_prevent_snapshot_file_delete
+                                               (EMsgComposer *composer);
+void           e_composer_allow_snapshot_file_delete
+                                               (EMsgComposer *composer);
 
 G_END_DECLS
 
diff --git a/src/modules/composer-autosave/e-composer-autosave.c 
b/src/modules/composer-autosave/e-composer-autosave.c
index 0e5705f..ab8a4f3 100644
--- a/src/modules/composer-autosave/e-composer-autosave.c
+++ b/src/modules/composer-autosave/e-composer-autosave.c
@@ -35,6 +35,9 @@ struct _EComposerAutosavePrivate {
 
        /* Prevent error dialogs from piling up. */
        gboolean error_shown;
+
+       GFile *malfunction_snapshot_file;
+       gboolean editor_is_malfunction;
 };
 
 G_DEFINE_DYNAMIC_TYPE (
@@ -95,6 +98,10 @@ composer_autosave_timeout_cb (gpointer user_data)
        EExtensible *extensible;
 
        autosave = E_COMPOSER_AUTOSAVE (user_data);
+
+       if (autosave->priv->editor_is_malfunction)
+               return FALSE;
+
        extensible = e_extension_get_extensible (E_EXTENSION (autosave));
 
        /* Cancel the previous snapshot if it's still in
@@ -126,7 +133,9 @@ composer_autosave_changed_cb (EComposerAutosave *autosave)
        editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible));
        cnt_editor = e_html_editor_get_content_editor (editor);
 
-       if (autosave->priv->timeout_id == 0 && e_content_editor_get_changed (cnt_editor)) {
+       if (autosave->priv->timeout_id == 0 &&
+           !autosave->priv->editor_is_malfunction &&
+           e_content_editor_get_changed (cnt_editor)) {
                autosave->priv->timeout_id = e_named_timeout_add_seconds (
                        AUTOSAVE_INTERVAL,
                        composer_autosave_timeout_cb, autosave);
@@ -134,6 +143,74 @@ composer_autosave_changed_cb (EComposerAutosave *autosave)
 }
 
 static void
+composer_autosave_editor_is_malfunction_cb (EComposerAutosave *autosave)
+{
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       EExtensible *extensible;
+
+       extensible = e_extension_get_extensible (E_EXTENSION (autosave));
+
+       editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       g_clear_object (&autosave->priv->malfunction_snapshot_file);
+       autosave->priv->editor_is_malfunction = e_content_editor_is_malfunction (cnt_editor);
+
+       if (autosave->priv->editor_is_malfunction) {
+               e_composer_prevent_snapshot_file_delete (E_MSG_COMPOSER (extensible));
+               autosave->priv->malfunction_snapshot_file = e_composer_get_snapshot_file (E_MSG_COMPOSER 
(extensible));
+               if (autosave->priv->malfunction_snapshot_file)
+                       g_object_ref (autosave->priv->malfunction_snapshot_file);
+       } else {
+               e_composer_allow_snapshot_file_delete (E_MSG_COMPOSER (extensible));
+               composer_autosave_changed_cb (autosave);
+       }
+}
+
+static void
+composer_autosave_recovered_cb (GObject *source_object,
+                               GAsyncResult *result,
+                               gpointer user_data)
+{
+       EMsgComposer *composer;
+       GError *local_error = NULL;
+
+       composer = e_composer_load_snapshot_finish (E_SHELL (source_object), result, &local_error);
+
+       if (local_error != NULL) {
+               /* FIXME Show an alert dialog here explaining
+                *       why we could not recover the message.
+                *       Will need a new error XML entry. */
+               g_warn_if_fail (composer == NULL);
+               g_warning ("%s: %s", G_STRFUNC, local_error->message);
+               g_error_free (local_error);
+       } else {
+               gtk_widget_show (GTK_WIDGET (composer));
+               g_object_unref (composer);
+       }
+}
+
+static void
+composer_autosave_msg_composer_before_destroy_cb (EMsgComposer *composer,
+                                                 gpointer user_data)
+{
+       EComposerAutosave *autosave = user_data;
+
+       g_return_if_fail (autosave != NULL);
+
+       if (autosave->priv->malfunction_snapshot_file) {
+               if (e_alert_run_dialog_for_args (GTK_WINDOW (composer), "mail-composer:recover-autosave", 
NULL) == GTK_RESPONSE_YES) {
+                       e_composer_load_snapshot (
+                               e_msg_composer_get_shell (composer), 
autosave->priv->malfunction_snapshot_file, NULL,
+                               composer_autosave_recovered_cb, NULL);
+               } else {
+                       g_file_delete (autosave->priv->malfunction_snapshot_file, NULL, NULL);
+               }
+       }
+}
+
+static void
 composer_autosave_dispose (GObject *object)
 {
        EComposerAutosavePrivate *priv;
@@ -150,6 +227,8 @@ composer_autosave_dispose (GObject *object)
 
        g_clear_object (&priv->cancellable);
 
+       g_clear_object (&priv->malfunction_snapshot_file);
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_composer_autosave_parent_class)->dispose (object);
 }
@@ -168,6 +247,14 @@ composer_autosave_constructed (GObject *object)
        editor = e_msg_composer_get_editor (E_MSG_COMPOSER (extensible));
        cnt_editor = e_html_editor_get_content_editor (editor);
 
+       g_signal_connect (
+               extensible, "before-destroy",
+               G_CALLBACK (composer_autosave_msg_composer_before_destroy_cb), object);
+
+       e_signal_connect_notify_swapped (
+               cnt_editor, "notify::is-malfunction",
+               G_CALLBACK (composer_autosave_editor_is_malfunction_cb), object);
+
        /* Do not use e_signal_connect_notify_swapped() here,
           this module relies on "false" change notifications. */
        g_signal_connect_swapped (
@@ -205,6 +292,8 @@ e_composer_autosave_init (EComposerAutosave *autosave)
 {
        autosave->priv = E_COMPOSER_AUTOSAVE_GET_PRIVATE (autosave);
        autosave->priv->cancellable = g_cancellable_new ();
+       autosave->priv->malfunction_snapshot_file = NULL;
+       autosave->priv->editor_is_malfunction = FALSE;
 }
 
 void
diff --git a/src/modules/composer-autosave/e-composer-registry.c 
b/src/modules/composer-autosave/e-composer-registry.c
index 29b2118..4ff50ce 100644
--- a/src/modules/composer-autosave/e-composer-registry.c
+++ b/src/modules/composer-autosave/e-composer-registry.c
@@ -62,7 +62,7 @@ composer_registry_recovered_cb (GObject *source_object,
                 *       why we could not recover the message.
                 *       Will need a new error XML entry. */
                g_warn_if_fail (composer == NULL);
-               g_warning ("%s", local_error->message);
+               g_warning ("%s: %s", G_STRFUNC, local_error->message);
                g_error_free (local_error);
                goto exit;
        }
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 48364bd..2353c14 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -38,6 +38,7 @@
 enum {
        PROP_0,
        PROP_WEB_EXTENSION, /* for test purposes */
+       PROP_IS_MALFUNCTION,
        PROP_CAN_COPY,
        PROP_CAN_CUT,
        PROP_CAN_PASTE,
@@ -143,6 +144,7 @@ struct _EWebKitEditorPrivate {
 
        EThreeState start_bottom;
        EThreeState top_signature;
+       gboolean is_malfunction;
 };
 
 static const GdkRGBA black = { 0, 0, 0, 1 };
@@ -253,6 +255,14 @@ webkit_editor_can_copy_cb (WebKitWebView *view,
 }
 
 static gboolean
+webkit_editor_is_malfunction (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->is_malfunction;
+}
+
+static gboolean
 webkit_editor_can_copy (EWebKitEditor *wk_editor)
 {
        g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
@@ -5517,6 +5527,12 @@ webkit_editor_get_property (GObject *object,
                                E_WEBKIT_EDITOR (object)));
                        return;
 
+               case PROP_IS_MALFUNCTION:
+                       g_value_set_boolean (
+                               value, webkit_editor_is_malfunction (
+                               E_WEBKIT_EDITOR (object)));
+                       return;
+
                case PROP_CAN_COPY:
                        g_value_set_boolean (
                                value, webkit_editor_can_copy (
@@ -6118,6 +6134,9 @@ webkit_editor_web_process_crashed_cb (EWebKitEditor *wk_editor)
 
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
+       wk_editor->priv->is_malfunction = TRUE;
+       g_object_notify (G_OBJECT (wk_editor), "is-malfunction");
+
        widget = GTK_WIDGET (wk_editor);
        while (widget) {
                if (E_IS_ALERT_SINK (widget)) {
@@ -6336,6 +6355,8 @@ e_webkit_editor_class_init (EWebKitEditorClass *class)
                        G_PARAM_STATIC_STRINGS));
 
        g_object_class_override_property (
+               object_class, PROP_IS_MALFUNCTION, "is-malfunction");
+       g_object_class_override_property (
                object_class, PROP_CAN_COPY, "can-copy");
        g_object_class_override_property (
                object_class, PROP_CAN_CUT, "can-cut");
@@ -6399,6 +6420,7 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
 
        /* To be able to cancel any pending calls when 'dispose' is called. */
        wk_editor->priv->cancellable = g_cancellable_new ();
+       wk_editor->priv->is_malfunction = FALSE;
        wk_editor->priv->spell_check_enabled = TRUE;
        wk_editor->priv->spell_checker = e_spell_checker_new ();
        wk_editor->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) g_variant_unref);


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