forked from EFForg/https-everywhere
-
Notifications
You must be signed in to change notification settings - Fork 4
/
toolbar_button.js
441 lines (385 loc) · 13.8 KB
/
toolbar_button.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
window.addEventListener("load", https_everywhere_load, true);
window.addEventListener("load", function load(event) {
// need to wrap migratePreferences in another callback so that notification
// always displays on browser restart
window.removeEventListener("load", load, false);
if (gBrowser) {
gBrowser.addEventListener("DOMContentLoaded",
migratePreferences.bind(null, gBrowser),
true);
}
}, false);
const CI = Components.interfaces;
const CC = Components.classes;
// LOG LEVELS ---
let VERB=1;
let DBUG=2;
let INFO=3;
let NOTE=4;
let WARN=5;
let HTTPSEverywhere = CC["@eff.org/https-everywhere;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
// avoid polluting global namespace
// see: https://developer.mozilla.org/en-US/docs/Security_best_practices_in_extensions#Code_wrapping
if (!httpsEverywhere) { var httpsEverywhere = {}; }
/**
* JS Object that acts as a namespace for the toolbar.
*
* Used to display toolbar hints to new users and change toolbar UI for cases
* such as when the toolbar is disabled.
*/
httpsEverywhere.toolbarButton = {
/**
* Name of preference for determining whether to show ruleset counter.
*/
COUNTER_PREF: "extensions.https_everywhere.show_counter",
/**
* Name of preference for whether HTTP Nowhere is on.
*/
HTTP_NOWHERE_PREF: "extensions.https_everywhere.http_nowhere.enabled",
/**
* Used to determine if a hint has been previously shown.
* TODO: Probably extraneous, look into removing
*/
hintShown: false,
/**
* Initialize the toolbar button used to hint new users and update UI on
* certain events.
*/
init: function() {
HTTPSEverywhere.log(DBUG, 'Removing listener for toolbarButton init.');
window.removeEventListener('load', httpsEverywhere.toolbarButton.init, false);
var tb = httpsEverywhere.toolbarButton;
// make sure the checkbox for showing counter is properly set
var showCounter = tb.shouldShowCounter();
var counterItem = document.getElementById('https-everywhere-counter-item');
if (counterItem) {
counterItem.setAttribute('checked', showCounter ? 'true' : 'false');
}
// make sure UI for HTTP Nowhere mode is properly set
var httpNowhereItem = document.getElementById('http-nowhere-item');
var showHttpNowhere = tb.shouldShowHttpNowhere();
var toolbarbutton = document.getElementById('https-everywhere-button');
if (httpNowhereItem) {
httpNowhereItem.setAttribute('checked', showHttpNowhere ? 'true' : 'false');
}
if (toolbarbutton) {
toolbarbutton.setAttribute('http_nowhere',
showHttpNowhere ? 'true' : 'false');
}
// Make icon state match current status and tab.
tb.updateIconState();
// There is no gBrowser object on Android. Instead Android uses the
// window.BrowserApp object:
// https://developer.mozilla.org/en-US/Add-ons/Firefox_for_Android/API/BrowserApp
if (gBrowser) {
gBrowser.tabContainer.addEventListener(
'TabSelect',
tb.updateIconState,
false
);
// add listener for top-level location change across all tabs
let httpseProgressListener = {
onLocationChange: function(aBrowser, aWebProgress, aReq, aLoc) {
HTTPSEverywhere.log(DBUG, "Got on location change!");
HTTPSEverywhere.resetApplicableList(aBrowser);
},
onStateChange: function(aBrowser, aWebProgress, aReq, aFlags, aStatus) {
if ((gBrowser.selectedBrowser === aBrowser) &&
(aFlags & CI.nsIWebProgressListener.STATE_STOP) &&
aWebProgress.isTopLevel) {
HTTPSEverywhere.log(DBUG, "Got on state change");
tb.updateIconState();
}
}
};
gBrowser.addTabsProgressListener(httpseProgressListener);
}
// decide whether to show toolbar hint
let hintPref = "extensions.https_everywhere.toolbar_hint_shown";
if (!Services.prefs.getPrefType(hintPref)
|| !Services.prefs.getBoolPref(hintPref)) {
// only run once
Services.prefs.setBoolPref(hintPref, true);
// gBrowser unavailable on Android, see above.
if (gBrowser) {
gBrowser.addEventListener("DOMContentLoaded",
tb.handleShowHint.bind(null, gBrowser),
true);
}
}
},
/**
* Shows toolbar hint if previously not shown.
*/
handleShowHint: function(gBrowser) {
var tb = httpsEverywhere.toolbarButton;
if (!tb.hintShown){
tb.hintShown = true;
const faqURL = "https://www.eff.org/https-everywhere/faq";
var nBox = gBrowser.getNotificationBox();
var strings = document.getElementById('HttpsEverywhereStrings');
var msg = strings.getString('https-everywhere.toolbar.hint');
var hint = nBox.appendNotification(
msg,
'https-everywhere',
'chrome://https-everywhere/skin/icon-active-24.png',
nBox.PRIORITY_WARNING_MEDIUM,
[
{ accessKey: 'F',
callback: function(ntf, btn) {
// see https://developer.mozilla.org/en-US/docs/XUL/Method/appendNotification#Notification_box_events
gBrowser.selectedTab = gBrowser.addTab(faqURL);
},
label: 'FAQ…',
}
]);
}
gBrowser.removeEventListener("DOMContentLoaded", tb.handleShowHint, true);
},
selectedBrowser: function() {
// gBrowser is unavailable on Android, see above.
if (window.gBrowser) {
return window.gBrowser.selectedBrowser;
} else if (window.BrowserApp) {
return window.BrowserApp.selectedBrowser;
}
},
/**
* Update the rulesets applied counter for the current tab.
*/
updateIconState: function() {
var toolbarbutton = document.getElementById('https-everywhere-button');
if (!toolbarbutton) {
return;
}
var enabled = HTTPSEverywhere.prefs.getBoolPref("globalEnabled");
var blocking = httpsEverywhere.toolbarButton.shouldShowHttpNowhere();
var showCounter = httpsEverywhere.toolbarButton.shouldShowCounter();
var browser = httpsEverywhere.toolbarButton.selectedBrowser();
if (!browser) {
return;
}
var alist = HTTPSEverywhere.getExpando(browser,"applicable_rules");
if (!alist) {
return;
}
// Make sure the list is up to date
alist.populate_list();
var counter = 0;
for (var x in alist.active) {
if (!(x in alist.breaking)) {
++counter;
}
}
for (var x in alist.moot) {
if (!(x in alist.active)) {
++counter;
}
}
// inactive: extension is enabled, but no rules were triggered on this page.
// active: extension is enabled and rewrote URLs on this page.
// blocking: extension is in "block all HTTP requests" mode.
// disabled: extension is disabled.
var iconState = 'inactive';
if (!enabled) {
iconState = 'disabled';
} else if (blocking) {
iconState = 'blocking';
} else if (counter) {
iconState = 'active';
}
toolbarbutton.setAttribute('state', iconState);
if (!showCounter) {
toolbarbutton.setAttribute('rulesetsApplied', 0);
} else {
toolbarbutton.setAttribute('rulesetsApplied', counter);
}
HTTPSEverywhere.log(INFO, 'Setting icon state to: ' + iconState + '[' + counter + ']');
},
/**
* Gets whether to show the rulesets applied counter.
*
* @return {boolean}
*/
shouldShowCounter: function() {
var tb = httpsEverywhere.toolbarButton;
var sp = Services.prefs;
var prefExists = sp.getPrefType(tb.COUNTER_PREF);
// the default behavior is to hide the rulesets applied counter.
return prefExists && sp.getBoolPref(tb.COUNTER_PREF);
},
/**
* Gets whether to show HTTP Nowhere UI.
*
* @return {boolean}
*/
shouldShowHttpNowhere: function() {
var tb = httpsEverywhere.toolbarButton;
var sp = Services.prefs;
return sp.getBoolPref(tb.HTTP_NOWHERE_PREF);
},
/**
* Toggles the user's preference for displaying the rulesets applied counter
* and updates the UI.
*/
toggleShowCounter: function() {
var tb = httpsEverywhere.toolbarButton;
var sp = Services.prefs;
var showCounter = tb.shouldShowCounter();
sp.setBoolPref(tb.COUNTER_PREF, !showCounter);
tb.updateIconState();
},
/**
* Toggles whether HTTP Nowhere mode is active, updates the toolbar icon.
*/
toggleHttpNowhere: function() {
HTTPSEverywhere.toggleHttpNowhere();
reload_window();
},
/**
* Resets all rules to their default state.
*/
resetToDefaults: function() {
HTTPSEverywhere.https_rules.resetRulesetsToDefaults()
}
};
function https_everywhere_load() {
window.removeEventListener('load', https_everywhere_load, true);
// on first run, put the context menu in the addons bar
try {
var first_run;
try {
first_run = Services.prefs.getBoolPref("extensions.https_everywhere.firstrun_context_menu");
} catch(e) {
Services.prefs.setBoolPref("extensions.https_everywhere.firstrun_context_menu", true);
first_run = true;
}
if(first_run) {
Services.prefs.setBoolPref("extensions.https_everywhere.firstrun_context_menu", false);
var navbar = document.getElementById("nav-bar");
if(navbar.currentSet.indexOf("https-everywhere-button") == -1) {
var set = navbar.currentSet+',https-everywhere-button';
navbar.setAttribute('currentset', set);
navbar.currentSet = set;
document.persist('nav-bar', 'currentset');
}
}
} catch(e) { }
}
function stitch_context_menu() {
// the same menu appears both under Tools and via the toolbar button:
var menu = document.getElementById("https-everywhere-menu");
if (!menu.firstChild) {
var popup = document.getElementById("https-everywhere-context");
menu.appendChild(popup.cloneNode(true));
}
}
function stitch_context_menu2() {
// the same menu appears both under Tools and via the toolbar button:
var menu = document.getElementById("https-everywhere-menu2");
if (!menu.firstChild) {
var popup = document.getElementById("https-everywhere-context");
menu.appendChild(popup.cloneNode(true));
}
}
var rulesetTestsMenuItem = null;
function show_applicable_list(menupopup) {
var browser = httpsEverywhere.toolbarButton.selectedBrowser();
if (!browser) {
HTTPSEverywhere.log(WARN, "No browser for applicable list");
return;
}
var alist = HTTPSEverywhere.getExpando(browser,"applicable_rules");
var weird=false;
if (!alist) {
// This case occurs for error pages and similar. We need a dummy alist
// because populate_menu lives in there. Would be good to refactor this
// away.
alist = new HTTPSEverywhere.ApplicableList(HTTPSEverywhere.log, browser.currentURI);
weird = true;
}
alist.populate_menu(document, menupopup, weird);
// should we also show the ruleset tests menu item?
if(HTTPSEverywhere.prefs.getBoolPref("show_ruleset_tests")) {
if(!rulesetTestsMenuItem) {
let strings = document.getElementById('HttpsEverywhereStrings');
let label = strings.getString('https-everywhere.menu.ruleset-tests');
rulesetTestsMenuItem = this.document.createElement('menuitem');
rulesetTestsMenuItem.setAttribute('command', 'https-everywhere-menuitem-ruleset-tests');
rulesetTestsMenuItem.setAttribute('label', label);
}
if(!menupopup.contains(rulesetTestsMenuItem))
menupopup.appendChild(rulesetTestsMenuItem);
}
}
function toggle_rule(rule_id) {
// toggle the rule state
HTTPSEverywhere.https_rules.rulesetsByID[rule_id].toggle();
reload_window();
}
function reload_window() {
var browser = httpsEverywhere.toolbarButton.selectedBrowser();
if (browser) {
browser.reload();
}
}
function toggleEnabledState(){
HTTPSEverywhere.toggleEnabledState();
reload_window();
toggleEnabledUI();
}
function toggleEnabledUI() {
// Add/remove menu items depending on whether HTTPS-E is enabled
var items = document.querySelectorAll(".hide-on-disable");
var enabled = HTTPSEverywhere.prefs.getBoolPref("globalEnabled");
for (let i = 0; i < items.length; i++) {
items[i].hidden = !enabled;
}
httpsEverywhere.toolbarButton.updateIconState();
}
function open_in_tab(url) {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var recentWindow = wm.getMostRecentWindow("navigator:browser");
recentWindow.delayedOpenTab(url, null, null, null, null);
}
function httpse_chrome_opener(url, prefs) {
HTTPSEverywhere.chrome_opener(url, prefs);
}
// hook event for showing hint
HTTPSEverywhere.log(DBUG, 'Adding listener for toolbarButton init.');
window.addEventListener("load", httpsEverywhere.toolbarButton.init, false);
function migratePreferences(gBrowser) {
gBrowser.removeEventListener("DOMContentLoaded", migratePreferences, true);
let prefs_version = HTTPSEverywhere.prefs.getIntPref("prefs_version");
// first migration loses saved prefs
if(prefs_version == 0) {
try {
// upgrades will have old rules as preferences, such as the EFF rule
let upgrade = false;
let childList = HTTPSEverywhere.prefs.getChildList("", {});
for(let i=0; i<childList.length; i++) {
if(childList[i] == 'EFF') {
upgrade = true;
break;
}
}
if(upgrade) {
let nBox = gBrowser.getNotificationBox();
let strings = document.getElementById('HttpsEverywhereStrings');
let msg = strings.getString('https-everywhere.migration.notification0');
nBox.appendNotification(
msg,
'https-everywhere-migration0',
'chrome://https-everywhere/skin/icon-active-24.png',
nBox.PRIORITY_WARNING_MEDIUM
);
}
} catch(e) {
HTTPSEverywhere.log(WARN, "Migration from prefs_version 0 error: "+e);
}
HTTPSEverywhere.prefs.setIntPref("prefs_version", prefs_version+1);
}
}