| Index: lib/adblockplus_compat.js |
| =================================================================== |
| new file mode 100644 |
| --- /dev/null |
| +++ b/lib/adblockplus_compat.js |
| @@ -0,0 +1,501 @@ |
| +/* |
| + * This Source Code is subject to the terms of the Mozilla Public License |
| + * version 2.0 (the "License"). You can obtain a copy of the License at |
| + * http://mozilla.org/MPL/2.0/. |
| + */ |
| + |
| +// TODO: Parts of this file are identical to ABP/Chrome, these should be split |
| +// away into a separate file. |
| + |
| +// |
| +// Module framework stuff |
| +// |
| + |
| +function require(module) |
| +{ |
| + return require.scopes[module]; |
| +} |
| +require.scopes = {__proto__: null}; |
| + |
| +function importAll(module, globalObj) |
| +{ |
| + var exports = require(module); |
| + for (var key in exports) |
| + globalObj[key] = exports[key]; |
| +} |
| + |
| +onShutdown = { |
| + done: false, |
| + add: function() {}, |
| + remove: function() {} |
| +}; |
| + |
| +// |
| +// XPCOM emulation |
| +// |
| + |
| +var Components = |
| +{ |
| + interfaces: |
| + { |
| + nsIFile: {DIRECTORY_TYPE: 0}, |
| + nsIFileURL: function() {}, |
| + nsIHttpChannel: function() {}, |
| + nsITimer: {TYPE_REPEATING_SLACK: 0}, |
| + nsIInterfaceRequestor: null, |
| + nsIChannelEventSink: null |
| + }, |
| + classes: |
| + { |
| + "@mozilla.org/timer;1": |
| + { |
| + createInstance: function() |
| + { |
| + return new FakeTimer(); |
| + } |
| + }, |
| + "@mozilla.org/xmlextras/xmlhttprequest;1": |
| + { |
| + createInstance: function() |
| + { |
| + return new XMLHttpRequest(); |
| + } |
| + } |
| + }, |
| + results: {}, |
| + utils: { |
| + reportError: function(e) |
| + { |
| + opera.postError(e + "\n" + (typeof e == "object" ? e.stack : new Error().stack)); |
| + } |
| + }, |
| + manager: null, |
| + ID: function() |
| + { |
| + return null; |
| + } |
| +}; |
| +const Cc = Components.classes; |
| +const Ci = Components.interfaces; |
| +const Cr = Components.results; |
| +const Cu = Components.utils; |
| + |
| +var XPCOMUtils = |
| +{ |
| + generateQI: function() {} |
| +}; |
| + |
| +// |
| +// Info pseudo-module |
| +// |
| + |
| +require.scopes.info = |
| +{ |
| + get addonID() |
| + { |
| + return widget.id; |
| + }, |
| + addonVersion: "2.0.3", // Hardcoded for now |
| + addonRoot: "", |
| + get addonName() |
| + { |
| + return widget.name; |
| + }, |
| + application: "opera" |
| +}; |
| + |
| +// |
| +// IO module: no direct file system access, using FileSystem API |
| +// |
| + |
| +require.scopes.io = |
| +{ |
| + IO: { |
| + _getFilePath: function(file) |
| + { |
| + if (file instanceof FakeFile) |
| + return file.path; |
| + else if ("spec" in file) |
| + return file.spec; |
| + |
| + throw new Error("Unexpected file type"); |
| + }, |
| + |
| + _setFileContents: function(path, contents, lastModified) |
| + { |
| + window.localStorage[path] = contents; |
| + window.localStorage[path + "/lastModified"] = lastModified || 0; |
| + }, |
| + |
| + lineBreak: "\n", |
| + |
| + resolveFilePath: function(path) |
| + { |
| + return new FakeFile(path); |
| + }, |
| + |
| + readFromFile: function(file, decode, listener, callback, timeLineID) |
| + { |
| + // Fake asynchronous execution |
| + setTimeout(function() |
| + { |
| + if ("spec" in file && /^defaults\b/.test(file.spec)) |
| + { |
| + // Code attempts to read the default patterns.ini, we don't have that. |
| + // Make sure to execute first-run actions instead. |
| + callback(null); |
| + executeFirstRunActions(); |
|
Felix Dahlke
2012/10/11 13:10:02
This function cannot be called from here.
|
| + return; |
| + } |
| + |
| + var path = this._getFilePath(file); |
| + if (!(path in window.localStorage)) |
| + { |
| + callback(new Error("File doesn't exist")) |
| + return; |
| + } |
| + |
| + var lines = window.localStorage[path].split(/[\r\n]+/); |
| + for (var i = 0; i < lines.length; i++) |
| + listener.process(lines[i]); |
| + listener.process(null); |
| + callback(null); |
| + }.bind(this), 0); |
| + }, |
| + |
| + writeToFile: function(file, encode, data, callback, timeLineID) |
| + { |
| + var path = this._getFilePath(file); |
| + this._setFileContents(path, data.join(this.lineBreak) + this.lineBreak, Date.now()); |
| + |
| + // Fake asynchronous execution |
| + setTimeout(callback.bind(null, null), 0); |
| + }, |
| + |
| + copyFile: function(fromFile, toFile, callback) |
| + { |
| + // Simply combine read and write operations |
| + var data = []; |
| + this.readFromFile(fromFile, false, { |
| + process: function(line) |
| + { |
| + if (line !== null) |
| + data.push(line); |
| + } |
| + }, function(e) |
| + { |
| + if (e) |
| + callback(e); |
| + else |
| + this.writeToFile(toFile, false, data, callback); |
| + }.bind(this)); |
| + }, |
| + |
| + renameFile: function(fromFile, newName, callback) |
| + { |
| + var path = this._getFilePath(fromFile); |
| + if (!(path in window.localStorage)) |
| + { |
| + callback(new Error("File doesn't exist")) |
| + return; |
| + } |
| + |
| + this._setFileContents(newName, window.localStorage[path], window.localStorage[path + "/lastModified"]); |
| + this.removeFile(fromFile, callback); |
| + }, |
| + |
| + removeFile: function(file, callback) |
| + { |
| + var path = this._getFilePath(file); |
| + delete window.localStorage[path]; |
| + delete window.localStorage[path + "/lastModified"]; |
| + callback(null); |
| + }, |
| + |
| + statFile: function(file, callback) |
| + { |
| + var path = this._getFilePath(file); |
| + callback(null, { |
| + exists: path in window.localStorage, |
| + isDirectory: false, |
| + isFile: true, |
| + lastModified: parseInt(window.localStorage[path + "/lastModified"], 10) || 0 |
| + }); |
| + } |
| + } |
| +}; |
| + |
| +// |
| +// Fake nsIFile implementation for our I/O |
| +// |
| + |
| +function FakeFile(path) |
| +{ |
| + this.path = path; |
| +} |
| +FakeFile.prototype = |
| +{ |
| + get leafName() |
| + { |
| + return this.path; |
| + }, |
| + set leafName(value) |
| + { |
| + this.path = value; |
| + }, |
| + append: function(path) |
| + { |
| + this.path += path; |
| + }, |
| + clone: function() |
| + { |
| + return new FakeFile(this.path); |
| + }, |
| + get parent() |
| + { |
| + return {create: function() {}}; |
| + }, |
| + normalize: function() {} |
| +}; |
| + |
| +// |
| +// Prefs module: the values are hardcoded for now. |
| +// |
| + |
| +require.scopes.prefs = { |
| + Prefs: { |
| + enabled: true, |
| + patternsfile: "patterns.ini", |
| + patternsbackups: 0, |
| + patternsbackupinterval: 24, |
| + data_directory: "", |
| + savestats: false, |
| + privateBrowsing: false, |
| + subscriptions_fallbackerrors: 5, |
| + subscriptions_fallbackurl: "https://adblockplus.org/getSubscription?version=%VERSION%&url=%SUBSCRIPTION%&downloadURL=%URL%&error=%ERROR%&channelStatus=%CHANNELSTATUS%&responseStatus=%RESPONSESTATUS%", |
| + subscriptions_autoupdate: true, |
| + subscriptions_exceptionsurl: "https://easylist-downloads.adblockplus.org/exceptionrules.txt", |
| + documentation_link: "https://adblockplus.org/redirect?link=%LINK%&lang=%LANG%", |
| + addListener: function() {} |
| + } |
| +}; |
| + |
| +// |
| +// Utils module |
| +// |
| + |
| +require.scopes.utils = |
| +{ |
| + Utils: { |
| + systemPrincipal: null, |
| + getString: function(id) |
| + { |
| + return id; |
| + }, |
| + runAsync: function(callback, thisPtr) |
| + { |
| + var params = Array.prototype.slice.call(arguments, 2); |
| + window.setTimeout(function() |
| + { |
| + callback.apply(thisPtr, params); |
| + }, 0); |
| + }, |
| + get appLocale() |
| + { |
| + // Note: navigator.language |
| + return window.navigator.browserLanguage; |
| + }, |
| + generateChecksum: function(lines) |
| + { |
| + // We cannot calculate MD5 checksums yet :-( |
| + return null; |
| + }, |
| + makeURI: function(url) |
| + { |
| + return Services.io.newURI(url); |
| + }, |
| + checkLocalePrefixMatch: function(prefixes) |
| + { |
| + if (!prefixes) |
| + return null; |
| + |
| + var list = prefixes.split(","); |
| + for (var i = 0; i < list.length; i++) |
| + if (new RegExp("^" + list[i] + "\\b").test(this.appLocale)) |
| + return list[i]; |
| + |
| + return null; |
| + } |
| + } |
| +}; |
| + |
| +// |
| +// ElemHideHitRegistration dummy implementation |
| +// |
| + |
| +require.scopes.elemHideHitRegistration = |
| +{ |
| + AboutHandler: {} |
| +}; |
| + |
| +// |
| +// Services.jsm module emulation |
| +// |
| + |
| +var Services = |
| +{ |
| + io: { |
| + newURI: function(uri) |
| + { |
| + if (!uri.length || uri[0] == "~") |
| + throw new Error("Invalid URI"); |
| + |
| + /^([^:\/]*)/.test(uri); |
| + var scheme = RegExp.$1.toLowerCase(); |
| + |
| + return { |
| + scheme: scheme, |
| + spec: uri, |
| + QueryInterface: function() |
| + { |
| + return this; |
| + } |
| + }; |
| + }, |
| + newFileURI: function(file) |
| + { |
| + var result = this.newURI("file:///" + file.path); |
| + result.file = file; |
| + return result; |
| + } |
| + }, |
| + obs: { |
| + addObserver: function() {}, |
| + removeObserver: function() {} |
| + }, |
| + vc: { |
| + compare: function(v1, v2) |
| + { |
| + function parsePart(s) |
| + { |
| + if (!s) |
| + return parsePart("0"); |
| + |
| + var part = { |
| + numA: 0, |
| + strB: "", |
| + numC: 0, |
| + extraD: "" |
| + }; |
| + |
| + if (s === "*") |
| + { |
| + part.numA = Number.MAX_VALUE; |
| + return part; |
| + } |
| + |
| + var matches = s.match(/(\d*)(\D*)(\d*)(.*)/); |
| + part.numA = parseInt(matches[1], 10) || part.numA; |
| + part.strB = matches[2] || part.strB; |
| + part.numC = parseInt(matches[3], 10) || part.numC; |
| + part.extraD = matches[4] || part.extraD; |
| + |
| + if (part.strB == "+") |
| + { |
| + part.numA++; |
| + part.strB = "pre"; |
| + } |
| + |
| + return part; |
| + } |
| + |
| + function comparePartElement(s1, s2) |
| + { |
| + if (s1 === "" && s2 !== "") |
| + return 1; |
| + if (s1 !== "" && s2 === "") |
| + return -1; |
| + return s1 === s2 ? 0 : (s1 > s2 ? 1 : -1); |
| + } |
| + |
| + function compareParts(p1, p2) |
| + { |
| + var result = 0; |
| + var elements = ["numA", "strB", "numC", "extraD"]; |
| + elements.some(function(element) |
| + { |
| + result = comparePartElement(p1[element], p2[element]); |
| + return result; |
| + }); |
| + return result; |
| + } |
| + |
| + var parts1 = v1.split("."); |
| + var parts2 = v2.split("."); |
| + for (var i = 0; i < Math.max(parts1.length, parts2.length); i++) |
| + { |
| + var result = compareParts(parsePart(parts1[i]), parsePart(parts2[i])); |
| + if (result) |
| + return result; |
| + } |
| + return 0; |
| + } |
| + } |
| +} |
| + |
| +// |
| +// FileUtils.jsm module emulation |
| +// |
| + |
| +var FileUtils = |
| +{ |
| + PERMS_DIRECTORY: 0 |
| +}; |
| + |
| +function FakeTimer() |
| +{ |
| +} |
| +FakeTimer.prototype = |
| +{ |
| + delay: 0, |
| + callback: null, |
| + initWithCallback: function(callback, delay) |
| + { |
| + this.callback = callback; |
| + this.delay = delay; |
| + this.scheduleTimeout(); |
| + }, |
| + scheduleTimeout: function() |
| + { |
| + var me = this; |
| + window.setTimeout(function() |
| + { |
| + try |
| + { |
| + me.callback(); |
| + } |
| + catch(e) |
| + { |
| + Cu.reportError(e); |
| + } |
| + me.scheduleTimeout(); |
| + }, this.delay); |
| + } |
| +}; |
| + |
| +// |
| +// Add a channel property to XMLHttpRequest, Synchronizer needs it |
| +// |
| + |
| +XMLHttpRequest.prototype.channel = |
| +{ |
| + status: -1, |
| + notificationCallbacks: {}, |
| + loadFlags: 0, |
| + INHIBIT_CACHING: 0, |
| + VALIDATE_ALWAYS: 0, |
| + QueryInterface: function() |
| + { |
| + return this; |
| + } |
| +}; |