[epiphany] Only load pages when their tab is switched to upon session restore



commit ce7ab1e34804d9f7529bed13267f4619c37e32d9
Author: Gustavo Noronha Silva <gns gnome org>
Date:   Sat Feb 9 16:43:02 2013 -0200

    Only load pages when their tab is switched to upon session restore
    
    Firefox has led the way implementing this behaviour to improve the experience
    of restoring a session with lots of tabs. By delaying the loading of pages to
    when the user shows interest in them, the time it takes for the browser to
    become usable is diminished, and less pages are loaded in parallel, which
    improves load time for the first pages the user sees.
    
    It also has the advante of displaying less HTTP Basic Auth dialogs, when the
    user has many tabs pointed to the same server.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=675302

 embed/ephy-embed-shell.c |   24 +++++++++++
 embed/ephy-embed-shell.h |    3 +
 embed/ephy-embed.c       |  101 +++++++++++++++++++++++++++++++++++++++++++--
 embed/ephy-embed.h       |    3 +
 embed/ephy-web-view.c    |   38 +++++++++++++++++
 embed/ephy-web-view.h    |    4 ++
 src/ephy-notebook.c      |    4 +-
 src/ephy-session.c       |   23 ++++++++---
 src/ephy-shell.c         |    9 +++-
 src/ephy-shell.h         |    3 +
 10 files changed, 197 insertions(+), 15 deletions(-)
---
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index ea45a14..b4366b2 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -62,6 +62,7 @@ enum
   DOWNLOAD_ADDED,
   DOWNLOAD_REMOVED,
   PREPARE_CLOSE,
+  RESTORED_WINDOW,
   LAST_SIGNAL
 };
 
@@ -263,6 +264,12 @@ ephy_embed_shell_prepare_close (EphyEmbedShell *shell)
   g_signal_emit (shell, signals[PREPARE_CLOSE], 0);
 }
 
+void
+ephy_embed_shell_restored_window (EphyEmbedShell *shell)
+{
+  g_signal_emit (shell, signals[RESTORED_WINDOW], 0);
+}
+
 static void
 ephy_embed_shell_set_property (GObject *object,
                                guint prop_id,
@@ -385,6 +392,23 @@ ephy_embed_shell_class_init (EphyEmbedShellClass *klass)
                   g_cclosure_marshal_VOID__VOID,
                   G_TYPE_NONE, 0);
 
+/**
+ * EphyEmbedShell::finished-restoring-window:
+ * @shell: the #EphyEmbedShell
+ *
+ * The ::finished-restoring-window signal is emitted when the
+ * session finishes restoring a window.
+ **/
+  signals[RESTORED_WINDOW] =
+    g_signal_new ("window-restored",
+                  EPHY_TYPE_EMBED_SHELL,
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (EphyEmbedShellClass, restored_window),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+
   g_type_class_add_private (object_class, sizeof (EphyEmbedShellPrivate));
 }
 
diff --git a/embed/ephy-embed-shell.h b/embed/ephy-embed-shell.h
index e9973a2..45261e9 100644
--- a/embed/ephy-embed-shell.h
+++ b/embed/ephy-embed-shell.h
@@ -72,6 +72,8 @@ struct _EphyEmbedShellClass
 
   void    (* prepare_close)    (EphyEmbedShell *shell);
 
+  void    (* restored_window)  (EphyEmbedShell *shell);
+
   /*< private >*/
   GObject * (* get_embed_single)  (EphyEmbedShell *shell);
 };
