Left: | ||
Right: |
OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * This Source Code is subject to the terms of the Mozilla Public License | |
3 * version 2.0 (the "License"). You can obtain a copy of the License at | |
4 * http://mozilla.org/MPL/2.0/. | |
5 */ | |
6 | |
7 // TODO: Parts of this file are identical to ABP/Chrome, these should be split | |
8 // away into a separate file. | |
9 | |
10 // | |
11 // Module framework stuff | |
12 // | |
13 | |
14 function require(module) | |
15 { | |
16 return require.scopes[module]; | |
17 } | |
18 require.scopes = {__proto__: null}; | |
19 | |
20 function importAll(module, globalObj) | |
21 { | |
22 var exports = require(module); | |
23 for (var key in exports) | |
24 globalObj[key] = exports[key]; | |
25 } | |
26 | |
27 onShutdown = { | |
28 done: false, | |
29 add: function() {}, | |
30 remove: function() {} | |
31 }; | |
32 | |
33 // | |
34 // XPCOM emulation | |
35 // | |
36 | |
37 var Components = | |
38 { | |
39 interfaces: | |
40 { | |
41 nsIFile: {DIRECTORY_TYPE: 0}, | |
42 nsIFileURL: function() {}, | |
43 nsIHttpChannel: function() {}, | |
44 nsITimer: {TYPE_REPEATING_SLACK: 0}, | |
45 nsIInterfaceRequestor: null, | |
46 nsIChannelEventSink: null | |
47 }, | |
48 classes: | |
49 { | |
50 "@mozilla.org/timer;1": | |
51 { | |
52 createInstance: function() | |
53 { | |
54 return new FakeTimer(); | |
55 } | |
56 }, | |
57 "@mozilla.org/xmlextras/xmlhttprequest;1": | |
58 { | |
59 createInstance: function() | |
60 { | |
61 return new XMLHttpRequest(); | |
62 } | |
63 } | |
64 }, | |
65 results: {}, | |
66 utils: { | |
67 reportError: function(e) | |
68 { | |
69 opera.postError(e + "\n" + (typeof e == "object" ? e.stack : new Error().s tack)); | |
70 } | |
71 }, | |
72 manager: null, | |
73 ID: function() | |
74 { | |
75 return null; | |
76 } | |
77 }; | |
78 const Cc = Components.classes; | |
79 const Ci = Components.interfaces; | |
80 const Cr = Components.results; | |
81 const Cu = Components.utils; | |
82 | |
83 var XPCOMUtils = | |
84 { | |
85 generateQI: function() {} | |
86 }; | |
87 | |
88 // | |
89 // Info pseudo-module | |
90 // | |
91 | |
92 require.scopes.info = | |
93 { | |
94 get addonID() | |
95 { | |
96 return widget.id; | |
97 }, | |
98 addonVersion: "2.0.3", // Hardcoded for now | |
99 addonRoot: "", | |
100 get addonName() | |
101 { | |
102 return widget.name; | |
103 }, | |
104 application: "opera" | |
105 }; | |
106 | |
107 // | |
108 // IO module: no direct file system access, using FileSystem API | |
109 // | |
110 | |
111 require.scopes.io = | |
112 { | |
113 IO: { | |
Felix Dahlke
2012/10/10 12:11:22
Opening brace not on its own line?
Felix Dahlke
2012/10/11 07:54:28
Nevermind.
| |
114 _getFilePath: function(file) | |
115 { | |
116 if (file instanceof FakeFile) | |
117 return file.path; | |
118 else if ("spec" in file) | |
119 return file.spec; | |
120 | |
121 throw new Error("Unexpected file type"); | |
122 }, | |
123 | |
124 lineBreak: "\n", | |
125 | |
126 resolveFilePath: function(path) | |
127 { | |
128 return new FakeFile(path); | |
129 }, | |
130 | |
131 readFromFile: function(file, decode, listener, callback, timeLineID) | |
132 { | |
133 if ("spec" in file && /^defaults\b/.test(file.spec)) | |
Felix Dahlke
2012/10/10 12:11:22
The first check is not necessary, .test(undefined)
Felix Dahlke
2012/10/11 07:54:28
Nevermind.
Wladimir Palant
2012/10/11 09:36:26
Still, I don't feel comfortable running regular ex
| |
134 { | |
135 // Code attempts to read the default patterns.ini, we don't have that. | |
136 // Make sure to execute first-run actions instead. | |
137 callback(null); | |
138 executeFirstRunActions(); | |
Felix Dahlke
2012/10/11 07:54:28
This function cannot be called from here.
| |
139 return; | |
140 } | |
141 | |
142 var path = this._getFilePath(file); | |
143 if (!(path in window.localStorage)) | |
144 { | |
145 callback(new Error("File doesn't exist")) | |
146 return; | |
147 } | |
148 | |
149 var lines = window.localStorage[path].split(/[\r\n]+/); | |
150 | |
151 // Fake asynchronous execution | |
152 setTimeout(function() | |
153 { | |
154 for (var i = 0; i < lines.length; i++) | |
155 listener.process(lines[i]); | |
156 listener.process(null); | |
157 callback(null); | |
158 }.bind(this), 0); | |
159 }, | |
160 | |
161 writeToFile: function(file, encode, data, callback, timeLineID) | |
162 { | |
163 var path = this._getFilePath(file); | |
164 window.localStorage[path] = data.join("\n") + "\n"; | |
Felix Dahlke
2012/10/10 12:11:22
Why not use this.lineBreak?
| |
165 window.localStorage[path + "/lastModified"] = Date.now(); | |
166 | |
167 // Fake asynchronous execution | |
168 setTimeout(callback.bind(null, null), 0); | |
169 }, | |
170 | |
171 copyFile: function(fromFile, toFile, callback) | |
172 { | |
173 // Simply combine read and write operations | |
174 var data = []; | |
175 this.readFromFile(fromFile, false, { | |
Felix Dahlke
2012/10/10 12:11:22
Shouldn't the opening brace be on its own line?
| |
176 process: function(line) | |
177 { | |
178 if (line !== null) | |
179 data.push(line); | |
180 } | |
181 }, function(e) | |
182 { | |
183 if (e) | |
184 callback(e); | |
185 else | |
186 this.writeToFile(toFile, false, data, callback); | |
187 }.bind(this)); | |
188 }, | |
189 | |
190 renameFile: function(fromFile, newName, callback) | |
191 { | |
192 var path = this._getFilePath(fromFile); | |
193 if (!(path in window.localStorage)) | |
194 { | |
195 callback(new Error("File doesn't exist")) | |
196 return; | |
197 } | |
198 | |
199 window.localStorage[newName] = window.localStorage[path]; | |
200 window.localStorage[newName + "/lastModified"] = window.localStorage[path + "/lastModified"] || 0; | |
Felix Dahlke
2012/10/10 12:11:22
How about a function that sets both localStorage[x
| |
201 delete window.localStorage[path]; | |
202 delete window.localStorage[path + "/lastModified"]; | |
203 callback(null); | |
204 }, | |
205 | |
206 removeFile: function(file, callback) | |
207 { | |
208 var path = this._getFilePath(file); | |
209 delete window.localStorage[path]; | |
Felix Dahlke
2012/10/10 12:11:22
How about a function to delete both localStorage[x
| |
210 delete window.localStorage[path + "/lastModified"]; | |
211 callback(null); | |
212 }, | |
213 | |
214 statFile: function(file, callback) | |
215 { | |
216 var path = this._getFilePath(file); | |
217 callback(null, { | |
218 exists: path in window.localStorage, | |
219 isDirectory: false, | |
220 isFile: true, | |
221 lastModified: parseInt(window.localStorage[path + "/lastModified"], 10) || 0 | |
222 }); | |
223 } | |
224 } | |
225 }; | |
226 | |
227 // | |
228 // Fake nsIFile implementation for our I/O | |
229 // | |
230 | |
231 function FakeFile(path) | |
232 { | |
233 this.path = path; | |
234 } | |
235 FakeFile.prototype = | |
236 { | |
237 get leafName() | |
238 { | |
239 return this.path; | |
240 }, | |
241 set leafName(value) | |
242 { | |
243 this.path = value; | |
244 }, | |
245 append: function(path) | |
246 { | |
247 this.path += path; | |
248 }, | |
249 clone: function() | |
250 { | |
251 return new FakeFile(this.path); | |
252 }, | |
253 get parent() | |
254 { | |
255 return {create: function() {}}; | |
256 }, | |
257 normalize: function() {} | |
258 }; | |
259 | |
260 // | |
261 // Prefs module: the values are hardcoded for now. | |
262 // | |
263 | |
264 require.scopes.prefs = { | |
265 Prefs: { | |
266 enabled: true, | |
267 patternsfile: "patterns.ini", | |
268 patternsbackups: 0, | |
269 patternsbackupinterval: 24, | |
270 data_directory: "", | |
271 savestats: false, | |
272 privateBrowsing: false, | |
273 subscriptions_fallbackerrors: 5, | |
274 subscriptions_fallbackurl: "https://adblockplus.org/getSubscription?version= %VERSION%&url=%SUBSCRIPTION%&downloadURL=%URL%&error=%ERROR%&channelStatus=%CHAN NELSTATUS%&responseStatus=%RESPONSESTATUS%", | |
275 subscriptions_autoupdate: true, | |
276 subscriptions_exceptionsurl: "https://easylist-downloads.adblockplus.org/exc eptionrules.txt", | |
277 documentation_link: "https://adblockplus.org/redirect?link=%LINK%&lang=%LANG %", | |
278 addListener: function() {} | |
279 } | |
280 }; | |
281 | |
282 // | |
283 // Utils module | |
284 // | |
285 | |
286 require.scopes.utils = | |
287 { | |
288 Utils: { | |
289 systemPrincipal: null, | |
290 getString: function(id) | |
291 { | |
292 return id; | |
293 }, | |
294 runAsync: function(callback, thisPtr) | |
295 { | |
296 var params = Array.prototype.slice.call(arguments, 2); | |
297 window.setTimeout(function() | |
298 { | |
299 callback.apply(thisPtr, params); | |
300 }, 0); | |
301 }, | |
302 get appLocale() | |
303 { | |
304 // Note: navigator.language | |
305 return window.navigator.browserLanguage; | |
306 }, | |
307 generateChecksum: function(lines) | |
308 { | |
309 // We cannot calculate MD5 checksums yet :-( | |
310 return null; | |
311 }, | |
312 makeURI: function(url) | |
313 { | |
314 return Services.io.newURI(url); | |
315 }, | |
316 checkLocalePrefixMatch: function(prefixes) | |
317 { | |
318 if (!prefixes) | |
319 return null; | |
320 | |
321 var list = prefixes.split(","); | |
322 for (var i = 0; i < list.length; i++) | |
323 if (new RegExp("^" + list[i] + "\\b").test(this.appLocale)) | |
324 return list[i]; | |
325 | |
326 return null; | |
327 } | |
328 } | |
329 }; | |
330 | |
331 // | |
332 // ElemHideHitRegistration dummy implementation | |
333 // | |
334 | |
335 require.scopes.elemHideHitRegistration = | |
336 { | |
337 AboutHandler: {} | |
338 }; | |
339 | |
340 // | |
341 // Services.jsm module emulation | |
342 // | |
343 | |
344 var Services = | |
345 { | |
346 io: { | |
347 newURI: function(uri) | |
348 { | |
349 if (!uri.length || uri[0] == "~") | |
350 throw new Error("Invalid URI"); | |
351 | |
352 /^([^:\/]*)/.test(uri); | |
353 var scheme = RegExp.$1.toLowerCase(); | |
354 | |
355 return { | |
356 scheme: scheme, | |
357 spec: uri, | |
358 QueryInterface: function() | |
359 { | |
360 return this; | |
361 } | |
362 }; | |
363 }, | |
364 newFileURI: function(file) | |
365 { | |
366 var result = this.newURI("file:///" + file.path); | |
367 result.file = file; | |
368 return result; | |
369 } | |
370 }, | |
371 obs: { | |
372 addObserver: function() {}, | |
373 removeObserver: function() {} | |
374 }, | |
375 vc: { | |
376 compare: function(v1, v2) | |
377 { | |
378 function parsePart(s) | |
379 { | |
380 if (!s) | |
381 return parsePart("0"); | |
382 | |
383 var part = { | |
384 numA: 0, | |
385 strB: "", | |
386 numC: 0, | |
387 extraD: "" | |
388 }; | |
389 | |
390 if (s === "*") | |
391 { | |
392 part.numA = Number.MAX_VALUE; | |
393 return part; | |
394 } | |
395 | |
396 var matches = s.match(/(\d*)(\D*)(\d*)(.*)/); | |
397 part.numA = parseInt(matches[1], 10) || part.numA; | |
398 part.strB = matches[2] || part.strB; | |
399 part.numC = parseInt(matches[3], 10) || part.numC; | |
400 part.extraD = matches[4] || part.extraD; | |
401 | |
402 if (part.strB == "+") | |
403 { | |
404 part.numA++; | |
405 part.strB = "pre"; | |
406 } | |
407 | |
408 return part; | |
409 } | |
410 | |
411 function comparePartElement(s1, s2) | |
412 { | |
413 if (s1 === "" && s2 !== "") | |
414 return 1; | |
415 if (s1 !== "" && s2 === "") | |
416 return -1; | |
417 return s1 === s2 ? 0 : (s1 > s2 ? 1 : -1); | |
418 } | |
419 | |
420 function compareParts(p1, p2) | |
421 { | |
422 var result = 0; | |
423 var elements = ["numA", "strB", "numC", "extraD"]; | |
424 elements.some(function(element) | |
425 { | |
426 result = comparePartElement(p1[element], p2[element]); | |
427 return result; | |
428 }); | |
429 return result; | |
430 } | |
431 | |
432 var parts1 = v1.split("."); | |
433 var parts2 = v2.split("."); | |
434 for (var i = 0; i < Math.max(parts1.length, parts2.length); i++) | |
435 { | |
436 var result = compareParts(parsePart(parts1[i]), parsePart(parts2[i])); | |
437 if (result) | |
438 return result; | |
439 } | |
440 return 0; | |
441 } | |
442 } | |
443 } | |
444 | |
445 // | |
446 // FileUtils.jsm module emulation | |
447 // | |
448 | |
449 var FileUtils = | |
450 { | |
451 PERMS_DIRECTORY: 0 | |
452 }; | |
453 | |
454 function FakeTimer() | |
455 { | |
456 } | |
457 FakeTimer.prototype = | |
458 { | |
459 delay: 0, | |
460 callback: null, | |
461 initWithCallback: function(callback, delay) | |
462 { | |
463 this.callback = callback; | |
464 this.delay = delay; | |
465 this.scheduleTimeout(); | |
466 }, | |
467 scheduleTimeout: function() | |
468 { | |
469 var me = this; | |
470 window.setTimeout(function() | |
471 { | |
472 try | |
473 { | |
474 me.callback(); | |
475 } | |
476 catch(e) | |
477 { | |
478 Cu.reportError(e); | |
479 } | |
480 me.scheduleTimeout(); | |
481 }, this.delay); | |
482 } | |
483 }; | |
484 | |
485 // | |
486 // Add a channel property to XMLHttpRequest, Synchronizer needs it | |
487 // | |
488 | |
489 XMLHttpRequest.prototype.channel = | |
490 { | |
491 status: -1, | |
492 notificationCallbacks: {}, | |
493 loadFlags: 0, | |
494 INHIBIT_CACHING: 0, | |
495 VALIDATE_ALWAYS: 0, | |
496 QueryInterface: function() | |
497 { | |
498 return this; | |
499 } | |
500 }; | |
OLD | NEW |