| LEFT | RIGHT | 
|---|
| (no file at all) |  | 
|  | 1 /* | 
|  | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|  | 3  * Copyright (C) 2006-2016 Eyeo GmbH | 
|  | 4  * | 
|  | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|  | 6  * it under the terms of the GNU General Public License version 3 as | 
|  | 7  * published by the Free Software Foundation. | 
|  | 8  * | 
|  | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
|  | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 12  * GNU General Public License for more details. | 
|  | 13  * | 
|  | 14  * You should have received a copy of the GNU General Public License | 
|  | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 16  */ | 
|  | 17 | 
|  | 18 "use strict"; | 
|  | 19 | 
|  | 20 let fs = require("fs"); | 
|  | 21 let path = require("path"); | 
|  | 22 let SandboxedModule = require("sandboxed-module"); | 
|  | 23 | 
|  | 24 const Cr = exports.Cr = { | 
|  | 25   NS_OK: 0, | 
|  | 26   NS_BINDING_ABORTED: 0x804B0002, | 
|  | 27   NS_ERROR_FAILURE: 0x80004005 | 
|  | 28 }; | 
|  | 29 | 
|  | 30 const MILLIS_IN_SECOND = exports.MILLIS_IN_SECOND = 1000; | 
|  | 31 const MILLIS_IN_MINUTE = exports.MILLIS_IN_MINUTE = 60 * MILLIS_IN_SECOND; | 
|  | 32 const MILLIS_IN_HOUR = exports.MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTE; | 
|  | 33 const MILLIS_IN_DAY = exports.MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR; | 
|  | 34 | 
|  | 35 let globals = { | 
|  | 36   atob: data => new Buffer(data, "base64").toString("binary"), | 
|  | 37   btoa: data => new Buffer(data, "binary").toString("base64"), | 
|  | 38   Ci: { | 
|  | 39   }, | 
|  | 40   Cu: { | 
|  | 41     import: () => {}, | 
|  | 42     reportError: e => undefined | 
|  | 43   }, | 
|  | 44   console: { | 
|  | 45     log: () => undefined, | 
|  | 46     error: () => undefined, | 
|  | 47   }, | 
|  | 48   navigator: { | 
|  | 49   }, | 
|  | 50   onShutdown: { | 
|  | 51     add: () => | 
|  | 52     { | 
|  | 53     } | 
|  | 54   }, | 
|  | 55   Services: { | 
|  | 56     obs: { | 
|  | 57       addObserver: () => | 
|  | 58       { | 
|  | 59       } | 
|  | 60     }, | 
|  | 61     vc: { | 
|  | 62       compare: (v1, v2) => | 
|  | 63       { | 
|  | 64         function comparePart(p1, p2) | 
|  | 65         { | 
|  | 66           if (p1 != "*" && p2 == "*") | 
|  | 67             return -1; | 
|  | 68           else if (p1 == "*" && p2 != "*") | 
|  | 69             return 1; | 
|  | 70           else if (p1 == p2) | 
|  | 71             return 0; | 
|  | 72           else | 
|  | 73             return parseInt(p1, 10) - parseInt(p2, 10); | 
|  | 74         } | 
|  | 75 | 
|  | 76         let parts1 = v1.split("."); | 
|  | 77         let parts2 = v2.split("."); | 
|  | 78         for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) | 
|  | 79         { | 
|  | 80           let result = comparePart(parts1[i] || "0", parts2[i] || "0"); | 
|  | 81           if (result != 0) | 
|  | 82             return result; | 
|  | 83         } | 
|  | 84         return 0; | 
|  | 85       } | 
|  | 86     } | 
|  | 87   }, | 
|  | 88   XPCOMUtils: { | 
|  | 89     generateQI: () => | 
|  | 90     { | 
|  | 91     } | 
|  | 92   }, | 
|  | 93   URL: function(urlString) | 
|  | 94   { | 
|  | 95     return require("url").parse(urlString); | 
|  | 96   } | 
|  | 97 }; | 
|  | 98 | 
|  | 99 let knownModules = new Map(); | 
|  | 100 for (let dir of [path.join(__dirname, "stub-modules"), | 
|  | 101                  path.join(__dirname, "..", "lib")]) | 
|  | 102   for (let file of fs.readdirSync(path.resolve(dir))) | 
|  | 103     if (path.extname(file) == ".js") | 
|  | 104       knownModules[path.basename(file, ".js")] = path.resolve(dir, file); | 
|  | 105 | 
|  | 106 function addExports(exports) | 
|  | 107 { | 
|  | 108   return function(source) | 
|  | 109   { | 
|  | 110     let extraExports = exports[path.basename(this.filename, ".js")]; | 
|  | 111     if (extraExports) | 
|  | 112       for (let name of extraExports) | 
|  | 113         source += ` | 
|  | 114           Object.defineProperty(exports, "${name}", {get: () => ${name}});`; | 
|  | 115     return source; | 
|  | 116   }; | 
|  | 117 } | 
|  | 118 | 
|  | 119 function rewriteRequires(source) | 
|  | 120 { | 
|  | 121   function escapeString(str) | 
|  | 122   { | 
|  | 123     return str.replace(/(["'\\])/g, "\\$1"); | 
|  | 124   } | 
|  | 125 | 
|  | 126   return source.replace(/(\brequire\(["'])([^"']+)/g, (match, prefix, request) =
     > | 
|  | 127   { | 
|  | 128     if (request in knownModules) | 
|  | 129       return prefix + escapeString(knownModules[request]); | 
|  | 130     return match; | 
|  | 131   }); | 
|  | 132 } | 
|  | 133 | 
|  | 134 exports.createSandbox = function(options) | 
|  | 135 { | 
|  | 136   if (!options) | 
|  | 137     options = {}; | 
|  | 138 | 
|  | 139   let sourceTransformers = [rewriteRequires]; | 
|  | 140   if (options.extraExports) | 
|  | 141     sourceTransformers.push(addExports(options.extraExports)); | 
|  | 142 | 
|  | 143   // This module loads itself into a sandbox, keeping track of the require | 
|  | 144   // function which can be used to load further modules into the sandbox. | 
|  | 145   return SandboxedModule.require("./_common", { | 
|  | 146     globals: Object.assign({}, globals, options.globals), | 
|  | 147     sourceTransformers: sourceTransformers | 
|  | 148   }).require; | 
|  | 149 }; | 
|  | 150 | 
|  | 151 exports.require = require; | 
|  | 152 | 
|  | 153 exports.setupTimerAndXMLHttp = function() | 
|  | 154 { | 
|  | 155   let currentTime = 100000 * MILLIS_IN_HOUR; | 
|  | 156   let startTime = currentTime; | 
|  | 157 | 
|  | 158   let fakeTimer = { | 
|  | 159     callback: null, | 
|  | 160     delay: -1, | 
|  | 161     nextExecution: 0, | 
|  | 162 | 
|  | 163     initWithCallback: function(callback, delay, type) | 
|  | 164     { | 
|  | 165       if (this.callback) | 
|  | 166         throw new Error("Only one timer instance supported"); | 
|  | 167       if (type != 1) | 
|  | 168         throw new Error("Only TYPE_REPEATING_SLACK timers supported"); | 
|  | 169 | 
|  | 170       this.callback = callback; | 
|  | 171       this.delay = delay; | 
|  | 172       this.nextExecution = currentTime + delay; | 
|  | 173     }, | 
|  | 174 | 
|  | 175     trigger: function() | 
|  | 176     { | 
|  | 177       if (currentTime < this.nextExecution) | 
|  | 178         currentTime = this.nextExecution; | 
|  | 179       try | 
|  | 180       { | 
|  | 181         this.callback(); | 
|  | 182       } | 
|  | 183       finally | 
|  | 184       { | 
|  | 185         this.nextExecution = currentTime + this.delay; | 
|  | 186       } | 
|  | 187     }, | 
|  | 188 | 
|  | 189     cancel: function() | 
|  | 190     { | 
|  | 191       this.nextExecution = -1; | 
|  | 192     } | 
|  | 193   }; | 
|  | 194 | 
|  | 195   let requests = []; | 
|  | 196   function XMLHttpRequest() | 
|  | 197   { | 
|  | 198     this._host = "http://example.com"; | 
|  | 199     this._loadHandlers = []; | 
|  | 200     this._errorHandlers = []; | 
|  | 201   }; | 
|  | 202   XMLHttpRequest.prototype = | 
|  | 203   { | 
|  | 204     _path: null, | 
|  | 205     _data: null, | 
|  | 206     _queryString: null, | 
|  | 207     _loadHandlers: null, | 
|  | 208     _errorHandlers: null, | 
|  | 209     status: 0, | 
|  | 210     readyState: 0, | 
|  | 211     responseText: null, | 
|  | 212 | 
|  | 213     addEventListener: function(eventName, handler, capture) | 
|  | 214     { | 
|  | 215       let list; | 
|  | 216       if (eventName == "load") | 
|  | 217         list = this._loadHandlers; | 
|  | 218       else if (eventName == "error") | 
|  | 219         list = this._errorHandlers; | 
|  | 220       else | 
|  | 221         throw new Error("Event type " + eventName + " not supported"); | 
|  | 222 | 
|  | 223       if (list.indexOf(handler) < 0) | 
|  | 224         list.push(handler); | 
|  | 225     }, | 
|  | 226 | 
|  | 227     removeEventListener: function(eventName, handler, capture) | 
|  | 228     { | 
|  | 229       let list; | 
|  | 230       if (eventName == "load") | 
|  | 231         list = this._loadHandlers; | 
|  | 232       else if (eventName == "error") | 
|  | 233         list = this._errorHandlers; | 
|  | 234       else | 
|  | 235         throw new Error("Event type " + eventName + " not supported"); | 
|  | 236 | 
|  | 237       let index = list.indexOf(handler); | 
|  | 238       if (index >= 0) | 
|  | 239         list.splice(index, 1); | 
|  | 240     }, | 
|  | 241 | 
|  | 242     open: function(method, url, async, user, password) | 
|  | 243     { | 
|  | 244       if (method != "GET") | 
|  | 245         throw new Error("Only GET requests are supported"); | 
|  | 246       if (typeof async != "undefined" && !async) | 
|  | 247         throw new Error("Sync requests are not supported"); | 
|  | 248       if (typeof user != "undefined" || typeof password != "undefined") | 
|  | 249         throw new Error("User authentification is not supported"); | 
|  | 250 | 
|  | 251       let match = /^data:[^,]+,/.exec(url); | 
|  | 252       if (match) | 
|  | 253       { | 
|  | 254         this._data = decodeURIComponent(url.substr(match[0].length)); | 
|  | 255         return; | 
|  | 256       } | 
|  | 257 | 
|  | 258       if (url.substr(0, this._host.length + 1) != this._host + "/") | 
|  | 259         throw new Error("Unexpected URL: " + url + " (URL starting with " + this
     ._host + "expected)"); | 
|  | 260 | 
|  | 261       this._path = url.substr(this._host.length); | 
|  | 262 | 
|  | 263       let queryIndex = this._path.indexOf("?"); | 
|  | 264       this._queryString = ""; | 
|  | 265       if (queryIndex >= 0) | 
|  | 266       { | 
|  | 267         this._queryString = this._path.substr(queryIndex + 1); | 
|  | 268         this._path = this._path.substr(0, queryIndex); | 
|  | 269       } | 
|  | 270     }, | 
|  | 271 | 
|  | 272     send: function(data) | 
|  | 273     { | 
|  | 274       if (!this._data && !this._path) | 
|  | 275         throw new Error("No request path set"); | 
|  | 276       if (typeof data != "undefined" && data) | 
|  | 277         throw new Error("Sending data to server is not supported"); | 
|  | 278 | 
|  | 279       requests.push(Promise.resolve().then(() => | 
|  | 280       { | 
|  | 281         let result = [Cr.NS_OK, 404, ""]; | 
|  | 282         if (this._data) | 
|  | 283           result = [Cr.NS_OK, 0, this._data]; | 
|  | 284         else if (this._path in XMLHttpRequest.requestHandlers) | 
|  | 285         { | 
|  | 286           result = XMLHttpRequest.requestHandlers[this._path]({ | 
|  | 287             method: "GET", | 
|  | 288             path: this._path, | 
|  | 289             queryString: this._queryString | 
|  | 290           }); | 
|  | 291         } | 
|  | 292 | 
|  | 293         [this.channel.status, this.channel.responseStatus, this.responseText] = 
     result; | 
|  | 294         this.status = this.channel.responseStatus; | 
|  | 295 | 
|  | 296         let eventName = (this.channel.status == Cr.NS_OK ? "load" : "error"); | 
|  | 297         let event = {type: eventName}; | 
|  | 298         for (let handler of this["_" + eventName + "Handlers"]) | 
|  | 299           handler.call(this, event); | 
|  | 300       })); | 
|  | 301     }, | 
|  | 302 | 
|  | 303     overrideMimeType: function(mime) | 
|  | 304     { | 
|  | 305     }, | 
|  | 306 | 
|  | 307     channel: | 
|  | 308     { | 
|  | 309       status: -1, | 
|  | 310       responseStatus: 0, | 
|  | 311       loadFlags: 0, | 
|  | 312       INHIBIT_CACHING: 0, | 
|  | 313       VALIDATE_ALWAYS: 0, | 
|  | 314       QueryInterface: () => this | 
|  | 315     } | 
|  | 316   }; | 
|  | 317 | 
|  | 318   XMLHttpRequest.requestHandlers = {}; | 
|  | 319   this.registerHandler = (path, handler) => | 
|  | 320   { | 
|  | 321     XMLHttpRequest.requestHandlers[path] = handler; | 
|  | 322   }; | 
|  | 323 | 
|  | 324   function waitForRequests() | 
|  | 325   { | 
|  | 326     if (requests.length) | 
|  | 327     { | 
|  | 328       let result = Promise.all(requests); | 
|  | 329       requests = []; | 
|  | 330       return result.catch(e => | 
|  | 331       { | 
|  | 332         console.error(e); | 
|  | 333       }).then(() => waitForRequests()); | 
|  | 334     } | 
|  | 335     else | 
|  | 336       return Promise.resolve(); | 
|  | 337   } | 
|  | 338 | 
|  | 339   function runScheduledTasks(maxMillis) | 
|  | 340   { | 
|  | 341     let endTime = currentTime + maxMillis; | 
|  | 342     if (fakeTimer.nextExecution < 0 || fakeTimer.nextExecution > endTime) | 
|  | 343     { | 
|  | 344       currentTime = endTime; | 
|  | 345       return Promise.resolve(); | 
|  | 346     } | 
|  | 347     else | 
|  | 348     { | 
|  | 349       fakeTimer.trigger(); | 
|  | 350       return waitForRequests().then(() => runScheduledTasks(endTime - currentTim
     e)); | 
|  | 351     } | 
|  | 352 | 
|  | 353     currentTime = endTime; | 
|  | 354   } | 
|  | 355 | 
|  | 356   this.runScheduledTasks = (maxHours, initial, skip) => | 
|  | 357   { | 
|  | 358     if (typeof maxHours != "number") | 
|  | 359       throw new Error("Numerical parameter expected"); | 
|  | 360     if (typeof initial != "number") | 
|  | 361       initial = 0; | 
|  | 362     if (typeof skip != "number") | 
|  | 363       skip = 0; | 
|  | 364 | 
|  | 365     startTime = currentTime; | 
|  | 366     return Promise.resolve().then(() => | 
|  | 367     { | 
|  | 368       if (initial >= 0) | 
|  | 369       { | 
|  | 370         maxHours -= initial; | 
|  | 371         return runScheduledTasks(initial * MILLIS_IN_HOUR); | 
|  | 372       } | 
|  | 373     }).then(() => | 
|  | 374     { | 
|  | 375       if (skip >= 0) | 
|  | 376       { | 
|  | 377         maxHours -= skip; | 
|  | 378         currentTime += skip * MILLIS_IN_HOUR; | 
|  | 379       } | 
|  | 380       return runScheduledTasks(maxHours * MILLIS_IN_HOUR); | 
|  | 381     }); | 
|  | 382   }; | 
|  | 383 | 
|  | 384   this.getTimeOffset = () => (currentTime - startTime) / MILLIS_IN_HOUR; | 
|  | 385   Object.defineProperty(this, "currentTime", { | 
|  | 386     get: () => currentTime | 
|  | 387   }); | 
|  | 388 | 
|  | 389   return { | 
|  | 390     Cc: { | 
|  | 391       "@mozilla.org/timer;1": { | 
|  | 392         createInstance: () => fakeTimer | 
|  | 393       } | 
|  | 394     }, | 
|  | 395     Ci: { | 
|  | 396       nsITimer: | 
|  | 397       { | 
|  | 398         TYPE_ONE_SHOT: 0, | 
|  | 399         TYPE_REPEATING_SLACK: 1, | 
|  | 400         TYPE_REPEATING_PRECISE: 2 | 
|  | 401       }, | 
|  | 402       nsIHttpChannel: () => null, | 
|  | 403     }, | 
|  | 404     Cr, | 
|  | 405     XMLHttpRequest, | 
|  | 406     Date: { | 
|  | 407       now: () => currentTime | 
|  | 408     } | 
|  | 409   }; | 
|  | 410 }; | 
|  | 411 | 
|  | 412 exports.setupRandomResult = function() | 
|  | 413 { | 
|  | 414   let randomResult = 0.5; | 
|  | 415   Object.defineProperty(this, "randomResult", { | 
|  | 416     get: () => randomResult, | 
|  | 417     set: value => randomResult = value | 
|  | 418   }); | 
|  | 419 | 
|  | 420   return { | 
|  | 421     Math: { | 
|  | 422       random: () => randomResult, | 
|  | 423       min: Math.min, | 
|  | 424       max: Math.max, | 
|  | 425       round: Math.round | 
|  | 426     } | 
|  | 427   }; | 
|  | 428 }; | 
|  | 429 | 
|  | 430 exports.unexpectedError = function(error) | 
|  | 431 { | 
|  | 432   console.error(error); | 
|  | 433   this.ok(false, "Unexpected error: " + error); | 
|  | 434 }; | 
| LEFT | RIGHT | 
|---|