|
1 <!DOCTYPE HTML> |
|
2 <html> |
|
3 <!-- |
|
4 https://bugzilla.mozilla.org/show_bug.cgi?id=897221 |
|
5 --> |
|
6 <head> |
|
7 <title>Test for User Agent Updates</title> |
|
8 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> |
|
9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> |
|
10 </head> |
|
11 <body> |
|
12 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=897221">Mozilla Bug 897221</a> |
|
13 <p id="display"></p> |
|
14 <div id="content" style="display: none"></div> |
|
15 <pre id="test"> |
|
16 <script class="testbody" type="text/javascript"> |
|
17 |
|
18 const PREF_APP_UPDATE_TIMERMINIMUMDELAY = "app.update.timerMinimumDelay"; |
|
19 const PREF_UPDATES = "general.useragent.updates."; |
|
20 const PREF_UPDATES_ENABLED = PREF_UPDATES + "enabled"; |
|
21 const PREF_UPDATES_URL = PREF_UPDATES + "url"; |
|
22 const PREF_UPDATES_INTERVAL = PREF_UPDATES + "interval"; |
|
23 const PREF_UPDATES_TIMEOUT = PREF_UPDATES + "timeout"; |
|
24 |
|
25 const KEY_PREFDIR = "PrefD"; |
|
26 const KEY_APPDIR = "XCurProcD"; |
|
27 const FILE_UPDATES = "ua-update.json"; |
|
28 |
|
29 const DEFAULT_UA = navigator.userAgent; |
|
30 const UA_OVERRIDE = "DummyUserAgent"; |
|
31 const UA_ALT_OVERRIDE = "AltUserAgent"; |
|
32 |
|
33 const UA_PARTIAL_FROM = "\\wozilla"; // /\wozilla |
|
34 const UA_PARTIAL_SEP = "#"; |
|
35 const UA_PARTIAL_TO = UA_OVERRIDE; |
|
36 const UA_PARTIAL_OVERRIDE = UA_PARTIAL_FROM + UA_PARTIAL_SEP + UA_PARTIAL_TO; |
|
37 const UA_PARTIAL_EXPECTED = DEFAULT_UA.replace(new RegExp(UA_PARTIAL_FROM, 'g'), UA_PARTIAL_TO); |
|
38 |
|
39 function getUA(host) { |
|
40 var url = location.pathname; |
|
41 url = host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs'; |
|
42 |
|
43 var xhr = new XMLHttpRequest(); |
|
44 xhr.open('GET', url, false); // sync request |
|
45 xhr.send(); |
|
46 is(xhr.status, 200, 'request failed'); |
|
47 is(typeof xhr.response, 'string', 'invalid response'); |
|
48 return xhr.response; |
|
49 } |
|
50 |
|
51 const OVERRIDES = [ |
|
52 { |
|
53 domain: 'example.org', |
|
54 override: '%DATE%', |
|
55 host: 'http://example.org' |
|
56 }, |
|
57 { |
|
58 domain: 'test1.example.org', |
|
59 override: '%PRODUCT%', |
|
60 expected: SpecialPowers.Services.appinfo.name, |
|
61 host: 'http://test1.example.org' |
|
62 }, |
|
63 { |
|
64 domain: 'test2.example.org', |
|
65 override: '%APP_ID%', |
|
66 expected: SpecialPowers.Services.appinfo.ID, |
|
67 host: 'http://test2.example.org' |
|
68 }, |
|
69 { |
|
70 domain: 'sub1.test1.example.org', |
|
71 override: '%APP_VERSION%', |
|
72 expected: SpecialPowers.Services.appinfo.version, |
|
73 host: 'http://sub1.test1.example.org' |
|
74 }, |
|
75 { |
|
76 domain: 'sub2.test1.example.org', |
|
77 override: '%BUILD_ID%', |
|
78 expected: SpecialPowers.Services.appinfo.appBuildID, |
|
79 host: 'http://sub2.test1.example.org' |
|
80 }, |
|
81 { |
|
82 domain: 'sub1.test2.example.org', |
|
83 override: '%OS%', |
|
84 expected: SpecialPowers.Services.appinfo.OS, |
|
85 host: 'http://sub1.test2.example.org' |
|
86 }, |
|
87 { |
|
88 domain: 'sub2.test2.example.org', |
|
89 override: UA_PARTIAL_OVERRIDE, |
|
90 expected: UA_PARTIAL_EXPECTED, |
|
91 host: 'http://sub2.test2.example.org' |
|
92 }, |
|
93 ]; |
|
94 |
|
95 function getServerURL() { |
|
96 var url = location.pathname; |
|
97 return location.origin + url.slice(0, url.lastIndexOf('/')) + '/user_agent_update.sjs?'; |
|
98 } |
|
99 |
|
100 function getUpdateURL() { |
|
101 var url = getServerURL(); |
|
102 var overrides = {}; |
|
103 overrides[location.hostname] = UA_OVERRIDE; |
|
104 OVERRIDES.forEach(function (val) { |
|
105 overrides[val.domain] = val.override; |
|
106 }); |
|
107 url = url + encodeURIComponent(JSON.stringify(overrides)).replace(/%25/g, '%'); |
|
108 return url; |
|
109 } |
|
110 |
|
111 function testDownload(callback) { |
|
112 var startTime = Date.now(); |
|
113 var url = getUpdateURL(); |
|
114 isnot(navigator.userAgent, UA_OVERRIDE, 'UA already overridden'); |
|
115 info('Waiting for UA update: ' + url); |
|
116 SpecialPowers.pushPrefEnv({ |
|
117 set: [ |
|
118 [PREF_UPDATES_ENABLED, true], |
|
119 [PREF_UPDATES_URL, url], |
|
120 [PREF_UPDATES_TIMEOUT, 10000], |
|
121 [PREF_UPDATES_INTERVAL, 1] // 1 second interval |
|
122 ] |
|
123 }, function waitForUpdate() setTimeout(function () { |
|
124 if (navigator.userAgent !== UA_OVERRIDE) { |
|
125 waitForUpdate(); |
|
126 return; |
|
127 } |
|
128 info('Overrode navigator UA'); |
|
129 is(getUA(location.origin), UA_OVERRIDE, 'Header UA not overridden'); |
|
130 |
|
131 var updateTime = parseInt(getUA('http://example.org')); |
|
132 ok(startTime <= updateTime, 'Update was before start time'); |
|
133 ok(updateTime <= Date.now(), 'Update was after present time'); |
|
134 |
|
135 OVERRIDES.forEach(function (val) { |
|
136 val.expected && is(getUA(val.host), val.expected, |
|
137 'Incorrect URL parameter: ' + val.override); |
|
138 }); |
|
139 callback(); |
|
140 }, 100)); |
|
141 } |
|
142 |
|
143 function testBadUpdate(callback) { |
|
144 var url = getServerURL() + 'invalid-json'; |
|
145 var prevOverride = navigator.userAgent; |
|
146 SpecialPowers.pushPrefEnv({ |
|
147 set: [ |
|
148 [PREF_UPDATES_URL, url], |
|
149 [PREF_UPDATES_INTERVAL, 1] // 1 second interval |
|
150 ] |
|
151 }, function () setTimeout(function () { |
|
152 // We want to make sure a bad update doesn't cancel out previous overrides. |
|
153 // We do this by waiting for 5 seconds (assuming the update occurs within 5 |
|
154 // seconds), and check that the previous override hasn't changed. |
|
155 is(navigator.userAgent, prevOverride, |
|
156 'Invalid update deleted previous override'); |
|
157 callback(); |
|
158 }, 5000)); |
|
159 } |
|
160 |
|
161 function testProfileLoad(callback) { |
|
162 var file = FU.getFile(KEY_APPDIR, [FILE_UPDATES]).path; |
|
163 var encoder = SpecialPowers.wrap(new TextEncoder()); |
|
164 var overrides = {}; |
|
165 overrides[location.hostname] = UA_ALT_OVERRIDE; |
|
166 var bytes = encoder.encode(JSON.stringify(overrides)); |
|
167 |
|
168 var badfile = FU.getFile(KEY_PREFDIR, [FILE_UPDATES]).path; |
|
169 var badbytes = encoder.encode("null"); |
|
170 |
|
171 OSF.writeAtomic(file, bytes, {tmpPath: file + ".tmp"}).then( |
|
172 () => OSF.writeAtomic(badfile, badbytes, {tmpPath: badfile + ".tmp"}) |
|
173 ).then( |
|
174 () => { |
|
175 SpecialPowers.pushPrefEnv({ |
|
176 set: [[PREF_UPDATES_ENABLED, true]] |
|
177 }, function () { |
|
178 // initialize UserAgentOverrides.jsm and |
|
179 // UserAgentUpdates.jsm and load saved file |
|
180 UAO.init(); |
|
181 (function waitForLoad() { |
|
182 if (navigator.userAgent !== UA_ALT_OVERRIDE) { |
|
183 setTimeout(waitForLoad, 100); |
|
184 return; |
|
185 } |
|
186 is(getUA(location.origin), UA_ALT_OVERRIDE, 'Did not apply saved override'); |
|
187 saveFilePreviousSize = file.fileSize; |
|
188 callback(); |
|
189 })(); |
|
190 }); |
|
191 }, |
|
192 (reason) => { |
|
193 throw reason |
|
194 } |
|
195 ); |
|
196 } |
|
197 |
|
198 function testProfileSave(callback) { |
|
199 info('Waiting for saving to profile'); |
|
200 var file = FU.getFile(KEY_PREFDIR, [FILE_UPDATES]).path; |
|
201 (function waitForSave() { |
|
202 OSF.exists(file).then( |
|
203 (exists) => { |
|
204 if (!exists) { |
|
205 setTimeout(waitForSave, 100); |
|
206 return; |
|
207 } |
|
208 return OSF.read(file).then( |
|
209 (bytes) => { |
|
210 info('Saved new overrides'); |
|
211 var decoder = SpecialPowers.wrap(new TextDecoder()); |
|
212 var overrides = JSON.parse(decoder.decode(bytes)); |
|
213 is(overrides[location.hostname], UA_OVERRIDE, 'Incorrect saved override'); |
|
214 OVERRIDES.forEach(function (val) { |
|
215 val.expected && is(overrides[val.domain], val.expected, |
|
216 'Incorrect saved override: ' + val.override); |
|
217 }); |
|
218 callback(); |
|
219 } |
|
220 ); |
|
221 } |
|
222 ).then(null, |
|
223 (reason) => { |
|
224 throw reason |
|
225 } |
|
226 ); |
|
227 })(); |
|
228 } |
|
229 |
|
230 SimpleTest.waitForExplicitFinish(); |
|
231 |
|
232 SpecialPowers.Cu.import("resource://gre/modules/FileUtils.jsm", window); |
|
233 var FU = SpecialPowers.wrap(FileUtils); |
|
234 |
|
235 SpecialPowers.Cu.import("resource://gre/modules/osfile.jsm", window); |
|
236 var OSF = SpecialPowers.wrap(OS).File; |
|
237 |
|
238 // Load UserAgentOverrides.jsm after we load update timer manager |
|
239 var UAO = null; |
|
240 |
|
241 var saveFilePreviousSize = 0; |
|
242 |
|
243 SpecialPowers.pushPrefEnv({ |
|
244 set: [ |
|
245 [PREF_APP_UPDATE_TIMERMINIMUMDELAY, 0] |
|
246 ] |
|
247 }, function () { |
|
248 // Enter update timer manager test mode |
|
249 (SpecialPowers.Cc["@mozilla.org/updates/timer-manager;1"].getService( |
|
250 SpecialPowers.Ci.nsIObserver)).observe(null, "utm-test-init", ""); |
|
251 |
|
252 SpecialPowers.Cu.import('resource://gre/modules/UserAgentOverrides.jsm', window); |
|
253 UAO = SpecialPowers.wrap(UserAgentOverrides); |
|
254 UAO.uninit(); |
|
255 |
|
256 // testProfileLoad, testDownload, and testProfileSave must run in this order |
|
257 // because testDownload depends on testProfileLoad to call UAO.init() |
|
258 // and testProfileSave depends on testDownload to save overrides to the profile |
|
259 testProfileLoad(function() |
|
260 testDownload(function() |
|
261 testBadUpdate(function() |
|
262 testProfileSave(SimpleTest.finish) |
|
263 ) |
|
264 ) |
|
265 ); |
|
266 }); |
|
267 |
|
268 </script> |
|
269 </pre> |
|
270 </body> |
|
271 </html> |
|
272 |