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