-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpage_context.js
300 lines (276 loc) · 10.5 KB
/
page_context.js
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
var __screenCapturePageContext__ = {
clone: function(object) {
function StubObj() { }
StubObj.prototype = object;
var newObj = new StubObj();
newObj.getInternalObject = function() {
return this.__proto__;
}
newObj.toString = function() {
try {
return this.__proto__.toString();
} catch (e) {
return 'object Object';
}
}
return newObj;
},
bind: function(newThis, func) {
var args = [];
for(var i = 2;i < arguments.length; i++) {
args.push(arguments[i]);
}
return function() {
return func.apply(newThis, args);
}
},
bodyWrapperDelegate_: null,
currentHookStatus_: false,
scrollValueHooker: function(oldValue, newValue, reason) {
// When we hook the value of scrollLeft/Top of body, it always returns 0.
return 0;
},
toggleBodyScrollValueHookStatus: function() {
this.currentHookStatus_ = !this.currentHookStatus_;
if (this.currentHookStatus_) {
var This = this;
try {
document.__defineGetter__('body', function() {
return This.bodyWrapperDelegate_.getWrapper();
});
} catch (e) {
window.console.log('error' + e);
}
this.bodyWrapperDelegate_.watch('scrollLeft', this.scrollValueHooker);
this.bodyWrapperDelegate_.watch('scrollTop', this.scrollValueHooker);
} else {
this.bodyWrapperDelegate_.unwatch('scrollLeft', this.scrollValueHooker);
this.bodyWrapperDelegate_.unwatch('scrollTop', this.scrollValueHooker);
var This = this;
try {
document.__defineGetter__('body', function() {
return This.bodyWrapperDelegate_.getWrapper().getInternalObject();
});
} catch (e) {
window.console.log('error' + e);
}
}
},
checkHookStatus: function() {
var needHookScrollValue = document.documentElement.getAttributeNode(
'__screen_capture_need_hook_scroll_value__');
needHookScrollValue =
!!(needHookScrollValue && needHookScrollValue.nodeValue == 'true');
if (this.currentHookStatus_ != needHookScrollValue)
this.toggleBodyScrollValueHookStatus();
},
init: function() {
if (!this.bodyWrapperDelegate_) {
this.bodyWrapperDelegate_ =
new __screenCapturePageContext__.ObjectWrapDelegate(
document.body, '^(DOCUMENT_[A-Z_]+|[A-Z_]+_NODE)$');
document.documentElement.addEventListener(
'__screen_capture_check_hook_status_event__',
__screenCapturePageContext__.bind(this, this.checkHookStatus));
}
}
};
// ObjectWrapDelegate class will create a empty object(wrapper), map its
// prototype to the 'originalObject', then search all non-function properties
// (except those properties which match the propertyNameFilter) of the
// 'orginalObject' and set corresponding getter/setter to the wrapper.
// Then you can manipulate the wrapper as 'originalObject' because the wrapper
// use the corresponding getter/setter to access the corresponding properties in
// 'originalObject' and the all function calls can be call through the prototype
// inherit.
// After createing the wrapper object, you can use watch method to monitor any
// property which you want to know when it has been read(get) or change(set).
// Please see the detail comment on method watch.
// Remember the ObjectWrapDelegate returns the wrapDelegateObject instead of
// really wrapper object. You have to use ObjectWrapDelegate.getWrapper to get
// real wrapper object.
// parameter @originalObject, object which you want to wrap
// parameter @propertyNameFilter, string, regular expression pattern string for
// those properties you don't put in the wrap object.
__screenCapturePageContext__.ObjectWrapDelegate = function(
originalObject, propertyNameFilter) {
this.window_ = window;
// The wrapper is the object we use to wrap the 'originalObject'.
this.wrapper_ = __screenCapturePageContext__.clone(originalObject);
// This array saves all properties we set our getter/setter for them.
this.properties_ = [];
// This object to save all watch handlers. Each watch handler is bind to one
// certain property which is in properties_.
this.watcherTable_ = {};
// Check the propertyNameFilter parameter.
if (typeof propertyNameFilter == 'undefined') {
propertyNameFilter = '';
} else if (typeof propertyNameFilter != 'string') {
try {
propertyNameFilter = propertyNameFilter.toString();
} catch (e) {
propertyNameFilter = '';
}
}
if (propertyNameFilter.length) {
this.propertyNameFilter_ = new RegExp('');
this.propertyNameFilter_.compile(propertyNameFilter);
} else {
this.propertyNameFilter_ = null;
}
// For closure to access the private data of class.
var This = this;
// Set the getter object.
function setGetterAndSetter(wrapper, propertyName) {
wrapper.__defineGetter__(propertyName, function() {
var internalObj = this.getInternalObject();
var originalReturnValue = internalObj[propertyName];
var returnValue = originalReturnValue;
// See whether this property has been watched.
var watchers = This.watcherTable_[propertyName];
if (watchers) {
// copy the watcher to a cache in case someone call unwatch inside the
// watchHandler.
var watchersCache = watchers.concat();
for (var i = 0, l = watchersCache.length; i < l; ++i) {
var watcher = watchersCache[i];
if (!watcher) {
window.console.log('wrapper\'s watch for ' + propertyName +
' is unavailable!');
continue; // should never happend
}
originalReturnValue = returnValue;
try {
returnValue = watcher(returnValue, returnValue, 'get');
} catch (e) {
returnValue = originalReturnValue;
}
}
}
return returnValue;
});
// Set the setter object.
wrapper.__defineSetter__(propertyName, function(value) {
var internalObj = this.getInternalObject();
var originalValue = value;
var userValue = originalValue;
var oldValue;
try {
oldValue = internalObj[propertyName];
} catch (e) {
oldValue = null;
}
// See whether this property has been watched.
var watchers = This.watcherTable_[propertyName];
if (watchers) {
// copy the watcher to a cache in case someone call unwatch inside the
// watchHandler.
var watchersCache = watchers.concat();
for (var i = 0, l = watchersCache.length; i < l; ++i) {
var watcher = watchersCache[i];
if (!watcher) {
window.console.log('wrapper\'s watch for ' + propertyName +
' is unavailable!');
continue; // should never happend
}
originalValue = userValue;
try {
userValue = watcher(oldValue, userValue, 'set');
} catch (e) {
userValue = originalValue;
}
}
}
internalObj[propertyName] = userValue;
});
};
this.cleanUp_ = function() {
This.window_.removeEventListener('unload', This.cleanUp_, false);
// Delete all properties
for (var i = 0, l = This.properties_.length; i < l; ++i) {
delete This.wrapper_[This.properties_[i]];
}
This.window_ = null;
This.wrapper_ = null;
This.properties_ = null;
This.watcherTable_ = null;
This.propertyNameFilter_ = null;
This = null;
}
// We only bridge the non-function properties.
for (var prop in originalObject) {
if (this.propertyNameFilter_ && this.propertyNameFilter_.test(prop)) {
this.propertyNameFilter_.test('');
continue;
}
if (typeof originalObject[prop] != 'function') {
this.properties_.push(prop);
setGetterAndSetter(this.wrapper_, prop);
}
}
// Listen the unload event.
this.window_.addEventListener('unload', this.cleanUp_, false);
};
__screenCapturePageContext__.ObjectWrapDelegate.prototype.getWrapper =
function() {
return this.wrapper_;
}
// Check whether a property is in the wrapper or not. If yes, return true.
// Otherwise return false.
__screenCapturePageContext__.ObjectWrapDelegate.prototype.hasProperty =
function(propertyName) {
for (var i = 0, l = this.properties_.length; i < l; ++i) {
if (propertyName == this.properties_[i])
return true;
}
return false;
}
// Watches for a property to be accessed or be assigned a value and runs a
// function when that occurs.
// Watches for accessing a property or assignment to a property named prop in
// this object, calling handler(oldval, newval, reason) whenever prop is
// get/set and storing the return value in that property.
// A watchpoint can filter (or nullify) the value assignment, by returning a
// modified newval (or by returning oldval).
// When watchpoint is trigering by get opeartor, the oldval is equal with
// newval. The reason will be 'get'.
// When watchpoint is trigering by set opeartor, The reason will be 'set'.
// If you delete a property for which a watchpoint has been set,
// that watchpoint does not disappear. If you later recreate the property,
// the watchpoint is still in effect.
// To remove a watchpoint, use the unwatch method.
// If register the watchpoint successfully, return true. Otherwise return false.
__screenCapturePageContext__.ObjectWrapDelegate.prototype.watch = function(
propertyName, watchHandler) {
if (!this.hasProperty(propertyName))
return false;
var watchers = this.watcherTable_[propertyName];
if (watchers) {
for (var i = 0, l = watchers.length; i < l; ++i) {
if (watchHandler == watchers[i])
return true;
}
} else {
watchers = new Array();
this.watcherTable_[propertyName] = watchers;
}
watchers.push(watchHandler);
return true;
}
// Removes a watchpoint set with the watch method.
__screenCapturePageContext__.ObjectWrapDelegate.prototype.unwatch = function(
propertyName, watchHandler) {
if (!this.hasProperty(propertyName))
return false;
var watchers = this.watcherTable_[propertyName];
if (watchers) {
for (var i = 0, l = watchers.length; i < l; ++i) {
if (watchHandler == watchers[i]) {
watchers.splice(i, 1);
return true;
}
}
}
return false;
}
__screenCapturePageContext__.init();