blob: ef35563970c4328e7d01dfc763f948838c04c72a [file] [log] [blame]
avm9996304def3e2016-11-27 22:53:05 +01001var __screenCapturePageContext__ = {
2 clone: function(object) {
3 function StubObj() { }
4 StubObj.prototype = object;
5 var newObj = new StubObj();
6 newObj.getInternalObject = function() {
7 return this.__proto__;
8 }
9 newObj.toString = function() {
10 try {
11 return this.__proto__.toString();
12 } catch (e) {
13 return 'object Object';
14 }
15 }
16 return newObj;
17 },
18
19 bind: function(newThis, func) {
20 var args = [];
21 for(var i = 2;i < arguments.length; i++) {
22 args.push(arguments[i]);
23 }
24 return function() {
25 return func.apply(newThis, args);
26 }
27 },
28
29 bodyWrapperDelegate_: null,
30 currentHookStatus_: false,
31
32 scrollValueHooker: function(oldValue, newValue, reason) {
33 // When we hook the value of scrollLeft/Top of body, it always returns 0.
34 return 0;
35 },
36
37 toggleBodyScrollValueHookStatus: function() {
38 this.currentHookStatus_ = !this.currentHookStatus_;
39 if (this.currentHookStatus_) {
40 var This = this;
41 try {
42 document.__defineGetter__('body', function() {
43 return This.bodyWrapperDelegate_.getWrapper();
44 });
45 } catch (e) {
46 window.console.log('error' + e);
47 }
48 this.bodyWrapperDelegate_.watch('scrollLeft', this.scrollValueHooker);
49 this.bodyWrapperDelegate_.watch('scrollTop', this.scrollValueHooker);
50 } else {
51 this.bodyWrapperDelegate_.unwatch('scrollLeft', this.scrollValueHooker);
52 this.bodyWrapperDelegate_.unwatch('scrollTop', this.scrollValueHooker);
53 var This = this;
54 try {
55 document.__defineGetter__('body', function() {
56 return This.bodyWrapperDelegate_.getWrapper().getInternalObject();
57 });
58 } catch (e) {
59 window.console.log('error' + e);
60 }
61 }
62 },
63
64 checkHookStatus: function() {
65 var needHookScrollValue = document.documentElement.getAttributeNode(
66 '__screen_capture_need_hook_scroll_value__');
67 needHookScrollValue =
68 !!(needHookScrollValue && needHookScrollValue.nodeValue == 'true');
69 if (this.currentHookStatus_ != needHookScrollValue)
70 this.toggleBodyScrollValueHookStatus();
71 },
72
73 init: function() {
74 if (!this.bodyWrapperDelegate_) {
75 this.bodyWrapperDelegate_ =
76 new __screenCapturePageContext__.ObjectWrapDelegate(
77 document.body, '^(DOCUMENT_[A-Z_]+|[A-Z_]+_NODE)$');
78 document.documentElement.addEventListener(
79 '__screen_capture_check_hook_status_event__',
80 __screenCapturePageContext__.bind(this, this.checkHookStatus));
81 }
82 }
83};
84
85// ObjectWrapDelegate class will create a empty object(wrapper), map its
86// prototype to the 'originalObject', then search all non-function properties
87// (except those properties which match the propertyNameFilter) of the
88// 'orginalObject' and set corresponding getter/setter to the wrapper.
89// Then you can manipulate the wrapper as 'originalObject' because the wrapper
90// use the corresponding getter/setter to access the corresponding properties in
91// 'originalObject' and the all function calls can be call through the prototype
92// inherit.
93// After createing the wrapper object, you can use watch method to monitor any
94// property which you want to know when it has been read(get) or change(set).
95// Please see the detail comment on method watch.
96// Remember the ObjectWrapDelegate returns the wrapDelegateObject instead of
97// really wrapper object. You have to use ObjectWrapDelegate.getWrapper to get
98// real wrapper object.
99// parameter @originalObject, object which you want to wrap
100// parameter @propertyNameFilter, string, regular expression pattern string for
101// those properties you don't put in the wrap object.
102__screenCapturePageContext__.ObjectWrapDelegate = function(
103 originalObject, propertyNameFilter) {
104 this.window_ = window;
105
106 // The wrapper is the object we use to wrap the 'originalObject'.
107 this.wrapper_ = __screenCapturePageContext__.clone(originalObject);
108
109 // This array saves all properties we set our getter/setter for them.
110 this.properties_ = [];
111
112 // This object to save all watch handlers. Each watch handler is bind to one
113 // certain property which is in properties_.
114 this.watcherTable_ = {};
115
116 // Check the propertyNameFilter parameter.
117 if (typeof propertyNameFilter == 'undefined') {
118 propertyNameFilter = '';
119 } else if (typeof propertyNameFilter != 'string') {
120 try {
121 propertyNameFilter = propertyNameFilter.toString();
122 } catch (e) {
123 propertyNameFilter = '';
124 }
125 }
126 if (propertyNameFilter.length) {
127 this.propertyNameFilter_ = new RegExp('');
128 this.propertyNameFilter_.compile(propertyNameFilter);
129 } else {
130 this.propertyNameFilter_ = null;
131 }
132
133 // For closure to access the private data of class.
134 var This = this;
135
136 // Set the getter object.
137 function setGetterAndSetter(wrapper, propertyName) {
138 wrapper.__defineGetter__(propertyName, function() {
139 var internalObj = this.getInternalObject();
140 var originalReturnValue = internalObj[propertyName];
141 var returnValue = originalReturnValue;
142
143 // See whether this property has been watched.
144 var watchers = This.watcherTable_[propertyName];
145 if (watchers) {
146 // copy the watcher to a cache in case someone call unwatch inside the
147 // watchHandler.
148 var watchersCache = watchers.concat();
149 for (var i = 0, l = watchersCache.length; i < l; ++i) {
150 var watcher = watchersCache[i];
151 if (!watcher) {
152 window.console.log('wrapper\'s watch for ' + propertyName +
153 ' is unavailable!');
154 continue; // should never happend
155 }
156 originalReturnValue = returnValue;
157 try {
158 returnValue = watcher(returnValue, returnValue, 'get');
159 } catch (e) {
160 returnValue = originalReturnValue;
161 }
162 }
163 }
164 return returnValue;
165 });
166
167 // Set the setter object.
168 wrapper.__defineSetter__(propertyName, function(value) {
169 var internalObj = this.getInternalObject();
170 var originalValue = value;
171 var userValue = originalValue;
172 var oldValue;
173 try {
174 oldValue = internalObj[propertyName];
175 } catch (e) {
176 oldValue = null;
177 }
178
179 // See whether this property has been watched.
180 var watchers = This.watcherTable_[propertyName];
181 if (watchers) {
182 // copy the watcher to a cache in case someone call unwatch inside the
183 // watchHandler.
184 var watchersCache = watchers.concat();
185 for (var i = 0, l = watchersCache.length; i < l; ++i) {
186 var watcher = watchersCache[i];
187 if (!watcher) {
188 window.console.log('wrapper\'s watch for ' + propertyName +
189 ' is unavailable!');
190 continue; // should never happend
191 }
192 originalValue = userValue;
193 try {
194 userValue = watcher(oldValue, userValue, 'set');
195 } catch (e) {
196 userValue = originalValue;
197 }
198 }
199 }
200 internalObj[propertyName] = userValue;
201 });
202 };
203
204 this.cleanUp_ = function() {
205 This.window_.removeEventListener('unload', This.cleanUp_, false);
206
207 // Delete all properties
208 for (var i = 0, l = This.properties_.length; i < l; ++i) {
209 delete This.wrapper_[This.properties_[i]];
210 }
211 This.window_ = null;
212 This.wrapper_ = null;
213 This.properties_ = null;
214 This.watcherTable_ = null;
215 This.propertyNameFilter_ = null;
216 This = null;
217 }
218
219 // We only bridge the non-function properties.
220 for (var prop in originalObject) {
221 if (this.propertyNameFilter_ && this.propertyNameFilter_.test(prop)) {
222 this.propertyNameFilter_.test('');
223 continue;
224 }
225 if (typeof originalObject[prop] != 'function') {
226 this.properties_.push(prop);
227 setGetterAndSetter(this.wrapper_, prop);
228 }
229 }
230
231 // Listen the unload event.
232 this.window_.addEventListener('unload', this.cleanUp_, false);
233};
234
235__screenCapturePageContext__.ObjectWrapDelegate.prototype.getWrapper =
236 function() {
237 return this.wrapper_;
238}
239
240// Check whether a property is in the wrapper or not. If yes, return true.
241// Otherwise return false.
242__screenCapturePageContext__.ObjectWrapDelegate.prototype.hasProperty =
243 function(propertyName) {
244 for (var i = 0, l = this.properties_.length; i < l; ++i) {
245 if (propertyName == this.properties_[i])
246 return true;
247 }
248 return false;
249}
250
251// Watches for a property to be accessed or be assigned a value and runs a
252// function when that occurs.
253// Watches for accessing a property or assignment to a property named prop in
254// this object, calling handler(oldval, newval, reason) whenever prop is
255// get/set and storing the return value in that property.
256// A watchpoint can filter (or nullify) the value assignment, by returning a
257// modified newval (or by returning oldval).
258// When watchpoint is trigering by get opeartor, the oldval is equal with
259// newval. The reason will be 'get'.
260// When watchpoint is trigering by set opeartor, The reason will be 'set'.
261// If you delete a property for which a watchpoint has been set,
262// that watchpoint does not disappear. If you later recreate the property,
263// the watchpoint is still in effect.
264// To remove a watchpoint, use the unwatch method.
265// If register the watchpoint successfully, return true. Otherwise return false.
266__screenCapturePageContext__.ObjectWrapDelegate.prototype.watch = function(
267 propertyName, watchHandler) {
268 if (!this.hasProperty(propertyName))
269 return false;
270 var watchers = this.watcherTable_[propertyName];
271 if (watchers) {
272 for (var i = 0, l = watchers.length; i < l; ++i) {
273 if (watchHandler == watchers[i])
274 return true;
275 }
276 } else {
277 watchers = new Array();
278 this.watcherTable_[propertyName] = watchers;
279 }
280 watchers.push(watchHandler);
281 return true;
282}
283
284// Removes a watchpoint set with the watch method.
285__screenCapturePageContext__.ObjectWrapDelegate.prototype.unwatch = function(
286 propertyName, watchHandler) {
287 if (!this.hasProperty(propertyName))
288 return false;
289 var watchers = this.watcherTable_[propertyName];
290 if (watchers) {
291 for (var i = 0, l = watchers.length; i < l; ++i) {
292 if (watchHandler == watchers[i]) {
293 watchers.splice(i, 1);
294 return true;
295 }
296 }
297 }
298 return false;
299}
300__screenCapturePageContext__.init();