807 lines
24 KiB
C
807 lines
24 KiB
C
/*
|
|
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
See the file COPYING for the full license text.
|
|
*/
|
|
|
|
#include "nojs-view.h"
|
|
#include "nojs-preferences.h"
|
|
|
|
/* Define this class in GObject system */
|
|
G_DEFINE_TYPE(NoJSView,
|
|
nojs_view,
|
|
G_TYPE_OBJECT)
|
|
|
|
/* Properties */
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_MANAGER,
|
|
PROP_BROWSER,
|
|
PROP_VIEW,
|
|
PROP_MENU_ICON_STATE,
|
|
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec* NoJSViewProperties[PROP_LAST]={ 0, };
|
|
|
|
/* Private structure - access only by public API if needed */
|
|
#define NOJS_VIEW_GET_PRIVATE(obj) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_NOJS_VIEW, NoJSViewPrivate))
|
|
|
|
struct _NoJSViewPrivate
|
|
{
|
|
/* Extension related */
|
|
NoJS *manager;
|
|
MidoriBrowser *browser;
|
|
MidoriView *view;
|
|
|
|
GtkWidget *menu;
|
|
gboolean menuPolicyWasChanged;
|
|
NoJSMenuIconState menuIconState;
|
|
|
|
GSList *resourceURIs;
|
|
};
|
|
|
|
/* IMPLEMENTATION: Private variables and methods */
|
|
|
|
/* Preferences of this extension should be opened */
|
|
static void _nojs_view_on_preferences_response(GtkWidget* inDialog,
|
|
gint inResponse,
|
|
gpointer *inUserData)
|
|
{
|
|
gtk_widget_destroy(inDialog);
|
|
}
|
|
|
|
static void _nojs_view_on_open_preferences(NoJSView *self, gpointer inUserData)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
|
|
/* Show preferences window */
|
|
GtkWidget* dialog;
|
|
|
|
dialog=nojs_preferences_new(priv->manager);
|
|
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
|
|
g_signal_connect(dialog, "response", G_CALLBACK (_nojs_view_on_preferences_response), self);
|
|
gtk_widget_show_all(dialog);
|
|
}
|
|
|
|
/* Selection was done in menu */
|
|
static void _nojs_view_on_menu_selection_done(NoJSView *self, gpointer inUserData)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
|
|
/* Check if any policy was changed and reload page */
|
|
if(priv->menuPolicyWasChanged!=FALSE)
|
|
{
|
|
/* Reset flag that any policy was changed */
|
|
priv->menuPolicyWasChanged=FALSE;
|
|
|
|
/* Reload page */
|
|
midori_view_reload(priv->view, FALSE);
|
|
g_message("%s: Reloading page %s as policy has changed", __func__, midori_view_get_display_uri(priv->view));
|
|
}
|
|
}
|
|
|
|
/* Destroy menu */
|
|
static void _nojs_view_destroy_menu(NoJSView *self)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
g_return_if_fail(self->priv->menu!=NULL);
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
|
|
/* Empty menu and list of domains added to menu */
|
|
gtk_widget_destroy(priv->menu);
|
|
priv->menu=NULL;
|
|
|
|
/* Reset menu icon to default state */
|
|
priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
|
|
g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
|
|
}
|
|
|
|
/* Create empty menu */
|
|
static void _nojs_view_create_empty_menu(NoJSView *self)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
g_return_if_fail(self->priv->menu==NULL);
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
GtkWidget *item;
|
|
|
|
/* Create new menu and set up default items */
|
|
priv->menu=gtk_menu_new();
|
|
|
|
item=gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
|
|
g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_open_preferences), self);
|
|
gtk_menu_shell_prepend(GTK_MENU_SHELL(priv->menu), item);
|
|
gtk_widget_show_all(item);
|
|
|
|
/* Reset flag that any policy was changed */
|
|
priv->menuPolicyWasChanged=FALSE;
|
|
|
|
/* Reset menu icon to default state */
|
|
priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
|
|
g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
|
|
|
|
/* Connect signal to menu */
|
|
g_signal_connect_swapped(priv->menu, "selection-done", G_CALLBACK(_nojs_view_on_menu_selection_done), self);
|
|
}
|
|
|
|
/* Change visibility state of menu item for a domain depending on policy */
|
|
static gboolean _nojs_view_menu_item_change_policy(NoJSView *self, const gchar *inDomain, NoJSPolicy inPolicy)
|
|
{
|
|
g_return_val_if_fail(NOJS_IS_VIEW(self), FALSE);
|
|
g_return_val_if_fail(inDomain, FALSE);
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
GList *items, *iter;
|
|
gboolean updated;
|
|
|
|
/* Handle accept-for-session like accept when showing or hiding menu items */
|
|
if(inPolicy==NOJS_POLICY_ACCEPT_TEMPORARILY) inPolicy=NOJS_POLICY_ACCEPT;
|
|
|
|
/* Update menu items */
|
|
updated=FALSE;
|
|
items=gtk_container_get_children(GTK_CONTAINER(priv->menu));
|
|
for(iter=items; iter; iter=iter->next)
|
|
{
|
|
/* Only check and update menu items (not separators and so on) */
|
|
if(GTK_IS_MENU_ITEM(iter->data))
|
|
{
|
|
GtkMenuItem *item=GTK_MENU_ITEM(iter->data);
|
|
const gchar *itemDomain;
|
|
NoJSPolicy itemPolicy;
|
|
|
|
itemDomain=(const gchar*)g_object_get_data(G_OBJECT(item), "domain");
|
|
itemPolicy=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "policy"));
|
|
|
|
/* Handle accept-for-session like accept when showing or hiding menu items */
|
|
if(itemPolicy==NOJS_POLICY_ACCEPT_TEMPORARILY) itemPolicy=NOJS_POLICY_ACCEPT;
|
|
|
|
/* If menu item has "domain"-data update its visibility state
|
|
* depending on matching policy
|
|
*/
|
|
if(g_strcmp0(itemDomain, inDomain)==0)
|
|
{
|
|
if(itemPolicy==inPolicy) gtk_widget_hide(GTK_WIDGET(item));
|
|
else gtk_widget_show_all(GTK_WIDGET(item));
|
|
|
|
/* Set flag that at least one menu item was updated */
|
|
updated=TRUE;
|
|
}
|
|
}
|
|
}
|
|
g_list_free(items);
|
|
|
|
/* Return flag indicating if at least one menu item was updated */
|
|
return(updated);
|
|
}
|
|
|
|
/* A menu item was selected */
|
|
static void _nojs_view_on_menu_item_activate(NoJSView *self, gpointer inUserData)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
g_return_if_fail(GTK_IS_MENU_ITEM(inUserData));
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
GtkMenuItem *item=GTK_MENU_ITEM(inUserData);
|
|
const gchar *domain;
|
|
NoJSPolicy policy;
|
|
|
|
/* Get domain and policy to set */
|
|
domain=(const gchar*)g_object_get_data(G_OBJECT(item), "domain");
|
|
policy=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "policy"));
|
|
g_return_if_fail(domain);
|
|
g_return_if_fail(policy>=NOJS_POLICY_ACCEPT && policy<=NOJS_POLICY_BLOCK);
|
|
|
|
/* Set policy for domain and update menu items */
|
|
_nojs_view_menu_item_change_policy(self, domain, policy);
|
|
nojs_set_policy(priv->manager, domain, policy);
|
|
|
|
/* Set flag that a policy was changed */
|
|
priv->menuPolicyWasChanged=TRUE;
|
|
}
|
|
|
|
/* Add site to menu */
|
|
static void _nojs_view_add_site_to_menu(NoJSView *self, const gchar *inDomain, NoJSPolicy inPolicy)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
g_return_if_fail(inDomain);
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
GtkWidget *item;
|
|
gchar *itemLabel;
|
|
GtkWidget *itemImage;
|
|
static gint INSERT_POSITION=1;
|
|
NoJSMenuIconState newMenuIconState;
|
|
|
|
/* Create menu object if not available */
|
|
if(!priv->menu) _nojs_view_create_empty_menu(self);
|
|
|
|
/* Check if domain was already added to menu. If it exists just update it. */
|
|
if(_nojs_view_menu_item_change_policy(self, inDomain, inPolicy)==TRUE) return;
|
|
|
|
/* Add menu item(s) for domain */
|
|
itemLabel=g_strdup_printf(_("Deny %s"), inDomain);
|
|
item=gtk_image_menu_item_new_with_label(itemLabel);
|
|
itemImage=gtk_image_new_from_stock (GTK_STOCK_NO, GTK_ICON_SIZE_MENU);
|
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
|
|
gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
|
|
gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
|
|
if(inPolicy!=NOJS_POLICY_BLOCK) gtk_widget_show_all(item);
|
|
g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
|
|
g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_BLOCK));
|
|
g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
|
|
g_free(itemLabel);
|
|
|
|
itemLabel=g_strdup_printf(_("Allow %s"), inDomain);
|
|
item=gtk_image_menu_item_new_with_label(itemLabel);
|
|
itemImage=gtk_image_new_from_stock (GTK_STOCK_YES, GTK_ICON_SIZE_MENU);
|
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
|
|
gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
|
|
gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
|
|
if(inPolicy!=NOJS_POLICY_ACCEPT && inPolicy!=NOJS_POLICY_ACCEPT_TEMPORARILY) gtk_widget_show_all(item);
|
|
g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
|
|
g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_ACCEPT));
|
|
g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
|
|
g_free(itemLabel);
|
|
|
|
itemLabel=g_strdup_printf(_("Allow %s this session"), inDomain);
|
|
item=gtk_image_menu_item_new_with_label(itemLabel);
|
|
itemImage=gtk_image_new_from_stock (GTK_STOCK_OK, GTK_ICON_SIZE_MENU);
|
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
|
|
gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
|
|
gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
|
|
if(inPolicy!=NOJS_POLICY_ACCEPT && inPolicy!=NOJS_POLICY_ACCEPT_TEMPORARILY) gtk_widget_show_all(item);
|
|
g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
|
|
g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_ACCEPT_TEMPORARILY));
|
|
g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
|
|
g_free(itemLabel);
|
|
|
|
/* Add seperator to seperate actions for this domain from the other domains */
|
|
item=gtk_separator_menu_item_new();
|
|
gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
|
|
gtk_widget_show_all(item);
|
|
|
|
/* Determine state of status icon */
|
|
if(priv->menuIconState!=NOJS_MENU_ICON_STATE_MIXED)
|
|
{
|
|
switch(inPolicy)
|
|
{
|
|
case NOJS_POLICY_ACCEPT:
|
|
case NOJS_POLICY_ACCEPT_TEMPORARILY:
|
|
newMenuIconState=NOJS_MENU_ICON_STATE_ALLOWED;
|
|
break;
|
|
|
|
case NOJS_POLICY_BLOCK:
|
|
newMenuIconState=NOJS_MENU_ICON_STATE_DENIED;
|
|
break;
|
|
|
|
default:
|
|
newMenuIconState=NOJS_MENU_ICON_STATE_MIXED;
|
|
break;
|
|
}
|
|
|
|
if(priv->menuIconState==NOJS_MENU_ICON_STATE_UNDETERMINED ||
|
|
priv->menuIconState!=newMenuIconState)
|
|
{
|
|
priv->menuIconState=newMenuIconState;
|
|
g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Status of loading a site has changed */
|
|
static void _nojs_view_on_load_status_changed(NoJSView *self, GParamSpec *inSpec, gpointer inUserData)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
g_return_if_fail(WEBKIT_IS_WEB_VIEW(inUserData));
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
WebKitWebView *webkitView=WEBKIT_WEB_VIEW(inUserData);
|
|
WebKitWebSettings *settings=webkit_web_view_get_settings(webkitView);
|
|
WebKitLoadStatus status;
|
|
SoupURI *uri;
|
|
|
|
/* Get URI of document loading/loaded */
|
|
uri=soup_uri_new(webkit_web_view_get_uri(webkitView));
|
|
|
|
/* Check load status */
|
|
status=webkit_web_view_get_load_status(webkitView);
|
|
|
|
/* Check if a view was emptied, e.g. for a new document going to be loaded soon */
|
|
if(status==WEBKIT_LOAD_PROVISIONAL)
|
|
{
|
|
/* Create a new empty menu */
|
|
_nojs_view_destroy_menu(self);
|
|
_nojs_view_create_empty_menu(self);
|
|
|
|
/* Free list of resource URIs, that's the list of URIs for all resources
|
|
* of a page
|
|
*/
|
|
if(priv->resourceURIs)
|
|
{
|
|
g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
|
|
priv->resourceURIs=NULL;
|
|
}
|
|
}
|
|
|
|
/* Check if document loading is going to start. Do not check special pages. */
|
|
if(status==WEBKIT_LOAD_COMMITTED &&
|
|
uri &&
|
|
uri->scheme &&
|
|
g_strcmp0(uri->scheme, "about")!=0)
|
|
{
|
|
/* Check if domain is black-listed or white-listed and enable or
|
|
* disable javascript accordingly. But if settings match already
|
|
* the state it should get do not set it again to avoid reloads of page.
|
|
*/
|
|
gchar *domain;
|
|
NoJSPolicy policy;
|
|
gboolean currentScriptsEnabled;
|
|
gboolean newScriptsEnabled;
|
|
|
|
domain=nojs_get_domain(priv->manager, uri);
|
|
policy=nojs_get_policy(priv->manager, uri);
|
|
if(policy==NOJS_POLICY_UNDETERMINED)
|
|
{
|
|
policy=nojs_get_policy_for_unknown_domain(priv->manager);
|
|
// TODO: Show nick_name of policy (enum) to use in warning
|
|
g_warning("Got invalid policy. Using default policy for unknown domains.");
|
|
}
|
|
|
|
newScriptsEnabled=(policy==NOJS_POLICY_BLOCK ? FALSE : TRUE);
|
|
g_object_get(G_OBJECT(settings), "enable-scripts", ¤tScriptsEnabled, NULL);
|
|
|
|
if(newScriptsEnabled!=currentScriptsEnabled)
|
|
{
|
|
g_object_set(G_OBJECT(settings), "enable-scripts", newScriptsEnabled, NULL);
|
|
// TODO: Set uri also to ensure this uri is going to be reloaded
|
|
}
|
|
|
|
if(domain)
|
|
{
|
|
_nojs_view_add_site_to_menu(self, domain, policy);
|
|
g_free(domain);
|
|
}
|
|
}
|
|
|
|
/* Free allocated resources */
|
|
if(uri) soup_uri_free(uri);
|
|
}
|
|
|
|
/* A request is going to sent */
|
|
static void _nojs_view_on_resource_request_starting(NoJSView *self,
|
|
WebKitWebFrame *inFrame,
|
|
WebKitWebResource *inResource,
|
|
WebKitNetworkRequest *inRequest,
|
|
WebKitNetworkResponse *inResponse,
|
|
gpointer inUserData)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
SoupMessage *message;
|
|
SoupURI *uri;
|
|
gchar *uriText;
|
|
|
|
/* Remember resource URIs requesting */
|
|
message=(inRequest ? webkit_network_request_get_message(inRequest) : NULL);
|
|
if(message)
|
|
{
|
|
uri=soup_message_get_uri(message);
|
|
if(uri)
|
|
{
|
|
uriText=soup_uri_to_string(uri, FALSE);
|
|
priv->resourceURIs=g_slist_prepend(priv->resourceURIs, uriText);
|
|
}
|
|
}
|
|
|
|
message=(inResponse ? webkit_network_response_get_message(inResponse) : NULL);
|
|
if(message)
|
|
{
|
|
uri=soup_message_get_uri(message);
|
|
if(uri)
|
|
{
|
|
uriText=soup_uri_to_string(uri, FALSE);
|
|
priv->resourceURIs=g_slist_prepend(priv->resourceURIs, uriText);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* A policy has changed */
|
|
static void _nojs_view_on_policy_changed(NoJSView *self, gchar *inDomain, gpointer inUserData)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
g_return_if_fail(inDomain);
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
GList *items, *iter;
|
|
gboolean reloaded;
|
|
|
|
/* Check if the policy of a domain has changed this view has referenced resources to */
|
|
reloaded=FALSE;
|
|
items=gtk_container_get_children(GTK_CONTAINER(priv->menu));
|
|
for(iter=items; reloaded==FALSE && iter; iter=iter->next)
|
|
{
|
|
if(GTK_IS_MENU_ITEM(iter->data))
|
|
{
|
|
const gchar *itemDomain;
|
|
|
|
/* Check if domain matches menu item */
|
|
itemDomain=(const gchar*)g_object_get_data(G_OBJECT(iter->data), "domain");
|
|
if(g_strcmp0(itemDomain, inDomain)==0)
|
|
{
|
|
/* Found domain in our menu so reload page */
|
|
midori_view_reload(priv->view, FALSE);
|
|
reloaded=TRUE;
|
|
}
|
|
}
|
|
}
|
|
g_list_free(items);
|
|
}
|
|
|
|
/* A javascript URI is going to loaded or blocked */
|
|
static void _nojs_view_on_uri_load_policy_status(NoJSView *self, gchar *inURI, NoJSPolicy inPolicy, gpointer inUserData)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
GSList *iter;
|
|
gchar *checkURI;
|
|
|
|
/* Check if uri (accepted or blocked) might be one of ours */
|
|
for(iter=priv->resourceURIs; iter; iter=iter->next)
|
|
{
|
|
checkURI=(gchar*)iter->data;
|
|
if(g_strcmp0(checkURI, inURI)==0)
|
|
{
|
|
SoupURI *uri;
|
|
gchar *domain;
|
|
|
|
uri=soup_uri_new(inURI);
|
|
domain=nojs_get_domain(priv->manager, uri);
|
|
if(domain)
|
|
{
|
|
_nojs_view_add_site_to_menu(self, domain, inPolicy);
|
|
g_free(domain);
|
|
}
|
|
|
|
soup_uri_free(uri);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Property "view" has changed */
|
|
static void _nojs_view_on_view_changed(NoJSView *self, MidoriView *inView)
|
|
{
|
|
NoJSViewPrivate *priv=self->priv;
|
|
WebKitWebView *webkitView;
|
|
|
|
/* Disconnect signal on old view */
|
|
if(priv->view)
|
|
{
|
|
webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(priv->view));
|
|
g_signal_handlers_disconnect_by_data(webkitView, self);
|
|
g_object_set_data(G_OBJECT(priv->view), "nojs-view-instance", NULL);
|
|
g_object_unref(priv->view);
|
|
priv->view=NULL;
|
|
}
|
|
|
|
/* Set new view if valid pointer */
|
|
if(!inView) return;
|
|
|
|
priv->view=g_object_ref(inView);
|
|
g_object_set_data(G_OBJECT(priv->view), "nojs-view-instance", self);
|
|
|
|
/* Listen to changes of load-status in view */
|
|
webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(priv->view));
|
|
g_signal_connect_swapped(webkitView, "notify::load-status", G_CALLBACK(_nojs_view_on_load_status_changed), self);
|
|
g_signal_connect_swapped(webkitView, "resource-request-starting", G_CALLBACK(_nojs_view_on_resource_request_starting), self);
|
|
|
|
/* Create empty menu */
|
|
_nojs_view_destroy_menu(self);
|
|
_nojs_view_create_empty_menu(self);
|
|
|
|
/* Release list of resource URIs */
|
|
if(priv->resourceURIs)
|
|
{
|
|
g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
|
|
priv->resourceURIs=NULL;
|
|
}
|
|
}
|
|
|
|
/* This extension is going to be deactivated */
|
|
static void _nojs_view_on_extension_deactivated(NoJSView *self, MidoriExtension *inExtension)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
|
|
/* Dispose allocated resources by unreferencing ourselve */
|
|
g_object_unref(self);
|
|
}
|
|
|
|
/* Property "manager" has changed */
|
|
static void _nojs_view_on_manager_changed(NoJSView *self, NoJS *inNoJS)
|
|
{
|
|
g_return_if_fail(NOJS_IS_VIEW(self));
|
|
g_return_if_fail(!inNoJS || IS_NOJS(inNoJS));
|
|
|
|
NoJSViewPrivate *priv=self->priv;
|
|
MidoriExtension *extension;
|
|
|
|
/* Release reference to old manager and clean up */
|
|
if(priv->manager)
|
|
{
|
|
g_object_get(priv->manager, "extension", &extension, NULL);
|
|
g_signal_handlers_disconnect_by_data(extension, self);
|
|
g_object_unref(extension);
|
|
|
|
g_signal_handlers_disconnect_by_data(priv->manager, self);
|
|
g_object_unref(priv->manager);
|
|
priv->manager=NULL;
|
|
}
|
|
|
|
/* Set new view if valid pointer */
|
|
if(!inNoJS) return;
|
|
|
|
priv->manager=g_object_ref(inNoJS);
|
|
|
|
/* Connect signals to manager */
|
|
g_signal_connect_swapped(priv->manager, "uri-load-policy-status", G_CALLBACK(_nojs_view_on_uri_load_policy_status), self);
|
|
g_signal_connect_swapped(priv->manager, "policy-changed", G_CALLBACK(_nojs_view_on_policy_changed), self);
|
|
|
|
/* Connect signal to get noticed when extension is going to be deactivated
|
|
* to release all references to GObjects
|
|
*/
|
|
g_object_get(priv->manager, "extension", &extension, NULL);
|
|
g_signal_connect_swapped(extension, "deactivate", G_CALLBACK(_nojs_view_on_extension_deactivated), self);
|
|
g_object_unref(extension);
|
|
}
|
|
|
|
/* IMPLEMENTATION: GObject */
|
|
|
|
/* Finalize this object */
|
|
static void nojs_view_finalize(GObject *inObject)
|
|
{
|
|
NoJSView *self=NOJS_VIEW(inObject);
|
|
NoJSViewPrivate *priv=self->priv;
|
|
|
|
/* Dispose allocated resources */
|
|
if(priv->manager)
|
|
{
|
|
MidoriExtension *extension;
|
|
|
|
g_object_get(priv->manager, "extension", &extension, NULL);
|
|
g_signal_handlers_disconnect_by_data(extension, self);
|
|
g_object_unref(extension);
|
|
|
|
g_signal_handlers_disconnect_by_data(priv->manager, self);
|
|
g_object_unref(priv->manager);
|
|
priv->manager=NULL;
|
|
}
|
|
|
|
if(priv->browser)
|
|
{
|
|
g_object_unref(priv->browser);
|
|
priv->browser=NULL;
|
|
}
|
|
|
|
if(priv->view)
|
|
{
|
|
_nojs_view_on_view_changed(self, NULL);
|
|
}
|
|
|
|
if(priv->menu)
|
|
{
|
|
gtk_widget_destroy(priv->menu);
|
|
priv->menu=NULL;
|
|
}
|
|
|
|
if(priv->resourceURIs)
|
|
{
|
|
g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
|
|
priv->resourceURIs=NULL;
|
|
}
|
|
|
|
/* Call parent's class finalize method */
|
|
G_OBJECT_CLASS(nojs_view_parent_class)->finalize(inObject);
|
|
}
|
|
|
|
/* Set/get properties */
|
|
static void nojs_view_set_property(GObject *inObject,
|
|
guint inPropID,
|
|
const GValue *inValue,
|
|
GParamSpec *inSpec)
|
|
{
|
|
NoJSView *self=NOJS_VIEW(inObject);
|
|
|
|
switch(inPropID)
|
|
{
|
|
/* Construct-only properties */
|
|
case PROP_MANAGER:
|
|
_nojs_view_on_manager_changed(self, NOJS(g_value_get_object(inValue)));
|
|
break;
|
|
|
|
case PROP_BROWSER:
|
|
if(self->priv->browser) g_object_unref(self->priv->browser);
|
|
self->priv->browser=g_object_ref(g_value_get_object(inValue));
|
|
break;
|
|
|
|
case PROP_VIEW:
|
|
_nojs_view_on_view_changed(self, MIDORI_VIEW(g_value_get_object(inValue)));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void nojs_view_get_property(GObject *inObject,
|
|
guint inPropID,
|
|
GValue *outValue,
|
|
GParamSpec *inSpec)
|
|
{
|
|
NoJSView *self=NOJS_VIEW(inObject);
|
|
|
|
switch(inPropID)
|
|
{
|
|
case PROP_MANAGER:
|
|
g_value_set_object(outValue, self->priv->manager);
|
|
break;
|
|
|
|
case PROP_BROWSER:
|
|
g_value_set_object(outValue, self->priv->browser);
|
|
break;
|
|
|
|
case PROP_VIEW:
|
|
g_value_set_object(outValue, self->priv->view);
|
|
break;
|
|
|
|
case PROP_MENU_ICON_STATE:
|
|
g_value_set_enum(outValue, self->priv->menuIconState);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Class initialization
|
|
* Override functions in parent classes and define properties and signals
|
|
*/
|
|
static void nojs_view_class_init(NoJSViewClass *klass)
|
|
{
|
|
GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
|
|
|
|
/* Override functions */
|
|
gobjectClass->finalize=nojs_view_finalize;
|
|
gobjectClass->set_property=nojs_view_set_property;
|
|
gobjectClass->get_property=nojs_view_get_property;
|
|
|
|
/* Set up private structure */
|
|
g_type_class_add_private(klass, sizeof(NoJSViewPrivate));
|
|
|
|
/* Define properties */
|
|
NoJSViewProperties[PROP_MANAGER]=
|
|
g_param_spec_object("manager",
|
|
_("Manager instance"),
|
|
_("Instance to global NoJS manager"),
|
|
TYPE_NOJS,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
|
|
|
NoJSViewProperties[PROP_BROWSER]=
|
|
g_param_spec_object("browser",
|
|
_("Browser window"),
|
|
_("The Midori browser instance this view belongs to"),
|
|
MIDORI_TYPE_BROWSER,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
|
|
|
NoJSViewProperties[PROP_VIEW]=
|
|
g_param_spec_object("view",
|
|
_("View"),
|
|
_("The Midori view instance this view belongs to"),
|
|
MIDORI_TYPE_VIEW,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
|
|
|
NoJSViewProperties[PROP_MENU_ICON_STATE]=
|
|
g_param_spec_enum("menu-icon-state",
|
|
_("Menu icon state"),
|
|
_("State of menu icon to show in status bar"),
|
|
NOJS_TYPE_MENU_ICON_STATE,
|
|
NOJS_MENU_ICON_STATE_UNDETERMINED,
|
|
G_PARAM_READABLE);
|
|
|
|
g_object_class_install_properties(gobjectClass, PROP_LAST, NoJSViewProperties);
|
|
}
|
|
|
|
/* Object initialization
|
|
* Create private structure and set up default values
|
|
*/
|
|
static void nojs_view_init(NoJSView *self)
|
|
{
|
|
NoJSViewPrivate *priv;
|
|
|
|
priv=self->priv=NOJS_VIEW_GET_PRIVATE(self);
|
|
|
|
/* Set up default values */
|
|
priv->manager=NULL;
|
|
priv->browser=NULL;
|
|
priv->view=NULL;
|
|
|
|
priv->menu=NULL;
|
|
priv->menuPolicyWasChanged=FALSE;
|
|
priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
|
|
|
|
priv->resourceURIs=NULL;
|
|
|
|
/* Create empty menu */
|
|
_nojs_view_create_empty_menu(self);
|
|
}
|
|
|
|
/* Implementation: Public API */
|
|
|
|
/* Create new object */
|
|
NoJSView* nojs_view_new(NoJS *inNoJS, MidoriBrowser *inBrowser, MidoriView *inView)
|
|
{
|
|
return(g_object_new(TYPE_NOJS_VIEW,
|
|
"manager", inNoJS,
|
|
"browser", inBrowser,
|
|
"view", inView,
|
|
NULL));
|
|
}
|
|
|
|
/* Get menu widget for this view */
|
|
GtkMenu* nojs_view_get_menu(NoJSView *self)
|
|
{
|
|
g_return_val_if_fail(NOJS_IS_VIEW(self), NULL);
|
|
|
|
return(GTK_MENU(self->priv->menu));
|
|
}
|
|
|
|
/* Get image used for menu icon in status bar */
|
|
NoJSMenuIconState nojs_view_get_menu_icon_state(NoJSView *self)
|
|
{
|
|
g_return_val_if_fail(NOJS_IS_VIEW(self), NOJS_MENU_ICON_STATE_UNDETERMINED);
|
|
|
|
return(self->priv->menuIconState);
|
|
}
|
|
|
|
/************************************************************************************/
|
|
|
|
/* Implementation: Enumeration */
|
|
GType nojs_menu_icon_state_get_type(void)
|
|
{
|
|
static volatile gsize g_define_type_id__volatile=0;
|
|
|
|
if(g_once_init_enter(&g_define_type_id__volatile))
|
|
{
|
|
static const GEnumValue values[]=
|
|
{
|
|
{ NOJS_MENU_ICON_STATE_UNDETERMINED, "NOJS_MENU_ICON_STATE_UNDETERMINED", N_("Undetermined") },
|
|
{ NOJS_MENU_ICON_STATE_ALLOWED, "NOJS_MENU_ICON_STATE_ALLOWED", N_("Allowed") },
|
|
{ NOJS_MENU_ICON_STATE_MIXED, "NOJS_MENU_ICON_STATE_MIXED", N_("Mixed") },
|
|
{ NOJS_MENU_ICON_STATE_DENIED, "NOJS_MENU_ICON_STATE_DENIED", N_("Denied") },
|
|
{ 0, NULL, NULL }
|
|
};
|
|
|
|
GType g_define_type_id=g_enum_register_static(g_intern_static_string("NoJSMenuIconState"), values);
|
|
g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
|
|
}
|
|
|
|
return(g_define_type_id__volatile);
|
|
}
|