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: { |
| 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 _setFileContents: function(path, contents, lastModified) |
| 125 { |
| 126 window.localStorage[path] = contents; |
| 127 window.localStorage[path + "/lastModified"] = lastModified || 0; |
| 128 }, |
| 129 |
| 130 lineBreak: "\n", |
| 131 |
| 132 resolveFilePath: function(path) |
| 133 { |
| 134 return new FakeFile(path); |
| 135 }, |
| 136 |
| 137 readFromFile: function(file, decode, listener, callback, timeLineID) |
| 138 { |
| 139 // Fake asynchronous execution |
| 140 setTimeout(function() |
| 141 { |
| 142 if ("spec" in file && /^defaults\b/.test(file.spec)) |
| 143 { |
| 144 // Code attempts to read the default patterns.ini, we don't have that. |
| 145 // Make sure to execute first-run actions instead. |
| 146 callback(null); |
| 147 executeFirstRunActions(); |
| 148 return; |
| 149 } |
| 150 |
| 151 var path = this._getFilePath(file); |
| 152 if (!(path in window.localStorage)) |
| 153 { |
| 154 callback(new Error("File doesn't exist")) |
| 155 return; |
| 156 } |
| 157 |
| 158 var lines = window.localStorage[path].split(/[\r\n]+/); |
| 159 for (var i = 0; i < lines.length; i++) |
| 160 listener.process(lines[i]); |
| 161 listener.process(null); |
| 162 callback(null); |
| 163 }.bind(this), 0); |
| 164 }, |
| 165 |
| 166 writeToFile: function(file, encode, data, callback, timeLineID) |
| 167 { |
| 168 var path = this._getFilePath(file); |
| 169 this._setFileContents(path, data.join(this.lineBreak) + this.lineBreak, Da
te.now()); |
| 170 |
| 171 // Fake asynchronous execution |
| 172 setTimeout(callback.bind(null, null), 0); |
| 173 }, |
| 174 |
| 175 copyFile: function(fromFile, toFile, callback) |
| 176 { |
| 177 // Simply combine read and write operations |
| 178 var data = []; |
| 179 this.readFromFile(fromFile, false, { |
| 180 process: function(line) |
| 181 { |
| 182 if (line !== null) |
| 183 data.push(line); |
| 184 } |
| 185 }, function(e) |
| 186 { |
| 187 if (e) |
| 188 callback(e); |
| 189 else |
| 190 this.writeToFile(toFile, false, data, callback); |
| 191 }.bind(this)); |
| 192 }, |
| 193 |
| 194 renameFile: function(fromFile, newName, callback) |
| 195 { |
| 196 var path = this._getFilePath(fromFile); |
| 197 if (!(path in window.localStorage)) |
| 198 { |
| 199 callback(new Error("File doesn't exist")) |
| 200 return; |
| 201 } |
| 202 |
| 203 this._setFileContents(newName, window.localStorage[path], window.localStor
age[path + "/lastModified"]); |
| 204 this.removeFile(fromFile, callback); |
| 205 }, |
| 206 |
| 207 removeFile: function(file, callback) |
| 208 { |
| 209 var path = this._getFilePath(file); |
| 210 delete window.localStorage[path]; |
| 211 delete window.localStorage[path + "/lastModified"]; |
| 212 callback(null); |
| 213 }, |
| 214 |
| 215 statFile: function(file, callback) |
| 216 { |
| 217 var path = this._getFilePath(file); |
| 218 callback(null, { |
| 219 exists: path in window.localStorage, |
| 220 isDirectory: false, |
| 221 isFile: true, |
| 222 lastModified: parseInt(window.localStorage[path + "/lastModified"], 10)
|| 0 |
| 223 }); |
| 224 } |
| 225 } |
| 226 }; |
| 227 |
| 228 // |
| 229 // Fake nsIFile implementation for our I/O |
| 230 // |
| 231 |
| 232 function FakeFile(path) |
| 233 { |
| 234 this.path = path; |
| 235 } |
| 236 FakeFile.prototype = |
| 237 { |
| 238 get leafName() |
| 239 { |
| 240 return this.path; |
| 241 }, |
| 242 set leafName(value) |
| 243 { |
| 244 this.path = value; |
| 245 }, |
| 246 append: function(path) |
| 247 { |
| 248 this.path += path; |
| 249 }, |
| 250 clone: function() |
| 251 { |
| 252 return new FakeFile(this.path); |
| 253 }, |
| 254 get parent() |
| 255 { |
| 256 return {create: function() {}}; |
| 257 }, |
| 258 normalize: function() {} |
| 259 }; |
| 260 |
| 261 // |
| 262 // Prefs module: the values are hardcoded for now. |
| 263 // |
| 264 |
| 265 require.scopes.prefs = { |
| 266 Prefs: { |
| 267 enabled: true, |
| 268 patternsfile: "patterns.ini", |
| 269 patternsbackups: 0, |
| 270 patternsbackupinterval: 24, |
| 271 data_directory: "", |
| 272 savestats: false, |
| 273 privateBrowsing: false, |
| 274 subscriptions_fallbackerrors: 5, |
| 275 subscriptions_fallbackurl: "https://adblockplus.org/getSubscription?version=
%VERSION%&url=%SUBSCRIPTION%&downloadURL=%URL%&error=%ERROR%&channelStatus=%CHAN
NELSTATUS%&responseStatus=%RESPONSESTATUS%", |
| 276 subscriptions_autoupdate: true, |
| 277 subscriptions_exceptionsurl: "https://easylist-downloads.adblockplus.org/exc
eptionrules.txt", |
| 278 documentation_link: "https://adblockplus.org/redirect?link=%LINK%&lang=%LANG
%", |
| 279 addListener: function() {} |
| 280 } |
| 281 }; |
| 282 |
| 283 // |
| 284 // Utils module |
| 285 // |
| 286 |
| 287 require.scopes.utils = |
| 288 { |
| 289 Utils: { |
| 290 systemPrincipal: null, |
| 291 getString: function(id) |
| 292 { |
| 293 return id; |
| 294 }, |
| 295 runAsync: function(callback, thisPtr) |
| 296 { |
| 297 var params = Array.prototype.slice.call(arguments, 2); |
| 298 window.setTimeout(function() |
| 299 { |
| 300 callback.apply(thisPtr, params); |
| 301 }, 0); |
| 302 }, |
| 303 get appLocale() |
| 304 { |
| 305 // Note: navigator.language |
| 306 return window.navigator.browserLanguage; |
| 307 }, |
| 308 generateChecksum: function(lines) |
| 309 { |
| 310 // We cannot calculate MD5 checksums yet :-( |
| 311 return null; |
| 312 }, |
| 313 makeURI: function(url) |
| 314 { |
| 315 return Services.io.newURI(url); |
| 316 }, |
| 317 checkLocalePrefixMatch: function(prefixes) |
| 318 { |
| 319 if (!prefixes) |
| 320 return null; |
| 321 |
| 322 var list = prefixes.split(","); |
| 323 for (var i = 0; i < list.length; i++) |
| 324 if (new RegExp("^" + list[i] + "\\b").test(this.appLocale)) |
| 325 return list[i]; |
| 326 |
| 327 return null; |
| 328 } |
| 329 } |
| 330 }; |
| 331 |
| 332 // |
| 333 // ElemHideHitRegistration dummy implementation |
| 334 // |
| 335 |
| 336 require.scopes.elemHideHitRegistration = |
| 337 { |
| 338 AboutHandler: {} |
| 339 }; |
| 340 |
| 341 // |
| 342 // Services.jsm module emulation |
| 343 // |
| 344 |
| 345 var Services = |
| 346 { |
| 347 io: { |
| 348 newURI: function(uri) |
| 349 { |
| 350 if (!uri.length || uri[0] == "~") |
| 351 throw new Error("Invalid URI"); |
| 352 |
| 353 /^([^:\/]*)/.test(uri); |
| 354 var scheme = RegExp.$1.toLowerCase(); |
| 355 |
| 356 return { |
| 357 scheme: scheme, |
| 358 spec: uri, |
| 359 QueryInterface: function() |
| 360 { |
| 361 return this; |
| 362 } |
| 363 }; |
| 364 }, |
| 365 newFileURI: function(file) |
| 366 { |
| 367 var result = this.newURI("file:///" + file.path); |
| 368 result.file = file; |
| 369 return result; |
| 370 } |
| 371 }, |
| 372 obs: { |
| 373 addObserver: function() {}, |
| 374 removeObserver: function() {} |
| 375 }, |
| 376 vc: { |
| 377 compare: function(v1, v2) |
| 378 { |
| 379 function parsePart(s) |
| 380 { |
| 381 if (!s) |
| 382 return parsePart("0"); |
| 383 |
| 384 var part = { |
| 385 numA: 0, |
| 386 strB: "", |
| 387 numC: 0, |
| 388 extraD: "" |
| 389 }; |
| 390 |
| 391 if (s === "*") |
| 392 { |
| 393 part.numA = Number.MAX_VALUE; |
| 394 return part; |
| 395 } |
| 396 |
| 397 var matches = s.match(/(\d*)(\D*)(\d*)(.*)/); |
| 398 part.numA = parseInt(matches[1], 10) || part.numA; |
| 399 part.strB = matches[2] || part.strB; |
| 400 part.numC = parseInt(matches[3], 10) || part.numC; |
| 401 part.extraD = matches[4] || part.extraD; |
| 402 |
| 403 if (part.strB == "+") |
| 404 { |
| 405 part.numA++; |
| 406 part.strB = "pre"; |
| 407 } |
| 408 |
| 409 return part; |
| 410 } |
| 411 |
| 412 function comparePartElement(s1, s2) |
| 413 { |
| 414 if (s1 === "" && s2 !== "") |
| 415 return 1; |
| 416 if (s1 !== "" && s2 === "") |
| 417 return -1; |
| 418 return s1 === s2 ? 0 : (s1 > s2 ? 1 : -1); |
| 419 } |
| 420 |
| 421 function compareParts(p1, p2) |
| 422 { |
| 423 var result = 0; |
| 424 var elements = ["numA", "strB", "numC", "extraD"]; |
| 425 elements.some(function(element) |
| 426 { |
| 427 result = comparePartElement(p1[element], p2[element]); |
| 428 return result; |
| 429 }); |
| 430 return result; |
| 431 } |
| 432 |
| 433 var parts1 = v1.split("."); |
| 434 var parts2 = v2.split("."); |
| 435 for (var i = 0; i < Math.max(parts1.length, parts2.length); i++) |
| 436 { |
| 437 var result = compareParts(parsePart(parts1[i]), parsePart(parts2[i])); |
| 438 if (result) |
| 439 return result; |
| 440 } |
| 441 return 0; |
| 442 } |
| 443 } |
| 444 } |
| 445 |
| 446 // |
| 447 // FileUtils.jsm module emulation |
| 448 // |
| 449 |
| 450 var FileUtils = |
| 451 { |
| 452 PERMS_DIRECTORY: 0 |
| 453 }; |
| 454 |
| 455 function FakeTimer() |
| 456 { |
| 457 } |
| 458 FakeTimer.prototype = |
| 459 { |
| 460 delay: 0, |
| 461 callback: null, |
| 462 initWithCallback: function(callback, delay) |
| 463 { |
| 464 this.callback = callback; |
| 465 this.delay = delay; |
| 466 this.scheduleTimeout(); |
| 467 }, |
| 468 scheduleTimeout: function() |
| 469 { |
| 470 var me = this; |
| 471 window.setTimeout(function() |
| 472 { |
| 473 try |
| 474 { |
| 475 me.callback(); |
| 476 } |
| 477 catch(e) |
| 478 { |
| 479 Cu.reportError(e); |
| 480 } |
| 481 me.scheduleTimeout(); |
| 482 }, this.delay); |
| 483 } |
| 484 }; |
| 485 |
| 486 // |
| 487 // Add a channel property to XMLHttpRequest, Synchronizer needs it |
| 488 // |
| 489 |
| 490 XMLHttpRequest.prototype.channel = |
| 491 { |
| 492 status: -1, |
| 493 notificationCallbacks: {}, |
| 494 loadFlags: 0, |
| 495 INHIBIT_CACHING: 0, |
| 496 VALIDATE_ALWAYS: 0, |
| 497 QueryInterface: function() |
| 498 { |
| 499 return this; |
| 500 } |
| 501 }; |
OLD | NEW |