[evolution/gnome-3-32] I#343 - Composer autosave can overwrite backup with empty content
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/gnome-3-32] I#343 - Composer autosave can overwrite backup with empty content
- Date: Tue, 12 Mar 2019 13:07:50 +0000 (UTC)
commit 30303a645fb8145bbe281b7e2afb4a91af0abb8c
Author: Milan Crha <mcrha redhat com>
Date: Tue Mar 12 14:08:45 2019 +0100
I#343 - Composer autosave can overwrite backup with empty content
Closes https://gitlab.gnome.org/GNOME/evolution/issues/343
src/composer/e-msg-composer.c | 12 +++-
src/e-util/e-content-editor.c | 40 +++++++++++
src/e-util/e-content-editor.h | 3 +
src/e-util/e-misc-utils.c | 32 +++++++++
src/e-util/e-misc-utils.h | 6 ++
src/e-util/test-html-editor.c | 4 +-
src/modules/composer-autosave/e-autosave-utils.c | 79 ++++++++--------------
.../composer-autosave/e-composer-autosave.c | 30 ++++----
src/modules/webkit-editor/e-webkit-editor.c | 52 ++++++++++++--
9 files changed, 185 insertions(+), 73 deletions(-)
---
diff --git a/src/composer/e-msg-composer.c b/src/composer/e-msg-composer.c
index 098340e285..eedc538251 100644
--- a/src/composer/e-msg-composer.c
+++ b/src/composer/e-msg-composer.c
@@ -1249,6 +1249,7 @@ composer_build_message (EMsgComposer *composer,
gchar *charset, *message_uid;
const gchar *from_domain;
gint i;
+ GError *last_error = NULL;
e_msg_composer_inc_soft_busy (composer);
@@ -1420,6 +1421,8 @@ composer_build_message (EMsgComposer *composer,
if (!text) {
g_warning ("%s: Failed to retrieve text/plain processed content", G_STRFUNC);
text = g_strdup ("");
+
+ last_error = e_content_editor_dup_last_error (cnt_editor);
}
g_byte_array_append (data, (guint8 *) text, strlen (text));
@@ -1529,6 +1532,9 @@ composer_build_message (EMsgComposer *composer,
}
}
+ if (!last_error)
+ last_error = e_content_editor_dup_last_error (cnt_editor);
+
length = strlen (text);
g_byte_array_append (data, (guint8 *) text, (guint) length);
if (!g_str_has_suffix (text, "\r\n"))
@@ -1646,8 +1652,12 @@ composer_build_message (EMsgComposer *composer,
context->top_level_part = CAMEL_DATA_WRAPPER (multipart);
}
+ if (last_error) {
+ g_simple_async_result_take_error (simple, last_error);
+ g_simple_async_result_complete (simple);
+
/* Run any blocking operations in a separate thread. */
- if (context->need_thread) {
+ } else if (context->need_thread) {
if (!context->is_draft)
context->recipients_with_certificate =
composer_get_completed_recipients_with_certificate (composer);
diff --git a/src/e-util/e-content-editor.c b/src/e-util/e-content-editor.c
index ba70189963..de6a20f64e 100644
--- a/src/e-util/e-content-editor.c
+++ b/src/e-util/e-content-editor.c
@@ -493,6 +493,23 @@ e_content_editor_default_init (EContentEditorInterface *iface)
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+ /**
+ * EContentEditor:last-error:
+ *
+ * GError of the last operation; can be %NULL.
+ *
+ * Since: 3.32.1
+ */
+ g_object_interface_install_property (
+ iface,
+ g_param_spec_boxed (
+ "last-error",
+ NULL,
+ NULL,
+ G_TYPE_ERROR,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
/**
* EContentEditor:paste-clipboard
*
@@ -2158,6 +2175,29 @@ e_content_editor_is_ready (EContentEditor *editor)
return iface->is_ready (editor);
}
+GError *
+e_content_editor_dup_last_error (EContentEditor *editor)
+{
+ GError *last_error = NULL;
+
+ g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+
+ g_object_get (G_OBJECT (editor), "last-error", &last_error, NULL);
+
+ return last_error;
+}
+
+void
+e_content_editor_take_last_error (EContentEditor *editor,
+ GError *error)
+{
+ g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+ g_object_set (G_OBJECT (editor), "last-error", error, NULL);
+
+ g_clear_error (&error);
+}
+
gchar *
e_content_editor_insert_signature (EContentEditor *editor,
const gchar *content,
diff --git a/src/e-util/e-content-editor.h b/src/e-util/e-content-editor.h
index 49aa65886b..f1630522a2 100644
--- a/src/e-util/e-content-editor.h
+++ b/src/e-util/e-content-editor.h
@@ -637,6 +637,9 @@ gchar * e_content_editor_get_current_signature_uid
(EContentEditor *editor);
gboolean e_content_editor_is_ready (EContentEditor *editor);
+GError * e_content_editor_dup_last_error (EContentEditor *editor);
+void e_content_editor_take_last_error(EContentEditor *editor,
+ GError *error);
gchar * e_content_editor_insert_signature
(EContentEditor *editor,
diff --git a/src/e-util/e-misc-utils.c b/src/e-util/e-misc-utils.c
index afc80c465b..03f51e7235 100644
--- a/src/e-util/e-misc-utils.c
+++ b/src/e-util/e-misc-utils.c
@@ -3848,6 +3848,38 @@ e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (GDBusProxy *dbus_
return result;
}
+/**
+ * e_util_invoke_g_dbus_proxy_call_sync_wrapper:
+ * @dbus_proxy: a #GDBusProxy instance
+ * @method_name: a method name to invoke
+ * @parameters: (allow-none): parameters of the method, or %NULL
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: (allow-none): Return location for error, or %NULL
+ *
+ * Wraps GDBusProxy synchronous call into an asynchronous without blocking
+ * the main context. This can be useful when doing calls on a WebExtension,
+ * because it can avoid freeze when this is called in the UI process and
+ * the WebProcess also does its own IPC call.
+ *
+ * This function should be called only from the main thread.
+ *
+ * See e_util_invoke_g_dbus_proxy_call_sync_wrapper_full().
+ *
+ * Returns: (transfer full): The result of the method call, or %NULL on error. Free with g_variant_unref().
+ *
+ * Since: 3.32.1
+ **/
+GVariant *
+e_util_invoke_g_dbus_proxy_call_sync_wrapper (GDBusProxy *dbus_proxy,
+ const gchar *method_name,
+ GVariant *parameters,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return e_util_invoke_g_dbus_proxy_call_sync_wrapper_full (dbus_proxy, method_name, parameters,
+ G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error);
+}
+
static void
sync_wrapper_result_callback (GObject *source_object,
GAsyncResult *result,
diff --git a/src/e-util/e-misc-utils.h b/src/e-util/e-misc-utils.h
index 086505c68a..b10bdf5adf 100644
--- a/src/e-util/e-misc-utils.h
+++ b/src/e-util/e-misc-utils.h
@@ -324,6 +324,12 @@ GVariant * e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check
const gchar *method_name,
GVariant *parameters,
GCancellable *cancellable);
+GVariant * e_util_invoke_g_dbus_proxy_call_sync_wrapper
+ (GDBusProxy *dbus_proxy,
+ const gchar *method_name,
+ GVariant *parameters,
+ GCancellable *cancellable,
+ GError **error);
GVariant * e_util_invoke_g_dbus_proxy_call_sync_wrapper_full
(GDBusProxy *dbus_proxy,
const gchar *method_name,
diff --git a/src/e-util/test-html-editor.c b/src/e-util/test-html-editor.c
index 339658260d..81b58a4eb5 100644
--- a/src/e-util/test-html-editor.c
+++ b/src/e-util/test-html-editor.c
@@ -195,11 +195,11 @@ view_source_dialog (EHTMLEditor *editor,
content = gtk_text_view_new ();
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (content));
- gtk_text_buffer_set_text (buffer, html, -1);
+ gtk_text_buffer_set_text (buffer, html ? html : "", -1);
gtk_text_view_set_editable (GTK_TEXT_VIEW (content), FALSE);
} else {
content = webkit_web_view_new ();
- webkit_web_view_load_html (WEBKIT_WEB_VIEW (content), html, "evo-file://");
+ webkit_web_view_load_html (WEBKIT_WEB_VIEW (content), html ? html : "", "evo-file://");
}
g_free (html);
diff --git a/src/modules/composer-autosave/e-autosave-utils.c
b/src/modules/composer-autosave/e-autosave-utils.c
index bd7035a857..fabd89d019 100644
--- a/src/modules/composer-autosave/e-autosave-utils.c
+++ b/src/modules/composer-autosave/e-autosave-utils.c
@@ -38,7 +38,7 @@ struct _LoadContext {
struct _SaveContext {
GCancellable *cancellable;
- GOutputStream *output_stream;
+ GFile *snapshot_file;
};
static void
@@ -53,11 +53,8 @@ load_context_free (LoadContext *context)
static void
save_context_free (SaveContext *context)
{
- if (context->cancellable != NULL)
- g_object_unref (context->cancellable);
-
- if (context->output_stream != NULL)
- g_object_unref (context->output_stream);
+ g_clear_object (&context->cancellable);
+ g_clear_object (&context->snapshot_file);
g_slice_free (SaveContext, context);
}
@@ -241,11 +238,27 @@ write_message_to_stream_thread (GTask *task,
gpointer task_data,
GCancellable *cancellable)
{
+ GFileOutputStream *file_output_stream;
GOutputStream *output_stream;
+ GFile *snapshot_file;
gssize bytes_written;
GError *local_error = NULL;
- output_stream = task_data;
+ snapshot_file = task_data;
+
+ file_output_stream = g_file_replace (snapshot_file, NULL, FALSE,
+ G_FILE_CREATE_PRIVATE, cancellable, &local_error);
+
+ if (!file_output_stream) {
+ if (local_error)
+ g_task_return_error (task, local_error);
+ else
+ g_task_return_int (task, 0);
+
+ return;
+ }
+
+ output_stream = G_OUTPUT_STREAM (file_output_stream);
bytes_written = camel_data_wrapper_decode_to_output_stream_sync (
CAMEL_DATA_WRAPPER (source_object),
@@ -253,6 +266,8 @@ write_message_to_stream_thread (GTask *task,
g_output_stream_close (output_stream, cancellable, local_error ? NULL : &local_error);
+ g_object_unref (file_output_stream);
+
if (local_error != NULL) {
g_task_return_error (task, local_error);
} else {
@@ -287,7 +302,7 @@ save_snapshot_get_message_cb (EMsgComposer *composer,
task = g_task_new (message, context->cancellable, (GAsyncReadyCallback) save_snapshot_splice_cb,
simple);
- g_task_set_task_data (task, g_object_ref (context->output_stream), g_object_unref);
+ g_task_set_task_data (task, g_object_ref (context->snapshot_file), g_object_unref);
g_task_run_in_thread (task, write_message_to_stream_thread);
@@ -295,45 +310,6 @@ save_snapshot_get_message_cb (EMsgComposer *composer,
g_object_unref (message);
}
-static void
-save_snapshot_replace_cb (GFile *snapshot_file,
- GAsyncResult *result,
- GSimpleAsyncResult *simple)
-{
- GObject *object;
- SaveContext *context;
- GFileOutputStream *output_stream;
- GError *local_error = NULL;
-
- context = g_simple_async_result_get_op_res_gpointer (simple);
-
- /* Output stream might be NULL, so don't use cast macro. */
- output_stream = g_file_replace_finish (
- snapshot_file, result, &local_error);
- context->output_stream = (GOutputStream *) output_stream;
-
- if (local_error != NULL) {
- g_warn_if_fail (output_stream == NULL);
- g_simple_async_result_take_error (simple, local_error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- g_return_if_fail (G_IS_OUTPUT_STREAM (output_stream));
-
- /* g_async_result_get_source_object() returns a new reference. */
- object = g_async_result_get_source_object (G_ASYNC_RESULT (simple));
-
- /* Extract a MIME message from the composer. */
- e_msg_composer_get_message_draft (
- E_MSG_COMPOSER (object), G_PRIORITY_DEFAULT,
- context->cancellable, (GAsyncReadyCallback)
- save_snapshot_get_message_cb, simple);
-
- g_object_unref (object);
-}
-
static EMsgComposer *
composer_registry_lookup (GQueue *registry,
const gchar *basename)
@@ -524,11 +500,12 @@ e_composer_save_snapshot (EMsgComposer *composer,
g_return_if_fail (G_IS_FILE (snapshot_file));
- g_file_replace_async (
- snapshot_file, NULL, FALSE,
- G_FILE_CREATE_PRIVATE, G_PRIORITY_DEFAULT,
+ context->snapshot_file = g_object_ref (snapshot_file);
+
+ e_msg_composer_get_message_draft (
+ composer, G_PRIORITY_DEFAULT,
context->cancellable, (GAsyncReadyCallback)
- save_snapshot_replace_cb, simple);
+ save_snapshot_get_message_cb, simple);
}
gboolean
diff --git a/src/modules/composer-autosave/e-composer-autosave.c
b/src/modules/composer-autosave/e-composer-autosave.c
index 0b214fe081..a6787058c0 100644
--- a/src/modules/composer-autosave/e-composer-autosave.c
+++ b/src/modules/composer-autosave/e-composer-autosave.c
@@ -33,9 +33,6 @@ struct _EComposerAutosavePrivate {
GCancellable *cancellable;
guint timeout_id;
- /* Prevent error dialogs from piling up. */
- gboolean error_shown;
-
GFile *malfunction_snapshot_file;
gboolean editor_is_malfunction;
};
@@ -45,6 +42,8 @@ G_DEFINE_DYNAMIC_TYPE (
e_composer_autosave,
E_TYPE_EXTENSION)
+static void composer_autosave_changed_cb (EComposerAutosave *autosave);
+
static void
composer_autosave_finished_cb (GObject *source_object,
GAsyncResult *result,
@@ -66,6 +65,7 @@ composer_autosave_finished_cb (GObject *source_object,
g_error_free (local_error);
else if (local_error != NULL) {
+ EHTMLEditor *editor;
gchar *basename;
if (G_IS_FILE (snapshot_file))
@@ -73,19 +73,21 @@ composer_autosave_finished_cb (GObject *source_object,
else
basename = g_strdup (" ");
- /* Only show one error dialog at a time. */
- if (!autosave->priv->error_shown) {
- autosave->priv->error_shown = TRUE;
- e_alert_run_dialog_for_args (
- GTK_WINDOW (composer),
+ editor = e_msg_composer_get_editor (composer);
+
+ if (editor) {
+ e_alert_submit (
+ E_ALERT_SINK (editor),
"mail-composer:no-autosave",
basename, local_error->message, NULL);
- autosave->priv->error_shown = FALSE;
} else
g_warning ("%s: %s", basename, local_error->message);
g_free (basename);
g_error_free (local_error);
+
+ /* Re-schedule on error, maybe it'll work a bit later */
+ composer_autosave_changed_cb (autosave);
}
g_object_unref (autosave);
@@ -109,10 +111,8 @@ composer_autosave_timeout_cb (gpointer user_data)
composer = E_MSG_COMPOSER (extensible);
/* Do not do anything when it's busy */
- if (e_msg_composer_is_soft_busy (composer)) {
- autosave->priv->timeout_id = 0;
- return FALSE;
- }
+ if (e_msg_composer_is_soft_busy (composer))
+ return TRUE;
/* Cancel the previous snapshot if it's still in
* progress and start a new snapshot operation. */
@@ -120,14 +120,14 @@ composer_autosave_timeout_cb (gpointer user_data)
g_object_unref (autosave->priv->cancellable);
autosave->priv->cancellable = g_cancellable_new ();
+ autosave->priv->timeout_id = 0;
+
e_composer_save_snapshot (
composer,
autosave->priv->cancellable,
composer_autosave_finished_cb,
g_object_ref (autosave));
- autosave->priv->timeout_id = 0;
-
return FALSE;
}
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 0b8f67f389..9ab8734158 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -52,6 +52,7 @@ enum {
PROP_START_BOTTOM,
PROP_TOP_SIGNATURE,
PROP_VISUALLY_WRAP_LONG_LINES,
+ PROP_LAST_ERROR,
PROP_ALIGNMENT,
PROP_BACKGROUND_COLOR,
@@ -148,6 +149,8 @@ struct _EWebKitEditorPrivate {
EThreeState start_bottom;
EThreeState top_signature;
gboolean is_malfunction;
+
+ GError *last_error;
};
static const GdkRGBA black = { 0, 0, 0, 1 };
@@ -191,6 +194,26 @@ e_webkit_editor_new (void)
return g_object_new (E_TYPE_WEBKIT_EDITOR, NULL);
}
+static void
+webkit_editor_set_last_error (EWebKitEditor *wk_editor,
+ const GError *error)
+{
+ g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+ g_clear_error (&wk_editor->priv->last_error);
+
+ if (error)
+ wk_editor->priv->last_error = g_error_copy (error);
+}
+
+static const GError *
+webkit_editor_get_last_error (EWebKitEditor *wk_editor)
+{
+ g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
+
+ return wk_editor->priv->last_error;
+}
+
static void
webkit_editor_can_paste_cb (WebKitWebView *view,
GAsyncResult *result,
@@ -2038,10 +2061,11 @@ webkit_editor_get_content (EContentEditor *editor,
{
EWebKitEditor *wk_editor;
GVariant *result;
+ GError *local_error = NULL;
wk_editor = E_WEBKIT_EDITOR (editor);
if (!wk_editor->priv->web_extension)
- return g_strdup ("");
+ return NULL;
if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) &&
!(flags & E_CONTENT_EDITOR_GET_PROCESSED) &&
@@ -2055,7 +2079,7 @@ webkit_editor_get_content (EContentEditor *editor,
wk_editor->priv->current_user_stylesheet),
wk_editor->priv->cancellable);
- result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
+ result = e_util_invoke_g_dbus_proxy_call_sync_wrapper (
wk_editor->priv->web_extension,
"DOMGetContent",
g_variant_new (
@@ -2063,7 +2087,11 @@ webkit_editor_get_content (EContentEditor *editor,
current_page_id (wk_editor),
inline_images_from_domain ? inline_images_from_domain : "",
(gint32) flags),
- NULL);
+ wk_editor->priv->cancellable,
+ &local_error);
+
+ webkit_editor_set_last_error (wk_editor, local_error);
+ g_clear_error (&local_error);
if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) &&
!(flags & E_CONTENT_EDITOR_GET_PROCESSED) &&
@@ -2087,7 +2115,7 @@ webkit_editor_get_content (EContentEditor *editor,
return value;
}
- return g_strdup ("");
+ return NULL;
}
static gboolean
@@ -5473,6 +5501,7 @@ webkit_editor_finalize (GObject *object)
g_clear_object (&priv->spell_checker);
g_clear_object (&priv->cancellable);
+ g_clear_error (&priv->last_error);
g_free (priv->font_name);
@@ -5620,6 +5649,12 @@ webkit_editor_set_property (GObject *object,
E_WEBKIT_EDITOR (object),
g_value_get_boolean (value));
return;
+
+ case PROP_LAST_ERROR:
+ webkit_editor_set_last_error (
+ E_WEBKIT_EDITOR (object),
+ g_value_get_boxed (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -5831,6 +5866,13 @@ webkit_editor_get_property (GObject *object,
webkit_editor_get_visually_wrap_long_lines (
E_WEBKIT_EDITOR (object)));
return;
+
+ case PROP_LAST_ERROR:
+ g_value_set_boxed (
+ value,
+ webkit_editor_get_last_error (
+ E_WEBKIT_EDITOR (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -6548,6 +6590,8 @@ e_webkit_editor_class_init (EWebKitEditorClass *class)
object_class, PROP_SPELL_CHECK_ENABLED, "spell-check-enabled");
g_object_class_override_property (
object_class, PROP_VISUALLY_WRAP_LONG_LINES, "visually-wrap-long-lines");
+ g_object_class_override_property (
+ object_class, PROP_LAST_ERROR, "last-error");
g_object_class_override_property (
object_class, PROP_SPELL_CHECKER, "spell-checker");
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]