netwerk/protocol/http/UserAgentOverrides.jsm

branch
TOR_BUG_9701
changeset 11
deefc01c0e14
equal deleted inserted replaced
-1:000000000000 0:84af3a1c59fe
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 "use strict";
6
7 this.EXPORTED_SYMBOLS = [ "UserAgentOverrides" ];
8
9 const Ci = Components.interfaces;
10 const Cc = Components.classes;
11
12 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
13 Components.utils.import("resource://gre/modules/Services.jsm");
14 Components.utils.import("resource://gre/modules/UserAgentUpdates.jsm");
15
16 const OVERRIDE_MESSAGE = "Useragent:GetOverride";
17 const PREF_OVERRIDES_ENABLED = "general.useragent.site_specific_overrides";
18 const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"]
19 .getService(Ci.nsIHttpProtocolHandler)
20 .userAgent;
21 const MAX_OVERRIDE_FOR_HOST_CACHE_SIZE = 250;
22
23 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
24 "@mozilla.org/parentprocessmessagemanager;1",
25 "nsIMessageListenerManager"); // Might have to make this broadcast?
26
27 var gPrefBranch;
28 var gOverrides = new Map;
29 var gUpdatedOverrides;
30 var gOverrideForHostCache = new Map;
31 var gInitialized = false;
32 var gOverrideFunctions = [
33 function (aHttpChannel) UserAgentOverrides.getOverrideForURI(aHttpChannel.URI)
34 ];
35 var gBuiltUAs = new Map;
36
37 this.UserAgentOverrides = {
38 init: function uao_init() {
39 if (gInitialized)
40 return;
41
42 gPrefBranch = Services.prefs.getBranch("general.useragent.override.");
43 gPrefBranch.addObserver("", buildOverrides, false);
44
45 ppmm.addMessageListener(OVERRIDE_MESSAGE, this);
46 Services.prefs.addObserver(PREF_OVERRIDES_ENABLED, buildOverrides, false);
47
48 try {
49 Services.obs.addObserver(HTTP_on_modify_request, "http-on-modify-request", false);
50 } catch (x) {
51 // The http-on-modify-request notification is disallowed in content processes.
52 }
53
54 UserAgentUpdates.init(function(overrides) {
55 gOverrideForHostCache.clear();
56 if (overrides) {
57 for (let domain in overrides) {
58 overrides[domain] = getUserAgentFromOverride(overrides[domain]);
59 }
60 overrides.get = function(key) this[key];
61 }
62 gUpdatedOverrides = overrides;
63 });
64
65 buildOverrides();
66 gInitialized = true;
67 },
68
69 addComplexOverride: function uao_addComplexOverride(callback) {
70 // Add to front of array so complex overrides have precedence
71 gOverrideFunctions.unshift(callback);
72 },
73
74 getOverrideForURI: function uao_getOverrideForURI(aURI) {
75 let host = aURI.asciiHost;
76 if (!gInitialized ||
77 (!gOverrides.size && !gUpdatedOverrides) ||
78 !(host)) {
79 return null;
80 }
81
82 let override = gOverrideForHostCache.get(host);
83 if (override !== undefined)
84 return override;
85
86 function findOverride(overrides) {
87 let searchHost = host;
88 let userAgent = overrides.get(searchHost);
89
90 while (!userAgent) {
91 let dot = searchHost.indexOf('.');
92 if (dot === -1) {
93 return null;
94 }
95 searchHost = searchHost.slice(dot + 1);
96 userAgent = overrides.get(searchHost);
97 }
98 return userAgent;
99 }
100
101 override = (gOverrides.size && findOverride(gOverrides))
102 || (gUpdatedOverrides && findOverride(gUpdatedOverrides));
103
104 if (gOverrideForHostCache.size >= MAX_OVERRIDE_FOR_HOST_CACHE_SIZE) {
105 gOverrideForHostCache.clear();
106 }
107 gOverrideForHostCache.set(host, override);
108
109 return override;
110 },
111
112 uninit: function uao_uninit() {
113 if (!gInitialized)
114 return;
115 gInitialized = false;
116
117 gPrefBranch.removeObserver("", buildOverrides);
118
119 Services.prefs.removeObserver(PREF_OVERRIDES_ENABLED, buildOverrides);
120
121 Services.obs.removeObserver(HTTP_on_modify_request, "http-on-modify-request");
122 },
123
124 receiveMessage: function(aMessage) {
125 let name = aMessage.name;
126 switch (name) {
127 case OVERRIDE_MESSAGE:
128 let uri = aMessage.data.uri;
129 return this.getOverrideForURI(uri);
130 default:
131 throw("Wrong Message in UserAgentOverride: " + name);
132 }
133 }
134 };
135
136 function getUserAgentFromOverride(override)
137 {
138 let userAgent = gBuiltUAs.get(override);
139 if (userAgent !== undefined) {
140 return userAgent;
141 }
142 let [search, replace] = override.split("#", 2);
143 if (search && replace) {
144 userAgent = DEFAULT_UA.replace(new RegExp(search, "g"), replace);
145 } else {
146 userAgent = override;
147 }
148 gBuiltUAs.set(override, userAgent);
149 return userAgent;
150 }
151
152 function buildOverrides() {
153 gOverrides.clear();
154 gOverrideForHostCache.clear();
155
156 if (!Services.prefs.getBoolPref(PREF_OVERRIDES_ENABLED))
157 return;
158
159 let builtUAs = new Map;
160 let domains = gPrefBranch.getChildList("");
161
162 for (let domain of domains) {
163 let override = gPrefBranch.getCharPref(domain);
164 let userAgent = getUserAgentFromOverride(override);
165
166 if (userAgent != DEFAULT_UA) {
167 gOverrides.set(domain, userAgent);
168 }
169 }
170 }
171
172 function HTTP_on_modify_request(aSubject, aTopic, aData) {
173 let channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
174
175 for (let callback of gOverrideFunctions) {
176 let modifiedUA = callback(channel, DEFAULT_UA);
177 if (modifiedUA) {
178 channel.setRequestHeader("User-Agent", modifiedUA, false);
179 return;
180 }
181 }
182 }

mercurial