1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3
* Copyright © 2002 Jorn Baayen
4
* Copyright © 2003, 2004 Marco Pesenti Gritti
5
* Copyright © 2003, 2004, 2005, 2006, 2008 Christian Persch
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2, or (at your option)
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24
#include "ephy-session.h"
26
#include "eggdesktopfile.h"
27
#include "eggsmclient.h"
28
#include "ephy-bookmarks-editor.h"
29
#include "ephy-debug.h"
30
#include "ephy-embed.h"
31
#include "ephy-embed-utils.h"
32
#include "ephy-embed-container.h"
33
#include "ephy-extension.h"
34
#include "ephy-file-helpers.h"
36
#include "ephy-history-window.h"
37
#include "ephy-notebook.h"
38
#include "ephy-prefs.h"
39
#include "ephy-settings.h"
40
#include "ephy-shell.h"
41
#include "ephy-stock-icons.h"
42
#include "ephy-window.h"
44
#include <glib/gi18n.h>
47
#include <libxml/tree.h>
48
#include <libxml/xmlwriter.h>
56
EphySessionCommand command;
62
#define EPHY_SESSION_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_SESSION, EphySessionPrivate))
64
struct _EphySessionPrivate
68
GtkWidget *resume_infobar;
69
GtkWidget *resume_window;
74
GtkWidget *quit_interact_dialog;
77
guint quit_while_resuming : 1;
78
guint loading_homepage : 1;
81
#define BOOKMARKS_EDITOR_ID "BookmarksEditor"
82
#define HISTORY_WINDOW_ID "HistoryWindow"
83
#define SESSION_CRASHED "type:session_crashed"
85
static void ephy_session_class_init (EphySessionClass *klass);
86
static void ephy_session_iface_init (EphyExtensionIface *iface);
87
static void ephy_session_init (EphySession *session);
88
static void session_command_queue_next (EphySession *session);
96
G_DEFINE_TYPE_WITH_CODE (EphySession, ephy_session, G_TYPE_OBJECT,
97
G_IMPLEMENT_INTERFACE (EPHY_TYPE_EXTENSION,
98
ephy_session_iface_init))
100
/* Gnome session client */
104
EphySession *session;
105
EggSMClient *sm_client;
114
confirm_shutdown_dialog_update_timeout_label (InteractData *data)
118
text = g_strdup_printf (ngettext ("Downloads will be aborted and logout proceed in %d second.",
119
"Downloads will be aborted and logout proceed in %d seconds.",
123
gtk_label_set_text (GTK_LABEL (data->label), text);
128
confirm_shutdown_dialog_tick_cb (InteractData *data)
133
confirm_shutdown_dialog_update_timeout_label (data);
137
data->timeout_id = 0;
138
gtk_dialog_response (GTK_DIALOG (data->dialog),
139
GTK_RESPONSE_ACCEPT);
144
confirm_shutdown_dialog_response_cb (GtkWidget *dialog,
148
LOG ("confirm_shutdown_dialog_response_cb response %d", response);
150
data->response = response;
152
gtk_widget_destroy (dialog);
156
confirm_shutdown_dialog_accept_cb (InteractData *data,
159
gtk_dialog_response (GTK_DIALOG (data->dialog),
160
GTK_RESPONSE_ACCEPT);
164
confirm_shutdown_dialog_weak_ref_cb (InteractData *data,
167
EphySessionPrivate *priv = data->session->priv;
168
EggSMClient *sm_client = data->sm_client;
172
LOG ("confirm_shutdown_dialog_weak_ref_cb response %d", data->response);
174
priv->quit_interact_dialog = NULL;
176
shell = ephy_shell_get_default ();
179
g_object_weak_unref (G_OBJECT (shell),
180
(GWeakNotify) confirm_shutdown_dialog_accept_cb,
184
if (data->timeout_id != 0)
186
g_source_remove (data->timeout_id);
189
will_quit = data->response == GTK_RESPONSE_ACCEPT;
193
egg_sm_client_will_quit (sm_client, will_quit);
194
g_object_unref (sm_client);
198
client_quit_requested_cb (EggSMClient *sm_client,
199
EphySession *session)
201
EphySessionPrivate *priv = session->priv;
202
GtkWidget *dialog, *box;
206
/* If we're shutting down, check if there are downloads
207
* remaining, since they can't be restarted.
210
downloads = ephy_embed_shell_get_downloads (embed_shell);
211
if (ephy_shell_get_default () == NULL || g_list_length (downloads) == 0)
213
egg_sm_client_will_quit (sm_client, TRUE);
217
dialog = gtk_message_dialog_new
222
_("Abort pending downloads?"));
223
priv->quit_interact_dialog = dialog;
225
gtk_message_dialog_format_secondary_text
226
(GTK_MESSAGE_DIALOG (dialog),
227
_("There are still downloads pending. If you log out, "
228
"they will be aborted and lost."));
230
gtk_dialog_add_button (GTK_DIALOG (dialog),
231
_("_Cancel Logout"), GTK_RESPONSE_REJECT);
232
gtk_dialog_add_button (GTK_DIALOG (dialog),
233
_("_Abort Downloads"), GTK_RESPONSE_ACCEPT);
235
gtk_window_set_title (GTK_WINDOW (dialog), "");
236
gtk_window_set_icon_name (GTK_WINDOW (dialog), EPHY_STOCK_EPHY);
237
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
238
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT);
240
data = g_new (InteractData, 1);
241
data->sm_client = g_object_ref (sm_client);
242
data->session = session;
243
data->dialog = dialog;
244
data->response = GTK_RESPONSE_REJECT;
246
/* This isn't very exact, but it's good enough here */
247
data->timeout_id = g_timeout_add_seconds (1,
248
(GSourceFunc) confirm_shutdown_dialog_tick_cb,
252
/* Add timeout label */
253
data->label = gtk_label_new (NULL);
254
gtk_label_set_line_wrap (GTK_LABEL (data->label), TRUE);
255
confirm_shutdown_dialog_update_timeout_label (data);
257
box = ephy_gui_message_dialog_get_content_box (dialog);
258
gtk_box_pack_end (GTK_BOX (box), data->label, FALSE, FALSE, 0);
259
gtk_widget_show (data->label);
261
/* When we're quitting, un-veto the shutdown */
262
g_object_weak_ref (G_OBJECT (ephy_shell_get_default ()),
263
(GWeakNotify) confirm_shutdown_dialog_accept_cb,
266
g_signal_connect (dialog, "response",
267
G_CALLBACK (confirm_shutdown_dialog_response_cb), data);
268
g_object_weak_ref (G_OBJECT (dialog),
269
(GWeakNotify) confirm_shutdown_dialog_weak_ref_cb,
272
gtk_window_present (GTK_WINDOW (dialog));
276
client_quit_cancelled_cb (EggSMClient *sm_client,
277
EphySession *session)
279
EphySessionPrivate *priv = session->priv;
281
if (priv->quit_interact_dialog)
283
gtk_dialog_response (GTK_DIALOG (priv->quit_interact_dialog),
284
GTK_RESPONSE_DELETE_EVENT);
289
client_quit_cb (EggSMClient *sm_client,
290
EphySession *session)
294
ephy_session_close (session);
298
client_save_state_cb (EggSMClient *sm_client,
300
EphySession *session)
302
char *argv[] = { NULL, "--load-session", NULL };
303
char *discard_argv[] = { "rm", "-f", NULL };
306
LOG ("save_yourself_cb");
308
tmp = g_build_filename (ephy_dot_dir (),
309
"session_gnome-XXXXXX",
311
save_to = ephy_file_tmp_filename (tmp, "xml");
314
argv[0] = g_get_prgname ();
316
egg_sm_client_set_restart_command (sm_client, 3, (const char **) argv);
318
discard_argv[2] = save_to;
319
egg_sm_client_set_discard_command (sm_client, 3, (const char **) discard_argv);
321
ephy_session_save (session, save_to);
326
/* Helper functions */
329
get_session_file (const char *filename)
334
if (filename == NULL)
339
if (strcmp (filename, SESSION_CRASHED) == 0)
341
path = g_build_filename (ephy_dot_dir (),
342
"session_crashed.xml",
347
path = g_strdup (filename);
350
file = g_file_new_for_path (path);
357
session_delete (EphySession *session,
358
const char *filename)
362
file = get_session_file (filename);
364
g_file_delete (file, NULL, NULL);
365
g_object_unref (file);
369
load_status_notify_cb (EphyWebView *view,
371
EphySession *session)
373
WebKitLoadStatus status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view));
375
/* We won't know the URL we are loading in PROVISIONAL because
376
of bug #593149, but save session anyway */
377
if (status == WEBKIT_LOAD_PROVISIONAL ||
378
status == WEBKIT_LOAD_COMMITTED ||
379
status == WEBKIT_LOAD_FINISHED)
380
ephy_session_save (session, SESSION_CRASHED);
384
notebook_page_added_cb (GtkWidget *notebook,
387
EphySession *session)
389
g_signal_connect (ephy_embed_get_web_view (embed), "notify::load-status",
390
G_CALLBACK (load_status_notify_cb), session);
394
notebook_page_removed_cb (GtkWidget *notebook,
397
EphySession *session)
399
ephy_session_save (session, SESSION_CRASHED);
401
g_signal_handlers_disconnect_by_func
402
(ephy_embed_get_web_view (embed), G_CALLBACK (load_status_notify_cb),
407
notebook_page_reordered_cb (GtkWidget *notebook,
410
EphySession *session)
412
ephy_session_save (session, SESSION_CRASHED);
416
window_focus_in_event_cb (EphyWindow *window,
417
GdkEventFocus *event,
418
EphySession *session)
420
LOG ("focus-in-event for window %p", window);
422
g_return_val_if_fail (g_list_find (session->priv->windows, window) != NULL, FALSE);
424
/* move the active window to the front of the list */
425
session->priv->windows = g_list_remove (session->priv->windows, window);
426
session->priv->windows = g_list_prepend (session->priv->windows, window);
428
g_object_notify (G_OBJECT (session), "active-window");
430
/* propagate event */
437
session_command_free (SessionCommand *cmd)
439
g_assert (cmd != NULL);
444
g_strfreev (cmd->args);
449
g_object_unref (ephy_shell_get_default ());
453
session_command_find (const SessionCommand *cmd,
456
EphySessionCommand command = GPOINTER_TO_INT (cmdptr);
458
return command != cmd->command;
462
resume_infobar_response_cb (GtkWidget *info_bar,
464
EphySession *session)
467
EphySessionPrivate *priv = session->priv;
469
LOG ("resume_infobar_response_cb response:%d", response);
471
gtk_widget_hide (info_bar);
473
user_time = gtk_get_current_event_time ();
475
priv->dont_save = FALSE;
477
gtk_widget_destroy (info_bar);
478
priv->resume_infobar = NULL;
480
if (response == GTK_RESPONSE_YES)
482
ephy_session_queue_command (session,
483
EPHY_SESSION_CMD_LOAD_SESSION,
484
SESSION_CRASHED, NULL,
490
resume_infobar_weak_ref_cb (EphySession *session,
493
EphySessionPrivate *priv = session->priv;
495
LOG ("resume_window_infobar_weak_ref_cb");
497
priv->dont_save = FALSE;
498
session_command_queue_next (session);
502
session_command_autoresume (EphySession *session,
505
EphySessionPrivate *priv = session->priv;
506
GFile *saved_session_file;
507
char *saved_session_file_path;
508
gboolean crashed_session;
510
LOG ("ephy_session_autoresume");
512
saved_session_file = get_session_file (SESSION_CRASHED);
513
saved_session_file_path = g_file_get_path (saved_session_file);
514
g_object_unref (saved_session_file);
515
crashed_session = g_file_test (saved_session_file_path, G_FILE_TEST_EXISTS);
517
g_free (saved_session_file_path);
519
if (crashed_session == FALSE ||
520
priv->windows != NULL ||
521
priv->tool_windows != NULL)
523
/* FIXME can this happen? */
524
if (priv->resume_infobar != NULL)
526
gtk_widget_hide (priv->resume_infobar);
527
gtk_widget_destroy (priv->resume_infobar);
530
ephy_session_queue_command (session,
531
EPHY_SESSION_CMD_MAYBE_OPEN_WINDOW,
532
NULL, NULL, user_time, FALSE);
537
if (priv->resume_window)
539
gtk_window_present_with_time (GTK_WINDOW (priv->resume_window),
545
ephy_session_queue_command (session,
546
EPHY_SESSION_CMD_MAYBE_OPEN_WINDOW_RESTORE,
547
NULL, NULL, user_time, FALSE);
551
session_command_open_bookmarks_editor (EphySession *session,
556
editor = ephy_shell_get_bookmarks_editor (ephy_shell_get_default ());
558
gtk_window_present_with_time (GTK_WINDOW (editor), user_time);
562
session_command_open_uris (EphySession *session,
570
EphySessionPrivate *priv;
571
EphyNewTabFlags flags = 0;
574
priv = session->priv;
576
shell = ephy_shell_get_default ();
578
g_object_ref (shell);
580
window = ephy_session_get_active_window (session);
582
if (options != NULL && strstr (options, "external") != NULL)
584
flags |= EPHY_NEW_TAB_FROM_EXTERNAL;
586
if (options != NULL && strstr (options, "new-window") != NULL)
589
flags |= EPHY_NEW_TAB_IN_NEW_WINDOW;
591
else if (options != NULL && strstr (options, "new-tab") != NULL)
593
flags |= EPHY_NEW_TAB_IN_EXISTING_WINDOW |
597
for (i = 0; uris[i] != NULL; ++i)
599
const char *url = uris[i];
600
EphyNewTabFlags page_flags;
601
WebKitNetworkRequest *request = NULL;
605
page_flags = EPHY_NEW_TAB_HOME_PAGE;
609
page_flags = EPHY_NEW_TAB_OPEN_PAGE;
610
request = webkit_network_request_new (url);
613
/* For the first URI, if we have a valid recovery
614
* window, reuse the already existing embed instead of
615
* creating a new one */
616
if (i == 0 && priv->resume_window != NULL)
618
EphyWebView *web_view;
620
embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (priv->resume_window));
621
web_view = ephy_embed_get_web_view (embed);
622
ephy_web_view_load_url (web_view, url);
626
embed = ephy_shell_new_tab_full (shell, window,
627
NULL /* parent tab */,
630
EPHY_WEB_VIEW_CHROME_ALL,
631
FALSE /* is popup? */,
636
g_object_unref (request);
638
window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed)));
641
g_object_unref (shell);
645
send_no_response_cb (GtkButton *button, GtkInfoBar *info_bar)
647
gtk_info_bar_response (info_bar, GTK_RESPONSE_NO);
651
send_yes_response_cb (GtkButton *button, GtkInfoBar *info_bar)
653
gtk_info_bar_response (info_bar, GTK_RESPONSE_YES);
657
loading_homepage_cb (EphyWebView *view, EphySession *session)
659
EphySessionPrivate *priv = session->priv;
661
priv->loading_homepage = TRUE;
665
new_document_now_cb (EphyWebView *view, const char *uri, EphySession *session)
667
EphySessionPrivate *priv = session->priv;
669
if (priv->loading_homepage)
671
priv->loading_homepage = FALSE;
675
if (priv->resume_infobar)
677
EphyEmbed *embed = EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (view);
678
ephy_embed_remove_top_widget (embed, priv->resume_infobar);
679
priv->resume_infobar = NULL;
680
priv->resume_window = NULL;
685
session_command_dispatch (EphySession *session)
687
EphySessionPrivate *priv = session->priv;
689
gboolean run_again = TRUE;
691
cmd = g_queue_pop_head (priv->queue);
692
g_assert (cmd != NULL);
694
LOG ("dispatching queue cmd:%d", cmd->command);
696
switch (cmd->command)
698
case EPHY_SESSION_CMD_RESUME_SESSION:
699
session_command_autoresume (session, cmd->user_time);
701
case EPHY_SESSION_CMD_LOAD_SESSION:
702
ephy_session_load (session, cmd->arg, cmd->user_time);
704
case EPHY_SESSION_CMD_OPEN_BOOKMARKS_EDITOR:
705
session_command_open_bookmarks_editor (session, cmd->user_time);
707
case EPHY_SESSION_CMD_OPEN_URIS:
708
session_command_open_uris (session, cmd->args, cmd->arg, cmd->user_time);
710
case EPHY_SESSION_CMD_MAYBE_OPEN_WINDOW:
711
/* FIXME: maybe just check for normal windows? */
712
if (priv->windows == NULL &&
713
priv->tool_windows == NULL)
715
ephy_shell_new_tab_full (ephy_shell_get_default (),
716
NULL /* window */, NULL /* tab */,
717
NULL /* NetworkRequest */,
718
EPHY_NEW_TAB_IN_NEW_WINDOW |
719
EPHY_NEW_TAB_HOME_PAGE,
720
EPHY_WEB_VIEW_CHROME_ALL,
721
FALSE /* is popup? */,
725
case EPHY_SESSION_CMD_MAYBE_OPEN_WINDOW_RESTORE:
726
/* FIXME: maybe just check for normal windows? */
727
if (priv->windows == NULL &&
728
priv->tool_windows == NULL)
731
GtkWidget *action_area;
732
GtkWidget *button_box;
733
GtkWidget *action_button;
734
GtkWidget *content_area;
739
session->priv->dont_save = TRUE;
741
embed = ephy_shell_new_tab_full (ephy_shell_get_default (),
742
NULL /* window */, NULL /* tab */,
743
NULL /* Networ kRequest */,
744
EPHY_NEW_TAB_IN_NEW_WINDOW |
745
EPHY_NEW_TAB_HOME_PAGE,
746
EPHY_WEB_VIEW_CHROME_ALL,
747
FALSE /* is popup? */,
750
info_bar = gtk_info_bar_new ();
752
session->priv->resume_infobar = info_bar;
753
session->priv->resume_window = gtk_widget_get_toplevel (GTK_WIDGET (embed));
755
action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar));
756
button_box = gtk_hbutton_box_new ();
757
gtk_container_add (GTK_CONTAINER (action_area), button_box);
759
action_button = gtk_button_new_with_label (_("Don't recover"));
760
g_signal_connect (action_button, "clicked",
761
G_CALLBACK (send_no_response_cb), info_bar);
762
gtk_box_pack_start (GTK_BOX (button_box), action_button, FALSE, FALSE, 0);
764
action_button = gtk_button_new_with_label (_("Recover session"));
765
g_signal_connect (action_button, "clicked",
766
G_CALLBACK (send_yes_response_cb), info_bar);
767
gtk_box_pack_start (GTK_BOX (button_box), action_button, FALSE, FALSE, 0);
769
label = gtk_label_new (_("Do you want to recover the previous browser windows and tabs?"));
770
content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
771
gtk_container_add (GTK_CONTAINER (content_area), label);
772
gtk_widget_show_all (info_bar);
774
g_signal_connect (info_bar, "response",
775
G_CALLBACK (resume_infobar_response_cb), session);
777
g_object_weak_ref (G_OBJECT (info_bar),
778
(GWeakNotify) resume_infobar_weak_ref_cb,
781
ephy_embed_add_top_widget (embed, info_bar, FALSE);
783
view = ephy_embed_get_web_view (embed);
785
g_signal_connect (view, "loading-homepage",
786
G_CALLBACK (loading_homepage_cb), session);
788
g_signal_connect (view, "new-document-now",
789
G_CALLBACK (new_document_now_cb), session);
793
g_assert_not_reached ();
797
/* Look if there's anything else to dispatch */
798
if (g_queue_is_empty (priv->queue) ||
799
priv->resume_window != NULL)
801
priv->queue_idle_id = 0;
805
/* This unrefs the shell! */
806
session_command_free (cmd);
812
session_command_queue_next (EphySession *session)
814
EphySessionPrivate *priv = session->priv;
818
if (!g_queue_is_empty (priv->queue) &&
819
priv->resume_infobar == NULL &&
820
priv->queue_idle_id == 0)
822
priv->queue_idle_id =
823
g_idle_add ((GSourceFunc) session_command_dispatch,
829
session_command_queue_clear (EphySession *session)
831
EphySessionPrivate *priv = session->priv;
833
if (priv->queue_idle_id != 0)
835
g_source_remove (priv->queue_idle_id);
836
priv->queue_idle_id = 0;
839
if (priv->queue != NULL)
841
g_queue_foreach (priv->queue, (GFunc) session_command_free, NULL);
842
g_queue_free (priv->queue);
847
/* EphyExtensionIface implementation */
850
impl_attach_window (EphyExtension *extension,
853
EphySession *session = EPHY_SESSION (extension);
856
LOG ("impl_attach_window");
858
session->priv->windows = g_list_append (session->priv->windows, window);
859
ephy_session_save (session, SESSION_CRASHED);
861
g_signal_connect (window, "focus-in-event",
862
G_CALLBACK (window_focus_in_event_cb), session);
864
notebook = ephy_window_get_notebook (window);
865
g_signal_connect (notebook, "page-added",
866
G_CALLBACK (notebook_page_added_cb), session);
867
g_signal_connect (notebook, "page-removed",
868
G_CALLBACK (notebook_page_removed_cb), session);
869
g_signal_connect (notebook, "page-reordered",
870
G_CALLBACK (notebook_page_reordered_cb), session);
872
/* Set unique identifier as role, so that on restore, the WM can
873
* place the window on the right workspace
876
if (gtk_window_get_role (GTK_WINDOW (window)) == NULL)
878
/* I guess rand() is unique enough, otherwise we could use
879
* time + pid or something
883
role = g_strdup_printf ("epiphany-window-%x", rand());
884
gtk_window_set_role (GTK_WINDOW (window), role);
890
impl_detach_window (EphyExtension *extension,
893
EphySession *session = EPHY_SESSION (extension);
895
LOG ("impl_detach_window");
897
session->priv->windows = g_list_remove (session->priv->windows, window);
898
ephy_session_save (session, SESSION_CRASHED);
900
/* NOTE: since the window will be destroyed anyway, we don't need to
901
* disconnect our signal handlers from its components.
905
/* Class implementation */
908
ephy_session_init (EphySession *session)
910
EphySessionPrivate *priv;
911
EggSMClient *sm_client;
913
LOG ("EphySession initialising");
915
priv = session->priv = EPHY_SESSION_GET_PRIVATE (session);
917
priv->queue = g_queue_new ();
919
sm_client = egg_sm_client_get ();
920
g_signal_connect (sm_client, "save-state",
921
G_CALLBACK (client_save_state_cb), session);
922
g_signal_connect (sm_client, "quit-requested",
923
G_CALLBACK (client_quit_requested_cb), session);
924
g_signal_connect (sm_client, "quit-cancelled",
925
G_CALLBACK (client_quit_cancelled_cb), session);
926
g_signal_connect (sm_client, "quit",
927
G_CALLBACK (client_quit_cb), session);
931
ephy_session_dispose (GObject *object)
933
EphySession *session = EPHY_SESSION (object);
934
EphySessionPrivate *priv = session->priv;
935
EggSMClient *sm_client;
937
LOG ("EphySession disposing");
939
/* Only remove the crashed session if we're not shutting down while
940
* the session resume dialogue was still shown!
942
if (priv->quit_while_resuming == FALSE)
944
session_delete (session, SESSION_CRASHED);
947
session_command_queue_clear (session);
949
sm_client = egg_sm_client_get ();
950
g_signal_handlers_disconnect_matched (sm_client, G_SIGNAL_MATCH_DATA,
951
0, 0, NULL, NULL, session);
953
G_OBJECT_CLASS (ephy_session_parent_class)->dispose (object);
957
ephy_session_finalize (GObject *object)
959
EphySession *session = EPHY_SESSION (object);
961
LOG ("EphySession finalising");
963
/* FIXME: those should be NULL already!? */
964
g_list_free (session->priv->windows);
965
g_list_free (session->priv->tool_windows);
967
G_OBJECT_CLASS (ephy_session_parent_class)->finalize (object);
971
ephy_session_iface_init (EphyExtensionIface *iface)
973
iface->attach_window = impl_attach_window;
974
iface->detach_window = impl_detach_window;
978
ephy_session_set_property (GObject *object,
983
/* no writeable properties */
984
g_return_if_reached ();
988
ephy_session_get_property (GObject *object,
993
EphySession *session = EPHY_SESSION (object);
997
case PROP_ACTIVE_WINDOW:
998
g_value_set_object (value, ephy_session_get_active_window (session));
1001
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1006
ephy_session_class_init (EphySessionClass *class)
1008
GObjectClass *object_class = G_OBJECT_CLASS (class);
1010
object_class->dispose = ephy_session_dispose;
1011
object_class->finalize = ephy_session_finalize;
1012
object_class->get_property = ephy_session_get_property;
1013
object_class->set_property = ephy_session_set_property;
1015
g_object_class_install_property
1018
g_param_spec_object ("active-window",
1020
"The active window",
1022
G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
1024
g_type_class_add_private (object_class, sizeof (EphySessionPrivate));
1027
/* Implementation */
1030
close_dialog (GtkWidget *widget)
1032
if (GTK_IS_DIALOG (widget))
1034
/* don't destroy them, someone might have a ref on them */
1035
gtk_dialog_response (GTK_DIALOG (widget),
1036
GTK_RESPONSE_DELETE_EVENT);
1041
ephy_session_close (EphySession *session)
1043
EphySessionPrivate *priv = session->priv;
1046
LOG ("ephy_session_close");
1048
/* we have to ref the shell or else we may get finalised between
1049
* destroying the windows and destroying the tool windows
1051
g_object_ref (ephy_shell_get_default ());
1053
priv->dont_save = TRUE;
1054
/* need to set this up here while the dialogue hasn't been killed yet */
1055
priv->quit_while_resuming = priv->resume_window != NULL;
1057
/* Clear command queue */
1058
session_command_queue_clear (session);
1060
ephy_embed_shell_prepare_close (embed_shell);
1062
/* there may still be windows open, like dialogues posed from
1063
* web pages, etc. Try to kill them, but be sure NOT to destroy
1064
* the gtkmozembed offscreen window!
1065
* Here, we just check if it's a dialogue and close it if it is one.
1067
windows = gtk_window_list_toplevels ();
1068
g_list_foreach (windows, (GFunc) close_dialog, NULL);
1069
g_list_free (windows);
1071
windows = ephy_session_get_windows (session);
1072
g_list_foreach (windows, (GFunc) gtk_widget_destroy, NULL);
1073
g_list_free (windows);
1075
windows = g_list_copy (session->priv->tool_windows);
1076
g_list_foreach (windows, (GFunc) gtk_widget_destroy, NULL);
1077
g_list_free (windows);
1079
ephy_embed_shell_prepare_close (embed_shell);
1081
/* Just to be really sure, do it again: */
1082
windows = gtk_window_list_toplevels ();
1083
g_list_foreach (windows, (GFunc) close_dialog, NULL);
1084
g_list_free (windows);
1086
session->priv->dont_save = FALSE;
1088
/* Clear command queue */
1089
session_command_queue_clear (session);
1091
g_object_unref (ephy_shell_get_default ());
1095
write_tab (xmlTextWriterPtr writer,
1098
const char *address, *title;
1101
ret = xmlTextWriterStartElement (writer, (xmlChar *) "embed");
1102
if (ret < 0) return ret;
1104
address = ephy_web_view_get_address (ephy_embed_get_web_view (embed));
1105
ret = xmlTextWriterWriteAttribute (writer, (xmlChar *) "url",
1106
(const xmlChar *) address);
1107
if (ret < 0) return ret;
1109
title = ephy_web_view_get_title (ephy_embed_get_web_view (embed));
1110
ret = xmlTextWriterWriteAttribute (writer, (xmlChar *) "title",
1111
(const xmlChar *) title);
1112
if (ret < 0) return ret;
1114
if (ephy_web_view_is_loading (ephy_embed_get_web_view (embed)))
1116
ret = xmlTextWriterWriteAttribute (writer,
1117
(const xmlChar *) "loading",
1118
(const xmlChar *) "true");
1119
if (ret < 0) return ret;
1122
ret = xmlTextWriterEndElement (writer); /* embed */
1127
write_active_tab (xmlTextWriterPtr writer,
1128
GtkWidget *notebook)
1133
current = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
1135
ret = xmlTextWriterWriteFormatAttribute (writer, (const xmlChar *) "active-tab", "%d", current);
1141
write_window_geometry (xmlTextWriterPtr writer,
1144
int x = 0, y = 0, width = -1, height = -1;
1147
/* get window geometry */
1148
gtk_window_get_size (window, &width, &height);
1149
gtk_window_get_position (window, &x, &y);
1151
/* set window properties */
1152
ret = xmlTextWriterWriteFormatAttribute (writer, (const xmlChar *) "x", "%d", x);
1153
if (ret < 0) return ret;
1155
ret = xmlTextWriterWriteFormatAttribute (writer, (const xmlChar *) "y", "%d", y);
1156
if (ret < 0) return ret;
1158
ret = xmlTextWriterWriteFormatAttribute (writer, (const xmlChar *) "width", "%d", width);
1159
if (ret < 0) return ret;
1161
ret = xmlTextWriterWriteFormatAttribute (writer, (const xmlChar *) "height", "%d", height);
1166
write_tool_window (xmlTextWriterPtr writer,
1172
if (EPHY_IS_BOOKMARKS_EDITOR (window))
1174
id = (const xmlChar *) BOOKMARKS_EDITOR_ID;
1176
else if (EPHY_IS_HISTORY_WINDOW (window))
1178
id = (const xmlChar *) HISTORY_WINDOW_ID;
1182
g_return_val_if_reached (-1);
1185
ret = xmlTextWriterStartElement (writer, (const xmlChar *) "toolwindow");
1186
if (ret < 0) return ret;
1188
ret = xmlTextWriterWriteAttribute (writer, (const xmlChar *) "id", id);
1189
if (ret < 0) return ret;
1191
ret = write_window_geometry (writer, window);
1192
if (ret < 0) return ret;
1194
ret = xmlTextWriterEndElement (writer); /* toolwindow */
1199
write_ephy_window (xmlTextWriterPtr writer,
1203
GtkWidget *notebook;
1207
tabs = ephy_embed_container_get_children (EPHY_EMBED_CONTAINER (window));
1208
notebook = ephy_window_get_notebook (window);
1210
/* Do not save an empty EphyWindow.
1211
* This only happens when the window was newly opened.
1213
if (tabs == NULL) return 0;
1215
ret = xmlTextWriterStartElement (writer, (xmlChar *) "window");
1216
if (ret < 0) return ret;
1218
ret = write_window_geometry (writer, GTK_WINDOW (window));
1219
if (ret < 0) return ret;
1221
ret = write_active_tab (writer, notebook);
1222
if (ret < 0) return ret;
1224
role = gtk_window_get_role (GTK_WINDOW (window));
1227
ret = xmlTextWriterWriteAttribute (writer,
1228
(const xmlChar *)"role",
1229
(const xmlChar *)role);
1230
if (ret < 0) return ret;
1233
for (l = tabs; l != NULL; l = l->next)
1235
EphyEmbed *embed = EPHY_EMBED (l->data);
1236
ret = write_tab (writer, embed);
1240
if (ret < 0) return ret;
1242
ret = xmlTextWriterEndElement (writer); /* window */
1247
ephy_session_save (EphySession *session,
1248
const char *filename)
1250
EphySessionPrivate *priv;
1251
xmlTextWriterPtr writer;
1253
GFile *save_to_file, *tmp_file;
1254
char *tmp_file_path, *save_to_file_path;
1257
g_return_val_if_fail (EPHY_IS_SESSION (session), FALSE);
1259
priv = session->priv;
1261
if (priv->dont_save)
1266
LOG ("ephy_sesion_save %s", filename);
1268
if (priv->windows == NULL && priv->tool_windows == NULL)
1270
session_delete (session, filename);
1274
save_to_file = get_session_file (filename);
1275
save_to_file_path = g_file_get_path (save_to_file);
1276
tmp_file_path = g_strconcat (save_to_file_path, ".tmp", NULL);
1277
g_free (save_to_file_path);
1278
tmp_file = g_file_new_for_path (tmp_file_path);
1280
/* FIXME: do we want to turn on compression? */
1281
writer = xmlNewTextWriterFilename (tmp_file_path, 0);
1284
g_free (tmp_file_path);
1289
ret = xmlTextWriterSetIndent (writer, 1);
1290
if (ret < 0) goto out;
1292
ret = xmlTextWriterSetIndentString (writer, (const xmlChar *) " ");
1293
if (ret < 0) goto out;
1295
START_PROFILER ("Saving session")
1297
ret = xmlTextWriterStartDocument (writer, "1.0", NULL, NULL);
1298
if (ret < 0) goto out;
1300
/* create and set the root node for the session */
1301
ret = xmlTextWriterStartElement (writer, (const xmlChar *) "session");
1302
if (ret < 0) goto out;
1304
/* iterate through all the windows */
1305
for (w = session->priv->windows; w != NULL && ret >= 0; w = w->next)
1307
ret = write_ephy_window (writer, EPHY_WINDOW (w->data));
1309
if (ret < 0) goto out;
1311
for (w = session->priv->tool_windows; w != NULL && ret >= 0; w = w->next)
1313
ret = write_tool_window (writer, GTK_WINDOW (w->data));
1315
if (ret < 0) goto out;
1317
ret = xmlTextWriterEndElement (writer); /* session */
1318
if (ret < 0) goto out;
1320
ret = xmlTextWriterEndDocument (writer);
1323
xmlFreeTextWriter (writer);
1327
if (ephy_file_switch_temp_file (save_to_file, tmp_file) == FALSE)
1333
g_free (tmp_file_path);
1334
g_object_unref (save_to_file);
1335
g_object_unref (tmp_file);
1337
STOP_PROFILER ("Saving session")
1339
return ret >= 0 ? TRUE : FALSE;
1343
confirm_before_recover (EphyWindow* window, char* url, char* title)
1347
embed = ephy_shell_new_tab (ephy_shell, window, NULL, NULL,
1348
EPHY_NEW_TAB_IN_EXISTING_WINDOW |
1349
EPHY_NEW_TAB_APPEND_LAST);
1351
ephy_web_view_load_error_page (ephy_embed_get_web_view (embed), url,
1352
EPHY_WEB_VIEW_ERROR_PAGE_CRASH, NULL);
1356
parse_embed (xmlNodePtr child,
1358
gboolean is_first_window,
1359
EphySession *session)
1361
EphySessionPrivate *priv = session->priv;
1363
while (child != NULL)
1365
if (strcmp ((char *) child->name, "embed") == 0)
1367
xmlChar *url, *attr;
1369
gboolean was_loading;
1371
g_return_if_fail (window != NULL);
1373
/* Check if that tab wasn't fully loaded yet when the session crashed */
1374
attr = xmlGetProp (child, (const xmlChar *) "loading");
1375
was_loading = attr != NULL &&
1376
xmlStrEqual (attr, (const xmlChar *) "true");
1379
url = xmlGetProp (child, (const xmlChar *) "url");
1383
/* in the case that crash happens before we receive the URL from the server,
1384
this will open an about:blank tab. See http://bugzilla.gnome.org/show_bug.cgi?id=591294
1386
Otherwise, if the web was fully loaded, it is reloaded again.
1390
strcmp ((const char *) url, "about:blank") == 0)
1392
recover_url = (char *) url;
1394
/* Reuse the window holding the recovery infobar instead of creating a new one */
1395
if (is_first_window == TRUE && priv->resume_window != NULL)
1397
EphyWebView *web_view;
1400
embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (priv->resume_window));
1401
web_view = ephy_embed_get_web_view (embed);
1402
ephy_web_view_load_url(web_view, recover_url);
1404
is_first_window = FALSE;
1408
ephy_shell_new_tab (ephy_shell, window, NULL, recover_url,
1409
EPHY_NEW_TAB_IN_EXISTING_WINDOW |
1410
EPHY_NEW_TAB_OPEN_PAGE |
1411
EPHY_NEW_TAB_APPEND_LAST);
1414
else if (was_loading && url != NULL &&
1415
strcmp ((const char *) url, "about:blank") != 0)
1417
/* shows a message to the user that warns that this page was
1418
loading during crash and make Epiphany crash again,
1419
in this case we know the URL */
1421
xmlChar* title = xmlGetProp (child, (const xmlChar *) "title");
1423
confirm_before_recover (window, (char*) url, (char*) title);
1429
child = child->next;
1434
int_from_string (const char *string,
1439
gboolean success = FALSE;
1441
if (string == NULL) return FALSE;
1444
val = strtol (string, &tail, 0);
1446
if (errno == 0 && tail != NULL && tail[0] == '\0')
1448
*retval = (int) val;
1456
restore_geometry (GtkWindow *window,
1460
int x = 0, y = 0, width = -1, height = -1;
1461
gboolean success = TRUE;
1463
g_return_if_fail (window != NULL);
1465
tmp = xmlGetProp (node, (xmlChar *) "x");
1466
success &= int_from_string ((char *) tmp, &x);
1468
tmp = xmlGetProp (node, (xmlChar *) "y");
1469
success &= int_from_string ((char *) tmp, &y);
1471
tmp = xmlGetProp (node, (xmlChar *) "width");
1472
success &= int_from_string ((char *) tmp, &width);
1474
tmp = xmlGetProp (node, (xmlChar *) "height");
1475
success &= int_from_string ((char *) tmp, &height);
1480
tmp = xmlGetProp (node, (xmlChar *)"role");
1483
gtk_window_set_role (GTK_WINDOW (window), (const char *)tmp);
1487
gtk_window_move (window, x, y);
1488
gtk_window_set_default_size (window, width, height);
1494
* ephy_session_load:
1495
* @session: a #EphySession
1496
* @filename: the path of the source file
1497
* @user_time: a user_time, or 0
1499
* Load a session from disk, restoring the windows and their state
1501
* Return value: TRUE if at least a window has been opened
1504
ephy_session_load (EphySession *session,
1505
const char *filename,
1508
EphySessionPrivate *priv = session->priv;
1512
GtkWidget *widget = NULL;
1513
GFile *save_to_file;
1515
gboolean first_window_created = FALSE;
1517
LOG ("ephy_sesion_load %s", filename);
1519
save_to_file = get_session_file (filename);
1520
save_to_path = g_file_get_path (save_to_file);
1521
g_object_unref (save_to_file);
1523
doc = xmlParseFile (save_to_path);
1524
g_free (save_to_path);
1531
g_object_ref (ephy_shell_get_default ());
1533
priv->dont_save = TRUE;
1535
child = xmlDocGetRootElement (doc);
1537
/* skip the session node */
1538
child = child->children;
1540
while (child != NULL)
1542
if (xmlStrEqual (child->name, (const xmlChar *) "window"))
1545
EphyEmbed *active_child;
1547
if (first_window_created == FALSE && priv->resume_window != NULL)
1549
window = EPHY_WINDOW (priv->resume_window);
1550
first_window_created = TRUE;
1553
window = ephy_window_new ();
1555
widget = GTK_WIDGET (window);
1556
restore_geometry (GTK_WINDOW (widget), child);
1558
ephy_gui_window_update_user_time (widget, user_time);
1560
/* Now add the tabs */
1561
parse_embed (child->children, window,
1562
window == EPHY_WINDOW (priv->resume_window), session);
1564
/* Set focus to something sane */
1565
tmp = xmlGetProp (child, (xmlChar *) "active-tab");
1571
success = int_from_string ((char *) tmp, &active_tab);
1575
GtkWidget *notebook;
1576
notebook = ephy_window_get_notebook (window);
1577
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), active_tab);
1581
active_child = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
1582
gtk_widget_grab_focus (GTK_WIDGET (active_child));
1583
gtk_widget_show (widget);
1585
else if (xmlStrEqual (child->name, (const xmlChar *) "toolwindow"))
1589
id = xmlGetProp (child, (const xmlChar *) "id");
1591
if (id && xmlStrEqual ((const xmlChar *) BOOKMARKS_EDITOR_ID, id))
1593
if (!g_settings_get_boolean
1594
(EPHY_SETTINGS_LOCKDOWN,
1595
EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING))
1597
widget = ephy_shell_get_bookmarks_editor (ephy_shell);
1600
else if (id && xmlStrEqual ((const xmlChar *) HISTORY_WINDOW_ID, id))
1602
if (!g_settings_get_boolean
1603
(EPHY_SETTINGS_LOCKDOWN,
1604
EPHY_PREFS_LOCKDOWN_HISTORY))
1606
widget = ephy_shell_get_history_window (ephy_shell);
1610
restore_geometry (GTK_WINDOW (widget), child);
1612
ephy_gui_window_update_user_time (widget, user_time);
1614
gtk_widget_show (widget);
1617
child = child->next;
1622
priv->dont_save = FALSE;
1623
priv->resume_window = NULL;
1625
ephy_session_save (session, SESSION_CRASHED);
1627
g_object_unref (ephy_shell_get_default ());
1629
return (priv->windows != NULL || priv->tool_windows != NULL);
1633
* ephy_session_get_windows:
1634
* @session: the #EphySession
1636
* Returns: (element-type EphyWindow) (transfer container): the list of
1637
* open #EphyWindow:s.
1640
ephy_session_get_windows (EphySession *session)
1642
g_return_val_if_fail (EPHY_IS_SESSION (session), NULL);
1644
return g_list_copy (session->priv->windows);
1648
* ephy_session_add_window:
1649
* @session: a #EphySession
1650
* @window: a #EphyWindow
1652
* Add a tool window to the session. #EphyWindow take care of adding
1653
* itself to session.
1656
ephy_session_add_window (EphySession *session,
1659
LOG ("ephy_session_add_window %p", window);
1661
session->priv->tool_windows =
1662
g_list_append (session->priv->tool_windows, window);
1664
ephy_session_save (session, SESSION_CRASHED);
1668
* ephy_session_remove_window:
1669
* @session: a #EphySession.
1670
* @window: a #GtkWindow, which must be either the bookmarks editor or the
1673
* Remove a tool window from the session.
1676
ephy_session_remove_window (EphySession *session,
1679
LOG ("ephy_session_remove_window %p", window);
1681
session->priv->tool_windows =
1682
g_list_remove (session->priv->tool_windows, window);
1684
ephy_session_save (session, SESSION_CRASHED);
1688
* ephy_session_get_active_window:
1689
* @session: a #EphySession
1691
* Get the current active browser window. Use it when you
1692
* need to take an action (like opening an url) on
1693
* a window but you dont have a target window.
1695
* Return value: (transfer none): the current active non-popup browser
1696
* window, or NULL of there is none.
1699
ephy_session_get_active_window (EphySession *session)
1701
EphyWindow *window = NULL;
1702
EphyEmbedContainer *w;
1705
g_return_val_if_fail (EPHY_IS_SESSION (session), NULL);
1707
for (l = session->priv->windows; l != NULL; l = l->next)
1709
w = EPHY_EMBED_CONTAINER (l->data);
1711
if (ephy_embed_container_get_is_popup (w) == FALSE)
1713
window = EPHY_WINDOW (w);
1722
* ephy_session_queue_command:
1723
* @session: a #EphySession
1726
ephy_session_queue_command (EphySession *session,
1727
EphySessionCommand command,
1733
EphySessionPrivate *priv;
1735
SessionCommand *cmd;
1737
LOG ("queue_command command:%d", command);
1739
g_return_if_fail (EPHY_IS_SESSION (session));
1740
g_return_if_fail (command != EPHY_SESSION_CMD_OPEN_URIS || args != NULL);
1742
priv = session->priv;
1744
/* First look if the same command is already queued */
1745
if (command > EPHY_SESSION_CMD_RESUME_SESSION &&
1746
command < EPHY_SESSION_CMD_OPEN_URIS)
1748
element = g_queue_find_custom (priv->queue,
1749
GINT_TO_POINTER (command),
1750
(GCompareFunc) session_command_find);
1751
if (element != NULL)
1753
cmd = (SessionCommand *) element->data;
1755
if ((command == EPHY_SESSION_CMD_LOAD_SESSION &&
1756
strcmp (cmd->arg, arg) == 0) ||
1757
command == EPHY_SESSION_CMD_OPEN_BOOKMARKS_EDITOR ||
1758
command == EPHY_SESSION_CMD_RESUME_SESSION)
1760
cmd->user_time = user_time;
1761
g_queue_remove (priv->queue, cmd);
1762
g_queue_push_tail (priv->queue, cmd);
1769
/* FIXME: use g_slice_new */
1770
cmd = g_new0 (SessionCommand, 1);
1771
cmd->command = command;
1772
cmd->arg = arg ? g_strdup (arg) : NULL;
1773
cmd->args = args ? g_strdupv (args) : NULL;
1774
cmd->user_time = user_time;
1775
/* This ref is released in session_command_free */
1776
g_object_ref (ephy_shell_get_default ());
1780
g_queue_push_head (priv->queue, cmd);
1784
g_queue_push_tail (priv->queue, cmd);
1787
session_command_queue_next (session);
1789
if (priv->resume_window != NULL)
1791
gtk_window_present_with_time (GTK_WINDOW (priv->resume_window),