/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Copyright © 2012 Igalia S.L.
*
* This file is part of Epiphany.
*
* Epiphany is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Epiphany is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Epiphany. If not, see .
*/
#include "config.h"
#include "ephy-file-monitor.h"
#include "ephy-debug.h"
#include
#define RELOAD_DELAY 250 /* ms */
#define RELOAD_DELAY_MAX_TICKS 40 /* RELOAD_DELAY * RELOAD_DELAY_MAX_TICKS = 10 s */
struct _EphyFileMonitor {
GObject parent;
GFileMonitor *monitor;
gboolean monitor_directory;
guint reload_scheduled_id;
guint reload_delay_ticks;
EphyWebView *view;
};
G_DEFINE_TYPE (EphyFileMonitor, ephy_file_monitor, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_VIEW,
LAST_PROP
};
static GParamSpec *obj_properties[LAST_PROP];
static void
ephy_file_monitor_cancel (EphyFileMonitor *monitor)
{
g_assert (EPHY_IS_FILE_MONITOR (monitor));
if (monitor->monitor != NULL) {
LOG ("Cancelling file monitor");
g_file_monitor_cancel (G_FILE_MONITOR (monitor->monitor));
g_object_unref (monitor->monitor);
monitor->monitor = NULL;
}
if (monitor->reload_scheduled_id != 0) {
LOG ("Cancelling scheduled reload");
g_source_remove (monitor->reload_scheduled_id);
monitor->reload_scheduled_id = 0;
}
monitor->reload_delay_ticks = 0;
}
static gboolean
ephy_file_monitor_reload_cb (EphyFileMonitor *monitor)
{
if (monitor->reload_delay_ticks > 0) {
monitor->reload_delay_ticks--;
/* Run again. */
return TRUE;
}
if (ephy_web_view_is_loading (monitor->view)) {
/* Wait a bit to reload if we're still loading! */
monitor->reload_delay_ticks = RELOAD_DELAY_MAX_TICKS / 2;
/* Run again. */
return TRUE;
}
monitor->reload_scheduled_id = 0;
LOG ("Reloading file '%s'", ephy_web_view_get_address (monitor->view));
webkit_web_view_reload (WEBKIT_WEB_VIEW (monitor->view));
/* Don't run again. */
return FALSE;
}
static void
ephy_file_monitor_changed_cb (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
EphyFileMonitor *file_monitor)
{
gboolean should_reload;
switch (event_type) {
/* These events will always trigger a reload: */
case G_FILE_MONITOR_EVENT_CHANGED:
case G_FILE_MONITOR_EVENT_CREATED:
should_reload = TRUE;
break;
/* These events will only trigger a reload for directories: */
case G_FILE_MONITOR_EVENT_DELETED:
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
should_reload = file_monitor->monitor_directory;
break;
/* These events don't trigger a reload: */
case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
case G_FILE_MONITOR_EVENT_UNMOUNTED:
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
default:
should_reload = FALSE;
break;
/* These events will never be sent: */
case G_FILE_MONITOR_EVENT_MOVED:
case G_FILE_MONITOR_EVENT_RENAMED:
case G_FILE_MONITOR_EVENT_MOVED_IN:
case G_FILE_MONITOR_EVENT_MOVED_OUT:
g_assert_not_reached ();
}
if (should_reload) {
/* We make a lot of assumptions here, but basically we know
* that we just have to reload, by construction.
* Delay the reload a little bit so we don't endlessly
* reload while a file is written.
*/
if (file_monitor->reload_delay_ticks == 0)
file_monitor->reload_delay_ticks = 1;
else {
/* Exponential backoff. */
file_monitor->reload_delay_ticks = MIN (file_monitor->reload_delay_ticks * 2,
RELOAD_DELAY_MAX_TICKS);
}
if (file_monitor->reload_scheduled_id == 0) {
file_monitor->reload_scheduled_id =
g_timeout_add (RELOAD_DELAY,
(GSourceFunc)ephy_file_monitor_reload_cb, file_monitor);
g_source_set_name_by_id (file_monitor->reload_scheduled_id, "[epiphany] file_monitor");
}
}
}
void
ephy_file_monitor_update_location (EphyFileMonitor *file_monitor,
const char *address)
{
gboolean local;
char *anchor;
char *url;
GFile *file;
GFileType file_type;
GFileInfo *file_info;
g_assert (EPHY_IS_FILE_MONITOR (file_monitor));
g_assert (address != NULL);
ephy_file_monitor_cancel (file_monitor);
local = g_str_has_prefix (address, "file://");
if (local == FALSE)
return;
/* strip off anchors */
anchor = strchr (address, '#');
if (anchor != NULL)
url = g_strndup (address, anchor - address);
else
url = g_strdup (address);
file = g_file_new_for_uri (url);
file_info = g_file_query_info (file,
G_FILE_ATTRIBUTE_STANDARD_TYPE,
0, NULL, NULL);
if (file_info == NULL) {
g_object_unref (file);
g_free (url);
return;
}
file_type = g_file_info_get_file_type (file_info);
g_object_unref (file_info);
if (file_type == G_FILE_TYPE_DIRECTORY) {
file_monitor->monitor = g_file_monitor_directory (file, 0, NULL, NULL);
g_signal_connect (file_monitor->monitor, "changed",
G_CALLBACK (ephy_file_monitor_changed_cb),
file_monitor);
file_monitor->monitor_directory = TRUE;
LOG ("Installed monitor for directory '%s'", url);
} else if (file_type == G_FILE_TYPE_REGULAR) {
file_monitor->monitor = g_file_monitor_file (file, 0, NULL, NULL);
g_signal_connect (file_monitor->monitor, "changed",
G_CALLBACK (ephy_file_monitor_changed_cb),
file_monitor);
file_monitor->monitor_directory = FALSE;
LOG ("Installed monitor for file '%s'", url);
}
g_object_unref (file);
g_free (url);
}
static void
ephy_file_monitor_dispose (GObject *object)
{
ephy_file_monitor_cancel (EPHY_FILE_MONITOR (object));
G_OBJECT_CLASS (ephy_file_monitor_parent_class)->dispose (object);
}
static void
ephy_file_monitor_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
EphyFileMonitor *monitor = EPHY_FILE_MONITOR (object);
switch (prop_id) {
case PROP_VIEW:
g_value_set_object (value, monitor->view);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
ephy_file_monitor_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EphyFileMonitor *monitor = EPHY_FILE_MONITOR (object);
switch (prop_id) {
case PROP_VIEW:
monitor->view = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
ephy_file_monitor_class_init (EphyFileMonitorClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = ephy_file_monitor_dispose;
gobject_class->get_property = ephy_file_monitor_get_property;
gobject_class->set_property = ephy_file_monitor_set_property;
obj_properties[PROP_VIEW] =
g_param_spec_object ("view",
"View",
"The file monitor's associated view",
EPHY_TYPE_WEB_VIEW,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, LAST_PROP, obj_properties);
}
static void
ephy_file_monitor_init (EphyFileMonitor *monitor)
{
}
EphyFileMonitor *
ephy_file_monitor_new (EphyWebView *view)
{
return g_object_new (EPHY_TYPE_FILE_MONITOR,
"view", view,
NULL);
}