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.
14 let permission_manager
= Cc
["@mozilla.org/permissionmanager;1"].getService(Ci
.nsIPermissionManager
);
16 let get_spaces
= function get_spaces(n
) {
18 while (x
.length
< n
) x
+= " ";
22 let word_wrap
= function word_wrap(str
, line_length
, line_prefix_first
, line_prefix
) {
23 if (line_prefix
=== undefined)
24 line_prefix
= line_prefix_first
;
25 else if (line_prefix
.length
< line_prefix_first
.length
) {
26 line_prefix
+= get_spaces(line_prefix_first
.length
- line_prefix
.length
);
29 line_length
-= line_prefix_first
.length
;
34 let cur_prefix
= line_prefix_first
;
37 while (line_length
< str
.length
) {
38 let i
= str
.lastIndexOf(" ", line_length
);
40 i
= str
.indexOf(" ", line_length
);
42 out
+= cur_prefix
+ str
+ "\n";
46 out
+= cur_prefix
+ str
.substr(0, i
) + "\n";
47 while (i
< str
.length
&& str
.charAt(i
) == " ")
49 str
= str
.substr(i
+ 1);
51 cur_prefix
= line_prefix
;
54 out
+= cur_prefix
+ str
+ "\n";
58 let permission_types
= {
59 popup
: {desc
: "specifies a whitelist and blacklist for the popup blocker",
60 values
: [ ["allow", Ci
.nsIPermissionManager
.ALLOW_ACTION
],
61 ["deny", Ci
.nsIPermissionManager
.DENY_ACTION
], ],
63 [ "dom.disable_open_during_load",
64 "This preference defines the default behavior of whether " +
65 "unrequested popups are allowed for sites not listed in the permission list." ],
66 [ "dom.popup_maximum", "The number of pop-ups to allow from a single non-click event."] ]
69 cookie
: {desc
: "specifies per-host cookie policies",
71 ["allow", Ci
.nsIPermissionManager
.ALLOW_ACTION
],
72 ["session", Ci
.nsICookiePermission
.ACCESS_SESSION
, "expire matching cookies when the browser exits"],
73 ["deny", Ci
.nsIPermissionManager
.DENY_ACTION
], ],
75 [ "network.cookie.lifetime.behavior.enabled" ],
76 [ "network.cookie.cookieBehavior",
77 "This preference defines the default cookie behavior for sites not listed in the " +
78 "permission list. The value 0 means to enable all cookies, 1 means to reject " +
79 "only third-party cookies, and 2 means to reject all cookies." ],
80 [ "network.cookie.lifetime.behavior",
81 "If network.cookie.lifetime.behavior.enabled is set to true, a value of 0 means all " +
82 "cookies expire at the end of the current session, while a value of 1 means that " +
83 "cookies expire after the number of days specified by network.cookie.lifetime.days."
85 [ "network.cookie.lifetime.days" ] ]
89 image
: {desc
: "specifies per-host image automatic loading policies",
91 ["allow", Ci
.nsIPermissionManager
.ALLOW_ACTION
],
92 ["deny", Ci
.nsIPermissionManager
.DENY_ACTION
], ],
94 [ "permissions.default.image", "This prefreence defines the default image loading policy "
95 + "for sites not listed in the permission list. The value "
96 + "1 means all images should be loaded, 2 means no images "
97 + "should be loaded, and 3 means only third-party images "
101 install
: {desc
: "specifies a whitelist of sites from which XPI files may be opened",
103 ["allow", Ci
.nsIPermissionManager
.ALLOW_ACTION
] ]
113 * dom.popup_maximum - The number of pop-ups to allow from a single non-click event
115 * dom.popup_allowed_events
117 * 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.
122 interactive("permission-manager", "View or edit the host-specific "
123 + "permission list.\nThis list is used for the popup"
124 + "blocker whitelist, among other things.",
126 I
.minibuffer
.message("Save the file and close the editor when done editing permissions.");
128 let existing_perms
= new string_hashmap();
132 "# Permission list\n\n";
136 let e
= permission_manager
.enumerator
;
138 let max_host_len
= 0;
139 let max_type_len
= 0;
140 while (e
.hasMoreElements()) {
141 let p
= e
.getNext().QueryInterface(Ci
.nsIPermission
);
144 let cap
= p
.capability
;
145 if (max_host_len
< host
.length
)
146 max_host_len
= host
.length
;
147 if (max_type_len
< type
.length
)
148 max_type_len
= type
.length
;
149 arr
.push([host
, type
, cap
]);
150 existing_perms
.put([host
, type
], cap
);
154 let max_host
= get_spaces(max_host_len
);
155 let max_type
= get_spaces(max_type_len
);
156 for (let i
= 0; i
< arr
.length
; ++i
) {
157 let [host
, type
, cap
] = arr
[i
];
158 if (permission_types
.hasOwnProperty(type
)) {
159 let values
= permission_types
[type
].values
;
160 for (let j
= 0; j
< values
.length
; ++j
) {
161 if (cap
== values
[j
][1]) {
167 file_buf
+= host
+ max_host
.substr(host
.length
) + type
+ max_type
.substr(type
.length
) + cap
+ "\n";
175 "# entry syntax (one per line): <domain> <type> <permission>\n\n" +
176 "# example: google.com popup allow\n\n" +
178 word_wrap("The <domain> must be a valid domain name. Depending on the <type>, only exact " +
179 "matches may be used, or alternatively it may match any sub-domain if a more " +
180 "specific entry is not found.", 80, "# ") + "\n" +
181 "# The possible values for the permission <type> include:\n";
182 for (let type
in permission_types
) {
183 let data
= permission_types
[type
];
184 file_buf
+= "# " + type
+ " - " + data
.desc
+ "\n\n";
185 file_buf
+= "# Supported <permission> values:\n";
186 for (let i
= 0; i
< data
.values
.length
; ++i
) {
187 let x
= data
.values
[i
];
188 file_buf
+= "# " + x
[0] + " (" + x
[1] + ")";
190 file_buf
+= " - " + x
[3];
193 if (data
.related_prefs
&& data
.related_prefs
.length
> 0) {
194 file_buf
+= "\n# Related Mozilla preferences:\n"
195 for (let i
= 0; i
< data
.related_prefs
.length
; ++i
) {
196 let x
= data
.related_prefs
[i
];
197 file_buf
+= "# " + x
[0] + " = " + get_pref(x
[0]) + "\n";
199 file_buf
+= word_wrap(x
[1], 80, "# ", "#") + "\n";
206 file
= get_temporary_file("permissions.txt");
211 write_text_file(file
, file_buf
);
212 yield open_file_with_external_editor(file
, $line
= line
);
214 let new_buf
= read_text_file(file
);
215 if (new_buf
== file_buf
) {
216 I
.minibuffer
.message("No permission changes made.");
221 let lines
= new_buf
.split("\n");
222 if (lines
[lines
.length
- 1].length
== 0) // Remove extra line at end
223 lines
.length
= lines
.length
- 1;
225 let prev_entries
= new string_hashset();
226 for (let i
= 0; i
< lines
.length
; ++i
) {
227 // Parse each line, checking for syntax errors
229 let idx
= x
.indexOf('#');
231 x
= x
.substr(0, idx
);
232 let parts
= x
.split(/\s+/);
233 if (parts
.length
== 1 && parts
[0].length
== 0)
234 continue; // ignore blank line
237 if (!/[a-zA-Z0-9-_]+(\.[a-zA-Z0-9-_]+)*/.test(host
))
238 throw "invalid host name: " + host
;
239 if (parts
.length
< 2)
240 throw "missing permission type";
242 if (parts
.length
< 3)
243 throw "missing permission value";
245 if (permission_types
.hasOwnProperty(type
)) {
246 let values
= permission_types
[type
].values
;
247 for (let i
= 0; i
< values
.length
; ++i
) {
248 if (cap
== values
[i
][0]) {
254 if (!/([0-9]+)/.test(cap
))
255 throw "invalid permission value: " + cap
;
257 if (parts
.length
> 3)
258 throw "too many terms";
259 if (prev_entries
.contains([host
,type
]))
260 throw "duplicate entry";
261 prev_entries
.add([host
,type
]);
262 arr
.push([host
,type
,cap
]);
263 } catch (syntax_err
) {
265 lines
.splice(i
+1, 0, "# ERROR on previous line: " + syntax_err
, "");
266 file_buf
= lines
.join("\n") + "\n";
267 I
.minibuffer
.message("Correct the syntax error in the permissions list, " +
268 "or close the editor to abort.");
274 for (let i
= 0; i
< arr
.length
; ++i
) {
275 let [host
,type
,cap
] = arr
[i
];
276 let x
= existing_perms
.get([host
,type
]);
278 if (x
=== undefined) {
286 existing_perms
.remove([host
,type
]);
289 permission_manager
.add(make_uri("http://" + host
), type
, cap
);
292 for (let k
in existing_perms
.iterator(true)) {
293 let [host
,type
] = k
.split(",",2);
295 permission_manager
.remove(host
,type
);
298 if (num_added
== 0 && num_changed
== 0 && num_removed
== 0)
299 msg
= "No permission changes made.";
301 msg
= "Updated permissions list: " +
302 [["added", num_added
],
303 ["changed", num_changed
],
304 ["removed", num_removed
]].
305 filter(function ([caption
, count
]) count
> 0).
306 map(function ([caption
, count
]) {
308 return "1 entry " + caption
;
309 return count
+ " entries " + caption
;
313 I
.minibuffer
.message(msg
);
318 throw interactive_error("Failed to edit permissions list in external editor.");