2 * (C) Copyright 2008 Jeremy Maitin-Shepard
4 * Use, modification, and distribution are subject to the terms specified in the
9 * Interface to nsIPermissionManager, which controls the popup
10 * blocking whitelist, among other things.
13 let permission_manager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
15 function word_wrap (str, line_length, line_prefix_first, line_prefix) {
16 if (line_prefix === undefined)
17 line_prefix = line_prefix_first;
18 else if (line_prefix.length < line_prefix_first.length) {
19 line_prefix += get_spaces(line_prefix_first.length - line_prefix.length);
22 line_length -= line_prefix_first.length;
27 let cur_prefix = line_prefix_first;
30 while (line_length < str.length) {
31 let i = str.lastIndexOf(" ", line_length);
33 i = str.indexOf(" ", line_length);
35 out += cur_prefix + str + "\n";
39 out += cur_prefix + str.substr(0, i) + "\n";
40 while (i < str.length && str.charAt(i) == " ")
44 cur_prefix = line_prefix;
47 out += cur_prefix + str + "\n";
51 let permission_types = {
52 popup : {desc: "specifies a whitelist and blacklist for the popup blocker",
53 values: [ ["allow", Ci.nsIPermissionManager.ALLOW_ACTION],
54 ["deny", Ci.nsIPermissionManager.DENY_ACTION] ],
56 [ "dom.disable_open_during_load",
57 "This preference defines the default behavior of whether " +
58 "unrequested popups are allowed for sites not listed in the permission list." ],
59 [ "dom.popup_maximum", "The number of pop-ups to allow from a single non-click event."] ]
62 cookie : {desc: "specifies per-host cookie policies",
64 ["allow", Ci.nsIPermissionManager.ALLOW_ACTION],
65 ["session", Ci.nsICookiePermission.ACCESS_SESSION, "expire matching cookies when the browser exits"],
66 ["deny", Ci.nsIPermissionManager.DENY_ACTION] ],
68 [ "network.cookie.lifetime.behavior.enabled" ],
69 [ "network.cookie.cookieBehavior",
70 "This preference defines the default cookie behavior for sites not listed in the " +
71 "permission list. The value 0 means to enable all cookies, 1 means to reject " +
72 "only third-party cookies, and 2 means to reject all cookies." ],
73 [ "network.cookie.lifetime.behavior",
74 "If network.cookie.lifetime.behavior.enabled is set to true, a value of 0 means all " +
75 "cookies expire at the end of the current session, while a value of 1 means that " +
76 "cookies expire after the number of days specified by network.cookie.lifetime.days."
78 [ "network.cookie.lifetime.days" ] ]
82 image : {desc: "specifies per-host image automatic loading policies",
84 ["allow", Ci.nsIPermissionManager.ALLOW_ACTION],
85 ["deny", Ci.nsIPermissionManager.DENY_ACTION] ],
87 [ "permissions.default.image", "This prefreence defines the default image loading policy "
88 + "for sites not listed in the permission list. The value "
89 + "1 means all images should be loaded, 2 means no images "
90 + "should be loaded, and 3 means only third-party images "
94 install : {desc: "specifies a whitelist of sites from which XPI files may be opened",
96 ["allow", Ci.nsIPermissionManager.ALLOW_ACTION] ]
106 * dom.popup_maximum - The number of pop-ups to allow from a single non-click event
108 * dom.popup_allowed_events
110 * 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.
115 interactive("permission-manager", "View or edit the host-specific "
116 + "permission list.\nThis list is used for the popup"
117 + "blocker whitelist, among other things.",
119 I.minibuffer.message("Save the file and close the editor when done editing permissions.");
121 let existing_perms = new string_hashmap();
125 "# Permission list\n\n";
129 let e = permission_manager.enumerator;
131 let max_host_len = 0;
132 let max_type_len = 0;
133 while (e.hasMoreElements()) {
134 let p = e.getNext().QueryInterface(Ci.nsIPermission);
137 let cap = p.capability;
138 if (max_host_len < host.length)
139 max_host_len = host.length;
140 if (max_type_len < type.length)
141 max_type_len = type.length;
142 arr.push([host, type, cap]);
143 existing_perms.put([host, type], cap);
147 let max_host = get_spaces(max_host_len);
148 let max_type = get_spaces(max_type_len);
149 for (let i = 0; i < arr.length; ++i) {
150 let [host, type, cap] = arr[i];
151 if (permission_types.hasOwnProperty(type)) {
152 let values = permission_types[type].values;
153 for (let j = 0; j < values.length; ++j) {
154 if (cap == values[j][1]) {
160 file_buf += host + max_host.substr(host.length) + type + max_type.substr(type.length) + cap + "\n";
168 "# entry syntax (one per line): <domain> <type> <permission>\n\n" +
169 "# example: google.com popup allow\n\n" +
171 word_wrap("The <domain> must be a valid domain name. Depending on the <type>, only exact " +
172 "matches may be used, or alternatively it may match any sub-domain if a more " +
173 "specific entry is not found.", 80, "# ") + "\n" +
174 "# The possible values for the permission <type> include:\n";
175 for (let type in permission_types) {
176 let data = permission_types[type];
177 file_buf += "# " + type + " - " + data.desc + "\n\n";
178 file_buf += "# Supported <permission> values:\n";
179 for (let i = 0; i < data.values.length; ++i) {
180 let x = data.values[i];
181 file_buf += "# " + x[0] + " (" + x[1] + ")";
183 file_buf += " - " + x[3];
186 if (data.related_prefs && data.related_prefs.length > 0) {
187 file_buf += "\n# Related Mozilla preferences:\n";
188 for (let i = 0; i < data.related_prefs.length; ++i) {
189 let x = data.related_prefs[i];
190 file_buf += "# " + x[0] + " = " + get_pref(x[0]) + "\n";
192 file_buf += word_wrap(x[1], 80, "# ", "#");
200 file = get_temporary_file("permissions.txt");
205 write_text_file(file, file_buf);
206 yield open_file_with_external_editor(file, $line = line);
208 let new_buf = read_text_file(file);
209 if (new_buf == file_buf) {
210 I.minibuffer.message("No permission changes made.");
215 let lines = new_buf.split("\n");
216 if (lines[lines.length - 1].length == 0) // Remove extra line at end
217 lines.length = lines.length - 1;
219 let prev_entries = new string_hashset();
220 for (let i = 0; i < lines.length; ++i) {
221 // Parse each line, checking for syntax errors
223 let idx = x.indexOf('#');
225 x = x.substr(0, idx);
226 let parts = x.split(/\s+/);
227 if (parts.length == 1 && parts[0].length == 0)
228 continue; // ignore blank line
231 if (!/[a-zA-Z0-9-_]+(\.[a-zA-Z0-9-_]+)*/.test(host))
232 throw "invalid host name: " + host;
233 if (parts.length < 2)
234 throw "missing permission type";
236 if (parts.length < 3)
237 throw "missing permission value";
239 if (permission_types.hasOwnProperty(type)) {
240 let values = permission_types[type].values;
241 for (let i = 0; i < values.length; ++i) {
242 if (cap == values[i][0]) {
248 if (!/([0-9]+)/.test(cap))
249 throw "invalid permission value: " + cap;
251 if (parts.length > 3)
252 throw "too many terms";
253 if (prev_entries.contains([host,type]))
254 throw "duplicate entry";
255 prev_entries.add([host,type]);
256 arr.push([host,type,cap]);
257 } catch (syntax_err) {
259 lines.splice(i+1, 0, "# ERROR on previous line: " + syntax_err, "");
260 file_buf = lines.join("\n") + "\n";
261 I.minibuffer.message("Correct the syntax error in the permissions list, " +
262 "or close the editor to abort.");
268 for (let i = 0; i < arr.length; ++i) {
269 let [host,type,cap] = arr[i];
270 let x = existing_perms.get([host,type]);
272 if (x === undefined) {
280 existing_perms.remove([host,type]);
283 permission_manager.add(make_uri("http://" + host), type, cap);
286 for (let k in existing_perms.iterator(true)) {
287 let [host,type] = k.split(",",2);
289 permission_manager.remove(host,type);
292 if (num_added == 0 && num_changed == 0 && num_removed == 0)
293 msg = "No permission changes made.";
295 msg = "Updated permissions list: " +
296 [["added", num_added],
297 ["changed", num_changed],
298 ["removed", num_removed]].
299 filter(function ([caption, count]) count > 0).
300 map(function ([caption, count]) {
302 return "1 entry " + caption;
303 return count + " entries " + caption;
307 I.minibuffer.message(msg);
312 throw interactive_error("Failed to edit permissions list in external editor.");