forked from Infocatcher/Private_Tab
-
Notifications
You must be signed in to change notification settings - Fork 0
/
patcher.jsm
151 lines (151 loc) · 4.66 KB
/
patcher.jsm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
var EXPORTED_SYMBOLS = ["patcher"];
var patcher = {
// Do some magic to restore third party wrappers from other extensions
wrapNS: "patcher::",
init: function(ns, logger) {
this.wrapNS = ns;
if(logger)
_log = logger;
},
destroy: function() {
_log = function() {};
},
wrapFunction: function(obj, meth, key, callBefore, callAfter) {
var win = this.getGlobal(obj);
var name = key;
key = this.wrapNS + key;
var orig, wrapped;
if(!(key in win)) {
_log("[patcher] Patch " + name);
orig = obj[meth];
wrapped = obj[meth] = callAfter
? function wrapper() {
var res = win[key].before.apply(this, arguments);
if(res)
return typeof res == "object" ? res.value : undefined;
try {
var ret = orig.apply(this, arguments);
}
catch(e) {
Components.utils.reportError(e);
}
// Note: used "win.Array" to work after unloading of our .jsm
win[key].after.apply(this, [ret].concat(win.Array.prototype.slice.call(arguments)));
return ret;
}
: function wrapper() {
var res = win[key].before.apply(this, arguments);
if(res)
return typeof res == "object" ? res.value : undefined;
return orig.apply(this, arguments);
};
// Someone may want to do eval() patch...
var getGlobal = function() {
if(win instanceof Components.interfaces.nsIDOMWindow)
return ["window", ""];
var global = "_global_" + Math.random().toFixed(14).substr(2);
return [
global,
"\n\tvar " + global + " = Components.utils.getGlobalForObject(this);"
];
};
var patch = callAfter
? function(s) {
var rnd = Math.random().toFixed(14).substr(2);
var res = "_res_" + rnd;
var ret = "_ret_" + rnd;
var [global, ensureGlobal] = getGlobal();
return s
.replace(
/\{(?:\s*("|')use strict\1;)?/,
"$&" + ensureGlobal
+ "\n\tvar " + res + " = " + global + '["' + key + '"].before.apply(this, arguments);\n'
+ '\tif(' + res + ') return typeof ' + res + ' == "object" ? ' + res + '.value : undefined;\n'
+ "\tvar " + ret + " = (function() {\n"
)
.replace(
/\}$/,
"\t}).apply(this, arguments);\n"
+ "\t" + global + '["' + key + '"].after'
+ '.apply(this, [' + ret + "].concat(Array.prototype.slice.call(arguments)));\n"
+ "\treturn " + ret + ";\n"
+ "}"
);
}
: function(s) {
var rnd = Math.random().toFixed(14).substr(2);
var res = "_res_" + rnd;
var [global, ensureGlobal] = getGlobal();
return s.replace(
/\{(?:\s*("|')use strict\1;)?/,
"$&" + ensureGlobal
+ "\n\tvar " + res + " = " + global + '["' + key + '"].before.apply(this, arguments);\n'
+ '\tif(' + res + ') return typeof ' + res + ' == "object" ? ' + res + '.value : undefined;\n'
);
};
wrapped.toString = function() {
return patch(orig.toString());
};
wrapped.toSource = function() {
return patch(orig.toSource());
};
}
else {
_log("[patcher] Will use previous patch for " + name);
orig = win[key].orig; // Leave ability to unwrap (and break all third-party wrappers!)
}
if(callAfter)
callAfter.before = callBefore;
win[key] = {
before: callBefore,
after: callAfter,
orig: orig,
wrapped: wrapped,
enabled: true
};
},
unwrapFunction: function(obj, meth, key, forceDestroy) {
var win = this.getGlobal(obj);
var name = key;
key = this.wrapNS + key;
if(!(key in win))
return;
var wrapper = win[key];
var wrapped = wrapper.wrapped;
var canRestore = obj[meth] == wrapped;
if(canRestore || forceDestroy) {
_log("[patcher] Restore " + name + (canRestore ? "" : " [force]"));
delete win[key];
obj[meth] = wrapper.orig;
}
else {
_log("[patcher] !!! Can't completely restore " + name + ": detected third-party wrapper!");
if(wrapped) { // First failure, all next iterations will use already existing wrapper
delete wrapped.toString;
delete wrapped.toSource;
wrapper.wrapped = undefined;
}
wrapper.before = wrapper.after = function empty() {};
wrapper.enabled = false;
if(win instanceof Components.interfaces.nsIDOMWindow) {
win.addEventListener("unload", function destroyWrapper(e) {
win.removeEventListener(e.type, destroyWrapper, false);
delete win[key];
obj[meth] = wrapper.orig;
}, false);
}
}
},
isWrapped: function(obj, key) {
var win = this.getGlobal(obj);
key = this.wrapNS + key;
return key in win && win[key].enabled;
},
getGlobal: function(obj) {
var g = Components.utils.getGlobalForObject(obj);
if(g instanceof Components.interfaces.nsIDOMWindow)
return g.window; // Trick for [object Sandbox] in Firefox 45+
return g;
}
};
function _log() {}