Commit ce7ab1e3 authored by Gustavo Noronha Silva's avatar Gustavo Noronha Silva

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
parent 551eb0ce
......@@ -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));
}
......
......@@ -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);
......
......@@ -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);
......@@ -861,11 +870,51 @@ 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
......@@ -1160,6 +1215,42 @@ ephy_embed_get_overview_mode (EphyEmbed *embed)
return embed->priv->overview_mode;
}
/**
* 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
......
......@@ -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);
......
......@@ -2369,6 +2369,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
......
......@@ -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,
......
......@@ -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);
......
......@@ -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;
......
......@@ -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.
......
......@@ -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,
......
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