@@ -83,6 +85,7 @@ GObject           *ephy_embed_shell_get_encodings              (EphyEmbedShell
 GObject           *ephy_embed_shell_get_embed_single           (EphyEmbedShell   *shell);
 GObject           *ephy_embed_shell_get_adblock_manager        (EphyEmbedShell   *shell);
 void               ephy_embed_shell_prepare_close              (EphyEmbedShell   *shell);
+void               ephy_embed_shell_restored_window            (EphyEmbedShell   *shell);
 void               ephy_embed_shell_set_page_setup             (EphyEmbedShell   *shell,
                                                                 GtkPageSetup     *page_setup);
 GtkPageSetup      *ephy_embed_shell_get_page_setup             (EphyEmbedShell   *shell);
diff --git a/embed/ephy-embed.c b/embed/ephy-embed.c
index 5d9f631..8ead506 100644
--- a/embed/ephy-embed.c
+++ b/embed/ephy-embed.c
@@ -43,13 +43,15 @@
 #include <webkit/webkit.h>
 #endif
 
-static void     ephy_embed_constructed      (GObject *object);
+static void     ephy_embed_constructed         (GObject *object);
 #ifndef HAVE_WEBKIT2
-static gboolean ephy_embed_inspect_show_cb  (WebKitWebInspector *inspector,
-                                             EphyEmbed *embed);
-static gboolean ephy_embed_inspect_close_cb (WebKitWebInspector *inspector,
-                                             EphyEmbed *embed);
+static gboolean ephy_embed_inspect_show_cb     (WebKitWebInspector *inspector,
+                                                EphyEmbed *embed);
+static gboolean ephy_embed_inspect_close_cb    (WebKitWebInspector *inspector,
+                                                EphyEmbed *embed);
 #endif
+static void     ephy_embed_restored_window_cb  (EphyEmbedShell *shell,
+                                                EphyEmbed *embed);
 
 #define EPHY_EMBED_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_EMBED, EphyEmbedPrivate))
 
@@ -79,6 +81,8 @@ struct _EphyEmbedPrivate
   GtkWidget *fullscreen_message_label;
   char *fullscreen_string;
 
+  WebKitNetworkRequest *delayed_request;
+
   GtkWidget *overview;
   guint overview_mode : 1;
   GSList *messages;
@@ -415,6 +419,8 @@ ephy_embed_dispose (GObject *object)
     priv->adblock_handler_id = 0;
   }
 
+  g_clear_object (&priv->delayed_request);
+
   G_OBJECT_CLASS (ephy_embed_parent_class)->dispose (object);
 }
 
