diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js')
-rw-r--r-- | Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js | 3216 |
1 files changed, 0 insertions, 3216 deletions
diff --git a/Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js b/Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js deleted file mode 100644 index 8036985d5..000000000 --- a/Source/WebCore/inspector/InjectedScriptCanvasModuleSource.js +++ /dev/null @@ -1,3216 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -/** - * @param {InjectedScriptHost} InjectedScriptHost - * @param {Window} inspectedWindow - * @param {number} injectedScriptId - */ -(function (InjectedScriptHost, inspectedWindow, injectedScriptId) { - -var TypeUtils = { - /** - * http://www.khronos.org/registry/typedarray/specs/latest/#7 - * @const - * @type {!Array.<function(new:ArrayBufferView, ArrayBufferView)>} - */ - _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, ArrayBufferView)|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; - } - - 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 {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; - } -} - -/** - * @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) -{ - // FIXME: Support JSC, and maybe other browsers. - return null; -} - -/** - * @constructor - */ -function Cache() -{ - this.reset(); -} - -Cache.prototype = { - /** - * @return {number} - */ - size: function() - { - return this._size; - }, - - reset: function() - { - /** @type {!Object.<number, Object>} */ - 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 {Object} - */ - get: function(key) - { - return this._items[key]; - }, - - /** - * @param {number} key - * @param {Object} 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>} */ - 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]; - }, - - 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} 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} cache - * @return {!Call} - */ - replay: function(replayableCall, cache) - { - 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 (replayableCall.result() instanceof ReplayableResource) { - var resource = replayableCall.result().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(); - if (attachments) - this._attachments = TypeUtils.cloneObject(attachments); - 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 {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; - }, - - /** - * @param {string} name - * @return {Object} - */ - attachment: function(name) - { - return this._attachments && this._attachments[name]; - }, - - /** - * @param {Cache} 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} 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 {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 {string} - */ - toDataURL: function() - { - return ""; - }, - - /** - * @param {!Cache} cache - * @return {!ReplayableResource} - */ - toReplayable: function(cache) - { - var result = /** @type {ReplayableResource} */ (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} cache - */ - _populateReplayableData: function(data, cache) - { - // Do nothing. Should be overridden by subclasses. - }, - - /** - * @param {!Object} data - * @param {!Cache} cache - * @return {!Resource} - */ - replay: function(data, cache) - { - var resource = /** @type {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} 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 {!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 self = this; - var customWrapFunctions = this._customWrapFunctions(); - function processProperty(property) - { - if (typeof wrappedObject[property] === "function") { - var customWrapFunction = customWrapFunctions[property]; - if (customWrapFunction) - proxy[property] = self._wrapCustomFunction(self, wrappedObject, wrappedObject[property], property, customWrapFunction); - else - proxy[property] = self._wrapFunction(self, wrappedObject, wrappedObject[property], property); - } else if (/^[A-Z0-9_]+$/.test(property) && typeof wrappedObject[property] === "number") { - // Fast access to enums and constants. - proxy[property] = wrappedObject[property]; - } else { - Object.defineProperty(proxy, property, { - get: function() - { - var obj = wrappedObject[property]; - var resource = Resource.forObject(obj); - return resource ? resource : obj; - }, - set: self._wrapPropertySetter(self, wrappedObject, property), - enumerable: true - }); - } - } - - var isEmpty = true; - for (var property in wrappedObject) { - isEmpty = false; - processProperty(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) -{ - /** @this Resource.WrapFunction */ - return 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} - */ - replayableContextResource: function() - { - return this._data.contextResource || this; - }, - - /** - * @param {!Cache} 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} 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} 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} 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} 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.BINDING !== target) { - this._state.BINDING = 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 - * @param {!Object} data - * @param {!Cache} 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 - * @param {!Object} data - * @param {!Cache} cache - */ - _populateReplayableData: function(data, cache) - { - var glResource = WebGLRenderingContextResource.forObject(this); - var gl = glResource.wrappedObject(); - var program = this.wrappedObject(); - - var originalErrors = glResource.getAllErrors(); - - var uniforms = []; - 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); - uniforms.push({ - name: activeInfo.name, - type: activeInfo.type, - value: value - }); - } - data.uniforms = uniforms; - - glResource.restoreErrors(originalErrors); - }, - - /** - * @override - * @param {!Object} data - * @param {!Cache} 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 = { - /** - * @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 - * @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 - * @param {!Call} call - */ - pushCall: function(call) - { - // FIXME: remove any older calls that no longer contribute to the resource state. - // 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 - * @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 - * @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 {ContextResource} - * @param {!WebGLRenderingContext} glContext - */ -function WebGLRenderingContextResource(glContext) -{ - ContextResource.call(this, glContext, "WebGLRenderingContext"); - /** @type {Object.<number, boolean>} */ - this._customErrors = null; - /** @type {!Object.<string, boolean>} */ - this._extensions = {}; -} - -/** - * @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", - "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", - "VIEWPORT" -]; - -/** - * @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 - */ - addExtension: function(name) - { - // FIXME: Wrap OES_vertex_array_object extension. - this._extensions[name.toLowerCase()] = true; - }, - - /** - * @override - * @param {!Object} data - * @param {!Cache} cache - */ - _populateReplayableData: function(data, cache) - { - var gl = this.wrappedObject(); - data.originalCanvas = gl.canvas; - data.originalContextAttributes = gl.getContextAttributes(); - data.extensions = TypeUtils.cloneObject(this._extensions); - - var originalErrors = this.getAllErrors(); - - // Take a full GL state snapshot. - var glState = {}; - WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) { - glState[parameter] = gl.isEnabled(gl[parameter]); - }); - WebGLRenderingContextResource.StateParameters.forEach(function(parameter) { - glState[parameter] = Resource.toReplayable(gl.getParameter(gl[parameter]), cache); - }); - - // 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"]; - var vertexAttribStates = []; - for (var i = 0; i < maxVertexAttribs; ++i) { - var state = {}; - vertexAttribParameters.forEach(function(attribParameter) { - state[attribParameter] = Resource.toReplayable(gl.getVertexAttrib(i, gl[attribParameter]), cache); - }); - state.VERTEX_ATTRIB_ARRAY_POINTER = gl.getVertexAttribOffset(i, gl.VERTEX_ATTRIB_ARRAY_POINTER); - vertexAttribStates.push(state); - } - glState.vertexAttribStates = vertexAttribStates; - - // TEXTURES - var currentTextureBinding = /** @type {number} */ (gl.getParameter(gl.ACTIVE_TEXTURE)); - var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)); - var textureBindings = []; - for (var i = 0; i < maxTextureImageUnits; ++i) { - gl.activeTexture(gl.TEXTURE0 + i); - var state = { - TEXTURE_2D: Resource.toReplayable(gl.getParameter(gl.TEXTURE_BINDING_2D), cache), - TEXTURE_CUBE_MAP: Resource.toReplayable(gl.getParameter(gl.TEXTURE_BINDING_CUBE_MAP), cache) - }; - textureBindings.push(state); - } - glState.textureBindings = textureBindings; - gl.activeTexture(currentTextureBinding); - - data.glState = glState; - - this.restoreErrors(originalErrors); - }, - - /** - * @override - * @param {!Object} data - * @param {!Cache} cache - */ - _doReplayCalls: function(data, cache) - { - this._customErrors = null; - this._extensions = TypeUtils.cloneObject(data.extensions) || {}; - - var canvas = data.originalCanvas.cloneNode(true); - var replayContext = null; - var contextIds = ["webgl", "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); - - 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.vertexAttribStates[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))); - - // TEXTURES - 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.textureBindings[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; - 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 - * @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(Resource, "WebGLUniformLocation"); - - /** - * @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()); - } - } - } - stateModifyingWrapFunction("bindAttribLocation"); - stateModifyingWrapFunction("compileShader"); - stateModifyingWrapFunction("detachShader"); - stateModifyingWrapFunction("linkProgram"); - stateModifyingWrapFunction("shaderSource"); - stateModifyingWrapFunction("bufferData"); - stateModifyingWrapFunction("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.addExtension(name); - } - - // - // 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._registerBoundResource("__bindBuffer_" + target, obj); - } - /** - * @param {number} target - * @param {WebGLTexture} obj - * @this Resource.WrapFunction - */ - wrapFunctions["bindTexture"] = function(target, obj) - { - 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; - } - 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", - "webkitLineDash", - "webkitLineDashOffset" -]; - -/** - * @const - * @type {!Array.<string>} - */ -CanvasRenderingContext2DResource.PathMethods = [ - "beginPath", - "moveTo", - "closePath", - "lineTo", - "quadraticCurveTo", - "bezierCurveTo", - "arcTo", - "arc", - "rect" -]; - -/** - * @const - * @type {!Array.<string>} - */ -CanvasRenderingContext2DResource.TransformationMatrixMethods = [ - "scale", - "rotate", - "translate", - "transform", - "setTransform" -]; - -/** - * @const - * @type {!Object.<string, boolean>} - */ -CanvasRenderingContext2DResource.DrawingMethods = TypeUtils.createPrefixedPropertyNamesSet([ - "clearRect", - "drawImage", - "drawImageFromRect", - "drawCustomFocusRing", - "drawSystemFocusRing", - "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 - * @param {!Object} data - * @param {!Cache} cache - */ - _populateReplayableData: function(data, cache) - { - data.currentAttributes = this._currentAttributesState(); - data.originalCanvasCloned = TypeUtils.cloneIntoCanvas(this.wrappedObject().canvas); - }, - - /** - * @override - * @param {!Object} data - * @param {!Cache} cache - */ - _doReplayCalls: function(data, cache) - { - var canvas = TypeUtils.cloneIntoCanvas(data.originalCanvasCloned); - var ctx = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d"))); - 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")); - this._calls.push(replayableCall.replay(cache)); - } - this._applyAttributesState(data.currentAttributes); - }, - - /** - * @param {!Call} call - */ - pushCall_setTransform: 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) - { - call.setAttachment("canvas2dAttributesState", this._currentAttributesState()); - 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() or setTransform() but after any PATH or corresponding save() method. - var lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform"]); - 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"], 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; - }, - - /** - * @return {!Object.<string, string>} - */ - _currentAttributesState: function() - { - var ctx = this.wrappedObject(); - var state = {}; - state.attributes = {}; - CanvasRenderingContext2DResource.AttributeProperties.forEach(function(attribute) { - state.attributes[attribute] = ctx[attribute]; - }); - if (ctx.getLineDash) - state.lineDash = ctx.getLineDash(); - return state; - }, - - /** - * @param {Object.<string, string>=} state - */ - _applyAttributesState: function(state) - { - if (!state) - return; - var ctx = this.wrappedObject(); - if (state.attributes) { - Object.keys(state.attributes).forEach(function(attribute) { - ctx[attribute] = state.attributes[attribute]; - }); - } - if (ctx.setLineDash) - ctx.setLineDash(state.lineDash); - }, - - /** - * @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"); - - /** - * @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()); - } - } - } - - for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.TransformationMatrixMethods[i]; ++i) - stateModifyingWrapFunction(methodName, methodName === "setTransform" ? this.pushCall_setTransform : 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; - } - 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 - * @return {!Object} - */ - formatCall: function(replayableCall) - { - var result = {}; - var functionName = replayableCall.functionName(); - if (functionName) { - result.functionName = functionName; - result.arguments = replayableCall.args().map(this.formatValue.bind(this)); - if (replayableCall.result() !== undefined) - result.result = this.formatValue(replayableCall.result()); - if (this._drawingMethodNames[functionName]) - result.isDrawingCall = true; - } else { - result.property = replayableCall.args()[0]; - result.value = this.formatValue(replayableCall.args()[1]); - } - return result; - }, - - /** - * @param {*} value - * @return {!Object} - */ - formatValue: function(value) - { - if (value instanceof ReplayableResource) - var description = value.description(); - else - var description = "" + value; - return { description: description }; - } -} - -/** - * @const - * @type {!Object.<string, !CallFormatter>} - */ -CallFormatter._formatters = {}; - -/** - * @param {string} resourceName - * @param {!CallFormatter} callFormatter - */ -CallFormatter.register = function(resourceName, callFormatter) -{ - CallFormatter._formatters[resourceName] = callFormatter; -} - -/** - * @param {!ReplayableCall} replayableCall - * @return {!Object} - */ -CallFormatter.formatCall = function(replayableCall) -{ - var resource = replayableCall.replayableResource(); - var formatter = CallFormatter._formatters[resource.name()]; - if (!formatter) { - var contextResource = resource.replayableContextResource(); - formatter = CallFormatter._formatters[contextResource.name()] || new CallFormatter(); - } - return formatter.formatCall(replayableCall); -} - -CallFormatter.register("CanvasRenderingContext2D", new CallFormatter(CanvasRenderingContext2DResource.DrawingMethods)); -CallFormatter.register("WebGLRenderingContext", new CallFormatter(WebGLRenderingContextResource.DrawingMethods)); - -/** - * @constructor - */ -function TraceLog() -{ - /** @type {!Array.<ReplayableCall>} */ - this._replayableCalls = []; - /** @type {!Cache} */ - this._replayablesCache = new Cache(); - /** @type {!Object.<number, boolean>} */ - this._frameEndCallIndexes = {}; -} - -TraceLog.prototype = { - /** - * @return {number} - */ - size: function() - { - return this._replayableCalls.length; - }, - - /** - * @return {!Array.<ReplayableCall>} - */ - replayableCalls: function() - { - return this._replayableCalls; - }, - - /** - * @param {number} id - * @return {ReplayableResource} - */ - replayableResource: function(id) - { - return /** @type {ReplayableResource} */ (this._replayablesCache.get(id)); - }, - - /** - * @param {!Resource} resource - */ - captureResource: function(resource) - { - resource.toReplayable(this._replayablesCache); - }, - - /** - * @param {!Call} call - */ - addCall: function(call) - { - 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} */ - this._replayWorldCache = new Cache(); -} - -TraceLogPlayer.prototype = { - /** - * @return {!TraceLog} - */ - traceLog: function() - { - return this._traceLog; - }, - - /** - * @param {number} id - * @return {Resource} - */ - replayWorldResource: function(id) - { - return /** @type {Resource} */ (this._replayWorldCache.get(id)); - }, - - /** - * @return {number} - */ - nextReplayStep: function() - { - return this._nextReplayStep; - }, - - reset: function() - { - this._nextReplayStep = 0; - this._replayWorldCache.reset(); - }, - - /** - * @return {Call} - */ - step: function() - { - return this.stepTo(this._nextReplayStep); - }, - - /** - * @param {number} stepNum - * @return {Call} - */ - stepTo: function(stepNum) - { - stepNum = Math.min(stepNum, this._traceLog.size() - 1); - console.assert(stepNum >= 0); - if (this._nextReplayStep > stepNum) - this.reset(); - // FIXME: Replay all the cached resources first to warm-up. - var lastCall = null; - var replayableCalls = this._traceLog.replayableCalls(); - while (this._nextReplayStep <= stepNum) - lastCall = replayableCalls[this._nextReplayStep++].replay(this._replayWorldCache); - return lastCall; - }, - - /** - * @return {Call} - */ - replay: function() - { - return this.stepTo(this._traceLog.size() - 1); - } -} - -/** - * @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 = 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: [], - alive: alive, - startOffset: fromIndex, - totalAvailableCalls: replayableCalls.length - }; - for (var i = fromIndex; i <= toIndex; ++i) { - var call = replayableCalls[i]; - var contextResource = call.replayableResource().replayableContextResource(); - var stackTrace = call.stackTrace(); - var callFrame = stackTrace ? stackTrace.callFrame(0) || {} : {}; - var item = CallFormatter.formatCall(call); - item.contextId = this._makeStringResourceId(contextResource.id()); - item.sourceURL = callFrame.sourceURL; - item.lineNumber = callFrame.lineNumber; - item.columnNumber = callFrame.columnNumber; - item.isFrameEndCall = traceLog.isFrameEndCallAt(i); - result.calls.push(item); - } - return result; - }, - - /** - * @param {*} obj - * @return {!CanvasAgent.CallArgument} - */ - _makeCallArgument: function(obj) - { - if (obj instanceof ReplayableResource) - var description = obj.description(); - else - var description = "" + obj; - return { description: description }; - }, - - /** - * @param {CanvasAgent.TraceLogId} traceLogId - * @param {number} stepNo - * @return {!CanvasAgent.ResourceState|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 lastCall = this._traceLogPlayers[traceLogId].stepTo(stepNo); - var resource = lastCall.resource(); - var dataURL = resource.toDataURL(); - if (!dataURL) { - resource = resource.contextResource(); - dataURL = resource.toDataURL(); - } - return this._makeResourceState(this._makeStringResourceId(resource.id()), traceLogId, dataURL); - }, - - /** - * @param {CanvasAgent.ResourceId} stringResourceId - * @return {!CanvasAgent.ResourceInfo|string} - */ - resourceInfo: function(stringResourceId) - { - var resourceId = this._parseStringId(stringResourceId).resourceId; - if (!resourceId) - return "Error: Wrong resource ID: " + stringResourceId; - - var replayableResource = null; - for (var id in this._traceLogs) { - replayableResource = this._traceLogs[id].replayableResource(resourceId); - if (replayableResource) - break; - } - if (!replayableResource) - return "Error: Resource with the given ID not found."; - - return this._makeResourceInfo(stringResourceId, replayableResource.description()); - }, - - /** - * @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 traceLogPlayer = this._traceLogPlayers[traceLogId]; - if (!traceLogPlayer) - return "Error: Trace log replay has not started yet."; - - 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 resource = traceLogPlayer.replayWorldResource(resourceId); - if (!resource) - return "Error: Resource with the given ID has not been replayed yet."; - - return this._makeResourceState(stringResourceId, traceLogId, resource.toDataURL()); - }, - - /** - * @return {CanvasAgent.TraceLogId} - */ - _makeTraceLogId: function() - { - return "{\"injectedScriptId\":" + injectedScriptId + ",\"traceLogId\":" + (++this._lastTraceLogId) + "}"; - }, - - /** - * @param {number} resourceId - * @return {CanvasAgent.ResourceId} - */ - _makeStringResourceId: function(resourceId) - { - return "{\"injectedScriptId\":" + injectedScriptId + ",\"resourceId\":" + resourceId + "}"; - }, - - /** - * @param {CanvasAgent.ResourceId} stringResourceId - * @param {string} description - * @return {!CanvasAgent.ResourceInfo} - */ - _makeResourceInfo: function(stringResourceId, description) - { - return { - id: stringResourceId, - description: description - }; - }, - - /** - * @param {CanvasAgent.ResourceId} stringResourceId - * @param {CanvasAgent.TraceLogId} traceLogId - * @param {string} imageURL - * @return {!CanvasAgent.ResourceState} - */ - _makeResourceState: function(stringResourceId, traceLogId, imageURL) - { - return { - id: stringResourceId, - traceLogId: traceLogId, - imageURL: imageURL - }; - }, - - /** - * @param {string} stringId - * @return {{injectedScriptId: number, traceLogId: ?number, resourceId: ?number}} - */ - _parseStringId: function(stringId) - { - return InjectedScriptHost.evaluate("(" + stringId + ")"); - } -} - -var injectedCanvasModule = new InjectedCanvasModule(); -return injectedCanvasModule; - -}) |