Debian: prepare debian/changelog for uploading a new snapshot
[conkeror.git] / modules / permission-manager.js
blob803b329b674e1d1129d05a09ae4df207b65c46d8
1 /**
2  * (C) Copyright 2008 Jeremy Maitin-Shepard
3  *
4  * Use, modification, and distribution are subject to the terms specified in the
5  * COPYING file.
6 **/
8 /**
9  * Interface to nsIPermissionManager, which controls the popup
10  * blocking whitelist, among other things.
11  */
13 let permission_manager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
15 let permission_types = {
16     popup : {desc: "specifies a whitelist and blacklist for the popup blocker",
17              values: [ ["allow", Ci.nsIPermissionManager.ALLOW_ACTION],
18                        ["deny", Ci.nsIPermissionManager.DENY_ACTION] ],
19              related_prefs: [
20                  [ "dom.disable_open_during_load",
21                    "This preference defines the default behavior of whether " +
22                    "unrequested popups are allowed for sites not listed in the permission list." ],
23                  [ "dom.popup_maximum", "The number of pop-ups to allow from a single non-click event."] ]
24             },
26     cookie : {desc: "specifies per-host cookie policies",
27               values: [
28                   ["allow", Ci.nsIPermissionManager.ALLOW_ACTION],
29                   ["session", Ci.nsICookiePermission.ACCESS_SESSION, "expire matching cookies when the browser exits"],
30                   ["deny", Ci.nsIPermissionManager.DENY_ACTION] ],
31               related_prefs: [
32                   [ "network.cookie.lifetime.behavior.enabled" ],
33                   [ "network.cookie.cookieBehavior",
34                     "This preference defines the default cookie behavior for sites not listed in the " +
35                     "permission list.  The value 0 means to enable all cookies, 1 means to reject " +
36                     "only third-party cookies, and 2 means to reject all cookies." ],
37                   [ "network.cookie.lifetime.behavior",
38                     "If network.cookie.lifetime.behavior.enabled is set to true, a value of 0 means all " +
39                     "cookies expire at the end of the current session,  while a value of 1 means that " +
40                     "cookies expire after the number of days specified by network.cookie.lifetime.days."
41                   ],
42                   [ "network.cookie.lifetime.days" ] ]
43              },
46     image : {desc: "specifies per-host image automatic loading policies",
47               values: [
48                   ["allow", Ci.nsIPermissionManager.ALLOW_ACTION],
49                   ["deny", Ci.nsIPermissionManager.DENY_ACTION] ],
50               related_prefs: [
51                   [ "permissions.default.image", "This prefreence defines the default image loading policy "
52                                                  + "for sites not listed in the permission list.  The value "
53                                                  + "1 means all images should be loaded, 2 means no images "
54                                                  + "should be loaded, and 3 means only third-party images "
55                                                  + "are blocked." ] ]
56             },
58     install : {desc: "specifies a whitelist of sites from which XPI files may be opened",
59               values: [
60                   ["allow", Ci.nsIPermissionManager.ALLOW_ACTION] ]
61               }
65  * cookie
66  *
67  *
68  *
69  * popup
70  *   dom.popup_maximum  -  The number of pop-ups to allow from a single non-click event
71  *
72  *   dom.popup_allowed_events
73  *
74  *   dom.disable_open_during_load - This preference defines the default behavior of whether unrequested popups are allowed for sites not listed in the permission list.
75  *
76  *
77  */
79 interactive("permission-manager", "View or edit the host-specific "
80             + "permission list.\nThis list is used for the popup"
81             + "blocker whitelist, among other things.",
82             function (I) {
83                 I.minibuffer.message("Save the file and close the editor when done editing permissions.");
85                 let existing_perms = {};
87                 var file_buf =
88                     "# -*- conf -*-\n" +
89                     "# Permission list\n\n";
92                 {
93                     let e = permission_manager.enumerator;
94                     let arr = [];
95                     let max_host_len = 0;
96                     let max_type_len = 0;
97                     while (e.hasMoreElements()) {
98                         let p = e.getNext().QueryInterface(Ci.nsIPermission);
99                         let host;
100                         if (version_compare(get_mozilla_version(), "42.0") >= 0)
101                             host = p.principal.origin;
102                         else
103                             host = p.host;
104                         let type = p.type;
105                         let cap = p.capability;
106                         if (max_host_len < host.length)
107                             max_host_len = host.length;
108                         if (max_type_len < type.length)
109                             max_type_len = type.length;
110                         arr.push([host, type, cap]);
111                         existing_perms[""+[host, type]] = cap;
112                     }
113                     ++max_host_len;
114                     ++max_type_len;
115                     let max_host = get_spaces(max_host_len);
116                     let max_type = get_spaces(max_type_len);
117                     for (let i = 0; i < arr.length; ++i) {
118                         let [host, type, cap] = arr[i];
119                         if (permission_types.hasOwnProperty(type)) {
120                             let values = permission_types[type].values;
121                             for (let j = 0; j < values.length; ++j) {
122                                 if (cap == values[j][1]) {
123                                     cap = values[j][0];
124                                     break;
125                                 }
126                             }
127                         }
128                         file_buf += host + max_host.substr(host.length) + type + max_type.substr(type.length) + cap + "\n";
129                     }
131                     if (arr.length == 0)
132                         file_buf += "\n";
133                 }
135                 let example = (version_compare(get_mozilla_version(), "42.0") >= 0) ?
136                     "https://google.com" : "google.com";
137                 let url_type = (version_compare(get_mozilla_version(), "42.0") >= 0) ?
138                     "protocol://domain" : "domain";
139                 file_buf += "\n" +
140                     "# entry syntax (one per line): <" + url_type + "> <type> <permission>\n\n" +
141                     "# example: " + example + " popup allow\n\n" +
143                     word_wrap("The <domain> must be a valid domain name.  Depending on the <type>, only exact " +
144                               "matches may be used, or alternatively it may match any sub-domain if a more " +
145                               "specific entry is not found.", 80, "# ") + "\n" +
146                     "# The possible values for the permission <type> include:\n";
147                 for (let type in permission_types) {
148                     let data = permission_types[type];
149                     file_buf += "#   " + type + " - " + data.desc + "\n\n";
150                     file_buf += "#     Supported <permission> values:\n";
151                     for (let i = 0; i < data.values.length; ++i) {
152                         let x = data.values[i];
153                         file_buf += "#       " + x[0] + " (" + x[1] + ")";
154                         if (x[3])
155                             file_buf += " - " + x[3];
156                         file_buf += "\n";
157                     }
158                     if (data.related_prefs && data.related_prefs.length > 0) {
159                         file_buf += "\n#     Related Mozilla preferences:\n";
160                         for (let i = 0; i < data.related_prefs.length; ++i) {
161                             let x = data.related_prefs[i];
162                             file_buf += "#       " + x[0] + " = " + get_pref(x[0]) + "\n";
163                             if (x.length > 1) {
164                                 file_buf += word_wrap(x[1], 80, "#         ", "#");
165                             }
166                             file_buf += "\n";
167                         }
168                     }
169                 }
170                 var file = null;
171                 try {
172                     file = get_temporary_file("permissions.txt");
173                     let line = 4;
175                     outer: while (1) {
177                         write_text_file(file, file_buf);
178                         yield open_file_with_external_editor(file, $line = line);
180                         let new_buf = read_text_file(file);
181                         if (new_buf == file_buf) {
182                             I.minibuffer.message("No permission changes made.");
183                             break;
184                         }
186                         // Parse
187                         let lines = new_buf.split("\n");
188                         if (lines[lines.length - 1].length == 0) // Remove extra line at end
189                             lines.length = lines.length - 1;
190                         let arr = [];
191                         let prev_entries = {};
192                         for (let i = 0; i < lines.length; ++i) {
193                             // Parse each line, checking for syntax errors
194                             let x = lines[i];
195                             let idx = x.indexOf('#');
196                             if (idx != -1)
197                                 x = x.substr(0, idx);
198                             let parts = x.split(/\s+/);
199                             if (parts.length == 1 && parts[0].length == 0)
200                                 continue; // ignore blank line
201                             try {
202                                 let host = parts[0];
203                                 if (!/[a-zA-Z0-9-_]+(\.[a-zA-Z0-9-_]+)*/.test(host))
204                                     throw "invalid host name: " + host;
205                                 if (parts.length < 2)
206                                     throw "missing permission type";
207                                 let type = parts[1];
208                                 if (parts.length < 3)
209                                     throw "missing permission value";
210                                 let cap = parts[2];
211                                 if (permission_types.hasOwnProperty(type)) {
212                                     let values = permission_types[type].values;
213                                     for (let i = 0; i < values.length; ++i) {
214                                         if (cap == values[i][0]) {
215                                             cap = values[i][1];
216                                             break;
217                                         }
218                                     }
219                                 }
220                                 if (!/([0-9]+)/.test(cap))
221                                     throw "invalid permission value: " + cap;
222                                 cap = parseInt(cap);
223                                 if (parts.length > 3)
224                                     throw "too many terms";
225                                 if (prev_entries[""+[host,type]])
226                                     throw "duplicate entry";
227                                 prev_entries[""+[host,type]] = true;
228                                 arr.push([host,type,cap]);
229                             } catch (syntax_err) {
230                                 line = i + 1;
231                                 lines.splice(i+1, 0, "# ERROR on previous line: " + syntax_err, "");
232                                 file_buf = lines.join("\n") + "\n";
233                                 I.minibuffer.message("Correct the syntax error in the permissions list, " +
234                                                      "or close the editor to abort.");
235                                 continue outer;
236                             }
237                         }
238                         let num_added = 0;
239                         let num_changed = 0;
240                         for (let i = 0; i < arr.length; ++i) {
241                             let [host,type,cap] = arr[i];
242                             let x = existing_perms[""+[host,type]];
243                             let add = false;
244                             if (x === undefined) {
245                                 ++num_added;
246                                 add = true;
247                             } else {
248                                 if (x != cap) {
249                                     ++num_changed;
250                                     add = true;
251                                 }
252                                 delete existing_perms[""+[host,type]];
253                             }
254                             if (add) {
255                                 if (version_compare(get_mozilla_version(), "42.0") >= 0) {
256                                     permission_manager.add(make_uri(host), type, cap);
257                                 } else {
258                                     permission_manager.add(make_uri("http://" + host), type, cap);
259                                 }
260                             }
261                         }
262                         let num_removed = 0;
263                         for (let [k,v] in Iterator(existing_perms)) {
264                             let [host,type] = k.split(",",2);
265                             ++num_removed;
266                             if (version_compare(get_mozilla_version(), "42.0") >= 0) {
267                                 permission_manager.remove(make_uri(host),type);
268                             } else {
269                                 permission_manager.remove(host,type);
270                             }
271                         }
272                         let msg;
273                         if (num_added == 0 && num_changed == 0 && num_removed == 0)
274                             msg = "No permission changes made.";
275                         else {
276                             msg = "Updated permissions list: " +
277                                 [["added", num_added],
278                                  ["changed", num_changed],
279                                  ["removed", num_removed]].
280                                 filter(function ([caption, count]) count > 0).
281                                 map(function ([caption, count]) {
282                                         if (count == 1)
283                                              return "1 entry " + caption;
284                                         return count + " entries " + caption;
285                                     }).join(", ") +
286                                 ".";
287                         }
288                         I.minibuffer.message(msg);
289                         break;
290                     }
291                 } catch (e) {
292                     dump_error(e);
293                     throw interactive_error("Failed to edit permissions list in external editor.");
294                 } finally {
295                     file.remove(false);
296                 }
297             });
299 provide("permission-manager");