@@ -423,8 +429,11 @@ ephy_embed_finalize (GObject *object)
 {
   EphyEmbed *embed = EPHY_EMBED (object);
   EphyEmbedPrivate *priv = embed->priv;
+  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
   GSList *list;
 
+  g_signal_handlers_disconnect_by_func(shell, ephy_embed_restored_window_cb, embed);
+
   list = priv->destroy_on_transition_list;
   for (; list; list = list->next) {
     GtkWidget *widget = GTK_WIDGET (list->data);
@@ -862,10 +871,50 @@ setup_adblock (GSettings *settings,
 #endif
 
 static void
+ephy_embed_maybe_load_delayed_request (EphyEmbed *embed)
+{
+  EphyEmbedPrivate *priv = embed->priv;
+  EphyWebView *web_view;
+
+  if (!priv->delayed_request)
+    return;
+
+  web_view = ephy_embed_get_web_view (embed);
+
+  ephy_web_view_load_request (web_view, priv->delayed_request);
+  g_clear_object (&priv->delayed_request);
+
+  /* This is to allow UI elements watching load status to show that the page is
+   * loading as soon as possible.
+   */
+#ifdef HAVE_WEBKIT2
+  g_signal_emit_by_name (web_view, "load-changed", WEBKIT_LOAD_STARTED);
+#else
+  g_object_notify (G_OBJECT (web_view), "load-status");
+#endif
+}
+
+static void
+ephy_embed_restored_window_cb (EphyEmbedShell *shell, EphyEmbed *embed)
+{
+  if (!gtk_widget_get_mapped (GTK_WIDGET (embed)))
+    return;
+
+  ephy_embed_maybe_load_delayed_request (embed);
+}
+
+static void
+ephy_embed_mapped_cb (GtkWidget *widget, gpointer data)
+{
+  ephy_embed_maybe_load_delayed_request ((EphyEmbed*)widget);
+}
+
+static void
 ephy_embed_constructed (GObject *object)
 {
   EphyEmbed *embed = (EphyEmbed*)object;
   EphyEmbedPrivate *priv = embed->priv;
+  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
 #ifndef HAVE_WEBKIT2
   GtkWidget *scrolled_window;
 #endif
@@ -879,6 +928,12 @@ ephy_embed_constructed (GObject *object)
   WebKitWebInspector *inspector;
   GtkWidget *overlay;
 
+  g_signal_connect (shell, "window-restored",
+                    G_CALLBACK (ephy_embed_restored_window_cb), embed);
+
+  g_signal_connect (embed, "map",
+                    G_CALLBACK (ephy_embed_mapped_cb), NULL);
+
   /* Skeleton */
   web_view = WEBKIT_WEB_VIEW (ephy_web_view_new ());
 #ifndef HAVE_WEBKIT2
@@ -1161,6 +1216,42 @@ ephy_embed_get_overview_mode (EphyEmbed *embed)
 }
 
 /**
+ * ephy_embed_set_delayed_load_request:
+ * @embed: a #EphyEmbed
+ * @request: a #WebKitNetworkRequest
+ *
+ * Sets the #WebKitNetworkRequest that should be loaded when the tab this embed
+ * is on is switched to.
+ */
+void
+ephy_embed_set_delayed_load_request (EphyEmbed *embed, WebKitNetworkRequest *request)
+{
+  g_return_if_fail (EPHY_IS_EMBED (embed));
+  g_return_if_fail (WEBKIT_IS_NETWORK_REQUEST (request));
+
+  g_clear_object (&embed->priv->delayed_request);
+
+  g_object_ref (request);
+  embed->priv->delayed_request = request;
+}
+
+/**
+ * ephy_embed_has_load_pending:
+ * @embed: a #EphyEmbed
+ *
+ * Checks whether a load has been delayed for this #EphyEmbed.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean
+ephy_embed_has_load_pending (EphyEmbed *embed)
+{
+  g_return_val_if_fail (EPHY_IS_EMBED (embed), FALSE);
+
+  return !!embed->priv->delayed_request;
+}
+
+/**
  * ephy_embed_get_overview:
  * @embed: a #EphyEmbed
  *
diff --git a/embed/ephy-embed.h b/embed/ephy-embed.h
index 7820318..afa007e 100644
--- a/embed/ephy-embed.h
+++ b/embed/ephy-embed.h
@@ -62,6 +62,9 @@ void         ephy_embed_auto_download_url        (EphyEmbed  *embed,
                                                   const char *url);
 void         ephy_embed_entering_fullscreen      (EphyEmbed *embed);
 void         ephy_embed_leaving_fullscreen       (EphyEmbed *embed);
+void         ephy_embed_set_delayed_load_request (EphyEmbed *embed,
+                                                  WebKitNetworkRequest *request);
+gboolean     ephy_embed_has_load_pending         (EphyEmbed *embed);
 void         ephy_embed_set_overview_mode        (EphyEmbed *embed,
                                                   gboolean   overview_mode);
 gboolean     ephy_embed_get_overview_mode        (EphyEmbed *embed);
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index 84580d0..c3594ee 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -2370,6 +2370,44 @@ load_status_cb (WebKitWebView *web_view,
 #endif
 
 /**
+ * ephy_web_view_set_placeholder:
+ * @view: an #EphyWebView
+ * @uri: uri that will eventually be loaded
+ * @title: last-known title of the page that will eventually be loaded
+ *
+ * Makes the #EphyWebView pretend a page that will eventually be loaded is
+ * already there.
+ *
+ **/
+void
+ephy_web_view_set_placeholder (EphyWebView *view,
+                               const char *uri,
+                               const char *title)
+{
+  char *html;
+
+  g_return_if_fail (EPHY_IS_WEB_VIEW (view));
+
+  /* We want only the actual load to be the one recorded in history, but
+   * doing a load here is the simplest way to replace the loading
+   * spinner with the favicon. */
+  ephy_web_view_freeze_history (view);
+
+  html = g_markup_printf_escaped ("<head><title>%s</title></head>", title);
+
+#ifdef HAVE_WEBKIT2
+  webkit_web_view_load_alternate_html (WEBKIT_WEB_VIEW (view), html, uri, NULL);
+#else
+  webkit_web_frame_load_alternate_string (webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view)),
+                                          html, uri, uri);
+#endif
+
+  g_free (html);
+
+  ephy_web_view_set_address (view, uri);
+}
+
+/**
  * ephy_web_view_load_error_page:
  * @view: an #EphyWebView
  * @uri: uri that caused the failure
diff --git a/embed/ephy-web-view.h b/embed/ephy-web-view.h
index e3f7161..52de781 100644
--- a/embed/ephy-web-view.h
+++ b/embed/ephy-web-view.h
@@ -165,6 +165,10 @@ const char *               ephy_web_view_get_title                (EphyWebView
 const char *               ephy_web_view_get_address              (EphyWebView               *view);
 const char *               ephy_web_view_get_title_composite      (EphyWebView               *view);
 
+void                       ephy_web_view_set_placeholder          (EphyWebView               *view,
+                                                                   const char                *uri,
+                                                                   const char                *title);
+
 void                       ephy_web_view_load_error_page          (EphyWebView               *view,
                                                                    const char                *uri,
                                                                    EphyWebViewErrorPage       page,
diff --git a/src/ephy-notebook.c b/src/ephy-notebook.c
index 2349af8..f9a9007 100644
--- a/src/ephy-notebook.c
+++ b/src/ephy-notebook.c
@@ -467,12 +467,14 @@ static void
 sync_load_status (EphyWebView *view, GParamSpec *pspec, GtkWidget *proxy)
 {
 	GtkWidget *spinner, *icon;
+	EphyEmbed *embed;
 
 	spinner = GTK_WIDGET (g_object_get_data (G_OBJECT (proxy), "spinner"));
 	icon = GTK_WIDGET (g_object_get_data (G_OBJECT (proxy), "icon"));
 	g_return_if_fail (spinner != NULL && icon != NULL);
 
-	if (ephy_web_view_is_loading (view))
+	embed = EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (view);
+	if (ephy_web_view_is_loading (view) && !ephy_embed_has_load_pending (embed))
 	{
 		gtk_widget_hide (icon);
 		gtk_widget_show (spinner);
diff --git a/src/ephy-session.c b/src/ephy-session.c
index 47dfe08..82ecf41 100644
--- a/src/ephy-session.c
+++ b/src/ephy-session.c
@@ -631,7 +631,7 @@ session_tab_new (EphyEmbed *embed)
 	}
 
 	session_tab->title = g_strdup (ephy_web_view_get_title (web_view));
-	session_tab->loading = ephy_web_view_is_loading (web_view);
+	session_tab->loading = ephy_web_view_is_loading (web_view) && !ephy_embed_has_load_pending (embed);
 
 	return session_tab;
 }
@@ -1102,11 +1102,19 @@ session_parse_embed (SessionParserContext *context,
 	 */
 	if (!was_loading || is_blank_page)
 	{
-		ephy_shell_new_tab (ephy_shell_get_default (),
-				    context->window, NULL, url,
-				    EPHY_NEW_TAB_IN_EXISTING_WINDOW |
-				    EPHY_NEW_TAB_OPEN_PAGE |
-				    EPHY_NEW_TAB_APPEND_LAST);
+		EphyNewTabFlags flags;
+		EphyEmbed *embed;
+		EphyWebView *web_view;
+
+		flags = EPHY_NEW_TAB_IN_EXISTING_WINDOW;
+		flags |= EPHY_NEW_TAB_APPEND_LAST;
+	        flags |= EPHY_NEW_TAB_DELAYED_OPEN_PAGE;
+
+		embed = ephy_shell_new_tab (ephy_shell_get_default (),
+					    context->window, NULL, url, flags);
+
+		web_view = ephy_embed_get_web_view (embed);
+		ephy_web_view_set_placeholder (web_view, url, title);
 	}
 	else if (was_loading && url != NULL)
 	{
@@ -1150,6 +1158,7 @@ session_end_element (GMarkupParseContext  *ctx,
 	if (strcmp (element_name, "window") == 0)
 	{
 		GtkWidget *notebook;
+		EphyEmbedShell *shell = ephy_embed_shell_get_default ();
 
 		notebook = ephy_window_get_notebook (context->window);
 		gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), context->active_tab);
@@ -1163,6 +1172,8 @@ session_end_element (GMarkupParseContext  *ctx,
 			gtk_widget_show (GTK_WIDGET (context->window));
 		}
 
+		ephy_embed_shell_restored_window (shell);
+
 		context->window = NULL;
 		context->active_tab = 0;
 		context->is_first_window = FALSE;
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index 7fd01d7..ebdd48f 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -730,6 +730,7 @@ ephy_shell_new_tab_full (EphyShell *shell,
   gboolean fullscreen_lockdown = FALSE;
   gboolean in_new_window = TRUE;
   gboolean open_page = FALSE;
+  gboolean delayed_open_page = FALSE;
   gboolean jump_to = FALSE;
   gboolean active_is_blank = FALSE;
   gboolean copy_history = TRUE;
@@ -748,6 +749,7 @@ ephy_shell_new_tab_full (EphyShell *shell,
   embed_shell = EPHY_EMBED_SHELL (shell);
 
   if (flags & EPHY_NEW_TAB_OPEN_PAGE) open_page = TRUE;
+  if (flags & EPHY_NEW_TAB_DELAYED_OPEN_PAGE) delayed_open_page = TRUE;
   if (flags & EPHY_NEW_TAB_IN_NEW_WINDOW) in_new_window = TRUE;
   if (flags & EPHY_NEW_TAB_IN_EXISTING_WINDOW) in_new_window = FALSE;
   if (flags & EPHY_NEW_TAB_DONT_COPY_HISTORY) copy_history = FALSE;
@@ -756,7 +758,7 @@ ephy_shell_new_tab_full (EphyShell *shell,
   fullscreen_lockdown = g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
                                                 EPHY_PREFS_LOCKDOWN_FULLSCREEN);
   in_new_window = in_new_window && !fullscreen_lockdown;
-  g_return_val_if_fail (open_page == (gboolean)(request != NULL), NULL);
+  g_return_val_if_fail ((open_page || delayed_open_page) == (gboolean)(request != NULL), NULL);
 
   LOG ("Opening new tab parent-window %p parent-embed %p in-new-window:%s jump-to:%s",
        parent_window, previous_embed, in_new_window ? "t" : "f", jump_to ? "t" : "f");
@@ -822,7 +824,7 @@ ephy_shell_new_tab_full (EphyShell *shell,
     ephy_window_activate_location (window);
     ephy_web_view_load_homepage (view);
     is_empty = TRUE;
-  } else if (flags & EPHY_NEW_TAB_OPEN_PAGE) {
+  } else if (open_page) {
     ephy_web_view_load_request (ephy_embed_get_web_view (embed),
                                 request);
 
@@ -831,7 +833,8 @@ ephy_shell_new_tab_full (EphyShell *shell,
 #else
     is_empty = ephy_embed_utils_url_is_empty (webkit_network_request_get_uri (request));
 #endif
-  }
+  } else if (delayed_open_page)
+      ephy_embed_set_delayed_load_request (embed, request);
 
   /* Make sure the initial focus is somewhere sensible and not, for
    * example, on the reload button.
diff --git a/src/ephy-shell.h b/src/ephy-shell.h
index f65083a..7b250fe 100644
--- a/src/ephy-shell.h
+++ b/src/ephy-shell.h
@@ -59,6 +59,8 @@ typedef struct _EphyShellPrivate  EphyShellPrivate;
  * @EPHY_NEW_TAB_HOME_PAGE: loads the home page in the new tab.
  * @EPHY_NEW_TAB_NEW_PAGE: legacy synonym for @EPHY_NEW_TAB_HOME_PAGE.
  * @EPHY_NEW_TAB_OPEN_PAGE: opens the provided network-request.
+ * @EPHY_NEW_TAB_DELAYED_OPEN_PAGE: store the provided network-request
+ *        so that it will be opened when the tab is switched to.
  * @EPHY_NEW_TAB_FULLSCREEN_MODE: calls gtk_window_fullscreen on the
  *        parent window of the new tab.
  * @EPHY_NEW_TAB_DONT_SHOW_WINDOW: do not show the window where the new
@@ -85,6 +87,7 @@ typedef enum {
   EPHY_NEW_TAB_HOME_PAGE    = 1 << 0,
   EPHY_NEW_TAB_NEW_PAGE   = 1 << 1,
   EPHY_NEW_TAB_OPEN_PAGE    = 1 << 2,
+  EPHY_NEW_TAB_DELAYED_OPEN_PAGE    = 1 << 3,
 
   /* Page mode */
   EPHY_NEW_TAB_FULLSCREEN_MODE  = 1 << 4,


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