Merge lp:~tuxator/midori/nojs into lp:midori

Proposed by Paweł Forysiuk
Status: Merged
Approved by: Cris Dywan
Approved revision: 6180
Merged at revision: 6291
Proposed branch: lp:~tuxator/midori/nojs
Merge into: lp:midori
Diff against target: 2975 lines (+2917/-0)
9 files modified
extensions/nojs/README.md (+1/-0)
extensions/nojs/main.c (+79/-0)
extensions/nojs/nojs-preferences.c (+746/-0)
extensions/nojs/nojs-preferences.h (+55/-0)
extensions/nojs/nojs-view.c (+804/-0)
extensions/nojs/nojs-view.h (+71/-0)
extensions/nojs/nojs.c (+1069/-0)
extensions/nojs/nojs.h (+89/-0)
po/POTFILES.in (+3/-0)
To merge this branch: bzr merge lp:~tuxator/midori/nojs
Reviewer Review Type Date Requested Status
Cris Dywan Approve
Review via email: mp+177460@code.launchpad.net

Commit message

Introduce extension for managing javascript execution policy per domain

To post a comment you must log in.
Revision history for this message
Cris Dywan (kalikiana) wrote :

Nice stuff. Let's get this in!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'data/nojs'
2=== added file 'data/nojs/nojs-statusicon-allowed.png'
3Binary files data/nojs/nojs-statusicon-allowed.png 1970-01-01 00:00:00 +0000 and data/nojs/nojs-statusicon-allowed.png 2013-07-29 19:51:25 +0000 differ
4=== added file 'data/nojs/nojs-statusicon-denied.png'
5Binary files data/nojs/nojs-statusicon-denied.png 1970-01-01 00:00:00 +0000 and data/nojs/nojs-statusicon-denied.png 2013-07-29 19:51:25 +0000 differ
6=== added file 'data/nojs/nojs-statusicon-mixed.png'
7Binary files data/nojs/nojs-statusicon-mixed.png 1970-01-01 00:00:00 +0000 and data/nojs/nojs-statusicon-mixed.png 2013-07-29 19:51:25 +0000 differ
8=== added directory 'extensions/nojs'
9=== added file 'extensions/nojs/README.md'
10--- extensions/nojs/README.md 1970-01-01 00:00:00 +0000
11+++ extensions/nojs/README.md 2013-07-29 19:51:25 +0000
12@@ -0,0 +1,1 @@
13+This is an extension for Midori web browser. With this extension you can manage which sites are allowed to execute javascript.
14
15=== added file 'extensions/nojs/main.c'
16--- extensions/nojs/main.c 1970-01-01 00:00:00 +0000
17+++ extensions/nojs/main.c 2013-07-29 19:51:25 +0000
18@@ -0,0 +1,79 @@
19+/*
20+ Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
21+
22+ This library is free software; you can redistribute it and/or
23+ modify it under the terms of the GNU Lesser General Public
24+ License as published by the Free Software Foundation; either
25+ version 2.1 of the License, or (at your option) any later version.
26+
27+ See the file COPYING for the full license text.
28+*/
29+
30+#include "nojs.h"
31+#include "nojs-preferences.h"
32+
33+/* Global instance */
34+NoJS *noJS=NULL;
35+
36+/* This extension was activated */
37+static void _nojs_on_activate(MidoriExtension *inExtension, MidoriApp *inApp, gpointer inUserData)
38+{
39+ g_return_if_fail(noJS==NULL);
40+
41+ noJS=nojs_new(inExtension, inApp);
42+ nojs_set_policy_for_unknown_domain(noJS, midori_extension_get_integer(inExtension, "unknown-domain-policy"));
43+ nojs_set_allow_all_sites(noJS, midori_extension_get_boolean(inExtension, "allow-all-sites"));
44+ nojs_set_only_second_level_domain(noJS, midori_extension_get_boolean(inExtension, "only-second-level"));
45+}
46+
47+/* This extension was deactivated */
48+static void _nojs_on_deactivate(MidoriExtension *inExtension, gpointer inUserData)
49+{
50+ g_return_if_fail(noJS);
51+
52+ g_object_unref(noJS);
53+ noJS=NULL;
54+}
55+
56+/* Preferences of this extension should be opened */
57+static void _nojs_on_preferences_response(GtkWidget* inDialog,
58+ gint inResponse,
59+ gpointer *inUserData)
60+{
61+ gtk_widget_destroy(inDialog);
62+}
63+
64+static void _nojs_on_open_preferences(MidoriExtension *inExtension)
65+{
66+ g_return_if_fail(noJS);
67+
68+ /* Show preferences window */
69+ GtkWidget* dialog;
70+
71+ dialog=nojs_preferences_new(noJS);
72+ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
73+ g_signal_connect(dialog, "response", G_CALLBACK (_nojs_on_preferences_response), NULL);
74+ gtk_widget_show_all(dialog);
75+}
76+
77+/* Main entry for extension */
78+MidoriExtension *extension_init(void)
79+{
80+ /* Set up extension */
81+ MidoriExtension *extension=g_object_new(MIDORI_TYPE_EXTENSION,
82+ "name", _("NoJS"),
83+ "description", _("Manage javascript permission per site"),
84+ "version", "0.1" MIDORI_VERSION_SUFFIX,
85+ "authors", "Stephan Haller <nomad@froevel.de>",
86+ NULL);
87+
88+ midori_extension_install_integer(extension, "unknown-domain-policy", NOJS_POLICY_BLOCK);
89+ midori_extension_install_boolean(extension, "allow-all-sites", FALSE);
90+ midori_extension_install_boolean(extension, "only-second-level", TRUE);
91+
92+ g_signal_connect(extension, "activate", G_CALLBACK(_nojs_on_activate), NULL);
93+ g_signal_connect(extension, "deactivate", G_CALLBACK(_nojs_on_deactivate), NULL);
94+ g_signal_connect(extension, "open-preferences", G_CALLBACK(_nojs_on_open_preferences), NULL);
95+
96+ return(extension);
97+}
98
99=== added file 'extensions/nojs/nojs-preferences.c'
100--- extensions/nojs/nojs-preferences.c 1970-01-01 00:00:00 +0000
101+++ extensions/nojs/nojs-preferences.c 2013-07-29 19:51:25 +0000
102@@ -0,0 +1,746 @@
103+/*
104+ Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
105+
106+ This library is free software; you can redistribute it and/or
107+ modify it under the terms of the GNU Lesser General Public
108+ License as published by the Free Software Foundation; either
109+ version 2.1 of the License, or (at your option) any later version.
110+
111+ See the file COPYING for the full license text.
112+*/
113+
114+#include "nojs-preferences.h"
115+
116+/* Define this class in GObject system */
117+G_DEFINE_TYPE(NoJSPreferences,
118+ nojs_preferences,
119+ GTK_TYPE_DIALOG)
120+
121+/* Properties */
122+enum
123+{
124+ PROP_0,
125+
126+ PROP_MANAGER,
127+
128+ PROP_LAST
129+};
130+
131+static GParamSpec* NoJSPreferencesProperties[PROP_LAST]={ 0, };
132+
133+/* Private structure - access only by public API if needed */
134+#define NOJS_PREFERENCES_GET_PRIVATE(obj) \
135+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_NOJS_PREFERENCES, NoJSPreferencesPrivate))
136+
137+struct _NoJSPreferencesPrivate
138+{
139+ /* Extension related */
140+ NoJS *manager;
141+ sqlite3 *database;
142+
143+ /* Dialog related */
144+ GtkWidget *contentArea;
145+ GtkListStore *listStore;
146+ GtkWidget *list;
147+ GtkTreeSelection *listSelection;
148+ GtkWidget *deleteButton;
149+ GtkWidget *deleteAllButton;
150+ GtkWidget *allowAllSitesCheckbox;
151+ GtkWidget *blockUnknownDomainsCheckbox;
152+ GtkWidget *checkSecondLevelOnlyCheckbox;
153+
154+ gint signalAllowAllSitesToggledID;
155+ gint signalBlockUnknownDomainsToggledID;
156+ gint signalCheckSecondLevelOnlyToggledID;
157+
158+ gint signalManagerChangedDatabaseID;
159+ gint signalManagerChangedAllowAllSitesID;
160+ gint signalManagerChangedUnknownDomainPolicyID;
161+ gint signalManagerChangedCheckSecondLevelID;
162+};
163+
164+enum
165+{
166+ DOMAIN_COLUMN,
167+ POLICY_COLUMN,
168+ N_COLUMN
169+};
170+
171+
172+/* IMPLEMENTATION: Private variables and methods */
173+
174+/* Fill domain list with stored policies */
175+static void _nojs_preferences_fill(NoJSPreferences *self)
176+{
177+ NoJSPreferencesPrivate *priv=self->priv;
178+ gint success;
179+ sqlite3_stmt *statement=NULL;
180+
181+ /* Clear tree/list view */
182+ gtk_list_store_clear(priv->listStore);
183+
184+ /* If no database is present return here */
185+ if(!priv->database) return;
186+
187+ /* Fill list store with policies from database */
188+ success=sqlite3_prepare_v2(priv->database,
189+ "SELECT site, value FROM policies;",
190+ -1,
191+ &statement,
192+ NULL);
193+ if(statement && success==SQLITE_OK)
194+ {
195+ gchar *domain;
196+ gint policy;
197+ gchar *policyName;
198+ GtkTreeIter iter;
199+
200+ while(sqlite3_step(statement)==SQLITE_ROW)
201+ {
202+ /* Get values */
203+ domain=(gchar*)sqlite3_column_text(statement, 0);
204+ policy=sqlite3_column_int(statement, 1);
205+
206+ switch(policy)
207+ {
208+ case NOJS_POLICY_ACCEPT:
209+ policyName=_("Accept");
210+ break;
211+
212+ case NOJS_POLICY_ACCEPT_TEMPORARILY:
213+ policyName=_("Accept for session");
214+ break;
215+
216+ case NOJS_POLICY_BLOCK:
217+ policyName=_("Block");
218+ break;
219+
220+ default:
221+ policyName=NULL;
222+ break;
223+ }
224+
225+ if(policyName)
226+ {
227+ gtk_list_store_append(priv->listStore, &iter);
228+ gtk_list_store_set(priv->listStore,
229+ &iter,
230+ DOMAIN_COLUMN, domain,
231+ POLICY_COLUMN, policyName,
232+ -1);
233+ }
234+ }
235+ }
236+ else g_warning(_("SQL fails: %s"), sqlite3_errmsg(priv->database));
237+
238+ sqlite3_finalize(statement);
239+}
240+
241+/* Database instance in manager changed */
242+static void _nojs_preferences_on_manager_database_changed(NoJSPreferences *self,
243+ GParamSpec *inSpec,
244+ gpointer inUserData)
245+{
246+ NoJSPreferencesPrivate *priv=self->priv;
247+ NoJS *manager=NOJS(inUserData);
248+ gchar *databaseFile;
249+
250+ /* Close connection to any open database */
251+ if(priv->database) sqlite3_close(priv->database);
252+ priv->database=NULL;
253+
254+ /* Get pointer to new database and open database */
255+ g_object_get(manager, "database-filename", &databaseFile, NULL);
256+ if(databaseFile)
257+ {
258+ gint success;
259+
260+ success=sqlite3_open(databaseFile, &priv->database);
261+ if(success!=SQLITE_OK)
262+ {
263+ g_warning(_("Could not open database of extension: %s"), sqlite3_errmsg(priv->database));
264+
265+ if(priv->database) sqlite3_close(priv->database);
266+ priv->database=NULL;
267+ }
268+
269+ g_free(databaseFile);
270+ }
271+
272+ /* Fill list with new database */
273+ _nojs_preferences_fill(self);
274+
275+ /* Set up availability of management buttons */
276+ gtk_widget_set_sensitive(priv->deleteAllButton, priv->database!=NULL);
277+ gtk_widget_set_sensitive(priv->list, priv->database!=NULL);
278+
279+ return;
280+}
281+
282+/* Allow-all-sites changed in check-box or manager */
283+static void _nojs_preferences_on_allow_all_sites_changed(NoJSPreferences *self,
284+ gpointer *inUserData)
285+{
286+ NoJSPreferencesPrivate *priv=self->priv;
287+ gboolean state;
288+
289+ /* Get toggle state of widget (but block signal for manager) and set in manager */
290+ g_signal_handler_block(priv->manager, priv->signalManagerChangedAllowAllSitesID);
291+
292+ state=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->allowAllSitesCheckbox));
293+ nojs_set_allow_all_sites(priv->manager, state);
294+
295+ g_signal_handler_unblock(priv->manager, priv->signalManagerChangedAllowAllSitesID);
296+}
297+
298+static void _nojs_preferences_on_manager_allow_all_sites_changed(NoJSPreferences *self,
299+ GParamSpec *inSpec,
300+ gpointer inUserData)
301+{
302+ NoJSPreferencesPrivate *priv=self->priv;
303+ NoJS *manager=NOJS(inUserData);
304+ gboolean state;
305+
306+ /* Get new value from manager */
307+ state=nojs_get_allow_all_sites(manager);
308+
309+ /* Set toggle in widget (but block signal for toggle) */
310+ g_signal_handler_block(priv->allowAllSitesCheckbox, priv->signalAllowAllSitesToggledID);
311+
312+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->allowAllSitesCheckbox), state);
313+
314+ g_signal_handler_unblock(priv->allowAllSitesCheckbox, priv->signalAllowAllSitesToggledID);
315+}
316+
317+/* Block-unknown-domains changed in check-box or manager */
318+static void _nojs_preferences_on_block_unknown_domains_changed(NoJSPreferences *self,
319+ gpointer *inUserData)
320+{
321+ NoJSPreferencesPrivate *priv=self->priv;
322+ gboolean state;
323+ NoJSPolicy policy;
324+
325+ /* Get toggle state of widget (but block signal for manager) and set in manager */
326+ g_signal_handler_block(priv->manager, priv->signalManagerChangedUnknownDomainPolicyID);
327+
328+ state=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->blockUnknownDomainsCheckbox));
329+ policy=(state ? NOJS_POLICY_BLOCK : NOJS_POLICY_ACCEPT);
330+ nojs_set_policy_for_unknown_domain(priv->manager, policy);
331+
332+ g_signal_handler_unblock(priv->manager, priv->signalManagerChangedUnknownDomainPolicyID);
333+}
334+
335+static void _nojs_preferences_on_manager_unknown_domain_policy_changed(NoJSPreferences *self,
336+ GParamSpec *inSpec,
337+ gpointer inUserData)
338+{
339+ NoJSPreferencesPrivate *priv=self->priv;
340+ NoJS *manager=NOJS(inUserData);
341+ NoJSPolicy policy;
342+ gboolean state;
343+
344+ /* Get new value from manager */
345+ policy=nojs_get_policy_for_unknown_domain(manager);
346+
347+ /* Set toggle in widget (but block signal for toggle) */
348+ g_signal_handler_block(priv->blockUnknownDomainsCheckbox, priv->signalBlockUnknownDomainsToggledID);
349+
350+ state=(policy==NOJS_POLICY_BLOCK ? TRUE : FALSE);
351+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->blockUnknownDomainsCheckbox), state);
352+
353+ g_signal_handler_unblock(priv->blockUnknownDomainsCheckbox, priv->signalBlockUnknownDomainsToggledID);
354+}
355+
356+/* Only-second-level changed in check-box or manager */
357+static void _nojs_preferences_on_check_second_level_only_changed(NoJSPreferences *self,
358+ gpointer *inUserData)
359+{
360+ NoJSPreferencesPrivate *priv=self->priv;
361+ gboolean state;
362+
363+ /* Get toggle state of widget (but block signal for manager) and set in manager */
364+ g_signal_handler_block(priv->manager, priv->signalManagerChangedCheckSecondLevelID);
365+
366+ state=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->checkSecondLevelOnlyCheckbox));
367+ nojs_set_only_second_level_domain(priv->manager, state);
368+
369+ g_signal_handler_unblock(priv->manager, priv->signalManagerChangedCheckSecondLevelID);
370+}
371+
372+static void _nojs_preferences_on_manager_only_second_level_changed(NoJSPreferences *self,
373+ GParamSpec *inSpec,
374+ gpointer inUserData)
375+{
376+ NoJSPreferencesPrivate *priv=self->priv;
377+ NoJS *manager=NOJS(inUserData);
378+ gboolean state;
379+
380+ /* Get new value from manager */
381+ state=nojs_get_only_second_level_domain(manager);
382+
383+ /* Set toggle in widget (but block signal for toggle) */
384+ g_signal_handler_block(priv->checkSecondLevelOnlyCheckbox, priv->signalCheckSecondLevelOnlyToggledID);
385+
386+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->checkSecondLevelOnlyCheckbox), state);
387+
388+ g_signal_handler_unblock(priv->checkSecondLevelOnlyCheckbox, priv->signalCheckSecondLevelOnlyToggledID);
389+}
390+
391+/* Selection in list changed */
392+void _nojs_preferences_changed_selection(NoJSPreferences *self,
393+ GtkTreeSelection *inSelection)
394+{
395+ gboolean selected=(gtk_tree_selection_count_selected_rows(inSelection)>0 ? TRUE: FALSE);
396+
397+ gtk_widget_set_sensitive(self->priv->deleteButton, selected);
398+}
399+
400+/* Delete button was clicked on selection */
401+void _nojs_preferences_on_delete_selection(NoJSPreferences *self,
402+ GtkButton *inButton)
403+{
404+ NoJSPreferencesPrivate *priv=self->priv;
405+ GList *rows, *row, *refs=NULL;
406+ GtkTreeRowReference *ref;
407+ GtkTreeModel *model=GTK_TREE_MODEL(priv->listStore);
408+ GtkTreeIter iter;
409+ GtkTreePath *path;
410+ gchar *domain;
411+ gchar *sql;
412+ gint success;
413+ gchar *error;
414+
415+ /* Get selected rows in list and create a row reference because
416+ * we will modify the model while iterating through selected rows
417+ */
418+ rows=gtk_tree_selection_get_selected_rows(priv->listSelection, &model);
419+ for(row=rows; row; row=row->next)
420+ {
421+ ref=gtk_tree_row_reference_new(model, (GtkTreePath*)row->data);
422+ refs=g_list_prepend(refs, ref);
423+ }
424+ g_list_foreach(rows,(GFunc)gtk_tree_path_free, NULL);
425+ g_list_free(rows);
426+
427+ /* Delete each selected row by its reference */
428+ for(row=refs; row; row=row->next)
429+ {
430+ /* Get domain from selected row */
431+ path=gtk_tree_row_reference_get_path((GtkTreeRowReference*)row->data);
432+ gtk_tree_model_get_iter(model, &iter, path);
433+ gtk_tree_model_get(model, &iter, DOMAIN_COLUMN, &domain, -1);
434+
435+ /* Delete domain from database */
436+ sql=sqlite3_mprintf("DELETE FROM policies WHERE site='%q';", domain);
437+ success=sqlite3_exec(priv->database,
438+ sql,
439+ NULL,
440+ NULL,
441+ &error);
442+ if(success!=SQLITE_OK || error)
443+ {
444+ if(error)
445+ {
446+ g_critical(_("Failed to execute database statement: %s"), error);
447+ sqlite3_free(error);
448+ }
449+ else g_critical(_("Failed to execute database statement: %s"), sqlite3_errmsg(priv->database));
450+ }
451+ sqlite3_free(sql);
452+
453+ /* Delete row from model */
454+ gtk_list_store_remove(priv->listStore, &iter);
455+ }
456+ g_list_foreach(refs,(GFunc)gtk_tree_row_reference_free, NULL);
457+ g_list_free(refs);
458+}
459+
460+/* Delete all button was clicked */
461+void _nojs_preferences_on_delete_all(NoJSPreferences *self,
462+ GtkButton *inButton)
463+{
464+ NoJSPreferencesPrivate *priv=self->priv;
465+ gint success;
466+ gchar *error=NULL;
467+ GtkWidget *dialog;
468+ gint dialogResponse;
469+
470+ /* Ask user if he really wants to delete all permissions */
471+ dialog=gtk_message_dialog_new(GTK_WINDOW(self),
472+ GTK_DIALOG_MODAL,
473+ GTK_MESSAGE_QUESTION,
474+ GTK_BUTTONS_YES_NO,
475+ _("Do you really want to delete all JavaScript permissions?"));
476+
477+ gtk_window_set_title(GTK_WINDOW(dialog), _("Delete all JavaScript permissions?"));
478+ gtk_window_set_icon_name(GTK_WINDOW(dialog), GTK_STOCK_PROPERTIES);
479+
480+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
481+ _("This action will delete all JavaScript permissions. "
482+ "You will be asked for permissions again for each web site visited."));
483+
484+ dialogResponse=gtk_dialog_run(GTK_DIALOG(dialog));
485+ gtk_widget_destroy(dialog);
486+
487+ if(dialogResponse==GTK_RESPONSE_NO) return;
488+
489+ /* Delete all permission */
490+ success=sqlite3_exec(priv->database,
491+ "DELETE FROM policies;",
492+ NULL,
493+ NULL,
494+ &error);
495+
496+ if(success!=SQLITE_OK || error)
497+ {
498+ if(error)
499+ {
500+ g_critical(_("Failed to execute database statement: %s"), error);
501+ sqlite3_free(error);
502+ }
503+ }
504+
505+ /* Re-setup list */
506+ _nojs_preferences_fill(self);
507+}
508+
509+/* Sorting callbacks */
510+static gint _nojs_preferences_sort_string_callback(GtkTreeModel *inModel,
511+ GtkTreeIter *inLeft,
512+ GtkTreeIter *inRight,
513+ gpointer inUserData)
514+{
515+ gchar *left, *right;
516+ gint column=GPOINTER_TO_INT(inUserData);
517+ gint result;
518+
519+ gtk_tree_model_get(inModel, inLeft, column, &left, -1);
520+ gtk_tree_model_get(inModel, inRight, column, &right, -1);
521+
522+ result=g_strcmp0(left, right);
523+
524+ g_free(left);
525+ g_free(right);
526+
527+ return(result);
528+}
529+
530+/* IMPLEMENTATION: GObject */
531+
532+/* Finalize this object */
533+static void nojs_preferences_finalize(GObject *inObject)
534+{
535+ NoJSPreferencesPrivate *priv=NOJS_PREFERENCES(inObject)->priv;
536+
537+ /* Dispose allocated resources */
538+ if(priv->database) sqlite3_close(priv->database);
539+ priv->database=NULL;
540+
541+ if(priv->manager)
542+ {
543+ if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
544+ priv->signalManagerChangedDatabaseID=0;
545+
546+ if(priv->signalManagerChangedAllowAllSitesID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedAllowAllSitesID);
547+ priv->signalManagerChangedAllowAllSitesID=0;
548+
549+ if(priv->signalManagerChangedUnknownDomainPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedUnknownDomainPolicyID);
550+ priv->signalManagerChangedUnknownDomainPolicyID=0;
551+
552+ if(priv->signalManagerChangedCheckSecondLevelID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedCheckSecondLevelID);
553+ priv->signalManagerChangedCheckSecondLevelID=0;
554+
555+ g_object_unref(priv->manager);
556+ priv->manager=NULL;
557+ }
558+
559+ /* Call parent's class finalize method */
560+ G_OBJECT_CLASS(nojs_preferences_parent_class)->finalize(inObject);
561+}
562+
563+/* Set/get properties */
564+static void nojs_preferences_set_property(GObject *inObject,
565+ guint inPropID,
566+ const GValue *inValue,
567+ GParamSpec *inSpec)
568+{
569+ NoJSPreferences *self=NOJS_PREFERENCES(inObject);
570+ NoJSPreferencesPrivate *priv=self->priv;
571+ GObject *manager;
572+
573+ switch(inPropID)
574+ {
575+ /* Construct-only properties */
576+ case PROP_MANAGER:
577+ /* Release old manager if available and disconnect signals */
578+ if(priv->manager)
579+ {
580+ if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
581+ priv->signalManagerChangedDatabaseID=0;
582+
583+ if(priv->signalManagerChangedAllowAllSitesID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedAllowAllSitesID);
584+ priv->signalManagerChangedAllowAllSitesID=0;
585+
586+ if(priv->signalManagerChangedUnknownDomainPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedUnknownDomainPolicyID);
587+ priv->signalManagerChangedUnknownDomainPolicyID=0;
588+
589+ if(priv->signalManagerChangedCheckSecondLevelID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedCheckSecondLevelID);
590+ priv->signalManagerChangedCheckSecondLevelID=0;
591+
592+ g_object_unref(priv->manager);
593+ priv->manager=NULL;
594+ }
595+
596+ /* Set new JavaScript permission manager and
597+ * listen to changes in database property
598+ */
599+ manager=g_value_get_object(inValue);
600+ if(manager)
601+ {
602+ priv->manager=g_object_ref(manager);
603+
604+ priv->signalManagerChangedDatabaseID=
605+ g_signal_connect_swapped(priv->manager,
606+ "notify::database-filename",
607+ G_CALLBACK(_nojs_preferences_on_manager_database_changed),
608+ self);
609+ _nojs_preferences_on_manager_database_changed(self, NULL, priv->manager);
610+
611+ priv->signalManagerChangedAllowAllSitesID=
612+ g_signal_connect_swapped(priv->manager,
613+ "notify::allow-all-sites",
614+ G_CALLBACK(_nojs_preferences_on_manager_allow_all_sites_changed),
615+ self);
616+ _nojs_preferences_on_manager_allow_all_sites_changed(self, NULL, priv->manager);
617+
618+ priv->signalManagerChangedUnknownDomainPolicyID=
619+ g_signal_connect_swapped(priv->manager,
620+ "notify::unknown-domain-policy",
621+ G_CALLBACK(_nojs_preferences_on_manager_unknown_domain_policy_changed),
622+ self);
623+ _nojs_preferences_on_manager_unknown_domain_policy_changed(self, NULL, priv->manager);
624+
625+ priv->signalManagerChangedCheckSecondLevelID=
626+ g_signal_connect_swapped(priv->manager,
627+ "notify::only-second-level",
628+ G_CALLBACK(_nojs_preferences_on_manager_only_second_level_changed),
629+ self);
630+ _nojs_preferences_on_manager_only_second_level_changed(self, NULL, priv->manager);
631+ }
632+ break;
633+
634+ default:
635+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
636+ break;
637+ }
638+}
639+
640+static void nojs_preferences_get_property(GObject *inObject,
641+ guint inPropID,
642+ GValue *outValue,
643+ GParamSpec *inSpec)
644+{
645+ NoJSPreferences *self=NOJS_PREFERENCES(inObject);
646+
647+ switch(inPropID)
648+ {
649+ case PROP_MANAGER:
650+ g_value_set_object(outValue, self->priv->manager);
651+ break;
652+
653+ default:
654+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
655+ break;
656+ }
657+}
658+
659+/* Class initialization
660+ * Override functions in parent classes and define properties and signals
661+ */
662+static void nojs_preferences_class_init(NoJSPreferencesClass *klass)
663+{
664+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
665+
666+ /* Override functions */
667+ gobjectClass->finalize=nojs_preferences_finalize;
668+ gobjectClass->set_property=nojs_preferences_set_property;
669+ gobjectClass->get_property=nojs_preferences_get_property;
670+
671+ /* Set up private structure */
672+ g_type_class_add_private(klass, sizeof(NoJSPreferencesPrivate));
673+
674+ /* Define properties */
675+ NoJSPreferencesProperties[PROP_MANAGER]=
676+ g_param_spec_object("manager",
677+ _("Manager instance"),
678+ _("Instance to global NoJS manager"),
679+ TYPE_NOJS,
680+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
681+
682+ g_object_class_install_properties(gobjectClass, PROP_LAST, NoJSPreferencesProperties);
683+}
684+
685+/* Object initialization
686+ * Create private structure and set up default values
687+ */
688+static void nojs_preferences_init(NoJSPreferences *self)
689+{
690+ NoJSPreferencesPrivate *priv;
691+ GtkTreeSortable *sortableList;
692+ GtkCellRenderer *renderer;
693+ GtkTreeViewColumn *column;
694+ GtkWidget *widget;
695+ gchar *dialogTitle;
696+ GtkWidget *scrolled;
697+ GtkWidget *vbox;
698+ GtkWidget *hbox;
699+ gint width, height;
700+
701+ priv=self->priv=NOJS_PREFERENCES_GET_PRIVATE(self);
702+
703+ /* Set up default values */
704+ priv->manager=NULL;
705+
706+ /* Get content area to add gui controls to */
707+ priv->contentArea=gtk_dialog_get_content_area(GTK_DIALOG(self));
708+#if GTK_CHECK_VERSION (3, 0, 0)
709+ vbox=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
710+ gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
711+#else
712+ vbox=gtk_vbox_new(FALSE, 0);
713+#endif
714+
715+ /* Set up dialog */
716+ dialogTitle=_("Configure NoJS");
717+
718+ gtk_window_set_title(GTK_WINDOW(self), dialogTitle);
719+ gtk_window_set_icon_name(GTK_WINDOW(self), GTK_STOCK_PROPERTIES);
720+
721+ sokoke_widget_get_text_size(GTK_WIDGET(self), "M", &width, &height);
722+ gtk_window_set_default_size(GTK_WINDOW(self), width*52, -1);
723+
724+ widget=sokoke_xfce_header_new(gtk_window_get_icon_name(GTK_WINDOW(self)), dialogTitle);
725+ if(widget) gtk_box_pack_start(GTK_BOX(priv->contentArea), widget, FALSE, FALSE, 0);
726+
727+ gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
728+
729+ /* Set up description */
730+ widget=gtk_label_new(NULL);
731+ gtk_label_set_markup(GTK_LABEL(widget),
732+ _("Below is a list of all web sites and the policy set for them. "
733+ "You can delete policies by marking the entries and clicking on <i>Delete</i>."));
734+ gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE);
735+ gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 4);
736+
737+ /* Set up domain list */
738+ priv->listStore=gtk_list_store_new(N_COLUMN,
739+ G_TYPE_STRING, /* DOMAIN_COLUMN */
740+ G_TYPE_STRING /* POLICY_COLUMN */);
741+
742+ sortableList=GTK_TREE_SORTABLE(priv->listStore);
743+ gtk_tree_sortable_set_sort_func(sortableList,
744+ DOMAIN_COLUMN,
745+ (GtkTreeIterCompareFunc)_nojs_preferences_sort_string_callback,
746+ GINT_TO_POINTER(DOMAIN_COLUMN),
747+ NULL);
748+ gtk_tree_sortable_set_sort_func(sortableList,
749+ POLICY_COLUMN,
750+ (GtkTreeIterCompareFunc)_nojs_preferences_sort_string_callback,
751+ GINT_TO_POINTER(POLICY_COLUMN),
752+ NULL);
753+ gtk_tree_sortable_set_sort_column_id(sortableList, DOMAIN_COLUMN, GTK_SORT_ASCENDING);
754+
755+ /* Set up domain list view */
756+ priv->list=gtk_tree_view_new_with_model(GTK_TREE_MODEL(priv->listStore));
757+
758+#if !GTK_CHECK_VERSION (3, 0, 0)
759+ gtk_widget_set_size_request(priv->list, -1, 300);
760+#endif
761+
762+ priv->listSelection=gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->list));
763+ gtk_tree_selection_set_mode(priv->listSelection, GTK_SELECTION_MULTIPLE);
764+ g_signal_connect_swapped(priv->listSelection, "changed", G_CALLBACK(_nojs_preferences_changed_selection), self);
765+
766+ renderer=gtk_cell_renderer_text_new();
767+ column=gtk_tree_view_column_new_with_attributes(_("Domain"),
768+ renderer,
769+ "text", DOMAIN_COLUMN,
770+ NULL);
771+ gtk_tree_view_column_set_sort_column_id(column, DOMAIN_COLUMN);
772+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->list), column);
773+
774+ renderer=gtk_cell_renderer_text_new();
775+ column=gtk_tree_view_column_new_with_attributes(_("Policy"),
776+ renderer,
777+ "text", POLICY_COLUMN,
778+ NULL);
779+ gtk_tree_view_column_set_sort_column_id(column, POLICY_COLUMN);
780+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->list), column);
781+
782+ scrolled=gtk_scrolled_window_new(NULL, NULL);
783+#if GTK_CHECK_VERSION (3, 0, 0)
784+ gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolled), height*10);
785+#endif
786+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
787+ gtk_container_add(GTK_CONTAINER(scrolled), priv->list);
788+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
789+ gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 5);
790+
791+ /* Set up JavaScript domain list management buttons */
792+#if GTK_CHECK_VERSION (3, 0, 0)
793+ hbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
794+ gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
795+#else
796+ hbox=gtk_hbox_new(FALSE, 0);
797+#endif
798+
799+ priv->deleteButton=gtk_button_new_from_stock(GTK_STOCK_DELETE);
800+ gtk_widget_set_sensitive(priv->deleteButton, FALSE);
801+ gtk_container_add(GTK_CONTAINER(hbox), priv->deleteButton);
802+ g_signal_connect_swapped(priv->deleteButton, "clicked", G_CALLBACK(_nojs_preferences_on_delete_selection), self);
803+
804+ priv->deleteAllButton=gtk_button_new_with_mnemonic(_("Delete _all"));
805+ gtk_button_set_image(GTK_BUTTON(priv->deleteAllButton), gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON));
806+ gtk_widget_set_sensitive(priv->deleteAllButton, FALSE);
807+ gtk_container_add(GTK_CONTAINER(hbox), priv->deleteAllButton);
808+ g_signal_connect_swapped(priv->deleteAllButton, "clicked", G_CALLBACK(_nojs_preferences_on_delete_all), self);
809+
810+ gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
811+
812+ /* Add "allow-all-sites" checkbox */
813+ priv->allowAllSitesCheckbox=gtk_check_button_new_with_mnemonic(_("A_llow scripts at all sites"));
814+ priv->signalAllowAllSitesToggledID=g_signal_connect_swapped(priv->allowAllSitesCheckbox,
815+ "toggled",
816+ G_CALLBACK(_nojs_preferences_on_allow_all_sites_changed),
817+ self);
818+ gtk_box_pack_start(GTK_BOX(vbox), priv->allowAllSitesCheckbox, TRUE, TRUE, 5);
819+
820+ /* Add "block-unknown-domains" checkbox */
821+ priv->blockUnknownDomainsCheckbox=gtk_check_button_new_with_mnemonic(_("Bloc_k scripts at unknown domains by default"));
822+ priv->signalBlockUnknownDomainsToggledID=g_signal_connect_swapped(priv->blockUnknownDomainsCheckbox,
823+ "toggled",
824+ G_CALLBACK(_nojs_preferences_on_block_unknown_domains_changed),
825+ self);
826+ gtk_box_pack_start(GTK_BOX(vbox), priv->blockUnknownDomainsCheckbox, TRUE, TRUE, 5);
827+
828+ /* Add "check-second-level-only" checkbox */
829+ priv->checkSecondLevelOnlyCheckbox=gtk_check_button_new_with_mnemonic(_("S_et permissions on second-level domain"));
830+ priv->signalCheckSecondLevelOnlyToggledID=g_signal_connect_swapped(priv->checkSecondLevelOnlyCheckbox,
831+ "toggled",
832+ G_CALLBACK(_nojs_preferences_on_check_second_level_only_changed),
833+ self);
834+ gtk_box_pack_start(GTK_BOX(vbox), priv->checkSecondLevelOnlyCheckbox, TRUE, TRUE, 5);
835+
836+ /* Finalize setup of content area */
837+ gtk_container_add(GTK_CONTAINER(priv->contentArea), vbox);
838+}
839+
840+/* Implementation: Public API */
841+
842+/* Create new object */
843+GtkWidget* nojs_preferences_new(NoJS *inManager)
844+{
845+ return(g_object_new(TYPE_NOJS_PREFERENCES,
846+ "manager", inManager,
847+ NULL));
848+}
849
850=== added file 'extensions/nojs/nojs-preferences.h'
851--- extensions/nojs/nojs-preferences.h 1970-01-01 00:00:00 +0000
852+++ extensions/nojs/nojs-preferences.h 2013-07-29 19:51:25 +0000
853@@ -0,0 +1,55 @@
854+/*
855+ Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
856+
857+ This library is free software; you can redistribute it and/or
858+ modify it under the terms of the GNU Lesser General Public
859+ License as published by the Free Software Foundation; either
860+ version 2.1 of the License, or (at your option) any later version.
861+
862+ See the file COPYING for the full license text.
863+*/
864+
865+#ifndef __NOJS_PREFERENCES__
866+#define __NOJS_PREFERENCES__
867+
868+#include "config.h"
869+#include <midori/midori.h>
870+
871+#include "nojs.h"
872+
873+G_BEGIN_DECLS
874+
875+#define TYPE_NOJS_PREFERENCES (nojs_preferences_get_type())
876+#define NOJS_PREFERENCES(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NOJS_PREFERENCES, NoJSPreferences))
877+#define IS_NOJS_PREFERENCES(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_NOJS_PREFERENCES))
878+#define NOJS_PREFERENCES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_NOJS_PREFERENCES, NoJSPreferencesClass))
879+#define IS_NOJS_PREFERENCES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_NOJS_PREFERENCES))
880+#define NOJS_PREFERENCES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_NOJS_PREFERENCES, NoJSPreferencesClass))
881+
882+typedef struct _NoJSPreferences NoJSPreferences;
883+typedef struct _NoJSPreferencesClass NoJSPreferencesClass;
884+typedef struct _NoJSPreferencesPrivate NoJSPreferencesPrivate;
885+
886+struct _NoJSPreferences
887+{
888+ /* Parent instance */
889+ GtkDialog parent_instance;
890+
891+ /* Private structure */
892+ NoJSPreferencesPrivate *priv;
893+};
894+
895+struct _NoJSPreferencesClass
896+{
897+ /* Parent class */
898+ GtkDialogClass parent_class;
899+};
900+
901+/* Public API */
902+GType nojs_preferences_get_type(void);
903+
904+GtkWidget* nojs_preferences_new(NoJS *inManager);
905+
906+G_END_DECLS
907+
908+#endif /* __NOJS_PREFERENCES__ */
909
910=== added file 'extensions/nojs/nojs-view.c'
911--- extensions/nojs/nojs-view.c 1970-01-01 00:00:00 +0000
912+++ extensions/nojs/nojs-view.c 2013-07-29 19:51:25 +0000
913@@ -0,0 +1,804 @@
914+/*
915+ Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
916+
917+ This library is free software; you can redistribute it and/or
918+ modify it under the terms of the GNU Lesser General Public
919+ License as published by the Free Software Foundation; either
920+ version 2.1 of the License, or (at your option) any later version.
921+
922+ See the file COPYING for the full license text.
923+*/
924+
925+#include "nojs-view.h"
926+#include "nojs-preferences.h"
927+
928+/* Define this class in GObject system */
929+G_DEFINE_TYPE(NoJSView,
930+ nojs_view,
931+ G_TYPE_OBJECT)
932+
933+/* Properties */
934+enum
935+{
936+ PROP_0,
937+
938+ PROP_MANAGER,
939+ PROP_BROWSER,
940+ PROP_VIEW,
941+ PROP_MENU_ICON_STATE,
942+
943+ PROP_LAST
944+};
945+
946+static GParamSpec* NoJSViewProperties[PROP_LAST]={ 0, };
947+
948+/* Private structure - access only by public API if needed */
949+#define NOJS_VIEW_GET_PRIVATE(obj) \
950+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_NOJS_VIEW, NoJSViewPrivate))
951+
952+struct _NoJSViewPrivate
953+{
954+ /* Extension related */
955+ NoJS *manager;
956+ MidoriBrowser *browser;
957+ MidoriView *view;
958+
959+ GtkWidget *menu;
960+ gboolean menuPolicyWasChanged;
961+ NoJSMenuIconState menuIconState;
962+
963+ GSList *resourceURIs;
964+};
965+
966+/* IMPLEMENTATION: Private variables and methods */
967+
968+/* Preferences of this extension should be opened */
969+static void _nojs_view_on_preferences_response(GtkWidget* inDialog,
970+ gint inResponse,
971+ gpointer *inUserData)
972+{
973+ gtk_widget_destroy(inDialog);
974+}
975+
976+static void _nojs_view_on_open_preferences(NoJSView *self, gpointer inUserData)
977+{
978+ g_return_if_fail(NOJS_IS_VIEW(self));
979+
980+ NoJSViewPrivate *priv=self->priv;
981+
982+ /* Show preferences window */
983+ GtkWidget* dialog;
984+
985+ dialog=nojs_preferences_new(priv->manager);
986+ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
987+ g_signal_connect(dialog, "response", G_CALLBACK (_nojs_view_on_preferences_response), self);
988+ gtk_widget_show_all(dialog);
989+}
990+
991+/* Selection was done in menu */
992+static void _nojs_view_on_menu_selection_done(NoJSView *self, gpointer inUserData)
993+{
994+ g_return_if_fail(NOJS_IS_VIEW(self));
995+
996+ NoJSViewPrivate *priv=self->priv;
997+
998+ /* Check if any policy was changed and reload page */
999+ if(priv->menuPolicyWasChanged!=FALSE)
1000+ {
1001+ /* Reset flag that any policy was changed */
1002+ priv->menuPolicyWasChanged=FALSE;
1003+
1004+ /* Reload page */
1005+ midori_view_reload(priv->view, FALSE);
1006+g_message("%s: Reloading page %s as policy has changed", __func__, midori_view_get_display_uri(priv->view));
1007+ }
1008+}
1009+
1010+/* Destroy menu */
1011+static void _nojs_view_destroy_menu(NoJSView *self)
1012+{
1013+ g_return_if_fail(NOJS_IS_VIEW(self));
1014+ g_return_if_fail(self->priv->menu!=NULL);
1015+
1016+ NoJSViewPrivate *priv=self->priv;
1017+
1018+ /* Empty menu and list of domains added to menu */
1019+ gtk_widget_destroy(priv->menu);
1020+ priv->menu=NULL;
1021+
1022+ /* Reset menu icon to default state */
1023+ priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
1024+ g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
1025+}
1026+
1027+/* Create empty menu */
1028+static void _nojs_view_create_empty_menu(NoJSView *self)
1029+{
1030+ g_return_if_fail(NOJS_IS_VIEW(self));
1031+ g_return_if_fail(self->priv->menu==NULL);
1032+
1033+ NoJSViewPrivate *priv=self->priv;
1034+ GtkWidget *item;
1035+
1036+ /* Create new menu and set up default items */
1037+ priv->menu=gtk_menu_new();
1038+
1039+ item=gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
1040+ g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_open_preferences), self);
1041+ gtk_menu_shell_prepend(GTK_MENU_SHELL(priv->menu), item);
1042+ gtk_widget_show_all(item);
1043+
1044+ /* Reset flag that any policy was changed */
1045+ priv->menuPolicyWasChanged=FALSE;
1046+
1047+ /* Reset menu icon to default state */
1048+ priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
1049+ g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
1050+
1051+ /* Connect signal to menu */
1052+ g_signal_connect_swapped(priv->menu, "selection-done", G_CALLBACK(_nojs_view_on_menu_selection_done), self);
1053+}
1054+
1055+/* Change visibility state of menu item for a domain depending on policy */
1056+static gboolean _nojs_view_menu_item_change_policy(NoJSView *self, const gchar *inDomain, NoJSPolicy inPolicy)
1057+{
1058+ g_return_val_if_fail(NOJS_IS_VIEW(self), FALSE);
1059+ g_return_val_if_fail(inDomain, FALSE);
1060+
1061+ NoJSViewPrivate *priv=self->priv;
1062+ GList *items, *iter;
1063+ gboolean updated;
1064+
1065+ /* Handle accept-for-session like accept when showing or hiding menu items */
1066+ if(inPolicy==NOJS_POLICY_ACCEPT_TEMPORARILY) inPolicy=NOJS_POLICY_ACCEPT;
1067+
1068+ /* Update menu items */
1069+ updated=FALSE;
1070+ items=gtk_container_get_children(GTK_CONTAINER(priv->menu));
1071+ for(iter=items; iter; iter=iter->next)
1072+ {
1073+ /* Only check and update menu items (not separators and so on) */
1074+ if(GTK_IS_MENU_ITEM(iter->data))
1075+ {
1076+ GtkMenuItem *item=GTK_MENU_ITEM(iter->data);
1077+ const gchar *itemDomain;
1078+ NoJSPolicy itemPolicy;
1079+
1080+ itemDomain=(const gchar*)g_object_get_data(G_OBJECT(item), "domain");
1081+ itemPolicy=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "policy"));
1082+
1083+ /* Handle accept-for-session like accept when showing or hiding menu items */
1084+ if(itemPolicy==NOJS_POLICY_ACCEPT_TEMPORARILY) itemPolicy=NOJS_POLICY_ACCEPT;
1085+
1086+ /* If menu item has "domain"-data update its visibility state
1087+ * depending on matching policy
1088+ */
1089+ if(g_strcmp0(itemDomain, inDomain)==0)
1090+ {
1091+ if(itemPolicy==inPolicy) gtk_widget_hide(GTK_WIDGET(item));
1092+ else gtk_widget_show_all(GTK_WIDGET(item));
1093+
1094+ /* Set flag that at least one menu item was updated */
1095+ updated=TRUE;
1096+ }
1097+ }
1098+ }
1099+ g_list_free(items);
1100+
1101+ /* Return flag indicating if at least one menu item was updated */
1102+ return(updated);
1103+}
1104+
1105+/* A menu item was selected */
1106+static void _nojs_view_on_menu_item_activate(NoJSView *self, gpointer inUserData)
1107+{
1108+ g_return_if_fail(NOJS_IS_VIEW(self));
1109+ g_return_if_fail(GTK_IS_MENU_ITEM(inUserData));
1110+
1111+ NoJSViewPrivate *priv=self->priv;
1112+ GtkMenuItem *item=GTK_MENU_ITEM(inUserData);
1113+ const gchar *domain;
1114+ NoJSPolicy policy;
1115+
1116+ /* Get domain and policy to set */
1117+ domain=(const gchar*)g_object_get_data(G_OBJECT(item), "domain");
1118+ policy=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "policy"));
1119+ g_return_if_fail(domain);
1120+ g_return_if_fail(policy>=NOJS_POLICY_ACCEPT && policy<=NOJS_POLICY_BLOCK);
1121+
1122+ /* Set policy for domain and update menu items */
1123+ _nojs_view_menu_item_change_policy(self, domain, policy);
1124+ nojs_set_policy(priv->manager, domain, policy);
1125+
1126+ /* Set flag that a policy was changed */
1127+ priv->menuPolicyWasChanged=TRUE;
1128+}
1129+
1130+/* Add site to menu */
1131+static void _nojs_view_add_site_to_menu(NoJSView *self, const gchar *inDomain, NoJSPolicy inPolicy)
1132+{
1133+ g_return_if_fail(NOJS_IS_VIEW(self));
1134+ g_return_if_fail(inDomain);
1135+
1136+ NoJSViewPrivate *priv=self->priv;
1137+ GtkWidget *item;
1138+ gchar *itemLabel;
1139+ GtkWidget *itemImage;
1140+ static gint INSERT_POSITION=1;
1141+ NoJSMenuIconState newMenuIconState;
1142+
1143+ /* Create menu object if not available */
1144+ if(!priv->menu) _nojs_view_create_empty_menu(self);
1145+
1146+ /* Check if domain was already added to menu. If it exists just update it. */
1147+ if(_nojs_view_menu_item_change_policy(self, inDomain, inPolicy)==TRUE) return;
1148+
1149+ /* Add menu item(s) for domain */
1150+ itemLabel=g_strdup_printf(_("Deny %s"), inDomain);
1151+ item=gtk_image_menu_item_new_with_label(itemLabel);
1152+ itemImage=gtk_image_new_from_stock (GTK_STOCK_NO, GTK_ICON_SIZE_MENU);
1153+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
1154+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
1155+ gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
1156+ if(inPolicy!=NOJS_POLICY_BLOCK) gtk_widget_show_all(item);
1157+ g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
1158+ g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_BLOCK));
1159+ g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
1160+ g_free(itemLabel);
1161+
1162+ itemLabel=g_strdup_printf(_("Allow %s"), inDomain);
1163+ item=gtk_image_menu_item_new_with_label(itemLabel);
1164+ itemImage=gtk_image_new_from_stock (GTK_STOCK_YES, GTK_ICON_SIZE_MENU);
1165+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
1166+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
1167+ gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
1168+ if(inPolicy!=NOJS_POLICY_ACCEPT && inPolicy!=NOJS_POLICY_ACCEPT_TEMPORARILY) gtk_widget_show_all(item);
1169+ g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
1170+ g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_ACCEPT));
1171+ g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
1172+ g_free(itemLabel);
1173+
1174+ itemLabel=g_strdup_printf(_("Allow %s this session"), inDomain);
1175+ item=gtk_image_menu_item_new_with_label(itemLabel);
1176+ itemImage=gtk_image_new_from_stock (GTK_STOCK_OK, GTK_ICON_SIZE_MENU);
1177+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
1178+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
1179+ gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
1180+ if(inPolicy!=NOJS_POLICY_ACCEPT && inPolicy!=NOJS_POLICY_ACCEPT_TEMPORARILY) gtk_widget_show_all(item);
1181+ g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
1182+ g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_ACCEPT_TEMPORARILY));
1183+ g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
1184+ g_free(itemLabel);
1185+
1186+ /* Add seperator to seperate actions for this domain from the other domains */
1187+ item=gtk_separator_menu_item_new();
1188+ gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
1189+ gtk_widget_show_all(item);
1190+
1191+ /* Determine state of status icon */
1192+ if(priv->menuIconState!=NOJS_MENU_ICON_STATE_MIXED)
1193+ {
1194+ switch(inPolicy)
1195+ {
1196+ case NOJS_POLICY_ACCEPT:
1197+ case NOJS_POLICY_ACCEPT_TEMPORARILY:
1198+ newMenuIconState=NOJS_MENU_ICON_STATE_ALLOWED;
1199+ break;
1200+
1201+ case NOJS_POLICY_BLOCK:
1202+ newMenuIconState=NOJS_MENU_ICON_STATE_DENIED;
1203+ break;
1204+
1205+ default:
1206+ newMenuIconState=NOJS_MENU_ICON_STATE_MIXED;
1207+ break;
1208+ }
1209+
1210+ if(priv->menuIconState==NOJS_MENU_ICON_STATE_UNDETERMINED ||
1211+ priv->menuIconState!=newMenuIconState)
1212+ {
1213+ priv->menuIconState=newMenuIconState;
1214+ g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
1215+ }
1216+ }
1217+}
1218+
1219+/* Status of loading a site has changed */
1220+static void _nojs_view_on_load_status_changed(NoJSView *self, GParamSpec *inSpec, gpointer inUserData)
1221+{
1222+ g_return_if_fail(NOJS_IS_VIEW(self));
1223+ g_return_if_fail(WEBKIT_IS_WEB_VIEW(inUserData));
1224+
1225+ NoJSViewPrivate *priv=self->priv;
1226+ WebKitWebView *webkitView=WEBKIT_WEB_VIEW(inUserData);
1227+ WebKitWebSettings *settings=webkit_web_view_get_settings(webkitView);
1228+ WebKitLoadStatus status;
1229+ SoupURI *uri;
1230+
1231+ /* Get URI of document loading/loaded */
1232+ uri=soup_uri_new(webkit_web_view_get_uri(webkitView));
1233+
1234+ /* Check load status */
1235+ status=webkit_web_view_get_load_status(webkitView);
1236+
1237+ /* Check if a view was emptied, e.g. for a new document going to be loaded soon */
1238+ if(status==WEBKIT_LOAD_PROVISIONAL)
1239+ {
1240+ /* Create a new empty menu */
1241+ _nojs_view_destroy_menu(self);
1242+ _nojs_view_create_empty_menu(self);
1243+
1244+ /* Free list of resource URIs, that's the list of URIs for all resources
1245+ * of a page
1246+ */
1247+ if(priv->resourceURIs)
1248+ {
1249+ g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
1250+ priv->resourceURIs=NULL;
1251+ }
1252+ }
1253+
1254+ /* Check if document loading is going to start. Do not check special pages. */
1255+ if(status==WEBKIT_LOAD_COMMITTED &&
1256+ uri &&
1257+ uri->scheme &&
1258+ g_strcmp0(uri->scheme, "about")!=0)
1259+ {
1260+ /* Check if domain is black-listed or white-listed and enable or
1261+ * disable javascript accordingly. But if settings match already
1262+ * the state it should get do not set it again to avoid reloads of page.
1263+ */
1264+ gchar *domain;
1265+ NoJSPolicy policy;
1266+ gboolean currentScriptsEnabled;
1267+ gboolean newScriptsEnabled;
1268+
1269+ domain=nojs_get_domain(priv->manager, uri);
1270+ policy=nojs_get_policy(priv->manager, domain);
1271+ if(policy==NOJS_POLICY_UNDETERMINED)
1272+ {
1273+ policy=nojs_get_policy_for_unknown_domain(priv->manager);
1274+ // TODO: Show nick_name of policy (enum) to use in warning
1275+ g_warning("Got invalid policy. Using default policy for unknown domains.");
1276+ }
1277+
1278+ newScriptsEnabled=(policy==NOJS_POLICY_BLOCK ? FALSE : TRUE);
1279+ g_object_get(G_OBJECT(settings), "enable-scripts", &currentScriptsEnabled, NULL);
1280+
1281+ if(newScriptsEnabled!=currentScriptsEnabled)
1282+ {
1283+ g_object_set(G_OBJECT(settings), "enable-scripts", newScriptsEnabled, NULL);
1284+ // TODO: Set uri also to ensure this uri is going to be reloaded
1285+ }
1286+
1287+ _nojs_view_add_site_to_menu(self, domain, policy);
1288+ if(domain) g_free(domain);
1289+ }
1290+
1291+ /* Free allocated resources */
1292+ if(uri) soup_uri_free(uri);
1293+}
1294+
1295+/* A request is going to sent */
1296+static void _nojs_view_on_resource_request_starting(NoJSView *self,
1297+ WebKitWebFrame *inFrame,
1298+ WebKitWebResource *inResource,
1299+ WebKitNetworkRequest *inRequest,
1300+ WebKitNetworkResponse *inResponse,
1301+ gpointer inUserData)
1302+{
1303+ g_return_if_fail(NOJS_IS_VIEW(self));
1304+
1305+ NoJSViewPrivate *priv=self->priv;
1306+ SoupMessage *message;
1307+ SoupURI *uri;
1308+ gchar *uriText;
1309+
1310+ /* Remember resource URIs requesting */
1311+ message=(inRequest ? webkit_network_request_get_message(inRequest) : NULL);
1312+ if(message)
1313+ {
1314+ uri=soup_message_get_uri(message);
1315+ if(uri)
1316+ {
1317+ uriText=soup_uri_to_string(uri, FALSE);
1318+ priv->resourceURIs=g_slist_prepend(priv->resourceURIs, uriText);
1319+ }
1320+ }
1321+
1322+ message=(inResponse ? webkit_network_response_get_message(inResponse) : NULL);
1323+ if(message)
1324+ {
1325+ uri=soup_message_get_uri(message);
1326+ if(uri)
1327+ {
1328+ uriText=soup_uri_to_string(uri, FALSE);
1329+ priv->resourceURIs=g_slist_prepend(priv->resourceURIs, uriText);
1330+ }
1331+ }
1332+}
1333+
1334+/* A policy has changed */
1335+static void _nojs_view_on_policy_changed(NoJSView *self, gchar *inDomain, gpointer inUserData)
1336+{
1337+ g_return_if_fail(NOJS_IS_VIEW(self));
1338+ g_return_if_fail(inDomain);
1339+
1340+ NoJSViewPrivate *priv=self->priv;
1341+ GList *items, *iter;
1342+ gboolean reloaded;
1343+
1344+ /* Check if the policy of a domain has changed this view has referenced resources to */
1345+ reloaded=FALSE;
1346+ items=gtk_container_get_children(GTK_CONTAINER(priv->menu));
1347+ for(iter=items; reloaded==FALSE && iter; iter=iter->next)
1348+ {
1349+ if(GTK_IS_MENU_ITEM(iter->data))
1350+ {
1351+ const gchar *itemDomain;
1352+
1353+ /* Check if domain matches menu item */
1354+ itemDomain=(const gchar*)g_object_get_data(G_OBJECT(iter->data), "domain");
1355+ if(g_strcmp0(itemDomain, inDomain)==0)
1356+ {
1357+ /* Found domain in our menu so reload page */
1358+ midori_view_reload(priv->view, FALSE);
1359+ reloaded=TRUE;
1360+ }
1361+ }
1362+ }
1363+ g_list_free(items);
1364+}
1365+
1366+/* A javascript URI is going to loaded or blocked */
1367+static void _nojs_view_on_uri_load_policy_status(NoJSView *self, gchar *inURI, NoJSPolicy inPolicy, gpointer inUserData)
1368+{
1369+ g_return_if_fail(NOJS_IS_VIEW(self));
1370+
1371+ NoJSViewPrivate *priv=self->priv;
1372+ GSList *iter;
1373+ gchar *checkURI;
1374+
1375+ /* Check if uri (accepted or blocked) might be one of ours */
1376+ for(iter=priv->resourceURIs; iter; iter=iter->next)
1377+ {
1378+ checkURI=(gchar*)iter->data;
1379+ if(g_strcmp0(checkURI, inURI)==0)
1380+ {
1381+ SoupURI *uri;
1382+ gchar *domain;
1383+
1384+ uri=soup_uri_new(inURI);
1385+ domain=nojs_get_domain(priv->manager, uri);
1386+ if(domain)
1387+ {
1388+ _nojs_view_add_site_to_menu(self, domain, inPolicy);
1389+ g_free(domain);
1390+ }
1391+
1392+ soup_uri_free(uri);
1393+ break;
1394+ }
1395+ }
1396+}
1397+
1398+/* Property "view" has changed */
1399+static void _nojs_view_on_view_changed(NoJSView *self, MidoriView *inView)
1400+{
1401+ NoJSViewPrivate *priv=self->priv;
1402+ WebKitWebView *webkitView;
1403+
1404+ /* Disconnect signal on old view */
1405+ if(priv->view)
1406+ {
1407+ webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(priv->view));
1408+ g_signal_handlers_disconnect_by_data(webkitView, self);
1409+ g_object_set_data(G_OBJECT(priv->view), "nojs-view-instance", NULL);
1410+ g_object_unref(priv->view);
1411+ priv->view=NULL;
1412+ }
1413+
1414+ /* Set new view if valid pointer */
1415+ if(!inView) return;
1416+
1417+ priv->view=g_object_ref(inView);
1418+ g_object_set_data(G_OBJECT(priv->view), "nojs-view-instance", self);
1419+
1420+ /* Listen to changes of load-status in view */
1421+ webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(priv->view));
1422+ g_signal_connect_swapped(webkitView, "notify::load-status", G_CALLBACK(_nojs_view_on_load_status_changed), self);
1423+ g_signal_connect_swapped(webkitView, "resource-request-starting", G_CALLBACK(_nojs_view_on_resource_request_starting), self);
1424+
1425+ /* Create empty menu */
1426+ _nojs_view_destroy_menu(self);
1427+ _nojs_view_create_empty_menu(self);
1428+
1429+ /* Release list of resource URIs */
1430+ if(priv->resourceURIs)
1431+ {
1432+ g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
1433+ priv->resourceURIs=NULL;
1434+ }
1435+}
1436+
1437+/* This extension is going to be deactivated */
1438+static void _nojs_view_on_extension_deactivated(NoJSView *self, MidoriExtension *inExtension)
1439+{
1440+ g_return_if_fail(NOJS_IS_VIEW(self));
1441+
1442+ /* Dispose allocated resources by unreferencing ourselve */
1443+ g_object_unref(self);
1444+}
1445+
1446+/* Property "manager" has changed */
1447+static void _nojs_view_on_manager_changed(NoJSView *self, NoJS *inNoJS)
1448+{
1449+ g_return_if_fail(NOJS_IS_VIEW(self));
1450+ g_return_if_fail(!inNoJS || IS_NOJS(inNoJS));
1451+
1452+ NoJSViewPrivate *priv=self->priv;
1453+ MidoriExtension *extension;
1454+
1455+ /* Release reference to old manager and clean up */
1456+ if(priv->manager)
1457+ {
1458+ g_object_get(priv->manager, "extension", &extension, NULL);
1459+ g_signal_handlers_disconnect_by_data(extension, self);
1460+ g_object_unref(extension);
1461+
1462+ g_signal_handlers_disconnect_by_data(priv->manager, self);
1463+ g_object_unref(priv->manager);
1464+ priv->manager=NULL;
1465+ }
1466+
1467+ /* Set new view if valid pointer */
1468+ if(!inNoJS) return;
1469+
1470+ priv->manager=g_object_ref(inNoJS);
1471+
1472+ /* Connect signals to manager */
1473+ g_signal_connect_swapped(priv->manager, "uri-load-policy-status", G_CALLBACK(_nojs_view_on_uri_load_policy_status), self);
1474+ g_signal_connect_swapped(priv->manager, "policy-changed", G_CALLBACK(_nojs_view_on_policy_changed), self);
1475+
1476+ /* Connect signal to get noticed when extension is going to be deactivated
1477+ * to release all references to GObjects
1478+ */
1479+ g_object_get(priv->manager, "extension", &extension, NULL);
1480+ g_signal_connect_swapped(extension, "deactivate", G_CALLBACK(_nojs_view_on_extension_deactivated), self);
1481+ g_object_unref(extension);
1482+}
1483+
1484+/* IMPLEMENTATION: GObject */
1485+
1486+/* Finalize this object */
1487+static void nojs_view_finalize(GObject *inObject)
1488+{
1489+ NoJSView *self=NOJS_VIEW(inObject);
1490+ NoJSViewPrivate *priv=self->priv;
1491+
1492+ /* Dispose allocated resources */
1493+ if(priv->manager)
1494+ {
1495+ MidoriExtension *extension;
1496+
1497+ g_object_get(priv->manager, "extension", &extension, NULL);
1498+ g_signal_handlers_disconnect_by_data(extension, self);
1499+ g_object_unref(extension);
1500+
1501+ g_signal_handlers_disconnect_by_data(priv->manager, self);
1502+ g_object_unref(priv->manager);
1503+ priv->manager=NULL;
1504+ }
1505+
1506+ if(priv->browser)
1507+ {
1508+ g_object_unref(priv->browser);
1509+ priv->browser=NULL;
1510+ }
1511+
1512+ if(priv->view)
1513+ {
1514+ _nojs_view_on_view_changed(self, NULL);
1515+ }
1516+
1517+ if(priv->menu)
1518+ {
1519+ gtk_widget_destroy(priv->menu);
1520+ priv->menu=NULL;
1521+ }
1522+
1523+ if(priv->resourceURIs)
1524+ {
1525+ g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
1526+ priv->resourceURIs=NULL;
1527+ }
1528+
1529+ /* Call parent's class finalize method */
1530+ G_OBJECT_CLASS(nojs_view_parent_class)->finalize(inObject);
1531+}
1532+
1533+/* Set/get properties */
1534+static void nojs_view_set_property(GObject *inObject,
1535+ guint inPropID,
1536+ const GValue *inValue,
1537+ GParamSpec *inSpec)
1538+{
1539+ NoJSView *self=NOJS_VIEW(inObject);
1540+
1541+ switch(inPropID)
1542+ {
1543+ /* Construct-only properties */
1544+ case PROP_MANAGER:
1545+ _nojs_view_on_manager_changed(self, NOJS(g_value_get_object(inValue)));
1546+ break;
1547+
1548+ case PROP_BROWSER:
1549+ if(self->priv->browser) g_object_unref(self->priv->browser);
1550+ self->priv->browser=g_object_ref(g_value_get_object(inValue));
1551+ break;
1552+
1553+ case PROP_VIEW:
1554+ _nojs_view_on_view_changed(self, MIDORI_VIEW(g_value_get_object(inValue)));
1555+ break;
1556+
1557+ default:
1558+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
1559+ break;
1560+ }
1561+}
1562+
1563+static void nojs_view_get_property(GObject *inObject,
1564+ guint inPropID,
1565+ GValue *outValue,
1566+ GParamSpec *inSpec)
1567+{
1568+ NoJSView *self=NOJS_VIEW(inObject);
1569+
1570+ switch(inPropID)
1571+ {
1572+ case PROP_MANAGER:
1573+ g_value_set_object(outValue, self->priv->manager);
1574+ break;
1575+
1576+ case PROP_BROWSER:
1577+ g_value_set_object(outValue, self->priv->browser);
1578+ break;
1579+
1580+ case PROP_VIEW:
1581+ g_value_set_object(outValue, self->priv->view);
1582+ break;
1583+
1584+ case PROP_MENU_ICON_STATE:
1585+ g_value_set_enum(outValue, self->priv->menuIconState);
1586+ break;
1587+
1588+ default:
1589+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
1590+ break;
1591+ }
1592+}
1593+
1594+/* Class initialization
1595+ * Override functions in parent classes and define properties and signals
1596+ */
1597+static void nojs_view_class_init(NoJSViewClass *klass)
1598+{
1599+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
1600+
1601+ /* Override functions */
1602+ gobjectClass->finalize=nojs_view_finalize;
1603+ gobjectClass->set_property=nojs_view_set_property;
1604+ gobjectClass->get_property=nojs_view_get_property;
1605+
1606+ /* Set up private structure */
1607+ g_type_class_add_private(klass, sizeof(NoJSViewPrivate));
1608+
1609+ /* Define properties */
1610+ NoJSViewProperties[PROP_MANAGER]=
1611+ g_param_spec_object("manager",
1612+ _("Manager instance"),
1613+ _("Instance to global NoJS manager"),
1614+ TYPE_NOJS,
1615+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
1616+
1617+ NoJSViewProperties[PROP_BROWSER]=
1618+ g_param_spec_object("browser",
1619+ _("Browser window"),
1620+ _("The Midori browser instance this view belongs to"),
1621+ MIDORI_TYPE_BROWSER,
1622+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
1623+
1624+ NoJSViewProperties[PROP_VIEW]=
1625+ g_param_spec_object("view",
1626+ _("View"),
1627+ _("The Midori view instance this view belongs to"),
1628+ MIDORI_TYPE_VIEW,
1629+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
1630+
1631+ NoJSViewProperties[PROP_MENU_ICON_STATE]=
1632+ g_param_spec_enum("menu-icon-state",
1633+ _("Menu icon state"),
1634+ _("State of menu icon to show in status bar"),
1635+ NOJS_TYPE_MENU_ICON_STATE,
1636+ NOJS_MENU_ICON_STATE_UNDETERMINED,
1637+ G_PARAM_READABLE);
1638+
1639+ g_object_class_install_properties(gobjectClass, PROP_LAST, NoJSViewProperties);
1640+}
1641+
1642+/* Object initialization
1643+ * Create private structure and set up default values
1644+ */
1645+static void nojs_view_init(NoJSView *self)
1646+{
1647+ NoJSViewPrivate *priv;
1648+
1649+ priv=self->priv=NOJS_VIEW_GET_PRIVATE(self);
1650+
1651+ /* Set up default values */
1652+ priv->manager=NULL;
1653+ priv->browser=NULL;
1654+ priv->view=NULL;
1655+
1656+ priv->menu=NULL;
1657+ priv->menuPolicyWasChanged=FALSE;
1658+ priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
1659+
1660+ priv->resourceURIs=NULL;
1661+
1662+ /* Create empty menu */
1663+ _nojs_view_create_empty_menu(self);
1664+}
1665+
1666+/* Implementation: Public API */
1667+
1668+/* Create new object */
1669+NoJSView* nojs_view_new(NoJS *inNoJS, MidoriBrowser *inBrowser, MidoriView *inView)
1670+{
1671+ return(g_object_new(TYPE_NOJS_VIEW,
1672+ "manager", inNoJS,
1673+ "browser", inBrowser,
1674+ "view", inView,
1675+ NULL));
1676+}
1677+
1678+/* Get menu widget for this view */
1679+GtkMenu* nojs_view_get_menu(NoJSView *self)
1680+{
1681+ g_return_val_if_fail(NOJS_IS_VIEW(self), NULL);
1682+
1683+ return(GTK_MENU(self->priv->menu));
1684+}
1685+
1686+/* Get image used for menu icon in status bar */
1687+NoJSMenuIconState nojs_view_get_menu_icon_state(NoJSView *self)
1688+{
1689+ g_return_val_if_fail(NOJS_IS_VIEW(self), NOJS_MENU_ICON_STATE_UNDETERMINED);
1690+
1691+ return(self->priv->menuIconState);
1692+}
1693+
1694+/************************************************************************************/
1695+
1696+/* Implementation: Enumeration */
1697+GType nojs_menu_icon_state_get_type(void)
1698+{
1699+ static volatile gsize g_define_type_id__volatile=0;
1700+
1701+ if(g_once_init_enter(&g_define_type_id__volatile))
1702+ {
1703+ static const GEnumValue values[]=
1704+ {
1705+ { NOJS_MENU_ICON_STATE_UNDETERMINED, "NOJS_MENU_ICON_STATE_UNDETERMINED", N_("Undetermined") },
1706+ { NOJS_MENU_ICON_STATE_ALLOWED, "NOJS_MENU_ICON_STATE_ALLOWED", N_("Allowed") },
1707+ { NOJS_MENU_ICON_STATE_MIXED, "NOJS_MENU_ICON_STATE_MIXED", N_("Mixed") },
1708+ { NOJS_MENU_ICON_STATE_DENIED, "NOJS_MENU_ICON_STATE_DENIED", N_("Denied") },
1709+ { 0, NULL, NULL }
1710+ };
1711+
1712+ GType g_define_type_id=g_enum_register_static(g_intern_static_string("NoJSMenuIconState"), values);
1713+ g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
1714+ }
1715+
1716+ return(g_define_type_id__volatile);
1717+}
1718
1719=== added file 'extensions/nojs/nojs-view.h'
1720--- extensions/nojs/nojs-view.h 1970-01-01 00:00:00 +0000
1721+++ extensions/nojs/nojs-view.h 2013-07-29 19:51:25 +0000
1722@@ -0,0 +1,71 @@
1723+/*
1724+ Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
1725+
1726+ This library is free software; you can redistribute it and/or
1727+ modify it under the terms of the GNU Lesser General Public
1728+ License as published by the Free Software Foundation; either
1729+ version 2.1 of the License, or (at your option) any later version.
1730+
1731+ See the file COPYING for the full license text.
1732+*/
1733+
1734+#ifndef __NOJS_VIEW__
1735+#define __NOJS_VIEW__
1736+
1737+#include "config.h"
1738+#include "nojs.h"
1739+#include <midori/midori.h>
1740+
1741+G_BEGIN_DECLS
1742+
1743+/* NoJS view enums */
1744+typedef enum
1745+{
1746+ NOJS_MENU_ICON_STATE_UNDETERMINED,
1747+ NOJS_MENU_ICON_STATE_ALLOWED,
1748+ NOJS_MENU_ICON_STATE_MIXED,
1749+ NOJS_MENU_ICON_STATE_DENIED
1750+} NoJSMenuIconState;
1751+
1752+/* NoJS view object */
1753+#define TYPE_NOJS_VIEW (nojs_view_get_type())
1754+#define NOJS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NOJS_VIEW, NoJSView))
1755+#define NOJS_IS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_NOJS_VIEW))
1756+#define NOJS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_NOJS_VIEW, NoJSViewClass))
1757+#define NOJS_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_NOJS_VIEW))
1758+#define NOJS_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_NOJS_VIEW, NoJSViewClass))
1759+
1760+typedef struct _NoJSView NoJSView;
1761+typedef struct _NoJSViewClass NoJSViewClass;
1762+typedef struct _NoJSViewPrivate NoJSViewPrivate;
1763+
1764+struct _NoJSView
1765+{
1766+ /* Parent instance */
1767+ GObject parent_instance;
1768+
1769+ /* Private structure */
1770+ NoJSViewPrivate *priv;
1771+};
1772+
1773+struct _NoJSViewClass
1774+{
1775+ /* Parent class */
1776+ GObjectClass parent_class;
1777+};
1778+
1779+/* Public API */
1780+GType nojs_view_get_type(void);
1781+
1782+NoJSView* nojs_view_new(NoJS *inNoJS, MidoriBrowser *inBrowser, MidoriView *inView);
1783+
1784+GtkMenu* nojs_view_get_menu(NoJSView *self);
1785+NoJSMenuIconState nojs_view_get_menu_icon_state(NoJSView *self);
1786+
1787+/* Enumeration */
1788+GType nojs_menu_icon_state_get_type(void) G_GNUC_CONST;
1789+#define NOJS_TYPE_MENU_ICON_STATE (nojs_menu_icon_state_get_type())
1790+
1791+G_END_DECLS
1792+
1793+#endif /* __NOJS_VIEW__ */
1794
1795=== added file 'extensions/nojs/nojs.c'
1796--- extensions/nojs/nojs.c 1970-01-01 00:00:00 +0000
1797+++ extensions/nojs/nojs.c 2013-07-29 19:51:25 +0000
1798@@ -0,0 +1,1069 @@
1799+/*
1800+ Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
1801+
1802+ This library is free software; you can redistribute it and/or
1803+ modify it under the terms of the GNU Lesser General Public
1804+ License as published by the Free Software Foundation; either
1805+ version 2.1 of the License, or (at your option) any later version.
1806+
1807+ See the file COPYING for the full license text.
1808+*/
1809+
1810+#include "nojs.h"
1811+#include "nojs-view.h"
1812+
1813+#include <errno.h>
1814+
1815+/* Define this class in GObject system */
1816+G_DEFINE_TYPE(NoJS,
1817+ nojs,
1818+ G_TYPE_OBJECT)
1819+
1820+/* Properties */
1821+enum
1822+{
1823+ PROP_0,
1824+
1825+ PROP_EXTENSION,
1826+ PROP_APPLICATION,
1827+
1828+ PROP_DATABASE,
1829+ PROP_DATABASE_FILENAME,
1830+ PROP_ALLOW_ALL_SITES,
1831+ PROP_ONLY_SECOND_LEVEL,
1832+ PROP_UNKNOWN_DOMAIN_POLICY,
1833+
1834+ PROP_LAST
1835+};
1836+
1837+static GParamSpec* NoJSProperties[PROP_LAST]={ 0, };
1838+
1839+/* Signals */
1840+enum
1841+{
1842+ URI_LOAD_POLICY_STATUS,
1843+ POLICY_CHANGED,
1844+
1845+ SIGNAL_LAST
1846+};
1847+
1848+static guint NoJSSignals[SIGNAL_LAST]={ 0, };
1849+
1850+/* Private structure - access only by public API if needed */
1851+#define NOJS_GET_PRIVATE(obj) \
1852+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_NOJS, NoJSPrivate))
1853+
1854+struct _NoJSPrivate
1855+{
1856+ /* Extension related */
1857+ MidoriExtension *extension;
1858+ MidoriApp *application;
1859+ sqlite3 *database;
1860+ gchar *databaseFilename;
1861+ gboolean allowAllSites;
1862+ gboolean checkOnlySecondLevel;
1863+ NoJSPolicy unknownDomainPolicy;
1864+
1865+ guint requestStartedSignalID;
1866+};
1867+
1868+/* Taken from http://www.w3.org/html/wg/drafts/html/master/scripting-1.html#scriptingLanguages
1869+ * A list of javascript mime types
1870+ */
1871+static const gchar* javascriptTypes[]= {
1872+ "application/ecmascript",
1873+ "application/javascript",
1874+ "application/x-ecmascript",
1875+ "application/x-javascript",
1876+ "text/ecmascript",
1877+ "text/javascript",
1878+ "text/javascript1.0",
1879+ "text/javascript1.1",
1880+ "text/javascript1.2",
1881+ "text/javascript1.3",
1882+ "text/javascript1.4",
1883+ "text/javascript1.5",
1884+ "text/jscript",
1885+ "text/livescript",
1886+ "text/x-ecmascript",
1887+ "text/x-javascript",
1888+ NULL
1889+ };
1890+
1891+/* IMPLEMENTATION: Private variables and methods */
1892+
1893+/* Closure for: void (*closure)(NoJS *self, gchar *inURI, NoJSPolicy inPolicy) */
1894+static void _nojs_closure_VOID__STRING_ENUM(GClosure *inClosure,
1895+ GValue *ioReturnValue G_GNUC_UNUSED,
1896+ guint inNumberValues,
1897+ const GValue *inValues,
1898+ gpointer inInvocationHint G_GNUC_UNUSED,
1899+ gpointer inMarshalData)
1900+{
1901+ typedef void (*GMarshalFunc_VOID__STRING_ENUM)(gpointer inObject, gpointer inArg1, gint inArg2, gpointer inUserData);
1902+
1903+ register GMarshalFunc_VOID__STRING_ENUM callback;
1904+ register GCClosure *closure=(GCClosure*)inClosure;
1905+ register gpointer object, userData;
1906+
1907+ g_return_if_fail(inNumberValues==3);
1908+
1909+ if(G_CCLOSURE_SWAP_DATA(inClosure))
1910+ {
1911+ object=inClosure->data;
1912+ userData=g_value_peek_pointer(inValues+0);
1913+ }
1914+ else
1915+ {
1916+ object=g_value_peek_pointer(inValues+0);
1917+ userData=inClosure->data;
1918+ }
1919+
1920+ callback=(GMarshalFunc_VOID__STRING_ENUM)(inMarshalData ? inMarshalData : closure->callback);
1921+
1922+ callback(object,
1923+ (gchar*)g_value_get_string(inValues+1),
1924+ g_value_get_enum(inValues+2),
1925+ userData);
1926+}
1927+
1928+/* Show common error dialog */
1929+static void _nojs_error(NoJS *self, const gchar *inReason)
1930+{
1931+ g_return_if_fail(IS_NOJS(self));
1932+ g_return_if_fail(inReason);
1933+
1934+ GtkWidget *dialog;
1935+
1936+ /* Show confirmation dialog for undetermined cookies */
1937+ dialog=gtk_message_dialog_new(NULL,
1938+ GTK_DIALOG_MODAL,
1939+ GTK_MESSAGE_ERROR,
1940+ GTK_BUTTONS_OK,
1941+ _("A fatal error occurred which prevents "
1942+ "the NoJS extension to continue. "
1943+ "You should disable it."));
1944+
1945+ gtk_window_set_title(GTK_WINDOW(dialog), _("Error in NoJS extension"));
1946+ gtk_window_set_icon_name(GTK_WINDOW (dialog), "midori");
1947+
1948+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1949+ "%s:\n%s",
1950+ _("Reason"),
1951+ inReason);
1952+
1953+ gtk_dialog_run(GTK_DIALOG(dialog));
1954+
1955+ /* Free up allocated resources */
1956+ gtk_widget_destroy(dialog);
1957+}
1958+
1959+/* Open database containing policies for javascript sites.
1960+ * Create database and setup table structure if it does not exist yet.
1961+ */
1962+static void _nojs_open_database(NoJS *self)
1963+{
1964+ g_return_if_fail(IS_NOJS(self));
1965+
1966+ NoJSPrivate *priv=self->priv;
1967+ const gchar *configDir;
1968+ gchar *sql;
1969+ gchar *error=NULL;
1970+ gint success;
1971+
1972+ /* Close any open database */
1973+ if(priv->database)
1974+ {
1975+ priv->databaseFilename=NULL;
1976+
1977+ sqlite3_close(priv->database);
1978+ priv->database=NULL;
1979+
1980+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_DATABASE]);
1981+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_DATABASE_FILENAME]);
1982+ }
1983+
1984+ /* Build path to database file */
1985+ configDir=midori_extension_get_config_dir(priv->extension);
1986+ if(!configDir)
1987+ {
1988+ g_warning(_("Could not get path to configuration of extension: path is NULL"));
1989+
1990+ _nojs_error(self, _("Could not get path to configuration of extension."));
1991+ return;
1992+ }
1993+
1994+ if(katze_mkdir_with_parents(configDir, 0700))
1995+ {
1996+ g_warning(_("Could not create configuration folder for extension: %s"), g_strerror(errno));
1997+
1998+ _nojs_error(self, _("Could not create configuration folder for extension."));
1999+ return;
2000+ }
2001+
2002+ /* Open database */
2003+ priv->databaseFilename=g_build_filename(configDir, NOJS_DATABASE, NULL);
2004+ success=sqlite3_open(priv->databaseFilename, &priv->database);
2005+ if(success!=SQLITE_OK)
2006+ {
2007+ g_warning(_("Could not open database of extension: %s"), sqlite3_errmsg(priv->database));
2008+
2009+ g_free(priv->databaseFilename);
2010+ priv->databaseFilename=NULL;
2011+
2012+ if(priv->database) sqlite3_close(priv->database);
2013+ priv->database=NULL;
2014+
2015+ _nojs_error(self, _("Could not open database of extension."));
2016+ return;
2017+ }
2018+
2019+ /* Create table structure if it does not exist */
2020+ success=sqlite3_exec(priv->database,
2021+ "CREATE TABLE IF NOT EXISTS "
2022+ "policies(site text, value integer);",
2023+ NULL,
2024+ NULL,
2025+ &error);
2026+
2027+ if(success==SQLITE_OK)
2028+ {
2029+ success=sqlite3_exec(priv->database,
2030+ "CREATE UNIQUE INDEX IF NOT EXISTS "
2031+ "site ON policies (site);",
2032+ NULL,
2033+ NULL,
2034+ &error);
2035+ }
2036+
2037+ if(success==SQLITE_OK)
2038+ {
2039+ success=sqlite3_exec(priv->database,
2040+ "PRAGMA journal_mode=TRUNCATE;",
2041+ NULL,
2042+ NULL,
2043+ &error);
2044+ }
2045+
2046+ if(success!=SQLITE_OK || error)
2047+ {
2048+ _nojs_error(self, _("Could not set up database structure of extension."));
2049+
2050+ if(error)
2051+ {
2052+ g_critical(_("Failed to execute database statement: %s"), error);
2053+ sqlite3_free(error);
2054+ }
2055+
2056+ g_free(priv->databaseFilename);
2057+ priv->databaseFilename=NULL;
2058+
2059+ sqlite3_close(priv->database);
2060+ priv->database=NULL;
2061+ return;
2062+ }
2063+
2064+ /* Delete all temporarily allowed sites */
2065+ sql=sqlite3_mprintf("DELETE FROM policies WHERE value=%d;", NOJS_POLICY_ACCEPT_TEMPORARILY);
2066+ success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
2067+ if(success!=SQLITE_OK) g_warning(_("SQL fails: %s"), error);
2068+ if(error) sqlite3_free(error);
2069+ sqlite3_free(sql);
2070+
2071+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_DATABASE]);
2072+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_DATABASE_FILENAME]);
2073+}
2074+
2075+/* A request through libsoup is going to start and http headers must be
2076+ * checked for content type
2077+ */
2078+static void _nojs_on_got_headers(NoJS *self, gpointer inUserData)
2079+{
2080+ g_return_if_fail(IS_NOJS(self));
2081+ g_return_if_fail(SOUP_IS_MESSAGE(inUserData));
2082+
2083+ NoJSPrivate *priv=self->priv;
2084+ SoupMessage *message=SOUP_MESSAGE(inUserData);
2085+ SoupSession *session=webkit_get_default_session();
2086+ SoupMessageHeaders *headers;
2087+ SoupMessageBody *body;
2088+ const gchar *contentType;
2089+ SoupURI *uri;
2090+ gchar *uriText;
2091+ gchar *domain;
2092+ NoJSPolicy policy;
2093+ gboolean isJS;
2094+ const gchar **iter;
2095+
2096+ /* Get headers from message to retrieve content type */
2097+ g_object_get(message, "response-headers", &headers, NULL);
2098+ if(!headers)
2099+ {
2100+ g_warning("Could not get headers from message to check for javascript.");
2101+ return;
2102+ }
2103+
2104+ /* Get content type of uri and check if it is a javascript resource */
2105+ contentType=soup_message_headers_get_content_type(headers, NULL);
2106+
2107+ isJS=FALSE;
2108+ iter=javascriptTypes;
2109+ while(*iter && !isJS)
2110+ {
2111+ isJS=(g_strcmp0(contentType, *iter)==0);
2112+ iter++;
2113+ }
2114+
2115+ if(!isJS) return;
2116+
2117+ /* The document being loaded is javascript so get URI from message,
2118+ * get policy for domain of URI and emit signal
2119+ */
2120+ uri=soup_message_get_uri(message);
2121+
2122+ domain=nojs_get_domain(self, uri);
2123+ g_return_if_fail(domain);
2124+
2125+ policy=nojs_get_policy(self, domain);
2126+ if(policy==NOJS_POLICY_UNDETERMINED)
2127+ {
2128+ g_warning("Got invalid policy. Using default policy for unknown domains.");
2129+ policy=priv->unknownDomainPolicy;
2130+ }
2131+
2132+ uriText=soup_uri_to_string(uri, FALSE);
2133+
2134+ g_signal_emit(self, NoJSSignals[URI_LOAD_POLICY_STATUS], 0, uriText, policy==NOJS_POLICY_UNDETERMINED ? NOJS_POLICY_BLOCK : policy);
2135+
2136+ g_free(uriText);
2137+ g_free(domain);
2138+
2139+ /* Return here if policy is any type of accept */
2140+ if(policy!=NOJS_POLICY_UNDETERMINED && policy!=NOJS_POLICY_BLOCK) return;
2141+
2142+ /* Cancel this message */
2143+ soup_session_cancel_message(session, message, SOUP_STATUS_CANCELLED);
2144+
2145+ /* Discard any load data */
2146+ g_object_get(message, "response-body", &body, NULL);
2147+ if(body) soup_message_body_truncate(body);
2148+}
2149+
2150+static void _nojs_on_request_started(NoJS *self,
2151+ SoupMessage *inMessage,
2152+ SoupSocket *inSocket,
2153+ gpointer inUserData)
2154+{
2155+ g_return_if_fail(IS_NOJS(self));
2156+ g_return_if_fail(SOUP_IS_MESSAGE(inMessage));
2157+
2158+ /* Connect to "got-headers" to cancel loading javascript documents early */
2159+ g_signal_connect_swapped(inMessage, "got-headers", G_CALLBACK(_nojs_on_got_headers), self);
2160+}
2161+
2162+/* The icon in statusbar was clicked */
2163+static void _nojs_on_statusbar_icon_clicked(MidoriBrowser *inBrowser, gpointer inUserData)
2164+{
2165+ g_return_if_fail(MIDORI_IS_BROWSER(inBrowser));
2166+
2167+ MidoriView *activeView;
2168+ NoJSView *view;
2169+ GtkMenu *menu;
2170+
2171+ /* Get current active midori view */
2172+ activeView=MIDORI_VIEW(midori_browser_get_current_tab(inBrowser));
2173+ g_return_if_fail(MIDORI_IS_VIEW(activeView));
2174+
2175+ /* Get NoJS view of current active midori view */
2176+ view=NOJS_VIEW(g_object_get_data(G_OBJECT(activeView), "nojs-view-instance"));
2177+ g_return_if_fail(NOJS_IS_VIEW(view));
2178+
2179+ /* Get menu of current view */
2180+ menu=nojs_view_get_menu(view);
2181+ g_return_if_fail(menu);
2182+
2183+ /* Show menu */
2184+ gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
2185+}
2186+
2187+gchar* nojs_get_icon_path (const gchar* icon)
2188+{
2189+ gchar* res_dir = midori_paths_get_res_filename ("");
2190+ return g_build_filename (res_dir, "nojs", icon, NULL);
2191+}
2192+
2193+/* Menu icon of a view has changed */
2194+static void _nojs_on_menu_icon_changed(MidoriBrowser *inBrowser, GParamSpec *inSpec, gpointer inUserData)
2195+{
2196+ g_return_if_fail(MIDORI_IS_BROWSER(inBrowser));
2197+ g_return_if_fail(NOJS_IS_VIEW(inUserData));
2198+
2199+ NoJSView *view=NOJS_VIEW(inUserData);
2200+ NoJSMenuIconState menuIconState;
2201+ GtkWidget *statusbarIcon;
2202+ GtkWidget *buttonImage;
2203+ gchar *imageFilename;
2204+
2205+ /* Get icon in status bar of this browser */
2206+ statusbarIcon=GTK_WIDGET(g_object_get_data(G_OBJECT(inBrowser), "nojs-statusicon"));
2207+ g_return_if_fail(GTK_IS_WIDGET(statusbarIcon));
2208+
2209+ /* Get menu icon state of view */
2210+ menuIconState=nojs_view_get_menu_icon_state(view);
2211+
2212+ /* Create image for statusbar button */
2213+ imageFilename=NULL;
2214+ switch(menuIconState)
2215+ {
2216+ case NOJS_MENU_ICON_STATE_ALLOWED:
2217+ imageFilename=nojs_get_icon_path("nojs-statusicon-allowed.png");
2218+ break;
2219+
2220+ case NOJS_MENU_ICON_STATE_MIXED:
2221+ imageFilename=nojs_get_icon_path("nojs-statusicon-mixed.png");
2222+ break;
2223+
2224+ case NOJS_MENU_ICON_STATE_DENIED:
2225+ case NOJS_MENU_ICON_STATE_UNDETERMINED:
2226+ imageFilename=nojs_get_icon_path("nojs-statusicon-denied.png");
2227+ break;
2228+ }
2229+
2230+ buttonImage=gtk_image_new_from_file(imageFilename);
2231+ g_free(imageFilename);
2232+
2233+ /* Set image at statusbar button */
2234+ gtk_button_set_image(GTK_BUTTON(statusbarIcon), buttonImage);
2235+}
2236+
2237+/* A tab in browser was activated */
2238+static void _nojs_on_switch_tab(NoJS *self, MidoriView *inOldView, MidoriView *inNewView, gpointer inUserData)
2239+{
2240+ g_return_if_fail(IS_NOJS(self));
2241+ g_return_if_fail(MIDORI_IS_BROWSER(inUserData));
2242+
2243+ MidoriBrowser *browser=MIDORI_BROWSER(inUserData);
2244+ NoJSView *view;
2245+
2246+ /* Disconnect signal handlers from old view */
2247+ if(inOldView)
2248+ {
2249+ /* Get NoJS view of old view */
2250+ view=(NoJSView*)g_object_get_data(G_OBJECT(inOldView), "nojs-view-instance");
2251+ g_return_if_fail(NOJS_IS_VIEW(view));
2252+
2253+ /* Disconnect signal handlers */
2254+ g_signal_handlers_disconnect_by_func(view, G_CALLBACK(_nojs_on_menu_icon_changed), browser);
2255+ }
2256+
2257+ /* Get NoJS view of new view */
2258+ view=(NoJSView*)g_object_get_data(G_OBJECT(inNewView), "nojs-view-instance");
2259+ g_return_if_fail(NOJS_IS_VIEW(view));
2260+
2261+ /* Connect signals */
2262+ g_signal_connect_swapped(view, "notify::menu-icon-state", G_CALLBACK(_nojs_on_menu_icon_changed), browser);
2263+
2264+ /* Update menu icon*/
2265+ _nojs_on_menu_icon_changed(browser, NULL, view);
2266+}
2267+
2268+/* A tab of a browser was removed */
2269+static void _nojs_on_remove_tab(NoJS *self, MidoriView *inView, gpointer inUserData)
2270+{
2271+ g_return_if_fail(IS_NOJS(self));
2272+
2273+ NoJSView *view;
2274+
2275+ /* Get NoJS view of current active midori view */
2276+ view=NOJS_VIEW(g_object_get_data(G_OBJECT(inView), "nojs-view-instance"));
2277+ g_return_if_fail(NOJS_IS_VIEW(view));
2278+
2279+ g_object_unref(view);
2280+}
2281+
2282+/* A tab of a browser was added */
2283+static void _nojs_on_add_tab(NoJS *self, MidoriView *inView, gpointer inUserData)
2284+{
2285+ g_return_if_fail(IS_NOJS(self));
2286+ g_return_if_fail(MIDORI_IS_BROWSER(inUserData));
2287+
2288+ /* Create nojs view and add to tab */
2289+ MidoriBrowser *browser=MIDORI_BROWSER(inUserData);
2290+
2291+ nojs_view_new(self, browser, inView);
2292+}
2293+
2294+/* A browser window was added */
2295+static void _nojs_on_add_browser(NoJS *self, MidoriBrowser *inBrowser, gpointer inUserData)
2296+{
2297+ g_return_if_fail(IS_NOJS(self));
2298+ g_return_if_fail(MIDORI_IS_BROWSER(inBrowser));
2299+
2300+ GList *tabs, *iter;
2301+ GtkWidget *statusbar;
2302+ GtkWidget *statusbarIcon;
2303+ MidoriView *view;
2304+ NoJSView *nojsView;
2305+
2306+ /* Set up all current available tabs in browser */
2307+ tabs=midori_browser_get_tabs(inBrowser);
2308+ for(iter=tabs; iter; iter=g_list_next(iter)) _nojs_on_add_tab(self, iter->data, inBrowser);
2309+ g_list_free(tabs);
2310+
2311+ /* Add status bar icon to browser */
2312+ g_object_get(inBrowser, "statusbar", &statusbar, NULL);
2313+ if(statusbar)
2314+ {
2315+ /* Create and set up status icon */
2316+ statusbarIcon=gtk_button_new();
2317+ gtk_button_set_relief(GTK_BUTTON(statusbarIcon), GTK_RELIEF_NONE);
2318+ gtk_widget_show_all(statusbarIcon);
2319+ gtk_box_pack_end(GTK_BOX(statusbar), statusbarIcon, FALSE, FALSE, 0);
2320+ g_object_set_data_full(G_OBJECT(inBrowser), "nojs-statusicon", g_object_ref(statusbarIcon), (GDestroyNotify)gtk_widget_destroy);
2321+
2322+ /* Connect signals */
2323+ g_signal_connect_swapped(statusbarIcon, "clicked", G_CALLBACK(_nojs_on_statusbar_icon_clicked), inBrowser);
2324+
2325+ /* Release our reference to statusbar and status icon */
2326+ g_object_unref(statusbarIcon);
2327+ g_object_unref(statusbar);
2328+
2329+ /* Update menu icon*/
2330+ view=MIDORI_VIEW(midori_browser_get_current_tab(inBrowser));
2331+ if(view)
2332+ {
2333+ nojsView=(NoJSView*)g_object_get_data(G_OBJECT(view), "nojs-view-instance");
2334+ if(nojsView) _nojs_on_menu_icon_changed(inBrowser, NULL, nojsView);
2335+ }
2336+ }
2337+
2338+ /* Listen to new tabs opened in browser */
2339+ g_signal_connect_swapped(inBrowser, "add-tab", G_CALLBACK(_nojs_on_add_tab), self);
2340+ g_signal_connect_swapped(inBrowser, "switch-tab", G_CALLBACK(_nojs_on_switch_tab), self);
2341+ g_signal_connect_swapped(inBrowser, "remove-tab", G_CALLBACK(_nojs_on_remove_tab), self);
2342+}
2343+
2344+/* Application property has changed */
2345+static void _nojs_on_application_changed(NoJS *self)
2346+{
2347+ g_return_if_fail(IS_NOJS(self));
2348+
2349+ NoJSPrivate *priv=NOJS(self)->priv;
2350+ GList *browsers, *iter;
2351+
2352+ /* Set up all current open browser windows */
2353+ browsers=midori_app_get_browsers(priv->application);
2354+ for(iter=browsers; iter; iter=g_list_next(iter)) _nojs_on_add_browser(self, MIDORI_BROWSER(iter->data), priv->application);
2355+ g_list_free(browsers);
2356+
2357+ /* Listen to new browser windows opened */
2358+ g_signal_connect_swapped(priv->application, "add-browser", G_CALLBACK(_nojs_on_add_browser), self);
2359+
2360+ /* Notify about property change */
2361+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_APPLICATION]);
2362+}
2363+
2364+/* IMPLEMENTATION: GObject */
2365+
2366+/* Finalize this object */
2367+static void nojs_finalize(GObject *inObject)
2368+{
2369+ NoJS *self=NOJS(inObject);
2370+ NoJSPrivate *priv=self->priv;
2371+ GList *browsers, *browser;
2372+ GList *tabs, *tab;
2373+ WebKitWebView *webkitView;
2374+ SoupSession *session;
2375+
2376+ /* Dispose allocated resources */
2377+ session=webkit_get_default_session();
2378+ g_signal_handlers_disconnect_by_data(session, self);
2379+
2380+ if(priv->databaseFilename)
2381+ {
2382+ g_free(priv->databaseFilename);
2383+ priv->databaseFilename=NULL;
2384+ }
2385+
2386+ if(priv->database)
2387+ {
2388+ sqlite3_close(priv->database);
2389+ priv->database=NULL;
2390+ }
2391+
2392+ if(priv->application)
2393+ {
2394+ g_signal_handlers_disconnect_by_data(priv->application, self);
2395+
2396+ browsers=midori_app_get_browsers(priv->application);
2397+ for(browser=browsers; browser; browser=g_list_next(browser))
2398+ {
2399+ g_signal_handlers_disconnect_by_data(browser->data, self);
2400+ g_object_set_data(G_OBJECT(browser->data), "nojs-statusicon", NULL);
2401+
2402+ tabs=midori_browser_get_tabs(MIDORI_BROWSER(browser->data));
2403+ for(tab=tabs; tab; tab=g_list_next(tab))
2404+ {
2405+ g_signal_handlers_disconnect_by_data(tab->data, self);
2406+
2407+ webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(MIDORI_VIEW(tab->data)));
2408+ g_signal_handlers_disconnect_by_data(webkitView, self);
2409+ }
2410+ g_list_free(tabs);
2411+ }
2412+ g_list_free(browsers);
2413+
2414+ priv->application=NULL;
2415+ }
2416+
2417+ /* Call parent's class finalize method */
2418+ G_OBJECT_CLASS(nojs_parent_class)->finalize(inObject);
2419+}
2420+
2421+/* Set/get properties */
2422+static void nojs_set_property(GObject *inObject,
2423+ guint inPropID,
2424+ const GValue *inValue,
2425+ GParamSpec *inSpec)
2426+{
2427+ NoJS *self=NOJS(inObject);
2428+
2429+ switch(inPropID)
2430+ {
2431+ /* Construct-only properties */
2432+ case PROP_EXTENSION:
2433+ self->priv->extension=g_value_get_object(inValue);
2434+ _nojs_open_database(self);
2435+ break;
2436+
2437+ case PROP_APPLICATION:
2438+ self->priv->application=g_value_get_object(inValue);
2439+ _nojs_on_application_changed(self);
2440+ break;
2441+
2442+ case PROP_ALLOW_ALL_SITES:
2443+ self->priv->allowAllSites=g_value_get_boolean(inValue);
2444+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_ALLOW_ALL_SITES]);
2445+ break;
2446+
2447+ case PROP_ONLY_SECOND_LEVEL:
2448+ self->priv->checkOnlySecondLevel=g_value_get_boolean(inValue);
2449+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_ONLY_SECOND_LEVEL]);
2450+ break;
2451+
2452+ case PROP_UNKNOWN_DOMAIN_POLICY:
2453+ self->priv->unknownDomainPolicy=g_value_get_enum(inValue);
2454+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_UNKNOWN_DOMAIN_POLICY]);
2455+ break;
2456+
2457+ default:
2458+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
2459+ break;
2460+ }
2461+}
2462+
2463+static void nojs_get_property(GObject *inObject,
2464+ guint inPropID,
2465+ GValue *outValue,
2466+ GParamSpec *inSpec)
2467+{
2468+ NoJS *self=NOJS(inObject);
2469+
2470+ switch(inPropID)
2471+ {
2472+ case PROP_EXTENSION:
2473+ g_value_set_object(outValue, self->priv->extension);
2474+ break;
2475+
2476+ case PROP_APPLICATION:
2477+ g_value_set_object(outValue, self->priv->application);
2478+ break;
2479+
2480+ case PROP_DATABASE:
2481+ g_value_set_pointer(outValue, self->priv->database);
2482+ break;
2483+
2484+ case PROP_DATABASE_FILENAME:
2485+ g_value_set_string(outValue, self->priv->databaseFilename);
2486+ break;
2487+
2488+ case PROP_ALLOW_ALL_SITES:
2489+ g_value_set_boolean(outValue, self->priv->allowAllSites);
2490+ break;
2491+
2492+ case PROP_ONLY_SECOND_LEVEL:
2493+ g_value_set_boolean(outValue, self->priv->checkOnlySecondLevel);
2494+ break;
2495+
2496+ case PROP_UNKNOWN_DOMAIN_POLICY:
2497+ g_value_set_enum(outValue, self->priv->unknownDomainPolicy);
2498+ break;
2499+
2500+ default:
2501+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
2502+ break;
2503+ }
2504+}
2505+
2506+/* Class initialization
2507+ * Override functions in parent classes and define properties and signals
2508+ */
2509+static void nojs_class_init(NoJSClass *klass)
2510+{
2511+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
2512+
2513+ /* Override functions */
2514+ gobjectClass->finalize=nojs_finalize;
2515+ gobjectClass->set_property=nojs_set_property;
2516+ gobjectClass->get_property=nojs_get_property;
2517+
2518+ /* Set up private structure */
2519+ g_type_class_add_private(klass, sizeof(NoJSPrivate));
2520+
2521+ /* Define properties */
2522+ NoJSProperties[PROP_EXTENSION]=
2523+ g_param_spec_object("extension",
2524+ _("Extension instance"),
2525+ _("The Midori extension instance for this extension"),
2526+ MIDORI_TYPE_EXTENSION,
2527+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
2528+
2529+ NoJSProperties[PROP_APPLICATION]=
2530+ g_param_spec_object("application",
2531+ _("Application instance"),
2532+ _("The Midori application instance this extension belongs to"),
2533+ MIDORI_TYPE_APP,
2534+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
2535+
2536+ NoJSProperties[PROP_DATABASE]=
2537+ g_param_spec_pointer("database",
2538+ _("Database instance"),
2539+ _("Pointer to sqlite database instance used by this extension"),
2540+ G_PARAM_READABLE);
2541+
2542+ NoJSProperties[PROP_DATABASE_FILENAME]=
2543+ g_param_spec_string("database-filename",
2544+ _("Database path"),
2545+ _("Path to sqlite database instance used by this extension"),
2546+ NULL,
2547+ G_PARAM_READABLE);
2548+
2549+ NoJSProperties[PROP_ALLOW_ALL_SITES]=
2550+ g_param_spec_boolean("allow-all-sites",
2551+ _("Allow all sites"),
2552+ _("If true this extension will not check policy for each site but allow them."),
2553+ FALSE,
2554+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
2555+
2556+ NoJSProperties[PROP_ONLY_SECOND_LEVEL]=
2557+ g_param_spec_boolean("only-second-level",
2558+ _("Only second level"),
2559+ _("If true this extension will reduce each domain to its second-level (www.example.org will reduced to example.org)"),
2560+ TRUE,
2561+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
2562+
2563+ NoJSProperties[PROP_UNKNOWN_DOMAIN_POLICY]=
2564+ g_param_spec_enum("unknown-domain-policy",
2565+ _("Unknown domain policy"),
2566+ _("Policy to use for unknown domains."),
2567+ NOJS_TYPE_POLICY,
2568+ NOJS_POLICY_BLOCK,
2569+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
2570+
2571+ g_object_class_install_properties(gobjectClass, PROP_LAST, NoJSProperties);
2572+
2573+ /* Define signals */
2574+
2575+ /* Why does this signal exist?
2576+ *
2577+ * The problem I faced when developing this extension was
2578+ * that I needed to cancel a SoupMessage as soon as possible
2579+ * (when http headers were received).
2580+ * I tried to connect to signal "resource-response-received"
2581+ * of WebKitWebView but the SoupMessage instance was not
2582+ * exactly the same which were sent or received by SoupSession.
2583+ * So I could not cancel the SoupMessage or better: I cancelled
2584+ * a SoupMessage which is not be handled so it had no effect.
2585+ * The body of SoupMessage was still being loaded and javascript
2586+ * was executed. I think the problem is that webkit-gtk creates
2587+ * a copy of the real SoupMessage which is going to be sent and
2588+ * received.
2589+ *
2590+ * So I decided to connect to signal "got-headers" of every
2591+ * SoupMessage sent by the default SoupSession which I notice
2592+ * by connecting to signal "request-started" of SoupSession. Each
2593+ * NoJSView connects to signal "resource-request-starting" of
2594+ * WebKitWebView to remember each URI going to be loaded. When
2595+ * a SoupMessage hits "got-headers" and is a javascript resource
2596+ * I can cancel the message immediately and clear the body which
2597+ * causes webkit-gtk to copy a empty body if it does at all as the
2598+ * SoupMessage was cancelled. Then I emit this signal
2599+ * "uri-load-policy-status" to notify each view but the cancellation.
2600+ * (It also notifies all views if it is going to load to keep the
2601+ * menu in right state.) Each view will check if it _could_ be a
2602+ * resource itself requested and will update its menu accordingly.
2603+ * It might happen that a request will match two views because only
2604+ * the URI will be checked by the view because I cannot determine
2605+ * to which view the SoupMessage belongs to. But it doesn't matter
2606+ * because if a javascript resource was denied or allowed in one view
2607+ * it is likely be denied or allowed in other views too ;)
2608+ */
2609+ NoJSSignals[URI_LOAD_POLICY_STATUS]=
2610+ g_signal_new("uri-load-policy-status",
2611+ G_TYPE_FROM_CLASS(klass),
2612+ G_SIGNAL_RUN_LAST,
2613+ G_STRUCT_OFFSET(NoJSClass, uri_load_policy_status),
2614+ NULL,
2615+ NULL,
2616+ _nojs_closure_VOID__STRING_ENUM,
2617+ G_TYPE_NONE,
2618+ 2,
2619+ G_TYPE_STRING,
2620+ NOJS_TYPE_POLICY);
2621+
2622+ NoJSSignals[POLICY_CHANGED]=
2623+ g_signal_new("policy-changed",
2624+ G_TYPE_FROM_CLASS(klass),
2625+ G_SIGNAL_RUN_LAST,
2626+ G_STRUCT_OFFSET(NoJSClass, policy_changed),
2627+ NULL,
2628+ NULL,
2629+ g_cclosure_marshal_VOID__STRING,
2630+ G_TYPE_NONE,
2631+ 1,
2632+ G_TYPE_STRING);
2633+}
2634+
2635+/* Object initialization
2636+ * Create private structure and set up default values
2637+ */
2638+
2639+static void nojs_init(NoJS *self)
2640+{
2641+ NoJSPrivate *priv;
2642+ SoupSession *session;
2643+
2644+ priv=self->priv=NOJS_GET_PRIVATE(self);
2645+
2646+ /* Set up default values */
2647+ priv->database=NULL;
2648+ priv->databaseFilename=NULL;
2649+ priv->allowAllSites=FALSE;
2650+ priv->checkOnlySecondLevel=TRUE;
2651+ priv->unknownDomainPolicy=NOJS_POLICY_BLOCK;
2652+
2653+ /* Connect to signals on session to be able to cancel messages
2654+ * loading javascript documents
2655+ */
2656+ session=webkit_get_default_session();
2657+ g_signal_connect_swapped(session, "request-started", G_CALLBACK(_nojs_on_request_started), self);
2658+}
2659+
2660+/* Implementation: Public API */
2661+
2662+/* Create new object */
2663+NoJS* nojs_new(MidoriExtension *inExtension, MidoriApp *inApp)
2664+{
2665+ return(g_object_new(TYPE_NOJS,
2666+ "extension", inExtension,
2667+ "application", inApp,
2668+ NULL));
2669+}
2670+
2671+/* Retrieves domain from uri depending on preferences (e.g. only second level domain) */
2672+gchar* nojs_get_domain(NoJS *self, SoupURI *inURI)
2673+{
2674+ g_return_val_if_fail(IS_NOJS(self), NULL);
2675+ g_return_val_if_fail(inURI, NULL);
2676+
2677+ NoJSPrivate *priv=self->priv;
2678+ const gchar *realDomain;
2679+ gchar *asciiDomain, *domain;
2680+ gchar *finalDomain;
2681+
2682+ /* Get domain of site to lookup */
2683+ realDomain=soup_uri_get_host(inURI);
2684+
2685+ domain=asciiDomain=g_hostname_to_ascii(realDomain);
2686+
2687+ if(priv->checkOnlySecondLevel)
2688+ {
2689+ /* Only get second level domain if host is not an IP address */
2690+ if(!g_hostname_is_ip_address(asciiDomain))
2691+ {
2692+ gint numberDots=0;
2693+
2694+ domain=asciiDomain+strlen(asciiDomain)-1;
2695+ while(domain>=asciiDomain && numberDots<2)
2696+ {
2697+ if(*domain=='.') numberDots++;
2698+ domain--;
2699+ }
2700+ domain++;
2701+ if(*domain=='.') domain++;
2702+ }
2703+ }
2704+
2705+ /* Create copy for return value */
2706+ if(strlen(domain)>0) finalDomain=g_strdup(domain);
2707+ else finalDomain=NULL;
2708+
2709+ /* Free allocated resources */
2710+ g_free(asciiDomain);
2711+
2712+ /* Return domain */
2713+ return(finalDomain);
2714+}
2715+
2716+/* Get/set policy for javascript from site */
2717+gint nojs_get_policy(NoJS *self, const gchar *inDomain)
2718+{
2719+ g_return_val_if_fail(IS_NOJS(self), NOJS_POLICY_UNDETERMINED);
2720+ g_return_val_if_fail(inDomain, NOJS_POLICY_UNDETERMINED);
2721+
2722+ NoJSPrivate *priv=self->priv;
2723+ sqlite3_stmt *statement=NULL;
2724+ gint error;
2725+ gint policy=NOJS_POLICY_UNDETERMINED;
2726+
2727+ /* Check to allow all sites */
2728+ if(priv->allowAllSites) return(NOJS_POLICY_ACCEPT);
2729+
2730+ /* Check for open database */
2731+ g_return_val_if_fail(priv->database, policy);
2732+
2733+ /* Lookup policy for site in database */
2734+ error=sqlite3_prepare_v2(priv->database,
2735+ "SELECT site, value FROM policies WHERE site LIKE ? LIMIT 1;",
2736+ -1,
2737+ &statement,
2738+ NULL);
2739+ if(statement && error==SQLITE_OK) error=sqlite3_bind_text(statement, 1, inDomain, -1, NULL);
2740+ if(statement && error==SQLITE_OK)
2741+ {
2742+ if(sqlite3_step(statement)==SQLITE_ROW) policy=sqlite3_column_int(statement, 1);
2743+ }
2744+ else g_warning(_("SQL fails: %s"), sqlite3_errmsg(priv->database));
2745+
2746+ sqlite3_finalize(statement);
2747+
2748+ /* If we have not found a policy for the domain then it is an unknown domain.
2749+ * Get default policy for unknown domains.
2750+ */
2751+ if(policy==NOJS_POLICY_UNDETERMINED) policy=priv->unknownDomainPolicy;
2752+
2753+ return(policy);
2754+}
2755+
2756+void nojs_set_policy(NoJS *self, const gchar *inDomain, NoJSPolicy inPolicy)
2757+{
2758+ g_return_if_fail(IS_NOJS(self));
2759+ g_return_if_fail(inDomain);
2760+ g_return_if_fail(inPolicy>=NOJS_POLICY_ACCEPT && inPolicy<=NOJS_POLICY_BLOCK);
2761+
2762+ NoJSPrivate *priv=self->priv;
2763+ gchar *sql;
2764+ gchar *error=NULL;
2765+ gint success;
2766+
2767+ /* Check for open database */
2768+ g_return_if_fail(priv->database);
2769+
2770+ /* Update policy in database */
2771+ sql=sqlite3_mprintf("INSERT OR REPLACE INTO policies (site, value) VALUES ('%q', %d);",
2772+ inDomain,
2773+ inPolicy);
2774+ success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
2775+ if(success!=SQLITE_OK) g_warning(_("SQL fails: %s"), error);
2776+ if(error) sqlite3_free(error);
2777+ sqlite3_free(sql);
2778+
2779+ /* Emit signal to notify about policy change */
2780+ if(success==SQLITE_OK) g_signal_emit(self, NoJSSignals[POLICY_CHANGED], 0, inDomain);
2781+}
2782+
2783+/* Get/set default policy for unknown domains */
2784+NoJSPolicy nojs_get_policy_for_unknown_domain(NoJS *self)
2785+{
2786+ g_return_val_if_fail(IS_NOJS(self), NOJS_POLICY_UNDETERMINED);
2787+
2788+ return(self->priv->unknownDomainPolicy);
2789+}
2790+
2791+void nojs_set_policy_for_unknown_domain(NoJS *self, NoJSPolicy inPolicy)
2792+{
2793+ g_return_if_fail(IS_NOJS(self));
2794+ g_return_if_fail(inPolicy>=NOJS_POLICY_ACCEPT && inPolicy<=NOJS_POLICY_BLOCK);
2795+
2796+ if(self->priv->unknownDomainPolicy!=inPolicy)
2797+ {
2798+ self->priv->unknownDomainPolicy=inPolicy;
2799+ midori_extension_set_integer(self->priv->extension, "unknown-domain-policy", inPolicy);
2800+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_UNKNOWN_DOMAIN_POLICY]);
2801+ }
2802+}
2803+
2804+/* Get/set flag to allow javascript at all sites */
2805+gboolean nojs_get_allow_all_sites(NoJS *self)
2806+{
2807+ g_return_val_if_fail(IS_NOJS(self), TRUE);
2808+
2809+ return(self->priv->allowAllSites);
2810+}
2811+
2812+void nojs_set_allow_all_sites(NoJS *self, gboolean inAllow)
2813+{
2814+ g_return_if_fail(IS_NOJS(self));
2815+
2816+ if(self->priv->allowAllSites!=inAllow)
2817+ {
2818+ self->priv->allowAllSites=inAllow;
2819+ midori_extension_set_boolean(self->priv->extension, "allow-all-sites", inAllow);
2820+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_ALLOW_ALL_SITES]);
2821+ }
2822+}
2823+
2824+/* Get/set flag to check for second-level domains only */
2825+gboolean nojs_get_only_second_level_domain(NoJS *self)
2826+{
2827+ g_return_val_if_fail(IS_NOJS(self), TRUE);
2828+
2829+ return(self->priv->checkOnlySecondLevel);
2830+}
2831+
2832+void nojs_set_only_second_level_domain(NoJS *self, gboolean inOnlySecondLevel)
2833+{
2834+ g_return_if_fail(IS_NOJS(self));
2835+
2836+ if(self->priv->checkOnlySecondLevel!=inOnlySecondLevel)
2837+ {
2838+ self->priv->checkOnlySecondLevel=inOnlySecondLevel;
2839+ midori_extension_set_boolean(self->priv->extension, "only-second-level", inOnlySecondLevel);
2840+ g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_ONLY_SECOND_LEVEL]);
2841+ }
2842+}
2843+
2844+/************************************************************************************/
2845+
2846+/* Implementation: Enumeration */
2847+GType nojs_policy_get_type(void)
2848+{
2849+ static volatile gsize g_define_type_id__volatile=0;
2850+
2851+ if(g_once_init_enter(&g_define_type_id__volatile))
2852+ {
2853+ static const GEnumValue values[]=
2854+ {
2855+ { NOJS_POLICY_UNDETERMINED, "NOJS_POLICY_UNDETERMINED", N_("Undetermined") },
2856+ { NOJS_POLICY_ACCEPT, "NOJS_POLICY_ACCEPT", N_("Accept") },
2857+ { NOJS_POLICY_ACCEPT_TEMPORARILY, "NOJS_POLICY_ACCEPT_TEMPORARILY", N_("Accept temporarily") },
2858+ { NOJS_POLICY_BLOCK, "NOJS_POLICY_BLOCK", N_("Block") },
2859+ { 0, NULL, NULL }
2860+ };
2861+
2862+ GType g_define_type_id=g_enum_register_static(g_intern_static_string("NoJSPolicy"), values);
2863+ g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
2864+ }
2865+
2866+ return(g_define_type_id__volatile);
2867+}
2868
2869=== added file 'extensions/nojs/nojs.h'
2870--- extensions/nojs/nojs.h 1970-01-01 00:00:00 +0000
2871+++ extensions/nojs/nojs.h 2013-07-29 19:51:25 +0000
2872@@ -0,0 +1,89 @@
2873+/*
2874+ Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
2875+
2876+ This library is free software; you can redistribute it and/or
2877+ modify it under the terms of the GNU Lesser General Public
2878+ License as published by the Free Software Foundation; either
2879+ version 2.1 of the License, or (at your option) any later version.
2880+
2881+ See the file COPYING for the full license text.
2882+*/
2883+
2884+#ifndef __NOJS__
2885+#define __NOJS__
2886+
2887+#include "config.h"
2888+#include <midori/midori.h>
2889+
2890+#define NOJS_DATABASE "nojs.db"
2891+
2892+G_BEGIN_DECLS
2893+
2894+/* NoJS manager enums */
2895+typedef enum
2896+{
2897+ NOJS_POLICY_UNDETERMINED,
2898+ NOJS_POLICY_ACCEPT,
2899+ NOJS_POLICY_ACCEPT_TEMPORARILY,
2900+ NOJS_POLICY_BLOCK
2901+} NoJSPolicy;
2902+
2903+/* NoJS manager object */
2904+#define TYPE_NOJS (nojs_get_type())
2905+#define NOJS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NOJS, NoJS))
2906+#define IS_NOJS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_NOJS))
2907+#define NOJS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_NOJS, NoJSClass))
2908+#define IS_NOJS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_NOJS))
2909+#define NOJS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_NOJS, NoJSClass))
2910+
2911+typedef struct _NoJS NoJS;
2912+typedef struct _NoJSClass NoJSClass;
2913+typedef struct _NoJSPrivate NoJSPrivate;
2914+
2915+struct _NoJS
2916+{
2917+ /* Parent instance */
2918+ GObject parent_instance;
2919+
2920+ /* Private structure */
2921+ NoJSPrivate *priv;
2922+};
2923+
2924+struct _NoJSClass
2925+{
2926+ /* Parent class */
2927+ GObjectClass parent_class;
2928+
2929+ /* Virtual functions */
2930+ void (*uri_load_policy_status)(NoJS *self, gchar *inURI, NoJSPolicy inPolicy);
2931+ void (*policy_changed)(NoJS *self, gchar *inDomain);
2932+};
2933+
2934+/* Public API */
2935+GType nojs_get_type(void);
2936+
2937+NoJS* nojs_new(MidoriExtension *inExtension, MidoriApp *inApp);
2938+
2939+gchar* nojs_get_domain(NoJS *self, SoupURI *inURI);
2940+
2941+gint nojs_get_policy(NoJS *self, const gchar *inDomain);
2942+void nojs_set_policy(NoJS *self, const gchar *inDomain, NoJSPolicy inPolicy);
2943+
2944+NoJSPolicy nojs_get_policy_for_unknown_domain(NoJS *self);
2945+void nojs_set_policy_for_unknown_domain(NoJS *self, NoJSPolicy inPolicy);
2946+
2947+gboolean nojs_get_allow_all_sites(NoJS *self);
2948+void nojs_set_allow_all_sites(NoJS *self, gboolean inAllow);
2949+
2950+gboolean nojs_get_only_second_level_domain(NoJS *self);
2951+void nojs_set_only_second_level_domain(NoJS *self, gboolean inOnlySecondLevel);
2952+
2953+gchar* nojs_get_icon_path (const gchar* icon);
2954+
2955+/* Enumeration */
2956+GType nojs_policy_get_type(void) G_GNUC_CONST;
2957+#define NOJS_TYPE_POLICY (nojs_policy_get_type())
2958+
2959+G_END_DECLS
2960+
2961+#endif /* __NOJS__ */
2962
2963=== modified file 'po/POTFILES.in'
2964--- po/POTFILES.in 2013-07-29 19:10:25 +0000
2965+++ po/POTFILES.in 2013-07-29 19:51:25 +0000
2966@@ -51,6 +51,9 @@
2967 extensions/formhistory/formhistory.c
2968 extensions/formhistory/formhistory-gdom-frontend.c
2969 extensions/history-list.vala
2970+extensions/nojs/nojs-preferences.c
2971+extensions/nojs/nojs-view.c
2972+extensions/nojs/nojs.c
2973 extensions/mouse-gestures.c
2974 extensions/shortcuts.c
2975 extensions/status-clock.c

Subscribers

People subscribed via source and target branches

to all changes: