This file is indexed.

/usr/share/xul-ext/https-everywhere/components/https-everywhere.js is in xul-ext-https-everywhere 4.0.2-3.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
// LOG LEVELS ---

VERB=1;
DBUG=2;
INFO=3;
NOTE=4;
WARN=5;

// PREFERENCE BRANCHES
let PREFBRANCH_ROOT=0;
let PREFBRANCH_RULE_TOGGLE=1;
let PREFBRANCH_NONE=2;

//---------------

https_domains = {};              // maps domain patterns (with at most one
                                 // wildcard) to RuleSets

https_everywhere_blacklist = {}; // URLs we've given up on rewriting because
                                 // of redirection loops

https_blacklist_domains = {};    // domains for which there is at least one
                                 // blacklisted URL

//
const CI = Components.interfaces;
const CC = Components.classes;
const CU = Components.utils;
const CR = Components.results;
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
const Cr = Components.results;

Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");

const CP_SHOULDPROCESS = 4;

const SERVICE_CTRID = "@eff.org/https-everywhere;1";
const SERVICE_ID=Components.ID("{32c165b4-fe5e-4964-9250-603c410631b4}");
const SERVICE_NAME = "Encrypts your communications with a number of major websites";

const LLVAR = "LogLevel";

const IOS = CC["@mozilla.org/network/io-service;1"].getService(CI.nsIIOService);
const OS = CC['@mozilla.org/observer-service;1'].getService(CI.nsIObserverService);
const LOADER = CC["@mozilla.org/moz/jssubscript-loader;1"].getService(CI.mozIJSSubScriptLoader);
const _INCLUDED = {};

// NoScript uses this blob to include js constructs that stored in the chrome/
// directory, but are not attached to the Firefox UI (normally, js located
// there is attached to an Overlay and therefore is part of the UI).

// Reasons for this: things in components/ directory cannot be split into
// separate files; things in chrome/ can be

const INCLUDE = function(name) {
  if (arguments.length > 1)
    for (var j = 0, len = arguments.length; j < len; j++)
      INCLUDE(arguments[j]);
  else if (!_INCLUDED[name]) {
    // we used to try/catch here, but that was less useful because it didn't
    // produce line numbers for syntax errors
    LOADER.loadSubScript("chrome://https-everywhere/content/code/"
            + name + ".js");
    _INCLUDED[name] = true;
  }
};

const WP_STATE_START = CI.nsIWebProgressListener.STATE_START;
const WP_STATE_STOP = CI.nsIWebProgressListener.STATE_STOP;
const WP_STATE_DOC = CI.nsIWebProgressListener.STATE_IS_DOCUMENT;
const WP_STATE_START_DOC = WP_STATE_START | WP_STATE_DOC;
const WP_STATE_RESTORING = CI.nsIWebProgressListener.STATE_RESTORING;

const LF_VALIDATE_ALWAYS = CI.nsIRequest.VALIDATE_ALWAYS;
const LF_LOAD_BYPASS_ALL_CACHES = CI.nsIRequest.LOAD_BYPASS_CACHE | CI.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE;

const NS_OK = 0;
const NS_BINDING_ABORTED = 0x804b0002;
const NS_BINDING_REDIRECTED = 0x804b0003;
const NS_ERROR_UNKNOWN_HOST = 0x804b001e;
const NS_ERROR_REDIRECT_LOOP = 0x804b001f;
const NS_ERROR_CONNECTION_REFUSED = 0x804b000e;
const NS_ERROR_NOT_AVAILABLE = 0x804b0111;

const LOG_CONTENT_BLOCK = 1;
const LOG_CONTENT_CALL = 2;
const LOG_CONTENT_INTERCEPT = 4;
const LOG_CHROME_WIN = 8;
const LOG_XSS_FILTER = 16;
const LOG_INJECTION_CHECK = 32;
const LOG_DOM = 64;
const LOG_JS = 128;
const LOG_LEAKS = 1024;
const LOG_SNIFF = 2048;
const LOG_CLEARCLICK = 4096;
const LOG_ABE = 8192;

const HTML_NS = "http://www.w3.org/1999/xhtml";

const WHERE_UNTRUSTED = 1;
const WHERE_TRUSTED = 2;
const ANYWHERE = 3;

const N_COHORTS = 1000; 

const DUMMY_OBJ = {};
DUMMY_OBJ.wrappedJSObject = DUMMY_OBJ;
const DUMMY_FUNC = function() {};
const DUMMY_ARRAY = [];

const EARLY_VERSION_CHECK = !("nsISessionStore" in CI && typeof(/ /) === "object");

// This is probably obsolete since the switch to the channel.redirectTo API
const OBSERVER_TOPIC_URI_REWRITE = "https-everywhere-uri-rewrite";

// XXX: Better plan for this?
// We need it to exist to make our updates of ChannelReplacement.js easier.
var ABE = {
  consoleDump: false,
  log: function(str) {
    https_everywhereLog(WARN, str);
  }
};

function xpcom_generateQI(iids) {
  var checks = [];
  for each (var iid in iids) {
    checks.push("CI." + iid.name + ".equals(iid)");
  }
  var src = checks.length
    ? "if (" + checks.join(" || ") + ") return this;\n"
    : "";
  return new Function("iid", src + "throw Components.results.NS_ERROR_NO_INTERFACE;");
}

function xpcom_checkInterfaces(iid,iids,ex) {
  for (var j = iids.length; j-- >0;) {
    if (iid.equals(iids[j])) return true;
  }
  throw ex;
}

INCLUDE('ChannelReplacement', 'IOUtil', 'HTTPSRules', 'HTTPS', 'Thread', 'ApplicableList');

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

// This is black magic for storing Expando data w/ an nsIDOMWindow 
// See http://pastebin.com/qY28Jwbv , 
// https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIControllers

StorageController.prototype = {
  QueryInterface: XPCOMUtils.generateQI(
    [ Components.interfaces.nsISupports,
      Components.interfaces.nsIController ]),
  wrappedJSObject: null,  // Initialized by constructor
  supportsCommand: function (cmd) {return (cmd == this.command);},
  isCommandEnabled: function (cmd) {return (cmd == this.command);},
  onEvent: function(eventName) {return true;},
  doCommand: function() {return true;}
};

function StorageController(command) {
  this.command = command;
  this.data = {};
  this.wrappedJSObject = this;
};

/*var Controller = Class("Controller", XPCOM(CI.nsIController), {
  init: function (command, data) {
      this.command = command;
      this.data = data;
  },
  supportsCommand: function (cmd) cmd === this.command
});*/

function HTTPSEverywhere() {

  // Set up logging in each component:
  HTTPS.log = HTTPSRules.log = RuleWriter.log = this.log = https_everywhereLog;

  this.log = https_everywhereLog;
  this.wrappedJSObject = this;
  this.https_rules = HTTPSRules;
  this.INCLUDE=INCLUDE;
  this.ApplicableList = ApplicableList;
  this.browser_initialised = false; // the browser is completely loaded


  this.prefs = this.get_prefs();
  this.rule_toggle_prefs = this.get_prefs(PREFBRANCH_RULE_TOGGLE);

  this.httpNowhereEnabled = this.prefs.getBoolPref("http_nowhere.enabled");
  this.isMobile = this.doMobileCheck();

  // Disable SSLv3 to prevent POODLE attack.
  // https://www.imperialviolet.org/2014/10/14/poodle.html
  var root_prefs = this.get_prefs(PREFBRANCH_NONE);
  root_prefs.setIntPref("security.tls.version.min", 1);
  
  // We need to use observers instead of categories for FF3.0 for these:
  // https://developer.mozilla.org/en/Observer_Notifications
  // https://developer.mozilla.org/en/nsIObserverService.
  // https://developer.mozilla.org/en/nsIObserver
  // We also use the observer service to let other extensions know about URIs
  // we rewrite.
  this.obsService = CC["@mozilla.org/observer-service;1"]
                    .getService(Components.interfaces.nsIObserverService);
                    
  if (this.prefs.getBoolPref("globalEnabled")) {
    this.obsService.addObserver(this, "profile-before-change", false);
    this.obsService.addObserver(this, "profile-after-change", false);
    this.obsService.addObserver(this, "sessionstore-windows-restored", false);
    this.obsService.addObserver(this, "browser:purge-session-history", false);
  } else {
    // Need this to initialize FF for Android UI even when HTTPS-E is off
    if (this.isMobile) {
      this.obsService.addObserver(this, "sessionstore-windows-restored", false);
    }
  }

  var pref_service = Components.classes["@mozilla.org/preferences-service;1"]
      .getService(Components.interfaces.nsIPrefBranchInternal);
  var branch = pref_service.QueryInterface(Components.interfaces.nsIPrefBranchInternal);

  branch.addObserver("extensions.https_everywhere.enable_mixed_rulesets",
                         this, false);
  branch.addObserver("security.mixed_content.block_active_content",
                         this, false);

  return;
}



/*
In recent versions of Firefox and HTTPS Everywhere, the call stack for performing an HTTP -> HTTPS rewrite looks like this:

1. HTTPSEverywhere.observe() gets a callback with the "http-on-modify-request" topic, and the channel as a subject

    2. HTTPS.replaceChannel() 

       3. HTTPSRules.rewrittenURI() 
            
           4. HTTPSRules.potentiallyApplicableRulesets uses <target host=""> elements to identify relevant rulesets

           foreach RuleSet:

               4. RuleSet.transformURI()

                   5. RuleSet.apply() does the tests and rewrites with RegExps, returning a string

               4. RuleSet.transformURI() makes a new uri object for the destination string, if required

    2. HTTPS.replaceChannel() calls channel.redirectTo() if a redirect is needed


In addition, the following other important tasks happen along the way:

HTTPSEverywhere.observe()    aborts if there is a redirect loop
                             finds a reference to the ApplicableList or alist that represents the toolbar context menu

HTTPS.replaceChannel()       notices redirect loops (and used to do much more complex XPCOM API work in the NoScript-based past)

HTTPSRules.rewrittenURI()    works around weird URI types like about: and http://user:pass@example.com/
                             and notifies the alist of what it should display for each ruleset

*/

// This defines for Mozilla what stuff HTTPSEverywhere will implement.

// ChannelEventSink used to be necessary in order to handle redirects (eg
// HTTP redirects) correctly.  It may now be obsolete? XXX

HTTPSEverywhere.prototype = {
  prefs: null,
  // properties required for XPCOM registration:
  classDescription: SERVICE_NAME,
  classID:          SERVICE_ID,
  contractID:       SERVICE_CTRID,

  _xpcom_factory: {
    createInstance: function (outer, iid) {
      if (outer != null)
        throw Components.results.NS_ERROR_NO_AGGREGATION;
      if (!HTTPSEverywhere.instance)
        HTTPSEverywhere.instance = new HTTPSEverywhere();
      return HTTPSEverywhere.instance.QueryInterface(iid);
    },

    QueryInterface: XPCOMUtils.generateQI(
      [ Components.interfaces.nsISupports,
        Components.interfaces.nsIModule,
        Components.interfaces.nsIFactory ])
  },

  // [optional] an array of categories to register this component in.
  _xpcom_categories: [
    {
      category: "app-startup",
    }
  ],

  // QueryInterface implementation, e.g. using the generateQI helper
  QueryInterface: XPCOMUtils.generateQI(
    [ Components.interfaces.nsIObserver,
      Components.interfaces.nsISupports,
      Components.interfaces.nsISupportsWeakReference,
      Components.interfaces.nsIWebProgressListener,
      Components.interfaces.nsIWebProgressListener2,
      Components.interfaces.nsIChannelEventSink ]),

  wrappedJSObject: null,  // Initialized by constructor

  getWeakReference: function () {
    return Components.utils.getWeakReference(this);
  },

  // An "expando" is an attribute glued onto something.  From NoScript.
  getExpando: function(domWin, key) {
    var c = domWin.controllers.getControllerForCommand("https-everywhere-storage");
    try {
      if (c) {
        c = c.wrappedJSObject;
        //this.log(DBUG, "Found a controller, returning data");
        return c.data[key];
      } else {
        this.log(INFO, "No controller attached to " + domWin);
        return null;
      }
    } catch(e) {
      // Firefox 3.5
      this.log(WARN,"exception in getExpando");
      this.getExpando = this.getExpando_old;
      this.setExpando = this.setExpando_old;
      return this.getExpando_old(domWin, key, null);
    }
  },
  setExpando: function(domWin, key, value) {
    var c = domWin.controllers.getControllerForCommand("https-everywhere-storage");
    try {
      if (!c) {
        this.log(DBUG, "Appending new StorageController for " + domWin);
        c = new StorageController("https-everywhere-storage");
        domWin.controllers.appendController(c);
      } else {
        c = c.wrappedJSObject;
      }
      c.data[key] = value;
    } catch(e) {
      this.log(WARN,"exception in setExpando");
      this.getExpando = this.getExpando_old;
      this.setExpando = this.setExpando_old;
      this.setExpando_old(domWin, key, value);
    }
  },

  // This method is straight out of NoScript... we fall back to it in FF 3.*?
  getExpando_old: function(domWin, key, defValue) {
    var domObject = domWin.document;
    return domObject && domObject.__httpsEStorage && domObject.__httpsEStorage[key] || 
           (defValue ? this.setExpando(domObject, key, defValue) : null);
  },
  setExpando_old: function(domWin, key, value) {
    var domObject = domWin.document;
    if (!domObject) return null;
    if (!domObject.__httpsEStorage) domObject.__httpsEStorage = {};
    if (domObject.__httpsEStorage) domObject.__httpsEStorage[key] = value;
    else this.log(WARN, "Warning: cannot set expando " + key + " to value " + value);
    return value;
  },

  // We use onLocationChange to make a fresh list of rulesets that could have
  // applied to the content in the current page (the "applicable list" is used
  // for the context menu in the UI).  This will be appended to as various
  // content is embedded / requested by JavaScript.
  onLocationChange: function(wp, req, uri) {
    if (wp instanceof CI.nsIWebProgress) {
      if (!this.newApplicableListForDOMWin(wp.DOMWindow)) 
        this.log(WARN,"Something went wrong in onLocationChange");
    } else {
      this.log(WARN,"onLocationChange: no nsIWebProgress");
    }
  },

  getWindowForChannel: function(channel) {
    // Obtain an nsIDOMWindow from a channel
    let loadContext;
    try {
      loadContext = channel.notificationCallbacks.getInterface(CI.nsILoadContext);
    } catch(e) {
      try {
        loadContext = channel.loadGroup.notificationCallbacks.getInterface(CI.nsILoadContext);
      } catch(e) {
        this.log(NOTE, "No loadContext for " + channel.URI.spec);
        return null;
      }
    }

    if (!loadContext) { return null; }

    let domWin = loadContext.associatedWindow;
    if (!domWin) {
      this.log(NOTE, "failed to get DOMWin for " + channel.URI.spec);
      return null;
    }

    domWin = domWin.top;
    return domWin;
  },

  // the lists get made when the urlbar is loading something new, but they
  // need to be appended to with reference only to the channel
  getApplicableListForChannel: function(channel) {
    var domWin = this.getWindowForChannel(channel);
    return this.getApplicableListForDOMWin(domWin, "on-modify-request w " + domWin);
  },

  newApplicableListForDOMWin: function(domWin) {
    if (!domWin || !(domWin instanceof CI.nsIDOMWindow)) {
      this.log(WARN, "Get alist without domWin");
      return null;
    }
    var dw = domWin.top;
    var alist = new ApplicableList(this.log,dw.document,dw);
    this.setExpando(dw,"applicable_rules",alist);
    return alist;
  },

  getApplicableListForDOMWin: function(domWin, where) {
    if (!domWin || !(domWin instanceof CI.nsIDOMWindow)) {
      //this.log(WARN, "Get alist without domWin");
      return null;
    }
    var dw = domWin.top;
    var alist= this.getExpando(dw,"applicable_rules");
    if (alist) {
      //this.log(DBUG,"get AL success in " + where);
      return alist;
    } else {
      //this.log(DBUG, "Making new AL in getApplicableListForDOMWin in " + where);
      alist = new ApplicableList(this.log,dw.document,dw);
      this.setExpando(dw,"applicable_rules",alist);
    }
    return alist;
  },

  observe: function(subject, topic, data) {
    // Top level glue for the nsIObserver API
    var channel = subject;
    //this.log(VERB,"Got observer topic: "+topic);

    if (topic == "http-on-modify-request") {
      if (!(channel instanceof CI.nsIHttpChannel)) return;
      
      this.log(DBUG,"Got http-on-modify-request: "+channel.URI.spec);
      var lst = this.getApplicableListForChannel(channel); // null if no window is associated (ex: xhr)
      if (channel.URI.spec in https_everywhere_blacklist) {
        this.log(DBUG, "Avoiding blacklisted " + channel.URI.spec);
        if (lst) lst.breaking_rule(https_everywhere_blacklist[channel.URI.spec]);
        else        this.log(NOTE,"Failed to indicate breakage in content menu");
        return;
      }
      HTTPS.replaceChannel(lst, channel, this.httpNowhereEnabled);
    } else if (topic == "http-on-examine-response") {
         this.log(DBUG, "Got http-on-examine-response @ "+ (channel.URI ? channel.URI.spec : '') );
         HTTPS.handleSecureCookies(channel);
    } else if (topic == "http-on-examine-merged-response") {
         this.log(DBUG, "Got http-on-examine-merged-response ");
         HTTPS.handleSecureCookies(channel);
    } else if (topic == "cookie-changed") {
      // Javascript can add cookies via document.cookie that are insecure.
      if (data == "added" || data == "changed") {
        // subject can also be an nsIArray! bleh.
        try {
          subject.QueryInterface(CI.nsIArray);
          var elems = subject.enumerate();
          while (elems.hasMoreElements()) {
            var cookie = elems.getNext()
                            .QueryInterface(CI.nsICookie2);
            if (!cookie.isSecure) {
              HTTPS.handleInsecureCookie(cookie);
            }
          }
        } catch(e) {
          subject.QueryInterface(CI.nsICookie2);
          if(!subject.isSecure) {
            HTTPS.handleInsecureCookie(subject);
          }
        }
      }
    } else if (topic == "profile-before-change") {
      this.log(INFO, "Got profile-before-change");
      var catman = Components.classes["@mozilla.org/categorymanager;1"]
           .getService(Components.interfaces.nsICategoryManager);
      catman.deleteCategoryEntry("net-channel-event-sinks", SERVICE_CTRID, true);
      Thread.hostRunning = false;
    } else if (topic == "profile-after-change") {
      this.log(DBUG, "Got profile-after-change");
      
      if(this.prefs.getBoolPref("globalEnabled")){
        OS.addObserver(this, "cookie-changed", false);
        OS.addObserver(this, "http-on-modify-request", false);
        OS.addObserver(this, "http-on-examine-merged-response", false);
        OS.addObserver(this, "http-on-examine-response", false);
        
        var dls = CC['@mozilla.org/docloaderservice;1']
            .getService(CI.nsIWebProgress);
        dls.addProgressListener(this, CI.nsIWebProgress.NOTIFY_LOCATION);
        this.log(INFO,"ChannelReplacement.supported = "+ChannelReplacement.supported);

        HTTPSRules.init();

        Thread.hostRunning = true;
        var catman = Components.classes["@mozilla.org/categorymanager;1"]
           .getService(Components.interfaces.nsICategoryManager);
        // hook on redirections (non persistent, otherwise crashes on 1.8.x)
        catman.addCategoryEntry("net-channel-event-sinks", SERVICE_CTRID,
            SERVICE_CTRID, false, true);
      }
    } else if (topic == "sessionstore-windows-restored") {
      this.log(DBUG,"Got sessionstore-windows-restored");
      if (!this.isMobile) {
        this.maybeShowObservatoryPopup();
      } else {
        this.log(WARN, "Initializing Firefox for Android UI");
        Cu.import("chrome://https-everywhere/content/code/AndroidUI.jsm");
        AndroidUI.init();
      }
      this.browser_initialised = true;
    } else if (topic == "nsPref:changed") {
        // If the user toggles the Mixed Content Blocker settings, reload the rulesets
        // to enable/disable the mixedcontent ones

        // this pref gets set to false and then true during FF 26 startup!
        // so do nothing if we're being notified during startup
        if (!this.browser_initialised)
            return;
        switch (data) {
            case "security.mixed_content.block_active_content":
            case "extensions.https_everywhere.enable_mixed_rulesets":
                var p = CC["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefBranch);
                var val = p.getBoolPref("security.mixed_content.block_active_content");
                this.log(INFO,"nsPref:changed for "+data + " to " + val);
                HTTPSRules.init();
                break;
        }
    } else if (topic == "browser:purge-session-history") {
      // The list of rulesets that have been loaded from the sqlite DB
      // constitutes a parallel history store, so we have to clear it.
      this.log(DBUG, "History cleared, reloading HTTPSRules to avoid information leak.");
      HTTPSRules.init();
    }
    return;
  },

  maybeShowObservatoryPopup: function() {
    // Show the popup at most once.  Users who enabled the Observatory before
    // a version that would have shown it to them, don't need to see it
    // again.
    var ssl_observatory = CC["@eff.org/ssl-observatory;1"]
                      .getService(Components.interfaces.nsISupports)
                      .wrappedJSObject;
    var shown = ssl_observatory.myGetBoolPref("popup_shown");
    var enabled = ssl_observatory.myGetBoolPref("enabled");
    var that = this;
    var obs_popup_callback = function(result) {
      if (result) that.log(INFO, "Got positive proxy test.");
      else        that.log(INFO, "Got negative proxy text.");
      // We are now ready to show the popup in its most informative state
      that.chrome_opener("chrome://https-everywhere/content/observatory-popup.xul");
    };
    if (!shown && !enabled)
      ssl_observatory.registerProxyTestNotification(obs_popup_callback);

    if (shown && enabled)
      this.maybeCleanupObservatoryPrefs(ssl_observatory);
  },

  maybeCleanupObservatoryPrefs: function(ssl_observatory) {
    // Recover from a past UI processing bug that would leave the Obsevatory
    // accidentally disabled for some users
    // https://trac.torproject.org/projects/tor/ticket/10728
    var clean = ssl_observatory.myGetBoolPref("clean_config");
    if (clean) return;

    // unchanged: returns true if a pref has not been modified
    var unchanged = function(p){return !ssl_observatory.prefs.prefHasUserValue("extensions.https_everywhere._observatory."+p)};
    var cleanup_obsprefs_callback = function(tor_avail) {
      // we only run this once
      ssl_observatory.prefs.setBoolPref("extensions.https_everywhere._observatory.clean_config", true);
      if (!tor_avail) {
        // use_custom_proxy is the variable that is often false when it should be true;
        if (!ssl_observatory.myGetBoolPref("use_custom_proxy")) {
           // however don't do anything if any of the prefs have been set by the user
           if (unchanged("alt_roots") && unchanged("self_signed") && unchanged ("send_asn") && unchanged("priv_dns")) {
             ssl_observatory.prefs.setBoolPref("extensions.https_everywhere._observatory.use_custom_proxy", true);
           }
        }
      }
    }
    ssl_observatory.registerProxyTestNotification(cleanup_obsprefs_callback);
  },


  getExperimentalFeatureCohort: function() {
    // This variable is used for gradually turning on features for testing and
    // scalability purposes.  It is a random integer [0,N_COHORTS) generated
    // once and stored thereafter.
    // 
    // This is not currently used/called in the development branch
    var cohort;
    try {
      cohort = this.prefs.getIntPref("experimental_feature_cohort");
    } catch(e) {
      cohort = Math.round(Math.random() * N_COHORTS);
      this.prefs.setIntPref("experimental_feature_cohort", cohort);
    }
    return cohort;
  },

  // nsIChannelEventSink implementation
  // XXX This was here for rewrites in the past.  Do we still need it?
  onChannelRedirect: function(oldChannel, newChannel, flags) {  
    const uri = newChannel.URI;
    this.log(DBUG,"Got onChannelRedirect to "+uri.spec);
    if (!(newChannel instanceof CI.nsIHttpChannel)) {
      this.log(DBUG, newChannel + " is not an instance of nsIHttpChannel");
      return;
    }
    var alist = this.juggleApplicableListsDuringRedirection(oldChannel, newChannel);
    HTTPS.replaceChannel(alist,newChannel, this.httpNowhereEnabled);
  },

  juggleApplicableListsDuringRedirection: function(oldChannel, newChannel) {
    // If the new channel doesn't yet have a list of applicable rulesets, start
    // with the old one because that's probably a better representation of how
    // secure the load process was for this page
    var domWin = this.getWindowForChannel(oldChannel);
    var old_alist = null;
    if (domWin) 
      old_alist = this.getExpando(domWin,"applicable_rules");
    domWin = this.getWindowForChannel(newChannel);
    if (!domWin) return null;
    var new_alist = this.getExpando(domWin,"applicable_rules");
    if (old_alist && !new_alist) {
      new_alist = old_alist;
      this.setExpando(domWin,"applicable_rules",new_alist);
    } else if (!new_alist) {
      new_alist = new ApplicableList(this.log, domWin.document, domWin);
      this.setExpando(domWin,"applicable_rules",new_alist);
    }
    return new_alist;
  },

  asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
        this.onChannelRedirect(oldChannel, newChannel, flags);
        callback.onRedirectVerifyCallback(0);
  },

  get_prefs: function(prefBranch) {
    if(!prefBranch) prefBranch = PREFBRANCH_ROOT;

    // get our preferences branch object
    // FIXME: Ugly hack stolen from https
    var branch_name;
    if(prefBranch === PREFBRANCH_RULE_TOGGLE)
      branch_name = "extensions.https_everywhere.rule_toggle.";
    else if (prefBranch === PREFBRANCH_NONE)
      branch_name = "";
    else
      branch_name = "extensions.https_everywhere.";
    var o_prefs = false;
    var o_branch = false;
    // this function needs to be called from inside https_everywhereLog, so
    // it needs to do its own logging...
    var econsole = Components.classes["@mozilla.org/consoleservice;1"]
      .getService(Components.interfaces.nsIConsoleService);

    o_prefs = Components.classes["@mozilla.org/preferences-service;1"]
                        .getService(Components.interfaces.nsIPrefService);

    if (!o_prefs)
    {
      econsole.logStringMessage("HTTPS Everywhere: Failed to get preferences-service!");
      return false;
    }

    o_branch = o_prefs.getBranch(branch_name);
    if (!o_branch)
    {
      econsole.logStringMessage("HTTPS Everywhere: Failed to get prefs branch!");
      return false;
    }

    if(prefBranch == PREFBRANCH_ROOT) {
      // make sure there's an entry for our log level
      try {
        o_branch.getIntPref(LLVAR);
      } catch (e) {
        econsole.logStringMessage("Creating new about:config https_everywhere.LogLevel variable");
        o_branch.setIntPref(LLVAR, WARN);
      }
    }

    return o_branch;
  },

  // Are we on Firefox for Android?
  doMobileCheck: function() {
    let appInfo = CC["@mozilla.org/xre/app-info;1"].getService(CI.nsIXULAppInfo);
    let ANDROID_ID = "{aa3c5121-dab2-40e2-81ca-7ea25febc110}";
    return (appInfo.ID === ANDROID_ID);
  },

  chrome_opener: function(uri, args) {
    // we don't use window.open, because we need to work around TorButton's 
    // state control
    args = args || 'chrome,centerscreen';
    return CC['@mozilla.org/appshell/window-mediator;1']
      .getService(CI.nsIWindowMediator) 
      .getMostRecentWindow('navigator:browser')
      .open(uri,'', args );
  },

  tab_opener: function(uri) {
    var gb = CC['@mozilla.org/appshell/window-mediator;1']
      .getService(CI.nsIWindowMediator) 
      .getMostRecentWindow('navigator:browser')
      .gBrowser;
    var tab = gb.addTab(uri);
    gb.selectedTab = tab;
    return tab;
  },

  toggleEnabledState: function() {
    if(this.prefs.getBoolPref("globalEnabled")){    
        try{    
            // toggling some of these after startup may be inconsequential...
            // this.obsService.removeObserver(this, "sessionstore-windows-restored");
            this.obsService.removeObserver(this, "profile-before-change");
            this.obsService.removeObserver(this, "profile-after-change");
            OS.removeObserver(this, "cookie-changed");
            OS.removeObserver(this, "http-on-modify-request");
            OS.removeObserver(this, "http-on-examine-merged-response");
            OS.removeObserver(this, "http-on-examine-response");  
            
            var catman = Components.classes["@mozilla.org/categorymanager;1"]
           .getService(Components.interfaces.nsICategoryManager);
            catman.deleteCategoryEntry("net-channel-event-sinks", SERVICE_CTRID, true);
                        
            var dls = CC['@mozilla.org/docloaderservice;1']
            .getService(CI.nsIWebProgress);
            dls.removeProgressListener(this);
            
            this.prefs.setBoolPref("globalEnabled", false);
        }
        catch(e){
            this.log(WARN, "Couldn't remove observers: " + e);          
        }
    }
    else{   
        try{      
            this.obsService.addObserver(this, "profile-before-change", false);
            this.obsService.addObserver(this, "profile-after-change", false);
            // this.obsService.addObserver(this, "sessionstore-windows-restored", false);
            OS.addObserver(this, "cookie-changed", false);
            OS.addObserver(this, "http-on-modify-request", false);
            OS.addObserver(this, "http-on-examine-merged-response", false);
            OS.addObserver(this, "http-on-examine-response", false);  
            
            var dls = CC['@mozilla.org/docloaderservice;1']
            .getService(CI.nsIWebProgress);
            dls.addProgressListener(this, CI.nsIWebProgress.NOTIFY_LOCATION);
            
            this.log(INFO,"ChannelReplacement.supported = "+ChannelReplacement.supported);

            if(!Thread.hostRunning)
                Thread.hostRunning = true;
            
            var catman = Components.classes["@mozilla.org/categorymanager;1"]
            .getService(Components.interfaces.nsICategoryManager);
            // hook on redirections (non persistent, otherwise crashes on 1.8.x)
            catman.addCategoryEntry("net-channel-event-sinks", SERVICE_CTRID,
                SERVICE_CTRID, false, true);            
            
            HTTPSRules.init();          
            this.prefs.setBoolPref("globalEnabled", true);
        }
        catch(e){
            this.log(WARN, "Couldn't add observers: " + e);         
        }
    }
  },

  toggleHttpNowhere: function() {
    let prefService = Services.prefs;
    let thisBranch =
      prefService.getBranch("extensions.https_everywhere.http_nowhere.");
    let securityBranch = prefService.getBranch("security.");

    // Whether cert is treated as invalid when OCSP connection fails
    let OCSP_REQUIRED = "OCSP.require";

    // Branch to save original settings
    let ORIG_OCSP_REQUIRED = "orig.ocsp.required";


    if (thisBranch.getBoolPref("enabled")) {
      // Restore original OCSP settings. TODO: What if user manually edits
      // these while HTTP Nowhere is enabled?
      let origOcspRequired = thisBranch.getBoolPref(ORIG_OCSP_REQUIRED);
      securityBranch.setBoolPref(OCSP_REQUIRED, origOcspRequired);

      thisBranch.setBoolPref("enabled", false);
      this.httpNowhereEnabled = false;
    } else {
      // Save original OCSP settings in HTTP Nowhere preferences branch.
      let origOcspRequired = securityBranch.getBoolPref(OCSP_REQUIRED);
      thisBranch.setBoolPref(ORIG_OCSP_REQUIRED, origOcspRequired);

      // Disable OCSP enforcement
      securityBranch.setBoolPref(OCSP_REQUIRED, false);

      thisBranch.setBoolPref("enabled", true);
      this.httpNowhereEnabled = true;
    }
  }
};

var prefs = 0;
var econsole = 0;
function https_everywhereLog(level, str) {
  if (prefs == 0) {
    prefs = HTTPSEverywhere.instance.get_prefs();
    econsole = Components.classes["@mozilla.org/consoleservice;1"]
               .getService(Components.interfaces.nsIConsoleService);
  } 
  try {
    var threshold = prefs.getIntPref(LLVAR);
  } catch (e) {
    econsole.logStringMessage( "HTTPS Everywhere: Failed to read about:config LogLevel");
    threshold = WARN;
  }
  if (level >= threshold) {
    dump("HTTPS Everywhere: "+str+"\n");
    econsole.logStringMessage("HTTPS Everywhere: " +str);
  }
}

/**
* XPCOMUtils.generateNSGetFactory was introduced in Mozilla 2 (Firefox 4).
* XPCOMUtils.generateNSGetModule is for Mozilla 1.9.2 (Firefox 3.6).
*/
if (XPCOMUtils.generateNSGetFactory)
    var NSGetFactory = XPCOMUtils.generateNSGetFactory([HTTPSEverywhere]);
else
    var NSGetModule = XPCOMUtils.generateNSGetModule([HTTPSEverywhere]);

/* vim: set tabstop=4 expandtab: */