| 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 |