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

Proposed by Cris Dywan
Status: Merged
Merge reported by: Cris Dywan
Merged at revision: not available
Proposed branch: lp:~midori/midori/nojs
Merge into: lp:midori
Diff against target: 2994 lines (+2942/-0)
8 files modified
extensions/nojs/README.md (+1/-0)
extensions/nojs/main.c (+79/-0)
extensions/nojs/nojs-preferences.c (+748/-0)
extensions/nojs/nojs-preferences.h (+55/-0)
extensions/nojs/nojs-view.c (+823/-0)
extensions/nojs/nojs-view.h (+71/-0)
extensions/nojs/nojs.c (+1078/-0)
extensions/nojs/nojs.h (+87/-0)
To merge this branch: bzr merge lp:~midori/midori/nojs
Reviewer Review Type Date Requested Status
gue5t gue5t Needs Fixing
Stephan Haller Pending
Review via email: mp+165265@code.launchpad.net

Commit message

Introduce NoJS extension managing Javascript execution

Description of the change

Introduce NoJS extension managing Javascript execution

To post a comment you must log in.
Revision history for this message
gue5t gue5t (gue5t) wrote :
Download full text (3.9 KiB)

This is a really important addition to the browser and I'm super excited to have this added. That said, it's a lot of code and so there are a lot of tiny issues which could be addressed:

Icons - it would be nice to use stock items/named icons (providing a fallback image) so that themes and users can override and theme the icons. This could be done after merging the extension.

main.c:38: the "/* Preferences of this extension should be opened */" comment above _nojs_on_preferences_response seems to go with the next function.

nojs.c:96: it looks like _nojs_closure_VOID__STRING_ENUM is a generated function by glib-genmarshal. It would be nice to do this at build-time rather than embedding generated code directly, so it's easier to change later. This isn't a critical change for now.

nojs.c:177: it seems this would leak priv->databaseFilename.

nojs.c:209: typo, "extenstion"

I'm not really qualified to comment on the SQLite calls, but nothing sticks out as wrong. However, I'm curious about nojs.c:255 and nojs.c:270. Is line 268 guaranteed to set error to NULL if no error occurs? If not, line 270 could be a double-free. It's probably clearer to set error to NULL by hand anyway after freeing the error on line 255.

nojs.c:786: I've run into this webkit behavior as well, and it really sucks. Your workaround seems as good as any. :)

nojs.c:905: I don't think soup_uri_get_host will return "", but if it does, this will read from domain[-1]. In any event, the loop here would be more clear if you used strrchr instead. Regardless, a comment to say exactly what all the looping and char-comparisons are intended to do would be good.

nojs.c:1035: typo, "domanins"

nojs-preferences.c:95: this should probably be a const gchar*, since the function neither owns its memory nor modifies its contents; this better matches the return type of sqlite3_column_text.

nojs-preferences.c:161: "extenstion" again

nojs-preferences.c:177: no need for explicit return.

nojs-preferences.c:185: typo, "toogle". Same on 207 and 223, 245, 261, 281,

nojs-preferences.c:326: it seems like doing one-item delete queries in a for loop is probably not the most efficient way to interface with the db here, but for now this is fine. An XXX or TODO comment might be worth adding.

nojs-preferences.c:373: "cookie" should be "script" or "JS". This is duplicated in a variety of places in the file.

nojs-preferences.c:607: "#ifdef GTK__3_0_VERSION" should be "#if GTK_CHECK_VERSION(3, 0, 0)". Same for other references to "GTK__3_0_VERSION".

nojs-preferences.c:630: g_strdup_printf is called with a format-string argument derived from translations without any arguments to format. The strdup/free pair can be completely removed here.

nojs-view.c:93: it might be better to use G_STRFUNC here instead of __func__. I'm not sure of the exact reason, but I would expect the former is safer w/r/t portability. Maybe this print should be hidden behind a debugging environment variable as well. Not a blocking concern.

nojs-view.c:292: typos, "seperator" and "seperate"

nojs-view.c:548: typo, "ourselve"

Throughout the code: Generally we don't require extensions to conform to the Midori coding styl...

