Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(25)

Side by Side Diff: Source/core/inspector/InjectedScriptCanvasModuleSource.js

Issue 1073863003: DevTools: remove Canvas profiler from DevTools source base. See details in the bug. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: tests gone Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /**
32 * FIXME: ES5 strict mode check is suppressed due to multiple uses of arguments. callee.
33 * @fileoverview
34 * @suppress {es5Strict}
35 */
36
37 /**
38 * @param {InjectedScriptHostClass} InjectedScriptHost
39 * @param {Window} inspectedWindow
40 * @param {number} injectedScriptId
41 * @param {!InjectedScript} injectedScript
42 */
43 (function (InjectedScriptHost, inspectedWindow, injectedScriptId, injectedScript ) {
44
45 var TypeUtils = {
46 /**
47 * http://www.khronos.org/registry/typedarray/specs/latest/#7
48 * @const
49 * @type {!Array.<function(new:ArrayBufferView, (!ArrayBuffer|!ArrayBufferVi ew), number=, number=)>}
50 */
51 _typedArrayClasses: (function(typeNames) {
52 var result = [];
53 for (var i = 0, n = typeNames.length; i < n; ++i) {
54 if (inspectedWindow[typeNames[i]])
55 result.push(inspectedWindow[typeNames[i]]);
56 }
57 return result;
58 })(["Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Uint16Arr ay", "Int32Array", "Uint32Array", "Float32Array", "Float64Array"]),
59
60 /**
61 * @const
62 * @type {!Array.<string>}
63 */
64 _supportedPropertyPrefixes: ["webkit"],
65
66 /**
67 * @param {*} array
68 * @return {function(new:ArrayBufferView, (!ArrayBuffer|!ArrayBufferView), n umber=, number=)|null}
69 */
70 typedArrayClass: function(array)
71 {
72 var classes = TypeUtils._typedArrayClasses;
73 for (var i = 0, n = classes.length; i < n; ++i) {
74 if (array instanceof classes[i])
75 return classes[i];
76 }
77 return null;
78 },
79
80 /**
81 * @param {*} obj
82 * @return {*}
83 */
84 clone: function(obj)
85 {
86 if (!obj)
87 return obj;
88
89 var type = typeof obj;
90 if (type !== "object" && type !== "function")
91 return obj;
92
93 // Handle Array and ArrayBuffer instances.
94 if (typeof obj.slice === "function") {
95 console.assert(obj instanceof Array || obj instanceof ArrayBuffer);
96 return obj.slice(0);
97 }
98
99 var typedArrayClass = TypeUtils.typedArrayClass(obj);
100 if (typedArrayClass)
101 return new typedArrayClass(/** @type {!ArrayBufferView} */ (obj));
102
103 if (obj instanceof HTMLImageElement) {
104 var img = /** @type {!HTMLImageElement} */ (obj);
105 // Special case for Images with Blob URIs: cloneNode will fail if th e Blob URI has already been revoked.
106 // FIXME: Maybe this is a bug in WebKit core?
107 if (/^blob:/.test(img.src))
108 return TypeUtils.cloneIntoCanvas(img);
109 return img.cloneNode(true);
110 }
111
112 if (obj instanceof HTMLCanvasElement)
113 return TypeUtils.cloneIntoCanvas(obj);
114
115 if (obj instanceof HTMLVideoElement)
116 return TypeUtils.cloneIntoCanvas(obj, obj.videoWidth, obj.videoHeigh t);
117
118 if (obj instanceof ImageData) {
119 var context = TypeUtils._dummyCanvas2dContext();
120 // FIXME: suppress type checks due to outdated builtin externs for c reateImageData.
121 var result = (/** @type {?} */ (context)).createImageData(obj);
122 for (var i = 0, n = obj.data.length; i < n; ++i)
123 result.data[i] = obj.data[i];
124 return result;
125 }
126
127 // Try to convert to a primitive value via valueOf().
128 if (typeof obj.valueOf === "function") {
129 var value = obj.valueOf();
130 var valueType = typeof value;
131 if (valueType !== "object" && valueType !== "function")
132 return value;
133 }
134
135 console.error("ASSERT_NOT_REACHED: failed to clone object: ", obj);
136 return obj;
137 },
138
139 /**
140 * @param {!HTMLImageElement|!HTMLCanvasElement|!HTMLVideoElement} obj
141 * @param {number=} width
142 * @param {number=} height
143 * @return {!HTMLCanvasElement}
144 */
145 cloneIntoCanvas: function(obj, width, height)
146 {
147 var canvas = /** @type {!HTMLCanvasElement} */ (inspectedWindow.document .createElement("canvas"));
148 canvas.width = width || +obj.width;
149 canvas.height = height || +obj.height;
150 var context = /** @type {!CanvasRenderingContext2D} */ (Resource.wrapped Object(canvas.getContext("2d")));
151 context.drawImage(obj, 0, 0);
152 return canvas;
153 },
154
155 /**
156 * @param {?Object=} obj
157 * @return {?Object}
158 */
159 cloneObject: function(obj)
160 {
161 if (!obj)
162 return null;
163 var result = {};
164 for (var key in obj)
165 result[key] = obj[key];
166 return result;
167 },
168
169 /**
170 * @param {!Array.<string>} names
171 * @return {!Object.<string, boolean>}
172 */
173 createPrefixedPropertyNamesSet: function(names)
174 {
175 var result = Object.create(null);
176 for (var i = 0, name; name = names[i]; ++i) {
177 result[name] = true;
178 var suffix = name.substr(0, 1).toUpperCase() + name.substr(1);
179 for (var j = 0, prefix; prefix = TypeUtils._supportedPropertyPrefixe s[j]; ++j)
180 result[prefix + suffix] = true;
181 }
182 return result;
183 },
184
185 /**
186 * @return {number}
187 */
188 now: function()
189 {
190 try {
191 return inspectedWindow.performance.now();
192 } catch(e) {
193 try {
194 return Date.now();
195 } catch(ex) {
196 }
197 }
198 return 0;
199 },
200
201 /**
202 * @param {string} property
203 * @param {!Object} obj
204 * @return {boolean}
205 */
206 isEnumPropertyName: function(property, obj)
207 {
208 return (/^[A-Z][A-Z0-9_]+$/.test(property) && typeof obj[property] === " number");
209 },
210
211 /**
212 * @return {!CanvasRenderingContext2D}
213 */
214 _dummyCanvas2dContext: function()
215 {
216 var context = TypeUtils._dummyCanvas2dContextInstance;
217 if (!context) {
218 var canvas = /** @type {!HTMLCanvasElement} */ (inspectedWindow.docu ment.createElement("canvas"));
219 context = /** @type {!CanvasRenderingContext2D} */ (Resource.wrapped Object(canvas.getContext("2d")));
220 TypeUtils._dummyCanvas2dContextInstance = context;
221 }
222 return context;
223 }
224 }
225
226 /** @typedef {{name:string, valueIsEnum:(boolean|undefined), value:*, values:(!A rray.<!TypeUtils.InternalResourceStateDescriptor>|undefined), isArray:(boolean|u ndefined)}} */
227 TypeUtils.InternalResourceStateDescriptor;
228
229 /**
230 * @interface
231 */
232 function StackTrace()
233 {
234 }
235
236 StackTrace.prototype = {
237 /**
238 * @param {number} index
239 * @return {{sourceURL: string, lineNumber: number, columnNumber: number}|un defined}
240 */
241 callFrame: function(index)
242 {
243 }
244 }
245
246 /**
247 * @param {number=} stackTraceLimit
248 * @param {?Function=} topMostFunctionToIgnore
249 * @return {?StackTrace}
250 */
251 StackTrace.create = function(stackTraceLimit, topMostFunctionToIgnore)
252 {
253 if (typeof Error.captureStackTrace === "function")
254 return new StackTraceV8(stackTraceLimit, topMostFunctionToIgnore || argu ments.callee);
255 // FIXME: Support JSC, and maybe other browsers.
256 return null;
257 }
258
259 /**
260 * @constructor
261 * @implements {StackTrace}
262 * @param {number=} stackTraceLimit
263 * @param {?Function=} topMostFunctionToIgnore
264 * @see http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
265 */
266 function StackTraceV8(stackTraceLimit, topMostFunctionToIgnore)
267 {
268 var oldPrepareStackTrace = Error.prepareStackTrace;
269 var oldStackTraceLimit = Error.stackTraceLimit;
270 if (typeof stackTraceLimit === "number")
271 Error.stackTraceLimit = stackTraceLimit;
272
273 /**
274 * @param {!Object} error
275 * @param {!Array.<!CallSite>} structuredStackTrace
276 * @return {!Array.<{sourceURL: string, lineNumber: number, columnNumber: nu mber}>}
277 */
278 Error.prepareStackTrace = function(error, structuredStackTrace)
279 {
280 return structuredStackTrace.map(function(callSite) {
281 return {
282 sourceURL: callSite.getFileName(),
283 lineNumber: callSite.getLineNumber(),
284 columnNumber: callSite.getColumnNumber()
285 };
286 });
287 }
288
289 var holder = /** @type {{stack: !Array.<{sourceURL: string, lineNumber: numb er, columnNumber: number}>}} */ ({});
290 Error.captureStackTrace(holder, topMostFunctionToIgnore || arguments.callee) ;
291 this._stackTrace = holder.stack;
292
293 Error.stackTraceLimit = oldStackTraceLimit;
294 Error.prepareStackTrace = oldPrepareStackTrace;
295 }
296
297 StackTraceV8.prototype = {
298 /**
299 * @override
300 * @param {number} index
301 * @return {{sourceURL: string, lineNumber: number, columnNumber: number}|un defined}
302 */
303 callFrame: function(index)
304 {
305 return this._stackTrace[index];
306 }
307 }
308
309 /**
310 * @constructor
311 * @template T
312 */
313 function Cache()
314 {
315 this.reset();
316 }
317
318 Cache.prototype = {
319 /**
320 * @return {number}
321 */
322 size: function()
323 {
324 return this._size;
325 },
326
327 reset: function()
328 {
329 /** @type {!Object.<number, !T>} */
330 this._items = Object.create(null);
331 /** @type {number} */
332 this._size = 0;
333 },
334
335 /**
336 * @param {number} key
337 * @return {boolean}
338 */
339 has: function(key)
340 {
341 return key in this._items;
342 },
343
344 /**
345 * @param {number} key
346 * @return {T|undefined}
347 */
348 get: function(key)
349 {
350 return this._items[key];
351 },
352
353 /**
354 * @param {number} key
355 * @param {!T} item
356 */
357 put: function(key, item)
358 {
359 if (!this.has(key))
360 ++this._size;
361 this._items[key] = item;
362 }
363 }
364
365 /**
366 * @constructor
367 * @param {?Resource|!Object} thisObject
368 * @param {string} functionName
369 * @param {!Array|!Arguments} args
370 * @param {!Resource|*=} result
371 * @param {?StackTrace=} stackTrace
372 */
373 function Call(thisObject, functionName, args, result, stackTrace)
374 {
375 this._thisObject = thisObject;
376 this._functionName = functionName;
377 this._args = Array.prototype.slice.call(args, 0);
378 this._result = result;
379 this._stackTrace = stackTrace || null;
380
381 if (!this._functionName)
382 console.assert(this._args.length === 2 && typeof this._args[0] === "stri ng");
383 }
384
385 Call.prototype = {
386 /**
387 * @return {?Resource}
388 */
389 resource: function()
390 {
391 return Resource.forObject(this._thisObject);
392 },
393
394 /**
395 * @return {string}
396 */
397 functionName: function()
398 {
399 return this._functionName;
400 },
401
402 /**
403 * @return {boolean}
404 */
405 isPropertySetter: function()
406 {
407 return !this._functionName;
408 },
409
410 /**
411 * @return {!Array}
412 */
413 args: function()
414 {
415 return this._args;
416 },
417
418 /**
419 * @return {*}
420 */
421 result: function()
422 {
423 return this._result;
424 },
425
426 /**
427 * @return {?StackTrace}
428 */
429 stackTrace: function()
430 {
431 return this._stackTrace;
432 },
433
434 /**
435 * @param {?StackTrace} stackTrace
436 */
437 setStackTrace: function(stackTrace)
438 {
439 this._stackTrace = stackTrace;
440 },
441
442 /**
443 * @param {*} result
444 */
445 setResult: function(result)
446 {
447 this._result = result;
448 },
449
450 /**
451 * @param {string} name
452 * @param {?Object} attachment
453 */
454 setAttachment: function(name, attachment)
455 {
456 if (attachment) {
457 /** @type {?Object.<string, !Object>|undefined} */
458 this._attachments = this._attachments || Object.create(null);
459 this._attachments[name] = attachment;
460 } else if (this._attachments) {
461 delete this._attachments[name];
462 }
463 },
464
465 /**
466 * @param {string} name
467 * @return {?Object}
468 */
469 attachment: function(name)
470 {
471 return this._attachments ? (this._attachments[name] || null) : null;
472 },
473
474 freeze: function()
475 {
476 if (this._freezed)
477 return;
478 this._freezed = true;
479 for (var i = 0, n = this._args.length; i < n; ++i) {
480 // FIXME: freeze the Resources also!
481 if (!Resource.forObject(this._args[i]))
482 this._args[i] = TypeUtils.clone(this._args[i]);
483 }
484 },
485
486 /**
487 * @param {!Cache.<!ReplayableResource>} cache
488 * @return {!ReplayableCall}
489 */
490 toReplayable: function(cache)
491 {
492 this.freeze();
493 var thisObject = /** @type {!ReplayableResource} */ (Resource.toReplayab le(this._thisObject, cache));
494 var result = Resource.toReplayable(this._result, cache);
495 var args = this._args.map(function(obj) {
496 return Resource.toReplayable(obj, cache);
497 });
498 var attachments = TypeUtils.cloneObject(this._attachments);
499 return new ReplayableCall(thisObject, this._functionName, args, result, this._stackTrace, attachments);
500 },
501
502 /**
503 * @param {!ReplayableCall} replayableCall
504 * @param {!Cache.<!Resource>} cache
505 * @return {!Call}
506 */
507 replay: function(replayableCall, cache)
508 {
509 var replayableResult = replayableCall.result();
510 if (replayableResult instanceof ReplayableResource && !cache.has(replaya bleResult.id())) {
511 var resource = replayableResult.replay(cache);
512 console.assert(resource.calls().length > 0, "Expected create* call f or the Resource");
513 return resource.calls()[0];
514 }
515
516 var replayObject = ReplayableResource.replay(replayableCall.replayableRe source(), cache);
517 var replayArgs = replayableCall.args().map(function(obj) {
518 return ReplayableResource.replay(obj, cache);
519 });
520 var replayResult = undefined;
521
522 if (replayableCall.isPropertySetter())
523 replayObject[replayArgs[0]] = replayArgs[1];
524 else {
525 var replayFunction = replayObject[replayableCall.functionName()];
526 console.assert(typeof replayFunction === "function", "Expected a fun ction to replay");
527 replayResult = replayFunction.apply(replayObject, replayArgs);
528
529 if (replayableResult instanceof ReplayableResource) {
530 var resource = replayableResult.replay(cache);
531 if (!resource.wrappedObject())
532 resource.setWrappedObject(replayResult);
533 }
534 }
535
536 this._thisObject = replayObject;
537 this._functionName = replayableCall.functionName();
538 this._args = replayArgs;
539 this._result = replayResult;
540 this._stackTrace = replayableCall.stackTrace();
541 this._freezed = true;
542 var attachments = replayableCall.attachments();
543 this._attachments = attachments ? TypeUtils.cloneObject(attachments) : n ull;
544 return this;
545 }
546 }
547
548 /**
549 * @constructor
550 * @param {!ReplayableResource} thisObject
551 * @param {string} functionName
552 * @param {!Array.<!ReplayableResource|*>} args
553 * @param {!ReplayableResource|*} result
554 * @param {?StackTrace} stackTrace
555 * @param {?Object.<string, !Object>} attachments
556 */
557 function ReplayableCall(thisObject, functionName, args, result, stackTrace, atta chments)
558 {
559 this._thisObject = thisObject;
560 this._functionName = functionName;
561 this._args = args;
562 this._result = result;
563 this._stackTrace = stackTrace;
564 if (attachments)
565 this._attachments = attachments;
566 }
567
568 ReplayableCall.prototype = {
569 /**
570 * @return {!ReplayableResource}
571 */
572 replayableResource: function()
573 {
574 return this._thisObject;
575 },
576
577 /**
578 * @return {string}
579 */
580 functionName: function()
581 {
582 return this._functionName;
583 },
584
585 /**
586 * @return {boolean}
587 */
588 isPropertySetter: function()
589 {
590 return !this._functionName;
591 },
592
593 /**
594 * @return {string}
595 */
596 propertyName: function()
597 {
598 console.assert(this.isPropertySetter());
599 return /** @type {string} */ (this._args[0]);
600 },
601
602 /**
603 * @return {*}
604 */
605 propertyValue: function()
606 {
607 console.assert(this.isPropertySetter());
608 return this._args[1];
609 },
610
611 /**
612 * @return {!Array.<!ReplayableResource|*>}
613 */
614 args: function()
615 {
616 return this._args;
617 },
618
619 /**
620 * @return {!ReplayableResource|*}
621 */
622 result: function()
623 {
624 return this._result;
625 },
626
627 /**
628 * @return {?StackTrace}
629 */
630 stackTrace: function()
631 {
632 return this._stackTrace;
633 },
634
635 /**
636 * @return {?Object.<string, !Object>}
637 */
638 attachments: function()
639 {
640 return this._attachments || null;
641 },
642
643 /**
644 * @param {string} name
645 * @return {!Object}
646 */
647 attachment: function(name)
648 {
649 return this._attachments && this._attachments[name];
650 },
651
652 /**
653 * @param {!Cache.<!Resource>} cache
654 * @return {!Call}
655 */
656 replay: function(cache)
657 {
658 var call = /** @type {!Call} */ (Object.create(Call.prototype));
659 return call.replay(this, cache);
660 }
661 }
662
663 /**
664 * @constructor
665 * @param {!Object} wrappedObject
666 * @param {string} name
667 */
668 function Resource(wrappedObject, name)
669 {
670 /** @type {number} */
671 this._id = ++Resource._uniqueId;
672 /** @type {string} */
673 this._name = name || "Resource";
674 /** @type {number} */
675 this._kindId = Resource._uniqueKindIds[this._name] = (Resource._uniqueKindId s[this._name] || 0) + 1;
676 /** @type {?ResourceTrackingManager} */
677 this._resourceManager = null;
678 /** @type {!Array.<!Call>} */
679 this._calls = [];
680 /**
681 * This is to prevent GC from collecting associated resources.
682 * Otherwise, for example in WebGL, subsequent calls to gl.getParameter()
683 * may return a recently created instance that is no longer bound to a
684 * Resource object (thus, no history to replay it later).
685 *
686 * @type {!Object.<string, !Resource>}
687 */
688 this._boundResources = Object.create(null);
689 this.setWrappedObject(wrappedObject);
690 }
691
692 /**
693 * @type {number}
694 */
695 Resource._uniqueId = 0;
696
697 /**
698 * @type {!Object.<string, number>}
699 */
700 Resource._uniqueKindIds = {};
701
702 /**
703 * @param {*} obj
704 * @return {?Resource}
705 */
706 Resource.forObject = function(obj)
707 {
708 if (!obj)
709 return null;
710 if (obj instanceof Resource)
711 return obj;
712 if (typeof obj === "object")
713 return obj["__resourceObject"];
714 return null;
715 }
716
717 /**
718 * @param {!Resource|*} obj
719 * @return {*}
720 */
721 Resource.wrappedObject = function(obj)
722 {
723 var resource = Resource.forObject(obj);
724 return resource ? resource.wrappedObject() : obj;
725 }
726
727 /**
728 * @param {!Resource|*} obj
729 * @param {!Cache.<!ReplayableResource>} cache
730 * @return {!ReplayableResource|*}
731 */
732 Resource.toReplayable = function(obj, cache)
733 {
734 var resource = Resource.forObject(obj);
735 return resource ? resource.toReplayable(cache) : obj;
736 }
737
738 Resource.prototype = {
739 /**
740 * @return {number}
741 */
742 id: function()
743 {
744 return this._id;
745 },
746
747 /**
748 * @return {string}
749 */
750 name: function()
751 {
752 return this._name;
753 },
754
755 /**
756 * @return {string}
757 */
758 description: function()
759 {
760 return this._name + "@" + this._kindId;
761 },
762
763 /**
764 * @return {!Object}
765 */
766 wrappedObject: function()
767 {
768 return this._wrappedObject;
769 },
770
771 /**
772 * @param {!Object} value
773 */
774 setWrappedObject: function(value)
775 {
776 console.assert(value, "wrappedObject should not be NULL");
777 console.assert(!(value instanceof Resource), "Binding a Resource object to another Resource object?");
778 this._wrappedObject = value;
779 this._bindObjectToResource(value);
780 },
781
782 /**
783 * @return {!Object}
784 */
785 proxyObject: function()
786 {
787 if (!this._proxyObject)
788 this._proxyObject = this._wrapObject();
789 return this._proxyObject;
790 },
791
792 /**
793 * @return {?ResourceTrackingManager}
794 */
795 manager: function()
796 {
797 return this._resourceManager;
798 },
799
800 /**
801 * @param {!ResourceTrackingManager} value
802 */
803 setManager: function(value)
804 {
805 this._resourceManager = value;
806 },
807
808 /**
809 * @return {!Array.<!Call>}
810 */
811 calls: function()
812 {
813 return this._calls;
814 },
815
816 /**
817 * @return {?ContextResource}
818 */
819 contextResource: function()
820 {
821 if (this instanceof ContextResource)
822 return /** @type {!ContextResource} */ (this);
823
824 if (this._calculatingContextResource)
825 return null;
826
827 this._calculatingContextResource = true;
828 var result = null;
829 for (var i = 0, n = this._calls.length; i < n; ++i) {
830 result = this._calls[i].resource().contextResource();
831 if (result)
832 break;
833 }
834 delete this._calculatingContextResource;
835 console.assert(result, "Failed to find context resource for " + this._na me + "@" + this._kindId);
836 return result;
837 },
838
839 /**
840 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
841 */
842 currentState: function()
843 {
844 var result = [];
845 var proxyObject = this.proxyObject();
846 if (!proxyObject)
847 return result;
848 var statePropertyNames = this._proxyStatePropertyNames || [];
849 for (var i = 0, n = statePropertyNames.length; i < n; ++i) {
850 var pname = statePropertyNames[i];
851 result.push({ name: pname, value: proxyObject[pname] });
852 }
853 result.push({ name: "context", value: this.contextResource() });
854 return result;
855 },
856
857 /**
858 * @return {string}
859 */
860 toDataURL: function()
861 {
862 return "";
863 },
864
865 /**
866 * @param {!Cache.<!ReplayableResource>} cache
867 * @return {!ReplayableResource}
868 */
869 toReplayable: function(cache)
870 {
871 var result = cache.get(this._id);
872 if (result)
873 return result;
874 var data = {
875 id: this._id,
876 name: this._name,
877 kindId: this._kindId
878 };
879 result = new ReplayableResource(this, data);
880 cache.put(this._id, result); // Put into the cache early to avoid loops.
881 data.calls = this._calls.map(function(call) {
882 return call.toReplayable(cache);
883 });
884 this._populateReplayableData(data, cache);
885 var contextResource = this.contextResource();
886 if (contextResource !== this)
887 data.contextResource = Resource.toReplayable(contextResource, cache) ;
888 return result;
889 },
890
891 /**
892 * @param {!Object} data
893 * @param {!Cache.<!ReplayableResource>} cache
894 */
895 _populateReplayableData: function(data, cache)
896 {
897 // Do nothing. Should be overridden by subclasses.
898 },
899
900 /**
901 * @param {!Object} data
902 * @param {!Cache.<!Resource>} cache
903 * @return {!Resource}
904 */
905 replay: function(data, cache)
906 {
907 var resource = cache.get(data.id);
908 if (resource)
909 return resource;
910 this._id = data.id;
911 this._name = data.name;
912 this._kindId = data.kindId;
913 this._resourceManager = null;
914 this._calls = [];
915 this._boundResources = Object.create(null);
916 this._wrappedObject = null;
917 cache.put(data.id, this); // Put into the cache early to avoid loops.
918 this._doReplayCalls(data, cache);
919 console.assert(this._wrappedObject, "Resource should be reconstructed!") ;
920 return this;
921 },
922
923 /**
924 * @param {!Object} data
925 * @param {!Cache.<!Resource>} cache
926 */
927 _doReplayCalls: function(data, cache)
928 {
929 for (var i = 0, n = data.calls.length; i < n; ++i)
930 this._calls.push(data.calls[i].replay(cache));
931 },
932
933 /**
934 * @param {!Call} call
935 */
936 pushCall: function(call)
937 {
938 call.freeze();
939 this._calls.push(call);
940 },
941
942 /**
943 * @param {!Call} call
944 */
945 onCallReplayed: function(call)
946 {
947 // Ignore by default.
948 },
949
950 /**
951 * @param {!Object} object
952 */
953 _bindObjectToResource: function(object)
954 {
955 Object.defineProperty(object, "__resourceObject", {
956 value: this,
957 writable: false,
958 enumerable: false,
959 configurable: true
960 });
961 },
962
963 /**
964 * @param {string} key
965 * @param {*} obj
966 */
967 _registerBoundResource: function(key, obj)
968 {
969 var resource = Resource.forObject(obj);
970 if (resource)
971 this._boundResources[key] = resource;
972 else
973 delete this._boundResources[key];
974 },
975
976 /**
977 * @return {?Object}
978 */
979 _wrapObject: function()
980 {
981 var wrappedObject = this.wrappedObject();
982 if (!wrappedObject)
983 return null;
984 var proxy = Object.create(wrappedObject.__proto__); // In order to emula te "instanceof".
985
986 var customWrapFunctions = this._customWrapFunctions();
987 /** @type {!Array.<string>} */
988 this._proxyStatePropertyNames = [];
989
990 /**
991 * @param {string} property
992 * @this {Resource}
993 */
994 function processProperty(property)
995 {
996 if (typeof wrappedObject[property] === "function") {
997 var customWrapFunction = customWrapFunctions[property];
998 if (customWrapFunction)
999 proxy[property] = this._wrapCustomFunction(this, wrappedObje ct, wrappedObject[property], property, customWrapFunction);
1000 else
1001 proxy[property] = this._wrapFunction(this, wrappedObject, wr appedObject[property], property);
1002 } else if (TypeUtils.isEnumPropertyName(property, wrappedObject)) {
1003 // Fast access to enums and constants.
1004 proxy[property] = wrappedObject[property];
1005 } else {
1006 this._proxyStatePropertyNames.push(property);
1007 Object.defineProperty(proxy, property, {
1008 get: function()
1009 {
1010 var obj = wrappedObject[property];
1011 var resource = Resource.forObject(obj);
1012 return resource ? resource : obj;
1013 },
1014 set: this._wrapPropertySetter(this, wrappedObject, property) ,
1015 enumerable: true
1016 });
1017 }
1018 }
1019
1020 var isEmpty = true;
1021 for (var property in wrappedObject) {
1022 isEmpty = false;
1023 processProperty.call(this, property);
1024 }
1025 if (isEmpty)
1026 return wrappedObject; // Nothing to proxy.
1027
1028 this._bindObjectToResource(proxy);
1029 return proxy;
1030 },
1031
1032 /**
1033 * @param {!Resource} resource
1034 * @param {!Object} originalObject
1035 * @param {!Function} originalFunction
1036 * @param {string} functionName
1037 * @param {!Function} customWrapFunction
1038 * @return {!Function}
1039 */
1040 _wrapCustomFunction: function(resource, originalObject, originalFunction, fu nctionName, customWrapFunction)
1041 {
1042 return function()
1043 {
1044 var manager = resource.manager();
1045 var isCapturing = manager && manager.capturing();
1046 if (isCapturing)
1047 manager.captureArguments(resource, arguments);
1048 var wrapFunction = new Resource.WrapFunction(originalObject, origina lFunction, functionName, arguments);
1049 customWrapFunction.apply(wrapFunction, arguments);
1050 if (isCapturing) {
1051 var call = wrapFunction.call();
1052 call.setStackTrace(StackTrace.create(1, arguments.callee));
1053 manager.captureCall(call);
1054 }
1055 return wrapFunction.result();
1056 };
1057 },
1058
1059 /**
1060 * @param {!Resource} resource
1061 * @param {!Object} originalObject
1062 * @param {!Function} originalFunction
1063 * @param {string} functionName
1064 * @return {!Function}
1065 */
1066 _wrapFunction: function(resource, originalObject, originalFunction, function Name)
1067 {
1068 return function()
1069 {
1070 var manager = resource.manager();
1071 if (!manager || !manager.capturing())
1072 return originalFunction.apply(originalObject, arguments);
1073 manager.captureArguments(resource, arguments);
1074 var result = originalFunction.apply(originalObject, arguments);
1075 var stackTrace = StackTrace.create(1, arguments.callee);
1076 var call = new Call(resource, functionName, arguments, result, stack Trace);
1077 manager.captureCall(call);
1078 return result;
1079 };
1080 },
1081
1082 /**
1083 * @param {!Resource} resource
1084 * @param {!Object} originalObject
1085 * @param {string} propertyName
1086 * @return {function(*)}
1087 */
1088 _wrapPropertySetter: function(resource, originalObject, propertyName)
1089 {
1090 return function(value)
1091 {
1092 resource._registerBoundResource(propertyName, value);
1093 var manager = resource.manager();
1094 if (!manager || !manager.capturing()) {
1095 originalObject[propertyName] = Resource.wrappedObject(value);
1096 return;
1097 }
1098 var args = [propertyName, value];
1099 manager.captureArguments(resource, args);
1100 originalObject[propertyName] = Resource.wrappedObject(value);
1101 var stackTrace = StackTrace.create(1, arguments.callee);
1102 var call = new Call(resource, "", args, undefined, stackTrace);
1103 manager.captureCall(call);
1104 };
1105 },
1106
1107 /**
1108 * @return {!Object.<string, !Function>}
1109 */
1110 _customWrapFunctions: function()
1111 {
1112 return Object.create(null); // May be overridden by subclasses.
1113 }
1114 }
1115
1116 /**
1117 * @constructor
1118 * @param {!Object} originalObject
1119 * @param {!Function} originalFunction
1120 * @param {string} functionName
1121 * @param {!Array|!Arguments} args
1122 */
1123 Resource.WrapFunction = function(originalObject, originalFunction, functionName, args)
1124 {
1125 this._originalObject = originalObject;
1126 this._originalFunction = originalFunction;
1127 this._functionName = functionName;
1128 this._args = args;
1129 this._resource = Resource.forObject(originalObject);
1130 console.assert(this._resource, "Expected a wrapped call on a Resource object .");
1131 }
1132
1133 Resource.WrapFunction.prototype = {
1134 /**
1135 * @return {*}
1136 */
1137 result: function()
1138 {
1139 if (!this._executed) {
1140 this._executed = true;
1141 this._result = this._originalFunction.apply(this._originalObject, th is._args);
1142 }
1143 return this._result;
1144 },
1145
1146 /**
1147 * @return {!Call}
1148 */
1149 call: function()
1150 {
1151 if (!this._call)
1152 this._call = new Call(this._resource, this._functionName, this._args , this.result());
1153 return this._call;
1154 },
1155
1156 /**
1157 * @param {*} result
1158 */
1159 overrideResult: function(result)
1160 {
1161 var call = this.call();
1162 call.setResult(result);
1163 this._result = result;
1164 }
1165 }
1166
1167 /**
1168 * @param {function(new:Resource, !Object, string)} resourceConstructor
1169 * @param {string} resourceName
1170 * @return {function(this:Resource.WrapFunction)}
1171 */
1172 Resource.WrapFunction.resourceFactoryMethod = function(resourceConstructor, reso urceName)
1173 {
1174 return /** @this {Resource.WrapFunction} */ function()
1175 {
1176 var wrappedObject = /** @type {?Object} */ (this.result());
1177 if (!wrappedObject)
1178 return;
1179 var resource = new resourceConstructor(wrappedObject, resourceName);
1180 var manager = this._resource.manager();
1181 if (manager)
1182 manager.registerResource(resource);
1183 this.overrideResult(resource.proxyObject());
1184 resource.pushCall(this.call());
1185 }
1186 }
1187
1188 /**
1189 * @constructor
1190 * @param {!Resource} originalResource
1191 * @param {!Object} data
1192 */
1193 function ReplayableResource(originalResource, data)
1194 {
1195 this._proto = originalResource.__proto__;
1196 this._data = data;
1197 }
1198
1199 ReplayableResource.prototype = {
1200 /**
1201 * @return {number}
1202 */
1203 id: function()
1204 {
1205 return this._data.id;
1206 },
1207
1208 /**
1209 * @return {string}
1210 */
1211 name: function()
1212 {
1213 return this._data.name;
1214 },
1215
1216 /**
1217 * @return {string}
1218 */
1219 description: function()
1220 {
1221 return this._data.name + "@" + this._data.kindId;
1222 },
1223
1224 /**
1225 * @return {!ReplayableResource}
1226 */
1227 contextResource: function()
1228 {
1229 return this._data.contextResource || this;
1230 },
1231
1232 /**
1233 * @param {!Cache.<!Resource>} cache
1234 * @return {!Resource}
1235 */
1236 replay: function(cache)
1237 {
1238 var result = /** @type {!Resource} */ (Object.create(this._proto));
1239 result = result.replay(this._data, cache)
1240 console.assert(result.__proto__ === this._proto, "Wrong type of a replay result");
1241 return result;
1242 }
1243 }
1244
1245 /**
1246 * @param {!ReplayableResource|*} obj
1247 * @param {!Cache.<!Resource>} cache
1248 * @return {*}
1249 */
1250 ReplayableResource.replay = function(obj, cache)
1251 {
1252 return (obj instanceof ReplayableResource) ? obj.replay(cache).wrappedObject () : obj;
1253 }
1254
1255 /**
1256 * @constructor
1257 * @extends {Resource}
1258 * @param {!Object} wrappedObject
1259 * @param {string} name
1260 */
1261 function ContextResource(wrappedObject, name)
1262 {
1263 Resource.call(this, wrappedObject, name);
1264 }
1265
1266 ContextResource.prototype = {
1267 __proto__: Resource.prototype
1268 }
1269
1270 /**
1271 * @constructor
1272 * @extends {Resource}
1273 * @param {!Object} wrappedObject
1274 * @param {string} name
1275 */
1276 function LogEverythingResource(wrappedObject, name)
1277 {
1278 Resource.call(this, wrappedObject, name);
1279 }
1280
1281 LogEverythingResource.prototype = {
1282 /**
1283 * @override
1284 * @return {!Object.<string, !Function>}
1285 */
1286 _customWrapFunctions: function()
1287 {
1288 var wrapFunctions = Object.create(null);
1289 var wrappedObject = this.wrappedObject();
1290 if (wrappedObject) {
1291 for (var property in wrappedObject) {
1292 /** @this {Resource.WrapFunction} */
1293 wrapFunctions[property] = function()
1294 {
1295 this._resource.pushCall(this.call());
1296 }
1297 }
1298 }
1299 return wrapFunctions;
1300 },
1301
1302 __proto__: Resource.prototype
1303 }
1304
1305 ////////////////////////////////////////////////////////////////////////////////
1306 // WebGL
1307 ////////////////////////////////////////////////////////////////////////////////
1308
1309 /**
1310 * @constructor
1311 * @extends {Resource}
1312 * @param {!Object} wrappedObject
1313 * @param {string} name
1314 */
1315 function WebGLBoundResource(wrappedObject, name)
1316 {
1317 Resource.call(this, wrappedObject, name);
1318 /** @type {!Object.<string, *>} */
1319 this._state = {};
1320 }
1321
1322 WebGLBoundResource.prototype = {
1323 /**
1324 * @override
1325 * @param {!Object} data
1326 * @param {!Cache.<!ReplayableResource>} cache
1327 */
1328 _populateReplayableData: function(data, cache)
1329 {
1330 var state = this._state;
1331 data.state = {};
1332 Object.keys(state).forEach(function(parameter) {
1333 data.state[parameter] = Resource.toReplayable(state[parameter], cach e);
1334 });
1335 },
1336
1337 /**
1338 * @override
1339 * @param {!Object} data
1340 * @param {!Cache.<!Resource>} cache
1341 */
1342 _doReplayCalls: function(data, cache)
1343 {
1344 var gl = this._replayContextResource(data, cache).wrappedObject();
1345
1346 /** @type {!Object.<string, !Array.<string>>} */
1347 var bindingsData = {
1348 TEXTURE_2D: ["bindTexture", "TEXTURE_BINDING_2D"],
1349 TEXTURE_CUBE_MAP: ["bindTexture", "TEXTURE_BINDING_CUBE_MAP"],
1350 ARRAY_BUFFER: ["bindBuffer", "ARRAY_BUFFER_BINDING"],
1351 ELEMENT_ARRAY_BUFFER: ["bindBuffer", "ELEMENT_ARRAY_BUFFER_BINDING"] ,
1352 FRAMEBUFFER: ["bindFramebuffer", "FRAMEBUFFER_BINDING"],
1353 RENDERBUFFER: ["bindRenderbuffer", "RENDERBUFFER_BINDING"]
1354 };
1355 var originalBindings = {};
1356 Object.keys(bindingsData).forEach(function(bindingTarget) {
1357 var bindingParameter = bindingsData[bindingTarget][1];
1358 originalBindings[bindingTarget] = gl.getParameter(gl[bindingParamete r]);
1359 });
1360
1361 var state = {};
1362 Object.keys(data.state).forEach(function(parameter) {
1363 state[parameter] = ReplayableResource.replay(data.state[parameter], cache);
1364 });
1365 this._state = state;
1366 Resource.prototype._doReplayCalls.call(this, data, cache);
1367
1368 Object.keys(bindingsData).forEach(function(bindingTarget) {
1369 var bindMethodName = bindingsData[bindingTarget][0];
1370 gl[bindMethodName].call(gl, gl[bindingTarget], originalBindings[bind ingTarget]);
1371 });
1372 },
1373
1374 /**
1375 * @param {!Object} data
1376 * @param {!Cache.<!Resource>} cache
1377 * @return {?WebGLRenderingContextResource}
1378 */
1379 _replayContextResource: function(data, cache)
1380 {
1381 var calls = /** @type {!Array.<!ReplayableCall>} */ (data.calls);
1382 for (var i = 0, n = calls.length; i < n; ++i) {
1383 var resource = ReplayableResource.replay(calls[i].replayableResource (), cache);
1384 var contextResource = WebGLRenderingContextResource.forObject(resour ce);
1385 if (contextResource)
1386 return contextResource;
1387 }
1388 return null;
1389 },
1390
1391 /**
1392 * @param {number} target
1393 * @param {string} bindMethodName
1394 */
1395 pushBinding: function(target, bindMethodName)
1396 {
1397 if (this._state.bindTarget !== target) {
1398 this._state.bindTarget = target;
1399 this.pushCall(new Call(WebGLRenderingContextResource.forObject(this) , bindMethodName, [target, this]));
1400 }
1401 },
1402
1403 __proto__: Resource.prototype
1404 }
1405
1406 /**
1407 * @constructor
1408 * @extends {WebGLBoundResource}
1409 * @param {!Object} wrappedObject
1410 * @param {string} name
1411 */
1412 function WebGLTextureResource(wrappedObject, name)
1413 {
1414 WebGLBoundResource.call(this, wrappedObject, name);
1415 }
1416
1417 WebGLTextureResource.prototype = {
1418 /**
1419 * @override (overrides @return type)
1420 * @return {!WebGLTexture}
1421 */
1422 wrappedObject: function()
1423 {
1424 return this._wrappedObject;
1425 },
1426
1427 /**
1428 * @override
1429 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
1430 */
1431 currentState: function()
1432 {
1433 var result = [];
1434 var glResource = WebGLRenderingContextResource.forObject(this);
1435 var gl = glResource.wrappedObject();
1436 var texture = this.wrappedObject();
1437 if (!gl || !texture)
1438 return result;
1439 result.push({ name: "isTexture", value: gl.isTexture(texture) });
1440 result.push({ name: "context", value: this.contextResource() });
1441
1442 var target = this._state.bindTarget;
1443 if (typeof target !== "number")
1444 return result;
1445
1446 var bindingParameter;
1447 switch (target) {
1448 case gl.TEXTURE_2D:
1449 bindingParameter = gl.TEXTURE_BINDING_2D;
1450 break;
1451 case gl.TEXTURE_CUBE_MAP:
1452 bindingParameter = gl.TEXTURE_BINDING_CUBE_MAP;
1453 break;
1454 default:
1455 console.error("ASSERT_NOT_REACHED: unknown texture target " + target );
1456 return result;
1457 }
1458 result.push({ name: "target", value: target, valueIsEnum: true });
1459
1460 var oldTexture = /** @type {!WebGLTexture} */ (gl.getParameter(bindingPa rameter));
1461 if (oldTexture !== texture)
1462 gl.bindTexture(target, texture);
1463
1464 var textureParameters = [
1465 "TEXTURE_MAG_FILTER",
1466 "TEXTURE_MIN_FILTER",
1467 "TEXTURE_WRAP_S",
1468 "TEXTURE_WRAP_T",
1469 "TEXTURE_MAX_ANISOTROPY_EXT" // EXT_texture_filter_anisotropic exten sion
1470 ];
1471 glResource.queryStateValues(gl.getTexParameter, target, textureParameter s, result);
1472
1473 if (oldTexture !== texture)
1474 gl.bindTexture(target, oldTexture);
1475 return result;
1476 },
1477
1478 /**
1479 * @override
1480 * @param {!Object} data
1481 * @param {!Cache.<!Resource>} cache
1482 */
1483 _doReplayCalls: function(data, cache)
1484 {
1485 var gl = this._replayContextResource(data, cache).wrappedObject();
1486
1487 var state = {};
1488 WebGLRenderingContextResource.PixelStoreParameters.forEach(function(para meter) {
1489 state[parameter] = gl.getParameter(gl[parameter]);
1490 });
1491
1492 WebGLBoundResource.prototype._doReplayCalls.call(this, data, cache);
1493
1494 WebGLRenderingContextResource.PixelStoreParameters.forEach(function(para meter) {
1495 gl.pixelStorei(gl[parameter], state[parameter]);
1496 });
1497 },
1498
1499 /**
1500 * @override
1501 * @param {!Call} call
1502 */
1503 pushCall: function(call)
1504 {
1505 var gl = WebGLRenderingContextResource.forObject(call.resource()).wrappe dObject();
1506 WebGLRenderingContextResource.PixelStoreParameters.forEach(function(para meter) {
1507 var value = gl.getParameter(gl[parameter]);
1508 if (this._state[parameter] !== value) {
1509 this._state[parameter] = value;
1510 var pixelStoreCall = new Call(gl, "pixelStorei", [gl[parameter], value]);
1511 WebGLBoundResource.prototype.pushCall.call(this, pixelStoreCall) ;
1512 }
1513 }, this);
1514
1515 // FIXME: remove any older calls that no longer contribute to the resour ce state.
1516 // FIXME: optimize memory usage: maybe it's more efficient to store one texImage2D call instead of many texSubImage2D.
1517 WebGLBoundResource.prototype.pushCall.call(this, call);
1518 },
1519
1520 /**
1521 * Handles: texParameteri, texParameterf
1522 * @param {!Call} call
1523 */
1524 pushCall_texParameter: function(call)
1525 {
1526 var args = call.args();
1527 var pname = args[1];
1528 var param = args[2];
1529 if (this._state[pname] !== param) {
1530 this._state[pname] = param;
1531 WebGLBoundResource.prototype.pushCall.call(this, call);
1532 }
1533 },
1534
1535 /**
1536 * Handles: copyTexImage2D, copyTexSubImage2D
1537 * copyTexImage2D and copyTexSubImage2D define a texture image with pixels f rom the current framebuffer.
1538 * @param {!Call} call
1539 */
1540 pushCall_copyTexImage2D: function(call)
1541 {
1542 var glResource = WebGLRenderingContextResource.forObject(call.resource() );
1543 var gl = glResource.wrappedObject();
1544 var framebufferResource = /** @type {!WebGLFramebufferResource} */ (glRe source.currentBinding(gl.FRAMEBUFFER));
1545 if (framebufferResource)
1546 this.pushCall(new Call(glResource, "bindFramebuffer", [gl.FRAMEBUFFE R, framebufferResource]));
1547 else {
1548 // FIXME: Implement this case.
1549 console.error("ASSERT_NOT_REACHED: Could not properly process a gl." + call.functionName() + " call while the DRAWING BUFFER is bound.");
1550 }
1551 this.pushCall(call);
1552 },
1553
1554 __proto__: WebGLBoundResource.prototype
1555 }
1556
1557 /**
1558 * @constructor
1559 * @extends {Resource}
1560 * @param {!Object} wrappedObject
1561 * @param {string} name
1562 */
1563 function WebGLProgramResource(wrappedObject, name)
1564 {
1565 Resource.call(this, wrappedObject, name);
1566 }
1567
1568 WebGLProgramResource.prototype = {
1569 /**
1570 * @override (overrides @return type)
1571 * @return {!WebGLProgram}
1572 */
1573 wrappedObject: function()
1574 {
1575 return this._wrappedObject;
1576 },
1577
1578 /**
1579 * @override
1580 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
1581 */
1582 currentState: function()
1583 {
1584 /**
1585 * @param {!Object} obj
1586 * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} output
1587 */
1588 function convertToStateDescriptors(obj, output)
1589 {
1590 for (var pname in obj)
1591 output.push({ name: pname, value: obj[pname], valueIsEnum: (pnam e === "type") });
1592 }
1593
1594 var result = [];
1595 var program = this.wrappedObject();
1596 if (!program)
1597 return result;
1598 var glResource = WebGLRenderingContextResource.forObject(this);
1599 var gl = glResource.wrappedObject();
1600 var programParameters = ["DELETE_STATUS", "LINK_STATUS", "VALIDATE_STATU S"];
1601 glResource.queryStateValues(gl.getProgramParameter, program, programPara meters, result);
1602 result.push({ name: "getProgramInfoLog", value: gl.getProgramInfoLog(pro gram) });
1603 result.push({ name: "isProgram", value: gl.isProgram(program) });
1604 result.push({ name: "context", value: this.contextResource() });
1605
1606 // ATTACHED_SHADERS
1607 var callFormatter = CallFormatter.forResource(this);
1608 var shaders = gl.getAttachedShaders(program) || [];
1609 var shaderDescriptors = [];
1610 for (var i = 0, n = shaders.length; i < n; ++i) {
1611 var shaderResource = Resource.forObject(shaders[i]);
1612 var pname = callFormatter.enumNameForValue(shaderResource.type());
1613 shaderDescriptors.push({ name: pname, value: shaderResource });
1614 }
1615 result.push({ name: "ATTACHED_SHADERS", values: shaderDescriptors, isArr ay: true });
1616
1617 // ACTIVE_UNIFORMS
1618 var uniformDescriptors = [];
1619 var uniforms = this._activeUniforms(true);
1620 for (var i = 0, n = uniforms.length; i < n; ++i) {
1621 var pname = "" + i;
1622 var values = [];
1623 convertToStateDescriptors(uniforms[i], values);
1624 uniformDescriptors.push({ name: pname, values: values });
1625 }
1626 result.push({ name: "ACTIVE_UNIFORMS", values: uniformDescriptors, isArr ay: true });
1627
1628 // ACTIVE_ATTRIBUTES
1629 var attributesCount = /** @type {number} */ (gl.getProgramParameter(prog ram, gl.ACTIVE_ATTRIBUTES));
1630 var attributeDescriptors = [];
1631 for (var i = 0; i < attributesCount; ++i) {
1632 var activeInfo = gl.getActiveAttrib(program, i);
1633 if (!activeInfo)
1634 continue;
1635 var pname = "" + i;
1636 var values = [];
1637 convertToStateDescriptors(activeInfo, values);
1638 attributeDescriptors.push({ name: pname, values: values });
1639 }
1640 result.push({ name: "ACTIVE_ATTRIBUTES", values: attributeDescriptors, i sArray: true });
1641
1642 return result;
1643 },
1644
1645 /**
1646 * @param {boolean=} includeAllInfo
1647 * @return {!Array.<{name:string, type:number, value:*, size:(number|undefin ed)}>}
1648 */
1649 _activeUniforms: function(includeAllInfo)
1650 {
1651 var uniforms = [];
1652 var program = this.wrappedObject();
1653 if (!program)
1654 return uniforms;
1655
1656 var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
1657 var uniformsCount = /** @type {number} */ (gl.getProgramParameter(progra m, gl.ACTIVE_UNIFORMS));
1658 for (var i = 0; i < uniformsCount; ++i) {
1659 var activeInfo = gl.getActiveUniform(program, i);
1660 if (!activeInfo)
1661 continue;
1662 var uniformLocation = gl.getUniformLocation(program, activeInfo.name );
1663 if (!uniformLocation)
1664 continue;
1665 var value = gl.getUniform(program, uniformLocation);
1666 var item = Object.create(null);
1667 item.name = activeInfo.name;
1668 item.type = activeInfo.type;
1669 item.value = value;
1670 if (includeAllInfo)
1671 item.size = activeInfo.size;
1672 uniforms.push(item);
1673 }
1674 return uniforms;
1675 },
1676
1677 /**
1678 * @override
1679 * @param {!Object} data
1680 * @param {!Cache.<!ReplayableResource>} cache
1681 */
1682 _populateReplayableData: function(data, cache)
1683 {
1684 var glResource = WebGLRenderingContextResource.forObject(this);
1685 var originalErrors = glResource.getAllErrors();
1686 data.uniforms = this._activeUniforms();
1687 glResource.restoreErrors(originalErrors);
1688 },
1689
1690 /**
1691 * @override
1692 * @param {!Object} data
1693 * @param {!Cache.<!Resource>} cache
1694 */
1695 _doReplayCalls: function(data, cache)
1696 {
1697 Resource.prototype._doReplayCalls.call(this, data, cache);
1698 var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
1699 var program = this.wrappedObject();
1700
1701 var originalProgram = /** @type {!WebGLProgram} */ (gl.getParameter(gl.C URRENT_PROGRAM));
1702 var currentProgram = originalProgram;
1703
1704 data.uniforms.forEach(function(uniform) {
1705 var uniformLocation = gl.getUniformLocation(program, uniform.name);
1706 if (!uniformLocation)
1707 return;
1708 if (currentProgram !== program) {
1709 currentProgram = program;
1710 gl.useProgram(program);
1711 }
1712 var methodName = this._uniformMethodNameByType(gl, uniform.type);
1713 if (methodName.indexOf("Matrix") === -1)
1714 gl[methodName].call(gl, uniformLocation, uniform.value);
1715 else
1716 gl[methodName].call(gl, uniformLocation, false, uniform.value);
1717 }.bind(this));
1718
1719 if (currentProgram !== originalProgram)
1720 gl.useProgram(originalProgram);
1721 },
1722
1723 /**
1724 * @param {!WebGLRenderingContext} gl
1725 * @param {number} type
1726 * @return {string}
1727 */
1728 _uniformMethodNameByType: function(gl, type)
1729 {
1730 var uniformMethodNames = WebGLProgramResource._uniformMethodNames;
1731 if (!uniformMethodNames) {
1732 uniformMethodNames = {};
1733 uniformMethodNames[gl.FLOAT] = "uniform1f";
1734 uniformMethodNames[gl.FLOAT_VEC2] = "uniform2fv";
1735 uniformMethodNames[gl.FLOAT_VEC3] = "uniform3fv";
1736 uniformMethodNames[gl.FLOAT_VEC4] = "uniform4fv";
1737 uniformMethodNames[gl.INT] = "uniform1i";
1738 uniformMethodNames[gl.BOOL] = "uniform1i";
1739 uniformMethodNames[gl.SAMPLER_2D] = "uniform1i";
1740 uniformMethodNames[gl.SAMPLER_CUBE] = "uniform1i";
1741 uniformMethodNames[gl.INT_VEC2] = "uniform2iv";
1742 uniformMethodNames[gl.BOOL_VEC2] = "uniform2iv";
1743 uniformMethodNames[gl.INT_VEC3] = "uniform3iv";
1744 uniformMethodNames[gl.BOOL_VEC3] = "uniform3iv";
1745 uniformMethodNames[gl.INT_VEC4] = "uniform4iv";
1746 uniformMethodNames[gl.BOOL_VEC4] = "uniform4iv";
1747 uniformMethodNames[gl.FLOAT_MAT2] = "uniformMatrix2fv";
1748 uniformMethodNames[gl.FLOAT_MAT3] = "uniformMatrix3fv";
1749 uniformMethodNames[gl.FLOAT_MAT4] = "uniformMatrix4fv";
1750 WebGLProgramResource._uniformMethodNames = uniformMethodNames;
1751 }
1752 console.assert(uniformMethodNames[type], "Unknown uniform type " + type) ;
1753 return uniformMethodNames[type];
1754 },
1755
1756 /**
1757 * @override
1758 * @param {!Call} call
1759 */
1760 pushCall: function(call)
1761 {
1762 // FIXME: remove any older calls that no longer contribute to the resour ce state.
1763 // FIXME: handle multiple attachShader && detachShader.
1764 Resource.prototype.pushCall.call(this, call);
1765 },
1766
1767 __proto__: Resource.prototype
1768 }
1769
1770 /**
1771 * @constructor
1772 * @extends {Resource}
1773 * @param {!Object} wrappedObject
1774 * @param {string} name
1775 */
1776 function WebGLShaderResource(wrappedObject, name)
1777 {
1778 Resource.call(this, wrappedObject, name);
1779 }
1780
1781 WebGLShaderResource.prototype = {
1782 /**
1783 * @override (overrides @return type)
1784 * @return {!WebGLShader}
1785 */
1786 wrappedObject: function()
1787 {
1788 return this._wrappedObject;
1789 },
1790
1791 /**
1792 * @return {number}
1793 */
1794 type: function()
1795 {
1796 var call = this._calls[0];
1797 if (call && call.functionName() === "createShader")
1798 return call.args()[0];
1799 console.error("ASSERT_NOT_REACHED: Failed to restore shader type from th e log.", call);
1800 return 0;
1801 },
1802
1803 /**
1804 * @override
1805 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
1806 */
1807 currentState: function()
1808 {
1809 var result = [];
1810 var shader = this.wrappedObject();
1811 if (!shader)
1812 return result;
1813 var glResource = WebGLRenderingContextResource.forObject(this);
1814 var gl = glResource.wrappedObject();
1815 var shaderParameters = ["SHADER_TYPE", "DELETE_STATUS", "COMPILE_STATUS" ];
1816 glResource.queryStateValues(gl.getShaderParameter, shader, shaderParamet ers, result);
1817 result.push({ name: "getShaderInfoLog", value: gl.getShaderInfoLog(shade r) });
1818 result.push({ name: "getShaderSource", value: gl.getShaderSource(shader) });
1819 result.push({ name: "isShader", value: gl.isShader(shader) });
1820 result.push({ name: "context", value: this.contextResource() });
1821
1822 // getShaderPrecisionFormat
1823 var shaderType = this.type();
1824 var precisionValues = [];
1825 var precisionParameters = ["LOW_FLOAT", "MEDIUM_FLOAT", "HIGH_FLOAT", "L OW_INT", "MEDIUM_INT", "HIGH_INT"];
1826 for (var i = 0, pname; pname = precisionParameters[i]; ++i)
1827 precisionValues.push({ name: pname, value: gl.getShaderPrecisionForm at(shaderType, gl[pname]) });
1828 result.push({ name: "getShaderPrecisionFormat", values: precisionValues });
1829
1830 return result;
1831 },
1832
1833 /**
1834 * @override
1835 * @param {!Call} call
1836 */
1837 pushCall: function(call)
1838 {
1839 // FIXME: remove any older calls that no longer contribute to the resour ce state.
1840 // FIXME: handle multiple shaderSource calls.
1841 Resource.prototype.pushCall.call(this, call);
1842 },
1843
1844 __proto__: Resource.prototype
1845 }
1846
1847 /**
1848 * @constructor
1849 * @extends {WebGLBoundResource}
1850 * @param {!Object} wrappedObject
1851 * @param {string} name
1852 */
1853 function WebGLBufferResource(wrappedObject, name)
1854 {
1855 WebGLBoundResource.call(this, wrappedObject, name);
1856 }
1857
1858 WebGLBufferResource.prototype = {
1859 /**
1860 * @override (overrides @return type)
1861 * @return {!WebGLBuffer}
1862 */
1863 wrappedObject: function()
1864 {
1865 return this._wrappedObject;
1866 },
1867
1868 /**
1869 * @return {?ArrayBufferView}
1870 */
1871 cachedBufferData: function()
1872 {
1873 /**
1874 * Creates a view to a given buffer, does NOT copy the buffer.
1875 * @param {!ArrayBuffer|!ArrayBufferView} buffer
1876 * @return {!Uint8Array}
1877 */
1878 function createUint8ArrayBufferView(buffer)
1879 {
1880 return buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
1881 }
1882
1883 if (!this._cachedBufferData) {
1884 for (var i = this._calls.length - 1; i >= 0; --i) {
1885 var call = this._calls[i];
1886 if (call.functionName() === "bufferData") {
1887 var sizeOrData = /** @type {number|!ArrayBuffer|!ArrayBuffer View} */ (call.args()[1]);
1888 if (typeof sizeOrData === "number")
1889 this._cachedBufferData = new ArrayBuffer(sizeOrData);
1890 else
1891 this._cachedBufferData = sizeOrData;
1892 this._lastBufferSubDataIndex = i + 1;
1893 break;
1894 }
1895 }
1896 if (!this._cachedBufferData)
1897 return null;
1898 }
1899
1900 // Apply any "bufferSubData" calls that have not been applied yet.
1901 var bufferDataView;
1902 while (this._lastBufferSubDataIndex < this._calls.length) {
1903 var call = this._calls[this._lastBufferSubDataIndex++];
1904 if (call.functionName() !== "bufferSubData")
1905 continue;
1906 var offset = /** @type {number} */ (call.args()[1]);
1907 var data = /** @type {!ArrayBuffer|!ArrayBufferView} */ (call.args() [2]);
1908 var view = createUint8ArrayBufferView(data);
1909 if (!bufferDataView)
1910 bufferDataView = createUint8ArrayBufferView(this._cachedBufferDa ta);
1911 bufferDataView.set(view, offset);
1912
1913 var isFullReplacement = (offset === 0 && bufferDataView.length === v iew.length);
1914 if (this._cachedBufferData instanceof ArrayBuffer) {
1915 // The buffer data has no type yet. Try to guess from the "buffe rSubData" call.
1916 var typedArrayClass = TypeUtils.typedArrayClass(data);
1917 if (typedArrayClass)
1918 this._cachedBufferData = new typedArrayClass(this._cachedBuf ferData); // Does not copy the buffer.
1919 } else if (isFullReplacement) {
1920 var typedArrayClass = TypeUtils.typedArrayClass(data);
1921 if (typedArrayClass) {
1922 var typedArrayData = /** @type {!ArrayBufferView} */ (data);
1923 this._cachedBufferData = new typedArrayClass(this._cachedBuf ferData.buffer, this._cachedBufferData.byteOffset, typedArrayData.length); // Do es not copy the buffer.
1924 }
1925 }
1926 }
1927
1928 if (this._cachedBufferData instanceof ArrayBuffer) {
1929 // If we failed to guess the data type yet, use Uint8Array.
1930 return new Uint8Array(this._cachedBufferData);
1931 }
1932 return this._cachedBufferData;
1933 },
1934
1935 /**
1936 * @override
1937 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
1938 */
1939 currentState: function()
1940 {
1941 var result = [];
1942 var glResource = WebGLRenderingContextResource.forObject(this);
1943 var gl = glResource.wrappedObject();
1944 var buffer = this.wrappedObject();
1945 if (!gl || !buffer)
1946 return result;
1947 result.push({ name: "isBuffer", value: gl.isBuffer(buffer) });
1948 result.push({ name: "context", value: this.contextResource() });
1949
1950 var target = this._state.bindTarget;
1951 if (typeof target !== "number")
1952 return result;
1953
1954 var bindingParameter;
1955 switch (target) {
1956 case gl.ARRAY_BUFFER:
1957 bindingParameter = gl.ARRAY_BUFFER_BINDING;
1958 break;
1959 case gl.ELEMENT_ARRAY_BUFFER:
1960 bindingParameter = gl.ELEMENT_ARRAY_BUFFER_BINDING;
1961 break;
1962 default:
1963 console.error("ASSERT_NOT_REACHED: unknown buffer target " + target) ;
1964 return result;
1965 }
1966 result.push({ name: "target", value: target, valueIsEnum: true });
1967
1968 var oldBuffer = /** @type {!WebGLBuffer} */ (gl.getParameter(bindingPara meter));
1969 if (oldBuffer !== buffer)
1970 gl.bindBuffer(target, buffer);
1971
1972 var bufferParameters = ["BUFFER_SIZE", "BUFFER_USAGE"];
1973 glResource.queryStateValues(gl.getBufferParameter, target, bufferParamet ers, result);
1974
1975 if (oldBuffer !== buffer)
1976 gl.bindBuffer(target, oldBuffer);
1977
1978 try {
1979 var data = this.cachedBufferData();
1980 if (data)
1981 result.push({ name: "bufferData", value: data });
1982 } catch (e) {
1983 console.error("Exception while restoring bufferData", e);
1984 }
1985
1986 return result;
1987 },
1988
1989 /**
1990 * @param {!Call} call
1991 */
1992 pushCall_bufferData: function(call)
1993 {
1994 // FIXME: remove any older calls that no longer contribute to the resour ce state.
1995 delete this._cachedBufferData;
1996 delete this._lastBufferSubDataIndex;
1997 WebGLBoundResource.prototype.pushCall.call(this, call);
1998 },
1999
2000 /**
2001 * @param {!Call} call
2002 */
2003 pushCall_bufferSubData: function(call)
2004 {
2005 // FIXME: Optimize memory for bufferSubData.
2006 WebGLBoundResource.prototype.pushCall.call(this, call);
2007 },
2008
2009 __proto__: WebGLBoundResource.prototype
2010 }
2011
2012 /**
2013 * @constructor
2014 * @extends {WebGLBoundResource}
2015 * @param {!Object} wrappedObject
2016 * @param {string} name
2017 */
2018 function WebGLFramebufferResource(wrappedObject, name)
2019 {
2020 WebGLBoundResource.call(this, wrappedObject, name);
2021 }
2022
2023 WebGLFramebufferResource.prototype = {
2024 /**
2025 * @override (overrides @return type)
2026 * @return {!WebGLFramebuffer}
2027 */
2028 wrappedObject: function()
2029 {
2030 return this._wrappedObject;
2031 },
2032
2033 /**
2034 * @override
2035 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
2036 */
2037 currentState: function()
2038 {
2039 var result = [];
2040 var framebuffer = this.wrappedObject();
2041 if (!framebuffer)
2042 return result;
2043 var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
2044
2045 var oldFramebuffer = /** @type {!WebGLFramebuffer} */ (gl.getParameter(g l.FRAMEBUFFER_BINDING));
2046 if (oldFramebuffer !== framebuffer)
2047 gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
2048
2049 var attachmentParameters = ["COLOR_ATTACHMENT0", "DEPTH_ATTACHMENT", "ST ENCIL_ATTACHMENT"];
2050 var framebufferParameters = ["FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", "FRAM EBUFFER_ATTACHMENT_OBJECT_NAME", "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", "FRAMEB UFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE"];
2051 for (var i = 0, attachment; attachment = attachmentParameters[i]; ++i) {
2052 var values = [];
2053 for (var j = 0, pname; pname = framebufferParameters[j]; ++j) {
2054 var value = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl[attachment], gl[pname]);
2055 value = Resource.forObject(value) || value;
2056 values.push({ name: pname, value: value, valueIsEnum: WebGLRende ringContextResource.GetResultIsEnum[pname] });
2057 }
2058 result.push({ name: attachment, values: values });
2059 }
2060 result.push({ name: "isFramebuffer", value: gl.isFramebuffer(framebuffer ) });
2061 result.push({ name: "context", value: this.contextResource() });
2062
2063 if (oldFramebuffer !== framebuffer)
2064 gl.bindFramebuffer(gl.FRAMEBUFFER, oldFramebuffer);
2065 return result;
2066 },
2067
2068 /**
2069 * @override
2070 * @param {!Call} call
2071 */
2072 pushCall: function(call)
2073 {
2074 // FIXME: remove any older calls that no longer contribute to the resour ce state.
2075 WebGLBoundResource.prototype.pushCall.call(this, call);
2076 },
2077
2078 __proto__: WebGLBoundResource.prototype
2079 }
2080
2081 /**
2082 * @constructor
2083 * @extends {WebGLBoundResource}
2084 * @param {!Object} wrappedObject
2085 * @param {string} name
2086 */
2087 function WebGLRenderbufferResource(wrappedObject, name)
2088 {
2089 WebGLBoundResource.call(this, wrappedObject, name);
2090 }
2091
2092 WebGLRenderbufferResource.prototype = {
2093 /**
2094 * @override (overrides @return type)
2095 * @return {!WebGLRenderbuffer}
2096 */
2097 wrappedObject: function()
2098 {
2099 return this._wrappedObject;
2100 },
2101
2102 /**
2103 * @override
2104 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
2105 */
2106 currentState: function()
2107 {
2108 var result = [];
2109 var renderbuffer = this.wrappedObject();
2110 if (!renderbuffer)
2111 return result;
2112 var glResource = WebGLRenderingContextResource.forObject(this);
2113 var gl = glResource.wrappedObject();
2114
2115 var oldRenderbuffer = /** @type {!WebGLRenderbuffer} */ (gl.getParameter (gl.RENDERBUFFER_BINDING));
2116 if (oldRenderbuffer !== renderbuffer)
2117 gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
2118
2119 var renderbufferParameters = ["RENDERBUFFER_WIDTH", "RENDERBUFFER_HEIGHT ", "RENDERBUFFER_INTERNAL_FORMAT", "RENDERBUFFER_RED_SIZE", "RENDERBUFFER_GREEN_ SIZE", "RENDERBUFFER_BLUE_SIZE", "RENDERBUFFER_ALPHA_SIZE", "RENDERBUFFER_DEPTH_ SIZE", "RENDERBUFFER_STENCIL_SIZE"];
2120 glResource.queryStateValues(gl.getRenderbufferParameter, gl.RENDERBUFFER , renderbufferParameters, result);
2121 result.push({ name: "isRenderbuffer", value: gl.isRenderbuffer(renderbuf fer) });
2122 result.push({ name: "context", value: this.contextResource() });
2123
2124 if (oldRenderbuffer !== renderbuffer)
2125 gl.bindRenderbuffer(gl.RENDERBUFFER, oldRenderbuffer);
2126 return result;
2127 },
2128
2129 /**
2130 * @override
2131 * @param {!Call} call
2132 */
2133 pushCall: function(call)
2134 {
2135 // FIXME: remove any older calls that no longer contribute to the resour ce state.
2136 WebGLBoundResource.prototype.pushCall.call(this, call);
2137 },
2138
2139 __proto__: WebGLBoundResource.prototype
2140 }
2141
2142 /**
2143 * @constructor
2144 * @extends {Resource}
2145 * @param {!Object} wrappedObject
2146 * @param {string} name
2147 */
2148 function WebGLUniformLocationResource(wrappedObject, name)
2149 {
2150 Resource.call(this, wrappedObject, name);
2151 }
2152
2153 WebGLUniformLocationResource.prototype = {
2154 /**
2155 * @override (overrides @return type)
2156 * @return {!WebGLUniformLocation}
2157 */
2158 wrappedObject: function()
2159 {
2160 return this._wrappedObject;
2161 },
2162
2163 /**
2164 * @return {?WebGLProgramResource}
2165 */
2166 program: function()
2167 {
2168 var call = this._calls[0];
2169 if (call && call.functionName() === "getUniformLocation")
2170 return /** @type {!WebGLProgramResource} */ (Resource.forObject(call .args()[0]));
2171 console.error("ASSERT_NOT_REACHED: Failed to restore WebGLUniformLocatio n from the log.", call);
2172 return null;
2173 },
2174
2175 /**
2176 * @return {string}
2177 */
2178 name: function()
2179 {
2180 var call = this._calls[0];
2181 if (call && call.functionName() === "getUniformLocation")
2182 return call.args()[1];
2183 console.error("ASSERT_NOT_REACHED: Failed to restore WebGLUniformLocatio n from the log.", call);
2184 return "";
2185 },
2186
2187 /**
2188 * @override
2189 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
2190 */
2191 currentState: function()
2192 {
2193 var result = [];
2194 var location = this.wrappedObject();
2195 if (!location)
2196 return result;
2197 var programResource = this.program();
2198 var program = programResource && programResource.wrappedObject();
2199 if (!program)
2200 return result;
2201 var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
2202 var uniformValue = gl.getUniform(program, location);
2203 var name = this.name();
2204 result.push({ name: "name", value: name });
2205 result.push({ name: "program", value: programResource });
2206 result.push({ name: "value", value: uniformValue });
2207 result.push({ name: "context", value: this.contextResource() });
2208
2209 if (typeof this._type !== "number") {
2210 var altName = name + "[0]";
2211 var uniformsCount = /** @type {number} */ (gl.getProgramParameter(pr ogram, gl.ACTIVE_UNIFORMS));
2212 for (var i = 0; i < uniformsCount; ++i) {
2213 var activeInfo = gl.getActiveUniform(program, i);
2214 if (!activeInfo)
2215 continue;
2216 if (activeInfo.name === name || activeInfo.name === altName) {
2217 this._type = activeInfo.type;
2218 this._size = activeInfo.size;
2219 if (activeInfo.name === name)
2220 break;
2221 }
2222 }
2223 }
2224 if (typeof this._type === "number")
2225 result.push({ name: "type", value: this._type, valueIsEnum: true });
2226 if (typeof this._size === "number")
2227 result.push({ name: "size", value: this._size });
2228
2229 return result;
2230 },
2231
2232 /**
2233 * @override
2234 * @param {!Object} data
2235 * @param {!Cache.<!ReplayableResource>} cache
2236 */
2237 _populateReplayableData: function(data, cache)
2238 {
2239 data.type = this._type;
2240 data.size = this._size;
2241 },
2242
2243 /**
2244 * @override
2245 * @param {!Object} data
2246 * @param {!Cache.<!Resource>} cache
2247 */
2248 _doReplayCalls: function(data, cache)
2249 {
2250 this._type = data.type;
2251 this._size = data.size;
2252 Resource.prototype._doReplayCalls.call(this, data, cache);
2253 },
2254
2255 __proto__: Resource.prototype
2256 }
2257
2258 /**
2259 * @constructor
2260 * @extends {ContextResource}
2261 * @param {!WebGLRenderingContext} glContext
2262 */
2263 function WebGLRenderingContextResource(glContext)
2264 {
2265 ContextResource.call(this, glContext, "WebGLRenderingContext");
2266 /** @type {?Object.<number, boolean>} */
2267 this._customErrors = null;
2268 /** @type {!Object.<string, string>} */
2269 this._extensions = {};
2270 /** @type {!Object.<string, number>} */
2271 this._extensionEnums = {};
2272 }
2273
2274 /**
2275 * @const
2276 * @type {!Array.<string>}
2277 */
2278 WebGLRenderingContextResource.GLCapabilities = [
2279 "BLEND",
2280 "CULL_FACE",
2281 "DEPTH_TEST",
2282 "DITHER",
2283 "POLYGON_OFFSET_FILL",
2284 "SAMPLE_ALPHA_TO_COVERAGE",
2285 "SAMPLE_COVERAGE",
2286 "SCISSOR_TEST",
2287 "STENCIL_TEST"
2288 ];
2289
2290 /**
2291 * @const
2292 * @type {!Array.<string>}
2293 */
2294 WebGLRenderingContextResource.PixelStoreParameters = [
2295 "PACK_ALIGNMENT",
2296 "UNPACK_ALIGNMENT",
2297 "UNPACK_COLORSPACE_CONVERSION_WEBGL",
2298 "UNPACK_FLIP_Y_WEBGL",
2299 "UNPACK_PREMULTIPLY_ALPHA_WEBGL"
2300 ];
2301
2302 /**
2303 * @const
2304 * @type {!Array.<string>}
2305 */
2306 WebGLRenderingContextResource.StateParameters = [
2307 "ACTIVE_TEXTURE",
2308 "ARRAY_BUFFER_BINDING",
2309 "BLEND_COLOR",
2310 "BLEND_DST_ALPHA",
2311 "BLEND_DST_RGB",
2312 "BLEND_EQUATION_ALPHA",
2313 "BLEND_EQUATION_RGB",
2314 "BLEND_SRC_ALPHA",
2315 "BLEND_SRC_RGB",
2316 "COLOR_CLEAR_VALUE",
2317 "COLOR_WRITEMASK",
2318 "CULL_FACE_MODE",
2319 "CURRENT_PROGRAM",
2320 "DEPTH_CLEAR_VALUE",
2321 "DEPTH_FUNC",
2322 "DEPTH_RANGE",
2323 "DEPTH_WRITEMASK",
2324 "ELEMENT_ARRAY_BUFFER_BINDING",
2325 "FRAGMENT_SHADER_DERIVATIVE_HINT_OES", // OES_standard_derivatives extension
2326 "FRAMEBUFFER_BINDING",
2327 "FRONT_FACE",
2328 "GENERATE_MIPMAP_HINT",
2329 "LINE_WIDTH",
2330 "PACK_ALIGNMENT",
2331 "POLYGON_OFFSET_FACTOR",
2332 "POLYGON_OFFSET_UNITS",
2333 "RENDERBUFFER_BINDING",
2334 "SAMPLE_COVERAGE_INVERT",
2335 "SAMPLE_COVERAGE_VALUE",
2336 "SCISSOR_BOX",
2337 "STENCIL_BACK_FAIL",
2338 "STENCIL_BACK_FUNC",
2339 "STENCIL_BACK_PASS_DEPTH_FAIL",
2340 "STENCIL_BACK_PASS_DEPTH_PASS",
2341 "STENCIL_BACK_REF",
2342 "STENCIL_BACK_VALUE_MASK",
2343 "STENCIL_BACK_WRITEMASK",
2344 "STENCIL_CLEAR_VALUE",
2345 "STENCIL_FAIL",
2346 "STENCIL_FUNC",
2347 "STENCIL_PASS_DEPTH_FAIL",
2348 "STENCIL_PASS_DEPTH_PASS",
2349 "STENCIL_REF",
2350 "STENCIL_VALUE_MASK",
2351 "STENCIL_WRITEMASK",
2352 "UNPACK_ALIGNMENT",
2353 "UNPACK_COLORSPACE_CONVERSION_WEBGL",
2354 "UNPACK_FLIP_Y_WEBGL",
2355 "UNPACK_PREMULTIPLY_ALPHA_WEBGL",
2356 "VERTEX_ARRAY_BINDING_OES", // OES_vertex_array_object extension
2357 "VIEWPORT"
2358 ];
2359
2360 /**
2361 * True for those enums that return also an enum via a getter API method (e.g. g etParameter, getShaderParameter, etc.).
2362 * @const
2363 * @type {!Object.<string, boolean>}
2364 */
2365 WebGLRenderingContextResource.GetResultIsEnum = TypeUtils.createPrefixedProperty NamesSet([
2366 // gl.getParameter()
2367 "ACTIVE_TEXTURE",
2368 "BLEND_DST_ALPHA",
2369 "BLEND_DST_RGB",
2370 "BLEND_EQUATION_ALPHA",
2371 "BLEND_EQUATION_RGB",
2372 "BLEND_SRC_ALPHA",
2373 "BLEND_SRC_RGB",
2374 "CULL_FACE_MODE",
2375 "DEPTH_FUNC",
2376 "FRONT_FACE",
2377 "GENERATE_MIPMAP_HINT",
2378 "FRAGMENT_SHADER_DERIVATIVE_HINT_OES",
2379 "STENCIL_BACK_FAIL",
2380 "STENCIL_BACK_FUNC",
2381 "STENCIL_BACK_PASS_DEPTH_FAIL",
2382 "STENCIL_BACK_PASS_DEPTH_PASS",
2383 "STENCIL_FAIL",
2384 "STENCIL_FUNC",
2385 "STENCIL_PASS_DEPTH_FAIL",
2386 "STENCIL_PASS_DEPTH_PASS",
2387 "UNPACK_COLORSPACE_CONVERSION_WEBGL",
2388 // gl.getBufferParameter()
2389 "BUFFER_USAGE",
2390 // gl.getFramebufferAttachmentParameter()
2391 "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE",
2392 // gl.getRenderbufferParameter()
2393 "RENDERBUFFER_INTERNAL_FORMAT",
2394 // gl.getTexParameter()
2395 "TEXTURE_MAG_FILTER",
2396 "TEXTURE_MIN_FILTER",
2397 "TEXTURE_WRAP_S",
2398 "TEXTURE_WRAP_T",
2399 // gl.getShaderParameter()
2400 "SHADER_TYPE",
2401 // gl.getVertexAttrib()
2402 "VERTEX_ATTRIB_ARRAY_TYPE"
2403 ]);
2404
2405 /**
2406 * @const
2407 * @type {!Object.<string, boolean>}
2408 */
2409 WebGLRenderingContextResource.DrawingMethods = TypeUtils.createPrefixedPropertyN amesSet([
2410 "clear",
2411 "drawArrays",
2412 "drawElements"
2413 ]);
2414
2415 /**
2416 * @param {*} obj
2417 * @return {?WebGLRenderingContextResource}
2418 */
2419 WebGLRenderingContextResource.forObject = function(obj)
2420 {
2421 var resource = Resource.forObject(obj);
2422 if (!resource)
2423 return null;
2424 resource = resource.contextResource();
2425 return (resource instanceof WebGLRenderingContextResource) ? resource : null ;
2426 }
2427
2428 WebGLRenderingContextResource.prototype = {
2429 /**
2430 * @override (overrides @return type)
2431 * @return {!WebGLRenderingContext}
2432 */
2433 wrappedObject: function()
2434 {
2435 return this._wrappedObject;
2436 },
2437
2438 /**
2439 * @override
2440 * @return {string}
2441 */
2442 toDataURL: function()
2443 {
2444 return this.wrappedObject().canvas.toDataURL();
2445 },
2446
2447 /**
2448 * @return {!Array.<number>}
2449 */
2450 getAllErrors: function()
2451 {
2452 var errors = [];
2453 var gl = this.wrappedObject();
2454 if (gl) {
2455 while (true) {
2456 var error = gl.getError();
2457 if (error === gl.NO_ERROR)
2458 break;
2459 this.clearError(error);
2460 errors.push(error);
2461 }
2462 }
2463 if (this._customErrors) {
2464 for (var key in this._customErrors) {
2465 var error = Number(key);
2466 errors.push(error);
2467 }
2468 delete this._customErrors;
2469 }
2470 return errors;
2471 },
2472
2473 /**
2474 * @param {!Array.<number>} errors
2475 */
2476 restoreErrors: function(errors)
2477 {
2478 var gl = this.wrappedObject();
2479 if (gl) {
2480 var wasError = false;
2481 while (gl.getError() !== gl.NO_ERROR)
2482 wasError = true;
2483 console.assert(!wasError, "Error(s) while capturing current WebGL st ate.");
2484 }
2485 if (!errors.length)
2486 delete this._customErrors;
2487 else {
2488 this._customErrors = {};
2489 for (var i = 0, n = errors.length; i < n; ++i)
2490 this._customErrors[errors[i]] = true;
2491 }
2492 },
2493
2494 /**
2495 * @param {number} error
2496 */
2497 clearError: function(error)
2498 {
2499 if (this._customErrors)
2500 delete this._customErrors[error];
2501 },
2502
2503 /**
2504 * @return {number}
2505 */
2506 nextError: function()
2507 {
2508 if (this._customErrors) {
2509 for (var key in this._customErrors) {
2510 var error = Number(key);
2511 delete this._customErrors[error];
2512 return error;
2513 }
2514 }
2515 delete this._customErrors;
2516 var gl = this.wrappedObject();
2517 return gl ? gl.NO_ERROR : 0;
2518 },
2519
2520 /**
2521 * @param {string} name
2522 * @param {?Object} obj
2523 */
2524 registerWebGLExtension: function(name, obj)
2525 {
2526 // FIXME: Wrap OES_vertex_array_object extension.
2527 var lowerName = name.toLowerCase();
2528 if (obj && !this._extensions[lowerName]) {
2529 this._extensions[lowerName] = name;
2530 for (var property in obj) {
2531 if (TypeUtils.isEnumPropertyName(property, obj))
2532 this._extensionEnums[property] = /** @type {number} */ (obj[ property]);
2533 }
2534 }
2535 },
2536
2537 /**
2538 * @param {string} name
2539 * @return {number|undefined}
2540 */
2541 _enumValueForName: function(name)
2542 {
2543 if (typeof this._extensionEnums[name] === "number")
2544 return this._extensionEnums[name];
2545 var gl = this.wrappedObject();
2546 return (typeof gl[name] === "number" ? gl[name] : undefined);
2547 },
2548
2549 /**
2550 * @param {function(this:WebGLRenderingContext, T, number):*} func
2551 * @param {T} targetOrWebGLObject
2552 * @param {!Array.<string>} pnames
2553 * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} output
2554 * @template T
2555 */
2556 queryStateValues: function(func, targetOrWebGLObject, pnames, output)
2557 {
2558 var gl = this.wrappedObject();
2559 for (var i = 0, pname; pname = pnames[i]; ++i) {
2560 var enumValue = this._enumValueForName(pname);
2561 if (typeof enumValue !== "number")
2562 continue;
2563 var value = func.call(gl, targetOrWebGLObject, enumValue);
2564 value = Resource.forObject(value) || value;
2565 output.push({ name: pname, value: value, valueIsEnum: WebGLRendering ContextResource.GetResultIsEnum[pname] });
2566 }
2567 },
2568
2569 /**
2570 * @override
2571 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
2572 */
2573 currentState: function()
2574 {
2575 /**
2576 * @param {!Object} obj
2577 * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} output
2578 */
2579 function convertToStateDescriptors(obj, output)
2580 {
2581 for (var pname in obj)
2582 output.push({ name: pname, value: obj[pname], valueIsEnum: WebGL RenderingContextResource.GetResultIsEnum[pname] });
2583 }
2584
2585 var gl = this.wrappedObject();
2586 var glState = this._internalCurrentState(null);
2587
2588 // VERTEX_ATTRIB_ARRAYS
2589 var vertexAttribStates = [];
2590 for (var i = 0, n = glState.VERTEX_ATTRIB_ARRAYS.length; i < n; ++i) {
2591 var pname = "" + i;
2592 var values = [];
2593 convertToStateDescriptors(glState.VERTEX_ATTRIB_ARRAYS[i], values);
2594 vertexAttribStates.push({ name: pname, values: values });
2595 }
2596 delete glState.VERTEX_ATTRIB_ARRAYS;
2597
2598 // TEXTURE_UNITS
2599 var textureUnits = [];
2600 for (var i = 0, n = glState.TEXTURE_UNITS.length; i < n; ++i) {
2601 var pname = "TEXTURE" + i;
2602 var values = [];
2603 convertToStateDescriptors(glState.TEXTURE_UNITS[i], values);
2604 textureUnits.push({ name: pname, values: values });
2605 }
2606 delete glState.TEXTURE_UNITS;
2607
2608 var result = [];
2609 convertToStateDescriptors(glState, result);
2610 result.push({ name: "VERTEX_ATTRIB_ARRAYS", values: vertexAttribStates, isArray: true });
2611 result.push({ name: "TEXTURE_UNITS", values: textureUnits, isArray: true });
2612
2613 var textureBindingParameters = ["TEXTURE_BINDING_2D", "TEXTURE_BINDING_C UBE_MAP"];
2614 for (var i = 0, pname; pname = textureBindingParameters[i]; ++i) {
2615 var value = gl.getParameter(gl[pname]);
2616 value = Resource.forObject(value) || value;
2617 result.push({ name: pname, value: value });
2618 }
2619
2620 // ENABLED_EXTENSIONS
2621 var enabledExtensions = [];
2622 for (var lowerName in this._extensions) {
2623 var pname = this._extensions[lowerName];
2624 var value = gl.getExtension(pname);
2625 value = Resource.forObject(value) || value;
2626 enabledExtensions.push({ name: pname, value: value });
2627 }
2628 result.push({ name: "ENABLED_EXTENSIONS", values: enabledExtensions, isA rray: true });
2629
2630 return result;
2631 },
2632
2633 /**
2634 * @param {?Cache.<!ReplayableResource>} cache
2635 * @return {!Object.<string, *>}
2636 */
2637 _internalCurrentState: function(cache)
2638 {
2639 /**
2640 * @param {!Resource|*} obj
2641 * @return {!Resource|!ReplayableResource|*}
2642 */
2643 function maybeToReplayable(obj)
2644 {
2645 return cache ? Resource.toReplayable(obj, cache) : (Resource.forObje ct(obj) || obj);
2646 }
2647
2648 var gl = this.wrappedObject();
2649 var originalErrors = this.getAllErrors();
2650
2651 // Take a full GL state snapshot.
2652 var glState = Object.create(null);
2653 WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {
2654 glState[parameter] = gl.isEnabled(gl[parameter]);
2655 });
2656 for (var i = 0, pname; pname = WebGLRenderingContextResource.StateParame ters[i]; ++i) {
2657 var enumValue = this._enumValueForName(pname);
2658 if (typeof enumValue === "number")
2659 glState[pname] = maybeToReplayable(gl.getParameter(enumValue));
2660 }
2661
2662 // VERTEX_ATTRIB_ARRAYS
2663 var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VER TEX_ATTRIBS));
2664 var vertexAttribParameters = [
2665 "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING",
2666 "VERTEX_ATTRIB_ARRAY_ENABLED",
2667 "VERTEX_ATTRIB_ARRAY_SIZE",
2668 "VERTEX_ATTRIB_ARRAY_STRIDE",
2669 "VERTEX_ATTRIB_ARRAY_TYPE",
2670 "VERTEX_ATTRIB_ARRAY_NORMALIZED",
2671 "CURRENT_VERTEX_ATTRIB",
2672 "VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE" // ANGLE_instanced_arrays extens ion
2673 ];
2674 var vertexAttribStates = [];
2675 for (var index = 0; index < maxVertexAttribs; ++index) {
2676 var state = Object.create(null);
2677 for (var i = 0, pname; pname = vertexAttribParameters[i]; ++i) {
2678 var enumValue = this._enumValueForName(pname);
2679 if (typeof enumValue === "number")
2680 state[pname] = maybeToReplayable(gl.getVertexAttrib(index, e numValue));
2681 }
2682 state.VERTEX_ATTRIB_ARRAY_POINTER = gl.getVertexAttribOffset(index, gl.VERTEX_ATTRIB_ARRAY_POINTER);
2683 vertexAttribStates.push(state);
2684 }
2685 glState.VERTEX_ATTRIB_ARRAYS = vertexAttribStates;
2686
2687 // TEXTURE_UNITS
2688 var savedActiveTexture = /** @type {number} */ (gl.getParameter(gl.ACTIV E_TEXTURE));
2689 var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX _TEXTURE_IMAGE_UNITS));
2690 var textureUnits = [];
2691 for (var i = 0; i < maxTextureImageUnits; ++i) {
2692 gl.activeTexture(gl.TEXTURE0 + i);
2693 var state = Object.create(null);
2694 state.TEXTURE_2D = maybeToReplayable(gl.getParameter(gl.TEXTURE_BIND ING_2D));
2695 state.TEXTURE_CUBE_MAP = maybeToReplayable(gl.getParameter(gl.TEXTUR E_BINDING_CUBE_MAP));
2696 textureUnits.push(state);
2697 }
2698 glState.TEXTURE_UNITS = textureUnits;
2699 gl.activeTexture(savedActiveTexture);
2700
2701 this.restoreErrors(originalErrors);
2702 return glState;
2703 },
2704
2705 /**
2706 * @override
2707 * @param {!Object} data
2708 * @param {!Cache.<!ReplayableResource>} cache
2709 */
2710 _populateReplayableData: function(data, cache)
2711 {
2712 var gl = this.wrappedObject();
2713 data.originalCanvas = gl.canvas;
2714 data.originalContextAttributes = gl.getContextAttributes();
2715 data.extensions = TypeUtils.cloneObject(this._extensions);
2716 data.extensionEnums = TypeUtils.cloneObject(this._extensionEnums);
2717 data.glState = this._internalCurrentState(cache);
2718 },
2719
2720 /**
2721 * @override
2722 * @param {!Object} data
2723 * @param {!Cache.<!Resource>} cache
2724 */
2725 _doReplayCalls: function(data, cache)
2726 {
2727 this._customErrors = null;
2728 this._extensions = TypeUtils.cloneObject(data.extensions) || {};
2729 this._extensionEnums = TypeUtils.cloneObject(data.extensionEnums) || {};
2730
2731 var canvas = data.originalCanvas.cloneNode(true);
2732 var replayContext = null;
2733 var contextIds = ["experimental-webgl", "webkit-3d", "3d"];
2734 for (var i = 0, contextId; contextId = contextIds[i]; ++i) {
2735 replayContext = canvas.getContext(contextId, data.originalContextAtt ributes);
2736 if (replayContext)
2737 break;
2738 }
2739
2740 console.assert(replayContext, "Failed to create a WebGLRenderingContext for the replay.");
2741
2742 var gl = /** @type {!WebGLRenderingContext} */ (Resource.wrappedObject(r eplayContext));
2743 this.setWrappedObject(gl);
2744
2745 // Enable corresponding WebGL extensions.
2746 for (var name in this._extensions)
2747 gl.getExtension(name);
2748
2749 var glState = data.glState;
2750 gl.bindFramebuffer(gl.FRAMEBUFFER, /** @type {!WebGLFramebuffer} */ (Rep layableResource.replay(glState.FRAMEBUFFER_BINDING, cache)));
2751 gl.bindRenderbuffer(gl.RENDERBUFFER, /** @type {!WebGLRenderbuffer} */ ( ReplayableResource.replay(glState.RENDERBUFFER_BINDING, cache)));
2752
2753 // Enable or disable server-side GL capabilities.
2754 WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {
2755 console.assert(parameter in glState);
2756 if (glState[parameter])
2757 gl.enable(gl[parameter]);
2758 else
2759 gl.disable(gl[parameter]);
2760 });
2761
2762 gl.blendColor(glState.BLEND_COLOR[0], glState.BLEND_COLOR[1], glState.BL END_COLOR[2], glState.BLEND_COLOR[3]);
2763 gl.blendEquationSeparate(glState.BLEND_EQUATION_RGB, glState.BLEND_EQUAT ION_ALPHA);
2764 gl.blendFuncSeparate(glState.BLEND_SRC_RGB, glState.BLEND_DST_RGB, glSta te.BLEND_SRC_ALPHA, glState.BLEND_DST_ALPHA);
2765 gl.clearColor(glState.COLOR_CLEAR_VALUE[0], glState.COLOR_CLEAR_VALUE[1] , glState.COLOR_CLEAR_VALUE[2], glState.COLOR_CLEAR_VALUE[3]);
2766 gl.clearDepth(glState.DEPTH_CLEAR_VALUE);
2767 gl.clearStencil(glState.STENCIL_CLEAR_VALUE);
2768 gl.colorMask(glState.COLOR_WRITEMASK[0], glState.COLOR_WRITEMASK[1], glS tate.COLOR_WRITEMASK[2], glState.COLOR_WRITEMASK[3]);
2769 gl.cullFace(glState.CULL_FACE_MODE);
2770 gl.depthFunc(glState.DEPTH_FUNC);
2771 gl.depthMask(glState.DEPTH_WRITEMASK);
2772 gl.depthRange(glState.DEPTH_RANGE[0], glState.DEPTH_RANGE[1]);
2773 gl.frontFace(glState.FRONT_FACE);
2774 gl.hint(gl.GENERATE_MIPMAP_HINT, glState.GENERATE_MIPMAP_HINT);
2775 gl.lineWidth(glState.LINE_WIDTH);
2776
2777 var enumValue = this._enumValueForName("FRAGMENT_SHADER_DERIVATIVE_HINT_ OES");
2778 if (typeof enumValue === "number")
2779 gl.hint(enumValue, glState.FRAGMENT_SHADER_DERIVATIVE_HINT_OES);
2780
2781 WebGLRenderingContextResource.PixelStoreParameters.forEach(function(para meter) {
2782 gl.pixelStorei(gl[parameter], glState[parameter]);
2783 });
2784
2785 gl.polygonOffset(glState.POLYGON_OFFSET_FACTOR, glState.POLYGON_OFFSET_U NITS);
2786 gl.sampleCoverage(glState.SAMPLE_COVERAGE_VALUE, glState.SAMPLE_COVERAGE _INVERT);
2787 gl.stencilFuncSeparate(gl.FRONT, glState.STENCIL_FUNC, glState.STENCIL_R EF, glState.STENCIL_VALUE_MASK);
2788 gl.stencilFuncSeparate(gl.BACK, glState.STENCIL_BACK_FUNC, glState.STENC IL_BACK_REF, glState.STENCIL_BACK_VALUE_MASK);
2789 gl.stencilOpSeparate(gl.FRONT, glState.STENCIL_FAIL, glState.STENCIL_PAS S_DEPTH_FAIL, glState.STENCIL_PASS_DEPTH_PASS);
2790 gl.stencilOpSeparate(gl.BACK, glState.STENCIL_BACK_FAIL, glState.STENCIL _BACK_PASS_DEPTH_FAIL, glState.STENCIL_BACK_PASS_DEPTH_PASS);
2791 gl.stencilMaskSeparate(gl.FRONT, glState.STENCIL_WRITEMASK);
2792 gl.stencilMaskSeparate(gl.BACK, glState.STENCIL_BACK_WRITEMASK);
2793
2794 gl.scissor(glState.SCISSOR_BOX[0], glState.SCISSOR_BOX[1], glState.SCISS OR_BOX[2], glState.SCISSOR_BOX[3]);
2795 gl.viewport(glState.VIEWPORT[0], glState.VIEWPORT[1], glState.VIEWPORT[2 ], glState.VIEWPORT[3]);
2796
2797 gl.useProgram(/** @type {!WebGLProgram} */ (ReplayableResource.replay(gl State.CURRENT_PROGRAM, cache)));
2798
2799 // VERTEX_ATTRIB_ARRAYS
2800 var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VER TEX_ATTRIBS));
2801 for (var i = 0; i < maxVertexAttribs; ++i) {
2802 var state = glState.VERTEX_ATTRIB_ARRAYS[i] || {};
2803 if (state.VERTEX_ATTRIB_ARRAY_ENABLED)
2804 gl.enableVertexAttribArray(i);
2805 else
2806 gl.disableVertexAttribArray(i);
2807 if (state.CURRENT_VERTEX_ATTRIB)
2808 gl.vertexAttrib4fv(i, state.CURRENT_VERTEX_ATTRIB);
2809 var buffer = /** @type {!WebGLBuffer} */ (ReplayableResource.replay( state.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, cache));
2810 if (buffer) {
2811 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
2812 gl.vertexAttribPointer(i, state.VERTEX_ATTRIB_ARRAY_SIZE, state. VERTEX_ATTRIB_ARRAY_TYPE, state.VERTEX_ATTRIB_ARRAY_NORMALIZED, state.VERTEX_ATT RIB_ARRAY_STRIDE, state.VERTEX_ATTRIB_ARRAY_POINTER);
2813 }
2814 }
2815 gl.bindBuffer(gl.ARRAY_BUFFER, /** @type {!WebGLBuffer} */ (ReplayableRe source.replay(glState.ARRAY_BUFFER_BINDING, cache)));
2816 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, /** @type {!WebGLBuffer} */ (Repl ayableResource.replay(glState.ELEMENT_ARRAY_BUFFER_BINDING, cache)));
2817
2818 // TEXTURE_UNITS
2819 var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX _TEXTURE_IMAGE_UNITS));
2820 for (var i = 0; i < maxTextureImageUnits; ++i) {
2821 gl.activeTexture(gl.TEXTURE0 + i);
2822 var state = glState.TEXTURE_UNITS[i] || {};
2823 gl.bindTexture(gl.TEXTURE_2D, /** @type {!WebGLTexture} */ (Replayab leResource.replay(state.TEXTURE_2D, cache)));
2824 gl.bindTexture(gl.TEXTURE_CUBE_MAP, /** @type {!WebGLTexture} */ (Re playableResource.replay(state.TEXTURE_CUBE_MAP, cache)));
2825 }
2826 gl.activeTexture(glState.ACTIVE_TEXTURE);
2827
2828 ContextResource.prototype._doReplayCalls.call(this, data, cache);
2829 },
2830
2831 /**
2832 * @param {!Object|number} target
2833 * @return {?Resource}
2834 */
2835 currentBinding: function(target)
2836 {
2837 var resource = Resource.forObject(target);
2838 if (resource)
2839 return resource;
2840 var gl = this.wrappedObject();
2841 var bindingParameter;
2842 var bindMethodName;
2843 target = +target; // Explicitly convert to a number.
2844 var bindMethodTarget = target;
2845 switch (target) {
2846 case gl.ARRAY_BUFFER:
2847 bindingParameter = gl.ARRAY_BUFFER_BINDING;
2848 bindMethodName = "bindBuffer";
2849 break;
2850 case gl.ELEMENT_ARRAY_BUFFER:
2851 bindingParameter = gl.ELEMENT_ARRAY_BUFFER_BINDING;
2852 bindMethodName = "bindBuffer";
2853 break;
2854 case gl.TEXTURE_2D:
2855 bindingParameter = gl.TEXTURE_BINDING_2D;
2856 bindMethodName = "bindTexture";
2857 break;
2858 case gl.TEXTURE_CUBE_MAP:
2859 case gl.TEXTURE_CUBE_MAP_POSITIVE_X:
2860 case gl.TEXTURE_CUBE_MAP_NEGATIVE_X:
2861 case gl.TEXTURE_CUBE_MAP_POSITIVE_Y:
2862 case gl.TEXTURE_CUBE_MAP_NEGATIVE_Y:
2863 case gl.TEXTURE_CUBE_MAP_POSITIVE_Z:
2864 case gl.TEXTURE_CUBE_MAP_NEGATIVE_Z:
2865 bindingParameter = gl.TEXTURE_BINDING_CUBE_MAP;
2866 bindMethodTarget = gl.TEXTURE_CUBE_MAP;
2867 bindMethodName = "bindTexture";
2868 break;
2869 case gl.FRAMEBUFFER:
2870 bindingParameter = gl.FRAMEBUFFER_BINDING;
2871 bindMethodName = "bindFramebuffer";
2872 break;
2873 case gl.RENDERBUFFER:
2874 bindingParameter = gl.RENDERBUFFER_BINDING;
2875 bindMethodName = "bindRenderbuffer";
2876 break;
2877 default:
2878 console.error("ASSERT_NOT_REACHED: unknown binding target " + target );
2879 return null;
2880 }
2881 resource = Resource.forObject(gl.getParameter(bindingParameter));
2882 if (resource)
2883 resource.pushBinding(bindMethodTarget, bindMethodName);
2884 return resource;
2885 },
2886
2887 /**
2888 * @override
2889 * @param {!Call} call
2890 */
2891 onCallReplayed: function(call)
2892 {
2893 var functionName = call.functionName();
2894 var args = call.args();
2895 switch (functionName) {
2896 case "bindBuffer":
2897 case "bindFramebuffer":
2898 case "bindRenderbuffer":
2899 case "bindTexture":
2900 // Update BINDING state for Resources in the replay world.
2901 var resource = Resource.forObject(args[1]);
2902 if (resource)
2903 resource.pushBinding(args[0], functionName);
2904 break;
2905 case "getExtension":
2906 this.registerWebGLExtension(args[0], /** @type {!Object} */ (call.re sult()));
2907 break;
2908 case "bufferData":
2909 var resource = /** @type {!WebGLBufferResource} */ (this.currentBind ing(args[0]));
2910 if (resource)
2911 resource.pushCall_bufferData(call);
2912 break;
2913 case "bufferSubData":
2914 var resource = /** @type {!WebGLBufferResource} */ (this.currentBind ing(args[0]));
2915 if (resource)
2916 resource.pushCall_bufferSubData(call);
2917 break;
2918 }
2919 },
2920
2921 /**
2922 * @override
2923 * @return {!Object.<string, !Function>}
2924 */
2925 _customWrapFunctions: function()
2926 {
2927 var wrapFunctions = WebGLRenderingContextResource._wrapFunctions;
2928 if (!wrapFunctions) {
2929 wrapFunctions = Object.create(null);
2930
2931 wrapFunctions["createBuffer"] = Resource.WrapFunction.resourceFactor yMethod(WebGLBufferResource, "WebGLBuffer");
2932 wrapFunctions["createShader"] = Resource.WrapFunction.resourceFactor yMethod(WebGLShaderResource, "WebGLShader");
2933 wrapFunctions["createProgram"] = Resource.WrapFunction.resourceFacto ryMethod(WebGLProgramResource, "WebGLProgram");
2934 wrapFunctions["createTexture"] = Resource.WrapFunction.resourceFacto ryMethod(WebGLTextureResource, "WebGLTexture");
2935 wrapFunctions["createFramebuffer"] = Resource.WrapFunction.resourceF actoryMethod(WebGLFramebufferResource, "WebGLFramebuffer");
2936 wrapFunctions["createRenderbuffer"] = Resource.WrapFunction.resource FactoryMethod(WebGLRenderbufferResource, "WebGLRenderbuffer");
2937 wrapFunctions["getUniformLocation"] = Resource.WrapFunction.resource FactoryMethod(WebGLUniformLocationResource, "WebGLUniformLocation");
2938
2939 stateModifyingWrapFunction("bindAttribLocation");
2940 stateModifyingWrapFunction("compileShader");
2941 stateModifyingWrapFunction("detachShader");
2942 stateModifyingWrapFunction("linkProgram");
2943 stateModifyingWrapFunction("shaderSource");
2944 stateModifyingWrapFunction("bufferData", WebGLBufferResource.prototy pe.pushCall_bufferData);
2945 stateModifyingWrapFunction("bufferSubData", WebGLBufferResource.prot otype.pushCall_bufferSubData);
2946 stateModifyingWrapFunction("compressedTexImage2D");
2947 stateModifyingWrapFunction("compressedTexSubImage2D");
2948 stateModifyingWrapFunction("copyTexImage2D", WebGLTextureResource.pr ototype.pushCall_copyTexImage2D);
2949 stateModifyingWrapFunction("copyTexSubImage2D", WebGLTextureResource .prototype.pushCall_copyTexImage2D);
2950 stateModifyingWrapFunction("generateMipmap");
2951 stateModifyingWrapFunction("texImage2D");
2952 stateModifyingWrapFunction("texSubImage2D");
2953 stateModifyingWrapFunction("texParameterf", WebGLTextureResource.pro totype.pushCall_texParameter);
2954 stateModifyingWrapFunction("texParameteri", WebGLTextureResource.pro totype.pushCall_texParameter);
2955 stateModifyingWrapFunction("renderbufferStorage");
2956
2957 /** @this {Resource.WrapFunction} */
2958 wrapFunctions["getError"] = function()
2959 {
2960 var gl = /** @type {!WebGLRenderingContext} */ (this._originalOb ject);
2961 var error = this.result();
2962 if (error !== gl.NO_ERROR)
2963 this._resource.clearError(error);
2964 else {
2965 error = this._resource.nextError();
2966 if (error !== gl.NO_ERROR)
2967 this.overrideResult(error);
2968 }
2969 }
2970
2971 /**
2972 * @param {string} name
2973 * @this {Resource.WrapFunction}
2974 */
2975 wrapFunctions["getExtension"] = function(name)
2976 {
2977 this._resource.registerWebGLExtension(name, this.result());
2978 }
2979
2980 //
2981 // Register bound WebGL resources.
2982 //
2983
2984 /**
2985 * @param {!WebGLProgram} program
2986 * @param {!WebGLShader} shader
2987 * @this {Resource.WrapFunction}
2988 */
2989 wrapFunctions["attachShader"] = function(program, shader)
2990 {
2991 var resource = this._resource.currentBinding(program);
2992 if (resource) {
2993 resource.pushCall(this.call());
2994 var shaderResource = /** @type {!WebGLShaderResource} */ (Re source.forObject(shader));
2995 if (shaderResource) {
2996 var shaderType = shaderResource.type();
2997 resource._registerBoundResource("__attachShader_" + shad erType, shaderResource);
2998 }
2999 }
3000 }
3001 /**
3002 * @param {number} target
3003 * @param {number} attachment
3004 * @param {number} objectTarget
3005 * @param {!WebGLRenderbuffer|!WebGLTexture} obj
3006 * @this {Resource.WrapFunction}
3007 */
3008 wrapFunctions["framebufferRenderbuffer"] = wrapFunctions["framebuffe rTexture2D"] = function(target, attachment, objectTarget, obj)
3009 {
3010 var resource = this._resource.currentBinding(target);
3011 if (resource) {
3012 resource.pushCall(this.call());
3013 resource._registerBoundResource("__framebufferAttachmentObje ctName", obj);
3014 }
3015 }
3016 /**
3017 * @param {number} target
3018 * @param {!Object} obj
3019 * @this {Resource.WrapFunction}
3020 */
3021 wrapFunctions["bindBuffer"] = wrapFunctions["bindFramebuffer"] = wra pFunctions["bindRenderbuffer"] = function(target, obj)
3022 {
3023 this._resource.currentBinding(target); // To call WebGLBoundReso urce.prototype.pushBinding().
3024 this._resource._registerBoundResource("__bindBuffer_" + target, obj);
3025 }
3026 /**
3027 * @param {number} target
3028 * @param {!WebGLTexture} obj
3029 * @this {Resource.WrapFunction}
3030 */
3031 wrapFunctions["bindTexture"] = function(target, obj)
3032 {
3033 this._resource.currentBinding(target); // To call WebGLBoundReso urce.prototype.pushBinding().
3034 var gl = /** @type {!WebGLRenderingContext} */ (this._originalOb ject);
3035 var currentTextureBinding = /** @type {number} */ (gl.getParamet er(gl.ACTIVE_TEXTURE));
3036 this._resource._registerBoundResource("__bindTexture_" + target + "_" + currentTextureBinding, obj);
3037 }
3038 /**
3039 * @param {!WebGLProgram} program
3040 * @this {Resource.WrapFunction}
3041 */
3042 wrapFunctions["useProgram"] = function(program)
3043 {
3044 this._resource._registerBoundResource("__useProgram", program);
3045 }
3046 /**
3047 * @param {number} index
3048 * @this {Resource.WrapFunction}
3049 */
3050 wrapFunctions["vertexAttribPointer"] = function(index)
3051 {
3052 var gl = /** @type {!WebGLRenderingContext} */ (this._originalOb ject);
3053 this._resource._registerBoundResource("__vertexAttribPointer_" + index, gl.getParameter(gl.ARRAY_BUFFER_BINDING));
3054 }
3055
3056 WebGLRenderingContextResource._wrapFunctions = wrapFunctions;
3057 }
3058
3059 /**
3060 * @param {string} methodName
3061 * @param {function(this:Resource, !Call)=} pushCallFunc
3062 */
3063 function stateModifyingWrapFunction(methodName, pushCallFunc)
3064 {
3065 if (pushCallFunc) {
3066 /**
3067 * @param {!Object|number} target
3068 * @this {Resource.WrapFunction}
3069 */
3070 wrapFunctions[methodName] = function(target)
3071 {
3072 var resource = this._resource.currentBinding(target);
3073 if (resource)
3074 pushCallFunc.call(resource, this.call());
3075 }
3076 } else {
3077 /**
3078 * @param {!Object|number} target
3079 * @this {Resource.WrapFunction}
3080 */
3081 wrapFunctions[methodName] = function(target)
3082 {
3083 var resource = this._resource.currentBinding(target);
3084 if (resource)
3085 resource.pushCall(this.call());
3086 }
3087 }
3088 }
3089
3090 return wrapFunctions;
3091 },
3092
3093 __proto__: ContextResource.prototype
3094 }
3095
3096 ////////////////////////////////////////////////////////////////////////////////
3097 // 2D Canvas
3098 ////////////////////////////////////////////////////////////////////////////////
3099
3100 /**
3101 * @constructor
3102 * @extends {ContextResource}
3103 * @param {!CanvasRenderingContext2D} context
3104 */
3105 function CanvasRenderingContext2DResource(context)
3106 {
3107 ContextResource.call(this, context, "CanvasRenderingContext2D");
3108 }
3109
3110 /**
3111 * @const
3112 * @type {!Array.<string>}
3113 */
3114 CanvasRenderingContext2DResource.AttributeProperties = [
3115 "strokeStyle",
3116 "fillStyle",
3117 "globalAlpha",
3118 "lineWidth",
3119 "lineCap",
3120 "lineJoin",
3121 "miterLimit",
3122 "shadowOffsetX",
3123 "shadowOffsetY",
3124 "shadowBlur",
3125 "shadowColor",
3126 "globalCompositeOperation",
3127 "font",
3128 "textAlign",
3129 "textBaseline",
3130 "lineDashOffset",
3131 "imageSmoothingEnabled",
3132 "webkitLineDash",
3133 "webkitLineDashOffset"
3134 ];
3135
3136 /**
3137 * @const
3138 * @type {!Array.<string>}
3139 */
3140 CanvasRenderingContext2DResource.PathMethods = [
3141 "beginPath",
3142 "moveTo",
3143 "closePath",
3144 "lineTo",
3145 "quadraticCurveTo",
3146 "bezierCurveTo",
3147 "arcTo",
3148 "arc",
3149 "rect",
3150 "ellipse"
3151 ];
3152
3153 /**
3154 * @const
3155 * @type {!Array.<string>}
3156 */
3157 CanvasRenderingContext2DResource.TransformationMatrixMethods = [
3158 "scale",
3159 "rotate",
3160 "translate",
3161 "transform",
3162 "setTransform",
3163 "resetTransform"
3164 ];
3165
3166 /**
3167 * @const
3168 * @type {!Object.<string, boolean>}
3169 */
3170 CanvasRenderingContext2DResource.DrawingMethods = TypeUtils.createPrefixedProper tyNamesSet([
3171 "clearRect",
3172 "drawImage",
3173 "drawImageFromRect",
3174 "drawCustomFocusRing",
3175 "drawFocusIfNeeded",
3176 "fill",
3177 "fillRect",
3178 "fillText",
3179 "putImageData",
3180 "putImageDataHD",
3181 "stroke",
3182 "strokeRect",
3183 "strokeText"
3184 ]);
3185
3186 CanvasRenderingContext2DResource.prototype = {
3187 /**
3188 * @override (overrides @return type)
3189 * @return {!CanvasRenderingContext2D}
3190 */
3191 wrappedObject: function()
3192 {
3193 return this._wrappedObject;
3194 },
3195
3196 /**
3197 * @override
3198 * @return {string}
3199 */
3200 toDataURL: function()
3201 {
3202 return this.wrappedObject().canvas.toDataURL();
3203 },
3204
3205 /**
3206 * @override
3207 * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>}
3208 */
3209 currentState: function()
3210 {
3211 var result = [];
3212 var state = this._internalCurrentState(null);
3213 for (var pname in state)
3214 result.push({ name: pname, value: state[pname] });
3215 result.push({ name: "context", value: this.contextResource() });
3216 return result;
3217 },
3218
3219 /**
3220 * @param {?Cache.<!ReplayableResource>} cache
3221 * @return {!Object.<string, *>}
3222 */
3223 _internalCurrentState: function(cache)
3224 {
3225 /**
3226 * @param {!Resource|*} obj
3227 * @return {!Resource|!ReplayableResource|*}
3228 */
3229 function maybeToReplayable(obj)
3230 {
3231 return cache ? Resource.toReplayable(obj, cache) : (Resource.forObje ct(obj) || obj);
3232 }
3233
3234 var ctx = this.wrappedObject();
3235 var state = Object.create(null);
3236 CanvasRenderingContext2DResource.AttributeProperties.forEach(function(at tribute) {
3237 if (attribute in ctx)
3238 state[attribute] = maybeToReplayable(ctx[attribute]);
3239 });
3240 if (ctx.getLineDash)
3241 state.lineDash = ctx.getLineDash();
3242 return state;
3243 },
3244
3245 /**
3246 * @param {?Object.<string, *>} state
3247 * @param {!Cache.<!Resource>} cache
3248 */
3249 _applyAttributesState: function(state, cache)
3250 {
3251 if (!state)
3252 return;
3253 var ctx = this.wrappedObject();
3254 for (var attribute in state) {
3255 if (attribute === "lineDash") {
3256 if (ctx.setLineDash)
3257 ctx.setLineDash(/** @type {!Array.<number>} */ (state[attrib ute]));
3258 } else
3259 ctx[attribute] = ReplayableResource.replay(state[attribute], cac he);
3260 }
3261 },
3262
3263 /**
3264 * @override
3265 * @param {!Object} data
3266 * @param {!Cache.<!ReplayableResource>} cache
3267 */
3268 _populateReplayableData: function(data, cache)
3269 {
3270 var ctx = this.wrappedObject();
3271 // FIXME: Convert resources in the state (CanvasGradient, CanvasPattern) to Replayable.
3272 data.currentAttributes = this._internalCurrentState(null);
3273 data.originalCanvasCloned = TypeUtils.cloneIntoCanvas(/** @type {!HTMLCa nvasElement} */ (ctx.canvas));
3274 if (ctx.getContextAttributes)
3275 data.originalContextAttributes = ctx.getContextAttributes();
3276 },
3277
3278 /**
3279 * @override
3280 * @param {!Object} data
3281 * @param {!Cache.<!Resource>} cache
3282 */
3283 _doReplayCalls: function(data, cache)
3284 {
3285 var canvas = TypeUtils.cloneIntoCanvas(data.originalCanvasCloned);
3286 var ctx = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObje ct(canvas.getContext("2d", data.originalContextAttributes)));
3287 this.setWrappedObject(ctx);
3288
3289 for (var i = 0, n = data.calls.length; i < n; ++i) {
3290 var replayableCall = /** @type {!ReplayableCall} */ (data.calls[i]);
3291 if (replayableCall.functionName() === "save")
3292 this._applyAttributesState(replayableCall.attachment("canvas2dAt tributesState"), cache);
3293 this._calls.push(replayableCall.replay(cache));
3294 }
3295 this._applyAttributesState(data.currentAttributes, cache);
3296 },
3297
3298 /**
3299 * @param {!Call} call
3300 */
3301 pushCall_setOrResetTransform: function(call)
3302 {
3303 var saveCallIndex = this._lastIndexOfMatchingSaveCall();
3304 var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.Pa thMethods);
3305 index = Math.max(index, saveCallIndex);
3306 if (this._removeCallsFromLog(CanvasRenderingContext2DResource.Transforma tionMatrixMethods, index + 1))
3307 this._removeAllObsoleteCallsFromLog();
3308 this.pushCall(call);
3309 },
3310
3311 /**
3312 * @param {!Call} call
3313 */
3314 pushCall_beginPath: function(call)
3315 {
3316 var index = this._lastIndexOfAnyCall(["clip"]);
3317 if (this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethod s, index + 1))
3318 this._removeAllObsoleteCallsFromLog();
3319 this.pushCall(call);
3320 },
3321
3322 /**
3323 * @param {!Call} call
3324 */
3325 pushCall_save: function(call)
3326 {
3327 // FIXME: Convert resources in the state (CanvasGradient, CanvasPattern) to Replayable.
3328 call.setAttachment("canvas2dAttributesState", this._internalCurrentState (null));
3329 this.pushCall(call);
3330 },
3331
3332 /**
3333 * @param {!Call} call
3334 */
3335 pushCall_restore: function(call)
3336 {
3337 var lastIndexOfSave = this._lastIndexOfMatchingSaveCall();
3338 if (lastIndexOfSave === -1)
3339 return;
3340 this._calls[lastIndexOfSave].setAttachment("canvas2dAttributesState", nu ll); // No longer needed, free memory.
3341
3342 var modified = false;
3343 if (this._removeCallsFromLog(["clip"], lastIndexOfSave + 1))
3344 modified = true;
3345
3346 var lastIndexOfAnyPathMethod = this._lastIndexOfAnyCall(CanvasRenderingC ontext2DResource.PathMethods);
3347 var index = Math.max(lastIndexOfSave, lastIndexOfAnyPathMethod);
3348 if (this._removeCallsFromLog(CanvasRenderingContext2DResource.Transforma tionMatrixMethods, index + 1))
3349 modified = true;
3350
3351 if (modified)
3352 this._removeAllObsoleteCallsFromLog();
3353
3354 var lastCall = this._calls[this._calls.length - 1];
3355 if (lastCall && lastCall.functionName() === "save")
3356 this._calls.pop();
3357 else
3358 this.pushCall(call);
3359 },
3360
3361 /**
3362 * @param {number=} fromIndex
3363 * @return {number}
3364 */
3365 _lastIndexOfMatchingSaveCall: function(fromIndex)
3366 {
3367 if (typeof fromIndex !== "number")
3368 fromIndex = this._calls.length - 1;
3369 else
3370 fromIndex = Math.min(fromIndex, this._calls.length - 1);
3371 var stackDepth = 1;
3372 for (var i = fromIndex; i >= 0; --i) {
3373 var functionName = this._calls[i].functionName();
3374 if (functionName === "restore")
3375 ++stackDepth;
3376 else if (functionName === "save") {
3377 --stackDepth;
3378 if (!stackDepth)
3379 return i;
3380 }
3381 }
3382 return -1;
3383 },
3384
3385 /**
3386 * @param {!Array.<string>} functionNames
3387 * @param {number=} fromIndex
3388 * @return {number}
3389 */
3390 _lastIndexOfAnyCall: function(functionNames, fromIndex)
3391 {
3392 if (typeof fromIndex !== "number")
3393 fromIndex = this._calls.length - 1;
3394 else
3395 fromIndex = Math.min(fromIndex, this._calls.length - 1);
3396 for (var i = fromIndex; i >= 0; --i) {
3397 if (functionNames.indexOf(this._calls[i].functionName()) !== -1)
3398 return i;
3399 }
3400 return -1;
3401 },
3402
3403 _removeAllObsoleteCallsFromLog: function()
3404 {
3405 // Remove all PATH methods between clip() and beginPath() calls.
3406 var lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"]);
3407 while (lastIndexOfBeginPath !== -1) {
3408 var index = this._lastIndexOfAnyCall(["clip"], lastIndexOfBeginPath - 1);
3409 this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethod s, index + 1, lastIndexOfBeginPath);
3410 lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"], index - 1);
3411 }
3412
3413 // Remove all TRASFORMATION MATRIX methods before restore(), setTransfor m() or resetTransform() but after any PATH or corresponding save() method.
3414 var lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform", " resetTransform"]);
3415 while (lastRestore !== -1) {
3416 var saveCallIndex = this._lastIndexOfMatchingSaveCall(lastRestore - 1);
3417 var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResourc e.PathMethods, lastRestore - 1);
3418 index = Math.max(index, saveCallIndex);
3419 this._removeCallsFromLog(CanvasRenderingContext2DResource.Transforma tionMatrixMethods, index + 1, lastRestore);
3420 lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform", " resetTransform"], index - 1);
3421 }
3422
3423 // Remove all save-restore consecutive pairs.
3424 var restoreCalls = 0;
3425 for (var i = this._calls.length - 1; i >= 0; --i) {
3426 var functionName = this._calls[i].functionName();
3427 if (functionName === "restore") {
3428 ++restoreCalls;
3429 continue;
3430 }
3431 if (functionName === "save" && restoreCalls > 0) {
3432 var saveCallIndex = i;
3433 for (var j = i - 1; j >= 0 && i - j < restoreCalls; --j) {
3434 if (this._calls[j].functionName() === "save")
3435 saveCallIndex = j;
3436 else
3437 break;
3438 }
3439 this._calls.splice(saveCallIndex, (i - saveCallIndex + 1) * 2);
3440 i = saveCallIndex;
3441 }
3442 restoreCalls = 0;
3443 }
3444 },
3445
3446 /**
3447 * @param {!Array.<string>} functionNames
3448 * @param {number} fromIndex
3449 * @param {number=} toIndex
3450 * @return {boolean}
3451 */
3452 _removeCallsFromLog: function(functionNames, fromIndex, toIndex)
3453 {
3454 var oldLength = this._calls.length;
3455 if (typeof toIndex !== "number")
3456 toIndex = oldLength;
3457 else
3458 toIndex = Math.min(toIndex, oldLength);
3459 var newIndex = Math.min(fromIndex, oldLength);
3460 for (var i = newIndex; i < toIndex; ++i) {
3461 var call = this._calls[i];
3462 if (functionNames.indexOf(call.functionName()) === -1)
3463 this._calls[newIndex++] = call;
3464 }
3465 if (newIndex >= toIndex)
3466 return false;
3467 this._calls.splice(newIndex, toIndex - newIndex);
3468 return true;
3469 },
3470
3471 /**
3472 * @override
3473 * @return {!Object.<string, !Function>}
3474 */
3475 _customWrapFunctions: function()
3476 {
3477 var wrapFunctions = CanvasRenderingContext2DResource._wrapFunctions;
3478 if (!wrapFunctions) {
3479 wrapFunctions = Object.create(null);
3480
3481 wrapFunctions["createLinearGradient"] = Resource.WrapFunction.resour ceFactoryMethod(LogEverythingResource, "CanvasGradient");
3482 wrapFunctions["createRadialGradient"] = Resource.WrapFunction.resour ceFactoryMethod(LogEverythingResource, "CanvasGradient");
3483 wrapFunctions["createPattern"] = Resource.WrapFunction.resourceFacto ryMethod(LogEverythingResource, "CanvasPattern");
3484
3485 for (var i = 0, methodName; methodName = CanvasRenderingContext2DRes ource.TransformationMatrixMethods[i]; ++i)
3486 stateModifyingWrapFunction(methodName, (methodName === "setTrans form" || methodName === "resetTransform") ? this.pushCall_setOrResetTransform : undefined);
3487 for (var i = 0, methodName; methodName = CanvasRenderingContext2DRes ource.PathMethods[i]; ++i)
3488 stateModifyingWrapFunction(methodName, methodName === "beginPath " ? this.pushCall_beginPath : undefined);
3489
3490 stateModifyingWrapFunction("save", this.pushCall_save);
3491 stateModifyingWrapFunction("restore", this.pushCall_restore);
3492 stateModifyingWrapFunction("clip");
3493
3494 CanvasRenderingContext2DResource._wrapFunctions = wrapFunctions;
3495 }
3496
3497 /**
3498 * @param {string} methodName
3499 * @param {function(this:Resource, !Call)=} func
3500 */
3501 function stateModifyingWrapFunction(methodName, func)
3502 {
3503 if (func) {
3504 /** @this {Resource.WrapFunction} */
3505 wrapFunctions[methodName] = function()
3506 {
3507 func.call(this._resource, this.call());
3508 }
3509 } else {
3510 /** @this {Resource.WrapFunction} */
3511 wrapFunctions[methodName] = function()
3512 {
3513 this._resource.pushCall(this.call());
3514 }
3515 }
3516 }
3517
3518 return wrapFunctions;
3519 },
3520
3521 __proto__: ContextResource.prototype
3522 }
3523
3524 /**
3525 * @constructor
3526 * @param {!Object.<string, boolean>=} drawingMethodNames
3527 */
3528 function CallFormatter(drawingMethodNames)
3529 {
3530 this._drawingMethodNames = drawingMethodNames || Object.create(null);
3531 }
3532
3533 CallFormatter.prototype = {
3534 /**
3535 * @param {!ReplayableCall} replayableCall
3536 * @param {string=} objectGroup
3537 * @return {!Object}
3538 */
3539 formatCall: function(replayableCall, objectGroup)
3540 {
3541 var result = {};
3542 var functionName = replayableCall.functionName();
3543 if (functionName) {
3544 result.functionName = functionName;
3545 result.arguments = [];
3546 var args = replayableCall.args();
3547 for (var i = 0, n = args.length; i < n; ++i)
3548 result.arguments.push(this.formatValue(args[i], objectGroup));
3549 if (replayableCall.result() !== undefined)
3550 result.result = this.formatValue(replayableCall.result(), object Group);
3551 if (this._drawingMethodNames[functionName])
3552 result.isDrawingCall = true;
3553 } else {
3554 result.property = replayableCall.propertyName();
3555 result.value = this.formatValue(replayableCall.propertyValue(), obje ctGroup);
3556 }
3557 return result;
3558 },
3559
3560 /**
3561 * @param {*} value
3562 * @param {string=} objectGroup
3563 * @return {!CanvasAgent.CallArgument}
3564 */
3565 formatValue: function(value, objectGroup)
3566 {
3567 if (value instanceof Resource || value instanceof ReplayableResource) {
3568 return {
3569 description: value.description(),
3570 resourceId: CallFormatter.makeStringResourceId(value.id())
3571 };
3572 }
3573
3574 var doNotBind = !objectGroup;
3575 var remoteObject = injectedScript.wrapObjectForModule(value, objectGroup || "", doNotBind);
3576 var description = remoteObject.description || ("" + value);
3577
3578 var result = {
3579 description: description,
3580 type: /** @type {!CanvasAgent.CallArgumentType} */ (remoteObject.typ e)
3581 };
3582 if (remoteObject.subtype)
3583 result.subtype = /** @type {!CanvasAgent.CallArgumentSubtype} */ (re moteObject.subtype);
3584 if (remoteObject.objectId && !doNotBind)
3585 result.remoteObject = remoteObject;
3586 return result;
3587 },
3588
3589 /**
3590 * @param {string} name
3591 * @return {?string}
3592 */
3593 enumValueForName: function(name)
3594 {
3595 return null;
3596 },
3597
3598 /**
3599 * @param {number} value
3600 * @param {!Array.<string>=} options
3601 * @return {?string}
3602 */
3603 enumNameForValue: function(value, options)
3604 {
3605 return null;
3606 },
3607
3608 /**
3609 * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} descriptors
3610 * @param {string=} objectGroup
3611 * @return {!Array.<!CanvasAgent.ResourceStateDescriptor>}
3612 */
3613 formatResourceStateDescriptors: function(descriptors, objectGroup)
3614 {
3615 var result = [];
3616 for (var i = 0, n = descriptors.length; i < n; ++i) {
3617 var d = descriptors[i];
3618 var item;
3619 if (d.values)
3620 item = { name: d.name, values: this.formatResourceStateDescripto rs(d.values, objectGroup) };
3621 else {
3622 item = { name: d.name, value: this.formatValue(d.value, objectGr oup) };
3623 if (d.valueIsEnum && typeof d.value === "number") {
3624 var enumName = this.enumNameForValue(d.value);
3625 if (enumName)
3626 item.value.enumName = enumName;
3627 }
3628 }
3629 var enumValue = this.enumValueForName(d.name);
3630 if (enumValue)
3631 item.enumValueForName = enumValue;
3632 if (d.isArray)
3633 item.isArray = true;
3634 result.push(item);
3635 }
3636 return result;
3637 }
3638 }
3639
3640 /**
3641 * @const
3642 * @type {!Object.<string, !CallFormatter>}
3643 */
3644 CallFormatter._formatters = {};
3645
3646 /**
3647 * @param {string} resourceName
3648 * @param {!CallFormatter} callFormatter
3649 */
3650 CallFormatter.register = function(resourceName, callFormatter)
3651 {
3652 CallFormatter._formatters[resourceName] = callFormatter;
3653 }
3654
3655 /**
3656 * @param {!Resource|!ReplayableResource} resource
3657 * @return {!CallFormatter}
3658 */
3659 CallFormatter.forResource = function(resource)
3660 {
3661 var formatter = CallFormatter._formatters[resource.name()];
3662 if (!formatter) {
3663 var contextResource = resource.contextResource();
3664 formatter = (contextResource && CallFormatter._formatters[contextResourc e.name()]) || new CallFormatter();
3665 }
3666 return formatter;
3667 }
3668
3669 /**
3670 * @param {number} resourceId
3671 * @return {!CanvasAgent.ResourceId}
3672 */
3673 CallFormatter.makeStringResourceId = function(resourceId)
3674 {
3675 return "{\"injectedScriptId\":" + injectedScriptId + ",\"resourceId\":" + re sourceId + "}";
3676 }
3677
3678 /**
3679 * @constructor
3680 * @extends {CallFormatter}
3681 * @param {!Object.<string, boolean>} drawingMethodNames
3682 */
3683 function WebGLCallFormatter(drawingMethodNames)
3684 {
3685 CallFormatter.call(this, drawingMethodNames);
3686 }
3687
3688 /**
3689 * NOTE: The code below is generated from the IDL file by the script:
3690 * /devtools/scripts/check_injected_webgl_calls_info.py
3691 *
3692 * @type {!Array.<{aname: string, enum: (!Array.<number>|undefined), bitfield: ( !Array.<number>|undefined), returnType: string, hints: (!Array.<string>|undefine d)}>}
3693 */
3694 WebGLCallFormatter.EnumsInfo = [
3695 {"aname": "activeTexture", "enum": [0]},
3696 {"aname": "bindBuffer", "enum": [0]},
3697 {"aname": "bindFramebuffer", "enum": [0]},
3698 {"aname": "bindRenderbuffer", "enum": [0]},
3699 {"aname": "bindTexture", "enum": [0]},
3700 {"aname": "blendEquation", "enum": [0]},
3701 {"aname": "blendEquationSeparate", "enum": [0, 1]},
3702 {"aname": "blendFunc", "enum": [0, 1], "hints": ["ZERO", "ONE"]},
3703 {"aname": "blendFuncSeparate", "enum": [0, 1, 2, 3], "hints": ["ZERO", "ONE" ]},
3704 {"aname": "bufferData", "enum": [0, 2]},
3705 {"aname": "bufferSubData", "enum": [0]},
3706 {"aname": "checkFramebufferStatus", "enum": [0], "returnType": "enum"},
3707 {"aname": "clear", "bitfield": [0]},
3708 {"aname": "compressedTexImage2D", "enum": [0, 2]},
3709 {"aname": "compressedTexSubImage2D", "enum": [0, 6]},
3710 {"aname": "copyTexImage2D", "enum": [0, 2]},
3711 {"aname": "copyTexSubImage2D", "enum": [0]},
3712 {"aname": "createShader", "enum": [0]},
3713 {"aname": "cullFace", "enum": [0]},
3714 {"aname": "depthFunc", "enum": [0]},
3715 {"aname": "disable", "enum": [0]},
3716 {"aname": "drawArrays", "enum": [0], "hints": ["POINTS", "LINES"]},
3717 {"aname": "drawElements", "enum": [0, 2], "hints": ["POINTS", "LINES"]},
3718 {"aname": "enable", "enum": [0]},
3719 {"aname": "framebufferRenderbuffer", "enum": [0, 1, 2]},
3720 {"aname": "framebufferTexture2D", "enum": [0, 1, 2]},
3721 {"aname": "frontFace", "enum": [0]},
3722 {"aname": "generateMipmap", "enum": [0]},
3723 {"aname": "getBufferParameter", "enum": [0, 1]},
3724 {"aname": "getError", "hints": ["NO_ERROR"], "returnType": "enum"},
3725 {"aname": "getFramebufferAttachmentParameter", "enum": [0, 1, 2]},
3726 {"aname": "getParameter", "enum": [0]},
3727 {"aname": "getProgramParameter", "enum": [1]},
3728 {"aname": "getRenderbufferParameter", "enum": [0, 1]},
3729 {"aname": "getShaderParameter", "enum": [1]},
3730 {"aname": "getShaderPrecisionFormat", "enum": [0, 1]},
3731 {"aname": "getTexParameter", "enum": [0, 1], "returnType": "enum"},
3732 {"aname": "getVertexAttrib", "enum": [1]},
3733 {"aname": "getVertexAttribOffset", "enum": [1]},
3734 {"aname": "hint", "enum": [0, 1]},
3735 {"aname": "isEnabled", "enum": [0]},
3736 {"aname": "pixelStorei", "enum": [0]},
3737 {"aname": "readPixels", "enum": [4, 5]},
3738 {"aname": "renderbufferStorage", "enum": [0, 1]},
3739 {"aname": "stencilFunc", "enum": [0]},
3740 {"aname": "stencilFuncSeparate", "enum": [0, 1]},
3741 {"aname": "stencilMaskSeparate", "enum": [0]},
3742 {"aname": "stencilOp", "enum": [0, 1, 2], "hints": ["ZERO", "ONE"]},
3743 {"aname": "stencilOpSeparate", "enum": [0, 1, 2, 3], "hints": ["ZERO", "ONE" ]},
3744 {"aname": "texParameterf", "enum": [0, 1, 2]},
3745 {"aname": "texParameteri", "enum": [0, 1, 2]},
3746 {"aname": "texImage2D", "enum": [0, 2, 6, 7]},
3747 {"aname": "texImage2D", "enum": [0, 2, 3, 4]},
3748 {"aname": "texSubImage2D", "enum": [0, 6, 7]},
3749 {"aname": "texSubImage2D", "enum": [0, 4, 5]},
3750 {"aname": "vertexAttribPointer", "enum": [2]}
3751 ];
3752
3753 WebGLCallFormatter.prototype = {
3754 /**
3755 * @override
3756 * @param {!ReplayableCall} replayableCall
3757 * @param {string=} objectGroup
3758 * @return {!Object}
3759 */
3760 formatCall: function(replayableCall, objectGroup)
3761 {
3762 var result = CallFormatter.prototype.formatCall.call(this, replayableCal l, objectGroup);
3763 if (!result.functionName)
3764 return result;
3765 var enumsInfo = this._findEnumsInfo(replayableCall);
3766 if (!enumsInfo)
3767 return result;
3768 var enumArgsIndexes = enumsInfo["enum"] || [];
3769 for (var i = 0, n = enumArgsIndexes.length; i < n; ++i) {
3770 var index = enumArgsIndexes[i];
3771 var callArgument = result.arguments[index];
3772 this._formatEnumValue(callArgument, enumsInfo["hints"]);
3773 }
3774 var bitfieldArgsIndexes = enumsInfo["bitfield"] || [];
3775 for (var i = 0, n = bitfieldArgsIndexes.length; i < n; ++i) {
3776 var index = bitfieldArgsIndexes[i];
3777 var callArgument = result.arguments[index];
3778 this._formatEnumBitmaskValue(callArgument, enumsInfo["hints"]);
3779 }
3780 if (enumsInfo.returnType === "enum")
3781 this._formatEnumValue(result.result, enumsInfo["hints"]);
3782 else if (enumsInfo.returnType === "bitfield")
3783 this._formatEnumBitmaskValue(result.result, enumsInfo["hints"]);
3784 return result;
3785 },
3786
3787 /**
3788 * @override
3789 * @param {string} name
3790 * @return {?string}
3791 */
3792 enumValueForName: function(name)
3793 {
3794 this._initialize();
3795 if (name in this._enumNameToValue)
3796 return "" + this._enumNameToValue[name];
3797 return null;
3798 },
3799
3800 /**
3801 * @override
3802 * @param {number} value
3803 * @param {!Array.<string>=} options
3804 * @return {?string}
3805 */
3806 enumNameForValue: function(value, options)
3807 {
3808 this._initialize();
3809 options = options || [];
3810 for (var i = 0, n = options.length; i < n; ++i) {
3811 if (this._enumNameToValue[options[i]] === value)
3812 return options[i];
3813 }
3814 var names = this._enumValueToNames[value];
3815 if (!names || names.length !== 1)
3816 return null;
3817 return names[0];
3818 },
3819
3820 /**
3821 * @param {!ReplayableCall} replayableCall
3822 * @return {?Object}
3823 */
3824 _findEnumsInfo: function(replayableCall)
3825 {
3826 function findMaxArgumentIndex(enumsInfo)
3827 {
3828 var result = -1;
3829 var enumArgsIndexes = enumsInfo["enum"] || [];
3830 for (var i = 0, n = enumArgsIndexes.length; i < n; ++i)
3831 result = Math.max(result, enumArgsIndexes[i]);
3832 var bitfieldArgsIndexes = enumsInfo["bitfield"] || [];
3833 for (var i = 0, n = bitfieldArgsIndexes.length; i < n; ++i)
3834 result = Math.max(result, bitfieldArgsIndexes[i]);
3835 return result;
3836 }
3837
3838 var result = null;
3839 for (var i = 0, enumsInfo; enumsInfo = WebGLCallFormatter.EnumsInfo[i]; ++i) {
3840 if (enumsInfo["aname"] !== replayableCall.functionName())
3841 continue;
3842 var argsCount = replayableCall.args().length;
3843 var maxArgumentIndex = findMaxArgumentIndex(enumsInfo);
3844 if (maxArgumentIndex >= argsCount)
3845 continue;
3846 // To resolve ambiguity (see texImage2D, texSubImage2D) choose descr iption with max argument indexes.
3847 if (!result || findMaxArgumentIndex(result) < maxArgumentIndex)
3848 result = enumsInfo;
3849 }
3850 return result;
3851 },
3852
3853 /**
3854 * @param {?CanvasAgent.CallArgument|undefined} callArgument
3855 * @param {!Array.<string>=} options
3856 */
3857 _formatEnumValue: function(callArgument, options)
3858 {
3859 if (!callArgument || isNaN(callArgument.description))
3860 return;
3861 this._initialize();
3862 var value = +callArgument.description;
3863 var enumName = this.enumNameForValue(value, options);
3864 if (enumName)
3865 callArgument.enumName = enumName;
3866 },
3867
3868 /**
3869 * @param {?CanvasAgent.CallArgument|undefined} callArgument
3870 * @param {!Array.<string>=} options
3871 */
3872 _formatEnumBitmaskValue: function(callArgument, options)
3873 {
3874 if (!callArgument || isNaN(callArgument.description))
3875 return;
3876 this._initialize();
3877 var value = +callArgument.description;
3878 options = options || [];
3879 /** @type {!Array.<string>} */
3880 var result = [];
3881 for (var i = 0, n = options.length; i < n; ++i) {
3882 var bitValue = this._enumNameToValue[options[i]] || 0;
3883 if (value & bitValue) {
3884 result.push(options[i]);
3885 value &= ~bitValue;
3886 }
3887 }
3888 while (value) {
3889 var nextValue = value & (value - 1);
3890 var bitValue = value ^ nextValue;
3891 var names = this._enumValueToNames[bitValue];
3892 if (!names || names.length !== 1) {
3893 console.warn("Ambiguous WebGL enum names for value " + bitValue + ": " + names);
3894 return;
3895 }
3896 result.push(names[0]);
3897 value = nextValue;
3898 }
3899 result.sort();
3900 callArgument.enumName = result.join(" | ");
3901 },
3902
3903 _initialize: function()
3904 {
3905 if (this._enumNameToValue)
3906 return;
3907
3908 /** @type {!Object.<string, number>} */
3909 this._enumNameToValue = Object.create(null);
3910 /** @type {!Object.<number, !Array.<string>>} */
3911 this._enumValueToNames = Object.create(null);
3912
3913 /**
3914 * @param {?Object} obj
3915 * @this WebGLCallFormatter
3916 */
3917 function iterateWebGLEnums(obj)
3918 {
3919 if (!obj)
3920 return;
3921 for (var property in obj) {
3922 if (TypeUtils.isEnumPropertyName(property, obj)) {
3923 var value = /** @type {number} */ (obj[property]);
3924 this._enumNameToValue[property] = value;
3925 var names = this._enumValueToNames[value];
3926 if (names) {
3927 if (names.indexOf(property) === -1)
3928 names.push(property);
3929 } else
3930 this._enumValueToNames[value] = [property];
3931 }
3932 }
3933 }
3934
3935 /**
3936 * @param {!Array.<string>} values
3937 * @return {string}
3938 */
3939 function commonSubstring(values)
3940 {
3941 var length = values.length;
3942 for (var i = 0; i < length; ++i) {
3943 for (var j = 0; j < length; ++j) {
3944 if (values[j].indexOf(values[i]) === -1)
3945 break;
3946 }
3947 if (j === length)
3948 return values[i];
3949 }
3950 return "";
3951 }
3952
3953 var gl = this._createUninstrumentedWebGLRenderingContext();
3954 iterateWebGLEnums.call(this, gl);
3955
3956 var extensions = gl.getSupportedExtensions() || [];
3957 for (var i = 0, n = extensions.length; i < n; ++i)
3958 iterateWebGLEnums.call(this, gl.getExtension(extensions[i]));
3959
3960 // Sort to get rid of ambiguity.
3961 for (var value in this._enumValueToNames) {
3962 var numericValue = Number(value);
3963 var names = this._enumValueToNames[numericValue];
3964 if (names.length > 1) {
3965 // Choose one enum name if possible. For example:
3966 // [BLEND_EQUATION, BLEND_EQUATION_RGB] => BLEND_EQUATION
3967 // [COLOR_ATTACHMENT0, COLOR_ATTACHMENT0_WEBGL] => COLOR_ATTAC HMENT0
3968 var common = commonSubstring(names);
3969 if (common)
3970 this._enumValueToNames[numericValue] = [common];
3971 else
3972 this._enumValueToNames[numericValue] = names.sort();
3973 }
3974 }
3975 },
3976
3977 /**
3978 * @return {?WebGLRenderingContext}
3979 */
3980 _createUninstrumentedWebGLRenderingContext: function()
3981 {
3982 var canvas = /** @type {!HTMLCanvasElement} */ (inspectedWindow.document .createElement("canvas"));
3983 var contextIds = ["experimental-webgl", "webkit-3d", "3d"];
3984 for (var i = 0, contextId; contextId = contextIds[i]; ++i) {
3985 var context = canvas.getContext(contextId);
3986 if (context)
3987 return /** @type {!WebGLRenderingContext} */ (Resource.wrappedOb ject(context));
3988 }
3989 return null;
3990 },
3991
3992 __proto__: CallFormatter.prototype
3993 }
3994
3995 CallFormatter.register("CanvasRenderingContext2D", new CallFormatter(CanvasRende ringContext2DResource.DrawingMethods));
3996 CallFormatter.register("WebGLRenderingContext", new WebGLCallFormatter(WebGLRend eringContextResource.DrawingMethods));
3997
3998 /**
3999 * @constructor
4000 */
4001 function TraceLog()
4002 {
4003 /** @type {!Array.<!ReplayableCall>} */
4004 this._replayableCalls = [];
4005 /** @type {!Cache.<!ReplayableResource>} */
4006 this._replayablesCache = new Cache();
4007 /** @type {!Object.<number, boolean>} */
4008 this._frameEndCallIndexes = {};
4009 /** @type {!Object.<number, boolean>} */
4010 this._resourcesCreatedInThisTraceLog = {};
4011 }
4012
4013 TraceLog.prototype = {
4014 /**
4015 * @return {number}
4016 */
4017 size: function()
4018 {
4019 return this._replayableCalls.length;
4020 },
4021
4022 /**
4023 * @return {!Array.<!ReplayableCall>}
4024 */
4025 replayableCalls: function()
4026 {
4027 return this._replayableCalls;
4028 },
4029
4030 /**
4031 * @param {number} id
4032 * @return {!ReplayableResource|undefined}
4033 */
4034 replayableResource: function(id)
4035 {
4036 return this._replayablesCache.get(id);
4037 },
4038
4039 /**
4040 * @param {number} resourceId
4041 * @return {boolean}
4042 */
4043 createdInThisTraceLog: function(resourceId)
4044 {
4045 return !!this._resourcesCreatedInThisTraceLog[resourceId];
4046 },
4047
4048 /**
4049 * @param {!Resource} resource
4050 */
4051 captureResource: function(resource)
4052 {
4053 resource.toReplayable(this._replayablesCache);
4054 },
4055
4056 /**
4057 * @param {!Call} call
4058 */
4059 addCall: function(call)
4060 {
4061 var resource = Resource.forObject(call.result());
4062 if (resource && !this._replayablesCache.has(resource.id()))
4063 this._resourcesCreatedInThisTraceLog[resource.id()] = true;
4064 this._replayableCalls.push(call.toReplayable(this._replayablesCache));
4065 },
4066
4067 addFrameEndMark: function()
4068 {
4069 var index = this._replayableCalls.length - 1;
4070 if (index >= 0)
4071 this._frameEndCallIndexes[index] = true;
4072 },
4073
4074 /**
4075 * @param {number} index
4076 * @return {boolean}
4077 */
4078 isFrameEndCallAt: function(index)
4079 {
4080 return !!this._frameEndCallIndexes[index];
4081 }
4082 }
4083
4084 /**
4085 * @constructor
4086 * @param {!TraceLog} traceLog
4087 */
4088 function TraceLogPlayer(traceLog)
4089 {
4090 /** @type {!TraceLog} */
4091 this._traceLog = traceLog;
4092 /** @type {number} */
4093 this._nextReplayStep = 0;
4094 /** @type {!Cache.<!Resource>} */
4095 this._replayWorldCache = new Cache();
4096 }
4097
4098 TraceLogPlayer.prototype = {
4099 /**
4100 * @return {!TraceLog}
4101 */
4102 traceLog: function()
4103 {
4104 return this._traceLog;
4105 },
4106
4107 /**
4108 * @param {number} id
4109 * @return {!Resource|undefined}
4110 */
4111 replayWorldResource: function(id)
4112 {
4113 return this._replayWorldCache.get(id);
4114 },
4115
4116 /**
4117 * @return {number}
4118 */
4119 nextReplayStep: function()
4120 {
4121 return this._nextReplayStep;
4122 },
4123
4124 reset: function()
4125 {
4126 this._nextReplayStep = 0;
4127 this._replayWorldCache.reset();
4128 },
4129
4130 /**
4131 * @param {number} stepNum
4132 * @return {{replayTime:number, lastCall:(!Call)}}
4133 */
4134 stepTo: function(stepNum)
4135 {
4136 stepNum = Math.min(stepNum, this._traceLog.size() - 1);
4137 console.assert(stepNum >= 0);
4138 if (this._nextReplayStep > stepNum)
4139 this.reset();
4140
4141 // Replay the calls' arguments first to warm-up, before measuring the ac tual replay time.
4142 this._replayCallArguments(stepNum);
4143
4144 var replayableCalls = this._traceLog.replayableCalls();
4145 var replayedCalls = [];
4146 replayedCalls.length = stepNum - this._nextReplayStep + 1;
4147
4148 var beforeTime = TypeUtils.now();
4149 for (var i = 0; this._nextReplayStep <= stepNum; ++this._nextReplayStep, ++i)
4150 replayedCalls[i] = replayableCalls[this._nextReplayStep].replay(this ._replayWorldCache);
4151 var replayTime = Math.max(0, TypeUtils.now() - beforeTime);
4152
4153 for (var i = 0, call; call = replayedCalls[i]; ++i)
4154 call.resource().onCallReplayed(call);
4155
4156 return {
4157 replayTime: replayTime,
4158 lastCall: replayedCalls[replayedCalls.length - 1]
4159 };
4160 },
4161
4162 /**
4163 * @param {number} stepNum
4164 */
4165 _replayCallArguments: function(stepNum)
4166 {
4167 /**
4168 * @param {*} obj
4169 * @this {TraceLogPlayer}
4170 */
4171 function replayIfNotCreatedInThisTraceLog(obj)
4172 {
4173 if (!(obj instanceof ReplayableResource))
4174 return;
4175 var replayableResource = /** @type {!ReplayableResource} */ (obj);
4176 if (!this._traceLog.createdInThisTraceLog(replayableResource.id()))
4177 replayableResource.replay(this._replayWorldCache)
4178 }
4179 var replayableCalls = this._traceLog.replayableCalls();
4180 for (var i = this._nextReplayStep; i <= stepNum; ++i) {
4181 replayIfNotCreatedInThisTraceLog.call(this, replayableCalls[i].repla yableResource());
4182 replayIfNotCreatedInThisTraceLog.call(this, replayableCalls[i].resul t());
4183 replayableCalls[i].args().forEach(replayIfNotCreatedInThisTraceLog.b ind(this));
4184 }
4185 }
4186 }
4187
4188 /**
4189 * @constructor
4190 */
4191 function ResourceTrackingManager()
4192 {
4193 this._capturing = false;
4194 this._stopCapturingOnFrameEnd = false;
4195 this._lastTraceLog = null;
4196 }
4197
4198 ResourceTrackingManager.prototype = {
4199 /**
4200 * @return {boolean}
4201 */
4202 capturing: function()
4203 {
4204 return this._capturing;
4205 },
4206
4207 /**
4208 * @return {?TraceLog}
4209 */
4210 lastTraceLog: function()
4211 {
4212 return this._lastTraceLog;
4213 },
4214
4215 /**
4216 * @param {!Resource} resource
4217 */
4218 registerResource: function(resource)
4219 {
4220 resource.setManager(this);
4221 },
4222
4223 startCapturing: function()
4224 {
4225 if (!this._capturing)
4226 this._lastTraceLog = new TraceLog();
4227 this._capturing = true;
4228 this._stopCapturingOnFrameEnd = false;
4229 },
4230
4231 /**
4232 * @param {!TraceLog=} traceLog
4233 */
4234 stopCapturing: function(traceLog)
4235 {
4236 if (traceLog && this._lastTraceLog !== traceLog)
4237 return;
4238 this._capturing = false;
4239 this._stopCapturingOnFrameEnd = false;
4240 if (this._lastTraceLog)
4241 this._lastTraceLog.addFrameEndMark();
4242 },
4243
4244 /**
4245 * @param {!TraceLog} traceLog
4246 */
4247 dropTraceLog: function(traceLog)
4248 {
4249 this.stopCapturing(traceLog);
4250 if (this._lastTraceLog === traceLog)
4251 this._lastTraceLog = null;
4252 },
4253
4254 captureFrame: function()
4255 {
4256 this._lastTraceLog = new TraceLog();
4257 this._capturing = true;
4258 this._stopCapturingOnFrameEnd = true;
4259 },
4260
4261 /**
4262 * @param {!Resource} resource
4263 * @param {!Array|!Arguments} args
4264 */
4265 captureArguments: function(resource, args)
4266 {
4267 if (!this._capturing)
4268 return;
4269 this._lastTraceLog.captureResource(resource);
4270 for (var i = 0, n = args.length; i < n; ++i) {
4271 var res = Resource.forObject(args[i]);
4272 if (res)
4273 this._lastTraceLog.captureResource(res);
4274 }
4275 },
4276
4277 /**
4278 * @param {!Call} call
4279 */
4280 captureCall: function(call)
4281 {
4282 if (!this._capturing)
4283 return;
4284 this._lastTraceLog.addCall(call);
4285 },
4286
4287 markFrameEnd: function()
4288 {
4289 if (!this._lastTraceLog)
4290 return;
4291 this._lastTraceLog.addFrameEndMark();
4292 if (this._stopCapturingOnFrameEnd && this._lastTraceLog.size())
4293 this.stopCapturing(this._lastTraceLog);
4294 }
4295 }
4296
4297 /**
4298 * @constructor
4299 */
4300 var InjectedCanvasModule = function()
4301 {
4302 /** @type {!ResourceTrackingManager} */
4303 this._manager = new ResourceTrackingManager();
4304 /** @type {number} */
4305 this._lastTraceLogId = 0;
4306 /** @type {!Object.<string, !TraceLog>} */
4307 this._traceLogs = {};
4308 /** @type {!Object.<string, !TraceLogPlayer>} */
4309 this._traceLogPlayers = {};
4310 }
4311
4312 InjectedCanvasModule.prototype = {
4313 /**
4314 * @param {!WebGLRenderingContext} glContext
4315 * @return {!Object}
4316 */
4317 wrapWebGLContext: function(glContext)
4318 {
4319 var resource = Resource.forObject(glContext) || new WebGLRenderingContex tResource(glContext);
4320 this._manager.registerResource(resource);
4321 return resource.proxyObject();
4322 },
4323
4324 /**
4325 * @param {!CanvasRenderingContext2D} context
4326 * @return {!Object}
4327 */
4328 wrapCanvas2DContext: function(context)
4329 {
4330 var resource = Resource.forObject(context) || new CanvasRenderingContext 2DResource(context);
4331 this._manager.registerResource(resource);
4332 return resource.proxyObject();
4333 },
4334
4335 /**
4336 * @return {!CanvasAgent.TraceLogId}
4337 */
4338 captureFrame: function()
4339 {
4340 return this._callStartCapturingFunction(this._manager.captureFrame);
4341 },
4342
4343 /**
4344 * @return {!CanvasAgent.TraceLogId}
4345 */
4346 startCapturing: function()
4347 {
4348 return this._callStartCapturingFunction(this._manager.startCapturing);
4349 },
4350
4351 markFrameEnd: function()
4352 {
4353 this._manager.markFrameEnd();
4354 },
4355
4356 /**
4357 * @param {function(this:ResourceTrackingManager)} func
4358 * @return {!CanvasAgent.TraceLogId}
4359 */
4360 _callStartCapturingFunction: function(func)
4361 {
4362 var oldTraceLog = this._manager.lastTraceLog();
4363 func.call(this._manager);
4364 var traceLog = /** @type {!TraceLog} */ (this._manager.lastTraceLog());
4365 if (traceLog === oldTraceLog) {
4366 for (var id in this._traceLogs) {
4367 if (this._traceLogs[id] === traceLog)
4368 return id;
4369 }
4370 }
4371 var id = this._makeTraceLogId();
4372 this._traceLogs[id] = traceLog;
4373 return id;
4374 },
4375
4376 /**
4377 * @param {!CanvasAgent.TraceLogId} id
4378 */
4379 stopCapturing: function(id)
4380 {
4381 var traceLog = this._traceLogs[id];
4382 if (traceLog)
4383 this._manager.stopCapturing(traceLog);
4384 },
4385
4386 /**
4387 * @param {!CanvasAgent.TraceLogId} id
4388 */
4389 dropTraceLog: function(id)
4390 {
4391 var traceLog = this._traceLogs[id];
4392 if (traceLog)
4393 this._manager.dropTraceLog(traceLog);
4394 delete this._traceLogs[id];
4395 delete this._traceLogPlayers[id];
4396 },
4397
4398 /**
4399 * @param {!CanvasAgent.TraceLogId} id
4400 * @param {number=} startOffset
4401 * @param {number=} maxLength
4402 * @return {!CanvasAgent.TraceLog|string}
4403 */
4404 traceLog: function(id, startOffset, maxLength)
4405 {
4406 var traceLog = this._traceLogs[id];
4407 if (!traceLog)
4408 return "Error: Trace log with the given ID not found.";
4409
4410 // Ensure last call ends a frame.
4411 traceLog.addFrameEndMark();
4412
4413 var replayableCalls = traceLog.replayableCalls();
4414 if (typeof startOffset !== "number")
4415 startOffset = 0;
4416 if (typeof maxLength !== "number")
4417 maxLength = replayableCalls.length;
4418
4419 var fromIndex = Math.max(0, startOffset);
4420 var toIndex = Math.min(replayableCalls.length - 1, fromIndex + maxLength - 1);
4421
4422 var alive = this._manager.capturing() && this._manager.lastTraceLog() == = traceLog;
4423 var result = {
4424 id: id,
4425 /** @type {!Array.<!CanvasAgent.Call>} */
4426 calls: [],
4427 /** @type {!Array.<!CanvasAgent.CallArgument>} */
4428 contexts: [],
4429 alive: alive,
4430 startOffset: fromIndex,
4431 totalAvailableCalls: replayableCalls.length
4432 };
4433 /** @type {!Object.<string, boolean>} */
4434 var contextIds = {};
4435 for (var i = fromIndex; i <= toIndex; ++i) {
4436 var call = replayableCalls[i];
4437 var resource = call.replayableResource();
4438 var contextResource = resource.contextResource();
4439 var stackTrace = call.stackTrace();
4440 var callFrame = stackTrace ? stackTrace.callFrame(0) || {} : {};
4441 var item = CallFormatter.forResource(resource).formatCall(call);
4442 item.contextId = CallFormatter.makeStringResourceId(contextResource. id());
4443 item.sourceURL = callFrame.sourceURL;
4444 item.lineNumber = callFrame.lineNumber;
4445 item.columnNumber = callFrame.columnNumber;
4446 item.isFrameEndCall = traceLog.isFrameEndCallAt(i);
4447 result.calls.push(item);
4448 if (!contextIds[item.contextId]) {
4449 contextIds[item.contextId] = true;
4450 result.contexts.push(CallFormatter.forResource(resource).formatV alue(contextResource));
4451 }
4452 }
4453 return result;
4454 },
4455
4456 /**
4457 * @param {!CanvasAgent.TraceLogId} traceLogId
4458 * @param {number} stepNo
4459 * @return {{resourceState: !CanvasAgent.ResourceState, replayTime: number}| string}
4460 */
4461 replayTraceLog: function(traceLogId, stepNo)
4462 {
4463 var traceLog = this._traceLogs[traceLogId];
4464 if (!traceLog)
4465 return "Error: Trace log with the given ID not found.";
4466 this._traceLogPlayers[traceLogId] = this._traceLogPlayers[traceLogId] || new TraceLogPlayer(traceLog);
4467
4468 var replayResult = this._traceLogPlayers[traceLogId].stepTo(stepNo);
4469 var resource = replayResult.lastCall.resource();
4470 var dataURL = resource.toDataURL();
4471 if (!dataURL) {
4472 resource = resource.contextResource();
4473 dataURL = resource.toDataURL();
4474 }
4475 return {
4476 resourceState: this._makeResourceState(resource.id(), traceLogId, re source, dataURL),
4477 replayTime: replayResult.replayTime
4478 };
4479 },
4480
4481 /**
4482 * @param {!CanvasAgent.TraceLogId} traceLogId
4483 * @param {!CanvasAgent.ResourceId} stringResourceId
4484 * @return {!CanvasAgent.ResourceState|string}
4485 */
4486 resourceState: function(traceLogId, stringResourceId)
4487 {
4488 var traceLog = this._traceLogs[traceLogId];
4489 if (!traceLog)
4490 return "Error: Trace log with the given ID not found.";
4491
4492 var parsedStringId1 = this._parseStringId(traceLogId);
4493 var parsedStringId2 = this._parseStringId(stringResourceId);
4494 if (parsedStringId1.injectedScriptId !== parsedStringId2.injectedScriptI d)
4495 return "Error: Both IDs must point to the same injected script.";
4496
4497 var resourceId = parsedStringId2.resourceId;
4498 if (!resourceId)
4499 return "Error: Wrong resource ID: " + stringResourceId;
4500
4501 var traceLogPlayer = this._traceLogPlayers[traceLogId];
4502 var resource = traceLogPlayer && traceLogPlayer.replayWorldResource(reso urceId);
4503 return this._makeResourceState(resourceId, traceLogId, resource);
4504 },
4505
4506 /**
4507 * @param {!CanvasAgent.TraceLogId} traceLogId
4508 * @param {number} callIndex
4509 * @param {number} argumentIndex
4510 * @param {string} objectGroup
4511 * @return {{result:(!RuntimeAgent.RemoteObject|undefined), resourceState:(! CanvasAgent.ResourceState|undefined)}|string}
4512 */
4513 evaluateTraceLogCallArgument: function(traceLogId, callIndex, argumentIndex, objectGroup)
4514 {
4515 var traceLog = this._traceLogs[traceLogId];
4516 if (!traceLog)
4517 return "Error: Trace log with the given ID not found.";
4518
4519 var replayableCall = traceLog.replayableCalls()[callIndex];
4520 if (!replayableCall)
4521 return "Error: No call found at index " + callIndex;
4522
4523 var value;
4524 if (replayableCall.isPropertySetter())
4525 value = replayableCall.propertyValue();
4526 else if (argumentIndex === -1)
4527 value = replayableCall.result();
4528 else {
4529 var args = replayableCall.args();
4530 if (argumentIndex < 0 || argumentIndex >= args.length)
4531 return "Error: No argument found at index " + argumentIndex + " for call at index " + callIndex;
4532 value = args[argumentIndex];
4533 }
4534
4535 if (value instanceof ReplayableResource) {
4536 var traceLogPlayer = this._traceLogPlayers[traceLogId];
4537 var resource = traceLogPlayer && traceLogPlayer.replayWorldResource( value.id());
4538 var resourceState = this._makeResourceState(value.id(), traceLogId, resource);
4539 return { resourceState: resourceState };
4540 }
4541
4542 var remoteObject = injectedScript.wrapObjectForModule(value, objectGroup );
4543 return { result: remoteObject };
4544 },
4545
4546 /**
4547 * @return {!CanvasAgent.TraceLogId}
4548 */
4549 _makeTraceLogId: function()
4550 {
4551 return "{\"injectedScriptId\":" + injectedScriptId + ",\"traceLogId\":" + (++this._lastTraceLogId) + "}";
4552 },
4553
4554 /**
4555 * @param {number} resourceId
4556 * @param {!CanvasAgent.TraceLogId} traceLogId
4557 * @param {?Resource=} resource
4558 * @param {string=} overrideImageURL
4559 * @return {!CanvasAgent.ResourceState}
4560 */
4561 _makeResourceState: function(resourceId, traceLogId, resource, overrideImage URL)
4562 {
4563 var result = {
4564 id: CallFormatter.makeStringResourceId(resourceId),
4565 traceLogId: traceLogId
4566 };
4567 if (resource) {
4568 result.imageURL = overrideImageURL || resource.toDataURL();
4569 result.descriptors = CallFormatter.forResource(resource).formatResou rceStateDescriptors(resource.currentState(), traceLogId);
4570 }
4571 return result;
4572 },
4573
4574 /**
4575 * @param {string} stringId
4576 * @return {{injectedScriptId: number, traceLogId: ?number, resourceId: ?num ber}}
4577 */
4578 _parseStringId: function(stringId)
4579 {
4580 return /** @type {?} */ (InjectedScriptHost.eval("(" + stringId + ")"));
4581 }
4582 }
4583
4584 var injectedCanvasModule = new InjectedCanvasModule();
4585 return injectedCanvasModule;
4586
4587 })
OLDNEW
« no previous file with comments | « Source/core/inspector/InjectedScriptCanvasModule.cpp ('k') | Source/core/inspector/InjectedScriptModule.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698