Commit a1798c9e authored by Milan Crha's avatar Milan Crha

I#343 - Composer autosave can overwrite backup with empty content

Closes #343
parent 82bb43d0
......@@ -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);
......
......@@ -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.34
*/
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,
......
......@@ -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,
......
......@@ -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.34
**/
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,
......
......@@ -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,
......
......@@ -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);
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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");
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment