How to use wrapFunctions method in Playwright Internal

Best JavaScript code snippet using playwright-internal

InjectedScriptCanvasModuleSource.js

Source:InjectedScriptCanvasModuleSource.js Github

copy

Full Screen

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;...

Full Screen

Full Screen

index.js

Source:index.js Github

copy

Full Screen

...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 };...

Full Screen

Full Screen

delta-db.js

Source:delta-db.js Github

copy

Full Screen

...30 }31 }32};33// Expose all methods of client as static DeltaDB functions34wrapFunctions();...

Full Screen

Full Screen

wrapReducer.js

Source:wrapReducer.js Github

copy

Full Screen

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}...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

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);

Full Screen

Using AI Code Generation

copy

Full Screen

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, [

Full Screen

Using AI Code Generation

copy

Full Screen

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');

Full Screen

Using AI Code Generation

copy

Full Screen

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"');

Full Screen

Using AI Code Generation

copy

Full Screen

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});

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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 () => {

Full Screen

Using AI Code Generation

copy

Full Screen

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(

Full Screen

Using AI Code Generation

copy

Full Screen

1const { wrapFunctions } = require('@playwright/test');2wrapFunctions(expect, (origImpl, origArgs) => {3 const result = origImpl(...origArgs);4 return result;5});6### `wrapFunctions(object, wrapper, options)`

Full Screen

Playwright tutorial

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.

Chapters:

  1. What is Playwright : Playwright is comparatively new but has gained good popularity. Get to know some history of the Playwright with some interesting facts connected with it.
  2. How To Install Playwright : Learn in detail about what basic configuration and dependencies are required for installing Playwright and run a test. Get a step-by-step direction for installing the Playwright automation framework.
  3. Playwright Futuristic Features: Launched in 2020, Playwright gained huge popularity quickly because of some obliging features such as Playwright Test Generator and Inspector, Playwright Reporter, Playwright auto-waiting mechanism and etc. Read up on those features to master Playwright testing.
  4. What is Component Testing: Component testing in Playwright is a unique feature that allows a tester to test a single component of a web application without integrating them with other elements. Learn how to perform Component testing on the Playwright automation framework.
  5. Inputs And Buttons In Playwright: Every website has Input boxes and buttons; learn about testing inputs and buttons with different scenarios and examples.
  6. Functions and Selectors in Playwright: Learn how to launch the Chromium browser with Playwright. Also, gain a better understanding of some important functions like “BrowserContext,” which allows you to run multiple browser sessions, and “newPage” which interacts with a page.
  7. Handling Alerts and Dropdowns in Playwright : Playwright interact with different types of alerts and pop-ups, such as simple, confirmation, and prompt, and different types of dropdowns, such as single selector and multi-selector get your hands-on with handling alerts and dropdown in Playright testing.
  8. Playwright vs Puppeteer: Get to know about the difference between two testing frameworks and how they are different than one another, which browsers they support, and what features they provide.
  9. Run Playwright Tests on LambdaTest: Playwright testing with LambdaTest leverages test performance to the utmost. You can run multiple Playwright tests in Parallel with the LammbdaTest test cloud. Get a step-by-step guide to run your Playwright test on the LambdaTest platform.
  10. Playwright Python Tutorial: Playwright automation framework support all major languages such as Python, JavaScript, TypeScript, .NET and etc. However, there are various advantages to Python end-to-end testing with Playwright because of its versatile utility. Get the hang of Playwright python testing with this chapter.
  11. Playwright End To End Testing Tutorial: Get your hands on with Playwright end-to-end testing and learn to use some exciting features such as TraceViewer, Debugging, Networking, Component testing, Visual testing, and many more.
  12. Playwright Video Tutorial: Watch the video tutorials on Playwright testing from experts and get a consecutive in-depth explanation of Playwright automation testing.

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful