Best JavaScript code snippet using playwright-internal
InjectedScriptCanvasModuleSource.js
Source:InjectedScriptCanvasModuleSource.js
1/*2 * Copyright (C) 2012 Google Inc. All rights reserved.3 *4 * Redistribution and use in source and binary forms, with or without5 * modification, are permitted provided that the following conditions are6 * met:7 *8 * * Redistributions of source code must retain the above copyright9 * notice, this list of conditions and the following disclaimer.10 * * Redistributions in binary form must reproduce the above11 * copyright notice, this list of conditions and the following disclaimer12 * in the documentation and/or other materials provided with the13 * distribution.14 * * Neither the name of Google Inc. nor the names of its15 * contributors may be used to endorse or promote products derived from16 * this software without specific prior written permission.17 *18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29 */30/**31 * @param {InjectedScriptHost} InjectedScriptHost32 * @param {Window} inspectedWindow33 * @param {number} injectedScriptId34 */35(function (InjectedScriptHost, inspectedWindow, injectedScriptId) {36var TypeUtils = {37 /**38 * http://www.khronos.org/registry/typedarray/specs/latest/#739 * @const40 * @type {!Array.<function(new:ArrayBufferView, ArrayBufferView)>}41 */42 _typedArrayClasses: (function(typeNames) {43 var result = [];44 for (var i = 0, n = typeNames.length; i < n; ++i) {45 if (inspectedWindow[typeNames[i]])46 result.push(inspectedWindow[typeNames[i]]);47 }48 return result;49 })(["Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Uint16Array", "Int32Array", "Uint32Array", "Float32Array", "Float64Array"]),50 /**51 * @const52 * @type {!Array.<string>}53 */54 _supportedPropertyPrefixes: ["webkit"],55 /**56 * @param {*} array57 * @return {function(new:ArrayBufferView, ArrayBufferView)|null}58 */59 typedArrayClass: function(array)60 {61 var classes = TypeUtils._typedArrayClasses;62 for (var i = 0, n = classes.length; i < n; ++i) {63 if (array instanceof classes[i])64 return classes[i];65 }66 return null;67 },68 /**69 * @param {*} obj70 * @return {*}71 */72 clone: function(obj)73 {74 if (!obj)75 return obj;76 var type = typeof obj;77 if (type !== "object" && type !== "function")78 return obj;79 // Handle Array and ArrayBuffer instances.80 if (typeof obj.slice === "function") {81 console.assert(obj instanceof Array || obj instanceof ArrayBuffer);82 return obj.slice(0);83 }84 var typedArrayClass = TypeUtils.typedArrayClass(obj);85 if (typedArrayClass)86 return new typedArrayClass(/** @type {ArrayBufferView} */ (obj));87 if (obj instanceof HTMLImageElement) {88 var img = /** @type {HTMLImageElement} */ (obj);89 // Special case for Images with Blob URIs: cloneNode will fail if the Blob URI has already been revoked.90 // FIXME: Maybe this is a bug in WebKit core?91 if (/^blob:/.test(img.src))92 return TypeUtils.cloneIntoCanvas(img);93 return img.cloneNode(true);94 }95 if (obj instanceof HTMLCanvasElement)96 return TypeUtils.cloneIntoCanvas(obj);97 if (obj instanceof HTMLVideoElement)98 return TypeUtils.cloneIntoCanvas(obj, obj.videoWidth, obj.videoHeight);99 if (obj instanceof ImageData) {100 var context = TypeUtils._dummyCanvas2dContext();101 // FIXME: suppress type checks due to outdated builtin externs for createImageData.102 var result = (/** @type {?} */ (context)).createImageData(obj);103 for (var i = 0, n = obj.data.length; i < n; ++i)104 result.data[i] = obj.data[i];105 return result;106 }107 console.error("ASSERT_NOT_REACHED: failed to clone object: ", obj);108 return obj;109 },110 /**111 * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} obj112 * @param {number=} width113 * @param {number=} height114 * @return {HTMLCanvasElement}115 */116 cloneIntoCanvas: function(obj, width, height)117 {118 var canvas = /** @type {HTMLCanvasElement} */ (inspectedWindow.document.createElement("canvas"));119 canvas.width = width || +obj.width;120 canvas.height = height || +obj.height;121 var context = /** @type {CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d")));122 context.drawImage(obj, 0, 0);123 return canvas;124 },125 /**126 * @param {Object=} obj127 * @return {Object}128 */129 cloneObject: function(obj)130 {131 if (!obj)132 return null;133 var result = {};134 for (var key in obj)135 result[key] = obj[key];136 return result;137 },138 /**139 * @param {!Array.<string>} names140 * @return {!Object.<string, boolean>}141 */142 createPrefixedPropertyNamesSet: function(names)143 {144 var result = Object.create(null);145 for (var i = 0, name; name = names[i]; ++i) {146 result[name] = true;147 var suffix = name.substr(0, 1).toUpperCase() + name.substr(1);148 for (var j = 0, prefix; prefix = TypeUtils._supportedPropertyPrefixes[j]; ++j)149 result[prefix + suffix] = true;150 }151 return result;152 },153 /**154 * @return {CanvasRenderingContext2D}155 */156 _dummyCanvas2dContext: function()157 {158 var context = TypeUtils._dummyCanvas2dContextInstance;159 if (!context) {160 var canvas = /** @type {HTMLCanvasElement} */ (inspectedWindow.document.createElement("canvas"));161 context = /** @type {CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d")));162 TypeUtils._dummyCanvas2dContextInstance = context;163 }164 return context;165 }166}167/**168 * @interface169 */170function StackTrace()171{172}173StackTrace.prototype = {174 /**175 * @param {number} index176 * @return {{sourceURL: string, lineNumber: number, columnNumber: number}|undefined}177 */178 callFrame: function(index)179 {180 }181}182/**183 * @param {number=} stackTraceLimit184 * @param {Function=} topMostFunctionToIgnore185 * @return {StackTrace}186 */187StackTrace.create = function(stackTraceLimit, topMostFunctionToIgnore)188{189 // FIXME: Support JSC, and maybe other browsers.190 return null;191}192/**193 * @constructor194 */195function Cache()196{197 this.reset();198}199Cache.prototype = {200 /**201 * @return {number}202 */203 size: function()204 {205 return this._size;206 },207 reset: function()208 {209 /** @type {!Object.<number, Object>} */210 this._items = Object.create(null);211 /** @type {number} */212 this._size = 0;213 },214 /**215 * @param {number} key216 * @return {boolean}217 */218 has: function(key)219 {220 return key in this._items;221 },222 /**223 * @param {number} key224 * @return {Object}225 */226 get: function(key)227 {228 return this._items[key];229 },230 /**231 * @param {number} key232 * @param {Object} item233 */234 put: function(key, item)235 {236 if (!this.has(key))237 ++this._size;238 this._items[key] = item;239 }240}241/**242 * @constructor243 * @param {Resource|Object} thisObject244 * @param {string} functionName245 * @param {Array|Arguments} args246 * @param {Resource|*=} result247 * @param {StackTrace=} stackTrace248 */249function Call(thisObject, functionName, args, result, stackTrace)250{251 this._thisObject = thisObject;252 this._functionName = functionName;253 this._args = Array.prototype.slice.call(args, 0);254 this._result = result;255 this._stackTrace = stackTrace || null;256 if (!this._functionName)257 console.assert(this._args.length === 2 && typeof this._args[0] === "string");258}259Call.prototype = {260 /**261 * @return {Resource}262 */263 resource: function()264 {265 return Resource.forObject(this._thisObject);266 },267 /**268 * @return {string}269 */270 functionName: function()271 {272 return this._functionName;273 },274 /**275 * @return {boolean}276 */277 isPropertySetter: function()278 {279 return !this._functionName;280 },281 282 /**283 * @return {!Array}284 */285 args: function()286 {287 return this._args;288 },289 /**290 * @return {*}291 */292 result: function()293 {294 return this._result;295 },296 /**297 * @return {StackTrace}298 */299 stackTrace: function()300 {301 return this._stackTrace;302 },303 /**304 * @param {StackTrace} stackTrace305 */306 setStackTrace: function(stackTrace)307 {308 this._stackTrace = stackTrace;309 },310 /**311 * @param {*} result312 */313 setResult: function(result)314 {315 this._result = result;316 },317 /**318 * @param {string} name319 * @param {Object} attachment320 */321 setAttachment: function(name, attachment)322 {323 if (attachment) {324 /** @type {Object.<string, Object>} */325 this._attachments = this._attachments || Object.create(null);326 this._attachments[name] = attachment;327 } else if (this._attachments)328 delete this._attachments[name];329 },330 /**331 * @param {string} name332 * @return {Object}333 */334 attachment: function(name)335 {336 return this._attachments && this._attachments[name];337 },338 freeze: function()339 {340 if (this._freezed)341 return;342 this._freezed = true;343 for (var i = 0, n = this._args.length; i < n; ++i) {344 // FIXME: freeze the Resources also!345 if (!Resource.forObject(this._args[i]))346 this._args[i] = TypeUtils.clone(this._args[i]);347 }348 },349 /**350 * @param {!Cache} cache351 * @return {!ReplayableCall}352 */353 toReplayable: function(cache)354 {355 this.freeze();356 var thisObject = /** @type {ReplayableResource} */ (Resource.toReplayable(this._thisObject, cache));357 var result = Resource.toReplayable(this._result, cache);358 var args = this._args.map(function(obj) {359 return Resource.toReplayable(obj, cache);360 });361 var attachments = TypeUtils.cloneObject(this._attachments);362 return new ReplayableCall(thisObject, this._functionName, args, result, this._stackTrace, attachments);363 },364 /**365 * @param {!ReplayableCall} replayableCall366 * @param {!Cache} cache367 * @return {!Call}368 */369 replay: function(replayableCall, cache)370 {371 var replayObject = ReplayableResource.replay(replayableCall.replayableResource(), cache);372 var replayArgs = replayableCall.args().map(function(obj) {373 return ReplayableResource.replay(obj, cache);374 });375 var replayResult = undefined;376 if (replayableCall.isPropertySetter())377 replayObject[replayArgs[0]] = replayArgs[1];378 else {379 var replayFunction = replayObject[replayableCall.functionName()];380 console.assert(typeof replayFunction === "function", "Expected a function to replay");381 replayResult = replayFunction.apply(replayObject, replayArgs);382 if (replayableCall.result() instanceof ReplayableResource) {383 var resource = replayableCall.result().replay(cache);384 if (!resource.wrappedObject())385 resource.setWrappedObject(replayResult);386 }387 }388 389 this._thisObject = replayObject;390 this._functionName = replayableCall.functionName();391 this._args = replayArgs;392 this._result = replayResult;393 this._stackTrace = replayableCall.stackTrace();394 this._freezed = true;395 var attachments = replayableCall.attachments();396 if (attachments)397 this._attachments = TypeUtils.cloneObject(attachments);398 return this;399 }400}401/**402 * @constructor403 * @param {ReplayableResource} thisObject404 * @param {string} functionName405 * @param {Array.<ReplayableResource|*>} args406 * @param {ReplayableResource|*} result407 * @param {StackTrace} stackTrace408 * @param {Object.<string, Object>} attachments409 */410function ReplayableCall(thisObject, functionName, args, result, stackTrace, attachments)411{412 this._thisObject = thisObject;413 this._functionName = functionName;414 this._args = args;415 this._result = result;416 this._stackTrace = stackTrace;417 if (attachments)418 this._attachments = attachments;419}420ReplayableCall.prototype = {421 /**422 * @return {ReplayableResource}423 */424 replayableResource: function()425 {426 return this._thisObject;427 },428 /**429 * @return {string}430 */431 functionName: function()432 {433 return this._functionName;434 },435 /**436 * @return {boolean}437 */438 isPropertySetter: function()439 {440 return !this._functionName;441 },442 /**443 * @return {Array.<ReplayableResource|*>}444 */445 args: function()446 {447 return this._args;448 },449 /**450 * @return {ReplayableResource|*}451 */452 result: function()453 {454 return this._result;455 },456 /**457 * @return {StackTrace}458 */459 stackTrace: function()460 {461 return this._stackTrace;462 },463 /**464 * @return {Object.<string, Object>}465 */466 attachments: function()467 {468 return this._attachments;469 },470 /**471 * @param {string} name472 * @return {Object}473 */474 attachment: function(name)475 {476 return this._attachments && this._attachments[name];477 },478 /**479 * @param {Cache} cache480 * @return {!Call}481 */482 replay: function(cache)483 {484 var call = /** @type {!Call} */ (Object.create(Call.prototype));485 return call.replay(this, cache);486 }487}488/**489 * @constructor490 * @param {!Object} wrappedObject491 * @param {string} name492 */493function Resource(wrappedObject, name)494{495 /** @type {number} */496 this._id = ++Resource._uniqueId;497 /** @type {string} */498 this._name = name || "Resource";499 /** @type {number} */500 this._kindId = Resource._uniqueKindIds[this._name] = (Resource._uniqueKindIds[this._name] || 0) + 1;501 /** @type {ResourceTrackingManager} */502 this._resourceManager = null;503 /** @type {!Array.<Call>} */504 this._calls = [];505 /**506 * This is to prevent GC from collecting associated resources.507 * Otherwise, for example in WebGL, subsequent calls to gl.getParameter()508 * may return a recently created instance that is no longer bound to a509 * Resource object (thus, no history to replay it later).510 *511 * @type {!Object.<string, Resource>}512 */513 this._boundResources = Object.create(null);514 this.setWrappedObject(wrappedObject);515}516/**517 * @type {number}518 */519Resource._uniqueId = 0;520/**521 * @type {!Object.<string, number>}522 */523Resource._uniqueKindIds = {};524/**525 * @param {*} obj526 * @return {Resource}527 */528Resource.forObject = function(obj)529{530 if (!obj)531 return null;532 if (obj instanceof Resource)533 return obj;534 if (typeof obj === "object")535 return obj["__resourceObject"];536 return null;537}538/**539 * @param {Resource|*} obj540 * @return {*}541 */542Resource.wrappedObject = function(obj)543{544 var resource = Resource.forObject(obj);545 return resource ? resource.wrappedObject() : obj;546}547/**548 * @param {Resource|*} obj549 * @param {!Cache} cache550 * @return {ReplayableResource|*}551 */552Resource.toReplayable = function(obj, cache)553{554 var resource = Resource.forObject(obj);555 return resource ? resource.toReplayable(cache) : obj;556}557Resource.prototype = {558 /**559 * @return {number}560 */561 id: function()562 {563 return this._id;564 },565 /**566 * @return {Object}567 */568 wrappedObject: function()569 {570 return this._wrappedObject;571 },572 /**573 * @param {!Object} value574 */575 setWrappedObject: function(value)576 {577 console.assert(value, "wrappedObject should not be NULL");578 console.assert(!(value instanceof Resource), "Binding a Resource object to another Resource object?");579 this._wrappedObject = value;580 this._bindObjectToResource(value);581 },582 /**583 * @return {Object}584 */585 proxyObject: function()586 {587 if (!this._proxyObject)588 this._proxyObject = this._wrapObject();589 return this._proxyObject;590 },591 /**592 * @return {ResourceTrackingManager}593 */594 manager: function()595 {596 return this._resourceManager;597 },598 /**599 * @param {ResourceTrackingManager} value600 */601 setManager: function(value)602 {603 this._resourceManager = value;604 },605 /**606 * @return {!Array.<Call>}607 */608 calls: function()609 {610 return this._calls;611 },612 /**613 * @return {ContextResource}614 */615 contextResource: function()616 {617 if (this instanceof ContextResource)618 return /** @type {ContextResource} */ (this);619 if (this._calculatingContextResource)620 return null;621 this._calculatingContextResource = true;622 var result = null;623 for (var i = 0, n = this._calls.length; i < n; ++i) {624 result = this._calls[i].resource().contextResource();625 if (result)626 break;627 }628 delete this._calculatingContextResource;629 console.assert(result, "Failed to find context resource for " + this._name + "@" + this._kindId);630 return result;631 },632 /**633 * @return {string}634 */635 toDataURL: function()636 {637 return "";638 },639 /**640 * @param {!Cache} cache641 * @return {!ReplayableResource}642 */643 toReplayable: function(cache)644 {645 var result = /** @type {ReplayableResource} */ (cache.get(this._id));646 if (result)647 return result;648 var data = {649 id: this._id,650 name: this._name,651 kindId: this._kindId652 };653 result = new ReplayableResource(this, data);654 cache.put(this._id, result); // Put into the cache early to avoid loops.655 data.calls = this._calls.map(function(call) {656 return call.toReplayable(cache);657 });658 this._populateReplayableData(data, cache);659 var contextResource = this.contextResource();660 if (contextResource !== this)661 data.contextResource = Resource.toReplayable(contextResource, cache);662 return result;663 },664 /**665 * @param {!Object} data666 * @param {!Cache} cache667 */668 _populateReplayableData: function(data, cache)669 {670 // Do nothing. Should be overridden by subclasses.671 },672 /**673 * @param {!Object} data674 * @param {!Cache} cache675 * @return {!Resource}676 */677 replay: function(data, cache)678 {679 var resource = /** @type {Resource} */ (cache.get(data.id));680 if (resource)681 return resource;682 this._id = data.id;683 this._name = data.name;684 this._kindId = data.kindId;685 this._resourceManager = null;686 this._calls = [];687 this._boundResources = Object.create(null);688 this._wrappedObject = null;689 cache.put(data.id, this); // Put into the cache early to avoid loops.690 this._doReplayCalls(data, cache);691 console.assert(this._wrappedObject, "Resource should be reconstructed!");692 return this;693 },694 /**695 * @param {!Object} data696 * @param {!Cache} cache697 */698 _doReplayCalls: function(data, cache)699 {700 for (var i = 0, n = data.calls.length; i < n; ++i)701 this._calls.push(data.calls[i].replay(cache));702 },703 /**704 * @param {!Call} call705 */706 pushCall: function(call)707 {708 call.freeze();709 this._calls.push(call);710 },711 /**712 * @param {!Object} object713 */714 _bindObjectToResource: function(object)715 {716 Object.defineProperty(object, "__resourceObject", {717 value: this,718 writable: false,719 enumerable: false,720 configurable: true721 });722 },723 /**724 * @param {string} key725 * @param {*} obj726 */727 _registerBoundResource: function(key, obj)728 {729 var resource = Resource.forObject(obj);730 if (resource)731 this._boundResources[key] = resource;732 else733 delete this._boundResources[key];734 },735 /**736 * @return {Object}737 */738 _wrapObject: function()739 {740 var wrappedObject = this.wrappedObject();741 if (!wrappedObject)742 return null;743 var proxy = Object.create(wrappedObject.__proto__); // In order to emulate "instanceof".744 var self = this;745 var customWrapFunctions = this._customWrapFunctions();746 function processProperty(property)747 {748 if (typeof wrappedObject[property] === "function") {749 var customWrapFunction = customWrapFunctions[property];750 if (customWrapFunction)751 proxy[property] = self._wrapCustomFunction(self, wrappedObject, wrappedObject[property], property, customWrapFunction);752 else753 proxy[property] = self._wrapFunction(self, wrappedObject, wrappedObject[property], property);754 } else if (/^[A-Z0-9_]+$/.test(property) && typeof wrappedObject[property] === "number") {755 // Fast access to enums and constants.756 proxy[property] = wrappedObject[property];757 } else {758 Object.defineProperty(proxy, property, {759 get: function()760 {761 var obj = wrappedObject[property];762 var resource = Resource.forObject(obj);763 return resource ? resource : obj;764 },765 set: self._wrapPropertySetter(self, wrappedObject, property),766 enumerable: true767 });768 }769 }770 var isEmpty = true;771 for (var property in wrappedObject) {772 isEmpty = false;773 processProperty(property);774 }775 if (isEmpty)776 return wrappedObject; // Nothing to proxy.777 this._bindObjectToResource(proxy);778 return proxy;779 },780 /**781 * @param {!Resource} resource782 * @param {!Object} originalObject783 * @param {!Function} originalFunction784 * @param {string} functionName785 * @param {!Function} customWrapFunction786 * @return {!Function}787 */788 _wrapCustomFunction: function(resource, originalObject, originalFunction, functionName, customWrapFunction)789 {790 return function()791 {792 var manager = resource.manager();793 var isCapturing = manager && manager.capturing();794 if (isCapturing)795 manager.captureArguments(resource, arguments);796 var wrapFunction = new Resource.WrapFunction(originalObject, originalFunction, functionName, arguments);797 customWrapFunction.apply(wrapFunction, arguments);798 if (isCapturing) {799 var call = wrapFunction.call();800 call.setStackTrace(StackTrace.create(1, arguments.callee));801 manager.captureCall(call);802 }803 return wrapFunction.result();804 };805 },806 /**807 * @param {!Resource} resource808 * @param {!Object} originalObject809 * @param {!Function} originalFunction810 * @param {string} functionName811 * @return {!Function}812 */813 _wrapFunction: function(resource, originalObject, originalFunction, functionName)814 {815 return function()816 {817 var manager = resource.manager();818 if (!manager || !manager.capturing())819 return originalFunction.apply(originalObject, arguments);820 manager.captureArguments(resource, arguments);821 var result = originalFunction.apply(originalObject, arguments);822 var stackTrace = StackTrace.create(1, arguments.callee);823 var call = new Call(resource, functionName, arguments, result, stackTrace);824 manager.captureCall(call);825 return result;826 };827 },828 /**829 * @param {!Resource} resource830 * @param {!Object} originalObject831 * @param {string} propertyName832 * @return {function(*)}833 */834 _wrapPropertySetter: function(resource, originalObject, propertyName)835 {836 return function(value)837 {838 resource._registerBoundResource(propertyName, value);839 var manager = resource.manager();840 if (!manager || !manager.capturing()) {841 originalObject[propertyName] = Resource.wrappedObject(value);842 return;843 }844 var args = [propertyName, value];845 manager.captureArguments(resource, args);846 originalObject[propertyName] = Resource.wrappedObject(value);847 var stackTrace = StackTrace.create(1, arguments.callee);848 var call = new Call(resource, "", args, undefined, stackTrace);849 manager.captureCall(call);850 };851 },852 /**853 * @return {!Object.<string, Function>}854 */855 _customWrapFunctions: function()856 {857 return Object.create(null); // May be overridden by subclasses.858 }859}860/**861 * @constructor862 * @param {Object} originalObject863 * @param {Function} originalFunction864 * @param {string} functionName865 * @param {Array|Arguments} args866 */867Resource.WrapFunction = function(originalObject, originalFunction, functionName, args)868{869 this._originalObject = originalObject;870 this._originalFunction = originalFunction;871 this._functionName = functionName;872 this._args = args;873 this._resource = Resource.forObject(originalObject);874 console.assert(this._resource, "Expected a wrapped call on a Resource object.");875}876Resource.WrapFunction.prototype = {877 /**878 * @return {*}879 */880 result: function()881 {882 if (!this._executed) {883 this._executed = true;884 this._result = this._originalFunction.apply(this._originalObject, this._args);885 }886 return this._result;887 },888 /**889 * @return {!Call}890 */891 call: function()892 {893 if (!this._call)894 this._call = new Call(this._resource, this._functionName, this._args, this.result());895 return this._call;896 },897 /**898 * @param {*} result899 */900 overrideResult: function(result)901 {902 var call = this.call();903 call.setResult(result);904 this._result = result;905 }906}907/**908 * @param {function(new:Resource, !Object, string)} resourceConstructor909 * @param {string} resourceName910 * @return {function(this:Resource.WrapFunction)}911 */912Resource.WrapFunction.resourceFactoryMethod = function(resourceConstructor, resourceName)913{914 /** @this Resource.WrapFunction */915 return function()916 {917 var wrappedObject = /** @type {Object} */ (this.result());918 if (!wrappedObject)919 return;920 var resource = new resourceConstructor(wrappedObject, resourceName);921 var manager = this._resource.manager();922 if (manager)923 manager.registerResource(resource);924 this.overrideResult(resource.proxyObject());925 resource.pushCall(this.call());926 }927}928/**929 * @constructor930 * @param {!Resource} originalResource931 * @param {!Object} data932 */933function ReplayableResource(originalResource, data)934{935 this._proto = originalResource.__proto__;936 this._data = data;937}938ReplayableResource.prototype = {939 /**940 * @return {number}941 */942 id: function()943 {944 return this._data.id;945 },946 /**947 * @return {string}948 */949 name: function()950 {951 return this._data.name;952 },953 /**954 * @return {string}955 */956 description: function()957 {958 return this._data.name + "@" + this._data.kindId;959 },960 /**961 * @return {!ReplayableResource}962 */963 replayableContextResource: function()964 {965 return this._data.contextResource || this;966 },967 /**968 * @param {!Cache} cache969 * @return {!Resource}970 */971 replay: function(cache)972 {973 var result = /** @type {!Resource} */ (Object.create(this._proto));974 result = result.replay(this._data, cache)975 console.assert(result.__proto__ === this._proto, "Wrong type of a replay result");976 return result;977 }978}979/**980 * @param {ReplayableResource|*} obj981 * @param {!Cache} cache982 * @return {*}983 */984ReplayableResource.replay = function(obj, cache)985{986 return (obj instanceof ReplayableResource) ? obj.replay(cache).wrappedObject() : obj;987}988/**989 * @constructor990 * @extends {Resource}991 * @param {!Object} wrappedObject992 * @param {string} name993 */994function ContextResource(wrappedObject, name)995{996 Resource.call(this, wrappedObject, name);997}998ContextResource.prototype = {999 __proto__: Resource.prototype1000}1001/**1002 * @constructor1003 * @extends {Resource}1004 * @param {!Object} wrappedObject1005 * @param {string} name1006 */1007function LogEverythingResource(wrappedObject, name)1008{1009 Resource.call(this, wrappedObject, name);1010}1011LogEverythingResource.prototype = {1012 /**1013 * @override1014 * @return {!Object.<string, Function>}1015 */1016 _customWrapFunctions: function()1017 {1018 var wrapFunctions = Object.create(null);1019 var wrappedObject = this.wrappedObject();1020 if (wrappedObject) {1021 for (var property in wrappedObject) {1022 /** @this Resource.WrapFunction */1023 wrapFunctions[property] = function()1024 {1025 this._resource.pushCall(this.call());1026 }1027 }1028 }1029 return wrapFunctions;1030 },1031 __proto__: Resource.prototype1032}1033////////////////////////////////////////////////////////////////////////////////1034// WebGL1035////////////////////////////////////////////////////////////////////////////////1036/**1037 * @constructor1038 * @extends {Resource}1039 * @param {!Object} wrappedObject1040 * @param {string} name1041 */1042function WebGLBoundResource(wrappedObject, name)1043{1044 Resource.call(this, wrappedObject, name);1045 /** @type {!Object.<string, *>} */1046 this._state = {};1047}1048WebGLBoundResource.prototype = {1049 /**1050 * @override1051 * @param {!Object} data1052 * @param {!Cache} cache1053 */1054 _populateReplayableData: function(data, cache)1055 {1056 var state = this._state;1057 data.state = {};1058 Object.keys(state).forEach(function(parameter) {1059 data.state[parameter] = Resource.toReplayable(state[parameter], cache);1060 });1061 },1062 /**1063 * @override1064 * @param {!Object} data1065 * @param {!Cache} cache1066 */1067 _doReplayCalls: function(data, cache)1068 {1069 var gl = this._replayContextResource(data, cache).wrappedObject();1070 /** @type {!Object.<string, Array.<string>>} */1071 var bindingsData = {1072 TEXTURE_2D: ["bindTexture", "TEXTURE_BINDING_2D"],1073 TEXTURE_CUBE_MAP: ["bindTexture", "TEXTURE_BINDING_CUBE_MAP"],1074 ARRAY_BUFFER: ["bindBuffer", "ARRAY_BUFFER_BINDING"],1075 ELEMENT_ARRAY_BUFFER: ["bindBuffer", "ELEMENT_ARRAY_BUFFER_BINDING"],1076 FRAMEBUFFER: ["bindFramebuffer", "FRAMEBUFFER_BINDING"],1077 RENDERBUFFER: ["bindRenderbuffer", "RENDERBUFFER_BINDING"]1078 };1079 var originalBindings = {};1080 Object.keys(bindingsData).forEach(function(bindingTarget) {1081 var bindingParameter = bindingsData[bindingTarget][1];1082 originalBindings[bindingTarget] = gl.getParameter(gl[bindingParameter]);1083 });1084 var state = {};1085 Object.keys(data.state).forEach(function(parameter) {1086 state[parameter] = ReplayableResource.replay(data.state[parameter], cache);1087 });1088 this._state = state;1089 Resource.prototype._doReplayCalls.call(this, data, cache);1090 Object.keys(bindingsData).forEach(function(bindingTarget) {1091 var bindMethodName = bindingsData[bindingTarget][0];1092 gl[bindMethodName].call(gl, gl[bindingTarget], originalBindings[bindingTarget]);1093 });1094 },1095 /**1096 * @param {!Object} data1097 * @param {!Cache} cache1098 * @return {WebGLRenderingContextResource}1099 */1100 _replayContextResource: function(data, cache)1101 {1102 var calls = /** @type {!Array.<ReplayableCall>} */ (data.calls);1103 for (var i = 0, n = calls.length; i < n; ++i) {1104 var resource = ReplayableResource.replay(calls[i].replayableResource(), cache);1105 var contextResource = WebGLRenderingContextResource.forObject(resource);1106 if (contextResource)1107 return contextResource;1108 }1109 return null;1110 },1111 /**1112 * @param {number} target1113 * @param {string} bindMethodName1114 */1115 pushBinding: function(target, bindMethodName)1116 {1117 if (this._state.BINDING !== target) {1118 this._state.BINDING = target;1119 this.pushCall(new Call(WebGLRenderingContextResource.forObject(this), bindMethodName, [target, this]));1120 }1121 },1122 __proto__: Resource.prototype1123}1124/**1125 * @constructor1126 * @extends {WebGLBoundResource}1127 * @param {!Object} wrappedObject1128 * @param {string} name1129 */1130function WebGLTextureResource(wrappedObject, name)1131{1132 WebGLBoundResource.call(this, wrappedObject, name);1133}1134WebGLTextureResource.prototype = {1135 /**1136 * @override1137 * @param {!Object} data1138 * @param {!Cache} cache1139 */1140 _doReplayCalls: function(data, cache)1141 {1142 var gl = this._replayContextResource(data, cache).wrappedObject();1143 var state = {};1144 WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {1145 state[parameter] = gl.getParameter(gl[parameter]);1146 });1147 WebGLBoundResource.prototype._doReplayCalls.call(this, data, cache);1148 WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {1149 gl.pixelStorei(gl[parameter], state[parameter]);1150 });1151 },1152 /**1153 * @override1154 * @param {!Call} call1155 */1156 pushCall: function(call)1157 {1158 var gl = WebGLRenderingContextResource.forObject(call.resource()).wrappedObject();1159 WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {1160 var value = gl.getParameter(gl[parameter]);1161 if (this._state[parameter] !== value) {1162 this._state[parameter] = value;1163 var pixelStoreCall = new Call(gl, "pixelStorei", [gl[parameter], value]);1164 WebGLBoundResource.prototype.pushCall.call(this, pixelStoreCall);1165 }1166 }, this);1167 // FIXME: remove any older calls that no longer contribute to the resource state.1168 // FIXME: optimize memory usage: maybe it's more efficient to store one texImage2D call instead of many texSubImage2D.1169 WebGLBoundResource.prototype.pushCall.call(this, call);1170 },1171 /**1172 * Handles: texParameteri, texParameterf1173 * @param {!Call} call1174 */1175 pushCall_texParameter: function(call)1176 {1177 var args = call.args();1178 var pname = args[1];1179 var param = args[2];1180 if (this._state[pname] !== param) {1181 this._state[pname] = param;1182 WebGLBoundResource.prototype.pushCall.call(this, call);1183 }1184 },1185 /**1186 * Handles: copyTexImage2D, copyTexSubImage2D1187 * copyTexImage2D and copyTexSubImage2D define a texture image with pixels from the current framebuffer.1188 * @param {!Call} call1189 */1190 pushCall_copyTexImage2D: function(call)1191 {1192 var glResource = WebGLRenderingContextResource.forObject(call.resource());1193 var gl = glResource.wrappedObject();1194 var framebufferResource = /** @type {WebGLFramebufferResource} */ (glResource.currentBinding(gl.FRAMEBUFFER));1195 if (framebufferResource)1196 this.pushCall(new Call(glResource, "bindFramebuffer", [gl.FRAMEBUFFER, framebufferResource]));1197 else {1198 // FIXME: Implement this case.1199 console.error("ASSERT_NOT_REACHED: Could not properly process a gl." + call.functionName() + " call while the DRAWING BUFFER is bound.");1200 }1201 this.pushCall(call);1202 },1203 __proto__: WebGLBoundResource.prototype1204}1205/**1206 * @constructor1207 * @extends {Resource}1208 * @param {!Object} wrappedObject1209 * @param {string} name1210 */1211function WebGLProgramResource(wrappedObject, name)1212{1213 Resource.call(this, wrappedObject, name);1214}1215WebGLProgramResource.prototype = {1216 /**1217 * @override (overrides @return type)1218 * @return {WebGLProgram}1219 */1220 wrappedObject: function()1221 {1222 return this._wrappedObject;1223 },1224 /**1225 * @override1226 * @param {!Object} data1227 * @param {!Cache} cache1228 */1229 _populateReplayableData: function(data, cache)1230 {1231 var glResource = WebGLRenderingContextResource.forObject(this);1232 var gl = glResource.wrappedObject();1233 var program = this.wrappedObject();1234 var originalErrors = glResource.getAllErrors();1235 var uniforms = [];1236 var uniformsCount = /** @type {number} */ (gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS));1237 for (var i = 0; i < uniformsCount; ++i) {1238 var activeInfo = gl.getActiveUniform(program, i);1239 if (!activeInfo)1240 continue;1241 var uniformLocation = gl.getUniformLocation(program, activeInfo.name);1242 if (!uniformLocation)1243 continue;1244 var value = gl.getUniform(program, uniformLocation);1245 uniforms.push({1246 name: activeInfo.name,1247 type: activeInfo.type,1248 value: value1249 });1250 }1251 data.uniforms = uniforms;1252 glResource.restoreErrors(originalErrors);1253 },1254 /**1255 * @override1256 * @param {!Object} data1257 * @param {!Cache} cache1258 */1259 _doReplayCalls: function(data, cache)1260 {1261 Resource.prototype._doReplayCalls.call(this, data, cache);1262 var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();1263 var program = this.wrappedObject();1264 var originalProgram = /** @type {WebGLProgram} */ (gl.getParameter(gl.CURRENT_PROGRAM));1265 var currentProgram = originalProgram;1266 1267 data.uniforms.forEach(function(uniform) {1268 var uniformLocation = gl.getUniformLocation(program, uniform.name);1269 if (!uniformLocation)1270 return;1271 if (currentProgram !== program) {1272 currentProgram = program;1273 gl.useProgram(program);1274 }1275 var methodName = this._uniformMethodNameByType(gl, uniform.type);1276 if (methodName.indexOf("Matrix") === -1)1277 gl[methodName].call(gl, uniformLocation, uniform.value);1278 else1279 gl[methodName].call(gl, uniformLocation, false, uniform.value);1280 }.bind(this));1281 if (currentProgram !== originalProgram)1282 gl.useProgram(originalProgram);1283 },1284 /**1285 * @param {WebGLRenderingContext} gl1286 * @param {number} type1287 * @return {string}1288 */1289 _uniformMethodNameByType: function(gl, type)1290 {1291 var uniformMethodNames = WebGLProgramResource._uniformMethodNames;1292 if (!uniformMethodNames) {1293 uniformMethodNames = {};1294 uniformMethodNames[gl.FLOAT] = "uniform1f";1295 uniformMethodNames[gl.FLOAT_VEC2] = "uniform2fv";1296 uniformMethodNames[gl.FLOAT_VEC3] = "uniform3fv";1297 uniformMethodNames[gl.FLOAT_VEC4] = "uniform4fv";1298 uniformMethodNames[gl.INT] = "uniform1i";1299 uniformMethodNames[gl.BOOL] = "uniform1i";1300 uniformMethodNames[gl.SAMPLER_2D] = "uniform1i";1301 uniformMethodNames[gl.SAMPLER_CUBE] = "uniform1i";1302 uniformMethodNames[gl.INT_VEC2] = "uniform2iv";1303 uniformMethodNames[gl.BOOL_VEC2] = "uniform2iv";1304 uniformMethodNames[gl.INT_VEC3] = "uniform3iv";1305 uniformMethodNames[gl.BOOL_VEC3] = "uniform3iv";1306 uniformMethodNames[gl.INT_VEC4] = "uniform4iv";1307 uniformMethodNames[gl.BOOL_VEC4] = "uniform4iv";1308 uniformMethodNames[gl.FLOAT_MAT2] = "uniformMatrix2fv";1309 uniformMethodNames[gl.FLOAT_MAT3] = "uniformMatrix3fv";1310 uniformMethodNames[gl.FLOAT_MAT4] = "uniformMatrix4fv";1311 WebGLProgramResource._uniformMethodNames = uniformMethodNames;1312 }1313 console.assert(uniformMethodNames[type], "Unknown uniform type " + type);1314 return uniformMethodNames[type];1315 },1316 /**1317 * @override1318 * @param {!Call} call1319 */1320 pushCall: function(call)1321 {1322 // FIXME: remove any older calls that no longer contribute to the resource state.1323 // FIXME: handle multiple attachShader && detachShader.1324 Resource.prototype.pushCall.call(this, call);1325 },1326 __proto__: Resource.prototype1327}1328/**1329 * @constructor1330 * @extends {Resource}1331 * @param {!Object} wrappedObject1332 * @param {string} name1333 */1334function WebGLShaderResource(wrappedObject, name)1335{1336 Resource.call(this, wrappedObject, name);1337}1338WebGLShaderResource.prototype = {1339 /**1340 * @return {number}1341 */1342 type: function()1343 {1344 var call = this._calls[0];1345 if (call && call.functionName() === "createShader")1346 return call.args()[0];1347 console.error("ASSERT_NOT_REACHED: Failed to restore shader type from the log.", call);1348 return 0;1349 },1350 /**1351 * @override1352 * @param {!Call} call1353 */1354 pushCall: function(call)1355 {1356 // FIXME: remove any older calls that no longer contribute to the resource state.1357 // FIXME: handle multiple shaderSource calls.1358 Resource.prototype.pushCall.call(this, call);1359 },1360 __proto__: Resource.prototype1361}1362/**1363 * @constructor1364 * @extends {WebGLBoundResource}1365 * @param {!Object} wrappedObject1366 * @param {string} name1367 */1368function WebGLBufferResource(wrappedObject, name)1369{1370 WebGLBoundResource.call(this, wrappedObject, name);1371}1372WebGLBufferResource.prototype = {1373 /**1374 * @override1375 * @param {!Call} call1376 */1377 pushCall: function(call)1378 {1379 // FIXME: remove any older calls that no longer contribute to the resource state.1380 // FIXME: Optimize memory for bufferSubData.1381 WebGLBoundResource.prototype.pushCall.call(this, call);1382 },1383 __proto__: WebGLBoundResource.prototype1384}1385/**1386 * @constructor1387 * @extends {WebGLBoundResource}1388 * @param {!Object} wrappedObject1389 * @param {string} name1390 */1391function WebGLFramebufferResource(wrappedObject, name)1392{1393 WebGLBoundResource.call(this, wrappedObject, name);1394}1395WebGLFramebufferResource.prototype = {1396 /**1397 * @override1398 * @param {!Call} call1399 */1400 pushCall: function(call)1401 {1402 // FIXME: remove any older calls that no longer contribute to the resource state.1403 WebGLBoundResource.prototype.pushCall.call(this, call);1404 },1405 __proto__: WebGLBoundResource.prototype1406}1407/**1408 * @constructor1409 * @extends {WebGLBoundResource}1410 * @param {!Object} wrappedObject1411 * @param {string} name1412 */1413function WebGLRenderbufferResource(wrappedObject, name)1414{1415 WebGLBoundResource.call(this, wrappedObject, name);1416}1417WebGLRenderbufferResource.prototype = {1418 /**1419 * @override1420 * @param {!Call} call1421 */1422 pushCall: function(call)1423 {1424 // FIXME: remove any older calls that no longer contribute to the resource state.1425 WebGLBoundResource.prototype.pushCall.call(this, call);1426 },1427 __proto__: WebGLBoundResource.prototype1428}1429/**1430 * @constructor1431 * @extends {ContextResource}1432 * @param {!WebGLRenderingContext} glContext1433 */1434function WebGLRenderingContextResource(glContext)1435{1436 ContextResource.call(this, glContext, "WebGLRenderingContext");1437 /** @type {Object.<number, boolean>} */1438 this._customErrors = null;1439 /** @type {!Object.<string, boolean>} */1440 this._extensions = {};1441}1442/**1443 * @const1444 * @type {!Array.<string>}1445 */1446WebGLRenderingContextResource.GLCapabilities = [1447 "BLEND",1448 "CULL_FACE",1449 "DEPTH_TEST",1450 "DITHER",1451 "POLYGON_OFFSET_FILL",1452 "SAMPLE_ALPHA_TO_COVERAGE",1453 "SAMPLE_COVERAGE",1454 "SCISSOR_TEST",1455 "STENCIL_TEST"1456];1457/**1458 * @const1459 * @type {!Array.<string>}1460 */1461WebGLRenderingContextResource.PixelStoreParameters = [1462 "PACK_ALIGNMENT",1463 "UNPACK_ALIGNMENT",1464 "UNPACK_COLORSPACE_CONVERSION_WEBGL",1465 "UNPACK_FLIP_Y_WEBGL",1466 "UNPACK_PREMULTIPLY_ALPHA_WEBGL"1467];1468/**1469 * @const1470 * @type {!Array.<string>}1471 */1472WebGLRenderingContextResource.StateParameters = [1473 "ACTIVE_TEXTURE",1474 "ARRAY_BUFFER_BINDING",1475 "BLEND_COLOR",1476 "BLEND_DST_ALPHA",1477 "BLEND_DST_RGB",1478 "BLEND_EQUATION_ALPHA",1479 "BLEND_EQUATION_RGB",1480 "BLEND_SRC_ALPHA",1481 "BLEND_SRC_RGB",1482 "COLOR_CLEAR_VALUE",1483 "COLOR_WRITEMASK",1484 "CULL_FACE_MODE",1485 "CURRENT_PROGRAM",1486 "DEPTH_CLEAR_VALUE",1487 "DEPTH_FUNC",1488 "DEPTH_RANGE",1489 "DEPTH_WRITEMASK",1490 "ELEMENT_ARRAY_BUFFER_BINDING",1491 "FRAMEBUFFER_BINDING",1492 "FRONT_FACE",1493 "GENERATE_MIPMAP_HINT",1494 "LINE_WIDTH",1495 "PACK_ALIGNMENT",1496 "POLYGON_OFFSET_FACTOR",1497 "POLYGON_OFFSET_UNITS",1498 "RENDERBUFFER_BINDING",1499 "SAMPLE_COVERAGE_INVERT",1500 "SAMPLE_COVERAGE_VALUE",1501 "SCISSOR_BOX",1502 "STENCIL_BACK_FAIL",1503 "STENCIL_BACK_FUNC",1504 "STENCIL_BACK_PASS_DEPTH_FAIL",1505 "STENCIL_BACK_PASS_DEPTH_PASS",1506 "STENCIL_BACK_REF",1507 "STENCIL_BACK_VALUE_MASK",1508 "STENCIL_BACK_WRITEMASK",1509 "STENCIL_CLEAR_VALUE",1510 "STENCIL_FAIL",1511 "STENCIL_FUNC",1512 "STENCIL_PASS_DEPTH_FAIL",1513 "STENCIL_PASS_DEPTH_PASS",1514 "STENCIL_REF",1515 "STENCIL_VALUE_MASK",1516 "STENCIL_WRITEMASK",1517 "UNPACK_ALIGNMENT",1518 "UNPACK_COLORSPACE_CONVERSION_WEBGL",1519 "UNPACK_FLIP_Y_WEBGL",1520 "UNPACK_PREMULTIPLY_ALPHA_WEBGL",1521 "VIEWPORT"1522];1523/**1524 * @const1525 * @type {!Object.<string, boolean>}1526 */1527WebGLRenderingContextResource.DrawingMethods = TypeUtils.createPrefixedPropertyNamesSet([1528 "clear",1529 "drawArrays",1530 "drawElements"1531]);1532/**1533 * @param {*} obj1534 * @return {WebGLRenderingContextResource}1535 */1536WebGLRenderingContextResource.forObject = function(obj)1537{1538 var resource = Resource.forObject(obj);1539 if (!resource)1540 return null;1541 resource = resource.contextResource();1542 return (resource instanceof WebGLRenderingContextResource) ? resource : null;1543}1544WebGLRenderingContextResource.prototype = {1545 /**1546 * @override (overrides @return type)1547 * @return {WebGLRenderingContext}1548 */1549 wrappedObject: function()1550 {1551 return this._wrappedObject;1552 },1553 /**1554 * @override1555 * @return {string}1556 */1557 toDataURL: function()1558 {1559 return this.wrappedObject().canvas.toDataURL();1560 },1561 /**1562 * @return {Array.<number>}1563 */1564 getAllErrors: function()1565 {1566 var errors = [];1567 var gl = this.wrappedObject();1568 if (gl) {1569 while (true) {1570 var error = gl.getError();1571 if (error === gl.NO_ERROR)1572 break;1573 this.clearError(error);1574 errors.push(error);1575 }1576 }1577 if (this._customErrors) {1578 for (var key in this._customErrors) {1579 var error = Number(key);1580 errors.push(error);1581 }1582 delete this._customErrors;1583 }1584 return errors;1585 },1586 /**1587 * @param {Array.<number>} errors1588 */1589 restoreErrors: function(errors)1590 {1591 var gl = this.wrappedObject();1592 if (gl) {1593 var wasError = false;1594 while (gl.getError() !== gl.NO_ERROR)1595 wasError = true;1596 console.assert(!wasError, "Error(s) while capturing current WebGL state.");1597 }1598 if (!errors.length)1599 delete this._customErrors;1600 else {1601 this._customErrors = {};1602 for (var i = 0, n = errors.length; i < n; ++i)1603 this._customErrors[errors[i]] = true;1604 }1605 },1606 /**1607 * @param {number} error1608 */1609 clearError: function(error)1610 {1611 if (this._customErrors)1612 delete this._customErrors[error];1613 },1614 /**1615 * @return {number}1616 */1617 nextError: function()1618 {1619 if (this._customErrors) {1620 for (var key in this._customErrors) {1621 var error = Number(key);1622 delete this._customErrors[error];1623 return error;1624 }1625 }1626 delete this._customErrors;1627 var gl = this.wrappedObject();1628 return gl ? gl.NO_ERROR : 0;1629 },1630 /**1631 * @param {string} name1632 */1633 addExtension: function(name)1634 {1635 // FIXME: Wrap OES_vertex_array_object extension.1636 this._extensions[name.toLowerCase()] = true;1637 },1638 /**1639 * @override1640 * @param {!Object} data1641 * @param {!Cache} cache1642 */1643 _populateReplayableData: function(data, cache)1644 {1645 var gl = this.wrappedObject();1646 data.originalCanvas = gl.canvas;1647 data.originalContextAttributes = gl.getContextAttributes();1648 data.extensions = TypeUtils.cloneObject(this._extensions);1649 var originalErrors = this.getAllErrors();1650 // Take a full GL state snapshot.1651 var glState = {};1652 WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {1653 glState[parameter] = gl.isEnabled(gl[parameter]);1654 });1655 WebGLRenderingContextResource.StateParameters.forEach(function(parameter) {1656 glState[parameter] = Resource.toReplayable(gl.getParameter(gl[parameter]), cache);1657 });1658 // VERTEX_ATTRIB_ARRAYS1659 var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VERTEX_ATTRIBS));1660 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"];1661 var vertexAttribStates = [];1662 for (var i = 0; i < maxVertexAttribs; ++i) {1663 var state = {};1664 vertexAttribParameters.forEach(function(attribParameter) {1665 state[attribParameter] = Resource.toReplayable(gl.getVertexAttrib(i, gl[attribParameter]), cache);1666 });1667 state.VERTEX_ATTRIB_ARRAY_POINTER = gl.getVertexAttribOffset(i, gl.VERTEX_ATTRIB_ARRAY_POINTER);1668 vertexAttribStates.push(state);1669 }1670 glState.vertexAttribStates = vertexAttribStates;1671 // TEXTURES1672 var currentTextureBinding = /** @type {number} */ (gl.getParameter(gl.ACTIVE_TEXTURE));1673 var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));1674 var textureBindings = [];1675 for (var i = 0; i < maxTextureImageUnits; ++i) {1676 gl.activeTexture(gl.TEXTURE0 + i);1677 var state = {1678 TEXTURE_2D: Resource.toReplayable(gl.getParameter(gl.TEXTURE_BINDING_2D), cache),1679 TEXTURE_CUBE_MAP: Resource.toReplayable(gl.getParameter(gl.TEXTURE_BINDING_CUBE_MAP), cache)1680 };1681 textureBindings.push(state);1682 }1683 glState.textureBindings = textureBindings;1684 gl.activeTexture(currentTextureBinding);1685 data.glState = glState;1686 this.restoreErrors(originalErrors);1687 },1688 /**1689 * @override1690 * @param {!Object} data1691 * @param {!Cache} cache1692 */1693 _doReplayCalls: function(data, cache)1694 {1695 this._customErrors = null;1696 this._extensions = TypeUtils.cloneObject(data.extensions) || {};1697 var canvas = data.originalCanvas.cloneNode(true);1698 var replayContext = null;1699 var contextIds = ["experimental-webgl", "webkit-3d", "3d"];1700 for (var i = 0, contextId; contextId = contextIds[i]; ++i) {1701 replayContext = canvas.getContext(contextId, data.originalContextAttributes);1702 if (replayContext)1703 break;1704 }1705 console.assert(replayContext, "Failed to create a WebGLRenderingContext for the replay.");1706 var gl = /** @type {!WebGLRenderingContext} */ (Resource.wrappedObject(replayContext));1707 this.setWrappedObject(gl);1708 // Enable corresponding WebGL extensions.1709 for (var name in this._extensions)1710 gl.getExtension(name);1711 var glState = data.glState;1712 gl.bindFramebuffer(gl.FRAMEBUFFER, /** @type {WebGLFramebuffer} */ (ReplayableResource.replay(glState.FRAMEBUFFER_BINDING, cache)));1713 gl.bindRenderbuffer(gl.RENDERBUFFER, /** @type {WebGLRenderbuffer} */ (ReplayableResource.replay(glState.RENDERBUFFER_BINDING, cache)));1714 // Enable or disable server-side GL capabilities.1715 WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {1716 console.assert(parameter in glState);1717 if (glState[parameter])1718 gl.enable(gl[parameter]);1719 else1720 gl.disable(gl[parameter]);1721 });1722 gl.blendColor(glState.BLEND_COLOR[0], glState.BLEND_COLOR[1], glState.BLEND_COLOR[2], glState.BLEND_COLOR[3]);1723 gl.blendEquationSeparate(glState.BLEND_EQUATION_RGB, glState.BLEND_EQUATION_ALPHA);1724 gl.blendFuncSeparate(glState.BLEND_SRC_RGB, glState.BLEND_DST_RGB, glState.BLEND_SRC_ALPHA, glState.BLEND_DST_ALPHA);1725 gl.clearColor(glState.COLOR_CLEAR_VALUE[0], glState.COLOR_CLEAR_VALUE[1], glState.COLOR_CLEAR_VALUE[2], glState.COLOR_CLEAR_VALUE[3]);1726 gl.clearDepth(glState.DEPTH_CLEAR_VALUE);1727 gl.clearStencil(glState.STENCIL_CLEAR_VALUE);1728 gl.colorMask(glState.COLOR_WRITEMASK[0], glState.COLOR_WRITEMASK[1], glState.COLOR_WRITEMASK[2], glState.COLOR_WRITEMASK[3]);1729 gl.cullFace(glState.CULL_FACE_MODE);1730 gl.depthFunc(glState.DEPTH_FUNC);1731 gl.depthMask(glState.DEPTH_WRITEMASK);1732 gl.depthRange(glState.DEPTH_RANGE[0], glState.DEPTH_RANGE[1]);1733 gl.frontFace(glState.FRONT_FACE);1734 gl.hint(gl.GENERATE_MIPMAP_HINT, glState.GENERATE_MIPMAP_HINT);1735 gl.lineWidth(glState.LINE_WIDTH);1736 WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {1737 gl.pixelStorei(gl[parameter], glState[parameter]);1738 });1739 gl.polygonOffset(glState.POLYGON_OFFSET_FACTOR, glState.POLYGON_OFFSET_UNITS);1740 gl.sampleCoverage(glState.SAMPLE_COVERAGE_VALUE, glState.SAMPLE_COVERAGE_INVERT);1741 gl.stencilFuncSeparate(gl.FRONT, glState.STENCIL_FUNC, glState.STENCIL_REF, glState.STENCIL_VALUE_MASK);1742 gl.stencilFuncSeparate(gl.BACK, glState.STENCIL_BACK_FUNC, glState.STENCIL_BACK_REF, glState.STENCIL_BACK_VALUE_MASK);1743 gl.stencilOpSeparate(gl.FRONT, glState.STENCIL_FAIL, glState.STENCIL_PASS_DEPTH_FAIL, glState.STENCIL_PASS_DEPTH_PASS);1744 gl.stencilOpSeparate(gl.BACK, glState.STENCIL_BACK_FAIL, glState.STENCIL_BACK_PASS_DEPTH_FAIL, glState.STENCIL_BACK_PASS_DEPTH_PASS);1745 gl.stencilMaskSeparate(gl.FRONT, glState.STENCIL_WRITEMASK);1746 gl.stencilMaskSeparate(gl.BACK, glState.STENCIL_BACK_WRITEMASK);1747 gl.scissor(glState.SCISSOR_BOX[0], glState.SCISSOR_BOX[1], glState.SCISSOR_BOX[2], glState.SCISSOR_BOX[3]);1748 gl.viewport(glState.VIEWPORT[0], glState.VIEWPORT[1], glState.VIEWPORT[2], glState.VIEWPORT[3]);1749 gl.useProgram(/** @type {WebGLProgram} */ (ReplayableResource.replay(glState.CURRENT_PROGRAM, cache)));1750 // VERTEX_ATTRIB_ARRAYS1751 var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VERTEX_ATTRIBS));1752 for (var i = 0; i < maxVertexAttribs; ++i) {1753 var state = glState.vertexAttribStates[i] || {};1754 if (state.VERTEX_ATTRIB_ARRAY_ENABLED)1755 gl.enableVertexAttribArray(i);1756 else1757 gl.disableVertexAttribArray(i);1758 if (state.CURRENT_VERTEX_ATTRIB)1759 gl.vertexAttrib4fv(i, state.CURRENT_VERTEX_ATTRIB);1760 var buffer = /** @type {WebGLBuffer} */ (ReplayableResource.replay(state.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, cache));1761 if (buffer) {1762 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);1763 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);1764 }1765 }1766 gl.bindBuffer(gl.ARRAY_BUFFER, /** @type {WebGLBuffer} */ (ReplayableResource.replay(glState.ARRAY_BUFFER_BINDING, cache)));1767 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, /** @type {WebGLBuffer} */ (ReplayableResource.replay(glState.ELEMENT_ARRAY_BUFFER_BINDING, cache)));1768 // TEXTURES1769 var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));1770 for (var i = 0; i < maxTextureImageUnits; ++i) {1771 gl.activeTexture(gl.TEXTURE0 + i);1772 var state = glState.textureBindings[i] || {};1773 gl.bindTexture(gl.TEXTURE_2D, /** @type {WebGLTexture} */ (ReplayableResource.replay(state.TEXTURE_2D, cache)));1774 gl.bindTexture(gl.TEXTURE_CUBE_MAP, /** @type {WebGLTexture} */ (ReplayableResource.replay(state.TEXTURE_CUBE_MAP, cache)));1775 }1776 gl.activeTexture(glState.ACTIVE_TEXTURE);1777 ContextResource.prototype._doReplayCalls.call(this, data, cache);1778 },1779 /**1780 * @param {Object|number} target1781 * @return {Resource}1782 */1783 currentBinding: function(target)1784 {1785 var resource = Resource.forObject(target);1786 if (resource)1787 return resource;1788 var gl = this.wrappedObject();1789 var bindingParameter;1790 var bindMethodName;1791 var bindMethodTarget = target;1792 switch (target) {1793 case gl.ARRAY_BUFFER:1794 bindingParameter = gl.ARRAY_BUFFER_BINDING;1795 bindMethodName = "bindBuffer";1796 break;1797 case gl.ELEMENT_ARRAY_BUFFER:1798 bindingParameter = gl.ELEMENT_ARRAY_BUFFER_BINDING;1799 bindMethodName = "bindBuffer";1800 break;1801 case gl.TEXTURE_2D:1802 bindingParameter = gl.TEXTURE_BINDING_2D;1803 bindMethodName = "bindTexture";1804 break;1805 case gl.TEXTURE_CUBE_MAP:1806 case gl.TEXTURE_CUBE_MAP_POSITIVE_X:1807 case gl.TEXTURE_CUBE_MAP_NEGATIVE_X:1808 case gl.TEXTURE_CUBE_MAP_POSITIVE_Y:1809 case gl.TEXTURE_CUBE_MAP_NEGATIVE_Y:1810 case gl.TEXTURE_CUBE_MAP_POSITIVE_Z:1811 case gl.TEXTURE_CUBE_MAP_NEGATIVE_Z:1812 bindingParameter = gl.TEXTURE_BINDING_CUBE_MAP;1813 bindMethodTarget = gl.TEXTURE_CUBE_MAP;1814 bindMethodName = "bindTexture";1815 break;1816 case gl.FRAMEBUFFER:1817 bindingParameter = gl.FRAMEBUFFER_BINDING;1818 bindMethodName = "bindFramebuffer";1819 break;1820 case gl.RENDERBUFFER:1821 bindingParameter = gl.RENDERBUFFER_BINDING;1822 bindMethodName = "bindRenderbuffer";1823 break;1824 default:1825 console.error("ASSERT_NOT_REACHED: unknown binding target " + target);1826 return null;1827 }1828 resource = Resource.forObject(gl.getParameter(bindingParameter));1829 if (resource)1830 resource.pushBinding(bindMethodTarget, bindMethodName);1831 return resource;1832 },1833 /**1834 * @override1835 * @return {!Object.<string, Function>}1836 */1837 _customWrapFunctions: function()1838 {1839 var wrapFunctions = WebGLRenderingContextResource._wrapFunctions;1840 if (!wrapFunctions) {1841 wrapFunctions = Object.create(null);1842 wrapFunctions["createBuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLBufferResource, "WebGLBuffer");1843 wrapFunctions["createShader"] = Resource.WrapFunction.resourceFactoryMethod(WebGLShaderResource, "WebGLShader");1844 wrapFunctions["createProgram"] = Resource.WrapFunction.resourceFactoryMethod(WebGLProgramResource, "WebGLProgram");1845 wrapFunctions["createTexture"] = Resource.WrapFunction.resourceFactoryMethod(WebGLTextureResource, "WebGLTexture");1846 wrapFunctions["createFramebuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLFramebufferResource, "WebGLFramebuffer");1847 wrapFunctions["createRenderbuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLRenderbufferResource, "WebGLRenderbuffer");1848 wrapFunctions["getUniformLocation"] = Resource.WrapFunction.resourceFactoryMethod(Resource, "WebGLUniformLocation");1849 /**1850 * @param {string} methodName1851 * @param {function(this:Resource, !Call)=} pushCallFunc1852 */1853 function stateModifyingWrapFunction(methodName, pushCallFunc)1854 {1855 if (pushCallFunc) {1856 /**1857 * @param {Object|number} target1858 * @this Resource.WrapFunction1859 */1860 wrapFunctions[methodName] = function(target)1861 {1862 var resource = this._resource.currentBinding(target);1863 if (resource)1864 pushCallFunc.call(resource, this.call());1865 }1866 } else {1867 /**1868 * @param {Object|number} target1869 * @this Resource.WrapFunction1870 */1871 wrapFunctions[methodName] = function(target)1872 {1873 var resource = this._resource.currentBinding(target);1874 if (resource)1875 resource.pushCall(this.call());1876 }1877 }1878 }1879 stateModifyingWrapFunction("bindAttribLocation");1880 stateModifyingWrapFunction("compileShader");1881 stateModifyingWrapFunction("detachShader");1882 stateModifyingWrapFunction("linkProgram");1883 stateModifyingWrapFunction("shaderSource");1884 stateModifyingWrapFunction("bufferData");1885 stateModifyingWrapFunction("bufferSubData");1886 stateModifyingWrapFunction("compressedTexImage2D");1887 stateModifyingWrapFunction("compressedTexSubImage2D");1888 stateModifyingWrapFunction("copyTexImage2D", WebGLTextureResource.prototype.pushCall_copyTexImage2D);1889 stateModifyingWrapFunction("copyTexSubImage2D", WebGLTextureResource.prototype.pushCall_copyTexImage2D);1890 stateModifyingWrapFunction("generateMipmap");1891 stateModifyingWrapFunction("texImage2D");1892 stateModifyingWrapFunction("texSubImage2D");1893 stateModifyingWrapFunction("texParameterf", WebGLTextureResource.prototype.pushCall_texParameter);1894 stateModifyingWrapFunction("texParameteri", WebGLTextureResource.prototype.pushCall_texParameter);1895 stateModifyingWrapFunction("renderbufferStorage");1896 /** @this Resource.WrapFunction */1897 wrapFunctions["getError"] = function()1898 {1899 var gl = /** @type {WebGLRenderingContext} */ (this._originalObject);1900 var error = this.result();1901 if (error !== gl.NO_ERROR)1902 this._resource.clearError(error);1903 else {1904 error = this._resource.nextError();1905 if (error !== gl.NO_ERROR)1906 this.overrideResult(error);1907 }1908 }1909 /**1910 * @param {string} name1911 * @this Resource.WrapFunction1912 */1913 wrapFunctions["getExtension"] = function(name)1914 {1915 this._resource.addExtension(name);1916 }1917 //1918 // Register bound WebGL resources.1919 //1920 /**1921 * @param {WebGLProgram} program1922 * @param {WebGLShader} shader1923 * @this Resource.WrapFunction1924 */1925 wrapFunctions["attachShader"] = function(program, shader)1926 {1927 var resource = this._resource.currentBinding(program);1928 if (resource) {1929 resource.pushCall(this.call());1930 var shaderResource = /** @type {WebGLShaderResource} */ (Resource.forObject(shader));1931 if (shaderResource) {1932 var shaderType = shaderResource.type();1933 resource._registerBoundResource("__attachShader_" + shaderType, shaderResource);1934 }1935 }1936 }1937 /**1938 * @param {number} target1939 * @param {number} attachment1940 * @param {number} objectTarget1941 * @param {WebGLRenderbuffer|WebGLTexture} obj1942 * @this Resource.WrapFunction1943 */1944 wrapFunctions["framebufferRenderbuffer"] = wrapFunctions["framebufferTexture2D"] = function(target, attachment, objectTarget, obj)1945 {1946 var resource = this._resource.currentBinding(target);1947 if (resource) {1948 resource.pushCall(this.call());1949 resource._registerBoundResource("__framebufferAttachmentObjectName", obj);1950 }1951 }1952 /**1953 * @param {number} target1954 * @param {Object} obj1955 * @this Resource.WrapFunction1956 */1957 wrapFunctions["bindBuffer"] = wrapFunctions["bindFramebuffer"] = wrapFunctions["bindRenderbuffer"] = function(target, obj)1958 {1959 this._resource._registerBoundResource("__bindBuffer_" + target, obj);1960 }1961 /**1962 * @param {number} target1963 * @param {WebGLTexture} obj1964 * @this Resource.WrapFunction1965 */1966 wrapFunctions["bindTexture"] = function(target, obj)1967 {1968 var gl = /** @type {WebGLRenderingContext} */ (this._originalObject);1969 var currentTextureBinding = /** @type {number} */ (gl.getParameter(gl.ACTIVE_TEXTURE));1970 this._resource._registerBoundResource("__bindTexture_" + target + "_" + currentTextureBinding, obj);1971 }1972 /**1973 * @param {WebGLProgram} program1974 * @this Resource.WrapFunction1975 */1976 wrapFunctions["useProgram"] = function(program)1977 {1978 this._resource._registerBoundResource("__useProgram", program);1979 }1980 /**1981 * @param {number} index1982 * @this Resource.WrapFunction1983 */1984 wrapFunctions["vertexAttribPointer"] = function(index)1985 {1986 var gl = /** @type {WebGLRenderingContext} */ (this._originalObject);1987 this._resource._registerBoundResource("__vertexAttribPointer_" + index, gl.getParameter(gl.ARRAY_BUFFER_BINDING));1988 }1989 WebGLRenderingContextResource._wrapFunctions = wrapFunctions;1990 }1991 return wrapFunctions;1992 },1993 __proto__: ContextResource.prototype1994}1995////////////////////////////////////////////////////////////////////////////////1996// 2D Canvas1997////////////////////////////////////////////////////////////////////////////////1998/**1999 * @constructor2000 * @extends {ContextResource}2001 * @param {!CanvasRenderingContext2D} context2002 */2003function CanvasRenderingContext2DResource(context)2004{2005 ContextResource.call(this, context, "CanvasRenderingContext2D");2006}2007/**2008 * @const2009 * @type {!Array.<string>}2010 */2011CanvasRenderingContext2DResource.AttributeProperties = [2012 "strokeStyle",2013 "fillStyle",2014 "globalAlpha",2015 "lineWidth",2016 "lineCap",2017 "lineJoin",2018 "miterLimit",2019 "shadowOffsetX",2020 "shadowOffsetY",2021 "shadowBlur",2022 "shadowColor",2023 "globalCompositeOperation",2024 "font",2025 "textAlign",2026 "textBaseline",2027 "lineDashOffset",2028 "webkitLineDash",2029 "webkitLineDashOffset"2030];2031/**2032 * @const2033 * @type {!Array.<string>}2034 */2035CanvasRenderingContext2DResource.PathMethods = [2036 "beginPath",2037 "moveTo",2038 "closePath",2039 "lineTo",2040 "quadraticCurveTo",2041 "bezierCurveTo",2042 "arcTo",2043 "arc",2044 "rect"2045];2046/**2047 * @const2048 * @type {!Array.<string>}2049 */2050CanvasRenderingContext2DResource.TransformationMatrixMethods = [2051 "scale",2052 "rotate",2053 "translate",2054 "transform",2055 "setTransform"2056];2057/**2058 * @const2059 * @type {!Object.<string, boolean>}2060 */2061CanvasRenderingContext2DResource.DrawingMethods = TypeUtils.createPrefixedPropertyNamesSet([2062 "clearRect",2063 "drawImage",2064 "drawImageFromRect",2065 "drawCustomFocusRing",2066 "drawSystemFocusRing",2067 "fill",2068 "fillRect",2069 "fillText",2070 "putImageData",2071 "putImageDataHD",2072 "stroke",2073 "strokeRect",2074 "strokeText"2075]);2076CanvasRenderingContext2DResource.prototype = {2077 /**2078 * @override (overrides @return type)2079 * @return {CanvasRenderingContext2D}2080 */2081 wrappedObject: function()2082 {2083 return this._wrappedObject;2084 },2085 /**2086 * @override2087 * @return {string}2088 */2089 toDataURL: function()2090 {2091 return this.wrappedObject().canvas.toDataURL();2092 },2093 /**2094 * @override2095 * @param {!Object} data2096 * @param {!Cache} cache2097 */2098 _populateReplayableData: function(data, cache)2099 {2100 data.currentAttributes = this._currentAttributesState();2101 data.originalCanvasCloned = TypeUtils.cloneIntoCanvas(this.wrappedObject().canvas);2102 },2103 /**2104 * @override2105 * @param {!Object} data2106 * @param {!Cache} cache2107 */2108 _doReplayCalls: function(data, cache)2109 {2110 var canvas = TypeUtils.cloneIntoCanvas(data.originalCanvasCloned);2111 var ctx = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d")));2112 this.setWrappedObject(ctx);2113 for (var i = 0, n = data.calls.length; i < n; ++i) {2114 var replayableCall = /** @type {ReplayableCall} */ (data.calls[i]);2115 if (replayableCall.functionName() === "save")2116 this._applyAttributesState(replayableCall.attachment("canvas2dAttributesState"));2117 this._calls.push(replayableCall.replay(cache));2118 }2119 this._applyAttributesState(data.currentAttributes);2120 },2121 /**2122 * @param {!Call} call2123 */2124 pushCall_setTransform: function(call)2125 {2126 var saveCallIndex = this._lastIndexOfMatchingSaveCall();2127 var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods);2128 index = Math.max(index, saveCallIndex);2129 if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1))2130 this._removeAllObsoleteCallsFromLog();2131 this.pushCall(call);2132 },2133 /**2134 * @param {!Call} call2135 */2136 pushCall_beginPath: function(call)2137 {2138 var index = this._lastIndexOfAnyCall(["clip"]);2139 if (this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1))2140 this._removeAllObsoleteCallsFromLog();2141 this.pushCall(call);2142 },2143 /**2144 * @param {!Call} call2145 */2146 pushCall_save: function(call)2147 {2148 call.setAttachment("canvas2dAttributesState", this._currentAttributesState());2149 this.pushCall(call);2150 },2151 /**2152 * @param {!Call} call2153 */2154 pushCall_restore: function(call)2155 {2156 var lastIndexOfSave = this._lastIndexOfMatchingSaveCall();2157 if (lastIndexOfSave === -1)2158 return;2159 this._calls[lastIndexOfSave].setAttachment("canvas2dAttributesState", null); // No longer needed, free memory.2160 var modified = false;2161 if (this._removeCallsFromLog(["clip"], lastIndexOfSave + 1))2162 modified = true;2163 var lastIndexOfAnyPathMethod = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods);2164 var index = Math.max(lastIndexOfSave, lastIndexOfAnyPathMethod);2165 if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1))2166 modified = true;2167 if (modified)2168 this._removeAllObsoleteCallsFromLog();2169 var lastCall = this._calls[this._calls.length - 1];2170 if (lastCall && lastCall.functionName() === "save")2171 this._calls.pop();2172 else2173 this.pushCall(call);2174 },2175 /**2176 * @param {number=} fromIndex2177 * @return {number}2178 */2179 _lastIndexOfMatchingSaveCall: function(fromIndex)2180 {2181 if (typeof fromIndex !== "number")2182 fromIndex = this._calls.length - 1;2183 else2184 fromIndex = Math.min(fromIndex, this._calls.length - 1);2185 var stackDepth = 1;2186 for (var i = fromIndex; i >= 0; --i) {2187 var functionName = this._calls[i].functionName();2188 if (functionName === "restore")2189 ++stackDepth;2190 else if (functionName === "save") {2191 --stackDepth;2192 if (!stackDepth)2193 return i;2194 }2195 }2196 return -1;2197 },2198 /**2199 * @param {!Array.<string>} functionNames2200 * @param {number=} fromIndex2201 * @return {number}2202 */2203 _lastIndexOfAnyCall: function(functionNames, fromIndex)2204 {2205 if (typeof fromIndex !== "number")2206 fromIndex = this._calls.length - 1;2207 else2208 fromIndex = Math.min(fromIndex, this._calls.length - 1);2209 for (var i = fromIndex; i >= 0; --i) {2210 if (functionNames.indexOf(this._calls[i].functionName()) !== -1)2211 return i;2212 }2213 return -1;2214 },2215 _removeAllObsoleteCallsFromLog: function()2216 {2217 // Remove all PATH methods between clip() and beginPath() calls.2218 var lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"]);2219 while (lastIndexOfBeginPath !== -1) {2220 var index = this._lastIndexOfAnyCall(["clip"], lastIndexOfBeginPath - 1);2221 this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1, lastIndexOfBeginPath);2222 lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"], index - 1);2223 }2224 // Remove all TRASFORMATION MATRIX methods before restore() or setTransform() but after any PATH or corresponding save() method.2225 var lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform"]);2226 while (lastRestore !== -1) {2227 var saveCallIndex = this._lastIndexOfMatchingSaveCall(lastRestore - 1);2228 var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods, lastRestore - 1);2229 index = Math.max(index, saveCallIndex);2230 this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1, lastRestore);2231 lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform"], index - 1);2232 }2233 // Remove all save-restore consecutive pairs.2234 var restoreCalls = 0;2235 for (var i = this._calls.length - 1; i >= 0; --i) {2236 var functionName = this._calls[i].functionName();2237 if (functionName === "restore") {2238 ++restoreCalls;2239 continue;2240 }2241 if (functionName === "save" && restoreCalls > 0) {2242 var saveCallIndex = i;2243 for (var j = i - 1; j >= 0 && i - j < restoreCalls; --j) {2244 if (this._calls[j].functionName() === "save")2245 saveCallIndex = j;2246 else2247 break;2248 }2249 this._calls.splice(saveCallIndex, (i - saveCallIndex + 1) * 2);2250 i = saveCallIndex;2251 }2252 restoreCalls = 0;2253 }2254 },2255 /**2256 * @param {!Array.<string>} functionNames2257 * @param {number} fromIndex2258 * @param {number=} toIndex2259 * @return {boolean}2260 */2261 _removeCallsFromLog: function(functionNames, fromIndex, toIndex)2262 {2263 var oldLength = this._calls.length;2264 if (typeof toIndex !== "number")2265 toIndex = oldLength;2266 else2267 toIndex = Math.min(toIndex, oldLength);2268 var newIndex = Math.min(fromIndex, oldLength);2269 for (var i = newIndex; i < toIndex; ++i) {2270 var call = this._calls[i];2271 if (functionNames.indexOf(call.functionName()) === -1)2272 this._calls[newIndex++] = call;2273 }2274 if (newIndex >= toIndex)2275 return false;2276 this._calls.splice(newIndex, toIndex - newIndex);2277 return true;2278 },2279 /**2280 * @return {!Object.<string, string>}2281 */2282 _currentAttributesState: function()2283 {2284 var ctx = this.wrappedObject();2285 var state = {};2286 state.attributes = {};2287 CanvasRenderingContext2DResource.AttributeProperties.forEach(function(attribute) {2288 state.attributes[attribute] = ctx[attribute];2289 });2290 if (ctx.getLineDash)2291 state.lineDash = ctx.getLineDash();2292 return state;2293 },2294 /**2295 * @param {Object.<string, string>=} state2296 */2297 _applyAttributesState: function(state)2298 {2299 if (!state)2300 return;2301 var ctx = this.wrappedObject();2302 if (state.attributes) {2303 Object.keys(state.attributes).forEach(function(attribute) {2304 ctx[attribute] = state.attributes[attribute];2305 });2306 }2307 if (ctx.setLineDash)2308 ctx.setLineDash(state.lineDash);2309 },2310 /**2311 * @override2312 * @return {!Object.<string, Function>}2313 */2314 _customWrapFunctions: function()2315 {2316 var wrapFunctions = CanvasRenderingContext2DResource._wrapFunctions;2317 if (!wrapFunctions) {2318 wrapFunctions = Object.create(null);2319 wrapFunctions["createLinearGradient"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasGradient");2320 wrapFunctions["createRadialGradient"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasGradient");2321 wrapFunctions["createPattern"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasPattern");2322 /**2323 * @param {string} methodName2324 * @param {function(this:Resource, !Call)=} func2325 */2326 function stateModifyingWrapFunction(methodName, func)2327 {2328 if (func) {2329 /** @this Resource.WrapFunction */2330 wrapFunctions[methodName] = function()2331 {2332 func.call(this._resource, this.call());2333 }2334 } else {2335 /** @this Resource.WrapFunction */2336 wrapFunctions[methodName] = function()2337 {2338 this._resource.pushCall(this.call());2339 }2340 }2341 }2342 for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.TransformationMatrixMethods[i]; ++i)2343 stateModifyingWrapFunction(methodName, methodName === "setTransform" ? this.pushCall_setTransform : undefined);2344 for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.PathMethods[i]; ++i)2345 stateModifyingWrapFunction(methodName, methodName === "beginPath" ? this.pushCall_beginPath : undefined);2346 stateModifyingWrapFunction("save", this.pushCall_save);2347 stateModifyingWrapFunction("restore", this.pushCall_restore);2348 stateModifyingWrapFunction("clip");2349 CanvasRenderingContext2DResource._wrapFunctions = wrapFunctions;2350 }2351 return wrapFunctions;2352 },2353 __proto__: ContextResource.prototype2354}2355/**2356 * @constructor2357 * @param {!Object.<string, boolean>=} drawingMethodNames2358 */2359function CallFormatter(drawingMethodNames)2360{2361 this._drawingMethodNames = drawingMethodNames || Object.create(null);2362}2363CallFormatter.prototype = {2364 /**2365 * @param {!ReplayableCall} replayableCall2366 * @return {!Object}2367 */2368 formatCall: function(replayableCall)2369 {2370 var result = {};2371 var functionName = replayableCall.functionName();2372 if (functionName) {2373 result.functionName = functionName;2374 result.arguments = replayableCall.args().map(this.formatValue.bind(this));2375 if (replayableCall.result() !== undefined)2376 result.result = this.formatValue(replayableCall.result());2377 if (this._drawingMethodNames[functionName])2378 result.isDrawingCall = true;2379 } else {2380 result.property = replayableCall.args()[0];2381 result.value = this.formatValue(replayableCall.args()[1]);2382 }2383 return result;2384 },2385 /**2386 * @param {*} value2387 * @return {!Object}2388 */2389 formatValue: function(value)2390 {2391 if (value instanceof ReplayableResource)2392 var description = value.description();2393 else2394 var description = "" + value;2395 return { description: description };2396 }2397}2398/**2399 * @const2400 * @type {!Object.<string, !CallFormatter>}2401 */2402CallFormatter._formatters = {};2403/**2404 * @param {string} resourceName2405 * @param {!CallFormatter} callFormatter2406 */2407CallFormatter.register = function(resourceName, callFormatter)2408{2409 CallFormatter._formatters[resourceName] = callFormatter;2410}2411/**2412 * @param {!ReplayableCall} replayableCall2413 * @return {!Object}2414 */2415CallFormatter.formatCall = function(replayableCall)2416{2417 var resource = replayableCall.replayableResource();2418 var formatter = CallFormatter._formatters[resource.name()];2419 if (!formatter) {2420 var contextResource = resource.replayableContextResource();2421 formatter = CallFormatter._formatters[contextResource.name()] || new CallFormatter();2422 }2423 return formatter.formatCall(replayableCall);2424}2425CallFormatter.register("CanvasRenderingContext2D", new CallFormatter(CanvasRenderingContext2DResource.DrawingMethods));2426CallFormatter.register("WebGLRenderingContext", new CallFormatter(WebGLRenderingContextResource.DrawingMethods));2427/**2428 * @constructor2429 */2430function TraceLog()2431{2432 /** @type {!Array.<ReplayableCall>} */2433 this._replayableCalls = [];2434 /** @type {!Cache} */2435 this._replayablesCache = new Cache();2436 /** @type {!Object.<number, boolean>} */2437 this._frameEndCallIndexes = {};2438}2439TraceLog.prototype = {2440 /**2441 * @return {number}2442 */2443 size: function()2444 {2445 return this._replayableCalls.length;2446 },2447 /**2448 * @return {!Array.<ReplayableCall>}2449 */2450 replayableCalls: function()2451 {2452 return this._replayableCalls;2453 },2454 /**2455 * @param {number} id2456 * @return {ReplayableResource}2457 */2458 replayableResource: function(id)2459 {2460 return /** @type {ReplayableResource} */ (this._replayablesCache.get(id));2461 },2462 /**2463 * @param {!Resource} resource2464 */2465 captureResource: function(resource)2466 {2467 resource.toReplayable(this._replayablesCache);2468 },2469 /**2470 * @param {!Call} call2471 */2472 addCall: function(call)2473 {2474 this._replayableCalls.push(call.toReplayable(this._replayablesCache));2475 },2476 addFrameEndMark: function()2477 {2478 var index = this._replayableCalls.length - 1;2479 if (index >= 0)2480 this._frameEndCallIndexes[index] = true;2481 },2482 /**2483 * @param {number} index2484 * @return {boolean}2485 */2486 isFrameEndCallAt: function(index)2487 {2488 return !!this._frameEndCallIndexes[index];2489 }2490}2491/**2492 * @constructor2493 * @param {!TraceLog} traceLog2494 */2495function TraceLogPlayer(traceLog)2496{2497 /** @type {!TraceLog} */2498 this._traceLog = traceLog;2499 /** @type {number} */2500 this._nextReplayStep = 0;2501 /** @type {!Cache} */2502 this._replayWorldCache = new Cache();2503}2504TraceLogPlayer.prototype = {2505 /**2506 * @return {!TraceLog}2507 */2508 traceLog: function()2509 {2510 return this._traceLog;2511 },2512 /**2513 * @param {number} id2514 * @return {Resource}2515 */2516 replayWorldResource: function(id)2517 {2518 return /** @type {Resource} */ (this._replayWorldCache.get(id));2519 },2520 /**2521 * @return {number}2522 */2523 nextReplayStep: function()2524 {2525 return this._nextReplayStep;2526 },2527 reset: function()2528 {2529 this._nextReplayStep = 0;2530 this._replayWorldCache.reset();2531 },2532 /**2533 * @return {Call}2534 */2535 step: function()2536 {2537 return this.stepTo(this._nextReplayStep);2538 },2539 /**2540 * @param {number} stepNum2541 * @return {Call}2542 */2543 stepTo: function(stepNum)2544 {2545 stepNum = Math.min(stepNum, this._traceLog.size() - 1);2546 console.assert(stepNum >= 0);2547 if (this._nextReplayStep > stepNum)2548 this.reset();2549 // FIXME: Replay all the cached resources first to warm-up.2550 var lastCall = null;2551 var replayableCalls = this._traceLog.replayableCalls();2552 while (this._nextReplayStep <= stepNum)2553 lastCall = replayableCalls[this._nextReplayStep++].replay(this._replayWorldCache);2554 return lastCall;2555 },2556 /**2557 * @return {Call}2558 */2559 replay: function()2560 {2561 return this.stepTo(this._traceLog.size() - 1);2562 }2563}2564/**2565 * @constructor2566 */2567function ResourceTrackingManager()2568{2569 this._capturing = false;2570 this._stopCapturingOnFrameEnd = false;2571 this._lastTraceLog = null;2572}2573ResourceTrackingManager.prototype = {2574 /**2575 * @return {boolean}2576 */2577 capturing: function()2578 {2579 return this._capturing;2580 },2581 /**2582 * @return {TraceLog}2583 */2584 lastTraceLog: function()2585 {2586 return this._lastTraceLog;2587 },2588 /**2589 * @param {!Resource} resource2590 */2591 registerResource: function(resource)2592 {2593 resource.setManager(this);2594 },2595 startCapturing: function()2596 {2597 if (!this._capturing)2598 this._lastTraceLog = new TraceLog();2599 this._capturing = true;2600 this._stopCapturingOnFrameEnd = false;2601 },2602 /**2603 * @param {TraceLog=} traceLog2604 */2605 stopCapturing: function(traceLog)2606 {2607 if (traceLog && this._lastTraceLog !== traceLog)2608 return;2609 this._capturing = false;2610 this._stopCapturingOnFrameEnd = false;2611 if (this._lastTraceLog)2612 this._lastTraceLog.addFrameEndMark();2613 },2614 /**2615 * @param {!TraceLog} traceLog2616 */2617 dropTraceLog: function(traceLog)2618 {2619 this.stopCapturing(traceLog);2620 if (this._lastTraceLog === traceLog)2621 this._lastTraceLog = null;2622 },2623 captureFrame: function()2624 {2625 this._lastTraceLog = new TraceLog();2626 this._capturing = true;2627 this._stopCapturingOnFrameEnd = true;2628 },2629 /**2630 * @param {!Resource} resource2631 * @param {Array|Arguments} args2632 */2633 captureArguments: function(resource, args)2634 {2635 if (!this._capturing)2636 return;2637 this._lastTraceLog.captureResource(resource);2638 for (var i = 0, n = args.length; i < n; ++i) {2639 var res = Resource.forObject(args[i]);2640 if (res)2641 this._lastTraceLog.captureResource(res);2642 }2643 },2644 /**2645 * @param {!Call} call2646 */2647 captureCall: function(call)2648 {2649 if (!this._capturing)2650 return;2651 this._lastTraceLog.addCall(call);2652 },2653 markFrameEnd: function()2654 {2655 if (!this._lastTraceLog)2656 return;2657 this._lastTraceLog.addFrameEndMark();2658 if (this._stopCapturingOnFrameEnd && this._lastTraceLog.size())2659 this.stopCapturing(this._lastTraceLog);2660 }2661}2662/**2663 * @constructor2664 */2665var InjectedCanvasModule = function()2666{2667 /** @type {!ResourceTrackingManager} */2668 this._manager = new ResourceTrackingManager();2669 /** @type {number} */2670 this._lastTraceLogId = 0;2671 /** @type {!Object.<string, TraceLog>} */2672 this._traceLogs = {};2673 /** @type {!Object.<string, TraceLogPlayer>} */2674 this._traceLogPlayers = {};2675}2676InjectedCanvasModule.prototype = {2677 /**2678 * @param {!WebGLRenderingContext} glContext2679 * @return {Object}2680 */2681 wrapWebGLContext: function(glContext)2682 {2683 var resource = Resource.forObject(glContext) || new WebGLRenderingContextResource(glContext);2684 this._manager.registerResource(resource);2685 return resource.proxyObject();2686 },2687 /**2688 * @param {!CanvasRenderingContext2D} context2689 * @return {Object}2690 */2691 wrapCanvas2DContext: function(context)2692 {2693 var resource = Resource.forObject(context) || new CanvasRenderingContext2DResource(context);2694 this._manager.registerResource(resource);2695 return resource.proxyObject();2696 },2697 /**2698 * @return {CanvasAgent.TraceLogId}2699 */2700 captureFrame: function()2701 {2702 return this._callStartCapturingFunction(this._manager.captureFrame);2703 },2704 /**2705 * @return {CanvasAgent.TraceLogId}2706 */2707 startCapturing: function()2708 {2709 return this._callStartCapturingFunction(this._manager.startCapturing);2710 },2711 markFrameEnd: function()2712 {2713 this._manager.markFrameEnd();2714 },2715 /**2716 * @param {function(this:ResourceTrackingManager)} func2717 * @return {CanvasAgent.TraceLogId}2718 */2719 _callStartCapturingFunction: function(func)2720 {2721 var oldTraceLog = this._manager.lastTraceLog();2722 func.call(this._manager);2723 var traceLog = this._manager.lastTraceLog();2724 if (traceLog === oldTraceLog) {2725 for (var id in this._traceLogs) {2726 if (this._traceLogs[id] === traceLog)2727 return id;2728 }2729 }2730 var id = this._makeTraceLogId();2731 this._traceLogs[id] = traceLog;2732 return id;2733 },2734 /**2735 * @param {CanvasAgent.TraceLogId} id2736 */2737 stopCapturing: function(id)2738 {2739 var traceLog = this._traceLogs[id];2740 if (traceLog)2741 this._manager.stopCapturing(traceLog);2742 },2743 /**2744 * @param {CanvasAgent.TraceLogId} id2745 */2746 dropTraceLog: function(id)2747 {2748 var traceLog = this._traceLogs[id];2749 if (traceLog)2750 this._manager.dropTraceLog(traceLog);2751 delete this._traceLogs[id];2752 delete this._traceLogPlayers[id];2753 },2754 /**2755 * @param {CanvasAgent.TraceLogId} id2756 * @param {number=} startOffset2757 * @param {number=} maxLength2758 * @return {!CanvasAgent.TraceLog|string}2759 */2760 traceLog: function(id, startOffset, maxLength)2761 {2762 var traceLog = this._traceLogs[id];2763 if (!traceLog)2764 return "Error: Trace log with the given ID not found.";2765 // Ensure last call ends a frame.2766 traceLog.addFrameEndMark();2767 var replayableCalls = traceLog.replayableCalls();2768 if (typeof startOffset !== "number")2769 startOffset = 0;2770 if (typeof maxLength !== "number")2771 maxLength = replayableCalls.length;2772 var fromIndex = Math.max(0, startOffset);2773 var toIndex = Math.min(replayableCalls.length - 1, fromIndex + maxLength - 1);2774 var alive = this._manager.capturing() && this._manager.lastTraceLog() === traceLog;2775 var result = {2776 id: id,2777 /** @type {Array.<CanvasAgent.Call>} */2778 calls: [],2779 alive: alive,2780 startOffset: fromIndex,2781 totalAvailableCalls: replayableCalls.length2782 };2783 for (var i = fromIndex; i <= toIndex; ++i) {2784 var call = replayableCalls[i];2785 var contextResource = call.replayableResource().replayableContextResource();2786 var stackTrace = call.stackTrace();2787 var callFrame = stackTrace ? stackTrace.callFrame(0) || {} : {};2788 var item = CallFormatter.formatCall(call);2789 item.contextId = this._makeStringResourceId(contextResource.id());2790 item.sourceURL = callFrame.sourceURL;2791 item.lineNumber = callFrame.lineNumber;2792 item.columnNumber = callFrame.columnNumber;2793 item.isFrameEndCall = traceLog.isFrameEndCallAt(i);2794 result.calls.push(item);2795 }2796 return result;2797 },2798 /**2799 * @param {*} obj2800 * @return {!CanvasAgent.CallArgument}2801 */2802 _makeCallArgument: function(obj)2803 {2804 if (obj instanceof ReplayableResource)2805 var description = obj.description();2806 else2807 var description = "" + obj;2808 return { description: description };2809 },2810 /**2811 * @param {CanvasAgent.TraceLogId} traceLogId2812 * @param {number} stepNo2813 * @return {!CanvasAgent.ResourceState|string}2814 */2815 replayTraceLog: function(traceLogId, stepNo)2816 {2817 var traceLog = this._traceLogs[traceLogId];2818 if (!traceLog)2819 return "Error: Trace log with the given ID not found.";2820 this._traceLogPlayers[traceLogId] = this._traceLogPlayers[traceLogId] || new TraceLogPlayer(traceLog);2821 var lastCall = this._traceLogPlayers[traceLogId].stepTo(stepNo);2822 var resource = lastCall.resource();2823 var dataURL = resource.toDataURL();2824 if (!dataURL) {2825 resource = resource.contextResource();2826 dataURL = resource.toDataURL();2827 }2828 return this._makeResourceState(this._makeStringResourceId(resource.id()), traceLogId, dataURL);2829 },2830 /**2831 * @param {CanvasAgent.ResourceId} stringResourceId2832 * @return {!CanvasAgent.ResourceInfo|string}2833 */2834 resourceInfo: function(stringResourceId)2835 {2836 var resourceId = this._parseStringId(stringResourceId).resourceId;2837 if (!resourceId)2838 return "Error: Wrong resource ID: " + stringResourceId;2839 var replayableResource = null;2840 for (var id in this._traceLogs) {2841 replayableResource = this._traceLogs[id].replayableResource(resourceId);2842 if (replayableResource)2843 break;2844 }2845 if (!replayableResource)2846 return "Error: Resource with the given ID not found.";2847 return this._makeResourceInfo(stringResourceId, replayableResource.description());2848 },2849 /**2850 * @param {CanvasAgent.TraceLogId} traceLogId2851 * @param {CanvasAgent.ResourceId} stringResourceId2852 * @return {!CanvasAgent.ResourceState|string}2853 */2854 resourceState: function(traceLogId, stringResourceId)2855 {2856 var traceLog = this._traceLogs[traceLogId];2857 if (!traceLog)2858 return "Error: Trace log with the given ID not found.";2859 var traceLogPlayer = this._traceLogPlayers[traceLogId];2860 if (!traceLogPlayer)2861 return "Error: Trace log replay has not started yet.";2862 var parsedStringId1 = this._parseStringId(traceLogId);2863 var parsedStringId2 = this._parseStringId(stringResourceId);2864 if (parsedStringId1.injectedScriptId !== parsedStringId2.injectedScriptId)2865 return "Error: Both IDs must point to the same injected script.";2866 var resourceId = parsedStringId2.resourceId;2867 if (!resourceId)2868 return "Error: Wrong resource ID: " + stringResourceId;2869 var resource = traceLogPlayer.replayWorldResource(resourceId);2870 if (!resource)2871 return "Error: Resource with the given ID has not been replayed yet.";2872 return this._makeResourceState(stringResourceId, traceLogId, resource.toDataURL());2873 },2874 /**2875 * @return {CanvasAgent.TraceLogId}2876 */2877 _makeTraceLogId: function()2878 {2879 return "{\"injectedScriptId\":" + injectedScriptId + ",\"traceLogId\":" + (++this._lastTraceLogId) + "}";2880 },2881 /**2882 * @param {number} resourceId2883 * @return {CanvasAgent.ResourceId}2884 */2885 _makeStringResourceId: function(resourceId)2886 {2887 return "{\"injectedScriptId\":" + injectedScriptId + ",\"resourceId\":" + resourceId + "}";2888 },2889 /**2890 * @param {CanvasAgent.ResourceId} stringResourceId2891 * @param {string} description2892 * @return {!CanvasAgent.ResourceInfo}2893 */2894 _makeResourceInfo: function(stringResourceId, description)2895 {2896 return {2897 id: stringResourceId,2898 description: description2899 };2900 },2901 /**2902 * @param {CanvasAgent.ResourceId} stringResourceId2903 * @param {CanvasAgent.TraceLogId} traceLogId2904 * @param {string} imageURL2905 * @return {!CanvasAgent.ResourceState}2906 */2907 _makeResourceState: function(stringResourceId, traceLogId, imageURL)2908 {2909 return {2910 id: stringResourceId,2911 traceLogId: traceLogId,2912 imageURL: imageURL2913 };2914 },2915 /**2916 * @param {string} stringId2917 * @return {{injectedScriptId: number, traceLogId: ?number, resourceId: ?number}}2918 */2919 _parseStringId: function(stringId)2920 {2921 return InjectedScriptHost.evaluate("(" + stringId + ")");2922 }2923}2924var injectedCanvasModule = new InjectedCanvasModule();2925return injectedCanvasModule;...
index.js
Source:index.js
...15 });16 console.log('Don\'t forget to be polite bitte()!');17 };18}19function wrapFunctions(obj) {20 obj.help = helpModule(obj);21 Object.keys(obj).forEach(function (funName) {22 var fun = obj[funName];23 obj[funName] = function () {24 var args = [];25 for (var _i = 0; _i < arguments.length; _i++) {26 args[_i - 0] = arguments[_i];27 }28 return {29 bitte: function () {30 return fun.apply(void 0, args);31 }32 };33 };34 });35 return obj;36}37exports.data = wrapFunctions(dataLib);38exports.grammar = wrapFunctions(grammarLib);39exports.time = wrapFunctions(timeLib);40exports.random = wrapFunctions(randomLib);41exports.volkswagen = volkswagenLib;42function eu() {43 [exports.data, exports.time, exports.grammar].forEach(function (lib) {44 console.log(lib);45 lib.extendNatives().bitte();46 });47}48exports.eu = eu;49function help() {50 return {51 bitte: function () {52 helpOverall();53 }54 };...
delta-db.js
Source:delta-db.js
...30 }31 }32};33// Expose all methods of client as static DeltaDB functions34wrapFunctions();...
wrapReducer.js
Source:wrapReducer.js
1import produce from 'immer';2import createReducer from "./reducerUtils";3const initialState = {4 viewModel: {5 viewMode: false,6 currentPage: ""7 },8 currentComponent: "",9 mainWidth: 010}11const wrapFunctions = {12 setMode(state, action) {13 state.viewModel.viewMode = action.payload14 },15 setLocation(state, action) {16 state.viewModel.currentPage = action.payload17 },18 setCurrentComponent(state, action) {19 state.currentComponent = action.payload20 },21 setMainWidth(state, action) {22 state.mainWidth = action.payload23 }24}...
Using AI Code Generation
1const { wrapFunctions } = require('playwright/lib/utils/utils');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch({ headless: false });5 const page = await browser.newPage();6 wrapFunctions(page, (handler, name) => async (...args) => {7 console.log(`calling ${name} with args: ${JSON.stringify(args)}`);8 return handler(...args);9 });10 await page.fill('input[aria-label="Search"]', 'Hello world!');11 await page.click('text=Google Search');12 await browser.close();13})();14const { wrapFunction } = require('playwright/lib/utils/utils');15const { chromium } = require('playwright');16(async () => {17 const browser = await chromium.launch({ headless: false });18 const page = await browser.newPage();19 wrapFunction(page, 'fill', (handler, name) => async (...args) => {20 console.log(`calling ${name} with args: ${JSON.stringify(args)}`);21 return handler(...args);22 });23 await page.fill('input[aria-label="Search"]', 'Hello world!');24 await page.click('text=Google Search');25 await browser.close();26})();27const { wrapFunction } = require('playwright/lib/utils/utils');28const { chromium } = require('playwright');29async function customHandler(handler, name, ...args) {30 console.log(`calling ${name} with args: ${JSON.stringify(args)}`);31 return handler(...args);32}33(async () => {34 const browser = await chromium.launch({ headless: false });35 const page = await browser.newPage();36 wrapFunction(page, 'fill', customHandler);
Using AI Code Generation
1const { wrapFunctions } = require('playwright-core/lib/client/helper');2const { chromium } = require('playwright-core');3(async () => {4 const browser = await chromium.launch();5 const page = await browser.newPage();6 const originalPage = page._delegate;7 wrapFunctions(originalPage, page, [
Using AI Code Generation
1const { wrapFunctions } = require('@playwright/test');2const { expect } = require('@playwright/test');3const { wrapFunctions } = require('@playwright/test');4const { expect } = require('@playwright/test');5const { wrapFunctions } = require('@playwright/test');6const { expect } = require('@playwright/test');7const { wrapFunctions } = require('@playwright/test');8const { expect } = require('@playwright/test');9const { wrapFunctions } = require('@playwright/test');10const { expect } = require('@playwright/test');11const { wrapFunctions } = require('@playwright/test');12const { expect } = require('@playwright/test');13const { wrapFunctions } = require('@playwright/test');14const { expect } = require('@playwright/test');15const { wrapFunctions } = require('@playwright/test');16const { expect } = require('@playwright/test');17const { wrapFunctions } = require('@playwright/test');18const { expect } = require('@playwright/test');19const { wrapFunctions } = require('@playwright/test');20const { expect } = require('@playwright/test');
Using AI Code Generation
1const { wrapFunctions } = require('@playwright/test/lib/utils/utils');2const { test, expect } = require('@playwright/test');3test('example test', async ({ page }) => {4 await page.click('text="Get started"');5 await page.click('text="Docs"');6 await page.click('text="Guides"');7 await page.click('text="API"');
Using AI Code Generation
1const { test } = require('@playwright/test');2test('test', async ({ page }) => {3 await page.click('text=Get started');4 const [popup] = await Promise.all([5 page.waitForEvent('popup'),6 page.click('text=Show me the code!'),7 ]);8 await popup.waitForLoadState('domcontentloaded');9 await popup.click('text=Close');10});
Using AI Code Generation
1const { wrapFunctions } = require('@playwright/test/lib/server/injected/injectedScript');2const { wrapFunctions: wrapFunctions2 } = require('@playwright/test/lib/server/injected/injectedScript');3const { wrapFunctions: wrapFunctions3 } = require('@playwright/test/lib/server/injected/injectedScript');4const { wrapFunctions: wrapFunctions4 } = require('@playwright/test/lib/server/injected/injectedScript');5const { wrapFunctions: wrapFunctions5 } = require('@playwright/test/lib/server/injected/injectedScript');6const { wrapFunctions: wrapFunctions6 } = require('@playwright/test/lib/server/injected/injectedScript');7const { wrapFunctions: wrapFunctions7 } = require('@playwright/test/lib/server/injected/injectedScript');8const { wrapFunctions: wrapFunctions8 } = require('@playwright/test/lib/server/injected/injectedScript');9const { wrapFunctions: wrapFunctions9 } = require('@playwright/test/lib/server/injected/injectedScript');10const { wrapFunctions: wrapFunctions10 } = require('@playwright/test/lib/server/injected/injectedScript');11const { wrapFunctions: wrapFunctions11 } = require('@playwright/test/lib/server/injected/injectedScript');12const { wrapFunctions: wrapFunctions12 } = require('@playwright/test/lib/server/injected/injectedScript');13const { wrapFunctions: wrapFunctions13 } = require('@playwright/test/lib/server/injected/injectedScript');14const { wrapFunctions: wrapFunctions14 } = require('@playwright/test/lib/server/injected/injectedScript');15const { wrapFunctions: wrapFunctions15 } = require('@playwright/test/lib/server/injected/injectedScript');16const { wrapFunctions: wrapFunctions16 } = require('@playwright/test/lib/server/injected/injectedScript');17const { wrapFunctions: wrapFunctions17 } = require('@playwright/test/lib/server/injected/injectedScript');18const { wrapFunctions: wrapFunctions18 } = require('@playwright/test/lib/server/injected/injectedScript');19const { wrapFunctions: wrapFunctions19 } = require('@playwright/test/lib/server/injected/injectedScript');20const { wrapFunctions: wrapFunctions20 } = require('@playwright/test/lib/server/injected/injectedScript');21const { wrapFunctions: wrapFunctions21 } = require('@playwright/test/lib/server/injected/injectedScript');22const { wrapFunctions: wrapFunctions22 } = require('@playwright/test/lib/server/injected
Using AI Code Generation
1const { wrapFunctions } = require('@playwright/test');2const { chromium, webkit, firefox } = require('playwright');3const { expect } = require('chai');4const { describe, it, beforeAll, afterAll } = require('mocha');5describe('Test', function () {6 let browser;7 let page;8 beforeAll(async () => {9 browser = await chromium.launch();10 page = await browser.newPage();11 });12 afterAll(async () => {13 await browser.close();14 });15 it('should work', async () => {16 await page.fill('input[name="q"]', 'Playwright');17 await page.click('input[type="submit"]');18 await page.waitForSelector('text=Playwright');19 await page.click('text=Playwright');20 const title = await page.title();21 expect(title).to.equal('Playwright');22 });23});24const { wrapFunctions } = require('@playwright/test');25const { chromium, webkit, firefox } = require('playwright');26const { expect } = require('chai');27const { describe, it, beforeAll, afterAll } = require('mocha');28describe('Test', function () {29 let browser;30 let page;31 beforeAll(async () => {32 browser = await chromium.launch();33 page = await browser.newPage();34 });35 afterAll(async () => {36 await browser.close();37 });38 it('should work', async () => {39 await page.fill('input[name="q"]', 'Playwright');40 await page.click('input[type="submit"]');41 await page.waitForSelector('text=Playwright');42 await page.click('text=Playwright');43 const title = await page.title();44 expect(title).to.equal('Playwright');45 });46});47const { wrapFunctions } = require('@playwright/test');48const { chromium, webkit, firefox } = require('playwright');49const { expect } = require('chai');50const { describe, it, beforeAll, afterAll } = require('mocha');51describe('Test', function () {52 let browser;53 let page;54 beforeAll(async () => {
Using AI Code Generation
1const { wrapFunctions } = require('@playwright/test/lib/utils/instrument');2const { test } = require('@playwright/test');3const { expect } = require('@playwright/test');4test('test', async ({ page }) => {5 await page.click('text=Get started');6 await page.click('text=Docs');7 await page.click('text=API');8 await page.click(
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!