Read more...

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'extensions/nojs'
=== added file 'extensions/nojs/README.md'
--- extensions/nojs/README.md 1970-01-01 00:00:00 +0000
+++ extensions/nojs/README.md 2013-05-22 23:07:26 +0000
@@ -0,0 +1,1 @@
1This is an extension for Midori web browser. With this extension you can manage which sites are allowed to execute javascript.
02
=== added file 'extensions/nojs/main.c'
--- extensions/nojs/main.c 1970-01-01 00:00:00 +0000
+++ extensions/nojs/main.c 2013-05-22 23:07:26 +0000
@@ -0,0 +1,79 @@
1/*
2 Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 See the file COPYING for the full license text.
10*/
11
12#include "nojs.h"
13#include "nojs-preferences.h"
14
15/* Global instance */
16NoJS *noJS=NULL;
17
18/* This extension was activated */
19static void _nojs_on_activate(MidoriExtension *inExtension, MidoriApp *inApp, gpointer inUserData)
20{
21 g_return_if_fail(noJS==NULL);
22
23 noJS=nojs_new(inExtension, inApp);
24 nojs_set_policy_for_unknown_domain(noJS, midori_extension_get_integer(inExtension, "unknown-domain-policy"));
25 nojs_set_allow_all_sites(noJS, midori_extension_get_boolean(inExtension, "allow-all-sites"));
26 nojs_set_only_second_level_domain(noJS, midori_extension_get_boolean(inExtension, "only-second-level"));
27}
28
29/* This extension was deactivated */
30static void _nojs_on_deactivate(MidoriExtension *inExtension, gpointer inUserData)
31{
32 g_return_if_fail(noJS);
33
34 g_object_unref(noJS);
35 noJS=NULL;
36}
37
38/* Preferences of this extension should be opened */
39static void _nojs_on_preferences_response(GtkWidget* inDialog,
40 gint inResponse,
41 gpointer *inUserData)
42{
43 gtk_widget_destroy(inDialog);
44}
45
46static void _nojs_on_open_preferences(MidoriExtension *inExtension)
47{
48 g_return_if_fail(noJS);
49
50 /* Show preferences window */
51 GtkWidget* dialog;
52
53 dialog=nojs_preferences_new(noJS);
54 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
55 g_signal_connect(dialog, "response", G_CALLBACK (_nojs_on_preferences_response), NULL);
56 gtk_widget_show_all(dialog);
57}
58
59/* Main entry for extension */
60MidoriExtension *extension_init(void)
61{
62 /* Set up extension */
63 MidoriExtension *extension=g_object_new(MIDORI_TYPE_EXTENSION,
64 "name", _("NoJS"),
65 "description", _("Manage javascript permission per site"),
66 "version", "0.1" MIDORI_VERSION_SUFFIX,
67 "authors", "Stephan Haller <nomad@froevel.de>",
68 NULL);
69
70 midori_extension_install_integer(extension, "unknown-domain-policy", NOJS_POLICY_BLOCK);
71 midori_extension_install_boolean(extension, "allow-all-sites", FALSE);
72 midori_extension_install_boolean(extension, "only-second-level", TRUE);
73
74 g_signal_connect(extension, "activate", G_CALLBACK(_nojs_on_activate), NULL);
75 g_signal_connect(extension, "deactivate", G_CALLBACK(_nojs_on_deactivate), NULL);
76 g_signal_connect(extension, "open-preferences", G_CALLBACK(_nojs_on_open_preferences), NULL);
77
78 return(extension);
79}
080
=== added file 'extensions/nojs/nojs-menu-accept.png'
1Binary files extensions/nojs/nojs-menu-accept.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-menu-accept.png 2013-05-22 23:07:26 +0000 differ81Binary files extensions/nojs/nojs-menu-accept.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-menu-accept.png 2013-05-22 23:07:26 +0000 differ
=== added file 'extensions/nojs/nojs-menu-deny.png'
2Binary files extensions/nojs/nojs-menu-deny.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-menu-deny.png 2013-05-22 23:07:26 +0000 differ82Binary files extensions/nojs/nojs-menu-deny.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-menu-deny.png 2013-05-22 23:07:26 +0000 differ
=== added file 'extensions/nojs/nojs-menu-temporary.png'
3Binary files extensions/nojs/nojs-menu-temporary.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-menu-temporary.png 2013-05-22 23:07:26 +0000 differ83Binary files extensions/nojs/nojs-menu-temporary.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-menu-temporary.png 2013-05-22 23:07:26 +0000 differ
=== added file 'extensions/nojs/nojs-preferences.c'
--- extensions/nojs/nojs-preferences.c 1970-01-01 00:00:00 +0000
+++ extensions/nojs/nojs-preferences.c 2013-05-22 23:07:26 +0000
@@ -0,0 +1,748 @@
1/*
2 Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 See the file COPYING for the full license text.
10*/
11
12#include "nojs-preferences.h"
13
14/* Define this class in GObject system */
15G_DEFINE_TYPE(NoJSPreferences,
16 nojs_preferences,
17 GTK_TYPE_DIALOG)
18
19/* Properties */
20enum
21{
22 PROP_0,
23
24 PROP_MANAGER,
25
26 PROP_LAST
27};
28
29static GParamSpec* NoJSPreferencesProperties[PROP_LAST]={ 0, };
30
31/* Private structure - access only by public API if needed */
32#define NOJS_PREFERENCES_GET_PRIVATE(obj) \
33 (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_NOJS_PREFERENCES, NoJSPreferencesPrivate))
34
35struct _NoJSPreferencesPrivate
36{
37 /* Extension related */
38 NoJS *manager;
39 sqlite3 *database;
40
41 /* Dialog related */
42 GtkWidget *contentArea;
43 GtkListStore *listStore;
44 GtkWidget *list;
45 GtkTreeSelection *listSelection;
46 GtkWidget *deleteButton;
47 GtkWidget *deleteAllButton;
48 GtkWidget *allowAllSitesCheckbox;
49 GtkWidget *blockUnknownDomainsCheckbox;
50 GtkWidget *checkSecondLevelOnlyCheckbox;
51
52 gint signalAllowAllSitesToggledID;
53 gint signalBlockUnknownDomainsToggledID;
54 gint signalCheckSecondLevelOnlyToggledID;
55
56 gint signalManagerChangedDatabaseID;
57 gint signalManagerChangedAllowAllSitesID;
58 gint signalManagerChangedUnknownDomainPolicyID;
59 gint signalManagerChangedCheckSecondLevelID;
60};
61
62enum
63{
64 DOMAIN_COLUMN,
65 POLICY_COLUMN,
66 N_COLUMN
67};
68
69
70/* IMPLEMENTATION: Private variables and methods */
71
72/* Fill domain list with stored policies */
73static void _nojs_preferences_fill(NoJSPreferences *self)
74{
75 NoJSPreferencesPrivate *priv=self->priv;
76 gint success;
77 sqlite3_stmt *statement=NULL;
78
79 /* Clear tree/list view */
80 gtk_list_store_clear(priv->listStore);
81
82 /* If no database is present return here */
83 if(!priv->database) return;
84
85 /* Fill list store with policies from database */
86 success=sqlite3_prepare_v2(priv->database,
87 "SELECT site, value FROM policies;",
88 -1,
89 &statement,
90 NULL);
91 if(statement && success==SQLITE_OK)
92 {
93 gchar *domain;
94 gint policy;
95 gchar *policyName;
96 GtkTreeIter iter;
97
98 while(sqlite3_step(statement)==SQLITE_ROW)
99 {
100 /* Get values */
101 domain=(gchar*)sqlite3_column_text(statement, 0);
102 policy=sqlite3_column_int(statement, 1);
103
104 switch(policy)
105 {
106 case NOJS_POLICY_ACCEPT:
107 policyName=_("Accept");
108 break;
109
110 case NOJS_POLICY_ACCEPT_TEMPORARILY:
111 policyName=_("Accept for session");
112 break;
113
114 case NOJS_POLICY_BLOCK:
115 policyName=_("Block");
116 break;
117
118 default:
119 policyName=NULL;
120 break;
121 }
122
123 if(policyName)
124 {
125 gtk_list_store_append(priv->listStore, &iter);
126 gtk_list_store_set(priv->listStore,
127 &iter,
128 DOMAIN_COLUMN, domain,
129 POLICY_COLUMN, policyName,
130 -1);
131 }
132 }
133 }
134 else g_warning(_("SQL fails: %s"), sqlite3_errmsg(priv->database));
135
136 sqlite3_finalize(statement);
137}
138
139/* Database instance in manager changed */
140static void _nojs_preferences_on_manager_database_changed(NoJSPreferences *self,
141 GParamSpec *inSpec,
142 gpointer inUserData)
143{
144 NoJSPreferencesPrivate *priv=self->priv;
145 NoJS *manager=NOJS(inUserData);
146 gchar *databaseFile;
147
148 /* Close connection to any open database */
149 if(priv->database) sqlite3_close(priv->database);
150 priv->database=NULL;
151
152 /* Get pointer to new database and open database */
153 g_object_get(manager, "database-filename", &databaseFile, NULL);
154 if(databaseFile)
155 {
156 gint success;
157
158 success=sqlite3_open(databaseFile, &priv->database);
159 if(success!=SQLITE_OK)
160 {
161 g_warning(_("Could not open database of extenstion: %s"), sqlite3_errmsg(priv->database));
162
163 if(priv->database) sqlite3_close(priv->database);
164 priv->database=NULL;
165 }
166
167 g_free(databaseFile);
168 }
169
170 /* Fill list with new database */
171 _nojs_preferences_fill(self);
172
173 /* Set up availability of management buttons */
174 gtk_widget_set_sensitive(priv->deleteAllButton, priv->database!=NULL);
175 gtk_widget_set_sensitive(priv->list, priv->database!=NULL);
176
177 return;
178}
179
180/* Allow-all-sites changed in check-box or manager */
181static void _nojs_preferences_on_allow_all_sites_changed(NoJSPreferences *self,
182 gpointer *inUserData)
183{
184 NoJSPreferencesPrivate *priv=self->priv;
185 gboolean state;
186
187 /* Get toogle state of widget (but block signal for manager) and set in manager */
188 g_signal_handler_block(priv->manager, priv->signalManagerChangedAllowAllSitesID);
189
190 state=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->allowAllSitesCheckbox));
191 nojs_set_allow_all_sites(priv->manager, state);
192
193 g_signal_handler_unblock(priv->manager, priv->signalManagerChangedAllowAllSitesID);
194}
195
196static void _nojs_preferences_on_manager_allow_all_sites_changed(NoJSPreferences *self,
197 GParamSpec *inSpec,
198 gpointer inUserData)
199{
200 NoJSPreferencesPrivate *priv=self->priv;
201 NoJS *manager=NOJS(inUserData);
202 gboolean state;
203
204 /* Get new value from manager */
205 state=nojs_get_allow_all_sites(manager);
206
207 /* Set toogle in widget (but block signal for toggle) */
208 g_signal_handler_block(priv->allowAllSitesCheckbox, priv->signalAllowAllSitesToggledID);
209
210 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->allowAllSitesCheckbox), state);
211
212 g_signal_handler_unblock(priv->allowAllSitesCheckbox, priv->signalAllowAllSitesToggledID);
213}
214
215/* Block-unknown-domains changed in check-box or manager */
216static void _nojs_preferences_on_block_unknown_domains_changed(NoJSPreferences *self,
217 gpointer *inUserData)
218{
219 NoJSPreferencesPrivate *priv=self->priv;
220 gboolean state;
221 NoJSPolicy policy;
222
223 /* Get toogle state of widget (but block signal for manager) and set in manager */
224 g_signal_handler_block(priv->manager, priv->signalManagerChangedUnknownDomainPolicyID);
225
226 state=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->blockUnknownDomainsCheckbox));
227 policy=(state ? NOJS_POLICY_BLOCK : NOJS_POLICY_ACCEPT);
228 nojs_set_policy_for_unknown_domain(priv->manager, policy);
229
230 g_signal_handler_unblock(priv->manager, priv->signalManagerChangedUnknownDomainPolicyID);
231}
232
233static void _nojs_preferences_on_manager_unknown_domain_policy_changed(NoJSPreferences *self,
234 GParamSpec *inSpec,
235 gpointer inUserData)
236{
237 NoJSPreferencesPrivate *priv=self->priv;
238 NoJS *manager=NOJS(inUserData);
239 NoJSPolicy policy;
240 gboolean state;
241
242 /* Get new value from manager */
243 policy=nojs_get_policy_for_unknown_domain(manager);
244
245 /* Set toogle in widget (but block signal for toggle) */
246 g_signal_handler_block(priv->blockUnknownDomainsCheckbox, priv->signalBlockUnknownDomainsToggledID);
247
248 state=(policy==NOJS_POLICY_BLOCK ? TRUE : FALSE);
249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->blockUnknownDomainsCheckbox), state);
250
251 g_signal_handler_unblock(priv->blockUnknownDomainsCheckbox, priv->signalBlockUnknownDomainsToggledID);
252}
253
254/* Only-second-level changed in check-box or manager */
255static void _nojs_preferences_on_check_second_level_only_changed(NoJSPreferences *self,
256 gpointer *inUserData)
257{
258 NoJSPreferencesPrivate *priv=self->priv;
259 gboolean state;
260
261 /* Get toogle state of widget (but block signal for manager) and set in manager */
262 g_signal_handler_block(priv->manager, priv->signalManagerChangedCheckSecondLevelID);
263
264 state=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->checkSecondLevelOnlyCheckbox));
265 nojs_set_only_second_level_domain(priv->manager, state);
266
267 g_signal_handler_unblock(priv->manager, priv->signalManagerChangedCheckSecondLevelID);
268}
269
270static void _nojs_preferences_on_manager_only_second_level_changed(NoJSPreferences *self,
271 GParamSpec *inSpec,
272 gpointer inUserData)
273{
274 NoJSPreferencesPrivate *priv=self->priv;
275 NoJS *manager=NOJS(inUserData);
276 gboolean state;
277
278 /* Get new value from manager */
279 state=nojs_get_only_second_level_domain(manager);
280
281 /* Set toogle in widget (but block signal for toggle) */
282 g_signal_handler_block(priv->checkSecondLevelOnlyCheckbox, priv->signalCheckSecondLevelOnlyToggledID);
283
284 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->checkSecondLevelOnlyCheckbox), state);
285
286 g_signal_handler_unblock(priv->checkSecondLevelOnlyCheckbox, priv->signalCheckSecondLevelOnlyToggledID);
287}
288
289/* Selection in list changed */
290void _nojs_preferences_changed_selection(NoJSPreferences *self,
291 GtkTreeSelection *inSelection)
292{
293 gboolean selected=(gtk_tree_selection_count_selected_rows(inSelection)>0 ? TRUE: FALSE);
294
295 gtk_widget_set_sensitive(self->priv->deleteButton, selected);
296}
297
298/* Delete button was clicked on selection */
299void _nojs_preferences_on_delete_selection(NoJSPreferences *self,
300 GtkButton *inButton)
301{
302 NoJSPreferencesPrivate *priv=self->priv;
303 GList *rows, *row, *refs=NULL;
304 GtkTreeRowReference *ref;
305 GtkTreeModel *model=GTK_TREE_MODEL(priv->listStore);
306 GtkTreeIter iter;
307 GtkTreePath *path;
308 gchar *domain;
309 gchar *sql;
310 gint success;
311 gchar *error;
312
313 /* Get selected rows in list and create a row reference because
314 * we will modify the model while iterating through selected rows
315 */
316 rows=gtk_tree_selection_get_selected_rows(priv->listSelection, &model);
317 for(row=rows; row; row=row->next)
318 {
319 ref=gtk_tree_row_reference_new(model, (GtkTreePath*)row->data);
320 refs=g_list_prepend(refs, ref);
321 }
322 g_list_foreach(rows,(GFunc)gtk_tree_path_free, NULL);
323 g_list_free(rows);
324
325 /* Delete each selected row by its reference */
326 for(row=refs; row; row=row->next)
327 {
328 /* Get domain from selected row */
329 path=gtk_tree_row_reference_get_path((GtkTreeRowReference*)row->data);
330 gtk_tree_model_get_iter(model, &iter, path);
331 gtk_tree_model_get(model, &iter, DOMAIN_COLUMN, &domain, -1);
332
333 /* Delete domain from database */
334 sql=sqlite3_mprintf("DELETE FROM policies WHERE site='%q';", domain);
335 success=sqlite3_exec(priv->database,
336 sql,
337 NULL,
338 NULL,
339 &error);
340 if(success!=SQLITE_OK || error)
341 {
342 if(error)
343 {
344 g_critical(_("Failed to execute database statement: %s"), error);
345 sqlite3_free(error);
346 }
347 else g_critical(_("Failed to execute database statement: %s"), sqlite3_errmsg(priv->database));
348 }
349 sqlite3_free(sql);
350
351 /* Delete row from model */
352 gtk_list_store_remove(priv->listStore, &iter);
353 }
354 g_list_foreach(refs,(GFunc)gtk_tree_row_reference_free, NULL);
355 g_list_free(refs);
356}
357
358/* Delete all button was clicked */
359void _nojs_preferences_on_delete_all(NoJSPreferences *self,
360 GtkButton *inButton)
361{
362 NoJSPreferencesPrivate *priv=self->priv;
363 gint success;
364 gchar *error=NULL;
365 GtkWidget *dialog;
366 gint dialogResponse;
367
368 /* Ask user if he really wants to delete all permissions */
369 dialog=gtk_message_dialog_new(GTK_WINDOW(self),
370 GTK_DIALOG_MODAL,
371 GTK_MESSAGE_QUESTION,
372 GTK_BUTTONS_YES_NO,
373 _("Do you really want to delete all cookie permissions?"));
374
375 gtk_window_set_title(GTK_WINDOW(dialog), _("Delete all cookie permissions?"));
376 gtk_window_set_icon_name(GTK_WINDOW(dialog), GTK_STOCK_PROPERTIES);
377
378 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
379 _("This action will delete all cookie permissions. "
380 "You will be asked for permissions again for each web site visited."));
381
382 dialogResponse=gtk_dialog_run(GTK_DIALOG(dialog));
383 gtk_widget_destroy(dialog);
384
385 if(dialogResponse==GTK_RESPONSE_NO) return;
386
387 /* Delete all permission */
388 success=sqlite3_exec(priv->database,
389 "DELETE FROM policies;",
390 NULL,
391 NULL,
392 &error);
393
394 if(success!=SQLITE_OK || error)
395 {
396 if(error)
397 {
398 g_critical(_("Failed to execute database statement: %s"), error);
399 sqlite3_free(error);
400 }
401 }
402
403 /* Re-setup list */
404 _nojs_preferences_fill(self);
405}
406
407/* Sorting callbacks */
408static gint _nojs_preferences_sort_string_callback(GtkTreeModel *inModel,
409 GtkTreeIter *inLeft,
410 GtkTreeIter *inRight,
411 gpointer inUserData)
412{
413 gchar *left, *right;
414 gint column=GPOINTER_TO_INT(inUserData);
415 gint result;
416
417 gtk_tree_model_get(inModel, inLeft, column, &left, -1);
418 gtk_tree_model_get(inModel, inRight, column, &right, -1);
419
420 result=g_strcmp0(left, right);
421
422 g_free(left);
423 g_free(right);
424
425 return(result);
426}
427
428/* IMPLEMENTATION: GObject */
429
430/* Finalize this object */
431static void nojs_preferences_finalize(GObject *inObject)
432{
433 NoJSPreferencesPrivate *priv=NOJS_PREFERENCES(inObject)->priv;
434
435 /* Dispose allocated resources */
436 if(priv->database) sqlite3_close(priv->database);
437 priv->database=NULL;
438
439 if(priv->manager)
440 {
441 if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
442 priv->signalManagerChangedDatabaseID=0;
443
444 if(priv->signalManagerChangedAllowAllSitesID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedAllowAllSitesID);
445 priv->signalManagerChangedAllowAllSitesID=0;
446
447 if(priv->signalManagerChangedUnknownDomainPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedUnknownDomainPolicyID);
448 priv->signalManagerChangedUnknownDomainPolicyID=0;
449
450 if(priv->signalManagerChangedCheckSecondLevelID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedCheckSecondLevelID);
451 priv->signalManagerChangedCheckSecondLevelID=0;
452
453 g_object_unref(priv->manager);
454 priv->manager=NULL;
455 }
456
457 /* Call parent's class finalize method */
458 G_OBJECT_CLASS(nojs_preferences_parent_class)->finalize(inObject);
459}
460
461/* Set/get properties */
462static void nojs_preferences_set_property(GObject *inObject,
463 guint inPropID,
464 const GValue *inValue,
465 GParamSpec *inSpec)
466{
467 NoJSPreferences *self=NOJS_PREFERENCES(inObject);
468 NoJSPreferencesPrivate *priv=self->priv;
469 GObject *manager;
470
471 switch(inPropID)
472 {
473 /* Construct-only properties */
474 case PROP_MANAGER:
475 /* Release old manager if available and disconnect signals */
476 if(priv->manager)
477 {
478 if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
479 priv->signalManagerChangedDatabaseID=0;
480
481 if(priv->signalManagerChangedAllowAllSitesID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedAllowAllSitesID);
482 priv->signalManagerChangedAllowAllSitesID=0;
483
484 if(priv->signalManagerChangedUnknownDomainPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedUnknownDomainPolicyID);
485 priv->signalManagerChangedUnknownDomainPolicyID=0;
486
487 if(priv->signalManagerChangedCheckSecondLevelID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedCheckSecondLevelID);
488 priv->signalManagerChangedCheckSecondLevelID=0;
489
490 g_object_unref(priv->manager);
491 priv->manager=NULL;
492 }
493
494 /* Set new cookie permission manager and
495 * listen to changes in database property
496 */
497 manager=g_value_get_object(inValue);
498 if(manager)
499 {
500 priv->manager=g_object_ref(manager);
501
502 priv->signalManagerChangedDatabaseID=
503 g_signal_connect_swapped(priv->manager,
504 "notify::database-filename",
505 G_CALLBACK(_nojs_preferences_on_manager_database_changed),
506 self);
507 _nojs_preferences_on_manager_database_changed(self, NULL, priv->manager);
508
509 priv->signalManagerChangedAllowAllSitesID=
510 g_signal_connect_swapped(priv->manager,
511 "notify::allow-all-sites",
512 G_CALLBACK(_nojs_preferences_on_manager_allow_all_sites_changed),
513 self);
514 _nojs_preferences_on_manager_allow_all_sites_changed(self, NULL, priv->manager);
515
516 priv->signalManagerChangedUnknownDomainPolicyID=
517 g_signal_connect_swapped(priv->manager,
518 "notify::unknown-domain-policy",
519 G_CALLBACK(_nojs_preferences_on_manager_unknown_domain_policy_changed),
520 self);
521 _nojs_preferences_on_manager_unknown_domain_policy_changed(self, NULL, priv->manager);
522
523 priv->signalManagerChangedCheckSecondLevelID=
524 g_signal_connect_swapped(priv->manager,
525 "notify::only-second-level",
526 G_CALLBACK(_nojs_preferences_on_manager_only_second_level_changed),
527 self);
528 _nojs_preferences_on_manager_only_second_level_changed(self, NULL, priv->manager);
529 }
530 break;
531
532 default:
533 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
534 break;
535 }
536}
537
538static void nojs_preferences_get_property(GObject *inObject,
539 guint inPropID,
540 GValue *outValue,
541 GParamSpec *inSpec)
542{
543 NoJSPreferences *self=NOJS_PREFERENCES(inObject);
544
545 switch(inPropID)
546 {
547 case PROP_MANAGER:
548 g_value_set_object(outValue, self->priv->manager);
549 break;
550
551 default:
552 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
553 break;
554 }
555}
556
557/* Class initialization
558 * Override functions in parent classes and define properties and signals
559 */
560static void nojs_preferences_class_init(NoJSPreferencesClass *klass)
561{
562 GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
563
564 /* Override functions */
565 gobjectClass->finalize=nojs_preferences_finalize;
566 gobjectClass->set_property=nojs_preferences_set_property;
567 gobjectClass->get_property=nojs_preferences_get_property;
568
569 /* Set up private structure */
570 g_type_class_add_private(klass, sizeof(NoJSPreferencesPrivate));
571
572 /* Define properties */
573 NoJSPreferencesProperties[PROP_MANAGER]=
574 g_param_spec_object("manager",
575 _("Manager instance"),
576 _("Instance to global NoJS manager"),
577 TYPE_NOJS,
578 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
579
580 g_object_class_install_properties(gobjectClass, PROP_LAST, NoJSPreferencesProperties);
581}
582
583/* Object initialization
584 * Create private structure and set up default values
585 */
586static void nojs_preferences_init(NoJSPreferences *self)
587{
588 NoJSPreferencesPrivate *priv;
589 GtkTreeSortable *sortableList;
590 GtkCellRenderer *renderer;
591 GtkTreeViewColumn *column;
592 GtkWidget *widget;
593 gchar *text;
594 gchar *dialogTitle;
595 GtkWidget *scrolled;
596 GtkWidget *vbox;
597 GtkWidget *hbox;
598 gint width, height;
599
600 priv=self->priv=NOJS_PREFERENCES_GET_PRIVATE(self);
601
602 /* Set up default values */
603 priv->manager=NULL;
604
605 /* Get content area to add gui controls to */
606 priv->contentArea=gtk_dialog_get_content_area(GTK_DIALOG(self));
607#ifdef GTK__3_0_VERSION
608 vbox=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
609 gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
610#else
611 vbox=gtk_vbox_new(FALSE, 0);
612#endif
613
614 /* Set up dialog */
615 dialogTitle=_("Configure NoJS");
616
617 gtk_window_set_title(GTK_WINDOW(self), dialogTitle);
618 gtk_window_set_icon_name(GTK_WINDOW(self), GTK_STOCK_PROPERTIES);
619
620 sokoke_widget_get_text_size(GTK_WIDGET(self), "M", &width, &height);
621 gtk_window_set_default_size(GTK_WINDOW(self), width*52, -1);
622
623 widget=sokoke_xfce_header_new(gtk_window_get_icon_name(GTK_WINDOW(self)), dialogTitle);
624 if(widget) gtk_box_pack_start(GTK_BOX(priv->contentArea), widget, FALSE, FALSE, 0);
625
626 gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
627
628 /* Set up description */
629 widget=gtk_label_new(NULL);
630 text=g_strdup_printf(_("Below is a list of all web sites and the policy set for them. "
631 "You can delete policies by marking the entries and clicking on <i>Delete</i>."));
632 gtk_label_set_markup(GTK_LABEL(widget), text);
633 g_free(text);
634 gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE);
635 gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 4);
636
637 /* Set up domain list */
638 priv->listStore=gtk_list_store_new(N_COLUMN,
639 G_TYPE_STRING, /* DOMAIN_COLUMN */
640 G_TYPE_STRING /* POLICY_COLUMN */);
641
642 sortableList=GTK_TREE_SORTABLE(priv->listStore);
643 gtk_tree_sortable_set_sort_func(sortableList,
644 DOMAIN_COLUMN,
645 (GtkTreeIterCompareFunc)_nojs_preferences_sort_string_callback,
646 GINT_TO_POINTER(DOMAIN_COLUMN),
647 NULL);
648 gtk_tree_sortable_set_sort_func(sortableList,
649 POLICY_COLUMN,
650 (GtkTreeIterCompareFunc)_nojs_preferences_sort_string_callback,
651 GINT_TO_POINTER(POLICY_COLUMN),
652 NULL);
653 gtk_tree_sortable_set_sort_column_id(sortableList, DOMAIN_COLUMN, GTK_SORT_ASCENDING);
654
655 /* Set up domain list view */
656 priv->list=gtk_tree_view_new_with_model(GTK_TREE_MODEL(priv->listStore));
657
658#ifndef GTK__3_0_VERSION
659 gtk_widget_set_size_request(priv->list, -1, 300);
660#endif
661
662 priv->listSelection=gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->list));
663 gtk_tree_selection_set_mode(priv->listSelection, GTK_SELECTION_MULTIPLE);
664 g_signal_connect_swapped(priv->listSelection, "changed", G_CALLBACK(_nojs_preferences_changed_selection), self);
665
666 renderer=gtk_cell_renderer_text_new();
667 column=gtk_tree_view_column_new_with_attributes(_("Domain"),
668 renderer,
669 "text", DOMAIN_COLUMN,
670 NULL);
671 gtk_tree_view_column_set_sort_column_id(column, DOMAIN_COLUMN);
672 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->list), column);
673
674 renderer=gtk_cell_renderer_text_new();
675 column=gtk_tree_view_column_new_with_attributes(_("Policy"),
676 renderer,
677 "text", POLICY_COLUMN,
678 NULL);
679 gtk_tree_view_column_set_sort_column_id(column, POLICY_COLUMN);
680 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->list), column);
681
682 scrolled=gtk_scrolled_window_new(NULL, NULL);
683#ifdef GTK__3_0_VERSION
684 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolled), height*10);
685#endif
686 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
687 gtk_container_add(GTK_CONTAINER(scrolled), priv->list);
688 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
689 gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 5);
690
691 /* Set up cookie domain list management buttons */
692#ifdef GTK__3_0_VERSION
693 hbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
694 gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
695#else
696 hbox=gtk_hbox_new(FALSE, 0);
697#endif
698
699 priv->deleteButton=gtk_button_new_from_stock(GTK_STOCK_DELETE);
700 gtk_widget_set_sensitive(priv->deleteButton, FALSE);
701 gtk_container_add(GTK_CONTAINER(hbox), priv->deleteButton);
702 g_signal_connect_swapped(priv->deleteButton, "clicked", G_CALLBACK(_nojs_preferences_on_delete_selection), self);
703
704 priv->deleteAllButton=gtk_button_new_with_mnemonic(_("Delete _all"));
705 gtk_button_set_image(GTK_BUTTON(priv->deleteAllButton), gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON));
706 gtk_widget_set_sensitive(priv->deleteAllButton, FALSE);
707 gtk_container_add(GTK_CONTAINER(hbox), priv->deleteAllButton);
708 g_signal_connect_swapped(priv->deleteAllButton, "clicked", G_CALLBACK(_nojs_preferences_on_delete_all), self);
709
710 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
711
712 /* Add "allow-all-sites" checkbox */
713 priv->allowAllSitesCheckbox=gtk_check_button_new_with_mnemonic(_("A_llow scripts at all sites"));
714 priv->signalAllowAllSitesToggledID=g_signal_connect_swapped(priv->allowAllSitesCheckbox,
715 "toggled",
716 G_CALLBACK(_nojs_preferences_on_allow_all_sites_changed),
717 self);
718 gtk_box_pack_start(GTK_BOX(vbox), priv->allowAllSitesCheckbox, TRUE, TRUE, 5);
719
720 /* Add "block-unknown-domains" checkbox */
721 priv->blockUnknownDomainsCheckbox=gtk_check_button_new_with_mnemonic(_("Bloc_k scripts at unknown domains by default"));
722 priv->signalBlockUnknownDomainsToggledID=g_signal_connect_swapped(priv->blockUnknownDomainsCheckbox,
723 "toggled",
724 G_CALLBACK(_nojs_preferences_on_block_unknown_domains_changed),
725 self);
726 gtk_box_pack_start(GTK_BOX(vbox), priv->blockUnknownDomainsCheckbox, TRUE, TRUE, 5);
727
728 /* Add "check-second-level-only" checkbox */
729 priv->checkSecondLevelOnlyCheckbox=gtk_check_button_new_with_mnemonic(_("S_et permissions on second-level domain"));
730 priv->signalCheckSecondLevelOnlyToggledID=g_signal_connect_swapped(priv->checkSecondLevelOnlyCheckbox,
731 "toggled",
732 G_CALLBACK(_nojs_preferences_on_check_second_level_only_changed),
733 self);
734 gtk_box_pack_start(GTK_BOX(vbox), priv->checkSecondLevelOnlyCheckbox, TRUE, TRUE, 5);
735
736 /* Finalize setup of content area */
737 gtk_container_add(GTK_CONTAINER(priv->contentArea), vbox);
738}
739
740/* Implementation: Public API */
741
742/* Create new object */
743GtkWidget* nojs_preferences_new(NoJS *inManager)
744{
745 return(g_object_new(TYPE_NOJS_PREFERENCES,
746 "manager", inManager,
747 NULL));
748}
0749
=== added file 'extensions/nojs/nojs-preferences.h'
--- extensions/nojs/nojs-preferences.h 1970-01-01 00:00:00 +0000
+++ extensions/nojs/nojs-preferences.h 2013-05-22 23:07:26 +0000
@@ -0,0 +1,55 @@
1/*
2 Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 See the file COPYING for the full license text.
10*/
11
12#ifndef __NOJS_PREFERENCES__
13#define __NOJS_PREFERENCES__
14
15#include "config.h"
16#include <midori/midori.h>
17
18#include "nojs.h"
19
20G_BEGIN_DECLS
21
22#define TYPE_NOJS_PREFERENCES (nojs_preferences_get_type())
23#define NOJS_PREFERENCES(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NOJS_PREFERENCES, NoJSPreferences))
24#define IS_NOJS_PREFERENCES(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_NOJS_PREFERENCES))
25#define NOJS_PREFERENCES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_NOJS_PREFERENCES, NoJSPreferencesClass))
26#define IS_NOJS_PREFERENCES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_NOJS_PREFERENCES))
27#define NOJS_PREFERENCES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_NOJS_PREFERENCES, NoJSPreferencesClass))
28
29typedef struct _NoJSPreferences NoJSPreferences;
30typedef struct _NoJSPreferencesClass NoJSPreferencesClass;
31typedef struct _NoJSPreferencesPrivate NoJSPreferencesPrivate;
32
33struct _NoJSPreferences
34{
35 /* Parent instance */
36 GtkDialog parent_instance;
37
38 /* Private structure */
39 NoJSPreferencesPrivate *priv;
40};
41
42struct _NoJSPreferencesClass
43{
44 /* Parent class */
45 GtkDialogClass parent_class;
46};
47
48/* Public API */
49GType nojs_preferences_get_type(void);
50
51GtkWidget* nojs_preferences_new(NoJS *inManager);
52
53G_END_DECLS
54
55#endif /* __NOJS_PREFERENCES__ */
056
=== added file 'extensions/nojs/nojs-statusicon-allowed.png'
1Binary files extensions/nojs/nojs-statusicon-allowed.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-statusicon-allowed.png 2013-05-22 23:07:26 +0000 differ57Binary files extensions/nojs/nojs-statusicon-allowed.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-statusicon-allowed.png 2013-05-22 23:07:26 +0000 differ
=== added file 'extensions/nojs/nojs-statusicon-denied.png'
2Binary files extensions/nojs/nojs-statusicon-denied.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-statusicon-denied.png 2013-05-22 23:07:26 +0000 differ58Binary files extensions/nojs/nojs-statusicon-denied.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-statusicon-denied.png 2013-05-22 23:07:26 +0000 differ
=== added file 'extensions/nojs/nojs-statusicon-mixed.png'
3Binary files extensions/nojs/nojs-statusicon-mixed.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-statusicon-mixed.png 2013-05-22 23:07:26 +0000 differ59Binary files extensions/nojs/nojs-statusicon-mixed.png 1970-01-01 00:00:00 +0000 and extensions/nojs/nojs-statusicon-mixed.png 2013-05-22 23:07:26 +0000 differ
=== added file 'extensions/nojs/nojs-view.c'
--- extensions/nojs/nojs-view.c 1970-01-01 00:00:00 +0000
+++ extensions/nojs/nojs-view.c 2013-05-22 23:07:26 +0000
@@ -0,0 +1,823 @@
1/*
2 Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 See the file COPYING for the full license text.
10*/
11
12#include "nojs-view.h"
13#include "nojs-preferences.h"
14
15/* Define this class in GObject system */
16G_DEFINE_TYPE(NoJSView,
17 nojs_view,
18 G_TYPE_OBJECT)
19
20/* Properties */
21enum
22{
23 PROP_0,
24
25 PROP_MANAGER,
26 PROP_BROWSER,
27 PROP_VIEW,
28 PROP_MENU_ICON_STATE,
29
30 PROP_LAST
31};
32
33static GParamSpec* NoJSViewProperties[PROP_LAST]={ 0, };
34
35/* Private structure - access only by public API if needed */
36#define NOJS_VIEW_GET_PRIVATE(obj) \
37 (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_NOJS_VIEW, NoJSViewPrivate))
38
39struct _NoJSViewPrivate
40{
41 /* Extension related */
42 NoJS *manager;
43 MidoriBrowser *browser;
44 MidoriView *view;
45
46 GtkWidget *menu;
47 gboolean menuPolicyWasChanged;
48 NoJSMenuIconState menuIconState;
49
50 GSList *resourceURIs;
51};
52
53/* IMPLEMENTATION: Private variables and methods */
54
55/* Preferences of this extension should be opened */
56static void _nojs_view_on_preferences_response(GtkWidget* inDialog,
57 gint inResponse,
58 gpointer *inUserData)
59{
60 gtk_widget_destroy(inDialog);
61}
62
63static void _nojs_view_on_open_preferences(NoJSView *self, gpointer inUserData)
64{
65 g_return_if_fail(NOJS_IS_VIEW(self));
66
67 NoJSViewPrivate *priv=self->priv;
68
69 /* Show preferences window */
70 GtkWidget* dialog;
71
72 dialog=nojs_preferences_new(priv->manager);
73 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
74 g_signal_connect(dialog, "response", G_CALLBACK (_nojs_view_on_preferences_response), self);
75 gtk_widget_show_all(dialog);
76}
77
78/* Selection was done in menu */
79static void _nojs_view_on_menu_selection_done(NoJSView *self, gpointer inUserData)
80{
81 g_return_if_fail(NOJS_IS_VIEW(self));
82
83 NoJSViewPrivate *priv=self->priv;
84
85 /* Check if any policy was changed and reload page */
86 if(priv->menuPolicyWasChanged!=FALSE)
87 {
88 /* Reset flag that any policy was changed */
89 priv->menuPolicyWasChanged=FALSE;
90
91 /* Reload page */
92 midori_view_reload(priv->view, FALSE);
93g_message("%s: Reloading page %s as policy has changed", __func__, midori_view_get_display_uri(priv->view));
94 }
95}
96
97/* Destroy menu */
98static void _nojs_view_destroy_menu(NoJSView *self)
99{
100 g_return_if_fail(NOJS_IS_VIEW(self));
101 g_return_if_fail(self->priv->menu!=NULL);
102
103 NoJSViewPrivate *priv=self->priv;
104
105 /* Empty menu and list of domains added to menu */
106 gtk_widget_destroy(priv->menu);
107 priv->menu=NULL;
108
109 /* Reset menu icon to default state */
110 priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
111 g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
112}
113
114/* Create empty menu */
115static void _nojs_view_create_empty_menu(NoJSView *self)
116{
117 g_return_if_fail(NOJS_IS_VIEW(self));
118 g_return_if_fail(self->priv->menu==NULL);
119
120 NoJSViewPrivate *priv=self->priv;
121 GtkWidget *item;
122
123 /* Create new menu and set up default items */
124 priv->menu=gtk_menu_new();
125
126 item=gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
127 g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_open_preferences), self);
128 gtk_menu_shell_prepend(GTK_MENU_SHELL(priv->menu), item);
129 gtk_widget_show_all(item);
130
131 /* Reset flag that any policy was changed */
132 priv->menuPolicyWasChanged=FALSE;
133
134 /* Reset menu icon to default state */
135 priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
136 g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
137
138 /* Connect signal to menu */
139 g_signal_connect_swapped(priv->menu, "selection-done", G_CALLBACK(_nojs_view_on_menu_selection_done), self);
140}
141
142/* Change visibility state of menu item for a domain depending on policy */
143static gboolean _nojs_view_menu_item_change_policy(NoJSView *self, const gchar *inDomain, NoJSPolicy inPolicy)
144{
145 g_return_val_if_fail(NOJS_IS_VIEW(self), FALSE);
146 g_return_val_if_fail(inDomain, FALSE);
147
148 NoJSViewPrivate *priv=self->priv;
149 GList *items, *iter;
150 gboolean updated;
151
152 /* Handle accept-for-session like accept when showing or hiding menu items */
153 if(inPolicy==NOJS_POLICY_ACCEPT_TEMPORARILY) inPolicy=NOJS_POLICY_ACCEPT;
154
155 /* Update menu items */
156 updated=FALSE;
157 items=gtk_container_get_children(GTK_CONTAINER(priv->menu));
158 for(iter=items; iter; iter=iter->next)
159 {
160 /* Only check and update menu items (not separators and so on) */
161 if(GTK_IS_MENU_ITEM(iter->data))
162 {
163 GtkMenuItem *item=GTK_MENU_ITEM(iter->data);
164 const gchar *itemDomain;
165 NoJSPolicy itemPolicy;
166
167 itemDomain=(const gchar*)g_object_get_data(G_OBJECT(item), "domain");
168 itemPolicy=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "policy"));
169
170 /* Handle accept-for-session like accept when showing or hiding menu items */
171 if(itemPolicy==NOJS_POLICY_ACCEPT_TEMPORARILY) itemPolicy=NOJS_POLICY_ACCEPT;
172
173 /* If menu item has "domain"-data update its visibility state
174 * depending on matching policy
175 */
176 if(g_strcmp0(itemDomain, inDomain)==0)
177 {
178 if(itemPolicy==inPolicy) gtk_widget_hide(GTK_WIDGET(item));
179 else gtk_widget_show_all(GTK_WIDGET(item));
180
181 /* Set flag that at least one menu item was updated */
182 updated=TRUE;
183 }
184 }
185 }
186 g_list_free(items);
187
188 /* Return flag indicating if at least one menu item was updated */
189 return(updated);
190}
191
192/* A menu item was selected */
193static void _nojs_view_on_menu_item_activate(NoJSView *self, gpointer inUserData)
194{
195 g_return_if_fail(NOJS_IS_VIEW(self));
196 g_return_if_fail(GTK_IS_MENU_ITEM(inUserData));
197
198 NoJSViewPrivate *priv=self->priv;
199 GtkMenuItem *item=GTK_MENU_ITEM(inUserData);
200 const gchar *domain;
201 NoJSPolicy policy;
202
203 /* Get domain and policy to set */
204 domain=(const gchar*)g_object_get_data(G_OBJECT(item), "domain");
205 policy=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "policy"));
206 g_return_if_fail(domain);
207 g_return_if_fail(policy>=NOJS_POLICY_ACCEPT && policy<=NOJS_POLICY_BLOCK);
208
209 /* Set policy for domain and update menu items */
210 _nojs_view_menu_item_change_policy(self, domain, policy);
211 nojs_set_policy(priv->manager, domain, policy);
212
213 /* Set flag that a policy was changed */
214 priv->menuPolicyWasChanged=TRUE;
215}
216
217/* Add site to menu */
218static void _nojs_view_add_site_to_menu(NoJSView *self, const gchar *inDomain, NoJSPolicy inPolicy)
219{
220 g_return_if_fail(NOJS_IS_VIEW(self));
221 g_return_if_fail(inDomain);
222
223 NoJSViewPrivate *priv=self->priv;
224 GtkWidget *item;
225 gchar *itemLabel;
226 GtkWidget *itemImage;
227 gchar *itemImageFile;
228 static gint INSERT_POSITION=1;
229 NoJSMenuIconState newMenuIconState;
230
231 /* Create menu object if not available */
232 if(!priv->menu) _nojs_view_create_empty_menu(self);
233
234 /* Check if domain was already added to menu. If it exists just update it. */
235 if(_nojs_view_menu_item_change_policy(self, inDomain, inPolicy)==TRUE) return;
236
237 /* Add menu item(s) for domain */
238 itemLabel=g_strdup_printf(_("Deny %s"), inDomain);
239 item=gtk_image_menu_item_new_with_label(itemLabel);
240#ifdef ALTERNATE_DATADIR
241 itemImageFile=g_build_filename(ALTERNATE_DATADIR, "nojs-menu-deny.png", NULL);
242#else
243 itemImageFile=g_build_filename(MDATADIR, PACKAGE_NAME, "nojs", "nojs-menu-deny.png", NULL);
244#endif
245 itemImage=gtk_image_new_from_file(itemImageFile);
246 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
247 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
248 gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
249 if(inPolicy!=NOJS_POLICY_BLOCK) gtk_widget_show_all(item);
250 g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
251 g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_BLOCK));
252 g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
253 g_free(itemLabel);
254 g_free(itemImageFile);
255
256 itemLabel=g_strdup_printf(_("Allow %s"), inDomain);
257 item=gtk_image_menu_item_new_with_label(itemLabel);
258#ifdef ALTERNATE_DATADIR
259 itemImageFile=g_build_filename(ALTERNATE_DATADIR, "nojs-menu-accept.png", NULL);
260#else
261 itemImageFile=g_build_filename(MDATADIR, PACKAGE_NAME, "nojs", "nojs-menu-accept.png", NULL);
262#endif
263 itemImage=gtk_image_new_from_file(itemImageFile);
264 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
265 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
266 gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
267 if(inPolicy!=NOJS_POLICY_ACCEPT && inPolicy!=NOJS_POLICY_ACCEPT_TEMPORARILY) gtk_widget_show_all(item);
268 g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
269 g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_ACCEPT));
270 g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
271 g_free(itemLabel);
272 g_free(itemImageFile);
273
274 itemLabel=g_strdup_printf(_("Allow %s this session"), inDomain);
275 item=gtk_image_menu_item_new_with_label(itemLabel);
276#ifdef ALTERNATE_DATADIR
277 itemImageFile=g_build_filename(ALTERNATE_DATADIR, "nojs-menu-temporary.png", NULL);
278#else
279 itemImageFile=g_build_filename(MDATADIR, PACKAGE_NAME, "nojs", "nojs-menu-temporary.png", NULL);
280#endif
281 itemImage=gtk_image_new_from_file(itemImageFile);
282 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
283 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
284 gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
285 if(inPolicy!=NOJS_POLICY_ACCEPT && inPolicy!=NOJS_POLICY_ACCEPT_TEMPORARILY) gtk_widget_show_all(item);
286 g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
287 g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_ACCEPT_TEMPORARILY));
288 g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
289 g_free(itemLabel);
290 g_free(itemImageFile);
291
292 /* Add seperator to seperate actions for this domain from the other domains */
293 item=gtk_separator_menu_item_new();
294 gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
295 gtk_widget_show_all(item);
296
297 /* Determine state of status icon */
298 if(priv->menuIconState!=NOJS_MENU_ICON_STATE_MIXED)
299 {
300 switch(inPolicy)
301 {
302 case NOJS_POLICY_ACCEPT:
303 case NOJS_POLICY_ACCEPT_TEMPORARILY:
304 newMenuIconState=NOJS_MENU_ICON_STATE_ALLOWED;
305 break;
306
307 case NOJS_POLICY_BLOCK:
308 newMenuIconState=NOJS_MENU_ICON_STATE_DENIED;
309 break;
310
311 default:
312 newMenuIconState=NOJS_MENU_ICON_STATE_MIXED;
313 break;
314 }
315
316 if(priv->menuIconState==NOJS_MENU_ICON_STATE_UNDETERMINED ||
317 priv->menuIconState!=newMenuIconState)
318 {
319 priv->menuIconState=newMenuIconState;
320 g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
321 }
322 }
323}
324
325/* Status of loading a site has changed */
326static void _nojs_view_on_load_status_changed(NoJSView *self, GParamSpec *inSpec, gpointer inUserData)
327{
328 g_return_if_fail(NOJS_IS_VIEW(self));
329 g_return_if_fail(WEBKIT_IS_WEB_VIEW(inUserData));
330
331 NoJSViewPrivate *priv=self->priv;
332 WebKitWebView *webkitView=WEBKIT_WEB_VIEW(inUserData);
333 WebKitWebSettings *settings=webkit_web_view_get_settings(webkitView);
334 WebKitLoadStatus status;
335 SoupURI *uri;
336
337 /* Get URI of document loading/loaded */
338 uri=soup_uri_new(webkit_web_view_get_uri(webkitView));
339
340 /* Check load status */
341 status=webkit_web_view_get_load_status(webkitView);
342
343 /* Check if a view was emptied, e.g. for a new document going to be loaded soon */
344 if(status==WEBKIT_LOAD_PROVISIONAL)
345 {
346 /* Create a new empty menu */
347 _nojs_view_destroy_menu(self);
348 _nojs_view_create_empty_menu(self);
349
350 /* Free list of resource URIs, that's the list of URIs for all resources
351 * of a page
352 */
353 if(priv->resourceURIs)
354 {
355 g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
356 priv->resourceURIs=NULL;
357 }
358 }
359
360 /* Check if document loading is going to start. Do not check special pages. */
361 if(status==WEBKIT_LOAD_COMMITTED &&
362 uri &&
363 uri->scheme &&
364 g_strcmp0(uri->scheme, "about")!=0)
365 {
366 /* Check if domain is black-listed or white-listed and enable or
367 * disable javascript accordingly. But if settings match already
368 * the state it should get do not set it again to avoid reloads of page.
369 */
370 gchar *domain;
371 NoJSPolicy policy;
372 gboolean currentScriptsEnabled;
373 gboolean newScriptsEnabled;
374
375 domain=nojs_get_domain(priv->manager, uri);
376 policy=nojs_get_policy(priv->manager, domain);
377 if(policy==NOJS_POLICY_UNDETERMINED)
378 {
379 policy=nojs_get_policy_for_unknown_domain(priv->manager);
380 // TODO: Show nick_name of policy (enum) to use in warning
381 g_warning("Got invalid policy. Using default policy for unknown domains.");
382 }
383
384 newScriptsEnabled=(policy==NOJS_POLICY_BLOCK ? FALSE : TRUE);
385 g_object_get(G_OBJECT(settings), "enable-scripts", &currentScriptsEnabled, NULL);
386
387 if(newScriptsEnabled!=currentScriptsEnabled)
388 {
389 g_object_set(G_OBJECT(settings), "enable-scripts", newScriptsEnabled, NULL);
390 // TODO: Set uri also to ensure this uri is going to be reloaded
391 }
392
393 _nojs_view_add_site_to_menu(self, domain, policy);
394 if(domain) g_free(domain);
395 }
396
397 /* Free allocated resources */
398 if(uri) soup_uri_free(uri);
399}
400
401/* A request is going to sent */
402static void _nojs_view_on_resource_request_starting(NoJSView *self,
403 WebKitWebFrame *inFrame,
404 WebKitWebResource *inResource,
405 WebKitNetworkRequest *inRequest,
406 WebKitNetworkResponse *inResponse,
407 gpointer inUserData)
408{
409 g_return_if_fail(NOJS_IS_VIEW(self));
410
411 NoJSViewPrivate *priv=self->priv;
412 SoupMessage *message;
413 SoupURI *uri;
414 gchar *uriText;
415
416 /* Remember resource URIs requesting */
417 message=(inRequest ? webkit_network_request_get_message(inRequest) : NULL);
418 if(message)
419 {
420 uri=soup_message_get_uri(message);
421 if(uri)
422 {
423 uriText=soup_uri_to_string(uri, FALSE);
424 priv->resourceURIs=g_slist_prepend(priv->resourceURIs, uriText);
425 }
426 }
427
428 message=(inResponse ? webkit_network_response_get_message(inResponse) : NULL);
429 if(message)
430 {
431 uri=soup_message_get_uri(message);
432 if(uri)
433 {
434 uriText=soup_uri_to_string(uri, FALSE);
435 priv->resourceURIs=g_slist_prepend(priv->resourceURIs, uriText);
436 }
437 }
438}
439
440/* A policy has changed */
441static void _nojs_view_on_policy_changed(NoJSView *self, gchar *inDomain, gpointer inUserData)
442{
443 g_return_if_fail(NOJS_IS_VIEW(self));
444 g_return_if_fail(inDomain);
445
446 NoJSViewPrivate *priv=self->priv;
447 GList *items, *iter;
448 gboolean reloaded;
449
450 /* Check if the policy of a domain has changed this view has referenced resources to */
451 reloaded=FALSE;
452 items=gtk_container_get_children(GTK_CONTAINER(priv->menu));
453 for(iter=items; reloaded==FALSE && iter; iter=iter->next)
454 {
455 if(GTK_IS_MENU_ITEM(iter->data))
456 {
457 const gchar *itemDomain;
458
459 /* Check if domain matches menu item */
460 itemDomain=(const gchar*)g_object_get_data(G_OBJECT(iter->data), "domain");
461 if(g_strcmp0(itemDomain, inDomain)==0)
462 {
463 /* Found domain in our menu so reload page */
464 midori_view_reload(priv->view, FALSE);
465 reloaded=TRUE;
466 }
467 }
468 }
469 g_list_free(items);
470}
471
472/* A javascript URI is going to loaded or blocked */
473static void _nojs_view_on_uri_load_policy_status(NoJSView *self, gchar *inURI, NoJSPolicy inPolicy, gpointer inUserData)
474{
475 g_return_if_fail(NOJS_IS_VIEW(self));
476
477 NoJSViewPrivate *priv=self->priv;
478 GSList *iter;
479 gchar *checkURI;
480
481 /* Check if uri (accepted or blocked) might be one of ours */
482 for(iter=priv->resourceURIs; iter; iter=iter->next)
483 {
484 checkURI=(gchar*)iter->data;
485 if(g_strcmp0(checkURI, inURI)==0)
486 {
487 SoupURI *uri;
488 gchar *domain;
489
490 uri=soup_uri_new(inURI);
491 domain=nojs_get_domain(priv->manager, uri);
492 if(domain)
493 {
494 _nojs_view_add_site_to_menu(self, domain, inPolicy);
495 g_free(domain);
496 }
497
498 soup_uri_free(uri);
499 break;
500 }
501 }
502}
503
504/* Property "view" has changed */
505static void _nojs_view_on_view_changed(NoJSView *self, MidoriView *inView)
506{
507 NoJSViewPrivate *priv=self->priv;
508 WebKitWebView *webkitView;
509
510 /* Disconnect signal on old view */
511 if(priv->view)
512 {
513 webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(priv->view));
514 g_signal_handlers_disconnect_by_data(webkitView, self);
515 g_object_set_data(G_OBJECT(priv->view), "nojs-view-instance", NULL);
516 g_object_unref(priv->view);
517 priv->view=NULL;
518 }
519
520 /* Set new view if valid pointer */
521 if(!inView) return;
522
523 priv->view=g_object_ref(inView);
524 g_object_set_data(G_OBJECT(priv->view), "nojs-view-instance", self);
525
526 /* Listen to changes of load-status in view */
527 webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(priv->view));
528 g_signal_connect_swapped(webkitView, "notify::load-status", G_CALLBACK(_nojs_view_on_load_status_changed), self);
529 g_signal_connect_swapped(webkitView, "resource-request-starting", G_CALLBACK(_nojs_view_on_resource_request_starting), self);
530
531 /* Create empty menu */
532 _nojs_view_destroy_menu(self);
533 _nojs_view_create_empty_menu(self);
534
535 /* Release list of resource URIs */
536 if(priv->resourceURIs)
537 {
538 g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
539 priv->resourceURIs=NULL;
540 }
541}
542
543/* This extension is going to be deactivated */
544static void _nojs_view_on_extension_deactivated(NoJSView *self, MidoriExtension *inExtension)
545{
546 g_return_if_fail(NOJS_IS_VIEW(self));
547
548 /* Dispose allocated resources by unreferencing ourselve */
549 g_object_unref(self);
550}
551
552/* Property "manager" has changed */
553static void _nojs_view_on_manager_changed(NoJSView *self, NoJS *inNoJS)
554{
555 g_return_if_fail(NOJS_IS_VIEW(self));
556 g_return_if_fail(!inNoJS || IS_NOJS(inNoJS));
557
558 NoJSViewPrivate *priv=self->priv;
559 MidoriExtension *extension;
560
561 /* Release reference to old manager and clean up */
562 if(priv->manager)
563 {
564 g_object_get(priv->manager, "extension", &extension, NULL);
565 g_signal_handlers_disconnect_by_data(extension, self);
566 g_object_unref(extension);
567
568 g_signal_handlers_disconnect_by_data(priv->manager, self);
569 g_object_unref(priv->manager);
570 priv->manager=NULL;
571 }
572
573 /* Set new view if valid pointer */
574 if(!inNoJS) return;
575
576 priv->manager=g_object_ref(inNoJS);
577
578 /* Connect signals to manager */
579 g_signal_connect_swapped(priv->manager, "uri-load-policy-status", G_CALLBACK(_nojs_view_on_uri_load_policy_status), self);
580 g_signal_connect_swapped(priv->manager, "policy-changed", G_CALLBACK(_nojs_view_on_policy_changed), self);
581
582 /* Connect signal to get noticed when extension is going to be deactivated
583 * to release all references to GObjects
584 */
585 g_object_get(priv->manager, "extension", &extension, NULL);
586 g_signal_connect_swapped(extension, "deactivate", G_CALLBACK(_nojs_view_on_extension_deactivated), self);
587 g_object_unref(extension);
588}
589
590/* IMPLEMENTATION: GObject */
591
592/* Finalize this object */
593static void nojs_view_finalize(GObject *inObject)
594{
595 NoJSView *self=NOJS_VIEW(inObject);
596 NoJSViewPrivate *priv=self->priv;
597
598 /* Dispose allocated resources */
599 if(priv->manager)
600 {
601 MidoriExtension *extension;
602
603 g_object_get(priv->manager, "extension", &extension, NULL);
604 g_signal_handlers_disconnect_by_data(extension, self);
605 g_object_unref(extension);
606
607 g_signal_handlers_disconnect_by_data(priv->manager, self);
608 g_object_unref(priv->manager);
609 priv->manager=NULL;
610 }
611
612 if(priv->browser)
613 {
614 g_object_unref(priv->browser);
615 priv->browser=NULL;
616 }
617
618 if(priv->view)
619 {
620 _nojs_view_on_view_changed(self, NULL);
621 }
622
623 if(priv->menu)
624 {
625 gtk_widget_destroy(priv->menu);
626 priv->menu=NULL;
627 }
628
629 if(priv->resourceURIs)
630 {
631 g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
632 priv->resourceURIs=NULL;
633 }
634
635 /* Call parent's class finalize method */
636 G_OBJECT_CLASS(nojs_view_parent_class)->finalize(inObject);
637}
638
639/* Set/get properties */
640static void nojs_view_set_property(GObject *inObject,
641 guint inPropID,
642 const GValue *inValue,
643 GParamSpec *inSpec)
644{
645 NoJSView *self=NOJS_VIEW(inObject);
646
647 switch(inPropID)
648 {
649 /* Construct-only properties */
650 case PROP_MANAGER:
651 _nojs_view_on_manager_changed(self, NOJS(g_value_get_object(inValue)));
652 break;
653
654 case PROP_BROWSER:
655 if(self->priv->browser) g_object_unref(self->priv->browser);
656 self->priv->browser=g_object_ref(g_value_get_object(inValue));
657 break;
658
659 case PROP_VIEW:
660 _nojs_view_on_view_changed(self, MIDORI_VIEW(g_value_get_object(inValue)));
661 break;
662
663 default:
664 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
665 break;
666 }
667}
668
669static void nojs_view_get_property(GObject *inObject,
670 guint inPropID,
671 GValue *outValue,
672 GParamSpec *inSpec)
673{
674 NoJSView *self=NOJS_VIEW(inObject);
675
676 switch(inPropID)
677 {
678 case PROP_MANAGER:
679 g_value_set_object(outValue, self->priv->manager);
680 break;
681
682 case PROP_BROWSER:
683 g_value_set_object(outValue, self->priv->browser);
684 break;
685
686 case PROP_VIEW:
687 g_value_set_object(outValue, self->priv->view);
688 break;
689
690 case PROP_MENU_ICON_STATE:
691 g_value_set_enum(outValue, self->priv->menuIconState);
692 break;
693
694 default:
695 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
696 break;
697 }
698}
699
700/* Class initialization
701 * Override functions in parent classes and define properties and signals
702 */
703static void nojs_view_class_init(NoJSViewClass *klass)
704{
705 GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
706
707 /* Override functions */
708 gobjectClass->finalize=nojs_view_finalize;
709 gobjectClass->set_property=nojs_view_set_property;
710 gobjectClass->get_property=nojs_view_get_property;
711
712 /* Set up private structure */
713 g_type_class_add_private(klass, sizeof(NoJSViewPrivate));
714
715 /* Define properties */
716 NoJSViewProperties[PROP_MANAGER]=
717 g_param_spec_object("manager",
718 _("Manager instance"),
719 _("Instance to global NoJS manager"),
720 TYPE_NOJS,
721 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
722
723 NoJSViewProperties[PROP_BROWSER]=
724 g_param_spec_object("browser",
725 _("Browser window"),
726 _("The Midori browser instance this view belongs to"),
727 MIDORI_TYPE_BROWSER,
728 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
729
730 NoJSViewProperties[PROP_VIEW]=
731 g_param_spec_object("view",
732 _("View"),
733 _("The Midori view instance this view belongs to"),
734 MIDORI_TYPE_VIEW,
735 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
736
737 NoJSViewProperties[PROP_MENU_ICON_STATE]=
738 g_param_spec_enum("menu-icon-state",
739 _("Menu icon state"),
740 _("State of menu icon to show in status bar"),
741 NOJS_TYPE_MENU_ICON_STATE,
742 NOJS_MENU_ICON_STATE_UNDETERMINED,
743 G_PARAM_READABLE);
744
745 g_object_class_install_properties(gobjectClass, PROP_LAST, NoJSViewProperties);
746}
747
748/* Object initialization
749 * Create private structure and set up default values
750 */
751static void nojs_view_init(NoJSView *self)
752{
753 NoJSViewPrivate *priv;
754
755 priv=self->priv=NOJS_VIEW_GET_PRIVATE(self);
756
757 /* Set up default values */
758 priv->manager=NULL;
759 priv->browser=NULL;
760 priv->view=NULL;
761
762 priv->menu=NULL;
763 priv->menuPolicyWasChanged=FALSE;
764 priv->menuIconState=NOJS_POLICY_UNDETERMINED;
765
766 priv->resourceURIs=NULL;
767
768 /* Create empty menu */
769 _nojs_view_create_empty_menu(self);
770}
771
772/* Implementation: Public API */
773
774/* Create new object */
775NoJSView* nojs_view_new(NoJS *inNoJS, MidoriBrowser *inBrowser, MidoriView *inView)
776{
777 return(g_object_new(TYPE_NOJS_VIEW,
778 "manager", inNoJS,
779 "browser", inBrowser,
780 "view", inView,
781 NULL));
782}
783
784/* Get menu widget for this view */
785GtkMenu* nojs_view_get_menu(NoJSView *self)
786{
787 g_return_val_if_fail(NOJS_IS_VIEW(self), NULL);
788
789 return(GTK_MENU(self->priv->menu));
790}
791
792/* Get image used for menu icon in status bar */
793NoJSMenuIconState nojs_view_get_menu_icon_state(NoJSView *self)
794{
795 g_return_val_if_fail(NOJS_IS_VIEW(self), NOJS_MENU_ICON_STATE_UNDETERMINED);
796
797 return(self->priv->menuIconState);
798}
799
800/************************************************************************************/
801
802/* Implementation: Enumeration */
803GType nojs_menu_icon_state_get_type(void)
804{
805 static volatile gsize g_define_type_id__volatile=0;
806
807 if(g_once_init_enter(&g_define_type_id__volatile))
808 {
809 static const GEnumValue values[]=
810 {
811 { NOJS_MENU_ICON_STATE_UNDETERMINED, "NOJS_MENU_ICON_STATE_UNDETERMINED", N_("Undetermined") },
812 { NOJS_MENU_ICON_STATE_ALLOWED, "NOJS_MENU_ICON_STATE_ALLOWED", N_("Allowed") },
813 { NOJS_MENU_ICON_STATE_MIXED, "NOJS_MENU_ICON_STATE_MIXED", N_("Mixed") },
814 { NOJS_MENU_ICON_STATE_DENIED, "NOJS_MENU_ICON_STATE_DENIED", N_("Denied") },
815 { 0, NULL, NULL }
816 };
817
818 GType g_define_type_id=g_enum_register_static(g_intern_static_string("NoJSMenuIconState"), values);
819 g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
820 }
821
822 return(g_define_type_id__volatile);
823}
0824
=== added file 'extensions/nojs/nojs-view.h'
--- extensions/nojs/nojs-view.h 1970-01-01 00:00:00 +0000
+++ extensions/nojs/nojs-view.h 2013-05-22 23:07:26 +0000
@@ -0,0 +1,71 @@
1/*
2 Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 See the file COPYING for the full license text.
10*/
11
12#ifndef __NOJS_VIEW__
13#define __NOJS_VIEW__
14
15#include "config.h"
16#include "nojs.h"
17#include <midori/midori.h>
18
19G_BEGIN_DECLS
20
21/* NoJS view enums */
22typedef enum
23{
24 NOJS_MENU_ICON_STATE_UNDETERMINED,
25 NOJS_MENU_ICON_STATE_ALLOWED,
26 NOJS_MENU_ICON_STATE_MIXED,
27 NOJS_MENU_ICON_STATE_DENIED
28} NoJSMenuIconState;
29
30/* NoJS view object */
31#define TYPE_NOJS_VIEW (nojs_view_get_type())
32#define NOJS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NOJS_VIEW, NoJSView))
33#define NOJS_IS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_NOJS_VIEW))
34#define NOJS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_NOJS_VIEW, NoJSViewClass))
35#define NOJS_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_NOJS_VIEW))
36#define NOJS_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_NOJS_VIEW, NoJSViewClass))
37
38typedef struct _NoJSView NoJSView;
39typedef struct _NoJSViewClass NoJSViewClass;
40typedef struct _NoJSViewPrivate NoJSViewPrivate;
41
42struct _NoJSView
43{
44 /* Parent instance */
45 GObject parent_instance;
46
47 /* Private structure */
48 NoJSViewPrivate *priv;
49};
50
51struct _NoJSViewClass
52{
53 /* Parent class */
54 GObjectClass parent_class;
55};
56
57/* Public API */
58GType nojs_view_get_type(void);
59
60NoJSView* nojs_view_new(NoJS *inNoJS, MidoriBrowser *inBrowser, MidoriView *inView);
61
62GtkMenu* nojs_view_get_menu(NoJSView *self);
63NoJSMenuIconState nojs_view_get_menu_icon_state(NoJSView *self);
64
65/* Enumeration */
66GType nojs_menu_icon_state_get_type(void) G_GNUC_CONST;
67#define NOJS_TYPE_MENU_ICON_STATE (nojs_menu_icon_state_get_type())
68
69G_END_DECLS
70
71#endif /* __NOJS_VIEW__ */
072
=== added file 'extensions/nojs/nojs.c'
--- extensions/nojs/nojs.c 1970-01-01 00:00:00 +0000
+++ extensions/nojs/nojs.c 2013-05-22 23:07:26 +0000
@@ -0,0 +1,1078 @@
1/*
2 Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 See the file COPYING for the full license text.
10*/
11
12#include "nojs.h"
13#include "nojs-view.h"
14
15#include <errno.h>
16
17/* Define this class in GObject system */
18G_DEFINE_TYPE(NoJS,
19 nojs,
20 G_TYPE_OBJECT)
21
22/* Properties */
23enum
24{
25 PROP_0,
26
27 PROP_EXTENSION,
28 PROP_APPLICATION,
29
30 PROP_DATABASE,
31 PROP_DATABASE_FILENAME,
32 PROP_ALLOW_ALL_SITES,
33 PROP_ONLY_SECOND_LEVEL,
34 PROP_UNKNOWN_DOMAIN_POLICY,
35
36 PROP_LAST
37};
38
39static GParamSpec* NoJSProperties[PROP_LAST]={ 0, };
40
41/* Signals */
42enum
43{
44 URI_LOAD_POLICY_STATUS,
45 POLICY_CHANGED,
46
47 SIGNAL_LAST
48};
49
50static guint NoJSSignals[SIGNAL_LAST]={ 0, };
51
52/* Private structure - access only by public API if needed */
53#define NOJS_GET_PRIVATE(obj) \
54 (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_NOJS, NoJSPrivate))
55
56struct _NoJSPrivate
57{
58 /* Extension related */
59 MidoriExtension *extension;
60 MidoriApp *application;
61 sqlite3 *database;
62 gchar *databaseFilename;
63 gboolean allowAllSites;
64 gboolean checkOnlySecondLevel;
65 NoJSPolicy unknownDomainPolicy;
66
67 guint requestStartedSignalID;
68};
69
70/* Taken from http://www.w3.org/html/wg/drafts/html/master/scripting-1.html#scriptingLanguages
71 * A list of javascript mime types
72 */
73static const gchar* javascriptTypes[]= {
74 "application/ecmascript",
75 "application/javascript",
76 "application/x-ecmascript",
77 "application/x-javascript",
78 "text/ecmascript",
79 "text/javascript",
80 "text/javascript1.0",
81 "text/javascript1.1",
82 "text/javascript1.2",
83 "text/javascript1.3",
84 "text/javascript1.4",
85 "text/javascript1.5",
86 "text/jscript",
87 "text/livescript",
88 "text/x-ecmascript",
89 "text/x-javascript",
90 NULL
91 };
92
93/* IMPLEMENTATION: Private variables and methods */
94
95/* Closure for: void (*closure)(NoJS *self, gchar *inURI, NoJSPolicy inPolicy) */
96static void _nojs_closure_VOID__STRING_ENUM(GClosure *inClosure,
97 GValue *ioReturnValue G_GNUC_UNUSED,
98 guint inNumberValues,
99 const GValue *inValues,
100 gpointer inInvocationHint G_GNUC_UNUSED,
101 gpointer inMarshalData)
102{
103 typedef void (*GMarshalFunc_VOID__STRING_ENUM)(gpointer inObject, gpointer inArg1, gint inArg2, gpointer inUserData);
104
105 register GMarshalFunc_VOID__STRING_ENUM callback;
106 register GCClosure *closure=(GCClosure*)inClosure;
107 register gpointer object, userData;
108
109 g_return_if_fail(inNumberValues==3);
110
111 if(G_CCLOSURE_SWAP_DATA(inClosure))
112 {
113 object=inClosure->data;
114 userData=g_value_peek_pointer(inValues+0);
115 }
116 else
117 {
118 object=g_value_peek_pointer(inValues+0);
119 userData=inClosure->data;
120 }
121
122 callback=(GMarshalFunc_VOID__STRING_ENUM)(inMarshalData ? inMarshalData : closure->callback);
123
124 callback(object,
125 (gchar*)g_value_get_string(inValues+1),
126 g_value_get_enum(inValues+2),
127 userData);
128}
129
130/* Show common error dialog */
131static void _nojs_error(NoJS *self, const gchar *inReason)
132{
133 g_return_if_fail(IS_NOJS(self));
134 g_return_if_fail(inReason);
135
136 GtkWidget *dialog;
137
138 /* Show confirmation dialog for undetermined cookies */
139 dialog=gtk_message_dialog_new(NULL,
140 GTK_DIALOG_MODAL,
141 GTK_MESSAGE_ERROR,
142 GTK_BUTTONS_OK,
143 _("A fatal error occurred which prevents "
144 "the NoJS extension to continue. "
145 "You should disable it."));
146
147 gtk_window_set_title(GTK_WINDOW(dialog), _("Error in NoJS extension"));
148 gtk_window_set_icon_name(GTK_WINDOW (dialog), "midori");
149
150 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
151 "%s:\n%s",
152 _("Reason"),
153 inReason);
154
155 gtk_dialog_run(GTK_DIALOG(dialog));
156
157 /* Free up allocated resources */
158 gtk_widget_destroy(dialog);
159}
160
161/* Open database containing policies for javascript sites.
162 * Create database and setup table structure if it does not exist yet.
163 */
164static void _nojs_open_database(NoJS *self)
165{
166 g_return_if_fail(IS_NOJS(self));
167
168 NoJSPrivate *priv=self->priv;
169 const gchar *configDir;
170 gchar *sql;
171 gchar *error=NULL;
172 gint success;
173
174 /* Close any open database */
175 if(priv->database)
176 {
177 priv->databaseFilename=NULL;
178
179 sqlite3_close(priv->database);
180 priv->database=NULL;
181
182 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_DATABASE]);
183 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_DATABASE_FILENAME]);
184 }
185
186 /* Build path to database file */
187 configDir=midori_extension_get_config_dir(priv->extension);
188 if(!configDir)
189 {
190 g_warning(_("Could not get path to configuration of extension: path is NULL"));
191
192 _nojs_error(self, _("Could not get path to configuration of extension."));
193 return;
194 }
195
196 if(katze_mkdir_with_parents(configDir, 0700))
197 {
198 g_warning(_("Could not create configuration folder for extension: %s"), g_strerror(errno));
199
200 _nojs_error(self, _("Could not create configuration folder for extension."));
201 return;
202 }
203
204 /* Open database */
205 priv->databaseFilename=g_build_filename(configDir, NOJS_DATABASE, NULL);
206 success=sqlite3_open(priv->databaseFilename, &priv->database);
207 if(success!=SQLITE_OK)
208 {
209 g_warning(_("Could not open database of extenstion: %s"), sqlite3_errmsg(priv->database));
210
211 g_free(priv->databaseFilename);
212 priv->databaseFilename=NULL;
213
214 if(priv->database) sqlite3_close(priv->database);
215 priv->database=NULL;
216
217 _nojs_error(self, _("Could not open database of extension."));
218 return;
219 }
220
221 /* Create table structure if it does not exist */
222 success=sqlite3_exec(priv->database,
223 "CREATE TABLE IF NOT EXISTS "
224 "policies(site text, value integer);",
225 NULL,
226 NULL,
227 &error);
228
229 if(success==SQLITE_OK)
230 {
231 success=sqlite3_exec(priv->database,
232 "CREATE UNIQUE INDEX IF NOT EXISTS "
233 "site ON policies (site);",
234 NULL,
235 NULL,
236 &error);
237 }
238
239 if(success==SQLITE_OK)
240 {
241 success=sqlite3_exec(priv->database,
242 "PRAGMA journal_mode=TRUNCATE;",
243 NULL,
244 NULL,
245 &error);
246 }
247
248 if(success!=SQLITE_OK || error)
249 {
250 _nojs_error(self, _("Could not set up database structure of extension."));
251
252 if(error)
253 {
254 g_critical(_("Failed to execute database statement: %s"), error);
255 sqlite3_free(error);
256 }
257
258 g_free(priv->databaseFilename);
259 priv->databaseFilename=NULL;
260
261 sqlite3_close(priv->database);
262 priv->database=NULL;
263 return;
264 }
265
266 /* Delete all temporarily allowed sites */
267 sql=sqlite3_mprintf("DELETE FROM policies WHERE value=%d;", NOJS_POLICY_ACCEPT_TEMPORARILY);
268 success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
269 if(success!=SQLITE_OK) g_warning(_("SQL fails: %s"), error);
270 if(error) sqlite3_free(error);
271 sqlite3_free(sql);
272
273 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_DATABASE]);
274 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_DATABASE_FILENAME]);
275}
276
277/* A request through libsoup is going to start and http headers must be
278 * checked for content type
279 */
280static void _nojs_on_got_headers(NoJS *self, gpointer inUserData)
281{
282 g_return_if_fail(IS_NOJS(self));
283 g_return_if_fail(SOUP_IS_MESSAGE(inUserData));
284
285 NoJSPrivate *priv=self->priv;
286 SoupMessage *message=SOUP_MESSAGE(inUserData);
287 SoupSession *session=webkit_get_default_session();
288 SoupMessageHeaders *headers;
289 SoupMessageBody *body;
290 const gchar *contentType;
291 SoupURI *uri;
292 gchar *uriText;
293 gchar *domain;
294 NoJSPolicy policy;
295 gboolean isJS;
296 const gchar **iter;
297
298 /* Get headers from message to retrieve content type */
299 g_object_get(message, "response-headers", &headers, NULL);
300 if(!headers)
301 {
302 g_warning("Could not get headers from message to check for javascript.");
303 return;
304 }
305
306 /* Get content type of uri and check if it is a javascript resource */
307 contentType=soup_message_headers_get_content_type(headers, NULL);
308
309 isJS=FALSE;
310 iter=javascriptTypes;
311 while(*iter && !isJS)
312 {
313 isJS=(g_strcmp0(contentType, *iter)==0);
314 iter++;
315 }
316
317 if(!isJS) return;
318
319 /* The document being loaded is javascript so get URI from message,
320 * get policy for domain of URI and emit signal
321 */
322 uri=soup_message_get_uri(message);
323
324 domain=nojs_get_domain(self, uri);
325 g_return_if_fail(domain);
326
327 policy=nojs_get_policy(self, domain);
328 if(policy==NOJS_POLICY_UNDETERMINED)
329 {
330 g_warning("Got invalid policy. Using default policy for unknown domains.");
331 policy=priv->unknownDomainPolicy;
332 }
333
334 uriText=soup_uri_to_string(uri, FALSE);
335
336 g_signal_emit(self, NoJSSignals[URI_LOAD_POLICY_STATUS], 0, uriText, policy==NOJS_POLICY_UNDETERMINED ? NOJS_POLICY_BLOCK : policy);
337
338 g_free(uriText);
339 g_free(domain);
340
341 /* Return here if policy is any type of accept */
342 if(policy!=NOJS_POLICY_UNDETERMINED && policy!=NOJS_POLICY_BLOCK) return;
343
344 /* Cancel this message */
345 soup_session_cancel_message(session, message, SOUP_STATUS_CANCELLED);
346
347 /* Discard any load data */
348 g_object_get(message, "response-body", &body, NULL);
349 if(body) soup_message_body_truncate(body);
350}
351
352static void _nojs_on_request_started(NoJS *self,
353 SoupMessage *inMessage,
354 SoupSocket *inSocket,
355 gpointer inUserData)
356{
357 g_return_if_fail(IS_NOJS(self));
358 g_return_if_fail(SOUP_IS_MESSAGE(inMessage));
359
360 /* Connect to "got-headers" to cancel loading javascript documents early */
361 g_signal_connect_swapped(inMessage, "got-headers", G_CALLBACK(_nojs_on_got_headers), self);
362}
363
364/* The icon in statusbar was clicked */
365static void _nojs_on_statusbar_icon_clicked(MidoriBrowser *inBrowser, gpointer inUserData)
366{
367 g_return_if_fail(MIDORI_IS_BROWSER(inBrowser));
368
369 MidoriView *activeView;
370 NoJSView *view;
371 GtkMenu *menu;
372
373 /* Get current active midori view */
374 activeView=MIDORI_VIEW(midori_browser_get_current_tab(inBrowser));
375 g_return_if_fail(MIDORI_IS_VIEW(activeView));
376
377 /* Get NoJS view of current active midori view */
378 view=NOJS_VIEW(g_object_get_data(G_OBJECT(activeView), "nojs-view-instance"));
379 g_return_if_fail(NOJS_IS_VIEW(view));
380
381 /* Get menu of current view */
382 menu=nojs_view_get_menu(view);
383 g_return_if_fail(menu);
384
385 /* Show menu */
386 gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
387}
388
389/* Menu icon of a view has changed */
390static void _nojs_on_menu_icon_changed(MidoriBrowser *inBrowser, GParamSpec *inSpec, gpointer inUserData)
391{
392 g_return_if_fail(MIDORI_IS_BROWSER(inBrowser));
393 g_return_if_fail(NOJS_IS_VIEW(inUserData));
394
395 NoJSView *view=NOJS_VIEW(inUserData);
396 NoJSMenuIconState menuIconState;
397 GtkWidget *statusbarIcon;
398 GtkWidget *buttonImage;
399 gchar *imageFilename;
400
401 /* Get icon in status bar of this browser */
402 statusbarIcon=GTK_WIDGET(g_object_get_data(G_OBJECT(inBrowser), "nojs-statusicon"));
403 g_return_if_fail(GTK_IS_WIDGET(statusbarIcon));
404
405 /* Get menu icon state of view */
406 menuIconState=nojs_view_get_menu_icon_state(view);
407
408 /* Create image for statusbar button */
409 imageFilename=NULL;
410 switch(menuIconState)
411 {
412#ifdef ALTERNATE_DATADIR
413 case NOJS_MENU_ICON_STATE_ALLOWED:
414 imageFilename=g_build_filename(ALTERNATE_DATADIR, "nojs-statusicon-allowed.png", NULL);
415 break;
416
417 case NOJS_MENU_ICON_STATE_MIXED:
418 imageFilename=g_build_filename(ALTERNATE_DATADIR, "nojs-statusicon-mixed.png", NULL);
419 break;
420
421 case NOJS_MENU_ICON_STATE_DENIED:
422 case NOJS_MENU_ICON_STATE_UNDETERMINED:
423 imageFilename=g_build_filename(ALTERNATE_DATADIR, "nojs-statusicon-denied.png", NULL);
424 break;
425#else
426 case NOJS_MENU_ICON_STATE_ALLOWED:
427 imageFilename=g_build_filename(MDATADIR, PACKAGE_NAME, "nojs", "nojs-statusicon-allowed.png", NULL);
428 break;
429
430 case NOJS_MENU_ICON_STATE_MIXED:
431 imageFilename=g_build_filename(MDATADIR, PACKAGE_NAME, "nojs", "nojs-statusicon-mixed.png", NULL);
432 break;
433
434 case NOJS_MENU_ICON_STATE_DENIED:
435 case NOJS_MENU_ICON_STATE_UNDETERMINED:
436 imageFilename=g_build_filename(MDATADIR, PACKAGE_NAME, "nojs", "nojs-statusicon-denied.png", NULL);
437 break;
438#endif
439 }
440
441 buttonImage=gtk_image_new_from_file(imageFilename);
442 g_free(imageFilename);
443
444 /* Set image at statusbar button */
445 gtk_button_set_image(GTK_BUTTON(statusbarIcon), buttonImage);
446}
447
448/* A tab in browser was activated */
449static void _nojs_on_switch_tab(NoJS *self, MidoriView *inOldView, MidoriView *inNewView, gpointer inUserData)
450{
451 g_return_if_fail(IS_NOJS(self));
452 g_return_if_fail(MIDORI_IS_BROWSER(inUserData));
453
454 MidoriBrowser *browser=MIDORI_BROWSER(inUserData);
455 NoJSView *view;
456
457 /* Disconnect signal handlers from old view */
458 if(inOldView)
459 {
460 /* Get NoJS view of old view */
461 view=(NoJSView*)g_object_get_data(G_OBJECT(inOldView), "nojs-view-instance");
462 g_return_if_fail(NOJS_IS_VIEW(view));
463
464 /* Disconnect signal handlers */
465 g_signal_handlers_disconnect_by_func(view, G_CALLBACK(_nojs_on_menu_icon_changed), browser);
466 }
467
468 /* Get NoJS view of new view */
469 view=(NoJSView*)g_object_get_data(G_OBJECT(inNewView), "nojs-view-instance");
470 g_return_if_fail(NOJS_IS_VIEW(view));
471
472 /* Connect signals */
473 g_signal_connect_swapped(view, "notify::menu-icon-state", G_CALLBACK(_nojs_on_menu_icon_changed), browser);
474
475 /* Update menu icon*/
476 _nojs_on_menu_icon_changed(browser, NULL, view);
477}
478
479/* A tab of a browser was removed */
480static void _nojs_on_remove_tab(NoJS *self, MidoriView *inView, gpointer inUserData)
481{
482 g_return_if_fail(IS_NOJS(self));
483
484 NoJSView *view;
485
486 /* Get NoJS view of current active midori view */
487 view=NOJS_VIEW(g_object_get_data(G_OBJECT(inView), "nojs-view-instance"));
488 g_return_if_fail(NOJS_IS_VIEW(view));
489
490 g_object_unref(view);
491}
492
493/* A tab of a browser was added */
494static void _nojs_on_add_tab(NoJS *self, MidoriView *inView, gpointer inUserData)
495{
496 g_return_if_fail(IS_NOJS(self));
497 g_return_if_fail(MIDORI_IS_BROWSER(inUserData));
498
499 /* Create nojs view and add to tab */
500 MidoriBrowser *browser=MIDORI_BROWSER(inUserData);
501
502 nojs_view_new(self, browser, inView);
503}
504
505/* A browser window was added */
506static void _nojs_on_add_browser(NoJS *self, MidoriBrowser *inBrowser, gpointer inUserData)
507{
508 g_return_if_fail(IS_NOJS(self));
509 g_return_if_fail(MIDORI_IS_BROWSER(inBrowser));
510
511 GList *tabs, *iter;
512 GtkWidget *statusbar;
513 GtkWidget *statusbarIcon;
514 MidoriView *view;
515 NoJSView *nojsView;
516
517 /* Set up all current available tabs in browser */
518 tabs=midori_browser_get_tabs(inBrowser);
519 for(iter=tabs; iter; iter=g_list_next(iter)) _nojs_on_add_tab(self, iter->data, inBrowser);
520 g_list_free(tabs);
521
522 /* Add status bar icon to browser */
523 g_object_get(inBrowser, "statusbar", &statusbar, NULL);
524 if(statusbar)
525 {
526 /* Create and set up status icon */
527 statusbarIcon=gtk_button_new();
528 gtk_button_set_relief(GTK_BUTTON(statusbarIcon), GTK_RELIEF_NONE);
529 gtk_widget_show_all(statusbarIcon);
530 gtk_box_pack_end(GTK_BOX(statusbar), statusbarIcon, FALSE, FALSE, 0);
531 g_object_set_data_full(G_OBJECT(inBrowser), "nojs-statusicon", g_object_ref(statusbarIcon), (GDestroyNotify)gtk_widget_destroy);
532
533 /* Connect signals */
534 g_signal_connect_swapped(statusbarIcon, "clicked", G_CALLBACK(_nojs_on_statusbar_icon_clicked), inBrowser);
535
536 /* Release our reference to statusbar and status icon */
537 g_object_unref(statusbarIcon);
538 g_object_unref(statusbar);
539
540 /* Update menu icon*/
541 view=MIDORI_VIEW(midori_browser_get_current_tab(inBrowser));
542 if(view)
543 {
544 nojsView=(NoJSView*)g_object_get_data(G_OBJECT(view), "nojs-view-instance");
545 if(nojsView) _nojs_on_menu_icon_changed(inBrowser, NULL, nojsView);
546 }
547 }
548
549 /* Listen to new tabs opened in browser */
550 g_signal_connect_swapped(inBrowser, "add-tab", G_CALLBACK(_nojs_on_add_tab), self);
551 g_signal_connect_swapped(inBrowser, "switch-tab", G_CALLBACK(_nojs_on_switch_tab), self);
552 g_signal_connect_swapped(inBrowser, "remove-tab", G_CALLBACK(_nojs_on_remove_tab), self);
553}
554
555/* Application property has changed */
556static void _nojs_on_application_changed(NoJS *self)
557{
558 g_return_if_fail(IS_NOJS(self));
559
560 NoJSPrivate *priv=NOJS(self)->priv;
561 GList *browsers, *iter;
562
563 /* Set up all current open browser windows */
564 browsers=midori_app_get_browsers(priv->application);
565 for(iter=browsers; iter; iter=g_list_next(iter)) _nojs_on_add_browser(self, MIDORI_BROWSER(iter->data), priv->application);
566 g_list_free(browsers);
567
568 /* Listen to new browser windows opened */
569 g_signal_connect_swapped(priv->application, "add-browser", G_CALLBACK(_nojs_on_add_browser), self);
570
571 /* Notify about property change */
572 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_APPLICATION]);
573}
574
575/* IMPLEMENTATION: GObject */
576
577/* Finalize this object */
578static void nojs_finalize(GObject *inObject)
579{
580 NoJS *self=NOJS(inObject);
581 NoJSPrivate *priv=self->priv;
582 GList *browsers, *browser;
583 GList *tabs, *tab;
584 WebKitWebView *webkitView;
585 SoupSession *session;
586
587 /* Dispose allocated resources */
588 session=webkit_get_default_session();
589 g_signal_handlers_disconnect_by_data(session, self);
590
591 if(priv->databaseFilename)
592 {
593 g_free(priv->databaseFilename);
594 priv->databaseFilename=NULL;
595 }
596
597 if(priv->database)
598 {
599 sqlite3_close(priv->database);
600 priv->database=NULL;
601 }
602
603 if(priv->application)
604 {
605 g_signal_handlers_disconnect_by_data(priv->application, self);
606
607 browsers=midori_app_get_browsers(priv->application);
608 for(browser=browsers; browser; browser=g_list_next(browser))
609 {
610 g_signal_handlers_disconnect_by_data(browser->data, self);
611 g_object_set_data(G_OBJECT(browser->data), "nojs-statusicon", NULL);
612
613 tabs=midori_browser_get_tabs(MIDORI_BROWSER(browser->data));
614 for(tab=tabs; tab; tab=g_list_next(tab))
615 {
616 g_signal_handlers_disconnect_by_data(tab->data, self);
617
618 webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(MIDORI_VIEW(tab->data)));
619 g_signal_handlers_disconnect_by_data(webkitView, self);
620 }
621 g_list_free(tabs);
622 }
623 g_list_free(browsers);
624
625 priv->application=NULL;
626 }
627
628 /* Call parent's class finalize method */
629 G_OBJECT_CLASS(nojs_parent_class)->finalize(inObject);
630}
631
632/* Set/get properties */
633static void nojs_set_property(GObject *inObject,
634 guint inPropID,
635 const GValue *inValue,
636 GParamSpec *inSpec)
637{
638 NoJS *self=NOJS(inObject);
639
640 switch(inPropID)
641 {
642 /* Construct-only properties */
643 case PROP_EXTENSION:
644 self->priv->extension=g_value_get_object(inValue);
645 _nojs_open_database(self);
646 break;
647
648 case PROP_APPLICATION:
649 self->priv->application=g_value_get_object(inValue);
650 _nojs_on_application_changed(self);
651 break;
652
653 case PROP_ALLOW_ALL_SITES:
654 self->priv->allowAllSites=g_value_get_boolean(inValue);
655 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_ALLOW_ALL_SITES]);
656 break;
657
658 case PROP_ONLY_SECOND_LEVEL:
659 self->priv->checkOnlySecondLevel=g_value_get_boolean(inValue);
660 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_ONLY_SECOND_LEVEL]);
661 break;
662
663 case PROP_UNKNOWN_DOMAIN_POLICY:
664 self->priv->unknownDomainPolicy=g_value_get_enum(inValue);
665 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_UNKNOWN_DOMAIN_POLICY]);
666 break;
667
668 default:
669 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
670 break;
671 }
672}
673
674static void nojs_get_property(GObject *inObject,
675 guint inPropID,
676 GValue *outValue,
677 GParamSpec *inSpec)
678{
679 NoJS *self=NOJS(inObject);
680
681 switch(inPropID)
682 {
683 case PROP_EXTENSION:
684 g_value_set_object(outValue, self->priv->extension);
685 break;
686
687 case PROP_APPLICATION:
688 g_value_set_object(outValue, self->priv->application);
689 break;
690
691 case PROP_DATABASE:
692 g_value_set_pointer(outValue, self->priv->database);
693 break;
694
695 case PROP_DATABASE_FILENAME:
696 g_value_set_string(outValue, self->priv->databaseFilename);
697 break;
698
699 case PROP_ALLOW_ALL_SITES:
700 g_value_set_boolean(outValue, self->priv->allowAllSites);
701 break;
702
703 case PROP_ONLY_SECOND_LEVEL:
704 g_value_set_boolean(outValue, self->priv->checkOnlySecondLevel);
705 break;
706
707 case PROP_UNKNOWN_DOMAIN_POLICY:
708 g_value_set_enum(outValue, self->priv->unknownDomainPolicy);
709 break;
710
711 default:
712 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
713 break;
714 }
715}
716
717/* Class initialization
718 * Override functions in parent classes and define properties and signals
719 */
720static void nojs_class_init(NoJSClass *klass)
721{
722 GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
723
724 /* Override functions */
725 gobjectClass->finalize=nojs_finalize;
726 gobjectClass->set_property=nojs_set_property;
727 gobjectClass->get_property=nojs_get_property;
728
729 /* Set up private structure */
730 g_type_class_add_private(klass, sizeof(NoJSPrivate));
731
732 /* Define properties */
733 NoJSProperties[PROP_EXTENSION]=
734 g_param_spec_object("extension",
735 _("Extension instance"),
736 _("The Midori extension instance for this extension"),
737 MIDORI_TYPE_EXTENSION,
738 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
739
740 NoJSProperties[PROP_APPLICATION]=
741 g_param_spec_object("application",
742 _("Application instance"),
743 _("The Midori application instance this extension belongs to"),
744 MIDORI_TYPE_APP,
745 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
746
747 NoJSProperties[PROP_DATABASE]=
748 g_param_spec_pointer("database",
749 _("Database instance"),
750 _("Pointer to sqlite database instance used by this extension"),
751 G_PARAM_READABLE);
752
753 NoJSProperties[PROP_DATABASE_FILENAME]=
754 g_param_spec_string("database-filename",
755 _("Database path"),
756 _("Path to sqlite database instance used by this extension"),
757 NULL,
758 G_PARAM_READABLE);
759
760 NoJSProperties[PROP_ALLOW_ALL_SITES]=
761 g_param_spec_boolean("allow-all-sites",
762 _("Allow all sites"),
763 _("If true this extension will not check policy for each site but allow them."),
764 FALSE,
765 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
766
767 NoJSProperties[PROP_ONLY_SECOND_LEVEL]=
768 g_param_spec_boolean("only-second-level",
769 _("Only second level"),
770 _("If true this extension will reduce each domain to its second-level (www.example.org will reduced to example.org)"),
771 TRUE,
772 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
773
774 NoJSProperties[PROP_UNKNOWN_DOMAIN_POLICY]=
775 g_param_spec_enum("unknown-domain-policy",
776 _("Unknown domain policy"),
777 _("Policy to use for unknown domains."),
778 NOJS_TYPE_POLICY,
779 NOJS_POLICY_BLOCK,
780 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
781
782 g_object_class_install_properties(gobjectClass, PROP_LAST, NoJSProperties);
783
784 /* Define signals */
785
786 /* Why does this signal exist?
787 *
788 * The problem I faced when developing this extension was
789 * that I needed to cancel a SoupMessage as soon as possible
790 * (when http headers were received).
791 * I tried to connect to signal "resource-response-received"
792 * of WebKitWebView but the SoupMessage instance was not
793 * exactly the same which were sent or received by SoupSession.
794 * So I could not cancel the SoupMessage or better: I cancelled
795 * a SoupMessage which is not be handled so it had no effect.
796 * The body of SoupMessage was still being loaded and javascript
797 * was executed. I think the problem is that webkit-gtk creates
798 * a copy of the real SoupMessage which is going to be sent and
799 * received.
800 *
801 * So I decided to connect to signal "got-headers" of every
802 * SoupMessage sent by the default SoupSession which I notice
803 * by connecting to signal "request-started" of SoupSession. Each
804 * NoJSView connects to signal "resource-request-starting" of
805 * WebKitWebView to remember each URI going to be loaded. When
806 * a SoupMessage hits "got-headers" and is a javascript resource
807 * I can cancel the message immediately and clear the body which
808 * causes webkit-gtk to copy a empty body if it does at all as the
809 * SoupMessage was cancelled. Then I emit this signal
810 * "uri-load-policy-status" to notify each view but the cancellation.
811 * (It also notifies all views if it is going to load to keep the
812 * menu in right state.) Each view will check if it _could_ be a
813 * resource itself requested and will update its menu accordingly.
814 * It might happen that a request will match two views because only
815 * the URI will be checked by the view because I cannot determine
816 * to which view the SoupMessage belongs to. But it doesn't matter
817 * because if a javascript resource was denied or allowed in one view
818 * it is likely be denied or allowed in other views too ;)
819 */
820 NoJSSignals[URI_LOAD_POLICY_STATUS]=
821 g_signal_new("uri-load-policy-status",
822 G_TYPE_FROM_CLASS(klass),
823 G_SIGNAL_RUN_LAST,
824 G_STRUCT_OFFSET(NoJSClass, uri_load_policy_status),
825 NULL,
826 NULL,
827 _nojs_closure_VOID__STRING_ENUM,
828 G_TYPE_NONE,
829 2,
830 G_TYPE_STRING,
831 NOJS_TYPE_POLICY);
832
833 NoJSSignals[POLICY_CHANGED]=
834 g_signal_new("policy-changed",
835 G_TYPE_FROM_CLASS(klass),
836 G_SIGNAL_RUN_LAST,
837 G_STRUCT_OFFSET(NoJSClass, policy_changed),
838 NULL,
839 NULL,
840 g_cclosure_marshal_VOID__STRING,
841 G_TYPE_NONE,
842 1,
843 G_TYPE_STRING);
844}
845
846/* Object initialization
847 * Create private structure and set up default values
848 */
849
850static void nojs_init(NoJS *self)
851{
852 NoJSPrivate *priv;
853 SoupSession *session;
854
855 priv=self->priv=NOJS_GET_PRIVATE(self);
856
857 /* Set up default values */
858 priv->database=NULL;
859 priv->databaseFilename=NULL;
860 priv->allowAllSites=FALSE;
861 priv->checkOnlySecondLevel=TRUE;
862 priv->unknownDomainPolicy=NOJS_POLICY_BLOCK;
863
864 /* Connect to signals on session to be able to cancel messages
865 * loading javascript documents
866 */
867 session=webkit_get_default_session();
868 g_signal_connect_swapped(session, "request-started", G_CALLBACK(_nojs_on_request_started), self);
869}
870
871/* Implementation: Public API */
872
873/* Create new object */
874NoJS* nojs_new(MidoriExtension *inExtension, MidoriApp *inApp)
875{
876 return(g_object_new(TYPE_NOJS,
877 "extension", inExtension,
878 "application", inApp,
879 NULL));
880}
881
882/* Retrieves domain from uri depending on preferences (e.g. only second level domain) */
883gchar* nojs_get_domain(NoJS *self, SoupURI *inURI)
884{
885 g_return_val_if_fail(IS_NOJS(self), NULL);
886 g_return_val_if_fail(inURI, NULL);
887
888 NoJSPrivate *priv=self->priv;
889 const gchar *realDomain;
890 gchar *asciiDomain, *domain;
891 gchar *finalDomain;
892
893 /* Get domain of site to lookup */
894 realDomain=soup_uri_get_host(inURI);
895
896 domain=asciiDomain=g_hostname_to_ascii(realDomain);
897
898 if(priv->checkOnlySecondLevel)
899 {
900 /* Only get second level domain if host is not an IP address */
901 if(!g_hostname_is_ip_address(asciiDomain))
902 {
903 gint numberDots=0;
904
905 domain=asciiDomain+strlen(asciiDomain)-1;
906 while(domain>=asciiDomain && numberDots<2)
907 {
908 if(*domain=='.') numberDots++;
909 domain--;
910 }
911 domain++;
912 if(*domain=='.') domain++;
913 }
914 }
915
916 /* Create copy for return value */
917 if(strlen(domain)>0) finalDomain=g_strdup(domain);
918 else finalDomain=NULL;
919
920 /* Free allocated resources */
921 g_free(asciiDomain);
922
923 /* Return domain */
924 return(finalDomain);
925}
926
927/* Get/set policy for javascript from site */
928gint nojs_get_policy(NoJS *self, const gchar *inDomain)
929{
930 g_return_val_if_fail(IS_NOJS(self), NOJS_POLICY_UNDETERMINED);
931 g_return_val_if_fail(inDomain, NOJS_POLICY_UNDETERMINED);
932
933 NoJSPrivate *priv=self->priv;
934 sqlite3_stmt *statement=NULL;
935 gint error;
936 gint policy=NOJS_POLICY_UNDETERMINED;
937
938 /* Check to allow all sites */
939 if(priv->allowAllSites) return(NOJS_POLICY_ACCEPT);
940
941 /* Check for open database */
942 g_return_val_if_fail(priv->database, policy);
943
944 /* Lookup policy for site in database */
945 error=sqlite3_prepare_v2(priv->database,
946 "SELECT site, value FROM policies WHERE site LIKE ? LIMIT 1;",
947 -1,
948 &statement,
949 NULL);
950 if(statement && error==SQLITE_OK) error=sqlite3_bind_text(statement, 1, inDomain, -1, NULL);
951 if(statement && error==SQLITE_OK)
952 {
953 if(sqlite3_step(statement)==SQLITE_ROW) policy=sqlite3_column_int(statement, 1);
954 }
955 else g_warning(_("SQL fails: %s"), sqlite3_errmsg(priv->database));
956
957 sqlite3_finalize(statement);
958
959 /* If we have not found a policy for the domain then it is an unknown domain.
960 * Get default policy for unknown domains.
961 */
962 if(policy==NOJS_POLICY_UNDETERMINED) policy=priv->unknownDomainPolicy;
963
964 return(policy);
965}
966
967void nojs_set_policy(NoJS *self, const gchar *inDomain, NoJSPolicy inPolicy)
968{
969 g_return_if_fail(IS_NOJS(self));
970 g_return_if_fail(inDomain);
971 g_return_if_fail(inPolicy>=NOJS_POLICY_ACCEPT && inPolicy<=NOJS_POLICY_BLOCK);
972
973 NoJSPrivate *priv=self->priv;
974 gchar *sql;
975 gchar *error=NULL;
976 gint success;
977
978 /* Check for open database */
979 g_return_if_fail(priv->database);
980
981 /* Update policy in database */
982 sql=sqlite3_mprintf("INSERT OR REPLACE INTO policies (site, value) VALUES ('%q', %d);",
983 inDomain,
984 inPolicy);
985 success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
986 if(success!=SQLITE_OK) g_warning(_("SQL fails: %s"), error);
987 if(error) sqlite3_free(error);
988 sqlite3_free(sql);
989
990 /* Emit signal to notify about policy change */
991 if(success==SQLITE_OK) g_signal_emit(self, NoJSSignals[POLICY_CHANGED], 0, inDomain);
992}
993
994/* Get/set default policy for unknown domains */
995NoJSPolicy nojs_get_policy_for_unknown_domain(NoJS *self)
996{
997 g_return_val_if_fail(IS_NOJS(self), NOJS_POLICY_UNDETERMINED);
998
999 return(self->priv->unknownDomainPolicy);
1000}
1001
1002void nojs_set_policy_for_unknown_domain(NoJS *self, NoJSPolicy inPolicy)
1003{
1004 g_return_if_fail(IS_NOJS(self));
1005 g_return_if_fail(inPolicy>=NOJS_POLICY_ACCEPT && inPolicy<=NOJS_POLICY_BLOCK);
1006
1007 if(self->priv->unknownDomainPolicy!=inPolicy)
1008 {
1009 self->priv->unknownDomainPolicy=inPolicy;
1010 midori_extension_set_integer(self->priv->extension, "unknown-domain-policy", inPolicy);
1011 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_UNKNOWN_DOMAIN_POLICY]);
1012 }
1013}
1014
1015/* Get/set flag to allow javascript at all sites */
1016gboolean nojs_get_allow_all_sites(NoJS *self)
1017{
1018 g_return_val_if_fail(IS_NOJS(self), TRUE);
1019
1020 return(self->priv->allowAllSites);
1021}
1022
1023void nojs_set_allow_all_sites(NoJS *self, gboolean inAllow)
1024{
1025 g_return_if_fail(IS_NOJS(self));
1026
1027 if(self->priv->allowAllSites!=inAllow)
1028 {
1029 self->priv->allowAllSites=inAllow;
1030 midori_extension_set_boolean(self->priv->extension, "allow-all-sites", inAllow);
1031 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_ALLOW_ALL_SITES]);
1032 }
1033}
1034
1035/* Get/set flag to check for second-level domanins only */
1036gboolean nojs_get_only_second_level_domain(NoJS *self)
1037{
1038 g_return_val_if_fail(IS_NOJS(self), TRUE);
1039
1040 return(self->priv->checkOnlySecondLevel);
1041}
1042
1043void nojs_set_only_second_level_domain(NoJS *self, gboolean inOnlySecondLevel)
1044{
1045 g_return_if_fail(IS_NOJS(self));
1046
1047 if(self->priv->checkOnlySecondLevel!=inOnlySecondLevel)
1048 {
1049 self->priv->checkOnlySecondLevel=inOnlySecondLevel;
1050 midori_extension_set_boolean(self->priv->extension, "only-second-level", inOnlySecondLevel);
1051 g_object_notify_by_pspec(G_OBJECT(self), NoJSProperties[PROP_ONLY_SECOND_LEVEL]);
1052 }
1053}
1054
1055/************************************************************************************/
1056
1057/* Implementation: Enumeration */
1058GType nojs_policy_get_type(void)
1059{
1060 static volatile gsize g_define_type_id__volatile=0;
1061
1062 if(g_once_init_enter(&g_define_type_id__volatile))
1063 {
1064 static const GEnumValue values[]=
1065 {
1066 { NOJS_POLICY_UNDETERMINED, "NOJS_POLICY_UNDETERMINED", N_("Undetermined") },
1067 { NOJS_POLICY_ACCEPT, "NOJS_POLICY_ACCEPT", N_("Accept") },
1068 { NOJS_POLICY_ACCEPT_TEMPORARILY, "NOJS_POLICY_ACCEPT_TEMPORARILY", N_("Accept temporarily") },
1069 { NOJS_POLICY_BLOCK, "NOJS_POLICY_BLOCK", N_("Block") },
1070 { 0, NULL, NULL }
1071 };
1072
1073 GType g_define_type_id=g_enum_register_static(g_intern_static_string("NoJSPolicy"), values);
1074 g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
1075 }
1076
1077 return(g_define_type_id__volatile);
1078}
01079
=== added file 'extensions/nojs/nojs.h'
--- extensions/nojs/nojs.h 1970-01-01 00:00:00 +0000
+++ extensions/nojs/nojs.h 2013-05-22 23:07:26 +0000
@@ -0,0 +1,87 @@
1/*
2 Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 See the file COPYING for the full license text.
10*/
11
12#ifndef __NOJS__
13#define __NOJS__
14
15#include "config.h"
16#include <midori/midori.h>
17
18#define NOJS_DATABASE "nojs.db"
19
20G_BEGIN_DECLS
21
22/* NoJS manager enums */
23typedef enum
24{
25 NOJS_POLICY_UNDETERMINED,
26 NOJS_POLICY_ACCEPT,
27 NOJS_POLICY_ACCEPT_TEMPORARILY,
28 NOJS_POLICY_BLOCK
29} NoJSPolicy;
30
31/* NoJS manager object */
32#define TYPE_NOJS (nojs_get_type())
33#define NOJS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NOJS, NoJS))
34#define IS_NOJS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_NOJS))
35#define NOJS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_NOJS, NoJSClass))
36#define IS_NOJS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_NOJS))
37#define NOJS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_NOJS, NoJSClass))
38
39typedef struct _NoJS NoJS;
40typedef struct _NoJSClass NoJSClass;
41typedef struct _NoJSPrivate NoJSPrivate;
42
43struct _NoJS
44{
45 /* Parent instance */
46 GObject parent_instance;
47
48 /* Private structure */
49 NoJSPrivate *priv;
50};
51
52struct _NoJSClass
53{
54 /* Parent class */
55 GObjectClass parent_class;
56
57 /* Virtual functions */
58 void (*uri_load_policy_status)(NoJS *self, gchar *inURI, NoJSPolicy inPolicy);
59 void (*policy_changed)(NoJS *self, gchar *inDomain);
60};
61
62/* Public API */
63GType nojs_get_type(void);
64
65NoJS* nojs_new(MidoriExtension *inExtension, MidoriApp *inApp);
66
67gchar* nojs_get_domain(NoJS *self, SoupURI *inURI);
68
69gint nojs_get_policy(NoJS *self, const gchar *inDomain);
70void nojs_set_policy(NoJS *self, const gchar *inDomain, NoJSPolicy inPolicy);
71
72NoJSPolicy nojs_get_policy_for_unknown_domain(NoJS *self);
73void nojs_set_policy_for_unknown_domain(NoJS *self, NoJSPolicy inPolicy);
74
75gboolean nojs_get_allow_all_sites(NoJS *self);
76void nojs_set_allow_all_sites(NoJS *self, gboolean inAllow);
77
78gboolean nojs_get_only_second_level_domain(NoJS *self);
79void nojs_set_only_second_level_domain(NoJS *self, gboolean inOnlySecondLevel);
80
81/* Enumeration */
82GType nojs_policy_get_type(void) G_GNUC_CONST;
83#define NOJS_TYPE_POLICY (nojs_policy_get_type())
84
85G_END_DECLS
86
87#endif /* __NOJS__ */

Subscribers

People subscribed via source and target branches

to all changes: