Fix for link prefetching. Possible fix for weird wikipedia
[https-everywhere/mikeperry.git] / src / components / https-everywhere.js
blobda1ea39288a10f07d859d94be31bba6ed9533fca
1 // LOG LEVELS ---
3 VERB=1;
4 DBUG=2;
5 INFO=3;
6 NOTE=4;
7 WARN=5;
9 //---------------
11 https_everywhere_blacklist = {};
14 const CI = Components.interfaces;
15 const CC = Components.classes;
16 const CU = Components.utils;
17 const CR = Components.results;
19 const CP_SHOULDPROCESS = 4;
21 const SERVICE_CTRID = "@eff.org/https-everywhere;1";
22 const SERVICE_ID=Components.ID("{32c165b4-fe5e-4964-9250-603c410631b4}");
24 const IOS = CC["@mozilla.org/network/io-service;1"].getService(CI.nsIIOService);
25 const OS = CC['@mozilla.org/observer-service;1'].getService(CI.nsIObserverService);
26 const LOADER = CC["@mozilla.org/moz/jssubscript-loader;1"].getService(CI.mozIJSSubScriptLoader);
27 const _INCLUDED = {};
29 // NoScript uses this blob to include js constructs that stored in the chrome/
30 // directory, but are not attached to the Firefox UI (normally, js located
31 // there is attached to an Overlay and therefore is part of the UI).
33 // Reasons for this: things in components/ directory cannot be split into
34 // separate files; things in chrome/ can be
36 const INCLUDE = function(name) {
37 if (arguments.length > 1)
38 for (var j = 0, len = arguments.length; j < len; j++)
39 arguments.callee(arguments[j]);
40 else if (!_INCLUDED[name]) {
41 try {
42 LOADER.loadSubScript("chrome://https-everywhere/content/code/"
43 + name + ".js");
44 _INCLUDED[name] = true;
45 } catch(e) {
46 dump("INCLUDE " + name + ": " + e + "\n");
51 const WP_STATE_START = CI.nsIWebProgressListener.STATE_START;
52 const WP_STATE_STOP = CI.nsIWebProgressListener.STATE_STOP;
53 const WP_STATE_DOC = CI.nsIWebProgressListener.STATE_IS_DOCUMENT;
54 const WP_STATE_START_DOC = WP_STATE_START | WP_STATE_DOC;
55 const WP_STATE_RESTORING = CI.nsIWebProgressListener.STATE_RESTORING;
57 const LF_VALIDATE_ALWAYS = CI.nsIRequest.VALIDATE_ALWAYS;
58 const LF_LOAD_BYPASS_ALL_CACHES = CI.nsIRequest.LOAD_BYPASS_CACHE | CI.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE;
60 const NS_OK = 0;
61 const NS_BINDING_ABORTED = 0x804b0002;
62 const NS_BINDING_REDIRECTED = 0x804b0003;
63 const NS_ERROR_UNKNOWN_HOST = 0x804b001e;
64 const NS_ERROR_REDIRECT_LOOP = 0x804b001f;
65 const NS_ERROR_CONNECTION_REFUSED = 0x804b000e;
66 const NS_ERROR_NOT_AVAILABLE = 0x804b0111;
68 const LOG_CONTENT_BLOCK = 1;
69 const LOG_CONTENT_CALL = 2;
70 const LOG_CONTENT_INTERCEPT = 4;
71 const LOG_CHROME_WIN = 8;
72 const LOG_XSS_FILTER = 16;
73 const LOG_INJECTION_CHECK = 32;
74 const LOG_DOM = 64;
75 const LOG_JS = 128;
76 const LOG_LEAKS = 1024;
77 const LOG_SNIFF = 2048;
78 const LOG_CLEARCLICK = 4096;
79 const LOG_ABE = 8192;
81 const HTML_NS = "http://www.w3.org/1999/xhtml";
83 const WHERE_UNTRUSTED = 1;
84 const WHERE_TRUSTED = 2;
85 const ANYWHERE = 3;
87 const DUMMYOBJ = {};
89 const EARLY_VERSION_CHECK = !("nsISessionStore" in CI && typeof(/ /) === "object");
94 function xpcom_generateQI(iids) {
95 var checks = [];
96 for each (var iid in iids) {
97 checks.push("CI." + iid.name + ".equals(iid)");
99 var src = checks.length
100 ? "if (" + checks.join(" || ") + ") return this;\n"
101 : "";
102 return new Function("iid", src + "throw Components.results.NS_ERROR_NO_INTERFACE;");
105 function xpcom_checkInterfaces(iid,iids,ex) {
106 for (var j = iids.length; j-- >0;) {
107 if (iid.equals(iids[j])) return true;
109 throw ex;
112 INCLUDE('IOUtil', 'HTTPSRules', 'HTTPS', 'Thread');
114 function https_everywhereLog(level, str) {
115 if (level >= WARN) {
116 dump(str+"\n");
117 var econsole = Components.classes["@mozilla.org/consoleservice;1"]
118 .getService(Components.interfaces.nsIConsoleService);
119 econsole.logStringMessage("HTTPS Everywhere: " +str);
123 function HTTPSEverywhere() {
124 // Hacks to set up logging in each component
125 HTTPS.log = https_everywhereLog;
126 HTTPSRules.log = https_everywhereLog;
127 RuleWriter.log = https_everywhereLog;
128 this.log = https_everywhereLog;
129 this.wrappedJSObject = this;
130 this.https_rules = HTTPSRules;
132 // We need to use observers instead of categories for FF3.0 for these:
133 // https://developer.mozilla.org/en/Observer_Notifications
134 // https://developer.mozilla.org/en/nsIObserverService.
135 // https://developer.mozilla.org/en/nsIObserver
136 var obsService = CC["@mozilla.org/observer-service;1"]
137 .getService(Components.interfaces.nsIObserverService);
138 obsService.addObserver(this, "profile-before-change", false);
139 obsService.addObserver(this, "profile-after-change", false);
140 return;
143 // This defines for Mozilla what stuff HTTPSEverywhere will implement.
145 // We need to use both ContentPolicy and Observer, because there are some
146 // things, such as Favicons, who don't get caught by ContentPolicy; we don't
147 // yet know why we don't just use the observer :/
149 // ChannelEventSink seems to be necessary in order to handle redirects (eg
150 // HTTP redirects) correctly.
152 HTTPSEverywhere.prototype = {
153 QueryInterface: function(iid) {
154 if (!iid.equals(CI.nsIObserver)
155 && !iid.equals(CI.nsISupports)
156 && !iid.equals(CI.nsIContentPolicy)
157 && !iid.equals(CI.nsISupportsWeakReference)
158 && !iid.equals(CI.nsIWebProgressListener)
159 && !iid.equals(CI.nsIChannelEventSink)) {
160 Components.returnCode = CR.NS_ERROR_NO_INTERFACE;
161 this.log(INFO,"Bad QI: "+iid);
162 return null;
164 this.log(VERB,"Good QI: "+iid);
165 return this;
167 wrappedJSObject: null, // Initialized by constructor
169 getWeakReference: function () {
170 return Components.utils.getWeakReference(this);
173 // This function is registered solely to detect favicon loads by virtue
174 // of their failure to pass through this function.
175 onStateChange: function(wp, req, stateFlags, status) {
176 if (stateFlags & WP_STATE_START) {
177 if (req instanceof CI.nsIChannel) {
178 if (req instanceof CI.nsIHttpChannel) {
179 PolicyState.attach(req);
185 observe: function(subject, topic, data) {
186 // Top level glue for the nsIObserver API
187 var channel = subject;
188 this.log(VERB,"Got observer topic: "+topic);
190 if (topic == "http-on-modify-request") {
191 if (!(channel instanceof CI.nsIHttpChannel)) return;
192 this.log(DBUG,"Got http-on-modify-request: "+channel.URI.spec);
193 if (channel.URI.spec in https_everywhere_blacklist) {
194 this.log(DBUG, "Avoiding blacklisted " + channel.URI.spec);
195 return;
197 HTTPS.forceChannel(channel);
198 } else if (topic == "app-startup") {
199 this.log(DBUG,"Got app-startup");
200 OS.addObserver(this, "http-on-modify-request", false);
201 var dls = CC['@mozilla.org/docloaderservice;1']
202 .getService(CI.nsIWebProgress);
203 dls.addProgressListener(this, CI.nsIWebProgress.NOTIFY_STATE_REQUEST);
204 this.log(INFO,"ChannelReplacement.supported = "+ChannelReplacement.supported);
205 } else if (topic == "profile-before-change") {
206 this.log(INFO, "Got profile-before-change");
207 var catman = Components.classes["@mozilla.org/categorymanager;1"]
208 .getService(Components.interfaces.nsICategoryManager);
209 catman.deleteCategoryEntry("net-channel-event-sinks", SERVICE_CTRID, true);
210 Thread.hostRunning = false;
211 } else if (topic == "profile-after-change") {
212 // This is currently separate from app-startup for hackish historical
213 // reasons; not sure if that's necessary.
214 this.log(DBUG, "Got profile-after-change");
215 HTTPSRules.init();
216 Thread.hostRunning = true;
217 var catman = Components.classes["@mozilla.org/categorymanager;1"]
218 .getService(Components.interfaces.nsICategoryManager);
219 // hook on redirections (non persistent, otherwise crashes on 1.8.x)
220 catman.addCategoryEntry("net-channel-event-sinks", SERVICE_CTRID,
221 SERVICE_CTRID, false, true);
223 return;
226 // nsIChannelEventSink implementation
227 onChannelRedirect: function(oldChannel, newChannel, flags) {
228 const uri = newChannel.URI;
229 this.log(DBUG,"Got onChannelRedirect.");
230 if (!(newChannel instanceof CI.nsIHttpChannel)) {
231 this.log(DBUG, newChannel + " is not an instance of nsIHttpChannel");
232 return;
235 HTTPS.forceChannel(newChannel);
237 // if (HTTPS.forceURI(uri.clone())) {
238 // if (!HTTPS.replaceChannel(newChannel)) {
239 // // Failed, try to put things back...
240 // this.log(DBUG, "reverting URI, " + oldChannel.URI.spec);
241 // try {
242 // oldChannel.URI.scheme = "http";
243 // newChannel.URI.scheme = "http";
244 // } catch (e) {
245 // this.log(WARN, "uri windback error " + e);
246 // }
247 // }
248 // }
251 // These implement the nsIContentPolicy API; they allow both yes/no answers
252 // to "should this load?", but also allow us to change the thing.
254 shouldLoad: function(aContentType, aContentLocation, aRequestOrigin, aContext, aMimeTypeGuess, aInternalCall) {
255 if (aContentType == 11)
256 this.log(DBUG, "shouldLoad: "+aContentLocation.spec);
257 var unwrappedLocation = IOUtil.unwrapURL(aContentLocation);
258 var scheme = unwrappedLocation.scheme;
259 var isHTTP = /^https?$/.test(scheme); // s? -> either http or https
260 if (isHTTP)
261 HTTPS.forceURI(aContentLocation, null, aContext);
262 return true;
265 shouldProcess: function(aContentType, aContentLocation, aRequestOrigin, aContext, aMimeType, aExtra) {
266 return this.shouldLoad(aContentType, aContentLocation, aRequestOrigin, aContext, aMimeType, CP_SHOULDPROCESS);
269 get_prefs: function() {
270 // get our preferences branch object
271 // FIXME: Ugly hack stolen from https
272 var branch_name = "extensions.https_everywhere.";
273 var o_prefs = false;
274 var o_branch = false;
276 this.log(1, "called get_prefbranch()");
277 o_prefs = Components.classes["@mozilla.org/preferences-service;1"]
278 .getService(Components.interfaces.nsIPrefService);
279 if (!o_prefs)
281 this.log(WARN, "Failed to get preferences-service!");
282 return false;
285 o_branch = o_prefs.getBranch(branch_name);
286 if (!o_branch)
288 this.log(WARN, "Failed to get prefs branch!");
289 return false;
292 return o_branch;
298 * Factory object
301 var HTTPSInstance = null;
303 const factory = {
304 // nsIFactory interface implementation
305 createInstance: function(outer, iid) {
306 if (outer != null) {
307 Components.returnCode = CR.NS_ERROR_NO_AGGREGATION;
308 return null;
311 if (!iid.equals(Components.interfaces.nsIContentPolicy) &&
312 !iid.equals(Components.interfaces.nsIChannelEventSink) &&
313 !iid.equals(Components.interfaces.nsISupports)) {
314 Components.returnCode = CR.NS_ERROR_NO_INTERFACE;
315 return null;
318 if(!HTTPSInstance)
319 HTTPSInstance = new HTTPSEverywhere();
321 return HTTPSInstance;
324 // nsISupports interface implementation
325 QueryInterface: function(iid) {
326 if (iid.equals(Components.interfaces.nsISupports) ||
327 iid.equals(Components.interfaces.nsIModule) ||
328 iid.equals(Components.interfaces.nsIFactory))
329 return this;
331 Components.returnCode = CR.NS_ERROR_NO_INTERFACE;
332 return null;
338 * Module object
340 const module = {
341 registerSelf: function(compMgr, fileSpec, location, type) {
342 compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
343 compMgr.registerFactoryLocation(SERVICE_ID,
344 "HTTPS-Everywhere",
345 SERVICE_CTRID,
346 fileSpec, location, type);
348 var catman = Components.classes["@mozilla.org/categorymanager;1"]
349 .getService(Components.interfaces.nsICategoryManager);
350 catman.addCategoryEntry("app-startup", SERVICE_CTRID,
351 SERVICE_CTRID, true, true);
352 catman.addCategoryEntry("content-policy", SERVICE_CTRID,
353 SERVICE_CTRID, true, true);
357 unregisterSelf: function(compMgr, fileSpec, location) {
358 compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
360 compMgr.unregisterFactoryLocation(SERVICE_ID, fileSpec);
362 var catman = Components.classes["@mozilla.org/categorymanager;1"]
363 .getService(Components.interfaces.nsICategoryManager);
364 catman.deleteCategoryEntry("app-startup", SERVICE_CTRID, true);
365 catman.deleteCategoryEntry("content-policy", SERVICE_CTRID, true);
368 getClassObject: function(compMgr, cid, iid) {
369 if (cid.equals(SERVICE_ID))
370 return factory;
372 Components.returnCode = CR.NS_ERROR_NOT_REGISTERED;
373 return null;
376 canUnload: function(compMgr) {
377 return true;
381 function NSGetModule(comMgr, fileSpec) {
382 return module;