Index: Source/core/inspector/InjectedScriptCanvasModuleSource.js |
diff --git a/Source/core/inspector/InjectedScriptCanvasModuleSource.js b/Source/core/inspector/InjectedScriptCanvasModuleSource.js |
deleted file mode 100644 |
index eb1d1ec99fb70c81d976494f72e15a782141a5cc..0000000000000000000000000000000000000000 |
--- a/Source/core/inspector/InjectedScriptCanvasModuleSource.js |
+++ /dev/null |
@@ -1,4587 +0,0 @@ |
-/* |
- * Copyright (C) 2013 Google Inc. All rights reserved. |
- * |
- * Redistribution and use in source and binary forms, with or without |
- * modification, are permitted provided that the following conditions are |
- * met: |
- * |
- * * Redistributions of source code must retain the above copyright |
- * notice, this list of conditions and the following disclaimer. |
- * * Redistributions in binary form must reproduce the above |
- * copyright notice, this list of conditions and the following disclaimer |
- * in the documentation and/or other materials provided with the |
- * distribution. |
- * * Neither the name of Google Inc. nor the names of its |
- * contributors may be used to endorse or promote products derived from |
- * this software without specific prior written permission. |
- * |
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
- */ |
- |
-/** |
- * FIXME: ES5 strict mode check is suppressed due to multiple uses of arguments.callee. |
- * @fileoverview |
- * @suppress {es5Strict} |
- */ |
- |
-/** |
- * @param {InjectedScriptHostClass} InjectedScriptHost |
- * @param {Window} inspectedWindow |
- * @param {number} injectedScriptId |
- * @param {!InjectedScript} injectedScript |
- */ |
-(function (InjectedScriptHost, inspectedWindow, injectedScriptId, injectedScript) { |
- |
-var TypeUtils = { |
- /** |
- * http://www.khronos.org/registry/typedarray/specs/latest/#7 |
- * @const |
- * @type {!Array.<function(new:ArrayBufferView, (!ArrayBuffer|!ArrayBufferView), number=, number=)>} |
- */ |
- _typedArrayClasses: (function(typeNames) { |
- var result = []; |
- for (var i = 0, n = typeNames.length; i < n; ++i) { |
- if (inspectedWindow[typeNames[i]]) |
- result.push(inspectedWindow[typeNames[i]]); |
- } |
- return result; |
- })(["Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Uint16Array", "Int32Array", "Uint32Array", "Float32Array", "Float64Array"]), |
- |
- /** |
- * @const |
- * @type {!Array.<string>} |
- */ |
- _supportedPropertyPrefixes: ["webkit"], |
- |
- /** |
- * @param {*} array |
- * @return {function(new:ArrayBufferView, (!ArrayBuffer|!ArrayBufferView), number=, number=)|null} |
- */ |
- typedArrayClass: function(array) |
- { |
- var classes = TypeUtils._typedArrayClasses; |
- for (var i = 0, n = classes.length; i < n; ++i) { |
- if (array instanceof classes[i]) |
- return classes[i]; |
- } |
- return null; |
- }, |
- |
- /** |
- * @param {*} obj |
- * @return {*} |
- */ |
- clone: function(obj) |
- { |
- if (!obj) |
- return obj; |
- |
- var type = typeof obj; |
- if (type !== "object" && type !== "function") |
- return obj; |
- |
- // Handle Array and ArrayBuffer instances. |
- if (typeof obj.slice === "function") { |
- console.assert(obj instanceof Array || obj instanceof ArrayBuffer); |
- return obj.slice(0); |
- } |
- |
- var typedArrayClass = TypeUtils.typedArrayClass(obj); |
- if (typedArrayClass) |
- return new typedArrayClass(/** @type {!ArrayBufferView} */ (obj)); |
- |
- if (obj instanceof HTMLImageElement) { |
- var img = /** @type {!HTMLImageElement} */ (obj); |
- // Special case for Images with Blob URIs: cloneNode will fail if the Blob URI has already been revoked. |
- // FIXME: Maybe this is a bug in WebKit core? |
- if (/^blob:/.test(img.src)) |
- return TypeUtils.cloneIntoCanvas(img); |
- return img.cloneNode(true); |
- } |
- |
- if (obj instanceof HTMLCanvasElement) |
- return TypeUtils.cloneIntoCanvas(obj); |
- |
- if (obj instanceof HTMLVideoElement) |
- return TypeUtils.cloneIntoCanvas(obj, obj.videoWidth, obj.videoHeight); |
- |
- if (obj instanceof ImageData) { |
- var context = TypeUtils._dummyCanvas2dContext(); |
- // FIXME: suppress type checks due to outdated builtin externs for createImageData. |
- var result = (/** @type {?} */ (context)).createImageData(obj); |
- for (var i = 0, n = obj.data.length; i < n; ++i) |
- result.data[i] = obj.data[i]; |
- return result; |
- } |
- |
- // Try to convert to a primitive value via valueOf(). |
- if (typeof obj.valueOf === "function") { |
- var value = obj.valueOf(); |
- var valueType = typeof value; |
- if (valueType !== "object" && valueType !== "function") |
- return value; |
- } |
- |
- console.error("ASSERT_NOT_REACHED: failed to clone object: ", obj); |
- return obj; |
- }, |
- |
- /** |
- * @param {!HTMLImageElement|!HTMLCanvasElement|!HTMLVideoElement} obj |
- * @param {number=} width |
- * @param {number=} height |
- * @return {!HTMLCanvasElement} |
- */ |
- cloneIntoCanvas: function(obj, width, height) |
- { |
- var canvas = /** @type {!HTMLCanvasElement} */ (inspectedWindow.document.createElement("canvas")); |
- canvas.width = width || +obj.width; |
- canvas.height = height || +obj.height; |
- var context = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d"))); |
- context.drawImage(obj, 0, 0); |
- return canvas; |
- }, |
- |
- /** |
- * @param {?Object=} obj |
- * @return {?Object} |
- */ |
- cloneObject: function(obj) |
- { |
- if (!obj) |
- return null; |
- var result = {}; |
- for (var key in obj) |
- result[key] = obj[key]; |
- return result; |
- }, |
- |
- /** |
- * @param {!Array.<string>} names |
- * @return {!Object.<string, boolean>} |
- */ |
- createPrefixedPropertyNamesSet: function(names) |
- { |
- var result = Object.create(null); |
- for (var i = 0, name; name = names[i]; ++i) { |
- result[name] = true; |
- var suffix = name.substr(0, 1).toUpperCase() + name.substr(1); |
- for (var j = 0, prefix; prefix = TypeUtils._supportedPropertyPrefixes[j]; ++j) |
- result[prefix + suffix] = true; |
- } |
- return result; |
- }, |
- |
- /** |
- * @return {number} |
- */ |
- now: function() |
- { |
- try { |
- return inspectedWindow.performance.now(); |
- } catch(e) { |
- try { |
- return Date.now(); |
- } catch(ex) { |
- } |
- } |
- return 0; |
- }, |
- |
- /** |
- * @param {string} property |
- * @param {!Object} obj |
- * @return {boolean} |
- */ |
- isEnumPropertyName: function(property, obj) |
- { |
- return (/^[A-Z][A-Z0-9_]+$/.test(property) && typeof obj[property] === "number"); |
- }, |
- |
- /** |
- * @return {!CanvasRenderingContext2D} |
- */ |
- _dummyCanvas2dContext: function() |
- { |
- var context = TypeUtils._dummyCanvas2dContextInstance; |
- if (!context) { |
- var canvas = /** @type {!HTMLCanvasElement} */ (inspectedWindow.document.createElement("canvas")); |
- context = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d"))); |
- TypeUtils._dummyCanvas2dContextInstance = context; |
- } |
- return context; |
- } |
-} |
- |
-/** @typedef {{name:string, valueIsEnum:(boolean|undefined), value:*, values:(!Array.<!TypeUtils.InternalResourceStateDescriptor>|undefined), isArray:(boolean|undefined)}} */ |
-TypeUtils.InternalResourceStateDescriptor; |
- |
-/** |
- * @interface |
- */ |
-function StackTrace() |
-{ |
-} |
- |
-StackTrace.prototype = { |
- /** |
- * @param {number} index |
- * @return {{sourceURL: string, lineNumber: number, columnNumber: number}|undefined} |
- */ |
- callFrame: function(index) |
- { |
- } |
-} |
- |
-/** |
- * @param {number=} stackTraceLimit |
- * @param {?Function=} topMostFunctionToIgnore |
- * @return {?StackTrace} |
- */ |
-StackTrace.create = function(stackTraceLimit, topMostFunctionToIgnore) |
-{ |
- if (typeof Error.captureStackTrace === "function") |
- return new StackTraceV8(stackTraceLimit, topMostFunctionToIgnore || arguments.callee); |
- // FIXME: Support JSC, and maybe other browsers. |
- return null; |
-} |
- |
-/** |
- * @constructor |
- * @implements {StackTrace} |
- * @param {number=} stackTraceLimit |
- * @param {?Function=} topMostFunctionToIgnore |
- * @see http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi |
- */ |
-function StackTraceV8(stackTraceLimit, topMostFunctionToIgnore) |
-{ |
- var oldPrepareStackTrace = Error.prepareStackTrace; |
- var oldStackTraceLimit = Error.stackTraceLimit; |
- if (typeof stackTraceLimit === "number") |
- Error.stackTraceLimit = stackTraceLimit; |
- |
- /** |
- * @param {!Object} error |
- * @param {!Array.<!CallSite>} structuredStackTrace |
- * @return {!Array.<{sourceURL: string, lineNumber: number, columnNumber: number}>} |
- */ |
- Error.prepareStackTrace = function(error, structuredStackTrace) |
- { |
- return structuredStackTrace.map(function(callSite) { |
- return { |
- sourceURL: callSite.getFileName(), |
- lineNumber: callSite.getLineNumber(), |
- columnNumber: callSite.getColumnNumber() |
- }; |
- }); |
- } |
- |
- var holder = /** @type {{stack: !Array.<{sourceURL: string, lineNumber: number, columnNumber: number}>}} */ ({}); |
- Error.captureStackTrace(holder, topMostFunctionToIgnore || arguments.callee); |
- this._stackTrace = holder.stack; |
- |
- Error.stackTraceLimit = oldStackTraceLimit; |
- Error.prepareStackTrace = oldPrepareStackTrace; |
-} |
- |
-StackTraceV8.prototype = { |
- /** |
- * @override |
- * @param {number} index |
- * @return {{sourceURL: string, lineNumber: number, columnNumber: number}|undefined} |
- */ |
- callFrame: function(index) |
- { |
- return this._stackTrace[index]; |
- } |
-} |
- |
-/** |
- * @constructor |
- * @template T |
- */ |
-function Cache() |
-{ |
- this.reset(); |
-} |
- |
-Cache.prototype = { |
- /** |
- * @return {number} |
- */ |
- size: function() |
- { |
- return this._size; |
- }, |
- |
- reset: function() |
- { |
- /** @type {!Object.<number, !T>} */ |
- this._items = Object.create(null); |
- /** @type {number} */ |
- this._size = 0; |
- }, |
- |
- /** |
- * @param {number} key |
- * @return {boolean} |
- */ |
- has: function(key) |
- { |
- return key in this._items; |
- }, |
- |
- /** |
- * @param {number} key |
- * @return {T|undefined} |
- */ |
- get: function(key) |
- { |
- return this._items[key]; |
- }, |
- |
- /** |
- * @param {number} key |
- * @param {!T} item |
- */ |
- put: function(key, item) |
- { |
- if (!this.has(key)) |
- ++this._size; |
- this._items[key] = item; |
- } |
-} |
- |
-/** |
- * @constructor |
- * @param {?Resource|!Object} thisObject |
- * @param {string} functionName |
- * @param {!Array|!Arguments} args |
- * @param {!Resource|*=} result |
- * @param {?StackTrace=} stackTrace |
- */ |
-function Call(thisObject, functionName, args, result, stackTrace) |
-{ |
- this._thisObject = thisObject; |
- this._functionName = functionName; |
- this._args = Array.prototype.slice.call(args, 0); |
- this._result = result; |
- this._stackTrace = stackTrace || null; |
- |
- if (!this._functionName) |
- console.assert(this._args.length === 2 && typeof this._args[0] === "string"); |
-} |
- |
-Call.prototype = { |
- /** |
- * @return {?Resource} |
- */ |
- resource: function() |
- { |
- return Resource.forObject(this._thisObject); |
- }, |
- |
- /** |
- * @return {string} |
- */ |
- functionName: function() |
- { |
- return this._functionName; |
- }, |
- |
- /** |
- * @return {boolean} |
- */ |
- isPropertySetter: function() |
- { |
- return !this._functionName; |
- }, |
- |
- /** |
- * @return {!Array} |
- */ |
- args: function() |
- { |
- return this._args; |
- }, |
- |
- /** |
- * @return {*} |
- */ |
- result: function() |
- { |
- return this._result; |
- }, |
- |
- /** |
- * @return {?StackTrace} |
- */ |
- stackTrace: function() |
- { |
- return this._stackTrace; |
- }, |
- |
- /** |
- * @param {?StackTrace} stackTrace |
- */ |
- setStackTrace: function(stackTrace) |
- { |
- this._stackTrace = stackTrace; |
- }, |
- |
- /** |
- * @param {*} result |
- */ |
- setResult: function(result) |
- { |
- this._result = result; |
- }, |
- |
- /** |
- * @param {string} name |
- * @param {?Object} attachment |
- */ |
- setAttachment: function(name, attachment) |
- { |
- if (attachment) { |
- /** @type {?Object.<string, !Object>|undefined} */ |
- this._attachments = this._attachments || Object.create(null); |
- this._attachments[name] = attachment; |
- } else if (this._attachments) { |
- delete this._attachments[name]; |
- } |
- }, |
- |
- /** |
- * @param {string} name |
- * @return {?Object} |
- */ |
- attachment: function(name) |
- { |
- return this._attachments ? (this._attachments[name] || null) : null; |
- }, |
- |
- freeze: function() |
- { |
- if (this._freezed) |
- return; |
- this._freezed = true; |
- for (var i = 0, n = this._args.length; i < n; ++i) { |
- // FIXME: freeze the Resources also! |
- if (!Resource.forObject(this._args[i])) |
- this._args[i] = TypeUtils.clone(this._args[i]); |
- } |
- }, |
- |
- /** |
- * @param {!Cache.<!ReplayableResource>} cache |
- * @return {!ReplayableCall} |
- */ |
- toReplayable: function(cache) |
- { |
- this.freeze(); |
- var thisObject = /** @type {!ReplayableResource} */ (Resource.toReplayable(this._thisObject, cache)); |
- var result = Resource.toReplayable(this._result, cache); |
- var args = this._args.map(function(obj) { |
- return Resource.toReplayable(obj, cache); |
- }); |
- var attachments = TypeUtils.cloneObject(this._attachments); |
- return new ReplayableCall(thisObject, this._functionName, args, result, this._stackTrace, attachments); |
- }, |
- |
- /** |
- * @param {!ReplayableCall} replayableCall |
- * @param {!Cache.<!Resource>} cache |
- * @return {!Call} |
- */ |
- replay: function(replayableCall, cache) |
- { |
- var replayableResult = replayableCall.result(); |
- if (replayableResult instanceof ReplayableResource && !cache.has(replayableResult.id())) { |
- var resource = replayableResult.replay(cache); |
- console.assert(resource.calls().length > 0, "Expected create* call for the Resource"); |
- return resource.calls()[0]; |
- } |
- |
- var replayObject = ReplayableResource.replay(replayableCall.replayableResource(), cache); |
- var replayArgs = replayableCall.args().map(function(obj) { |
- return ReplayableResource.replay(obj, cache); |
- }); |
- var replayResult = undefined; |
- |
- if (replayableCall.isPropertySetter()) |
- replayObject[replayArgs[0]] = replayArgs[1]; |
- else { |
- var replayFunction = replayObject[replayableCall.functionName()]; |
- console.assert(typeof replayFunction === "function", "Expected a function to replay"); |
- replayResult = replayFunction.apply(replayObject, replayArgs); |
- |
- if (replayableResult instanceof ReplayableResource) { |
- var resource = replayableResult.replay(cache); |
- if (!resource.wrappedObject()) |
- resource.setWrappedObject(replayResult); |
- } |
- } |
- |
- this._thisObject = replayObject; |
- this._functionName = replayableCall.functionName(); |
- this._args = replayArgs; |
- this._result = replayResult; |
- this._stackTrace = replayableCall.stackTrace(); |
- this._freezed = true; |
- var attachments = replayableCall.attachments(); |
- this._attachments = attachments ? TypeUtils.cloneObject(attachments) : null; |
- return this; |
- } |
-} |
- |
-/** |
- * @constructor |
- * @param {!ReplayableResource} thisObject |
- * @param {string} functionName |
- * @param {!Array.<!ReplayableResource|*>} args |
- * @param {!ReplayableResource|*} result |
- * @param {?StackTrace} stackTrace |
- * @param {?Object.<string, !Object>} attachments |
- */ |
-function ReplayableCall(thisObject, functionName, args, result, stackTrace, attachments) |
-{ |
- this._thisObject = thisObject; |
- this._functionName = functionName; |
- this._args = args; |
- this._result = result; |
- this._stackTrace = stackTrace; |
- if (attachments) |
- this._attachments = attachments; |
-} |
- |
-ReplayableCall.prototype = { |
- /** |
- * @return {!ReplayableResource} |
- */ |
- replayableResource: function() |
- { |
- return this._thisObject; |
- }, |
- |
- /** |
- * @return {string} |
- */ |
- functionName: function() |
- { |
- return this._functionName; |
- }, |
- |
- /** |
- * @return {boolean} |
- */ |
- isPropertySetter: function() |
- { |
- return !this._functionName; |
- }, |
- |
- /** |
- * @return {string} |
- */ |
- propertyName: function() |
- { |
- console.assert(this.isPropertySetter()); |
- return /** @type {string} */ (this._args[0]); |
- }, |
- |
- /** |
- * @return {*} |
- */ |
- propertyValue: function() |
- { |
- console.assert(this.isPropertySetter()); |
- return this._args[1]; |
- }, |
- |
- /** |
- * @return {!Array.<!ReplayableResource|*>} |
- */ |
- args: function() |
- { |
- return this._args; |
- }, |
- |
- /** |
- * @return {!ReplayableResource|*} |
- */ |
- result: function() |
- { |
- return this._result; |
- }, |
- |
- /** |
- * @return {?StackTrace} |
- */ |
- stackTrace: function() |
- { |
- return this._stackTrace; |
- }, |
- |
- /** |
- * @return {?Object.<string, !Object>} |
- */ |
- attachments: function() |
- { |
- return this._attachments || null; |
- }, |
- |
- /** |
- * @param {string} name |
- * @return {!Object} |
- */ |
- attachment: function(name) |
- { |
- return this._attachments && this._attachments[name]; |
- }, |
- |
- /** |
- * @param {!Cache.<!Resource>} cache |
- * @return {!Call} |
- */ |
- replay: function(cache) |
- { |
- var call = /** @type {!Call} */ (Object.create(Call.prototype)); |
- return call.replay(this, cache); |
- } |
-} |
- |
-/** |
- * @constructor |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function Resource(wrappedObject, name) |
-{ |
- /** @type {number} */ |
- this._id = ++Resource._uniqueId; |
- /** @type {string} */ |
- this._name = name || "Resource"; |
- /** @type {number} */ |
- this._kindId = Resource._uniqueKindIds[this._name] = (Resource._uniqueKindIds[this._name] || 0) + 1; |
- /** @type {?ResourceTrackingManager} */ |
- this._resourceManager = null; |
- /** @type {!Array.<!Call>} */ |
- this._calls = []; |
- /** |
- * This is to prevent GC from collecting associated resources. |
- * Otherwise, for example in WebGL, subsequent calls to gl.getParameter() |
- * may return a recently created instance that is no longer bound to a |
- * Resource object (thus, no history to replay it later). |
- * |
- * @type {!Object.<string, !Resource>} |
- */ |
- this._boundResources = Object.create(null); |
- this.setWrappedObject(wrappedObject); |
-} |
- |
-/** |
- * @type {number} |
- */ |
-Resource._uniqueId = 0; |
- |
-/** |
- * @type {!Object.<string, number>} |
- */ |
-Resource._uniqueKindIds = {}; |
- |
-/** |
- * @param {*} obj |
- * @return {?Resource} |
- */ |
-Resource.forObject = function(obj) |
-{ |
- if (!obj) |
- return null; |
- if (obj instanceof Resource) |
- return obj; |
- if (typeof obj === "object") |
- return obj["__resourceObject"]; |
- return null; |
-} |
- |
-/** |
- * @param {!Resource|*} obj |
- * @return {*} |
- */ |
-Resource.wrappedObject = function(obj) |
-{ |
- var resource = Resource.forObject(obj); |
- return resource ? resource.wrappedObject() : obj; |
-} |
- |
-/** |
- * @param {!Resource|*} obj |
- * @param {!Cache.<!ReplayableResource>} cache |
- * @return {!ReplayableResource|*} |
- */ |
-Resource.toReplayable = function(obj, cache) |
-{ |
- var resource = Resource.forObject(obj); |
- return resource ? resource.toReplayable(cache) : obj; |
-} |
- |
-Resource.prototype = { |
- /** |
- * @return {number} |
- */ |
- id: function() |
- { |
- return this._id; |
- }, |
- |
- /** |
- * @return {string} |
- */ |
- name: function() |
- { |
- return this._name; |
- }, |
- |
- /** |
- * @return {string} |
- */ |
- description: function() |
- { |
- return this._name + "@" + this._kindId; |
- }, |
- |
- /** |
- * @return {!Object} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @param {!Object} value |
- */ |
- setWrappedObject: function(value) |
- { |
- console.assert(value, "wrappedObject should not be NULL"); |
- console.assert(!(value instanceof Resource), "Binding a Resource object to another Resource object?"); |
- this._wrappedObject = value; |
- this._bindObjectToResource(value); |
- }, |
- |
- /** |
- * @return {!Object} |
- */ |
- proxyObject: function() |
- { |
- if (!this._proxyObject) |
- this._proxyObject = this._wrapObject(); |
- return this._proxyObject; |
- }, |
- |
- /** |
- * @return {?ResourceTrackingManager} |
- */ |
- manager: function() |
- { |
- return this._resourceManager; |
- }, |
- |
- /** |
- * @param {!ResourceTrackingManager} value |
- */ |
- setManager: function(value) |
- { |
- this._resourceManager = value; |
- }, |
- |
- /** |
- * @return {!Array.<!Call>} |
- */ |
- calls: function() |
- { |
- return this._calls; |
- }, |
- |
- /** |
- * @return {?ContextResource} |
- */ |
- contextResource: function() |
- { |
- if (this instanceof ContextResource) |
- return /** @type {!ContextResource} */ (this); |
- |
- if (this._calculatingContextResource) |
- return null; |
- |
- this._calculatingContextResource = true; |
- var result = null; |
- for (var i = 0, n = this._calls.length; i < n; ++i) { |
- result = this._calls[i].resource().contextResource(); |
- if (result) |
- break; |
- } |
- delete this._calculatingContextResource; |
- console.assert(result, "Failed to find context resource for " + this._name + "@" + this._kindId); |
- return result; |
- }, |
- |
- /** |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- var result = []; |
- var proxyObject = this.proxyObject(); |
- if (!proxyObject) |
- return result; |
- var statePropertyNames = this._proxyStatePropertyNames || []; |
- for (var i = 0, n = statePropertyNames.length; i < n; ++i) { |
- var pname = statePropertyNames[i]; |
- result.push({ name: pname, value: proxyObject[pname] }); |
- } |
- result.push({ name: "context", value: this.contextResource() }); |
- return result; |
- }, |
- |
- /** |
- * @return {string} |
- */ |
- toDataURL: function() |
- { |
- return ""; |
- }, |
- |
- /** |
- * @param {!Cache.<!ReplayableResource>} cache |
- * @return {!ReplayableResource} |
- */ |
- toReplayable: function(cache) |
- { |
- var result = cache.get(this._id); |
- if (result) |
- return result; |
- var data = { |
- id: this._id, |
- name: this._name, |
- kindId: this._kindId |
- }; |
- result = new ReplayableResource(this, data); |
- cache.put(this._id, result); // Put into the cache early to avoid loops. |
- data.calls = this._calls.map(function(call) { |
- return call.toReplayable(cache); |
- }); |
- this._populateReplayableData(data, cache); |
- var contextResource = this.contextResource(); |
- if (contextResource !== this) |
- data.contextResource = Resource.toReplayable(contextResource, cache); |
- return result; |
- }, |
- |
- /** |
- * @param {!Object} data |
- * @param {!Cache.<!ReplayableResource>} cache |
- */ |
- _populateReplayableData: function(data, cache) |
- { |
- // Do nothing. Should be overridden by subclasses. |
- }, |
- |
- /** |
- * @param {!Object} data |
- * @param {!Cache.<!Resource>} cache |
- * @return {!Resource} |
- */ |
- replay: function(data, cache) |
- { |
- var resource = cache.get(data.id); |
- if (resource) |
- return resource; |
- this._id = data.id; |
- this._name = data.name; |
- this._kindId = data.kindId; |
- this._resourceManager = null; |
- this._calls = []; |
- this._boundResources = Object.create(null); |
- this._wrappedObject = null; |
- cache.put(data.id, this); // Put into the cache early to avoid loops. |
- this._doReplayCalls(data, cache); |
- console.assert(this._wrappedObject, "Resource should be reconstructed!"); |
- return this; |
- }, |
- |
- /** |
- * @param {!Object} data |
- * @param {!Cache.<!Resource>} cache |
- */ |
- _doReplayCalls: function(data, cache) |
- { |
- for (var i = 0, n = data.calls.length; i < n; ++i) |
- this._calls.push(data.calls[i].replay(cache)); |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- pushCall: function(call) |
- { |
- call.freeze(); |
- this._calls.push(call); |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- onCallReplayed: function(call) |
- { |
- // Ignore by default. |
- }, |
- |
- /** |
- * @param {!Object} object |
- */ |
- _bindObjectToResource: function(object) |
- { |
- Object.defineProperty(object, "__resourceObject", { |
- value: this, |
- writable: false, |
- enumerable: false, |
- configurable: true |
- }); |
- }, |
- |
- /** |
- * @param {string} key |
- * @param {*} obj |
- */ |
- _registerBoundResource: function(key, obj) |
- { |
- var resource = Resource.forObject(obj); |
- if (resource) |
- this._boundResources[key] = resource; |
- else |
- delete this._boundResources[key]; |
- }, |
- |
- /** |
- * @return {?Object} |
- */ |
- _wrapObject: function() |
- { |
- var wrappedObject = this.wrappedObject(); |
- if (!wrappedObject) |
- return null; |
- var proxy = Object.create(wrappedObject.__proto__); // In order to emulate "instanceof". |
- |
- var customWrapFunctions = this._customWrapFunctions(); |
- /** @type {!Array.<string>} */ |
- this._proxyStatePropertyNames = []; |
- |
- /** |
- * @param {string} property |
- * @this {Resource} |
- */ |
- function processProperty(property) |
- { |
- if (typeof wrappedObject[property] === "function") { |
- var customWrapFunction = customWrapFunctions[property]; |
- if (customWrapFunction) |
- proxy[property] = this._wrapCustomFunction(this, wrappedObject, wrappedObject[property], property, customWrapFunction); |
- else |
- proxy[property] = this._wrapFunction(this, wrappedObject, wrappedObject[property], property); |
- } else if (TypeUtils.isEnumPropertyName(property, wrappedObject)) { |
- // Fast access to enums and constants. |
- proxy[property] = wrappedObject[property]; |
- } else { |
- this._proxyStatePropertyNames.push(property); |
- Object.defineProperty(proxy, property, { |
- get: function() |
- { |
- var obj = wrappedObject[property]; |
- var resource = Resource.forObject(obj); |
- return resource ? resource : obj; |
- }, |
- set: this._wrapPropertySetter(this, wrappedObject, property), |
- enumerable: true |
- }); |
- } |
- } |
- |
- var isEmpty = true; |
- for (var property in wrappedObject) { |
- isEmpty = false; |
- processProperty.call(this, property); |
- } |
- if (isEmpty) |
- return wrappedObject; // Nothing to proxy. |
- |
- this._bindObjectToResource(proxy); |
- return proxy; |
- }, |
- |
- /** |
- * @param {!Resource} resource |
- * @param {!Object} originalObject |
- * @param {!Function} originalFunction |
- * @param {string} functionName |
- * @param {!Function} customWrapFunction |
- * @return {!Function} |
- */ |
- _wrapCustomFunction: function(resource, originalObject, originalFunction, functionName, customWrapFunction) |
- { |
- return function() |
- { |
- var manager = resource.manager(); |
- var isCapturing = manager && manager.capturing(); |
- if (isCapturing) |
- manager.captureArguments(resource, arguments); |
- var wrapFunction = new Resource.WrapFunction(originalObject, originalFunction, functionName, arguments); |
- customWrapFunction.apply(wrapFunction, arguments); |
- if (isCapturing) { |
- var call = wrapFunction.call(); |
- call.setStackTrace(StackTrace.create(1, arguments.callee)); |
- manager.captureCall(call); |
- } |
- return wrapFunction.result(); |
- }; |
- }, |
- |
- /** |
- * @param {!Resource} resource |
- * @param {!Object} originalObject |
- * @param {!Function} originalFunction |
- * @param {string} functionName |
- * @return {!Function} |
- */ |
- _wrapFunction: function(resource, originalObject, originalFunction, functionName) |
- { |
- return function() |
- { |
- var manager = resource.manager(); |
- if (!manager || !manager.capturing()) |
- return originalFunction.apply(originalObject, arguments); |
- manager.captureArguments(resource, arguments); |
- var result = originalFunction.apply(originalObject, arguments); |
- var stackTrace = StackTrace.create(1, arguments.callee); |
- var call = new Call(resource, functionName, arguments, result, stackTrace); |
- manager.captureCall(call); |
- return result; |
- }; |
- }, |
- |
- /** |
- * @param {!Resource} resource |
- * @param {!Object} originalObject |
- * @param {string} propertyName |
- * @return {function(*)} |
- */ |
- _wrapPropertySetter: function(resource, originalObject, propertyName) |
- { |
- return function(value) |
- { |
- resource._registerBoundResource(propertyName, value); |
- var manager = resource.manager(); |
- if (!manager || !manager.capturing()) { |
- originalObject[propertyName] = Resource.wrappedObject(value); |
- return; |
- } |
- var args = [propertyName, value]; |
- manager.captureArguments(resource, args); |
- originalObject[propertyName] = Resource.wrappedObject(value); |
- var stackTrace = StackTrace.create(1, arguments.callee); |
- var call = new Call(resource, "", args, undefined, stackTrace); |
- manager.captureCall(call); |
- }; |
- }, |
- |
- /** |
- * @return {!Object.<string, !Function>} |
- */ |
- _customWrapFunctions: function() |
- { |
- return Object.create(null); // May be overridden by subclasses. |
- } |
-} |
- |
-/** |
- * @constructor |
- * @param {!Object} originalObject |
- * @param {!Function} originalFunction |
- * @param {string} functionName |
- * @param {!Array|!Arguments} args |
- */ |
-Resource.WrapFunction = function(originalObject, originalFunction, functionName, args) |
-{ |
- this._originalObject = originalObject; |
- this._originalFunction = originalFunction; |
- this._functionName = functionName; |
- this._args = args; |
- this._resource = Resource.forObject(originalObject); |
- console.assert(this._resource, "Expected a wrapped call on a Resource object."); |
-} |
- |
-Resource.WrapFunction.prototype = { |
- /** |
- * @return {*} |
- */ |
- result: function() |
- { |
- if (!this._executed) { |
- this._executed = true; |
- this._result = this._originalFunction.apply(this._originalObject, this._args); |
- } |
- return this._result; |
- }, |
- |
- /** |
- * @return {!Call} |
- */ |
- call: function() |
- { |
- if (!this._call) |
- this._call = new Call(this._resource, this._functionName, this._args, this.result()); |
- return this._call; |
- }, |
- |
- /** |
- * @param {*} result |
- */ |
- overrideResult: function(result) |
- { |
- var call = this.call(); |
- call.setResult(result); |
- this._result = result; |
- } |
-} |
- |
-/** |
- * @param {function(new:Resource, !Object, string)} resourceConstructor |
- * @param {string} resourceName |
- * @return {function(this:Resource.WrapFunction)} |
- */ |
-Resource.WrapFunction.resourceFactoryMethod = function(resourceConstructor, resourceName) |
-{ |
- return /** @this {Resource.WrapFunction} */ function() |
- { |
- var wrappedObject = /** @type {?Object} */ (this.result()); |
- if (!wrappedObject) |
- return; |
- var resource = new resourceConstructor(wrappedObject, resourceName); |
- var manager = this._resource.manager(); |
- if (manager) |
- manager.registerResource(resource); |
- this.overrideResult(resource.proxyObject()); |
- resource.pushCall(this.call()); |
- } |
-} |
- |
-/** |
- * @constructor |
- * @param {!Resource} originalResource |
- * @param {!Object} data |
- */ |
-function ReplayableResource(originalResource, data) |
-{ |
- this._proto = originalResource.__proto__; |
- this._data = data; |
-} |
- |
-ReplayableResource.prototype = { |
- /** |
- * @return {number} |
- */ |
- id: function() |
- { |
- return this._data.id; |
- }, |
- |
- /** |
- * @return {string} |
- */ |
- name: function() |
- { |
- return this._data.name; |
- }, |
- |
- /** |
- * @return {string} |
- */ |
- description: function() |
- { |
- return this._data.name + "@" + this._data.kindId; |
- }, |
- |
- /** |
- * @return {!ReplayableResource} |
- */ |
- contextResource: function() |
- { |
- return this._data.contextResource || this; |
- }, |
- |
- /** |
- * @param {!Cache.<!Resource>} cache |
- * @return {!Resource} |
- */ |
- replay: function(cache) |
- { |
- var result = /** @type {!Resource} */ (Object.create(this._proto)); |
- result = result.replay(this._data, cache) |
- console.assert(result.__proto__ === this._proto, "Wrong type of a replay result"); |
- return result; |
- } |
-} |
- |
-/** |
- * @param {!ReplayableResource|*} obj |
- * @param {!Cache.<!Resource>} cache |
- * @return {*} |
- */ |
-ReplayableResource.replay = function(obj, cache) |
-{ |
- return (obj instanceof ReplayableResource) ? obj.replay(cache).wrappedObject() : obj; |
-} |
- |
-/** |
- * @constructor |
- * @extends {Resource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function ContextResource(wrappedObject, name) |
-{ |
- Resource.call(this, wrappedObject, name); |
-} |
- |
-ContextResource.prototype = { |
- __proto__: Resource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @extends {Resource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function LogEverythingResource(wrappedObject, name) |
-{ |
- Resource.call(this, wrappedObject, name); |
-} |
- |
-LogEverythingResource.prototype = { |
- /** |
- * @override |
- * @return {!Object.<string, !Function>} |
- */ |
- _customWrapFunctions: function() |
- { |
- var wrapFunctions = Object.create(null); |
- var wrappedObject = this.wrappedObject(); |
- if (wrappedObject) { |
- for (var property in wrappedObject) { |
- /** @this {Resource.WrapFunction} */ |
- wrapFunctions[property] = function() |
- { |
- this._resource.pushCall(this.call()); |
- } |
- } |
- } |
- return wrapFunctions; |
- }, |
- |
- __proto__: Resource.prototype |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// WebGL |
-//////////////////////////////////////////////////////////////////////////////// |
- |
-/** |
- * @constructor |
- * @extends {Resource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function WebGLBoundResource(wrappedObject, name) |
-{ |
- Resource.call(this, wrappedObject, name); |
- /** @type {!Object.<string, *>} */ |
- this._state = {}; |
-} |
- |
-WebGLBoundResource.prototype = { |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!ReplayableResource>} cache |
- */ |
- _populateReplayableData: function(data, cache) |
- { |
- var state = this._state; |
- data.state = {}; |
- Object.keys(state).forEach(function(parameter) { |
- data.state[parameter] = Resource.toReplayable(state[parameter], cache); |
- }); |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!Resource>} cache |
- */ |
- _doReplayCalls: function(data, cache) |
- { |
- var gl = this._replayContextResource(data, cache).wrappedObject(); |
- |
- /** @type {!Object.<string, !Array.<string>>} */ |
- var bindingsData = { |
- TEXTURE_2D: ["bindTexture", "TEXTURE_BINDING_2D"], |
- TEXTURE_CUBE_MAP: ["bindTexture", "TEXTURE_BINDING_CUBE_MAP"], |
- ARRAY_BUFFER: ["bindBuffer", "ARRAY_BUFFER_BINDING"], |
- ELEMENT_ARRAY_BUFFER: ["bindBuffer", "ELEMENT_ARRAY_BUFFER_BINDING"], |
- FRAMEBUFFER: ["bindFramebuffer", "FRAMEBUFFER_BINDING"], |
- RENDERBUFFER: ["bindRenderbuffer", "RENDERBUFFER_BINDING"] |
- }; |
- var originalBindings = {}; |
- Object.keys(bindingsData).forEach(function(bindingTarget) { |
- var bindingParameter = bindingsData[bindingTarget][1]; |
- originalBindings[bindingTarget] = gl.getParameter(gl[bindingParameter]); |
- }); |
- |
- var state = {}; |
- Object.keys(data.state).forEach(function(parameter) { |
- state[parameter] = ReplayableResource.replay(data.state[parameter], cache); |
- }); |
- this._state = state; |
- Resource.prototype._doReplayCalls.call(this, data, cache); |
- |
- Object.keys(bindingsData).forEach(function(bindingTarget) { |
- var bindMethodName = bindingsData[bindingTarget][0]; |
- gl[bindMethodName].call(gl, gl[bindingTarget], originalBindings[bindingTarget]); |
- }); |
- }, |
- |
- /** |
- * @param {!Object} data |
- * @param {!Cache.<!Resource>} cache |
- * @return {?WebGLRenderingContextResource} |
- */ |
- _replayContextResource: function(data, cache) |
- { |
- var calls = /** @type {!Array.<!ReplayableCall>} */ (data.calls); |
- for (var i = 0, n = calls.length; i < n; ++i) { |
- var resource = ReplayableResource.replay(calls[i].replayableResource(), cache); |
- var contextResource = WebGLRenderingContextResource.forObject(resource); |
- if (contextResource) |
- return contextResource; |
- } |
- return null; |
- }, |
- |
- /** |
- * @param {number} target |
- * @param {string} bindMethodName |
- */ |
- pushBinding: function(target, bindMethodName) |
- { |
- if (this._state.bindTarget !== target) { |
- this._state.bindTarget = target; |
- this.pushCall(new Call(WebGLRenderingContextResource.forObject(this), bindMethodName, [target, this])); |
- } |
- }, |
- |
- __proto__: Resource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @extends {WebGLBoundResource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function WebGLTextureResource(wrappedObject, name) |
-{ |
- WebGLBoundResource.call(this, wrappedObject, name); |
-} |
- |
-WebGLTextureResource.prototype = { |
- /** |
- * @override (overrides @return type) |
- * @return {!WebGLTexture} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @override |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- var result = []; |
- var glResource = WebGLRenderingContextResource.forObject(this); |
- var gl = glResource.wrappedObject(); |
- var texture = this.wrappedObject(); |
- if (!gl || !texture) |
- return result; |
- result.push({ name: "isTexture", value: gl.isTexture(texture) }); |
- result.push({ name: "context", value: this.contextResource() }); |
- |
- var target = this._state.bindTarget; |
- if (typeof target !== "number") |
- return result; |
- |
- var bindingParameter; |
- switch (target) { |
- case gl.TEXTURE_2D: |
- bindingParameter = gl.TEXTURE_BINDING_2D; |
- break; |
- case gl.TEXTURE_CUBE_MAP: |
- bindingParameter = gl.TEXTURE_BINDING_CUBE_MAP; |
- break; |
- default: |
- console.error("ASSERT_NOT_REACHED: unknown texture target " + target); |
- return result; |
- } |
- result.push({ name: "target", value: target, valueIsEnum: true }); |
- |
- var oldTexture = /** @type {!WebGLTexture} */ (gl.getParameter(bindingParameter)); |
- if (oldTexture !== texture) |
- gl.bindTexture(target, texture); |
- |
- var textureParameters = [ |
- "TEXTURE_MAG_FILTER", |
- "TEXTURE_MIN_FILTER", |
- "TEXTURE_WRAP_S", |
- "TEXTURE_WRAP_T", |
- "TEXTURE_MAX_ANISOTROPY_EXT" // EXT_texture_filter_anisotropic extension |
- ]; |
- glResource.queryStateValues(gl.getTexParameter, target, textureParameters, result); |
- |
- if (oldTexture !== texture) |
- gl.bindTexture(target, oldTexture); |
- return result; |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!Resource>} cache |
- */ |
- _doReplayCalls: function(data, cache) |
- { |
- var gl = this._replayContextResource(data, cache).wrappedObject(); |
- |
- var state = {}; |
- WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) { |
- state[parameter] = gl.getParameter(gl[parameter]); |
- }); |
- |
- WebGLBoundResource.prototype._doReplayCalls.call(this, data, cache); |
- |
- WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) { |
- gl.pixelStorei(gl[parameter], state[parameter]); |
- }); |
- }, |
- |
- /** |
- * @override |
- * @param {!Call} call |
- */ |
- pushCall: function(call) |
- { |
- var gl = WebGLRenderingContextResource.forObject(call.resource()).wrappedObject(); |
- WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) { |
- var value = gl.getParameter(gl[parameter]); |
- if (this._state[parameter] !== value) { |
- this._state[parameter] = value; |
- var pixelStoreCall = new Call(gl, "pixelStorei", [gl[parameter], value]); |
- WebGLBoundResource.prototype.pushCall.call(this, pixelStoreCall); |
- } |
- }, this); |
- |
- // FIXME: remove any older calls that no longer contribute to the resource state. |
- // FIXME: optimize memory usage: maybe it's more efficient to store one texImage2D call instead of many texSubImage2D. |
- WebGLBoundResource.prototype.pushCall.call(this, call); |
- }, |
- |
- /** |
- * Handles: texParameteri, texParameterf |
- * @param {!Call} call |
- */ |
- pushCall_texParameter: function(call) |
- { |
- var args = call.args(); |
- var pname = args[1]; |
- var param = args[2]; |
- if (this._state[pname] !== param) { |
- this._state[pname] = param; |
- WebGLBoundResource.prototype.pushCall.call(this, call); |
- } |
- }, |
- |
- /** |
- * Handles: copyTexImage2D, copyTexSubImage2D |
- * copyTexImage2D and copyTexSubImage2D define a texture image with pixels from the current framebuffer. |
- * @param {!Call} call |
- */ |
- pushCall_copyTexImage2D: function(call) |
- { |
- var glResource = WebGLRenderingContextResource.forObject(call.resource()); |
- var gl = glResource.wrappedObject(); |
- var framebufferResource = /** @type {!WebGLFramebufferResource} */ (glResource.currentBinding(gl.FRAMEBUFFER)); |
- if (framebufferResource) |
- this.pushCall(new Call(glResource, "bindFramebuffer", [gl.FRAMEBUFFER, framebufferResource])); |
- else { |
- // FIXME: Implement this case. |
- console.error("ASSERT_NOT_REACHED: Could not properly process a gl." + call.functionName() + " call while the DRAWING BUFFER is bound."); |
- } |
- this.pushCall(call); |
- }, |
- |
- __proto__: WebGLBoundResource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @extends {Resource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function WebGLProgramResource(wrappedObject, name) |
-{ |
- Resource.call(this, wrappedObject, name); |
-} |
- |
-WebGLProgramResource.prototype = { |
- /** |
- * @override (overrides @return type) |
- * @return {!WebGLProgram} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @override |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- /** |
- * @param {!Object} obj |
- * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} output |
- */ |
- function convertToStateDescriptors(obj, output) |
- { |
- for (var pname in obj) |
- output.push({ name: pname, value: obj[pname], valueIsEnum: (pname === "type") }); |
- } |
- |
- var result = []; |
- var program = this.wrappedObject(); |
- if (!program) |
- return result; |
- var glResource = WebGLRenderingContextResource.forObject(this); |
- var gl = glResource.wrappedObject(); |
- var programParameters = ["DELETE_STATUS", "LINK_STATUS", "VALIDATE_STATUS"]; |
- glResource.queryStateValues(gl.getProgramParameter, program, programParameters, result); |
- result.push({ name: "getProgramInfoLog", value: gl.getProgramInfoLog(program) }); |
- result.push({ name: "isProgram", value: gl.isProgram(program) }); |
- result.push({ name: "context", value: this.contextResource() }); |
- |
- // ATTACHED_SHADERS |
- var callFormatter = CallFormatter.forResource(this); |
- var shaders = gl.getAttachedShaders(program) || []; |
- var shaderDescriptors = []; |
- for (var i = 0, n = shaders.length; i < n; ++i) { |
- var shaderResource = Resource.forObject(shaders[i]); |
- var pname = callFormatter.enumNameForValue(shaderResource.type()); |
- shaderDescriptors.push({ name: pname, value: shaderResource }); |
- } |
- result.push({ name: "ATTACHED_SHADERS", values: shaderDescriptors, isArray: true }); |
- |
- // ACTIVE_UNIFORMS |
- var uniformDescriptors = []; |
- var uniforms = this._activeUniforms(true); |
- for (var i = 0, n = uniforms.length; i < n; ++i) { |
- var pname = "" + i; |
- var values = []; |
- convertToStateDescriptors(uniforms[i], values); |
- uniformDescriptors.push({ name: pname, values: values }); |
- } |
- result.push({ name: "ACTIVE_UNIFORMS", values: uniformDescriptors, isArray: true }); |
- |
- // ACTIVE_ATTRIBUTES |
- var attributesCount = /** @type {number} */ (gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES)); |
- var attributeDescriptors = []; |
- for (var i = 0; i < attributesCount; ++i) { |
- var activeInfo = gl.getActiveAttrib(program, i); |
- if (!activeInfo) |
- continue; |
- var pname = "" + i; |
- var values = []; |
- convertToStateDescriptors(activeInfo, values); |
- attributeDescriptors.push({ name: pname, values: values }); |
- } |
- result.push({ name: "ACTIVE_ATTRIBUTES", values: attributeDescriptors, isArray: true }); |
- |
- return result; |
- }, |
- |
- /** |
- * @param {boolean=} includeAllInfo |
- * @return {!Array.<{name:string, type:number, value:*, size:(number|undefined)}>} |
- */ |
- _activeUniforms: function(includeAllInfo) |
- { |
- var uniforms = []; |
- var program = this.wrappedObject(); |
- if (!program) |
- return uniforms; |
- |
- var gl = WebGLRenderingContextResource.forObject(this).wrappedObject(); |
- var uniformsCount = /** @type {number} */ (gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS)); |
- for (var i = 0; i < uniformsCount; ++i) { |
- var activeInfo = gl.getActiveUniform(program, i); |
- if (!activeInfo) |
- continue; |
- var uniformLocation = gl.getUniformLocation(program, activeInfo.name); |
- if (!uniformLocation) |
- continue; |
- var value = gl.getUniform(program, uniformLocation); |
- var item = Object.create(null); |
- item.name = activeInfo.name; |
- item.type = activeInfo.type; |
- item.value = value; |
- if (includeAllInfo) |
- item.size = activeInfo.size; |
- uniforms.push(item); |
- } |
- return uniforms; |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!ReplayableResource>} cache |
- */ |
- _populateReplayableData: function(data, cache) |
- { |
- var glResource = WebGLRenderingContextResource.forObject(this); |
- var originalErrors = glResource.getAllErrors(); |
- data.uniforms = this._activeUniforms(); |
- glResource.restoreErrors(originalErrors); |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!Resource>} cache |
- */ |
- _doReplayCalls: function(data, cache) |
- { |
- Resource.prototype._doReplayCalls.call(this, data, cache); |
- var gl = WebGLRenderingContextResource.forObject(this).wrappedObject(); |
- var program = this.wrappedObject(); |
- |
- var originalProgram = /** @type {!WebGLProgram} */ (gl.getParameter(gl.CURRENT_PROGRAM)); |
- var currentProgram = originalProgram; |
- |
- data.uniforms.forEach(function(uniform) { |
- var uniformLocation = gl.getUniformLocation(program, uniform.name); |
- if (!uniformLocation) |
- return; |
- if (currentProgram !== program) { |
- currentProgram = program; |
- gl.useProgram(program); |
- } |
- var methodName = this._uniformMethodNameByType(gl, uniform.type); |
- if (methodName.indexOf("Matrix") === -1) |
- gl[methodName].call(gl, uniformLocation, uniform.value); |
- else |
- gl[methodName].call(gl, uniformLocation, false, uniform.value); |
- }.bind(this)); |
- |
- if (currentProgram !== originalProgram) |
- gl.useProgram(originalProgram); |
- }, |
- |
- /** |
- * @param {!WebGLRenderingContext} gl |
- * @param {number} type |
- * @return {string} |
- */ |
- _uniformMethodNameByType: function(gl, type) |
- { |
- var uniformMethodNames = WebGLProgramResource._uniformMethodNames; |
- if (!uniformMethodNames) { |
- uniformMethodNames = {}; |
- uniformMethodNames[gl.FLOAT] = "uniform1f"; |
- uniformMethodNames[gl.FLOAT_VEC2] = "uniform2fv"; |
- uniformMethodNames[gl.FLOAT_VEC3] = "uniform3fv"; |
- uniformMethodNames[gl.FLOAT_VEC4] = "uniform4fv"; |
- uniformMethodNames[gl.INT] = "uniform1i"; |
- uniformMethodNames[gl.BOOL] = "uniform1i"; |
- uniformMethodNames[gl.SAMPLER_2D] = "uniform1i"; |
- uniformMethodNames[gl.SAMPLER_CUBE] = "uniform1i"; |
- uniformMethodNames[gl.INT_VEC2] = "uniform2iv"; |
- uniformMethodNames[gl.BOOL_VEC2] = "uniform2iv"; |
- uniformMethodNames[gl.INT_VEC3] = "uniform3iv"; |
- uniformMethodNames[gl.BOOL_VEC3] = "uniform3iv"; |
- uniformMethodNames[gl.INT_VEC4] = "uniform4iv"; |
- uniformMethodNames[gl.BOOL_VEC4] = "uniform4iv"; |
- uniformMethodNames[gl.FLOAT_MAT2] = "uniformMatrix2fv"; |
- uniformMethodNames[gl.FLOAT_MAT3] = "uniformMatrix3fv"; |
- uniformMethodNames[gl.FLOAT_MAT4] = "uniformMatrix4fv"; |
- WebGLProgramResource._uniformMethodNames = uniformMethodNames; |
- } |
- console.assert(uniformMethodNames[type], "Unknown uniform type " + type); |
- return uniformMethodNames[type]; |
- }, |
- |
- /** |
- * @override |
- * @param {!Call} call |
- */ |
- pushCall: function(call) |
- { |
- // FIXME: remove any older calls that no longer contribute to the resource state. |
- // FIXME: handle multiple attachShader && detachShader. |
- Resource.prototype.pushCall.call(this, call); |
- }, |
- |
- __proto__: Resource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @extends {Resource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function WebGLShaderResource(wrappedObject, name) |
-{ |
- Resource.call(this, wrappedObject, name); |
-} |
- |
-WebGLShaderResource.prototype = { |
- /** |
- * @override (overrides @return type) |
- * @return {!WebGLShader} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @return {number} |
- */ |
- type: function() |
- { |
- var call = this._calls[0]; |
- if (call && call.functionName() === "createShader") |
- return call.args()[0]; |
- console.error("ASSERT_NOT_REACHED: Failed to restore shader type from the log.", call); |
- return 0; |
- }, |
- |
- /** |
- * @override |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- var result = []; |
- var shader = this.wrappedObject(); |
- if (!shader) |
- return result; |
- var glResource = WebGLRenderingContextResource.forObject(this); |
- var gl = glResource.wrappedObject(); |
- var shaderParameters = ["SHADER_TYPE", "DELETE_STATUS", "COMPILE_STATUS"]; |
- glResource.queryStateValues(gl.getShaderParameter, shader, shaderParameters, result); |
- result.push({ name: "getShaderInfoLog", value: gl.getShaderInfoLog(shader) }); |
- result.push({ name: "getShaderSource", value: gl.getShaderSource(shader) }); |
- result.push({ name: "isShader", value: gl.isShader(shader) }); |
- result.push({ name: "context", value: this.contextResource() }); |
- |
- // getShaderPrecisionFormat |
- var shaderType = this.type(); |
- var precisionValues = []; |
- var precisionParameters = ["LOW_FLOAT", "MEDIUM_FLOAT", "HIGH_FLOAT", "LOW_INT", "MEDIUM_INT", "HIGH_INT"]; |
- for (var i = 0, pname; pname = precisionParameters[i]; ++i) |
- precisionValues.push({ name: pname, value: gl.getShaderPrecisionFormat(shaderType, gl[pname]) }); |
- result.push({ name: "getShaderPrecisionFormat", values: precisionValues }); |
- |
- return result; |
- }, |
- |
- /** |
- * @override |
- * @param {!Call} call |
- */ |
- pushCall: function(call) |
- { |
- // FIXME: remove any older calls that no longer contribute to the resource state. |
- // FIXME: handle multiple shaderSource calls. |
- Resource.prototype.pushCall.call(this, call); |
- }, |
- |
- __proto__: Resource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @extends {WebGLBoundResource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function WebGLBufferResource(wrappedObject, name) |
-{ |
- WebGLBoundResource.call(this, wrappedObject, name); |
-} |
- |
-WebGLBufferResource.prototype = { |
- /** |
- * @override (overrides @return type) |
- * @return {!WebGLBuffer} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @return {?ArrayBufferView} |
- */ |
- cachedBufferData: function() |
- { |
- /** |
- * Creates a view to a given buffer, does NOT copy the buffer. |
- * @param {!ArrayBuffer|!ArrayBufferView} buffer |
- * @return {!Uint8Array} |
- */ |
- function createUint8ArrayBufferView(buffer) |
- { |
- return buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); |
- } |
- |
- if (!this._cachedBufferData) { |
- for (var i = this._calls.length - 1; i >= 0; --i) { |
- var call = this._calls[i]; |
- if (call.functionName() === "bufferData") { |
- var sizeOrData = /** @type {number|!ArrayBuffer|!ArrayBufferView} */ (call.args()[1]); |
- if (typeof sizeOrData === "number") |
- this._cachedBufferData = new ArrayBuffer(sizeOrData); |
- else |
- this._cachedBufferData = sizeOrData; |
- this._lastBufferSubDataIndex = i + 1; |
- break; |
- } |
- } |
- if (!this._cachedBufferData) |
- return null; |
- } |
- |
- // Apply any "bufferSubData" calls that have not been applied yet. |
- var bufferDataView; |
- while (this._lastBufferSubDataIndex < this._calls.length) { |
- var call = this._calls[this._lastBufferSubDataIndex++]; |
- if (call.functionName() !== "bufferSubData") |
- continue; |
- var offset = /** @type {number} */ (call.args()[1]); |
- var data = /** @type {!ArrayBuffer|!ArrayBufferView} */ (call.args()[2]); |
- var view = createUint8ArrayBufferView(data); |
- if (!bufferDataView) |
- bufferDataView = createUint8ArrayBufferView(this._cachedBufferData); |
- bufferDataView.set(view, offset); |
- |
- var isFullReplacement = (offset === 0 && bufferDataView.length === view.length); |
- if (this._cachedBufferData instanceof ArrayBuffer) { |
- // The buffer data has no type yet. Try to guess from the "bufferSubData" call. |
- var typedArrayClass = TypeUtils.typedArrayClass(data); |
- if (typedArrayClass) |
- this._cachedBufferData = new typedArrayClass(this._cachedBufferData); // Does not copy the buffer. |
- } else if (isFullReplacement) { |
- var typedArrayClass = TypeUtils.typedArrayClass(data); |
- if (typedArrayClass) { |
- var typedArrayData = /** @type {!ArrayBufferView} */ (data); |
- this._cachedBufferData = new typedArrayClass(this._cachedBufferData.buffer, this._cachedBufferData.byteOffset, typedArrayData.length); // Does not copy the buffer. |
- } |
- } |
- } |
- |
- if (this._cachedBufferData instanceof ArrayBuffer) { |
- // If we failed to guess the data type yet, use Uint8Array. |
- return new Uint8Array(this._cachedBufferData); |
- } |
- return this._cachedBufferData; |
- }, |
- |
- /** |
- * @override |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- var result = []; |
- var glResource = WebGLRenderingContextResource.forObject(this); |
- var gl = glResource.wrappedObject(); |
- var buffer = this.wrappedObject(); |
- if (!gl || !buffer) |
- return result; |
- result.push({ name: "isBuffer", value: gl.isBuffer(buffer) }); |
- result.push({ name: "context", value: this.contextResource() }); |
- |
- var target = this._state.bindTarget; |
- if (typeof target !== "number") |
- return result; |
- |
- var bindingParameter; |
- switch (target) { |
- case gl.ARRAY_BUFFER: |
- bindingParameter = gl.ARRAY_BUFFER_BINDING; |
- break; |
- case gl.ELEMENT_ARRAY_BUFFER: |
- bindingParameter = gl.ELEMENT_ARRAY_BUFFER_BINDING; |
- break; |
- default: |
- console.error("ASSERT_NOT_REACHED: unknown buffer target " + target); |
- return result; |
- } |
- result.push({ name: "target", value: target, valueIsEnum: true }); |
- |
- var oldBuffer = /** @type {!WebGLBuffer} */ (gl.getParameter(bindingParameter)); |
- if (oldBuffer !== buffer) |
- gl.bindBuffer(target, buffer); |
- |
- var bufferParameters = ["BUFFER_SIZE", "BUFFER_USAGE"]; |
- glResource.queryStateValues(gl.getBufferParameter, target, bufferParameters, result); |
- |
- if (oldBuffer !== buffer) |
- gl.bindBuffer(target, oldBuffer); |
- |
- try { |
- var data = this.cachedBufferData(); |
- if (data) |
- result.push({ name: "bufferData", value: data }); |
- } catch (e) { |
- console.error("Exception while restoring bufferData", e); |
- } |
- |
- return result; |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- pushCall_bufferData: function(call) |
- { |
- // FIXME: remove any older calls that no longer contribute to the resource state. |
- delete this._cachedBufferData; |
- delete this._lastBufferSubDataIndex; |
- WebGLBoundResource.prototype.pushCall.call(this, call); |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- pushCall_bufferSubData: function(call) |
- { |
- // FIXME: Optimize memory for bufferSubData. |
- WebGLBoundResource.prototype.pushCall.call(this, call); |
- }, |
- |
- __proto__: WebGLBoundResource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @extends {WebGLBoundResource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function WebGLFramebufferResource(wrappedObject, name) |
-{ |
- WebGLBoundResource.call(this, wrappedObject, name); |
-} |
- |
-WebGLFramebufferResource.prototype = { |
- /** |
- * @override (overrides @return type) |
- * @return {!WebGLFramebuffer} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @override |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- var result = []; |
- var framebuffer = this.wrappedObject(); |
- if (!framebuffer) |
- return result; |
- var gl = WebGLRenderingContextResource.forObject(this).wrappedObject(); |
- |
- var oldFramebuffer = /** @type {!WebGLFramebuffer} */ (gl.getParameter(gl.FRAMEBUFFER_BINDING)); |
- if (oldFramebuffer !== framebuffer) |
- gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); |
- |
- var attachmentParameters = ["COLOR_ATTACHMENT0", "DEPTH_ATTACHMENT", "STENCIL_ATTACHMENT"]; |
- var framebufferParameters = ["FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME", "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE"]; |
- for (var i = 0, attachment; attachment = attachmentParameters[i]; ++i) { |
- var values = []; |
- for (var j = 0, pname; pname = framebufferParameters[j]; ++j) { |
- var value = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl[attachment], gl[pname]); |
- value = Resource.forObject(value) || value; |
- values.push({ name: pname, value: value, valueIsEnum: WebGLRenderingContextResource.GetResultIsEnum[pname] }); |
- } |
- result.push({ name: attachment, values: values }); |
- } |
- result.push({ name: "isFramebuffer", value: gl.isFramebuffer(framebuffer) }); |
- result.push({ name: "context", value: this.contextResource() }); |
- |
- if (oldFramebuffer !== framebuffer) |
- gl.bindFramebuffer(gl.FRAMEBUFFER, oldFramebuffer); |
- return result; |
- }, |
- |
- /** |
- * @override |
- * @param {!Call} call |
- */ |
- pushCall: function(call) |
- { |
- // FIXME: remove any older calls that no longer contribute to the resource state. |
- WebGLBoundResource.prototype.pushCall.call(this, call); |
- }, |
- |
- __proto__: WebGLBoundResource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @extends {WebGLBoundResource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function WebGLRenderbufferResource(wrappedObject, name) |
-{ |
- WebGLBoundResource.call(this, wrappedObject, name); |
-} |
- |
-WebGLRenderbufferResource.prototype = { |
- /** |
- * @override (overrides @return type) |
- * @return {!WebGLRenderbuffer} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @override |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- var result = []; |
- var renderbuffer = this.wrappedObject(); |
- if (!renderbuffer) |
- return result; |
- var glResource = WebGLRenderingContextResource.forObject(this); |
- var gl = glResource.wrappedObject(); |
- |
- var oldRenderbuffer = /** @type {!WebGLRenderbuffer} */ (gl.getParameter(gl.RENDERBUFFER_BINDING)); |
- if (oldRenderbuffer !== renderbuffer) |
- gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); |
- |
- 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"]; |
- glResource.queryStateValues(gl.getRenderbufferParameter, gl.RENDERBUFFER, renderbufferParameters, result); |
- result.push({ name: "isRenderbuffer", value: gl.isRenderbuffer(renderbuffer) }); |
- result.push({ name: "context", value: this.contextResource() }); |
- |
- if (oldRenderbuffer !== renderbuffer) |
- gl.bindRenderbuffer(gl.RENDERBUFFER, oldRenderbuffer); |
- return result; |
- }, |
- |
- /** |
- * @override |
- * @param {!Call} call |
- */ |
- pushCall: function(call) |
- { |
- // FIXME: remove any older calls that no longer contribute to the resource state. |
- WebGLBoundResource.prototype.pushCall.call(this, call); |
- }, |
- |
- __proto__: WebGLBoundResource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @extends {Resource} |
- * @param {!Object} wrappedObject |
- * @param {string} name |
- */ |
-function WebGLUniformLocationResource(wrappedObject, name) |
-{ |
- Resource.call(this, wrappedObject, name); |
-} |
- |
-WebGLUniformLocationResource.prototype = { |
- /** |
- * @override (overrides @return type) |
- * @return {!WebGLUniformLocation} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @return {?WebGLProgramResource} |
- */ |
- program: function() |
- { |
- var call = this._calls[0]; |
- if (call && call.functionName() === "getUniformLocation") |
- return /** @type {!WebGLProgramResource} */ (Resource.forObject(call.args()[0])); |
- console.error("ASSERT_NOT_REACHED: Failed to restore WebGLUniformLocation from the log.", call); |
- return null; |
- }, |
- |
- /** |
- * @return {string} |
- */ |
- name: function() |
- { |
- var call = this._calls[0]; |
- if (call && call.functionName() === "getUniformLocation") |
- return call.args()[1]; |
- console.error("ASSERT_NOT_REACHED: Failed to restore WebGLUniformLocation from the log.", call); |
- return ""; |
- }, |
- |
- /** |
- * @override |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- var result = []; |
- var location = this.wrappedObject(); |
- if (!location) |
- return result; |
- var programResource = this.program(); |
- var program = programResource && programResource.wrappedObject(); |
- if (!program) |
- return result; |
- var gl = WebGLRenderingContextResource.forObject(this).wrappedObject(); |
- var uniformValue = gl.getUniform(program, location); |
- var name = this.name(); |
- result.push({ name: "name", value: name }); |
- result.push({ name: "program", value: programResource }); |
- result.push({ name: "value", value: uniformValue }); |
- result.push({ name: "context", value: this.contextResource() }); |
- |
- if (typeof this._type !== "number") { |
- var altName = name + "[0]"; |
- var uniformsCount = /** @type {number} */ (gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS)); |
- for (var i = 0; i < uniformsCount; ++i) { |
- var activeInfo = gl.getActiveUniform(program, i); |
- if (!activeInfo) |
- continue; |
- if (activeInfo.name === name || activeInfo.name === altName) { |
- this._type = activeInfo.type; |
- this._size = activeInfo.size; |
- if (activeInfo.name === name) |
- break; |
- } |
- } |
- } |
- if (typeof this._type === "number") |
- result.push({ name: "type", value: this._type, valueIsEnum: true }); |
- if (typeof this._size === "number") |
- result.push({ name: "size", value: this._size }); |
- |
- return result; |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!ReplayableResource>} cache |
- */ |
- _populateReplayableData: function(data, cache) |
- { |
- data.type = this._type; |
- data.size = this._size; |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!Resource>} cache |
- */ |
- _doReplayCalls: function(data, cache) |
- { |
- this._type = data.type; |
- this._size = data.size; |
- Resource.prototype._doReplayCalls.call(this, data, cache); |
- }, |
- |
- __proto__: Resource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @extends {ContextResource} |
- * @param {!WebGLRenderingContext} glContext |
- */ |
-function WebGLRenderingContextResource(glContext) |
-{ |
- ContextResource.call(this, glContext, "WebGLRenderingContext"); |
- /** @type {?Object.<number, boolean>} */ |
- this._customErrors = null; |
- /** @type {!Object.<string, string>} */ |
- this._extensions = {}; |
- /** @type {!Object.<string, number>} */ |
- this._extensionEnums = {}; |
-} |
- |
-/** |
- * @const |
- * @type {!Array.<string>} |
- */ |
-WebGLRenderingContextResource.GLCapabilities = [ |
- "BLEND", |
- "CULL_FACE", |
- "DEPTH_TEST", |
- "DITHER", |
- "POLYGON_OFFSET_FILL", |
- "SAMPLE_ALPHA_TO_COVERAGE", |
- "SAMPLE_COVERAGE", |
- "SCISSOR_TEST", |
- "STENCIL_TEST" |
-]; |
- |
-/** |
- * @const |
- * @type {!Array.<string>} |
- */ |
-WebGLRenderingContextResource.PixelStoreParameters = [ |
- "PACK_ALIGNMENT", |
- "UNPACK_ALIGNMENT", |
- "UNPACK_COLORSPACE_CONVERSION_WEBGL", |
- "UNPACK_FLIP_Y_WEBGL", |
- "UNPACK_PREMULTIPLY_ALPHA_WEBGL" |
-]; |
- |
-/** |
- * @const |
- * @type {!Array.<string>} |
- */ |
-WebGLRenderingContextResource.StateParameters = [ |
- "ACTIVE_TEXTURE", |
- "ARRAY_BUFFER_BINDING", |
- "BLEND_COLOR", |
- "BLEND_DST_ALPHA", |
- "BLEND_DST_RGB", |
- "BLEND_EQUATION_ALPHA", |
- "BLEND_EQUATION_RGB", |
- "BLEND_SRC_ALPHA", |
- "BLEND_SRC_RGB", |
- "COLOR_CLEAR_VALUE", |
- "COLOR_WRITEMASK", |
- "CULL_FACE_MODE", |
- "CURRENT_PROGRAM", |
- "DEPTH_CLEAR_VALUE", |
- "DEPTH_FUNC", |
- "DEPTH_RANGE", |
- "DEPTH_WRITEMASK", |
- "ELEMENT_ARRAY_BUFFER_BINDING", |
- "FRAGMENT_SHADER_DERIVATIVE_HINT_OES", // OES_standard_derivatives extension |
- "FRAMEBUFFER_BINDING", |
- "FRONT_FACE", |
- "GENERATE_MIPMAP_HINT", |
- "LINE_WIDTH", |
- "PACK_ALIGNMENT", |
- "POLYGON_OFFSET_FACTOR", |
- "POLYGON_OFFSET_UNITS", |
- "RENDERBUFFER_BINDING", |
- "SAMPLE_COVERAGE_INVERT", |
- "SAMPLE_COVERAGE_VALUE", |
- "SCISSOR_BOX", |
- "STENCIL_BACK_FAIL", |
- "STENCIL_BACK_FUNC", |
- "STENCIL_BACK_PASS_DEPTH_FAIL", |
- "STENCIL_BACK_PASS_DEPTH_PASS", |
- "STENCIL_BACK_REF", |
- "STENCIL_BACK_VALUE_MASK", |
- "STENCIL_BACK_WRITEMASK", |
- "STENCIL_CLEAR_VALUE", |
- "STENCIL_FAIL", |
- "STENCIL_FUNC", |
- "STENCIL_PASS_DEPTH_FAIL", |
- "STENCIL_PASS_DEPTH_PASS", |
- "STENCIL_REF", |
- "STENCIL_VALUE_MASK", |
- "STENCIL_WRITEMASK", |
- "UNPACK_ALIGNMENT", |
- "UNPACK_COLORSPACE_CONVERSION_WEBGL", |
- "UNPACK_FLIP_Y_WEBGL", |
- "UNPACK_PREMULTIPLY_ALPHA_WEBGL", |
- "VERTEX_ARRAY_BINDING_OES", // OES_vertex_array_object extension |
- "VIEWPORT" |
-]; |
- |
-/** |
- * True for those enums that return also an enum via a getter API method (e.g. getParameter, getShaderParameter, etc.). |
- * @const |
- * @type {!Object.<string, boolean>} |
- */ |
-WebGLRenderingContextResource.GetResultIsEnum = TypeUtils.createPrefixedPropertyNamesSet([ |
- // gl.getParameter() |
- "ACTIVE_TEXTURE", |
- "BLEND_DST_ALPHA", |
- "BLEND_DST_RGB", |
- "BLEND_EQUATION_ALPHA", |
- "BLEND_EQUATION_RGB", |
- "BLEND_SRC_ALPHA", |
- "BLEND_SRC_RGB", |
- "CULL_FACE_MODE", |
- "DEPTH_FUNC", |
- "FRONT_FACE", |
- "GENERATE_MIPMAP_HINT", |
- "FRAGMENT_SHADER_DERIVATIVE_HINT_OES", |
- "STENCIL_BACK_FAIL", |
- "STENCIL_BACK_FUNC", |
- "STENCIL_BACK_PASS_DEPTH_FAIL", |
- "STENCIL_BACK_PASS_DEPTH_PASS", |
- "STENCIL_FAIL", |
- "STENCIL_FUNC", |
- "STENCIL_PASS_DEPTH_FAIL", |
- "STENCIL_PASS_DEPTH_PASS", |
- "UNPACK_COLORSPACE_CONVERSION_WEBGL", |
- // gl.getBufferParameter() |
- "BUFFER_USAGE", |
- // gl.getFramebufferAttachmentParameter() |
- "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", |
- // gl.getRenderbufferParameter() |
- "RENDERBUFFER_INTERNAL_FORMAT", |
- // gl.getTexParameter() |
- "TEXTURE_MAG_FILTER", |
- "TEXTURE_MIN_FILTER", |
- "TEXTURE_WRAP_S", |
- "TEXTURE_WRAP_T", |
- // gl.getShaderParameter() |
- "SHADER_TYPE", |
- // gl.getVertexAttrib() |
- "VERTEX_ATTRIB_ARRAY_TYPE" |
-]); |
- |
-/** |
- * @const |
- * @type {!Object.<string, boolean>} |
- */ |
-WebGLRenderingContextResource.DrawingMethods = TypeUtils.createPrefixedPropertyNamesSet([ |
- "clear", |
- "drawArrays", |
- "drawElements" |
-]); |
- |
-/** |
- * @param {*} obj |
- * @return {?WebGLRenderingContextResource} |
- */ |
-WebGLRenderingContextResource.forObject = function(obj) |
-{ |
- var resource = Resource.forObject(obj); |
- if (!resource) |
- return null; |
- resource = resource.contextResource(); |
- return (resource instanceof WebGLRenderingContextResource) ? resource : null; |
-} |
- |
-WebGLRenderingContextResource.prototype = { |
- /** |
- * @override (overrides @return type) |
- * @return {!WebGLRenderingContext} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @override |
- * @return {string} |
- */ |
- toDataURL: function() |
- { |
- return this.wrappedObject().canvas.toDataURL(); |
- }, |
- |
- /** |
- * @return {!Array.<number>} |
- */ |
- getAllErrors: function() |
- { |
- var errors = []; |
- var gl = this.wrappedObject(); |
- if (gl) { |
- while (true) { |
- var error = gl.getError(); |
- if (error === gl.NO_ERROR) |
- break; |
- this.clearError(error); |
- errors.push(error); |
- } |
- } |
- if (this._customErrors) { |
- for (var key in this._customErrors) { |
- var error = Number(key); |
- errors.push(error); |
- } |
- delete this._customErrors; |
- } |
- return errors; |
- }, |
- |
- /** |
- * @param {!Array.<number>} errors |
- */ |
- restoreErrors: function(errors) |
- { |
- var gl = this.wrappedObject(); |
- if (gl) { |
- var wasError = false; |
- while (gl.getError() !== gl.NO_ERROR) |
- wasError = true; |
- console.assert(!wasError, "Error(s) while capturing current WebGL state."); |
- } |
- if (!errors.length) |
- delete this._customErrors; |
- else { |
- this._customErrors = {}; |
- for (var i = 0, n = errors.length; i < n; ++i) |
- this._customErrors[errors[i]] = true; |
- } |
- }, |
- |
- /** |
- * @param {number} error |
- */ |
- clearError: function(error) |
- { |
- if (this._customErrors) |
- delete this._customErrors[error]; |
- }, |
- |
- /** |
- * @return {number} |
- */ |
- nextError: function() |
- { |
- if (this._customErrors) { |
- for (var key in this._customErrors) { |
- var error = Number(key); |
- delete this._customErrors[error]; |
- return error; |
- } |
- } |
- delete this._customErrors; |
- var gl = this.wrappedObject(); |
- return gl ? gl.NO_ERROR : 0; |
- }, |
- |
- /** |
- * @param {string} name |
- * @param {?Object} obj |
- */ |
- registerWebGLExtension: function(name, obj) |
- { |
- // FIXME: Wrap OES_vertex_array_object extension. |
- var lowerName = name.toLowerCase(); |
- if (obj && !this._extensions[lowerName]) { |
- this._extensions[lowerName] = name; |
- for (var property in obj) { |
- if (TypeUtils.isEnumPropertyName(property, obj)) |
- this._extensionEnums[property] = /** @type {number} */ (obj[property]); |
- } |
- } |
- }, |
- |
- /** |
- * @param {string} name |
- * @return {number|undefined} |
- */ |
- _enumValueForName: function(name) |
- { |
- if (typeof this._extensionEnums[name] === "number") |
- return this._extensionEnums[name]; |
- var gl = this.wrappedObject(); |
- return (typeof gl[name] === "number" ? gl[name] : undefined); |
- }, |
- |
- /** |
- * @param {function(this:WebGLRenderingContext, T, number):*} func |
- * @param {T} targetOrWebGLObject |
- * @param {!Array.<string>} pnames |
- * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} output |
- * @template T |
- */ |
- queryStateValues: function(func, targetOrWebGLObject, pnames, output) |
- { |
- var gl = this.wrappedObject(); |
- for (var i = 0, pname; pname = pnames[i]; ++i) { |
- var enumValue = this._enumValueForName(pname); |
- if (typeof enumValue !== "number") |
- continue; |
- var value = func.call(gl, targetOrWebGLObject, enumValue); |
- value = Resource.forObject(value) || value; |
- output.push({ name: pname, value: value, valueIsEnum: WebGLRenderingContextResource.GetResultIsEnum[pname] }); |
- } |
- }, |
- |
- /** |
- * @override |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- /** |
- * @param {!Object} obj |
- * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} output |
- */ |
- function convertToStateDescriptors(obj, output) |
- { |
- for (var pname in obj) |
- output.push({ name: pname, value: obj[pname], valueIsEnum: WebGLRenderingContextResource.GetResultIsEnum[pname] }); |
- } |
- |
- var gl = this.wrappedObject(); |
- var glState = this._internalCurrentState(null); |
- |
- // VERTEX_ATTRIB_ARRAYS |
- var vertexAttribStates = []; |
- for (var i = 0, n = glState.VERTEX_ATTRIB_ARRAYS.length; i < n; ++i) { |
- var pname = "" + i; |
- var values = []; |
- convertToStateDescriptors(glState.VERTEX_ATTRIB_ARRAYS[i], values); |
- vertexAttribStates.push({ name: pname, values: values }); |
- } |
- delete glState.VERTEX_ATTRIB_ARRAYS; |
- |
- // TEXTURE_UNITS |
- var textureUnits = []; |
- for (var i = 0, n = glState.TEXTURE_UNITS.length; i < n; ++i) { |
- var pname = "TEXTURE" + i; |
- var values = []; |
- convertToStateDescriptors(glState.TEXTURE_UNITS[i], values); |
- textureUnits.push({ name: pname, values: values }); |
- } |
- delete glState.TEXTURE_UNITS; |
- |
- var result = []; |
- convertToStateDescriptors(glState, result); |
- result.push({ name: "VERTEX_ATTRIB_ARRAYS", values: vertexAttribStates, isArray: true }); |
- result.push({ name: "TEXTURE_UNITS", values: textureUnits, isArray: true }); |
- |
- var textureBindingParameters = ["TEXTURE_BINDING_2D", "TEXTURE_BINDING_CUBE_MAP"]; |
- for (var i = 0, pname; pname = textureBindingParameters[i]; ++i) { |
- var value = gl.getParameter(gl[pname]); |
- value = Resource.forObject(value) || value; |
- result.push({ name: pname, value: value }); |
- } |
- |
- // ENABLED_EXTENSIONS |
- var enabledExtensions = []; |
- for (var lowerName in this._extensions) { |
- var pname = this._extensions[lowerName]; |
- var value = gl.getExtension(pname); |
- value = Resource.forObject(value) || value; |
- enabledExtensions.push({ name: pname, value: value }); |
- } |
- result.push({ name: "ENABLED_EXTENSIONS", values: enabledExtensions, isArray: true }); |
- |
- return result; |
- }, |
- |
- /** |
- * @param {?Cache.<!ReplayableResource>} cache |
- * @return {!Object.<string, *>} |
- */ |
- _internalCurrentState: function(cache) |
- { |
- /** |
- * @param {!Resource|*} obj |
- * @return {!Resource|!ReplayableResource|*} |
- */ |
- function maybeToReplayable(obj) |
- { |
- return cache ? Resource.toReplayable(obj, cache) : (Resource.forObject(obj) || obj); |
- } |
- |
- var gl = this.wrappedObject(); |
- var originalErrors = this.getAllErrors(); |
- |
- // Take a full GL state snapshot. |
- var glState = Object.create(null); |
- WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) { |
- glState[parameter] = gl.isEnabled(gl[parameter]); |
- }); |
- for (var i = 0, pname; pname = WebGLRenderingContextResource.StateParameters[i]; ++i) { |
- var enumValue = this._enumValueForName(pname); |
- if (typeof enumValue === "number") |
- glState[pname] = maybeToReplayable(gl.getParameter(enumValue)); |
- } |
- |
- // VERTEX_ATTRIB_ARRAYS |
- var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VERTEX_ATTRIBS)); |
- var vertexAttribParameters = [ |
- "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING", |
- "VERTEX_ATTRIB_ARRAY_ENABLED", |
- "VERTEX_ATTRIB_ARRAY_SIZE", |
- "VERTEX_ATTRIB_ARRAY_STRIDE", |
- "VERTEX_ATTRIB_ARRAY_TYPE", |
- "VERTEX_ATTRIB_ARRAY_NORMALIZED", |
- "CURRENT_VERTEX_ATTRIB", |
- "VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE" // ANGLE_instanced_arrays extension |
- ]; |
- var vertexAttribStates = []; |
- for (var index = 0; index < maxVertexAttribs; ++index) { |
- var state = Object.create(null); |
- for (var i = 0, pname; pname = vertexAttribParameters[i]; ++i) { |
- var enumValue = this._enumValueForName(pname); |
- if (typeof enumValue === "number") |
- state[pname] = maybeToReplayable(gl.getVertexAttrib(index, enumValue)); |
- } |
- state.VERTEX_ATTRIB_ARRAY_POINTER = gl.getVertexAttribOffset(index, gl.VERTEX_ATTRIB_ARRAY_POINTER); |
- vertexAttribStates.push(state); |
- } |
- glState.VERTEX_ATTRIB_ARRAYS = vertexAttribStates; |
- |
- // TEXTURE_UNITS |
- var savedActiveTexture = /** @type {number} */ (gl.getParameter(gl.ACTIVE_TEXTURE)); |
- var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)); |
- var textureUnits = []; |
- for (var i = 0; i < maxTextureImageUnits; ++i) { |
- gl.activeTexture(gl.TEXTURE0 + i); |
- var state = Object.create(null); |
- state.TEXTURE_2D = maybeToReplayable(gl.getParameter(gl.TEXTURE_BINDING_2D)); |
- state.TEXTURE_CUBE_MAP = maybeToReplayable(gl.getParameter(gl.TEXTURE_BINDING_CUBE_MAP)); |
- textureUnits.push(state); |
- } |
- glState.TEXTURE_UNITS = textureUnits; |
- gl.activeTexture(savedActiveTexture); |
- |
- this.restoreErrors(originalErrors); |
- return glState; |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!ReplayableResource>} cache |
- */ |
- _populateReplayableData: function(data, cache) |
- { |
- var gl = this.wrappedObject(); |
- data.originalCanvas = gl.canvas; |
- data.originalContextAttributes = gl.getContextAttributes(); |
- data.extensions = TypeUtils.cloneObject(this._extensions); |
- data.extensionEnums = TypeUtils.cloneObject(this._extensionEnums); |
- data.glState = this._internalCurrentState(cache); |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!Resource>} cache |
- */ |
- _doReplayCalls: function(data, cache) |
- { |
- this._customErrors = null; |
- this._extensions = TypeUtils.cloneObject(data.extensions) || {}; |
- this._extensionEnums = TypeUtils.cloneObject(data.extensionEnums) || {}; |
- |
- var canvas = data.originalCanvas.cloneNode(true); |
- var replayContext = null; |
- var contextIds = ["experimental-webgl", "webkit-3d", "3d"]; |
- for (var i = 0, contextId; contextId = contextIds[i]; ++i) { |
- replayContext = canvas.getContext(contextId, data.originalContextAttributes); |
- if (replayContext) |
- break; |
- } |
- |
- console.assert(replayContext, "Failed to create a WebGLRenderingContext for the replay."); |
- |
- var gl = /** @type {!WebGLRenderingContext} */ (Resource.wrappedObject(replayContext)); |
- this.setWrappedObject(gl); |
- |
- // Enable corresponding WebGL extensions. |
- for (var name in this._extensions) |
- gl.getExtension(name); |
- |
- var glState = data.glState; |
- gl.bindFramebuffer(gl.FRAMEBUFFER, /** @type {!WebGLFramebuffer} */ (ReplayableResource.replay(glState.FRAMEBUFFER_BINDING, cache))); |
- gl.bindRenderbuffer(gl.RENDERBUFFER, /** @type {!WebGLRenderbuffer} */ (ReplayableResource.replay(glState.RENDERBUFFER_BINDING, cache))); |
- |
- // Enable or disable server-side GL capabilities. |
- WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) { |
- console.assert(parameter in glState); |
- if (glState[parameter]) |
- gl.enable(gl[parameter]); |
- else |
- gl.disable(gl[parameter]); |
- }); |
- |
- gl.blendColor(glState.BLEND_COLOR[0], glState.BLEND_COLOR[1], glState.BLEND_COLOR[2], glState.BLEND_COLOR[3]); |
- gl.blendEquationSeparate(glState.BLEND_EQUATION_RGB, glState.BLEND_EQUATION_ALPHA); |
- gl.blendFuncSeparate(glState.BLEND_SRC_RGB, glState.BLEND_DST_RGB, glState.BLEND_SRC_ALPHA, glState.BLEND_DST_ALPHA); |
- gl.clearColor(glState.COLOR_CLEAR_VALUE[0], glState.COLOR_CLEAR_VALUE[1], glState.COLOR_CLEAR_VALUE[2], glState.COLOR_CLEAR_VALUE[3]); |
- gl.clearDepth(glState.DEPTH_CLEAR_VALUE); |
- gl.clearStencil(glState.STENCIL_CLEAR_VALUE); |
- gl.colorMask(glState.COLOR_WRITEMASK[0], glState.COLOR_WRITEMASK[1], glState.COLOR_WRITEMASK[2], glState.COLOR_WRITEMASK[3]); |
- gl.cullFace(glState.CULL_FACE_MODE); |
- gl.depthFunc(glState.DEPTH_FUNC); |
- gl.depthMask(glState.DEPTH_WRITEMASK); |
- gl.depthRange(glState.DEPTH_RANGE[0], glState.DEPTH_RANGE[1]); |
- gl.frontFace(glState.FRONT_FACE); |
- gl.hint(gl.GENERATE_MIPMAP_HINT, glState.GENERATE_MIPMAP_HINT); |
- gl.lineWidth(glState.LINE_WIDTH); |
- |
- var enumValue = this._enumValueForName("FRAGMENT_SHADER_DERIVATIVE_HINT_OES"); |
- if (typeof enumValue === "number") |
- gl.hint(enumValue, glState.FRAGMENT_SHADER_DERIVATIVE_HINT_OES); |
- |
- WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) { |
- gl.pixelStorei(gl[parameter], glState[parameter]); |
- }); |
- |
- gl.polygonOffset(glState.POLYGON_OFFSET_FACTOR, glState.POLYGON_OFFSET_UNITS); |
- gl.sampleCoverage(glState.SAMPLE_COVERAGE_VALUE, glState.SAMPLE_COVERAGE_INVERT); |
- gl.stencilFuncSeparate(gl.FRONT, glState.STENCIL_FUNC, glState.STENCIL_REF, glState.STENCIL_VALUE_MASK); |
- gl.stencilFuncSeparate(gl.BACK, glState.STENCIL_BACK_FUNC, glState.STENCIL_BACK_REF, glState.STENCIL_BACK_VALUE_MASK); |
- gl.stencilOpSeparate(gl.FRONT, glState.STENCIL_FAIL, glState.STENCIL_PASS_DEPTH_FAIL, glState.STENCIL_PASS_DEPTH_PASS); |
- gl.stencilOpSeparate(gl.BACK, glState.STENCIL_BACK_FAIL, glState.STENCIL_BACK_PASS_DEPTH_FAIL, glState.STENCIL_BACK_PASS_DEPTH_PASS); |
- gl.stencilMaskSeparate(gl.FRONT, glState.STENCIL_WRITEMASK); |
- gl.stencilMaskSeparate(gl.BACK, glState.STENCIL_BACK_WRITEMASK); |
- |
- gl.scissor(glState.SCISSOR_BOX[0], glState.SCISSOR_BOX[1], glState.SCISSOR_BOX[2], glState.SCISSOR_BOX[3]); |
- gl.viewport(glState.VIEWPORT[0], glState.VIEWPORT[1], glState.VIEWPORT[2], glState.VIEWPORT[3]); |
- |
- gl.useProgram(/** @type {!WebGLProgram} */ (ReplayableResource.replay(glState.CURRENT_PROGRAM, cache))); |
- |
- // VERTEX_ATTRIB_ARRAYS |
- var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VERTEX_ATTRIBS)); |
- for (var i = 0; i < maxVertexAttribs; ++i) { |
- var state = glState.VERTEX_ATTRIB_ARRAYS[i] || {}; |
- if (state.VERTEX_ATTRIB_ARRAY_ENABLED) |
- gl.enableVertexAttribArray(i); |
- else |
- gl.disableVertexAttribArray(i); |
- if (state.CURRENT_VERTEX_ATTRIB) |
- gl.vertexAttrib4fv(i, state.CURRENT_VERTEX_ATTRIB); |
- var buffer = /** @type {!WebGLBuffer} */ (ReplayableResource.replay(state.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, cache)); |
- if (buffer) { |
- gl.bindBuffer(gl.ARRAY_BUFFER, buffer); |
- gl.vertexAttribPointer(i, state.VERTEX_ATTRIB_ARRAY_SIZE, state.VERTEX_ATTRIB_ARRAY_TYPE, state.VERTEX_ATTRIB_ARRAY_NORMALIZED, state.VERTEX_ATTRIB_ARRAY_STRIDE, state.VERTEX_ATTRIB_ARRAY_POINTER); |
- } |
- } |
- gl.bindBuffer(gl.ARRAY_BUFFER, /** @type {!WebGLBuffer} */ (ReplayableResource.replay(glState.ARRAY_BUFFER_BINDING, cache))); |
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, /** @type {!WebGLBuffer} */ (ReplayableResource.replay(glState.ELEMENT_ARRAY_BUFFER_BINDING, cache))); |
- |
- // TEXTURE_UNITS |
- var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)); |
- for (var i = 0; i < maxTextureImageUnits; ++i) { |
- gl.activeTexture(gl.TEXTURE0 + i); |
- var state = glState.TEXTURE_UNITS[i] || {}; |
- gl.bindTexture(gl.TEXTURE_2D, /** @type {!WebGLTexture} */ (ReplayableResource.replay(state.TEXTURE_2D, cache))); |
- gl.bindTexture(gl.TEXTURE_CUBE_MAP, /** @type {!WebGLTexture} */ (ReplayableResource.replay(state.TEXTURE_CUBE_MAP, cache))); |
- } |
- gl.activeTexture(glState.ACTIVE_TEXTURE); |
- |
- ContextResource.prototype._doReplayCalls.call(this, data, cache); |
- }, |
- |
- /** |
- * @param {!Object|number} target |
- * @return {?Resource} |
- */ |
- currentBinding: function(target) |
- { |
- var resource = Resource.forObject(target); |
- if (resource) |
- return resource; |
- var gl = this.wrappedObject(); |
- var bindingParameter; |
- var bindMethodName; |
- target = +target; // Explicitly convert to a number. |
- var bindMethodTarget = target; |
- switch (target) { |
- case gl.ARRAY_BUFFER: |
- bindingParameter = gl.ARRAY_BUFFER_BINDING; |
- bindMethodName = "bindBuffer"; |
- break; |
- case gl.ELEMENT_ARRAY_BUFFER: |
- bindingParameter = gl.ELEMENT_ARRAY_BUFFER_BINDING; |
- bindMethodName = "bindBuffer"; |
- break; |
- case gl.TEXTURE_2D: |
- bindingParameter = gl.TEXTURE_BINDING_2D; |
- bindMethodName = "bindTexture"; |
- break; |
- case gl.TEXTURE_CUBE_MAP: |
- case gl.TEXTURE_CUBE_MAP_POSITIVE_X: |
- case gl.TEXTURE_CUBE_MAP_NEGATIVE_X: |
- case gl.TEXTURE_CUBE_MAP_POSITIVE_Y: |
- case gl.TEXTURE_CUBE_MAP_NEGATIVE_Y: |
- case gl.TEXTURE_CUBE_MAP_POSITIVE_Z: |
- case gl.TEXTURE_CUBE_MAP_NEGATIVE_Z: |
- bindingParameter = gl.TEXTURE_BINDING_CUBE_MAP; |
- bindMethodTarget = gl.TEXTURE_CUBE_MAP; |
- bindMethodName = "bindTexture"; |
- break; |
- case gl.FRAMEBUFFER: |
- bindingParameter = gl.FRAMEBUFFER_BINDING; |
- bindMethodName = "bindFramebuffer"; |
- break; |
- case gl.RENDERBUFFER: |
- bindingParameter = gl.RENDERBUFFER_BINDING; |
- bindMethodName = "bindRenderbuffer"; |
- break; |
- default: |
- console.error("ASSERT_NOT_REACHED: unknown binding target " + target); |
- return null; |
- } |
- resource = Resource.forObject(gl.getParameter(bindingParameter)); |
- if (resource) |
- resource.pushBinding(bindMethodTarget, bindMethodName); |
- return resource; |
- }, |
- |
- /** |
- * @override |
- * @param {!Call} call |
- */ |
- onCallReplayed: function(call) |
- { |
- var functionName = call.functionName(); |
- var args = call.args(); |
- switch (functionName) { |
- case "bindBuffer": |
- case "bindFramebuffer": |
- case "bindRenderbuffer": |
- case "bindTexture": |
- // Update BINDING state for Resources in the replay world. |
- var resource = Resource.forObject(args[1]); |
- if (resource) |
- resource.pushBinding(args[0], functionName); |
- break; |
- case "getExtension": |
- this.registerWebGLExtension(args[0], /** @type {!Object} */ (call.result())); |
- break; |
- case "bufferData": |
- var resource = /** @type {!WebGLBufferResource} */ (this.currentBinding(args[0])); |
- if (resource) |
- resource.pushCall_bufferData(call); |
- break; |
- case "bufferSubData": |
- var resource = /** @type {!WebGLBufferResource} */ (this.currentBinding(args[0])); |
- if (resource) |
- resource.pushCall_bufferSubData(call); |
- break; |
- } |
- }, |
- |
- /** |
- * @override |
- * @return {!Object.<string, !Function>} |
- */ |
- _customWrapFunctions: function() |
- { |
- var wrapFunctions = WebGLRenderingContextResource._wrapFunctions; |
- if (!wrapFunctions) { |
- wrapFunctions = Object.create(null); |
- |
- wrapFunctions["createBuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLBufferResource, "WebGLBuffer"); |
- wrapFunctions["createShader"] = Resource.WrapFunction.resourceFactoryMethod(WebGLShaderResource, "WebGLShader"); |
- wrapFunctions["createProgram"] = Resource.WrapFunction.resourceFactoryMethod(WebGLProgramResource, "WebGLProgram"); |
- wrapFunctions["createTexture"] = Resource.WrapFunction.resourceFactoryMethod(WebGLTextureResource, "WebGLTexture"); |
- wrapFunctions["createFramebuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLFramebufferResource, "WebGLFramebuffer"); |
- wrapFunctions["createRenderbuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLRenderbufferResource, "WebGLRenderbuffer"); |
- wrapFunctions["getUniformLocation"] = Resource.WrapFunction.resourceFactoryMethod(WebGLUniformLocationResource, "WebGLUniformLocation"); |
- |
- stateModifyingWrapFunction("bindAttribLocation"); |
- stateModifyingWrapFunction("compileShader"); |
- stateModifyingWrapFunction("detachShader"); |
- stateModifyingWrapFunction("linkProgram"); |
- stateModifyingWrapFunction("shaderSource"); |
- stateModifyingWrapFunction("bufferData", WebGLBufferResource.prototype.pushCall_bufferData); |
- stateModifyingWrapFunction("bufferSubData", WebGLBufferResource.prototype.pushCall_bufferSubData); |
- stateModifyingWrapFunction("compressedTexImage2D"); |
- stateModifyingWrapFunction("compressedTexSubImage2D"); |
- stateModifyingWrapFunction("copyTexImage2D", WebGLTextureResource.prototype.pushCall_copyTexImage2D); |
- stateModifyingWrapFunction("copyTexSubImage2D", WebGLTextureResource.prototype.pushCall_copyTexImage2D); |
- stateModifyingWrapFunction("generateMipmap"); |
- stateModifyingWrapFunction("texImage2D"); |
- stateModifyingWrapFunction("texSubImage2D"); |
- stateModifyingWrapFunction("texParameterf", WebGLTextureResource.prototype.pushCall_texParameter); |
- stateModifyingWrapFunction("texParameteri", WebGLTextureResource.prototype.pushCall_texParameter); |
- stateModifyingWrapFunction("renderbufferStorage"); |
- |
- /** @this {Resource.WrapFunction} */ |
- wrapFunctions["getError"] = function() |
- { |
- var gl = /** @type {!WebGLRenderingContext} */ (this._originalObject); |
- var error = this.result(); |
- if (error !== gl.NO_ERROR) |
- this._resource.clearError(error); |
- else { |
- error = this._resource.nextError(); |
- if (error !== gl.NO_ERROR) |
- this.overrideResult(error); |
- } |
- } |
- |
- /** |
- * @param {string} name |
- * @this {Resource.WrapFunction} |
- */ |
- wrapFunctions["getExtension"] = function(name) |
- { |
- this._resource.registerWebGLExtension(name, this.result()); |
- } |
- |
- // |
- // Register bound WebGL resources. |
- // |
- |
- /** |
- * @param {!WebGLProgram} program |
- * @param {!WebGLShader} shader |
- * @this {Resource.WrapFunction} |
- */ |
- wrapFunctions["attachShader"] = function(program, shader) |
- { |
- var resource = this._resource.currentBinding(program); |
- if (resource) { |
- resource.pushCall(this.call()); |
- var shaderResource = /** @type {!WebGLShaderResource} */ (Resource.forObject(shader)); |
- if (shaderResource) { |
- var shaderType = shaderResource.type(); |
- resource._registerBoundResource("__attachShader_" + shaderType, shaderResource); |
- } |
- } |
- } |
- /** |
- * @param {number} target |
- * @param {number} attachment |
- * @param {number} objectTarget |
- * @param {!WebGLRenderbuffer|!WebGLTexture} obj |
- * @this {Resource.WrapFunction} |
- */ |
- wrapFunctions["framebufferRenderbuffer"] = wrapFunctions["framebufferTexture2D"] = function(target, attachment, objectTarget, obj) |
- { |
- var resource = this._resource.currentBinding(target); |
- if (resource) { |
- resource.pushCall(this.call()); |
- resource._registerBoundResource("__framebufferAttachmentObjectName", obj); |
- } |
- } |
- /** |
- * @param {number} target |
- * @param {!Object} obj |
- * @this {Resource.WrapFunction} |
- */ |
- wrapFunctions["bindBuffer"] = wrapFunctions["bindFramebuffer"] = wrapFunctions["bindRenderbuffer"] = function(target, obj) |
- { |
- this._resource.currentBinding(target); // To call WebGLBoundResource.prototype.pushBinding(). |
- this._resource._registerBoundResource("__bindBuffer_" + target, obj); |
- } |
- /** |
- * @param {number} target |
- * @param {!WebGLTexture} obj |
- * @this {Resource.WrapFunction} |
- */ |
- wrapFunctions["bindTexture"] = function(target, obj) |
- { |
- this._resource.currentBinding(target); // To call WebGLBoundResource.prototype.pushBinding(). |
- var gl = /** @type {!WebGLRenderingContext} */ (this._originalObject); |
- var currentTextureBinding = /** @type {number} */ (gl.getParameter(gl.ACTIVE_TEXTURE)); |
- this._resource._registerBoundResource("__bindTexture_" + target + "_" + currentTextureBinding, obj); |
- } |
- /** |
- * @param {!WebGLProgram} program |
- * @this {Resource.WrapFunction} |
- */ |
- wrapFunctions["useProgram"] = function(program) |
- { |
- this._resource._registerBoundResource("__useProgram", program); |
- } |
- /** |
- * @param {number} index |
- * @this {Resource.WrapFunction} |
- */ |
- wrapFunctions["vertexAttribPointer"] = function(index) |
- { |
- var gl = /** @type {!WebGLRenderingContext} */ (this._originalObject); |
- this._resource._registerBoundResource("__vertexAttribPointer_" + index, gl.getParameter(gl.ARRAY_BUFFER_BINDING)); |
- } |
- |
- WebGLRenderingContextResource._wrapFunctions = wrapFunctions; |
- } |
- |
- /** |
- * @param {string} methodName |
- * @param {function(this:Resource, !Call)=} pushCallFunc |
- */ |
- function stateModifyingWrapFunction(methodName, pushCallFunc) |
- { |
- if (pushCallFunc) { |
- /** |
- * @param {!Object|number} target |
- * @this {Resource.WrapFunction} |
- */ |
- wrapFunctions[methodName] = function(target) |
- { |
- var resource = this._resource.currentBinding(target); |
- if (resource) |
- pushCallFunc.call(resource, this.call()); |
- } |
- } else { |
- /** |
- * @param {!Object|number} target |
- * @this {Resource.WrapFunction} |
- */ |
- wrapFunctions[methodName] = function(target) |
- { |
- var resource = this._resource.currentBinding(target); |
- if (resource) |
- resource.pushCall(this.call()); |
- } |
- } |
- } |
- |
- return wrapFunctions; |
- }, |
- |
- __proto__: ContextResource.prototype |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-// 2D Canvas |
-//////////////////////////////////////////////////////////////////////////////// |
- |
-/** |
- * @constructor |
- * @extends {ContextResource} |
- * @param {!CanvasRenderingContext2D} context |
- */ |
-function CanvasRenderingContext2DResource(context) |
-{ |
- ContextResource.call(this, context, "CanvasRenderingContext2D"); |
-} |
- |
-/** |
- * @const |
- * @type {!Array.<string>} |
- */ |
-CanvasRenderingContext2DResource.AttributeProperties = [ |
- "strokeStyle", |
- "fillStyle", |
- "globalAlpha", |
- "lineWidth", |
- "lineCap", |
- "lineJoin", |
- "miterLimit", |
- "shadowOffsetX", |
- "shadowOffsetY", |
- "shadowBlur", |
- "shadowColor", |
- "globalCompositeOperation", |
- "font", |
- "textAlign", |
- "textBaseline", |
- "lineDashOffset", |
- "imageSmoothingEnabled", |
- "webkitLineDash", |
- "webkitLineDashOffset" |
-]; |
- |
-/** |
- * @const |
- * @type {!Array.<string>} |
- */ |
-CanvasRenderingContext2DResource.PathMethods = [ |
- "beginPath", |
- "moveTo", |
- "closePath", |
- "lineTo", |
- "quadraticCurveTo", |
- "bezierCurveTo", |
- "arcTo", |
- "arc", |
- "rect", |
- "ellipse" |
-]; |
- |
-/** |
- * @const |
- * @type {!Array.<string>} |
- */ |
-CanvasRenderingContext2DResource.TransformationMatrixMethods = [ |
- "scale", |
- "rotate", |
- "translate", |
- "transform", |
- "setTransform", |
- "resetTransform" |
-]; |
- |
-/** |
- * @const |
- * @type {!Object.<string, boolean>} |
- */ |
-CanvasRenderingContext2DResource.DrawingMethods = TypeUtils.createPrefixedPropertyNamesSet([ |
- "clearRect", |
- "drawImage", |
- "drawImageFromRect", |
- "drawCustomFocusRing", |
- "drawFocusIfNeeded", |
- "fill", |
- "fillRect", |
- "fillText", |
- "putImageData", |
- "putImageDataHD", |
- "stroke", |
- "strokeRect", |
- "strokeText" |
-]); |
- |
-CanvasRenderingContext2DResource.prototype = { |
- /** |
- * @override (overrides @return type) |
- * @return {!CanvasRenderingContext2D} |
- */ |
- wrappedObject: function() |
- { |
- return this._wrappedObject; |
- }, |
- |
- /** |
- * @override |
- * @return {string} |
- */ |
- toDataURL: function() |
- { |
- return this.wrappedObject().canvas.toDataURL(); |
- }, |
- |
- /** |
- * @override |
- * @return {!Array.<!TypeUtils.InternalResourceStateDescriptor>} |
- */ |
- currentState: function() |
- { |
- var result = []; |
- var state = this._internalCurrentState(null); |
- for (var pname in state) |
- result.push({ name: pname, value: state[pname] }); |
- result.push({ name: "context", value: this.contextResource() }); |
- return result; |
- }, |
- |
- /** |
- * @param {?Cache.<!ReplayableResource>} cache |
- * @return {!Object.<string, *>} |
- */ |
- _internalCurrentState: function(cache) |
- { |
- /** |
- * @param {!Resource|*} obj |
- * @return {!Resource|!ReplayableResource|*} |
- */ |
- function maybeToReplayable(obj) |
- { |
- return cache ? Resource.toReplayable(obj, cache) : (Resource.forObject(obj) || obj); |
- } |
- |
- var ctx = this.wrappedObject(); |
- var state = Object.create(null); |
- CanvasRenderingContext2DResource.AttributeProperties.forEach(function(attribute) { |
- if (attribute in ctx) |
- state[attribute] = maybeToReplayable(ctx[attribute]); |
- }); |
- if (ctx.getLineDash) |
- state.lineDash = ctx.getLineDash(); |
- return state; |
- }, |
- |
- /** |
- * @param {?Object.<string, *>} state |
- * @param {!Cache.<!Resource>} cache |
- */ |
- _applyAttributesState: function(state, cache) |
- { |
- if (!state) |
- return; |
- var ctx = this.wrappedObject(); |
- for (var attribute in state) { |
- if (attribute === "lineDash") { |
- if (ctx.setLineDash) |
- ctx.setLineDash(/** @type {!Array.<number>} */ (state[attribute])); |
- } else |
- ctx[attribute] = ReplayableResource.replay(state[attribute], cache); |
- } |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!ReplayableResource>} cache |
- */ |
- _populateReplayableData: function(data, cache) |
- { |
- var ctx = this.wrappedObject(); |
- // FIXME: Convert resources in the state (CanvasGradient, CanvasPattern) to Replayable. |
- data.currentAttributes = this._internalCurrentState(null); |
- data.originalCanvasCloned = TypeUtils.cloneIntoCanvas(/** @type {!HTMLCanvasElement} */ (ctx.canvas)); |
- if (ctx.getContextAttributes) |
- data.originalContextAttributes = ctx.getContextAttributes(); |
- }, |
- |
- /** |
- * @override |
- * @param {!Object} data |
- * @param {!Cache.<!Resource>} cache |
- */ |
- _doReplayCalls: function(data, cache) |
- { |
- var canvas = TypeUtils.cloneIntoCanvas(data.originalCanvasCloned); |
- var ctx = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d", data.originalContextAttributes))); |
- this.setWrappedObject(ctx); |
- |
- for (var i = 0, n = data.calls.length; i < n; ++i) { |
- var replayableCall = /** @type {!ReplayableCall} */ (data.calls[i]); |
- if (replayableCall.functionName() === "save") |
- this._applyAttributesState(replayableCall.attachment("canvas2dAttributesState"), cache); |
- this._calls.push(replayableCall.replay(cache)); |
- } |
- this._applyAttributesState(data.currentAttributes, cache); |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- pushCall_setOrResetTransform: function(call) |
- { |
- var saveCallIndex = this._lastIndexOfMatchingSaveCall(); |
- var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods); |
- index = Math.max(index, saveCallIndex); |
- if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1)) |
- this._removeAllObsoleteCallsFromLog(); |
- this.pushCall(call); |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- pushCall_beginPath: function(call) |
- { |
- var index = this._lastIndexOfAnyCall(["clip"]); |
- if (this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1)) |
- this._removeAllObsoleteCallsFromLog(); |
- this.pushCall(call); |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- pushCall_save: function(call) |
- { |
- // FIXME: Convert resources in the state (CanvasGradient, CanvasPattern) to Replayable. |
- call.setAttachment("canvas2dAttributesState", this._internalCurrentState(null)); |
- this.pushCall(call); |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- pushCall_restore: function(call) |
- { |
- var lastIndexOfSave = this._lastIndexOfMatchingSaveCall(); |
- if (lastIndexOfSave === -1) |
- return; |
- this._calls[lastIndexOfSave].setAttachment("canvas2dAttributesState", null); // No longer needed, free memory. |
- |
- var modified = false; |
- if (this._removeCallsFromLog(["clip"], lastIndexOfSave + 1)) |
- modified = true; |
- |
- var lastIndexOfAnyPathMethod = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods); |
- var index = Math.max(lastIndexOfSave, lastIndexOfAnyPathMethod); |
- if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1)) |
- modified = true; |
- |
- if (modified) |
- this._removeAllObsoleteCallsFromLog(); |
- |
- var lastCall = this._calls[this._calls.length - 1]; |
- if (lastCall && lastCall.functionName() === "save") |
- this._calls.pop(); |
- else |
- this.pushCall(call); |
- }, |
- |
- /** |
- * @param {number=} fromIndex |
- * @return {number} |
- */ |
- _lastIndexOfMatchingSaveCall: function(fromIndex) |
- { |
- if (typeof fromIndex !== "number") |
- fromIndex = this._calls.length - 1; |
- else |
- fromIndex = Math.min(fromIndex, this._calls.length - 1); |
- var stackDepth = 1; |
- for (var i = fromIndex; i >= 0; --i) { |
- var functionName = this._calls[i].functionName(); |
- if (functionName === "restore") |
- ++stackDepth; |
- else if (functionName === "save") { |
- --stackDepth; |
- if (!stackDepth) |
- return i; |
- } |
- } |
- return -1; |
- }, |
- |
- /** |
- * @param {!Array.<string>} functionNames |
- * @param {number=} fromIndex |
- * @return {number} |
- */ |
- _lastIndexOfAnyCall: function(functionNames, fromIndex) |
- { |
- if (typeof fromIndex !== "number") |
- fromIndex = this._calls.length - 1; |
- else |
- fromIndex = Math.min(fromIndex, this._calls.length - 1); |
- for (var i = fromIndex; i >= 0; --i) { |
- if (functionNames.indexOf(this._calls[i].functionName()) !== -1) |
- return i; |
- } |
- return -1; |
- }, |
- |
- _removeAllObsoleteCallsFromLog: function() |
- { |
- // Remove all PATH methods between clip() and beginPath() calls. |
- var lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"]); |
- while (lastIndexOfBeginPath !== -1) { |
- var index = this._lastIndexOfAnyCall(["clip"], lastIndexOfBeginPath - 1); |
- this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1, lastIndexOfBeginPath); |
- lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"], index - 1); |
- } |
- |
- // Remove all TRASFORMATION MATRIX methods before restore(), setTransform() or resetTransform() but after any PATH or corresponding save() method. |
- var lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform", "resetTransform"]); |
- while (lastRestore !== -1) { |
- var saveCallIndex = this._lastIndexOfMatchingSaveCall(lastRestore - 1); |
- var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods, lastRestore - 1); |
- index = Math.max(index, saveCallIndex); |
- this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1, lastRestore); |
- lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform", "resetTransform"], index - 1); |
- } |
- |
- // Remove all save-restore consecutive pairs. |
- var restoreCalls = 0; |
- for (var i = this._calls.length - 1; i >= 0; --i) { |
- var functionName = this._calls[i].functionName(); |
- if (functionName === "restore") { |
- ++restoreCalls; |
- continue; |
- } |
- if (functionName === "save" && restoreCalls > 0) { |
- var saveCallIndex = i; |
- for (var j = i - 1; j >= 0 && i - j < restoreCalls; --j) { |
- if (this._calls[j].functionName() === "save") |
- saveCallIndex = j; |
- else |
- break; |
- } |
- this._calls.splice(saveCallIndex, (i - saveCallIndex + 1) * 2); |
- i = saveCallIndex; |
- } |
- restoreCalls = 0; |
- } |
- }, |
- |
- /** |
- * @param {!Array.<string>} functionNames |
- * @param {number} fromIndex |
- * @param {number=} toIndex |
- * @return {boolean} |
- */ |
- _removeCallsFromLog: function(functionNames, fromIndex, toIndex) |
- { |
- var oldLength = this._calls.length; |
- if (typeof toIndex !== "number") |
- toIndex = oldLength; |
- else |
- toIndex = Math.min(toIndex, oldLength); |
- var newIndex = Math.min(fromIndex, oldLength); |
- for (var i = newIndex; i < toIndex; ++i) { |
- var call = this._calls[i]; |
- if (functionNames.indexOf(call.functionName()) === -1) |
- this._calls[newIndex++] = call; |
- } |
- if (newIndex >= toIndex) |
- return false; |
- this._calls.splice(newIndex, toIndex - newIndex); |
- return true; |
- }, |
- |
- /** |
- * @override |
- * @return {!Object.<string, !Function>} |
- */ |
- _customWrapFunctions: function() |
- { |
- var wrapFunctions = CanvasRenderingContext2DResource._wrapFunctions; |
- if (!wrapFunctions) { |
- wrapFunctions = Object.create(null); |
- |
- wrapFunctions["createLinearGradient"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasGradient"); |
- wrapFunctions["createRadialGradient"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasGradient"); |
- wrapFunctions["createPattern"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasPattern"); |
- |
- for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.TransformationMatrixMethods[i]; ++i) |
- stateModifyingWrapFunction(methodName, (methodName === "setTransform" || methodName === "resetTransform") ? this.pushCall_setOrResetTransform : undefined); |
- for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.PathMethods[i]; ++i) |
- stateModifyingWrapFunction(methodName, methodName === "beginPath" ? this.pushCall_beginPath : undefined); |
- |
- stateModifyingWrapFunction("save", this.pushCall_save); |
- stateModifyingWrapFunction("restore", this.pushCall_restore); |
- stateModifyingWrapFunction("clip"); |
- |
- CanvasRenderingContext2DResource._wrapFunctions = wrapFunctions; |
- } |
- |
- /** |
- * @param {string} methodName |
- * @param {function(this:Resource, !Call)=} func |
- */ |
- function stateModifyingWrapFunction(methodName, func) |
- { |
- if (func) { |
- /** @this {Resource.WrapFunction} */ |
- wrapFunctions[methodName] = function() |
- { |
- func.call(this._resource, this.call()); |
- } |
- } else { |
- /** @this {Resource.WrapFunction} */ |
- wrapFunctions[methodName] = function() |
- { |
- this._resource.pushCall(this.call()); |
- } |
- } |
- } |
- |
- return wrapFunctions; |
- }, |
- |
- __proto__: ContextResource.prototype |
-} |
- |
-/** |
- * @constructor |
- * @param {!Object.<string, boolean>=} drawingMethodNames |
- */ |
-function CallFormatter(drawingMethodNames) |
-{ |
- this._drawingMethodNames = drawingMethodNames || Object.create(null); |
-} |
- |
-CallFormatter.prototype = { |
- /** |
- * @param {!ReplayableCall} replayableCall |
- * @param {string=} objectGroup |
- * @return {!Object} |
- */ |
- formatCall: function(replayableCall, objectGroup) |
- { |
- var result = {}; |
- var functionName = replayableCall.functionName(); |
- if (functionName) { |
- result.functionName = functionName; |
- result.arguments = []; |
- var args = replayableCall.args(); |
- for (var i = 0, n = args.length; i < n; ++i) |
- result.arguments.push(this.formatValue(args[i], objectGroup)); |
- if (replayableCall.result() !== undefined) |
- result.result = this.formatValue(replayableCall.result(), objectGroup); |
- if (this._drawingMethodNames[functionName]) |
- result.isDrawingCall = true; |
- } else { |
- result.property = replayableCall.propertyName(); |
- result.value = this.formatValue(replayableCall.propertyValue(), objectGroup); |
- } |
- return result; |
- }, |
- |
- /** |
- * @param {*} value |
- * @param {string=} objectGroup |
- * @return {!CanvasAgent.CallArgument} |
- */ |
- formatValue: function(value, objectGroup) |
- { |
- if (value instanceof Resource || value instanceof ReplayableResource) { |
- return { |
- description: value.description(), |
- resourceId: CallFormatter.makeStringResourceId(value.id()) |
- }; |
- } |
- |
- var doNotBind = !objectGroup; |
- var remoteObject = injectedScript.wrapObjectForModule(value, objectGroup || "", doNotBind); |
- var description = remoteObject.description || ("" + value); |
- |
- var result = { |
- description: description, |
- type: /** @type {!CanvasAgent.CallArgumentType} */ (remoteObject.type) |
- }; |
- if (remoteObject.subtype) |
- result.subtype = /** @type {!CanvasAgent.CallArgumentSubtype} */ (remoteObject.subtype); |
- if (remoteObject.objectId && !doNotBind) |
- result.remoteObject = remoteObject; |
- return result; |
- }, |
- |
- /** |
- * @param {string} name |
- * @return {?string} |
- */ |
- enumValueForName: function(name) |
- { |
- return null; |
- }, |
- |
- /** |
- * @param {number} value |
- * @param {!Array.<string>=} options |
- * @return {?string} |
- */ |
- enumNameForValue: function(value, options) |
- { |
- return null; |
- }, |
- |
- /** |
- * @param {!Array.<!TypeUtils.InternalResourceStateDescriptor>} descriptors |
- * @param {string=} objectGroup |
- * @return {!Array.<!CanvasAgent.ResourceStateDescriptor>} |
- */ |
- formatResourceStateDescriptors: function(descriptors, objectGroup) |
- { |
- var result = []; |
- for (var i = 0, n = descriptors.length; i < n; ++i) { |
- var d = descriptors[i]; |
- var item; |
- if (d.values) |
- item = { name: d.name, values: this.formatResourceStateDescriptors(d.values, objectGroup) }; |
- else { |
- item = { name: d.name, value: this.formatValue(d.value, objectGroup) }; |
- if (d.valueIsEnum && typeof d.value === "number") { |
- var enumName = this.enumNameForValue(d.value); |
- if (enumName) |
- item.value.enumName = enumName; |
- } |
- } |
- var enumValue = this.enumValueForName(d.name); |
- if (enumValue) |
- item.enumValueForName = enumValue; |
- if (d.isArray) |
- item.isArray = true; |
- result.push(item); |
- } |
- return result; |
- } |
-} |
- |
-/** |
- * @const |
- * @type {!Object.<string, !CallFormatter>} |
- */ |
-CallFormatter._formatters = {}; |
- |
-/** |
- * @param {string} resourceName |
- * @param {!CallFormatter} callFormatter |
- */ |
-CallFormatter.register = function(resourceName, callFormatter) |
-{ |
- CallFormatter._formatters[resourceName] = callFormatter; |
-} |
- |
-/** |
- * @param {!Resource|!ReplayableResource} resource |
- * @return {!CallFormatter} |
- */ |
-CallFormatter.forResource = function(resource) |
-{ |
- var formatter = CallFormatter._formatters[resource.name()]; |
- if (!formatter) { |
- var contextResource = resource.contextResource(); |
- formatter = (contextResource && CallFormatter._formatters[contextResource.name()]) || new CallFormatter(); |
- } |
- return formatter; |
-} |
- |
-/** |
- * @param {number} resourceId |
- * @return {!CanvasAgent.ResourceId} |
- */ |
-CallFormatter.makeStringResourceId = function(resourceId) |
-{ |
- return "{\"injectedScriptId\":" + injectedScriptId + ",\"resourceId\":" + resourceId + "}"; |
-} |
- |
-/** |
- * @constructor |
- * @extends {CallFormatter} |
- * @param {!Object.<string, boolean>} drawingMethodNames |
- */ |
-function WebGLCallFormatter(drawingMethodNames) |
-{ |
- CallFormatter.call(this, drawingMethodNames); |
-} |
- |
-/** |
- * NOTE: The code below is generated from the IDL file by the script: |
- * /devtools/scripts/check_injected_webgl_calls_info.py |
- * |
- * @type {!Array.<{aname: string, enum: (!Array.<number>|undefined), bitfield: (!Array.<number>|undefined), returnType: string, hints: (!Array.<string>|undefined)}>} |
- */ |
-WebGLCallFormatter.EnumsInfo = [ |
- {"aname": "activeTexture", "enum": [0]}, |
- {"aname": "bindBuffer", "enum": [0]}, |
- {"aname": "bindFramebuffer", "enum": [0]}, |
- {"aname": "bindRenderbuffer", "enum": [0]}, |
- {"aname": "bindTexture", "enum": [0]}, |
- {"aname": "blendEquation", "enum": [0]}, |
- {"aname": "blendEquationSeparate", "enum": [0, 1]}, |
- {"aname": "blendFunc", "enum": [0, 1], "hints": ["ZERO", "ONE"]}, |
- {"aname": "blendFuncSeparate", "enum": [0, 1, 2, 3], "hints": ["ZERO", "ONE"]}, |
- {"aname": "bufferData", "enum": [0, 2]}, |
- {"aname": "bufferSubData", "enum": [0]}, |
- {"aname": "checkFramebufferStatus", "enum": [0], "returnType": "enum"}, |
- {"aname": "clear", "bitfield": [0]}, |
- {"aname": "compressedTexImage2D", "enum": [0, 2]}, |
- {"aname": "compressedTexSubImage2D", "enum": [0, 6]}, |
- {"aname": "copyTexImage2D", "enum": [0, 2]}, |
- {"aname": "copyTexSubImage2D", "enum": [0]}, |
- {"aname": "createShader", "enum": [0]}, |
- {"aname": "cullFace", "enum": [0]}, |
- {"aname": "depthFunc", "enum": [0]}, |
- {"aname": "disable", "enum": [0]}, |
- {"aname": "drawArrays", "enum": [0], "hints": ["POINTS", "LINES"]}, |
- {"aname": "drawElements", "enum": [0, 2], "hints": ["POINTS", "LINES"]}, |
- {"aname": "enable", "enum": [0]}, |
- {"aname": "framebufferRenderbuffer", "enum": [0, 1, 2]}, |
- {"aname": "framebufferTexture2D", "enum": [0, 1, 2]}, |
- {"aname": "frontFace", "enum": [0]}, |
- {"aname": "generateMipmap", "enum": [0]}, |
- {"aname": "getBufferParameter", "enum": [0, 1]}, |
- {"aname": "getError", "hints": ["NO_ERROR"], "returnType": "enum"}, |
- {"aname": "getFramebufferAttachmentParameter", "enum": [0, 1, 2]}, |
- {"aname": "getParameter", "enum": [0]}, |
- {"aname": "getProgramParameter", "enum": [1]}, |
- {"aname": "getRenderbufferParameter", "enum": [0, 1]}, |
- {"aname": "getShaderParameter", "enum": [1]}, |
- {"aname": "getShaderPrecisionFormat", "enum": [0, 1]}, |
- {"aname": "getTexParameter", "enum": [0, 1], "returnType": "enum"}, |
- {"aname": "getVertexAttrib", "enum": [1]}, |
- {"aname": "getVertexAttribOffset", "enum": [1]}, |
- {"aname": "hint", "enum": [0, 1]}, |
- {"aname": "isEnabled", "enum": [0]}, |
- {"aname": "pixelStorei", "enum": [0]}, |
- {"aname": "readPixels", "enum": [4, 5]}, |
- {"aname": "renderbufferStorage", "enum": [0, 1]}, |
- {"aname": "stencilFunc", "enum": [0]}, |
- {"aname": "stencilFuncSeparate", "enum": [0, 1]}, |
- {"aname": "stencilMaskSeparate", "enum": [0]}, |
- {"aname": "stencilOp", "enum": [0, 1, 2], "hints": ["ZERO", "ONE"]}, |
- {"aname": "stencilOpSeparate", "enum": [0, 1, 2, 3], "hints": ["ZERO", "ONE"]}, |
- {"aname": "texParameterf", "enum": [0, 1, 2]}, |
- {"aname": "texParameteri", "enum": [0, 1, 2]}, |
- {"aname": "texImage2D", "enum": [0, 2, 6, 7]}, |
- {"aname": "texImage2D", "enum": [0, 2, 3, 4]}, |
- {"aname": "texSubImage2D", "enum": [0, 6, 7]}, |
- {"aname": "texSubImage2D", "enum": [0, 4, 5]}, |
- {"aname": "vertexAttribPointer", "enum": [2]} |
-]; |
- |
-WebGLCallFormatter.prototype = { |
- /** |
- * @override |
- * @param {!ReplayableCall} replayableCall |
- * @param {string=} objectGroup |
- * @return {!Object} |
- */ |
- formatCall: function(replayableCall, objectGroup) |
- { |
- var result = CallFormatter.prototype.formatCall.call(this, replayableCall, objectGroup); |
- if (!result.functionName) |
- return result; |
- var enumsInfo = this._findEnumsInfo(replayableCall); |
- if (!enumsInfo) |
- return result; |
- var enumArgsIndexes = enumsInfo["enum"] || []; |
- for (var i = 0, n = enumArgsIndexes.length; i < n; ++i) { |
- var index = enumArgsIndexes[i]; |
- var callArgument = result.arguments[index]; |
- this._formatEnumValue(callArgument, enumsInfo["hints"]); |
- } |
- var bitfieldArgsIndexes = enumsInfo["bitfield"] || []; |
- for (var i = 0, n = bitfieldArgsIndexes.length; i < n; ++i) { |
- var index = bitfieldArgsIndexes[i]; |
- var callArgument = result.arguments[index]; |
- this._formatEnumBitmaskValue(callArgument, enumsInfo["hints"]); |
- } |
- if (enumsInfo.returnType === "enum") |
- this._formatEnumValue(result.result, enumsInfo["hints"]); |
- else if (enumsInfo.returnType === "bitfield") |
- this._formatEnumBitmaskValue(result.result, enumsInfo["hints"]); |
- return result; |
- }, |
- |
- /** |
- * @override |
- * @param {string} name |
- * @return {?string} |
- */ |
- enumValueForName: function(name) |
- { |
- this._initialize(); |
- if (name in this._enumNameToValue) |
- return "" + this._enumNameToValue[name]; |
- return null; |
- }, |
- |
- /** |
- * @override |
- * @param {number} value |
- * @param {!Array.<string>=} options |
- * @return {?string} |
- */ |
- enumNameForValue: function(value, options) |
- { |
- this._initialize(); |
- options = options || []; |
- for (var i = 0, n = options.length; i < n; ++i) { |
- if (this._enumNameToValue[options[i]] === value) |
- return options[i]; |
- } |
- var names = this._enumValueToNames[value]; |
- if (!names || names.length !== 1) |
- return null; |
- return names[0]; |
- }, |
- |
- /** |
- * @param {!ReplayableCall} replayableCall |
- * @return {?Object} |
- */ |
- _findEnumsInfo: function(replayableCall) |
- { |
- function findMaxArgumentIndex(enumsInfo) |
- { |
- var result = -1; |
- var enumArgsIndexes = enumsInfo["enum"] || []; |
- for (var i = 0, n = enumArgsIndexes.length; i < n; ++i) |
- result = Math.max(result, enumArgsIndexes[i]); |
- var bitfieldArgsIndexes = enumsInfo["bitfield"] || []; |
- for (var i = 0, n = bitfieldArgsIndexes.length; i < n; ++i) |
- result = Math.max(result, bitfieldArgsIndexes[i]); |
- return result; |
- } |
- |
- var result = null; |
- for (var i = 0, enumsInfo; enumsInfo = WebGLCallFormatter.EnumsInfo[i]; ++i) { |
- if (enumsInfo["aname"] !== replayableCall.functionName()) |
- continue; |
- var argsCount = replayableCall.args().length; |
- var maxArgumentIndex = findMaxArgumentIndex(enumsInfo); |
- if (maxArgumentIndex >= argsCount) |
- continue; |
- // To resolve ambiguity (see texImage2D, texSubImage2D) choose description with max argument indexes. |
- if (!result || findMaxArgumentIndex(result) < maxArgumentIndex) |
- result = enumsInfo; |
- } |
- return result; |
- }, |
- |
- /** |
- * @param {?CanvasAgent.CallArgument|undefined} callArgument |
- * @param {!Array.<string>=} options |
- */ |
- _formatEnumValue: function(callArgument, options) |
- { |
- if (!callArgument || isNaN(callArgument.description)) |
- return; |
- this._initialize(); |
- var value = +callArgument.description; |
- var enumName = this.enumNameForValue(value, options); |
- if (enumName) |
- callArgument.enumName = enumName; |
- }, |
- |
- /** |
- * @param {?CanvasAgent.CallArgument|undefined} callArgument |
- * @param {!Array.<string>=} options |
- */ |
- _formatEnumBitmaskValue: function(callArgument, options) |
- { |
- if (!callArgument || isNaN(callArgument.description)) |
- return; |
- this._initialize(); |
- var value = +callArgument.description; |
- options = options || []; |
- /** @type {!Array.<string>} */ |
- var result = []; |
- for (var i = 0, n = options.length; i < n; ++i) { |
- var bitValue = this._enumNameToValue[options[i]] || 0; |
- if (value & bitValue) { |
- result.push(options[i]); |
- value &= ~bitValue; |
- } |
- } |
- while (value) { |
- var nextValue = value & (value - 1); |
- var bitValue = value ^ nextValue; |
- var names = this._enumValueToNames[bitValue]; |
- if (!names || names.length !== 1) { |
- console.warn("Ambiguous WebGL enum names for value " + bitValue + ": " + names); |
- return; |
- } |
- result.push(names[0]); |
- value = nextValue; |
- } |
- result.sort(); |
- callArgument.enumName = result.join(" | "); |
- }, |
- |
- _initialize: function() |
- { |
- if (this._enumNameToValue) |
- return; |
- |
- /** @type {!Object.<string, number>} */ |
- this._enumNameToValue = Object.create(null); |
- /** @type {!Object.<number, !Array.<string>>} */ |
- this._enumValueToNames = Object.create(null); |
- |
- /** |
- * @param {?Object} obj |
- * @this WebGLCallFormatter |
- */ |
- function iterateWebGLEnums(obj) |
- { |
- if (!obj) |
- return; |
- for (var property in obj) { |
- if (TypeUtils.isEnumPropertyName(property, obj)) { |
- var value = /** @type {number} */ (obj[property]); |
- this._enumNameToValue[property] = value; |
- var names = this._enumValueToNames[value]; |
- if (names) { |
- if (names.indexOf(property) === -1) |
- names.push(property); |
- } else |
- this._enumValueToNames[value] = [property]; |
- } |
- } |
- } |
- |
- /** |
- * @param {!Array.<string>} values |
- * @return {string} |
- */ |
- function commonSubstring(values) |
- { |
- var length = values.length; |
- for (var i = 0; i < length; ++i) { |
- for (var j = 0; j < length; ++j) { |
- if (values[j].indexOf(values[i]) === -1) |
- break; |
- } |
- if (j === length) |
- return values[i]; |
- } |
- return ""; |
- } |
- |
- var gl = this._createUninstrumentedWebGLRenderingContext(); |
- iterateWebGLEnums.call(this, gl); |
- |
- var extensions = gl.getSupportedExtensions() || []; |
- for (var i = 0, n = extensions.length; i < n; ++i) |
- iterateWebGLEnums.call(this, gl.getExtension(extensions[i])); |
- |
- // Sort to get rid of ambiguity. |
- for (var value in this._enumValueToNames) { |
- var numericValue = Number(value); |
- var names = this._enumValueToNames[numericValue]; |
- if (names.length > 1) { |
- // Choose one enum name if possible. For example: |
- // [BLEND_EQUATION, BLEND_EQUATION_RGB] => BLEND_EQUATION |
- // [COLOR_ATTACHMENT0, COLOR_ATTACHMENT0_WEBGL] => COLOR_ATTACHMENT0 |
- var common = commonSubstring(names); |
- if (common) |
- this._enumValueToNames[numericValue] = [common]; |
- else |
- this._enumValueToNames[numericValue] = names.sort(); |
- } |
- } |
- }, |
- |
- /** |
- * @return {?WebGLRenderingContext} |
- */ |
- _createUninstrumentedWebGLRenderingContext: function() |
- { |
- var canvas = /** @type {!HTMLCanvasElement} */ (inspectedWindow.document.createElement("canvas")); |
- var contextIds = ["experimental-webgl", "webkit-3d", "3d"]; |
- for (var i = 0, contextId; contextId = contextIds[i]; ++i) { |
- var context = canvas.getContext(contextId); |
- if (context) |
- return /** @type {!WebGLRenderingContext} */ (Resource.wrappedObject(context)); |
- } |
- return null; |
- }, |
- |
- __proto__: CallFormatter.prototype |
-} |
- |
-CallFormatter.register("CanvasRenderingContext2D", new CallFormatter(CanvasRenderingContext2DResource.DrawingMethods)); |
-CallFormatter.register("WebGLRenderingContext", new WebGLCallFormatter(WebGLRenderingContextResource.DrawingMethods)); |
- |
-/** |
- * @constructor |
- */ |
-function TraceLog() |
-{ |
- /** @type {!Array.<!ReplayableCall>} */ |
- this._replayableCalls = []; |
- /** @type {!Cache.<!ReplayableResource>} */ |
- this._replayablesCache = new Cache(); |
- /** @type {!Object.<number, boolean>} */ |
- this._frameEndCallIndexes = {}; |
- /** @type {!Object.<number, boolean>} */ |
- this._resourcesCreatedInThisTraceLog = {}; |
-} |
- |
-TraceLog.prototype = { |
- /** |
- * @return {number} |
- */ |
- size: function() |
- { |
- return this._replayableCalls.length; |
- }, |
- |
- /** |
- * @return {!Array.<!ReplayableCall>} |
- */ |
- replayableCalls: function() |
- { |
- return this._replayableCalls; |
- }, |
- |
- /** |
- * @param {number} id |
- * @return {!ReplayableResource|undefined} |
- */ |
- replayableResource: function(id) |
- { |
- return this._replayablesCache.get(id); |
- }, |
- |
- /** |
- * @param {number} resourceId |
- * @return {boolean} |
- */ |
- createdInThisTraceLog: function(resourceId) |
- { |
- return !!this._resourcesCreatedInThisTraceLog[resourceId]; |
- }, |
- |
- /** |
- * @param {!Resource} resource |
- */ |
- captureResource: function(resource) |
- { |
- resource.toReplayable(this._replayablesCache); |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- addCall: function(call) |
- { |
- var resource = Resource.forObject(call.result()); |
- if (resource && !this._replayablesCache.has(resource.id())) |
- this._resourcesCreatedInThisTraceLog[resource.id()] = true; |
- this._replayableCalls.push(call.toReplayable(this._replayablesCache)); |
- }, |
- |
- addFrameEndMark: function() |
- { |
- var index = this._replayableCalls.length - 1; |
- if (index >= 0) |
- this._frameEndCallIndexes[index] = true; |
- }, |
- |
- /** |
- * @param {number} index |
- * @return {boolean} |
- */ |
- isFrameEndCallAt: function(index) |
- { |
- return !!this._frameEndCallIndexes[index]; |
- } |
-} |
- |
-/** |
- * @constructor |
- * @param {!TraceLog} traceLog |
- */ |
-function TraceLogPlayer(traceLog) |
-{ |
- /** @type {!TraceLog} */ |
- this._traceLog = traceLog; |
- /** @type {number} */ |
- this._nextReplayStep = 0; |
- /** @type {!Cache.<!Resource>} */ |
- this._replayWorldCache = new Cache(); |
-} |
- |
-TraceLogPlayer.prototype = { |
- /** |
- * @return {!TraceLog} |
- */ |
- traceLog: function() |
- { |
- return this._traceLog; |
- }, |
- |
- /** |
- * @param {number} id |
- * @return {!Resource|undefined} |
- */ |
- replayWorldResource: function(id) |
- { |
- return this._replayWorldCache.get(id); |
- }, |
- |
- /** |
- * @return {number} |
- */ |
- nextReplayStep: function() |
- { |
- return this._nextReplayStep; |
- }, |
- |
- reset: function() |
- { |
- this._nextReplayStep = 0; |
- this._replayWorldCache.reset(); |
- }, |
- |
- /** |
- * @param {number} stepNum |
- * @return {{replayTime:number, lastCall:(!Call)}} |
- */ |
- stepTo: function(stepNum) |
- { |
- stepNum = Math.min(stepNum, this._traceLog.size() - 1); |
- console.assert(stepNum >= 0); |
- if (this._nextReplayStep > stepNum) |
- this.reset(); |
- |
- // Replay the calls' arguments first to warm-up, before measuring the actual replay time. |
- this._replayCallArguments(stepNum); |
- |
- var replayableCalls = this._traceLog.replayableCalls(); |
- var replayedCalls = []; |
- replayedCalls.length = stepNum - this._nextReplayStep + 1; |
- |
- var beforeTime = TypeUtils.now(); |
- for (var i = 0; this._nextReplayStep <= stepNum; ++this._nextReplayStep, ++i) |
- replayedCalls[i] = replayableCalls[this._nextReplayStep].replay(this._replayWorldCache); |
- var replayTime = Math.max(0, TypeUtils.now() - beforeTime); |
- |
- for (var i = 0, call; call = replayedCalls[i]; ++i) |
- call.resource().onCallReplayed(call); |
- |
- return { |
- replayTime: replayTime, |
- lastCall: replayedCalls[replayedCalls.length - 1] |
- }; |
- }, |
- |
- /** |
- * @param {number} stepNum |
- */ |
- _replayCallArguments: function(stepNum) |
- { |
- /** |
- * @param {*} obj |
- * @this {TraceLogPlayer} |
- */ |
- function replayIfNotCreatedInThisTraceLog(obj) |
- { |
- if (!(obj instanceof ReplayableResource)) |
- return; |
- var replayableResource = /** @type {!ReplayableResource} */ (obj); |
- if (!this._traceLog.createdInThisTraceLog(replayableResource.id())) |
- replayableResource.replay(this._replayWorldCache) |
- } |
- var replayableCalls = this._traceLog.replayableCalls(); |
- for (var i = this._nextReplayStep; i <= stepNum; ++i) { |
- replayIfNotCreatedInThisTraceLog.call(this, replayableCalls[i].replayableResource()); |
- replayIfNotCreatedInThisTraceLog.call(this, replayableCalls[i].result()); |
- replayableCalls[i].args().forEach(replayIfNotCreatedInThisTraceLog.bind(this)); |
- } |
- } |
-} |
- |
-/** |
- * @constructor |
- */ |
-function ResourceTrackingManager() |
-{ |
- this._capturing = false; |
- this._stopCapturingOnFrameEnd = false; |
- this._lastTraceLog = null; |
-} |
- |
-ResourceTrackingManager.prototype = { |
- /** |
- * @return {boolean} |
- */ |
- capturing: function() |
- { |
- return this._capturing; |
- }, |
- |
- /** |
- * @return {?TraceLog} |
- */ |
- lastTraceLog: function() |
- { |
- return this._lastTraceLog; |
- }, |
- |
- /** |
- * @param {!Resource} resource |
- */ |
- registerResource: function(resource) |
- { |
- resource.setManager(this); |
- }, |
- |
- startCapturing: function() |
- { |
- if (!this._capturing) |
- this._lastTraceLog = new TraceLog(); |
- this._capturing = true; |
- this._stopCapturingOnFrameEnd = false; |
- }, |
- |
- /** |
- * @param {!TraceLog=} traceLog |
- */ |
- stopCapturing: function(traceLog) |
- { |
- if (traceLog && this._lastTraceLog !== traceLog) |
- return; |
- this._capturing = false; |
- this._stopCapturingOnFrameEnd = false; |
- if (this._lastTraceLog) |
- this._lastTraceLog.addFrameEndMark(); |
- }, |
- |
- /** |
- * @param {!TraceLog} traceLog |
- */ |
- dropTraceLog: function(traceLog) |
- { |
- this.stopCapturing(traceLog); |
- if (this._lastTraceLog === traceLog) |
- this._lastTraceLog = null; |
- }, |
- |
- captureFrame: function() |
- { |
- this._lastTraceLog = new TraceLog(); |
- this._capturing = true; |
- this._stopCapturingOnFrameEnd = true; |
- }, |
- |
- /** |
- * @param {!Resource} resource |
- * @param {!Array|!Arguments} args |
- */ |
- captureArguments: function(resource, args) |
- { |
- if (!this._capturing) |
- return; |
- this._lastTraceLog.captureResource(resource); |
- for (var i = 0, n = args.length; i < n; ++i) { |
- var res = Resource.forObject(args[i]); |
- if (res) |
- this._lastTraceLog.captureResource(res); |
- } |
- }, |
- |
- /** |
- * @param {!Call} call |
- */ |
- captureCall: function(call) |
- { |
- if (!this._capturing) |
- return; |
- this._lastTraceLog.addCall(call); |
- }, |
- |
- markFrameEnd: function() |
- { |
- if (!this._lastTraceLog) |
- return; |
- this._lastTraceLog.addFrameEndMark(); |
- if (this._stopCapturingOnFrameEnd && this._lastTraceLog.size()) |
- this.stopCapturing(this._lastTraceLog); |
- } |
-} |
- |
-/** |
- * @constructor |
- */ |
-var InjectedCanvasModule = function() |
-{ |
- /** @type {!ResourceTrackingManager} */ |
- this._manager = new ResourceTrackingManager(); |
- /** @type {number} */ |
- this._lastTraceLogId = 0; |
- /** @type {!Object.<string, !TraceLog>} */ |
- this._traceLogs = {}; |
- /** @type {!Object.<string, !TraceLogPlayer>} */ |
- this._traceLogPlayers = {}; |
-} |
- |
-InjectedCanvasModule.prototype = { |
- /** |
- * @param {!WebGLRenderingContext} glContext |
- * @return {!Object} |
- */ |
- wrapWebGLContext: function(glContext) |
- { |
- var resource = Resource.forObject(glContext) || new WebGLRenderingContextResource(glContext); |
- this._manager.registerResource(resource); |
- return resource.proxyObject(); |
- }, |
- |
- /** |
- * @param {!CanvasRenderingContext2D} context |
- * @return {!Object} |
- */ |
- wrapCanvas2DContext: function(context) |
- { |
- var resource = Resource.forObject(context) || new CanvasRenderingContext2DResource(context); |
- this._manager.registerResource(resource); |
- return resource.proxyObject(); |
- }, |
- |
- /** |
- * @return {!CanvasAgent.TraceLogId} |
- */ |
- captureFrame: function() |
- { |
- return this._callStartCapturingFunction(this._manager.captureFrame); |
- }, |
- |
- /** |
- * @return {!CanvasAgent.TraceLogId} |
- */ |
- startCapturing: function() |
- { |
- return this._callStartCapturingFunction(this._manager.startCapturing); |
- }, |
- |
- markFrameEnd: function() |
- { |
- this._manager.markFrameEnd(); |
- }, |
- |
- /** |
- * @param {function(this:ResourceTrackingManager)} func |
- * @return {!CanvasAgent.TraceLogId} |
- */ |
- _callStartCapturingFunction: function(func) |
- { |
- var oldTraceLog = this._manager.lastTraceLog(); |
- func.call(this._manager); |
- var traceLog = /** @type {!TraceLog} */ (this._manager.lastTraceLog()); |
- if (traceLog === oldTraceLog) { |
- for (var id in this._traceLogs) { |
- if (this._traceLogs[id] === traceLog) |
- return id; |
- } |
- } |
- var id = this._makeTraceLogId(); |
- this._traceLogs[id] = traceLog; |
- return id; |
- }, |
- |
- /** |
- * @param {!CanvasAgent.TraceLogId} id |
- */ |
- stopCapturing: function(id) |
- { |
- var traceLog = this._traceLogs[id]; |
- if (traceLog) |
- this._manager.stopCapturing(traceLog); |
- }, |
- |
- /** |
- * @param {!CanvasAgent.TraceLogId} id |
- */ |
- dropTraceLog: function(id) |
- { |
- var traceLog = this._traceLogs[id]; |
- if (traceLog) |
- this._manager.dropTraceLog(traceLog); |
- delete this._traceLogs[id]; |
- delete this._traceLogPlayers[id]; |
- }, |
- |
- /** |
- * @param {!CanvasAgent.TraceLogId} id |
- * @param {number=} startOffset |
- * @param {number=} maxLength |
- * @return {!CanvasAgent.TraceLog|string} |
- */ |
- traceLog: function(id, startOffset, maxLength) |
- { |
- var traceLog = this._traceLogs[id]; |
- if (!traceLog) |
- return "Error: Trace log with the given ID not found."; |
- |
- // Ensure last call ends a frame. |
- traceLog.addFrameEndMark(); |
- |
- var replayableCalls = traceLog.replayableCalls(); |
- if (typeof startOffset !== "number") |
- startOffset = 0; |
- if (typeof maxLength !== "number") |
- maxLength = replayableCalls.length; |
- |
- var fromIndex = Math.max(0, startOffset); |
- var toIndex = Math.min(replayableCalls.length - 1, fromIndex + maxLength - 1); |
- |
- var alive = this._manager.capturing() && this._manager.lastTraceLog() === traceLog; |
- var result = { |
- id: id, |
- /** @type {!Array.<!CanvasAgent.Call>} */ |
- calls: [], |
- /** @type {!Array.<!CanvasAgent.CallArgument>} */ |
- contexts: [], |
- alive: alive, |
- startOffset: fromIndex, |
- totalAvailableCalls: replayableCalls.length |
- }; |
- /** @type {!Object.<string, boolean>} */ |
- var contextIds = {}; |
- for (var i = fromIndex; i <= toIndex; ++i) { |
- var call = replayableCalls[i]; |
- var resource = call.replayableResource(); |
- var contextResource = resource.contextResource(); |
- var stackTrace = call.stackTrace(); |
- var callFrame = stackTrace ? stackTrace.callFrame(0) || {} : {}; |
- var item = CallFormatter.forResource(resource).formatCall(call); |
- item.contextId = CallFormatter.makeStringResourceId(contextResource.id()); |
- item.sourceURL = callFrame.sourceURL; |
- item.lineNumber = callFrame.lineNumber; |
- item.columnNumber = callFrame.columnNumber; |
- item.isFrameEndCall = traceLog.isFrameEndCallAt(i); |
- result.calls.push(item); |
- if (!contextIds[item.contextId]) { |
- contextIds[item.contextId] = true; |
- result.contexts.push(CallFormatter.forResource(resource).formatValue(contextResource)); |
- } |
- } |
- return result; |
- }, |
- |
- /** |
- * @param {!CanvasAgent.TraceLogId} traceLogId |
- * @param {number} stepNo |
- * @return {{resourceState: !CanvasAgent.ResourceState, replayTime: number}|string} |
- */ |
- replayTraceLog: function(traceLogId, stepNo) |
- { |
- var traceLog = this._traceLogs[traceLogId]; |
- if (!traceLog) |
- return "Error: Trace log with the given ID not found."; |
- this._traceLogPlayers[traceLogId] = this._traceLogPlayers[traceLogId] || new TraceLogPlayer(traceLog); |
- |
- var replayResult = this._traceLogPlayers[traceLogId].stepTo(stepNo); |
- var resource = replayResult.lastCall.resource(); |
- var dataURL = resource.toDataURL(); |
- if (!dataURL) { |
- resource = resource.contextResource(); |
- dataURL = resource.toDataURL(); |
- } |
- return { |
- resourceState: this._makeResourceState(resource.id(), traceLogId, resource, dataURL), |
- replayTime: replayResult.replayTime |
- }; |
- }, |
- |
- /** |
- * @param {!CanvasAgent.TraceLogId} traceLogId |
- * @param {!CanvasAgent.ResourceId} stringResourceId |
- * @return {!CanvasAgent.ResourceState|string} |
- */ |
- resourceState: function(traceLogId, stringResourceId) |
- { |
- var traceLog = this._traceLogs[traceLogId]; |
- if (!traceLog) |
- return "Error: Trace log with the given ID not found."; |
- |
- var parsedStringId1 = this._parseStringId(traceLogId); |
- var parsedStringId2 = this._parseStringId(stringResourceId); |
- if (parsedStringId1.injectedScriptId !== parsedStringId2.injectedScriptId) |
- return "Error: Both IDs must point to the same injected script."; |
- |
- var resourceId = parsedStringId2.resourceId; |
- if (!resourceId) |
- return "Error: Wrong resource ID: " + stringResourceId; |
- |
- var traceLogPlayer = this._traceLogPlayers[traceLogId]; |
- var resource = traceLogPlayer && traceLogPlayer.replayWorldResource(resourceId); |
- return this._makeResourceState(resourceId, traceLogId, resource); |
- }, |
- |
- /** |
- * @param {!CanvasAgent.TraceLogId} traceLogId |
- * @param {number} callIndex |
- * @param {number} argumentIndex |
- * @param {string} objectGroup |
- * @return {{result:(!RuntimeAgent.RemoteObject|undefined), resourceState:(!CanvasAgent.ResourceState|undefined)}|string} |
- */ |
- evaluateTraceLogCallArgument: function(traceLogId, callIndex, argumentIndex, objectGroup) |
- { |
- var traceLog = this._traceLogs[traceLogId]; |
- if (!traceLog) |
- return "Error: Trace log with the given ID not found."; |
- |
- var replayableCall = traceLog.replayableCalls()[callIndex]; |
- if (!replayableCall) |
- return "Error: No call found at index " + callIndex; |
- |
- var value; |
- if (replayableCall.isPropertySetter()) |
- value = replayableCall.propertyValue(); |
- else if (argumentIndex === -1) |
- value = replayableCall.result(); |
- else { |
- var args = replayableCall.args(); |
- if (argumentIndex < 0 || argumentIndex >= args.length) |
- return "Error: No argument found at index " + argumentIndex + " for call at index " + callIndex; |
- value = args[argumentIndex]; |
- } |
- |
- if (value instanceof ReplayableResource) { |
- var traceLogPlayer = this._traceLogPlayers[traceLogId]; |
- var resource = traceLogPlayer && traceLogPlayer.replayWorldResource(value.id()); |
- var resourceState = this._makeResourceState(value.id(), traceLogId, resource); |
- return { resourceState: resourceState }; |
- } |
- |
- var remoteObject = injectedScript.wrapObjectForModule(value, objectGroup); |
- return { result: remoteObject }; |
- }, |
- |
- /** |
- * @return {!CanvasAgent.TraceLogId} |
- */ |
- _makeTraceLogId: function() |
- { |
- return "{\"injectedScriptId\":" + injectedScriptId + ",\"traceLogId\":" + (++this._lastTraceLogId) + "}"; |
- }, |
- |
- /** |
- * @param {number} resourceId |
- * @param {!CanvasAgent.TraceLogId} traceLogId |
- * @param {?Resource=} resource |
- * @param {string=} overrideImageURL |
- * @return {!CanvasAgent.ResourceState} |
- */ |
- _makeResourceState: function(resourceId, traceLogId, resource, overrideImageURL) |
- { |
- var result = { |
- id: CallFormatter.makeStringResourceId(resourceId), |
- traceLogId: traceLogId |
- }; |
- if (resource) { |
- result.imageURL = overrideImageURL || resource.toDataURL(); |
- result.descriptors = CallFormatter.forResource(resource).formatResourceStateDescriptors(resource.currentState(), traceLogId); |
- } |
- return result; |
- }, |
- |
- /** |
- * @param {string} stringId |
- * @return {{injectedScriptId: number, traceLogId: ?number, resourceId: ?number}} |
- */ |
- _parseStringId: function(stringId) |
- { |
- return /** @type {?} */ (InjectedScriptHost.eval("(" + stringId + ")")); |
- } |
-} |
- |
-var injectedCanvasModule = new InjectedCanvasModule(); |
-return injectedCanvasModule; |
- |
-}) |