InjectedScriptCanvasModuleSource.js 96 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217
  1. /*
  2. * Copyright (C) 2012 Google Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are
  6. * met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above
  11. * copyright notice, this list of conditions and the following disclaimer
  12. * in the documentation and/or other materials provided with the
  13. * distribution.
  14. * * Neither the name of Google Inc. nor the names of its
  15. * contributors may be used to endorse or promote products derived from
  16. * this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. /**
  31. * @param {InjectedScriptHost} InjectedScriptHost
  32. * @param {Window} inspectedWindow
  33. * @param {number} injectedScriptId
  34. */
  35. (function (InjectedScriptHost, inspectedWindow, injectedScriptId) {
  36. var TypeUtils = {
  37. /**
  38. * http://www.khronos.org/registry/typedarray/specs/latest/#7
  39. * @const
  40. * @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. * @const
  52. * @type {!Array.<string>}
  53. */
  54. _supportedPropertyPrefixes: ["webkit"],
  55. /**
  56. * @param {*} array
  57. * @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 {*} obj
  70. * @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} obj
  112. * @param {number=} width
  113. * @param {number=} height
  114. * @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=} obj
  127. * @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>} names
  140. * @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. * @interface
  169. */
  170. function StackTrace()
  171. {
  172. }
  173. StackTrace.prototype = {
  174. /**
  175. * @param {number} index
  176. * @return {{sourceURL: string, lineNumber: number, columnNumber: number}|undefined}
  177. */
  178. callFrame: function(index)
  179. {
  180. }
  181. }
  182. /**
  183. * @param {number=} stackTraceLimit
  184. * @param {Function=} topMostFunctionToIgnore
  185. * @return {StackTrace}
  186. */
  187. StackTrace.create = function(stackTraceLimit, topMostFunctionToIgnore)
  188. {
  189. // FIXME: Support JSC, and maybe other browsers.
  190. return null;
  191. }
  192. /**
  193. * @constructor
  194. */
  195. function Cache()
  196. {
  197. this.reset();
  198. }
  199. Cache.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} key
  216. * @return {boolean}
  217. */
  218. has: function(key)
  219. {
  220. return key in this._items;
  221. },
  222. /**
  223. * @param {number} key
  224. * @return {Object}
  225. */
  226. get: function(key)
  227. {
  228. return this._items[key];
  229. },
  230. /**
  231. * @param {number} key
  232. * @param {Object} item
  233. */
  234. put: function(key, item)
  235. {
  236. if (!this.has(key))
  237. ++this._size;
  238. this._items[key] = item;
  239. }
  240. }
  241. /**
  242. * @constructor
  243. * @param {Resource|Object} thisObject
  244. * @param {string} functionName
  245. * @param {Array|Arguments} args
  246. * @param {Resource|*=} result
  247. * @param {StackTrace=} stackTrace
  248. */
  249. function 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. }
  259. Call.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. * @return {!Array}
  283. */
  284. args: function()
  285. {
  286. return this._args;
  287. },
  288. /**
  289. * @return {*}
  290. */
  291. result: function()
  292. {
  293. return this._result;
  294. },
  295. /**
  296. * @return {StackTrace}
  297. */
  298. stackTrace: function()
  299. {
  300. return this._stackTrace;
  301. },
  302. /**
  303. * @param {StackTrace} stackTrace
  304. */
  305. setStackTrace: function(stackTrace)
  306. {
  307. this._stackTrace = stackTrace;
  308. },
  309. /**
  310. * @param {*} result
  311. */
  312. setResult: function(result)
  313. {
  314. this._result = result;
  315. },
  316. /**
  317. * @param {string} name
  318. * @param {Object} attachment
  319. */
  320. setAttachment: function(name, attachment)
  321. {
  322. if (attachment) {
  323. /** @type {Object.<string, Object>} */
  324. this._attachments = this._attachments || Object.create(null);
  325. this._attachments[name] = attachment;
  326. } else if (this._attachments)
  327. delete this._attachments[name];
  328. },
  329. /**
  330. * @param {string} name
  331. * @return {Object}
  332. */
  333. attachment: function(name)
  334. {
  335. return this._attachments && this._attachments[name];
  336. },
  337. freeze: function()
  338. {
  339. if (this._freezed)
  340. return;
  341. this._freezed = true;
  342. for (var i = 0, n = this._args.length; i < n; ++i) {
  343. // FIXME: freeze the Resources also!
  344. if (!Resource.forObject(this._args[i]))
  345. this._args[i] = TypeUtils.clone(this._args[i]);
  346. }
  347. },
  348. /**
  349. * @param {!Cache} cache
  350. * @return {!ReplayableCall}
  351. */
  352. toReplayable: function(cache)
  353. {
  354. this.freeze();
  355. var thisObject = /** @type {ReplayableResource} */ (Resource.toReplayable(this._thisObject, cache));
  356. var result = Resource.toReplayable(this._result, cache);
  357. var args = this._args.map(function(obj) {
  358. return Resource.toReplayable(obj, cache);
  359. });
  360. var attachments = TypeUtils.cloneObject(this._attachments);
  361. return new ReplayableCall(thisObject, this._functionName, args, result, this._stackTrace, attachments);
  362. },
  363. /**
  364. * @param {!ReplayableCall} replayableCall
  365. * @param {!Cache} cache
  366. * @return {!Call}
  367. */
  368. replay: function(replayableCall, cache)
  369. {
  370. var replayObject = ReplayableResource.replay(replayableCall.replayableResource(), cache);
  371. var replayArgs = replayableCall.args().map(function(obj) {
  372. return ReplayableResource.replay(obj, cache);
  373. });
  374. var replayResult = undefined;
  375. if (replayableCall.isPropertySetter())
  376. replayObject[replayArgs[0]] = replayArgs[1];
  377. else {
  378. var replayFunction = replayObject[replayableCall.functionName()];
  379. console.assert(typeof replayFunction === "function", "Expected a function to replay");
  380. replayResult = replayFunction.apply(replayObject, replayArgs);
  381. if (replayableCall.result() instanceof ReplayableResource) {
  382. var resource = replayableCall.result().replay(cache);
  383. if (!resource.wrappedObject())
  384. resource.setWrappedObject(replayResult);
  385. }
  386. }
  387. this._thisObject = replayObject;
  388. this._functionName = replayableCall.functionName();
  389. this._args = replayArgs;
  390. this._result = replayResult;
  391. this._stackTrace = replayableCall.stackTrace();
  392. this._freezed = true;
  393. var attachments = replayableCall.attachments();
  394. if (attachments)
  395. this._attachments = TypeUtils.cloneObject(attachments);
  396. return this;
  397. }
  398. }
  399. /**
  400. * @constructor
  401. * @param {ReplayableResource} thisObject
  402. * @param {string} functionName
  403. * @param {Array.<ReplayableResource|*>} args
  404. * @param {ReplayableResource|*} result
  405. * @param {StackTrace} stackTrace
  406. * @param {Object.<string, Object>} attachments
  407. */
  408. function ReplayableCall(thisObject, functionName, args, result, stackTrace, attachments)
  409. {
  410. this._thisObject = thisObject;
  411. this._functionName = functionName;
  412. this._args = args;
  413. this._result = result;
  414. this._stackTrace = stackTrace;
  415. if (attachments)
  416. this._attachments = attachments;
  417. }
  418. ReplayableCall.prototype = {
  419. /**
  420. * @return {ReplayableResource}
  421. */
  422. replayableResource: function()
  423. {
  424. return this._thisObject;
  425. },
  426. /**
  427. * @return {string}
  428. */
  429. functionName: function()
  430. {
  431. return this._functionName;
  432. },
  433. /**
  434. * @return {boolean}
  435. */
  436. isPropertySetter: function()
  437. {
  438. return !this._functionName;
  439. },
  440. /**
  441. * @return {Array.<ReplayableResource|*>}
  442. */
  443. args: function()
  444. {
  445. return this._args;
  446. },
  447. /**
  448. * @return {ReplayableResource|*}
  449. */
  450. result: function()
  451. {
  452. return this._result;
  453. },
  454. /**
  455. * @return {StackTrace}
  456. */
  457. stackTrace: function()
  458. {
  459. return this._stackTrace;
  460. },
  461. /**
  462. * @return {Object.<string, Object>}
  463. */
  464. attachments: function()
  465. {
  466. return this._attachments;
  467. },
  468. /**
  469. * @param {string} name
  470. * @return {Object}
  471. */
  472. attachment: function(name)
  473. {
  474. return this._attachments && this._attachments[name];
  475. },
  476. /**
  477. * @param {Cache} cache
  478. * @return {!Call}
  479. */
  480. replay: function(cache)
  481. {
  482. var call = /** @type {!Call} */ (Object.create(Call.prototype));
  483. return call.replay(this, cache);
  484. }
  485. }
  486. /**
  487. * @constructor
  488. * @param {!Object} wrappedObject
  489. * @param {string} name
  490. */
  491. function Resource(wrappedObject, name)
  492. {
  493. /** @type {number} */
  494. this._id = ++Resource._uniqueId;
  495. /** @type {string} */
  496. this._name = name || "Resource";
  497. /** @type {number} */
  498. this._kindId = Resource._uniqueKindIds[this._name] = (Resource._uniqueKindIds[this._name] || 0) + 1;
  499. /** @type {ResourceTrackingManager} */
  500. this._resourceManager = null;
  501. /** @type {!Array.<Call>} */
  502. this._calls = [];
  503. /**
  504. * This is to prevent GC from collecting associated resources.
  505. * Otherwise, for example in WebGL, subsequent calls to gl.getParameter()
  506. * may return a recently created instance that is no longer bound to a
  507. * Resource object (thus, no history to replay it later).
  508. *
  509. * @type {!Object.<string, Resource>}
  510. */
  511. this._boundResources = Object.create(null);
  512. this.setWrappedObject(wrappedObject);
  513. }
  514. /**
  515. * @type {number}
  516. */
  517. Resource._uniqueId = 0;
  518. /**
  519. * @type {!Object.<string, number>}
  520. */
  521. Resource._uniqueKindIds = {};
  522. /**
  523. * @param {*} obj
  524. * @return {Resource}
  525. */
  526. Resource.forObject = function(obj)
  527. {
  528. if (!obj)
  529. return null;
  530. if (obj instanceof Resource)
  531. return obj;
  532. if (typeof obj === "object")
  533. return obj["__resourceObject"];
  534. return null;
  535. }
  536. /**
  537. * @param {Resource|*} obj
  538. * @return {*}
  539. */
  540. Resource.wrappedObject = function(obj)
  541. {
  542. var resource = Resource.forObject(obj);
  543. return resource ? resource.wrappedObject() : obj;
  544. }
  545. /**
  546. * @param {Resource|*} obj
  547. * @param {!Cache} cache
  548. * @return {ReplayableResource|*}
  549. */
  550. Resource.toReplayable = function(obj, cache)
  551. {
  552. var resource = Resource.forObject(obj);
  553. return resource ? resource.toReplayable(cache) : obj;
  554. }
  555. Resource.prototype = {
  556. /**
  557. * @return {number}
  558. */
  559. id: function()
  560. {
  561. return this._id;
  562. },
  563. /**
  564. * @return {Object}
  565. */
  566. wrappedObject: function()
  567. {
  568. return this._wrappedObject;
  569. },
  570. /**
  571. * @param {!Object} value
  572. */
  573. setWrappedObject: function(value)
  574. {
  575. console.assert(value, "wrappedObject should not be NULL");
  576. console.assert(!(value instanceof Resource), "Binding a Resource object to another Resource object?");
  577. this._wrappedObject = value;
  578. this._bindObjectToResource(value);
  579. },
  580. /**
  581. * @return {Object}
  582. */
  583. proxyObject: function()
  584. {
  585. if (!this._proxyObject)
  586. this._proxyObject = this._wrapObject();
  587. return this._proxyObject;
  588. },
  589. /**
  590. * @return {ResourceTrackingManager}
  591. */
  592. manager: function()
  593. {
  594. return this._resourceManager;
  595. },
  596. /**
  597. * @param {ResourceTrackingManager} value
  598. */
  599. setManager: function(value)
  600. {
  601. this._resourceManager = value;
  602. },
  603. /**
  604. * @return {!Array.<Call>}
  605. */
  606. calls: function()
  607. {
  608. return this._calls;
  609. },
  610. /**
  611. * @return {ContextResource}
  612. */
  613. contextResource: function()
  614. {
  615. if (this instanceof ContextResource)
  616. return /** @type {ContextResource} */ (this);
  617. if (this._calculatingContextResource)
  618. return null;
  619. this._calculatingContextResource = true;
  620. var result = null;
  621. for (var i = 0, n = this._calls.length; i < n; ++i) {
  622. result = this._calls[i].resource().contextResource();
  623. if (result)
  624. break;
  625. }
  626. delete this._calculatingContextResource;
  627. console.assert(result, "Failed to find context resource for " + this._name + "@" + this._kindId);
  628. return result;
  629. },
  630. /**
  631. * @return {string}
  632. */
  633. toDataURL: function()
  634. {
  635. return "";
  636. },
  637. /**
  638. * @param {!Cache} cache
  639. * @return {!ReplayableResource}
  640. */
  641. toReplayable: function(cache)
  642. {
  643. var result = /** @type {ReplayableResource} */ (cache.get(this._id));
  644. if (result)
  645. return result;
  646. var data = {
  647. id: this._id,
  648. name: this._name,
  649. kindId: this._kindId
  650. };
  651. result = new ReplayableResource(this, data);
  652. cache.put(this._id, result); // Put into the cache early to avoid loops.
  653. data.calls = this._calls.map(function(call) {
  654. return call.toReplayable(cache);
  655. });
  656. this._populateReplayableData(data, cache);
  657. var contextResource = this.contextResource();
  658. if (contextResource !== this)
  659. data.contextResource = Resource.toReplayable(contextResource, cache);
  660. return result;
  661. },
  662. /**
  663. * @param {!Object} data
  664. * @param {!Cache} cache
  665. */
  666. _populateReplayableData: function(data, cache)
  667. {
  668. // Do nothing. Should be overridden by subclasses.
  669. },
  670. /**
  671. * @param {!Object} data
  672. * @param {!Cache} cache
  673. * @return {!Resource}
  674. */
  675. replay: function(data, cache)
  676. {
  677. var resource = /** @type {Resource} */ (cache.get(data.id));
  678. if (resource)
  679. return resource;
  680. this._id = data.id;
  681. this._name = data.name;
  682. this._kindId = data.kindId;
  683. this._resourceManager = null;
  684. this._calls = [];
  685. this._boundResources = Object.create(null);
  686. this._wrappedObject = null;
  687. cache.put(data.id, this); // Put into the cache early to avoid loops.
  688. this._doReplayCalls(data, cache);
  689. console.assert(this._wrappedObject, "Resource should be reconstructed!");
  690. return this;
  691. },
  692. /**
  693. * @param {!Object} data
  694. * @param {!Cache} cache
  695. */
  696. _doReplayCalls: function(data, cache)
  697. {
  698. for (var i = 0, n = data.calls.length; i < n; ++i)
  699. this._calls.push(data.calls[i].replay(cache));
  700. },
  701. /**
  702. * @param {!Call} call
  703. */
  704. pushCall: function(call)
  705. {
  706. call.freeze();
  707. this._calls.push(call);
  708. },
  709. /**
  710. * @param {!Object} object
  711. */
  712. _bindObjectToResource: function(object)
  713. {
  714. Object.defineProperty(object, "__resourceObject", {
  715. value: this,
  716. writable: false,
  717. enumerable: false,
  718. configurable: true
  719. });
  720. },
  721. /**
  722. * @param {string} key
  723. * @param {*} obj
  724. */
  725. _registerBoundResource: function(key, obj)
  726. {
  727. var resource = Resource.forObject(obj);
  728. if (resource)
  729. this._boundResources[key] = resource;
  730. else
  731. delete this._boundResources[key];
  732. },
  733. /**
  734. * @return {Object}
  735. */
  736. _wrapObject: function()
  737. {
  738. var wrappedObject = this.wrappedObject();
  739. if (!wrappedObject)
  740. return null;
  741. var proxy = Object.create(wrappedObject.__proto__); // In order to emulate "instanceof".
  742. var self = this;
  743. var customWrapFunctions = this._customWrapFunctions();
  744. function processProperty(property)
  745. {
  746. if (typeof wrappedObject[property] === "function") {
  747. var customWrapFunction = customWrapFunctions[property];
  748. if (customWrapFunction)
  749. proxy[property] = self._wrapCustomFunction(self, wrappedObject, wrappedObject[property], property, customWrapFunction);
  750. else
  751. proxy[property] = self._wrapFunction(self, wrappedObject, wrappedObject[property], property);
  752. } else if (/^[A-Z0-9_]+$/.test(property) && typeof wrappedObject[property] === "number") {
  753. // Fast access to enums and constants.
  754. proxy[property] = wrappedObject[property];
  755. } else {
  756. Object.defineProperty(proxy, property, {
  757. get: function()
  758. {
  759. var obj = wrappedObject[property];
  760. var resource = Resource.forObject(obj);
  761. return resource ? resource : obj;
  762. },
  763. set: self._wrapPropertySetter(self, wrappedObject, property),
  764. enumerable: true
  765. });
  766. }
  767. }
  768. var isEmpty = true;
  769. for (var property in wrappedObject) {
  770. isEmpty = false;
  771. processProperty(property);
  772. }
  773. if (isEmpty)
  774. return wrappedObject; // Nothing to proxy.
  775. this._bindObjectToResource(proxy);
  776. return proxy;
  777. },
  778. /**
  779. * @param {!Resource} resource
  780. * @param {!Object} originalObject
  781. * @param {!Function} originalFunction
  782. * @param {string} functionName
  783. * @param {!Function} customWrapFunction
  784. * @return {!Function}
  785. */
  786. _wrapCustomFunction: function(resource, originalObject, originalFunction, functionName, customWrapFunction)
  787. {
  788. return function()
  789. {
  790. var manager = resource.manager();
  791. var isCapturing = manager && manager.capturing();
  792. if (isCapturing)
  793. manager.captureArguments(resource, arguments);
  794. var wrapFunction = new Resource.WrapFunction(originalObject, originalFunction, functionName, arguments);
  795. customWrapFunction.apply(wrapFunction, arguments);
  796. if (isCapturing) {
  797. var call = wrapFunction.call();
  798. call.setStackTrace(StackTrace.create(1, arguments.callee));
  799. manager.captureCall(call);
  800. }
  801. return wrapFunction.result();
  802. };
  803. },
  804. /**
  805. * @param {!Resource} resource
  806. * @param {!Object} originalObject
  807. * @param {!Function} originalFunction
  808. * @param {string} functionName
  809. * @return {!Function}
  810. */
  811. _wrapFunction: function(resource, originalObject, originalFunction, functionName)
  812. {
  813. return function()
  814. {
  815. var manager = resource.manager();
  816. if (!manager || !manager.capturing())
  817. return originalFunction.apply(originalObject, arguments);
  818. manager.captureArguments(resource, arguments);
  819. var result = originalFunction.apply(originalObject, arguments);
  820. var stackTrace = StackTrace.create(1, arguments.callee);
  821. var call = new Call(resource, functionName, arguments, result, stackTrace);
  822. manager.captureCall(call);
  823. return result;
  824. };
  825. },
  826. /**
  827. * @param {!Resource} resource
  828. * @param {!Object} originalObject
  829. * @param {string} propertyName
  830. * @return {function(*)}
  831. */
  832. _wrapPropertySetter: function(resource, originalObject, propertyName)
  833. {
  834. return function(value)
  835. {
  836. resource._registerBoundResource(propertyName, value);
  837. var manager = resource.manager();
  838. if (!manager || !manager.capturing()) {
  839. originalObject[propertyName] = Resource.wrappedObject(value);
  840. return;
  841. }
  842. var args = [propertyName, value];
  843. manager.captureArguments(resource, args);
  844. originalObject[propertyName] = Resource.wrappedObject(value);
  845. var stackTrace = StackTrace.create(1, arguments.callee);
  846. var call = new Call(resource, "", args, undefined, stackTrace);
  847. manager.captureCall(call);
  848. };
  849. },
  850. /**
  851. * @return {!Object.<string, Function>}
  852. */
  853. _customWrapFunctions: function()
  854. {
  855. return Object.create(null); // May be overridden by subclasses.
  856. }
  857. }
  858. /**
  859. * @constructor
  860. * @param {Object} originalObject
  861. * @param {Function} originalFunction
  862. * @param {string} functionName
  863. * @param {Array|Arguments} args
  864. */
  865. Resource.WrapFunction = function(originalObject, originalFunction, functionName, args)
  866. {
  867. this._originalObject = originalObject;
  868. this._originalFunction = originalFunction;
  869. this._functionName = functionName;
  870. this._args = args;
  871. this._resource = Resource.forObject(originalObject);
  872. console.assert(this._resource, "Expected a wrapped call on a Resource object.");
  873. }
  874. Resource.WrapFunction.prototype = {
  875. /**
  876. * @return {*}
  877. */
  878. result: function()
  879. {
  880. if (!this._executed) {
  881. this._executed = true;
  882. this._result = this._originalFunction.apply(this._originalObject, this._args);
  883. }
  884. return this._result;
  885. },
  886. /**
  887. * @return {!Call}
  888. */
  889. call: function()
  890. {
  891. if (!this._call)
  892. this._call = new Call(this._resource, this._functionName, this._args, this.result());
  893. return this._call;
  894. },
  895. /**
  896. * @param {*} result
  897. */
  898. overrideResult: function(result)
  899. {
  900. var call = this.call();
  901. call.setResult(result);
  902. this._result = result;
  903. }
  904. }
  905. /**
  906. * @param {function(new:Resource, !Object, string)} resourceConstructor
  907. * @param {string} resourceName
  908. * @return {function(this:Resource.WrapFunction)}
  909. */
  910. Resource.WrapFunction.resourceFactoryMethod = function(resourceConstructor, resourceName)
  911. {
  912. /** @this Resource.WrapFunction */
  913. return function()
  914. {
  915. var wrappedObject = /** @type {Object} */ (this.result());
  916. if (!wrappedObject)
  917. return;
  918. var resource = new resourceConstructor(wrappedObject, resourceName);
  919. var manager = this._resource.manager();
  920. if (manager)
  921. manager.registerResource(resource);
  922. this.overrideResult(resource.proxyObject());
  923. resource.pushCall(this.call());
  924. }
  925. }
  926. /**
  927. * @constructor
  928. * @param {!Resource} originalResource
  929. * @param {!Object} data
  930. */
  931. function ReplayableResource(originalResource, data)
  932. {
  933. this._proto = originalResource.__proto__;
  934. this._data = data;
  935. }
  936. ReplayableResource.prototype = {
  937. /**
  938. * @return {number}
  939. */
  940. id: function()
  941. {
  942. return this._data.id;
  943. },
  944. /**
  945. * @return {string}
  946. */
  947. name: function()
  948. {
  949. return this._data.name;
  950. },
  951. /**
  952. * @return {string}
  953. */
  954. description: function()
  955. {
  956. return this._data.name + "@" + this._data.kindId;
  957. },
  958. /**
  959. * @return {!ReplayableResource}
  960. */
  961. replayableContextResource: function()
  962. {
  963. return this._data.contextResource || this;
  964. },
  965. /**
  966. * @param {!Cache} cache
  967. * @return {!Resource}
  968. */
  969. replay: function(cache)
  970. {
  971. var result = /** @type {!Resource} */ (Object.create(this._proto));
  972. result = result.replay(this._data, cache)
  973. console.assert(result.__proto__ === this._proto, "Wrong type of a replay result");
  974. return result;
  975. }
  976. }
  977. /**
  978. * @param {ReplayableResource|*} obj
  979. * @param {!Cache} cache
  980. * @return {*}
  981. */
  982. ReplayableResource.replay = function(obj, cache)
  983. {
  984. return (obj instanceof ReplayableResource) ? obj.replay(cache).wrappedObject() : obj;
  985. }
  986. /**
  987. * @constructor
  988. * @extends {Resource}
  989. * @param {!Object} wrappedObject
  990. * @param {string} name
  991. */
  992. function ContextResource(wrappedObject, name)
  993. {
  994. Resource.call(this, wrappedObject, name);
  995. }
  996. ContextResource.prototype = {
  997. __proto__: Resource.prototype
  998. }
  999. /**
  1000. * @constructor
  1001. * @extends {Resource}
  1002. * @param {!Object} wrappedObject
  1003. * @param {string} name
  1004. */
  1005. function LogEverythingResource(wrappedObject, name)
  1006. {
  1007. Resource.call(this, wrappedObject, name);
  1008. }
  1009. LogEverythingResource.prototype = {
  1010. /**
  1011. * @override
  1012. * @return {!Object.<string, Function>}
  1013. */
  1014. _customWrapFunctions: function()
  1015. {
  1016. var wrapFunctions = Object.create(null);
  1017. var wrappedObject = this.wrappedObject();
  1018. if (wrappedObject) {
  1019. for (var property in wrappedObject) {
  1020. /** @this Resource.WrapFunction */
  1021. wrapFunctions[property] = function()
  1022. {
  1023. this._resource.pushCall(this.call());
  1024. }
  1025. }
  1026. }
  1027. return wrapFunctions;
  1028. },
  1029. __proto__: Resource.prototype
  1030. }
  1031. ////////////////////////////////////////////////////////////////////////////////
  1032. // WebGL
  1033. ////////////////////////////////////////////////////////////////////////////////
  1034. /**
  1035. * @constructor
  1036. * @extends {Resource}
  1037. * @param {!Object} wrappedObject
  1038. * @param {string} name
  1039. */
  1040. function WebGLBoundResource(wrappedObject, name)
  1041. {
  1042. Resource.call(this, wrappedObject, name);
  1043. /** @type {!Object.<string, *>} */
  1044. this._state = {};
  1045. }
  1046. WebGLBoundResource.prototype = {
  1047. /**
  1048. * @override
  1049. * @param {!Object} data
  1050. * @param {!Cache} cache
  1051. */
  1052. _populateReplayableData: function(data, cache)
  1053. {
  1054. var state = this._state;
  1055. data.state = {};
  1056. Object.keys(state).forEach(function(parameter) {
  1057. data.state[parameter] = Resource.toReplayable(state[parameter], cache);
  1058. });
  1059. },
  1060. /**
  1061. * @override
  1062. * @param {!Object} data
  1063. * @param {!Cache} cache
  1064. */
  1065. _doReplayCalls: function(data, cache)
  1066. {
  1067. var gl = this._replayContextResource(data, cache).wrappedObject();
  1068. /** @type {!Object.<string, Array.<string>>} */
  1069. var bindingsData = {
  1070. TEXTURE_2D: ["bindTexture", "TEXTURE_BINDING_2D"],
  1071. TEXTURE_CUBE_MAP: ["bindTexture", "TEXTURE_BINDING_CUBE_MAP"],
  1072. ARRAY_BUFFER: ["bindBuffer", "ARRAY_BUFFER_BINDING"],
  1073. ELEMENT_ARRAY_BUFFER: ["bindBuffer", "ELEMENT_ARRAY_BUFFER_BINDING"],
  1074. FRAMEBUFFER: ["bindFramebuffer", "FRAMEBUFFER_BINDING"],
  1075. RENDERBUFFER: ["bindRenderbuffer", "RENDERBUFFER_BINDING"]
  1076. };
  1077. var originalBindings = {};
  1078. Object.keys(bindingsData).forEach(function(bindingTarget) {
  1079. var bindingParameter = bindingsData[bindingTarget][1];
  1080. originalBindings[bindingTarget] = gl.getParameter(gl[bindingParameter]);
  1081. });
  1082. var state = {};
  1083. Object.keys(data.state).forEach(function(parameter) {
  1084. state[parameter] = ReplayableResource.replay(data.state[parameter], cache);
  1085. });
  1086. this._state = state;
  1087. Resource.prototype._doReplayCalls.call(this, data, cache);
  1088. Object.keys(bindingsData).forEach(function(bindingTarget) {
  1089. var bindMethodName = bindingsData[bindingTarget][0];
  1090. gl[bindMethodName].call(gl, gl[bindingTarget], originalBindings[bindingTarget]);
  1091. });
  1092. },
  1093. /**
  1094. * @param {!Object} data
  1095. * @param {!Cache} cache
  1096. * @return {WebGLRenderingContextResource}
  1097. */
  1098. _replayContextResource: function(data, cache)
  1099. {
  1100. var calls = /** @type {!Array.<ReplayableCall>} */ (data.calls);
  1101. for (var i = 0, n = calls.length; i < n; ++i) {
  1102. var resource = ReplayableResource.replay(calls[i].replayableResource(), cache);
  1103. var contextResource = WebGLRenderingContextResource.forObject(resource);
  1104. if (contextResource)
  1105. return contextResource;
  1106. }
  1107. return null;
  1108. },
  1109. /**
  1110. * @param {number} target
  1111. * @param {string} bindMethodName
  1112. */
  1113. pushBinding: function(target, bindMethodName)
  1114. {
  1115. if (this._state.BINDING !== target) {
  1116. this._state.BINDING = target;
  1117. this.pushCall(new Call(WebGLRenderingContextResource.forObject(this), bindMethodName, [target, this]));
  1118. }
  1119. },
  1120. __proto__: Resource.prototype
  1121. }
  1122. /**
  1123. * @constructor
  1124. * @extends {WebGLBoundResource}
  1125. * @param {!Object} wrappedObject
  1126. * @param {string} name
  1127. */
  1128. function WebGLTextureResource(wrappedObject, name)
  1129. {
  1130. WebGLBoundResource.call(this, wrappedObject, name);
  1131. }
  1132. WebGLTextureResource.prototype = {
  1133. /**
  1134. * @override
  1135. * @param {!Object} data
  1136. * @param {!Cache} cache
  1137. */
  1138. _doReplayCalls: function(data, cache)
  1139. {
  1140. var gl = this._replayContextResource(data, cache).wrappedObject();
  1141. var state = {};
  1142. WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
  1143. state[parameter] = gl.getParameter(gl[parameter]);
  1144. });
  1145. WebGLBoundResource.prototype._doReplayCalls.call(this, data, cache);
  1146. WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
  1147. gl.pixelStorei(gl[parameter], state[parameter]);
  1148. });
  1149. },
  1150. /**
  1151. * @override
  1152. * @param {!Call} call
  1153. */
  1154. pushCall: function(call)
  1155. {
  1156. var gl = WebGLRenderingContextResource.forObject(call.resource()).wrappedObject();
  1157. WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
  1158. var value = gl.getParameter(gl[parameter]);
  1159. if (this._state[parameter] !== value) {
  1160. this._state[parameter] = value;
  1161. var pixelStoreCall = new Call(gl, "pixelStorei", [gl[parameter], value]);
  1162. WebGLBoundResource.prototype.pushCall.call(this, pixelStoreCall);
  1163. }
  1164. }, this);
  1165. // FIXME: remove any older calls that no longer contribute to the resource state.
  1166. // FIXME: optimize memory usage: maybe it's more efficient to store one texImage2D call instead of many texSubImage2D.
  1167. WebGLBoundResource.prototype.pushCall.call(this, call);
  1168. },
  1169. /**
  1170. * Handles: texParameteri, texParameterf
  1171. * @param {!Call} call
  1172. */
  1173. pushCall_texParameter: function(call)
  1174. {
  1175. var args = call.args();
  1176. var pname = args[1];
  1177. var param = args[2];
  1178. if (this._state[pname] !== param) {
  1179. this._state[pname] = param;
  1180. WebGLBoundResource.prototype.pushCall.call(this, call);
  1181. }
  1182. },
  1183. /**
  1184. * Handles: copyTexImage2D, copyTexSubImage2D
  1185. * copyTexImage2D and copyTexSubImage2D define a texture image with pixels from the current framebuffer.
  1186. * @param {!Call} call
  1187. */
  1188. pushCall_copyTexImage2D: function(call)
  1189. {
  1190. var glResource = WebGLRenderingContextResource.forObject(call.resource());
  1191. var gl = glResource.wrappedObject();
  1192. var framebufferResource = /** @type {WebGLFramebufferResource} */ (glResource.currentBinding(gl.FRAMEBUFFER));
  1193. if (framebufferResource)
  1194. this.pushCall(new Call(glResource, "bindFramebuffer", [gl.FRAMEBUFFER, framebufferResource]));
  1195. else {
  1196. // FIXME: Implement this case.
  1197. console.error("ASSERT_NOT_REACHED: Could not properly process a gl." + call.functionName() + " call while the DRAWING BUFFER is bound.");
  1198. }
  1199. this.pushCall(call);
  1200. },
  1201. __proto__: WebGLBoundResource.prototype
  1202. }
  1203. /**
  1204. * @constructor
  1205. * @extends {Resource}
  1206. * @param {!Object} wrappedObject
  1207. * @param {string} name
  1208. */
  1209. function WebGLProgramResource(wrappedObject, name)
  1210. {
  1211. Resource.call(this, wrappedObject, name);
  1212. }
  1213. WebGLProgramResource.prototype = {
  1214. /**
  1215. * @override (overrides @return type)
  1216. * @return {WebGLProgram}
  1217. */
  1218. wrappedObject: function()
  1219. {
  1220. return this._wrappedObject;
  1221. },
  1222. /**
  1223. * @override
  1224. * @param {!Object} data
  1225. * @param {!Cache} cache
  1226. */
  1227. _populateReplayableData: function(data, cache)
  1228. {
  1229. var glResource = WebGLRenderingContextResource.forObject(this);
  1230. var gl = glResource.wrappedObject();
  1231. var program = this.wrappedObject();
  1232. var originalErrors = glResource.getAllErrors();
  1233. var uniforms = [];
  1234. var uniformsCount = /** @type {number} */ (gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS));
  1235. for (var i = 0; i < uniformsCount; ++i) {
  1236. var activeInfo = gl.getActiveUniform(program, i);
  1237. if (!activeInfo)
  1238. continue;
  1239. var uniformLocation = gl.getUniformLocation(program, activeInfo.name);
  1240. if (!uniformLocation)
  1241. continue;
  1242. var value = gl.getUniform(program, uniformLocation);
  1243. uniforms.push({
  1244. name: activeInfo.name,
  1245. type: activeInfo.type,
  1246. value: value
  1247. });
  1248. }
  1249. data.uniforms = uniforms;
  1250. glResource.restoreErrors(originalErrors);
  1251. },
  1252. /**
  1253. * @override
  1254. * @param {!Object} data
  1255. * @param {!Cache} cache
  1256. */
  1257. _doReplayCalls: function(data, cache)
  1258. {
  1259. Resource.prototype._doReplayCalls.call(this, data, cache);
  1260. var gl = WebGLRenderingContextResource.forObject(this).wrappedObject();
  1261. var program = this.wrappedObject();
  1262. var originalProgram = /** @type {WebGLProgram} */ (gl.getParameter(gl.CURRENT_PROGRAM));
  1263. var currentProgram = originalProgram;
  1264. data.uniforms.forEach(function(uniform) {
  1265. var uniformLocation = gl.getUniformLocation(program, uniform.name);
  1266. if (!uniformLocation)
  1267. return;
  1268. if (currentProgram !== program) {
  1269. currentProgram = program;
  1270. gl.useProgram(program);
  1271. }
  1272. var methodName = this._uniformMethodNameByType(gl, uniform.type);
  1273. if (methodName.indexOf("Matrix") === -1)
  1274. gl[methodName].call(gl, uniformLocation, uniform.value);
  1275. else
  1276. gl[methodName].call(gl, uniformLocation, false, uniform.value);
  1277. }.bind(this));
  1278. if (currentProgram !== originalProgram)
  1279. gl.useProgram(originalProgram);
  1280. },
  1281. /**
  1282. * @param {WebGLRenderingContext} gl
  1283. * @param {number} type
  1284. * @return {string}
  1285. */
  1286. _uniformMethodNameByType: function(gl, type)
  1287. {
  1288. var uniformMethodNames = WebGLProgramResource._uniformMethodNames;
  1289. if (!uniformMethodNames) {
  1290. uniformMethodNames = {};
  1291. uniformMethodNames[gl.FLOAT] = "uniform1f";
  1292. uniformMethodNames[gl.FLOAT_VEC2] = "uniform2fv";
  1293. uniformMethodNames[gl.FLOAT_VEC3] = "uniform3fv";
  1294. uniformMethodNames[gl.FLOAT_VEC4] = "uniform4fv";
  1295. uniformMethodNames[gl.INT] = "uniform1i";
  1296. uniformMethodNames[gl.BOOL] = "uniform1i";
  1297. uniformMethodNames[gl.SAMPLER_2D] = "uniform1i";
  1298. uniformMethodNames[gl.SAMPLER_CUBE] = "uniform1i";
  1299. uniformMethodNames[gl.INT_VEC2] = "uniform2iv";
  1300. uniformMethodNames[gl.BOOL_VEC2] = "uniform2iv";
  1301. uniformMethodNames[gl.INT_VEC3] = "uniform3iv";
  1302. uniformMethodNames[gl.BOOL_VEC3] = "uniform3iv";
  1303. uniformMethodNames[gl.INT_VEC4] = "uniform4iv";
  1304. uniformMethodNames[gl.BOOL_VEC4] = "uniform4iv";
  1305. uniformMethodNames[gl.FLOAT_MAT2] = "uniformMatrix2fv";
  1306. uniformMethodNames[gl.FLOAT_MAT3] = "uniformMatrix3fv";
  1307. uniformMethodNames[gl.FLOAT_MAT4] = "uniformMatrix4fv";
  1308. WebGLProgramResource._uniformMethodNames = uniformMethodNames;
  1309. }
  1310. console.assert(uniformMethodNames[type], "Unknown uniform type " + type);
  1311. return uniformMethodNames[type];
  1312. },
  1313. /**
  1314. * @override
  1315. * @param {!Call} call
  1316. */
  1317. pushCall: function(call)
  1318. {
  1319. // FIXME: remove any older calls that no longer contribute to the resource state.
  1320. // FIXME: handle multiple attachShader && detachShader.
  1321. Resource.prototype.pushCall.call(this, call);
  1322. },
  1323. __proto__: Resource.prototype
  1324. }
  1325. /**
  1326. * @constructor
  1327. * @extends {Resource}
  1328. * @param {!Object} wrappedObject
  1329. * @param {string} name
  1330. */
  1331. function WebGLShaderResource(wrappedObject, name)
  1332. {
  1333. Resource.call(this, wrappedObject, name);
  1334. }
  1335. WebGLShaderResource.prototype = {
  1336. /**
  1337. * @return {number}
  1338. */
  1339. type: function()
  1340. {
  1341. var call = this._calls[0];
  1342. if (call && call.functionName() === "createShader")
  1343. return call.args()[0];
  1344. console.error("ASSERT_NOT_REACHED: Failed to restore shader type from the log.", call);
  1345. return 0;
  1346. },
  1347. /**
  1348. * @override
  1349. * @param {!Call} call
  1350. */
  1351. pushCall: function(call)
  1352. {
  1353. // FIXME: remove any older calls that no longer contribute to the resource state.
  1354. // FIXME: handle multiple shaderSource calls.
  1355. Resource.prototype.pushCall.call(this, call);
  1356. },
  1357. __proto__: Resource.prototype
  1358. }
  1359. /**
  1360. * @constructor
  1361. * @extends {WebGLBoundResource}
  1362. * @param {!Object} wrappedObject
  1363. * @param {string} name
  1364. */
  1365. function WebGLBufferResource(wrappedObject, name)
  1366. {
  1367. WebGLBoundResource.call(this, wrappedObject, name);
  1368. }
  1369. WebGLBufferResource.prototype = {
  1370. /**
  1371. * @override
  1372. * @param {!Call} call
  1373. */
  1374. pushCall: function(call)
  1375. {
  1376. // FIXME: remove any older calls that no longer contribute to the resource state.
  1377. // FIXME: Optimize memory for bufferSubData.
  1378. WebGLBoundResource.prototype.pushCall.call(this, call);
  1379. },
  1380. __proto__: WebGLBoundResource.prototype
  1381. }
  1382. /**
  1383. * @constructor
  1384. * @extends {WebGLBoundResource}
  1385. * @param {!Object} wrappedObject
  1386. * @param {string} name
  1387. */
  1388. function WebGLFramebufferResource(wrappedObject, name)
  1389. {
  1390. WebGLBoundResource.call(this, wrappedObject, name);
  1391. }
  1392. WebGLFramebufferResource.prototype = {
  1393. /**
  1394. * @override
  1395. * @param {!Call} call
  1396. */
  1397. pushCall: function(call)
  1398. {
  1399. // FIXME: remove any older calls that no longer contribute to the resource state.
  1400. WebGLBoundResource.prototype.pushCall.call(this, call);
  1401. },
  1402. __proto__: WebGLBoundResource.prototype
  1403. }
  1404. /**
  1405. * @constructor
  1406. * @extends {WebGLBoundResource}
  1407. * @param {!Object} wrappedObject
  1408. * @param {string} name
  1409. */
  1410. function WebGLRenderbufferResource(wrappedObject, name)
  1411. {
  1412. WebGLBoundResource.call(this, wrappedObject, name);
  1413. }
  1414. WebGLRenderbufferResource.prototype = {
  1415. /**
  1416. * @override
  1417. * @param {!Call} call
  1418. */
  1419. pushCall: function(call)
  1420. {
  1421. // FIXME: remove any older calls that no longer contribute to the resource state.
  1422. WebGLBoundResource.prototype.pushCall.call(this, call);
  1423. },
  1424. __proto__: WebGLBoundResource.prototype
  1425. }
  1426. /**
  1427. * @constructor
  1428. * @extends {ContextResource}
  1429. * @param {!WebGLRenderingContext} glContext
  1430. */
  1431. function WebGLRenderingContextResource(glContext)
  1432. {
  1433. ContextResource.call(this, glContext, "WebGLRenderingContext");
  1434. /** @type {Object.<number, boolean>} */
  1435. this._customErrors = null;
  1436. /** @type {!Object.<string, boolean>} */
  1437. this._extensions = {};
  1438. }
  1439. /**
  1440. * @const
  1441. * @type {!Array.<string>}
  1442. */
  1443. WebGLRenderingContextResource.GLCapabilities = [
  1444. "BLEND",
  1445. "CULL_FACE",
  1446. "DEPTH_TEST",
  1447. "DITHER",
  1448. "POLYGON_OFFSET_FILL",
  1449. "SAMPLE_ALPHA_TO_COVERAGE",
  1450. "SAMPLE_COVERAGE",
  1451. "SCISSOR_TEST",
  1452. "STENCIL_TEST"
  1453. ];
  1454. /**
  1455. * @const
  1456. * @type {!Array.<string>}
  1457. */
  1458. WebGLRenderingContextResource.PixelStoreParameters = [
  1459. "PACK_ALIGNMENT",
  1460. "UNPACK_ALIGNMENT",
  1461. "UNPACK_COLORSPACE_CONVERSION_WEBGL",
  1462. "UNPACK_FLIP_Y_WEBGL",
  1463. "UNPACK_PREMULTIPLY_ALPHA_WEBGL"
  1464. ];
  1465. /**
  1466. * @const
  1467. * @type {!Array.<string>}
  1468. */
  1469. WebGLRenderingContextResource.StateParameters = [
  1470. "ACTIVE_TEXTURE",
  1471. "ARRAY_BUFFER_BINDING",
  1472. "BLEND_COLOR",
  1473. "BLEND_DST_ALPHA",
  1474. "BLEND_DST_RGB",
  1475. "BLEND_EQUATION_ALPHA",
  1476. "BLEND_EQUATION_RGB",
  1477. "BLEND_SRC_ALPHA",
  1478. "BLEND_SRC_RGB",
  1479. "COLOR_CLEAR_VALUE",
  1480. "COLOR_WRITEMASK",
  1481. "CULL_FACE_MODE",
  1482. "CURRENT_PROGRAM",
  1483. "DEPTH_CLEAR_VALUE",
  1484. "DEPTH_FUNC",
  1485. "DEPTH_RANGE",
  1486. "DEPTH_WRITEMASK",
  1487. "ELEMENT_ARRAY_BUFFER_BINDING",
  1488. "FRAMEBUFFER_BINDING",
  1489. "FRONT_FACE",
  1490. "GENERATE_MIPMAP_HINT",
  1491. "LINE_WIDTH",
  1492. "PACK_ALIGNMENT",
  1493. "POLYGON_OFFSET_FACTOR",
  1494. "POLYGON_OFFSET_UNITS",
  1495. "RENDERBUFFER_BINDING",
  1496. "SAMPLE_COVERAGE_INVERT",
  1497. "SAMPLE_COVERAGE_VALUE",
  1498. "SCISSOR_BOX",
  1499. "STENCIL_BACK_FAIL",
  1500. "STENCIL_BACK_FUNC",
  1501. "STENCIL_BACK_PASS_DEPTH_FAIL",
  1502. "STENCIL_BACK_PASS_DEPTH_PASS",
  1503. "STENCIL_BACK_REF",
  1504. "STENCIL_BACK_VALUE_MASK",
  1505. "STENCIL_BACK_WRITEMASK",
  1506. "STENCIL_CLEAR_VALUE",
  1507. "STENCIL_FAIL",
  1508. "STENCIL_FUNC",
  1509. "STENCIL_PASS_DEPTH_FAIL",
  1510. "STENCIL_PASS_DEPTH_PASS",
  1511. "STENCIL_REF",
  1512. "STENCIL_VALUE_MASK",
  1513. "STENCIL_WRITEMASK",
  1514. "UNPACK_ALIGNMENT",
  1515. "UNPACK_COLORSPACE_CONVERSION_WEBGL",
  1516. "UNPACK_FLIP_Y_WEBGL",
  1517. "UNPACK_PREMULTIPLY_ALPHA_WEBGL",
  1518. "VIEWPORT"
  1519. ];
  1520. /**
  1521. * @const
  1522. * @type {!Object.<string, boolean>}
  1523. */
  1524. WebGLRenderingContextResource.DrawingMethods = TypeUtils.createPrefixedPropertyNamesSet([
  1525. "clear",
  1526. "drawArrays",
  1527. "drawElements"
  1528. ]);
  1529. /**
  1530. * @param {*} obj
  1531. * @return {WebGLRenderingContextResource}
  1532. */
  1533. WebGLRenderingContextResource.forObject = function(obj)
  1534. {
  1535. var resource = Resource.forObject(obj);
  1536. if (!resource)
  1537. return null;
  1538. resource = resource.contextResource();
  1539. return (resource instanceof WebGLRenderingContextResource) ? resource : null;
  1540. }
  1541. WebGLRenderingContextResource.prototype = {
  1542. /**
  1543. * @override (overrides @return type)
  1544. * @return {WebGLRenderingContext}
  1545. */
  1546. wrappedObject: function()
  1547. {
  1548. return this._wrappedObject;
  1549. },
  1550. /**
  1551. * @override
  1552. * @return {string}
  1553. */
  1554. toDataURL: function()
  1555. {
  1556. return this.wrappedObject().canvas.toDataURL();
  1557. },
  1558. /**
  1559. * @return {Array.<number>}
  1560. */
  1561. getAllErrors: function()
  1562. {
  1563. var errors = [];
  1564. var gl = this.wrappedObject();
  1565. if (gl) {
  1566. while (true) {
  1567. var error = gl.getError();
  1568. if (error === gl.NO_ERROR)
  1569. break;
  1570. this.clearError(error);
  1571. errors.push(error);
  1572. }
  1573. }
  1574. if (this._customErrors) {
  1575. for (var key in this._customErrors) {
  1576. var error = Number(key);
  1577. errors.push(error);
  1578. }
  1579. delete this._customErrors;
  1580. }
  1581. return errors;
  1582. },
  1583. /**
  1584. * @param {Array.<number>} errors
  1585. */
  1586. restoreErrors: function(errors)
  1587. {
  1588. var gl = this.wrappedObject();
  1589. if (gl) {
  1590. var wasError = false;
  1591. while (gl.getError() !== gl.NO_ERROR)
  1592. wasError = true;
  1593. console.assert(!wasError, "Error(s) while capturing current WebGL state.");
  1594. }
  1595. if (!errors.length)
  1596. delete this._customErrors;
  1597. else {
  1598. this._customErrors = {};
  1599. for (var i = 0, n = errors.length; i < n; ++i)
  1600. this._customErrors[errors[i]] = true;
  1601. }
  1602. },
  1603. /**
  1604. * @param {number} error
  1605. */
  1606. clearError: function(error)
  1607. {
  1608. if (this._customErrors)
  1609. delete this._customErrors[error];
  1610. },
  1611. /**
  1612. * @return {number}
  1613. */
  1614. nextError: function()
  1615. {
  1616. if (this._customErrors) {
  1617. for (var key in this._customErrors) {
  1618. var error = Number(key);
  1619. delete this._customErrors[error];
  1620. return error;
  1621. }
  1622. }
  1623. delete this._customErrors;
  1624. var gl = this.wrappedObject();
  1625. return gl ? gl.NO_ERROR : 0;
  1626. },
  1627. /**
  1628. * @param {string} name
  1629. */
  1630. addExtension: function(name)
  1631. {
  1632. // FIXME: Wrap OES_vertex_array_object extension.
  1633. this._extensions[name.toLowerCase()] = true;
  1634. },
  1635. /**
  1636. * @override
  1637. * @param {!Object} data
  1638. * @param {!Cache} cache
  1639. */
  1640. _populateReplayableData: function(data, cache)
  1641. {
  1642. var gl = this.wrappedObject();
  1643. data.originalCanvas = gl.canvas;
  1644. data.originalContextAttributes = gl.getContextAttributes();
  1645. data.extensions = TypeUtils.cloneObject(this._extensions);
  1646. var originalErrors = this.getAllErrors();
  1647. // Take a full GL state snapshot.
  1648. var glState = {};
  1649. WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {
  1650. glState[parameter] = gl.isEnabled(gl[parameter]);
  1651. });
  1652. WebGLRenderingContextResource.StateParameters.forEach(function(parameter) {
  1653. glState[parameter] = Resource.toReplayable(gl.getParameter(gl[parameter]), cache);
  1654. });
  1655. // VERTEX_ATTRIB_ARRAYS
  1656. var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VERTEX_ATTRIBS));
  1657. 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"];
  1658. var vertexAttribStates = [];
  1659. for (var i = 0; i < maxVertexAttribs; ++i) {
  1660. var state = {};
  1661. vertexAttribParameters.forEach(function(attribParameter) {
  1662. state[attribParameter] = Resource.toReplayable(gl.getVertexAttrib(i, gl[attribParameter]), cache);
  1663. });
  1664. state.VERTEX_ATTRIB_ARRAY_POINTER = gl.getVertexAttribOffset(i, gl.VERTEX_ATTRIB_ARRAY_POINTER);
  1665. vertexAttribStates.push(state);
  1666. }
  1667. glState.vertexAttribStates = vertexAttribStates;
  1668. // TEXTURES
  1669. var currentTextureBinding = /** @type {number} */ (gl.getParameter(gl.ACTIVE_TEXTURE));
  1670. var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
  1671. var textureBindings = [];
  1672. for (var i = 0; i < maxTextureImageUnits; ++i) {
  1673. gl.activeTexture(gl.TEXTURE0 + i);
  1674. var state = {
  1675. TEXTURE_2D: Resource.toReplayable(gl.getParameter(gl.TEXTURE_BINDING_2D), cache),
  1676. TEXTURE_CUBE_MAP: Resource.toReplayable(gl.getParameter(gl.TEXTURE_BINDING_CUBE_MAP), cache)
  1677. };
  1678. textureBindings.push(state);
  1679. }
  1680. glState.textureBindings = textureBindings;
  1681. gl.activeTexture(currentTextureBinding);
  1682. data.glState = glState;
  1683. this.restoreErrors(originalErrors);
  1684. },
  1685. /**
  1686. * @override
  1687. * @param {!Object} data
  1688. * @param {!Cache} cache
  1689. */
  1690. _doReplayCalls: function(data, cache)
  1691. {
  1692. this._customErrors = null;
  1693. this._extensions = TypeUtils.cloneObject(data.extensions) || {};
  1694. var canvas = data.originalCanvas.cloneNode(true);
  1695. var replayContext = null;
  1696. var contextIds = ["experimental-webgl", "webkit-3d", "3d"];
  1697. for (var i = 0, contextId; contextId = contextIds[i]; ++i) {
  1698. replayContext = canvas.getContext(contextId, data.originalContextAttributes);
  1699. if (replayContext)
  1700. break;
  1701. }
  1702. console.assert(replayContext, "Failed to create a WebGLRenderingContext for the replay.");
  1703. var gl = /** @type {!WebGLRenderingContext} */ (Resource.wrappedObject(replayContext));
  1704. this.setWrappedObject(gl);
  1705. // Enable corresponding WebGL extensions.
  1706. for (var name in this._extensions)
  1707. gl.getExtension(name);
  1708. var glState = data.glState;
  1709. gl.bindFramebuffer(gl.FRAMEBUFFER, /** @type {WebGLFramebuffer} */ (ReplayableResource.replay(glState.FRAMEBUFFER_BINDING, cache)));
  1710. gl.bindRenderbuffer(gl.RENDERBUFFER, /** @type {WebGLRenderbuffer} */ (ReplayableResource.replay(glState.RENDERBUFFER_BINDING, cache)));
  1711. // Enable or disable server-side GL capabilities.
  1712. WebGLRenderingContextResource.GLCapabilities.forEach(function(parameter) {
  1713. console.assert(parameter in glState);
  1714. if (glState[parameter])
  1715. gl.enable(gl[parameter]);
  1716. else
  1717. gl.disable(gl[parameter]);
  1718. });
  1719. gl.blendColor(glState.BLEND_COLOR[0], glState.BLEND_COLOR[1], glState.BLEND_COLOR[2], glState.BLEND_COLOR[3]);
  1720. gl.blendEquationSeparate(glState.BLEND_EQUATION_RGB, glState.BLEND_EQUATION_ALPHA);
  1721. gl.blendFuncSeparate(glState.BLEND_SRC_RGB, glState.BLEND_DST_RGB, glState.BLEND_SRC_ALPHA, glState.BLEND_DST_ALPHA);
  1722. gl.clearColor(glState.COLOR_CLEAR_VALUE[0], glState.COLOR_CLEAR_VALUE[1], glState.COLOR_CLEAR_VALUE[2], glState.COLOR_CLEAR_VALUE[3]);
  1723. gl.clearDepth(glState.DEPTH_CLEAR_VALUE);
  1724. gl.clearStencil(glState.STENCIL_CLEAR_VALUE);
  1725. gl.colorMask(glState.COLOR_WRITEMASK[0], glState.COLOR_WRITEMASK[1], glState.COLOR_WRITEMASK[2], glState.COLOR_WRITEMASK[3]);
  1726. gl.cullFace(glState.CULL_FACE_MODE);
  1727. gl.depthFunc(glState.DEPTH_FUNC);
  1728. gl.depthMask(glState.DEPTH_WRITEMASK);
  1729. gl.depthRange(glState.DEPTH_RANGE[0], glState.DEPTH_RANGE[1]);
  1730. gl.frontFace(glState.FRONT_FACE);
  1731. gl.hint(gl.GENERATE_MIPMAP_HINT, glState.GENERATE_MIPMAP_HINT);
  1732. gl.lineWidth(glState.LINE_WIDTH);
  1733. WebGLRenderingContextResource.PixelStoreParameters.forEach(function(parameter) {
  1734. gl.pixelStorei(gl[parameter], glState[parameter]);
  1735. });
  1736. gl.polygonOffset(glState.POLYGON_OFFSET_FACTOR, glState.POLYGON_OFFSET_UNITS);
  1737. gl.sampleCoverage(glState.SAMPLE_COVERAGE_VALUE, glState.SAMPLE_COVERAGE_INVERT);
  1738. gl.stencilFuncSeparate(gl.FRONT, glState.STENCIL_FUNC, glState.STENCIL_REF, glState.STENCIL_VALUE_MASK);
  1739. gl.stencilFuncSeparate(gl.BACK, glState.STENCIL_BACK_FUNC, glState.STENCIL_BACK_REF, glState.STENCIL_BACK_VALUE_MASK);
  1740. gl.stencilOpSeparate(gl.FRONT, glState.STENCIL_FAIL, glState.STENCIL_PASS_DEPTH_FAIL, glState.STENCIL_PASS_DEPTH_PASS);
  1741. gl.stencilOpSeparate(gl.BACK, glState.STENCIL_BACK_FAIL, glState.STENCIL_BACK_PASS_DEPTH_FAIL, glState.STENCIL_BACK_PASS_DEPTH_PASS);
  1742. gl.stencilMaskSeparate(gl.FRONT, glState.STENCIL_WRITEMASK);
  1743. gl.stencilMaskSeparate(gl.BACK, glState.STENCIL_BACK_WRITEMASK);
  1744. gl.scissor(glState.SCISSOR_BOX[0], glState.SCISSOR_BOX[1], glState.SCISSOR_BOX[2], glState.SCISSOR_BOX[3]);
  1745. gl.viewport(glState.VIEWPORT[0], glState.VIEWPORT[1], glState.VIEWPORT[2], glState.VIEWPORT[3]);
  1746. gl.useProgram(/** @type {WebGLProgram} */ (ReplayableResource.replay(glState.CURRENT_PROGRAM, cache)));
  1747. // VERTEX_ATTRIB_ARRAYS
  1748. var maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VERTEX_ATTRIBS));
  1749. for (var i = 0; i < maxVertexAttribs; ++i) {
  1750. var state = glState.vertexAttribStates[i] || {};
  1751. if (state.VERTEX_ATTRIB_ARRAY_ENABLED)
  1752. gl.enableVertexAttribArray(i);
  1753. else
  1754. gl.disableVertexAttribArray(i);
  1755. if (state.CURRENT_VERTEX_ATTRIB)
  1756. gl.vertexAttrib4fv(i, state.CURRENT_VERTEX_ATTRIB);
  1757. var buffer = /** @type {WebGLBuffer} */ (ReplayableResource.replay(state.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, cache));
  1758. if (buffer) {
  1759. gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  1760. 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);
  1761. }
  1762. }
  1763. gl.bindBuffer(gl.ARRAY_BUFFER, /** @type {WebGLBuffer} */ (ReplayableResource.replay(glState.ARRAY_BUFFER_BINDING, cache)));
  1764. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, /** @type {WebGLBuffer} */ (ReplayableResource.replay(glState.ELEMENT_ARRAY_BUFFER_BINDING, cache)));
  1765. // TEXTURES
  1766. var maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
  1767. for (var i = 0; i < maxTextureImageUnits; ++i) {
  1768. gl.activeTexture(gl.TEXTURE0 + i);
  1769. var state = glState.textureBindings[i] || {};
  1770. gl.bindTexture(gl.TEXTURE_2D, /** @type {WebGLTexture} */ (ReplayableResource.replay(state.TEXTURE_2D, cache)));
  1771. gl.bindTexture(gl.TEXTURE_CUBE_MAP, /** @type {WebGLTexture} */ (ReplayableResource.replay(state.TEXTURE_CUBE_MAP, cache)));
  1772. }
  1773. gl.activeTexture(glState.ACTIVE_TEXTURE);
  1774. ContextResource.prototype._doReplayCalls.call(this, data, cache);
  1775. },
  1776. /**
  1777. * @param {Object|number} target
  1778. * @return {Resource}
  1779. */
  1780. currentBinding: function(target)
  1781. {
  1782. var resource = Resource.forObject(target);
  1783. if (resource)
  1784. return resource;
  1785. var gl = this.wrappedObject();
  1786. var bindingParameter;
  1787. var bindMethodName;
  1788. var bindMethodTarget = target;
  1789. switch (target) {
  1790. case gl.ARRAY_BUFFER:
  1791. bindingParameter = gl.ARRAY_BUFFER_BINDING;
  1792. bindMethodName = "bindBuffer";
  1793. break;
  1794. case gl.ELEMENT_ARRAY_BUFFER:
  1795. bindingParameter = gl.ELEMENT_ARRAY_BUFFER_BINDING;
  1796. bindMethodName = "bindBuffer";
  1797. break;
  1798. case gl.TEXTURE_2D:
  1799. bindingParameter = gl.TEXTURE_BINDING_2D;
  1800. bindMethodName = "bindTexture";
  1801. break;
  1802. case gl.TEXTURE_CUBE_MAP:
  1803. case gl.TEXTURE_CUBE_MAP_POSITIVE_X:
  1804. case gl.TEXTURE_CUBE_MAP_NEGATIVE_X:
  1805. case gl.TEXTURE_CUBE_MAP_POSITIVE_Y:
  1806. case gl.TEXTURE_CUBE_MAP_NEGATIVE_Y:
  1807. case gl.TEXTURE_CUBE_MAP_POSITIVE_Z:
  1808. case gl.TEXTURE_CUBE_MAP_NEGATIVE_Z:
  1809. bindingParameter = gl.TEXTURE_BINDING_CUBE_MAP;
  1810. bindMethodTarget = gl.TEXTURE_CUBE_MAP;
  1811. bindMethodName = "bindTexture";
  1812. break;
  1813. case gl.FRAMEBUFFER:
  1814. bindingParameter = gl.FRAMEBUFFER_BINDING;
  1815. bindMethodName = "bindFramebuffer";
  1816. break;
  1817. case gl.RENDERBUFFER:
  1818. bindingParameter = gl.RENDERBUFFER_BINDING;
  1819. bindMethodName = "bindRenderbuffer";
  1820. break;
  1821. default:
  1822. console.error("ASSERT_NOT_REACHED: unknown binding target " + target);
  1823. return null;
  1824. }
  1825. resource = Resource.forObject(gl.getParameter(bindingParameter));
  1826. if (resource)
  1827. resource.pushBinding(bindMethodTarget, bindMethodName);
  1828. return resource;
  1829. },
  1830. /**
  1831. * @override
  1832. * @return {!Object.<string, Function>}
  1833. */
  1834. _customWrapFunctions: function()
  1835. {
  1836. var wrapFunctions = WebGLRenderingContextResource._wrapFunctions;
  1837. if (!wrapFunctions) {
  1838. wrapFunctions = Object.create(null);
  1839. wrapFunctions["createBuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLBufferResource, "WebGLBuffer");
  1840. wrapFunctions["createShader"] = Resource.WrapFunction.resourceFactoryMethod(WebGLShaderResource, "WebGLShader");
  1841. wrapFunctions["createProgram"] = Resource.WrapFunction.resourceFactoryMethod(WebGLProgramResource, "WebGLProgram");
  1842. wrapFunctions["createTexture"] = Resource.WrapFunction.resourceFactoryMethod(WebGLTextureResource, "WebGLTexture");
  1843. wrapFunctions["createFramebuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLFramebufferResource, "WebGLFramebuffer");
  1844. wrapFunctions["createRenderbuffer"] = Resource.WrapFunction.resourceFactoryMethod(WebGLRenderbufferResource, "WebGLRenderbuffer");
  1845. wrapFunctions["getUniformLocation"] = Resource.WrapFunction.resourceFactoryMethod(Resource, "WebGLUniformLocation");
  1846. /**
  1847. * @param {string} methodName
  1848. * @param {function(this:Resource, !Call)=} pushCallFunc
  1849. */
  1850. function stateModifyingWrapFunction(methodName, pushCallFunc)
  1851. {
  1852. if (pushCallFunc) {
  1853. /**
  1854. * @param {Object|number} target
  1855. * @this Resource.WrapFunction
  1856. */
  1857. wrapFunctions[methodName] = function(target)
  1858. {
  1859. var resource = this._resource.currentBinding(target);
  1860. if (resource)
  1861. pushCallFunc.call(resource, this.call());
  1862. }
  1863. } else {
  1864. /**
  1865. * @param {Object|number} target
  1866. * @this Resource.WrapFunction
  1867. */
  1868. wrapFunctions[methodName] = function(target)
  1869. {
  1870. var resource = this._resource.currentBinding(target);
  1871. if (resource)
  1872. resource.pushCall(this.call());
  1873. }
  1874. }
  1875. }
  1876. stateModifyingWrapFunction("bindAttribLocation");
  1877. stateModifyingWrapFunction("compileShader");
  1878. stateModifyingWrapFunction("detachShader");
  1879. stateModifyingWrapFunction("linkProgram");
  1880. stateModifyingWrapFunction("shaderSource");
  1881. stateModifyingWrapFunction("bufferData");
  1882. stateModifyingWrapFunction("bufferSubData");
  1883. stateModifyingWrapFunction("compressedTexImage2D");
  1884. stateModifyingWrapFunction("compressedTexSubImage2D");
  1885. stateModifyingWrapFunction("copyTexImage2D", WebGLTextureResource.prototype.pushCall_copyTexImage2D);
  1886. stateModifyingWrapFunction("copyTexSubImage2D", WebGLTextureResource.prototype.pushCall_copyTexImage2D);
  1887. stateModifyingWrapFunction("generateMipmap");
  1888. stateModifyingWrapFunction("texImage2D");
  1889. stateModifyingWrapFunction("texSubImage2D");
  1890. stateModifyingWrapFunction("texParameterf", WebGLTextureResource.prototype.pushCall_texParameter);
  1891. stateModifyingWrapFunction("texParameteri", WebGLTextureResource.prototype.pushCall_texParameter);
  1892. stateModifyingWrapFunction("renderbufferStorage");
  1893. /** @this Resource.WrapFunction */
  1894. wrapFunctions["getError"] = function()
  1895. {
  1896. var gl = /** @type {WebGLRenderingContext} */ (this._originalObject);
  1897. var error = this.result();
  1898. if (error !== gl.NO_ERROR)
  1899. this._resource.clearError(error);
  1900. else {
  1901. error = this._resource.nextError();
  1902. if (error !== gl.NO_ERROR)
  1903. this.overrideResult(error);
  1904. }
  1905. }
  1906. /**
  1907. * @param {string} name
  1908. * @this Resource.WrapFunction
  1909. */
  1910. wrapFunctions["getExtension"] = function(name)
  1911. {
  1912. this._resource.addExtension(name);
  1913. }
  1914. //
  1915. // Register bound WebGL resources.
  1916. //
  1917. /**
  1918. * @param {WebGLProgram} program
  1919. * @param {WebGLShader} shader
  1920. * @this Resource.WrapFunction
  1921. */
  1922. wrapFunctions["attachShader"] = function(program, shader)
  1923. {
  1924. var resource = this._resource.currentBinding(program);
  1925. if (resource) {
  1926. resource.pushCall(this.call());
  1927. var shaderResource = /** @type {WebGLShaderResource} */ (Resource.forObject(shader));
  1928. if (shaderResource) {
  1929. var shaderType = shaderResource.type();
  1930. resource._registerBoundResource("__attachShader_" + shaderType, shaderResource);
  1931. }
  1932. }
  1933. }
  1934. /**
  1935. * @param {number} target
  1936. * @param {number} attachment
  1937. * @param {number} objectTarget
  1938. * @param {WebGLRenderbuffer|WebGLTexture} obj
  1939. * @this Resource.WrapFunction
  1940. */
  1941. wrapFunctions["framebufferRenderbuffer"] = wrapFunctions["framebufferTexture2D"] = function(target, attachment, objectTarget, obj)
  1942. {
  1943. var resource = this._resource.currentBinding(target);
  1944. if (resource) {
  1945. resource.pushCall(this.call());
  1946. resource._registerBoundResource("__framebufferAttachmentObjectName", obj);
  1947. }
  1948. }
  1949. /**
  1950. * @param {number} target
  1951. * @param {Object} obj
  1952. * @this Resource.WrapFunction
  1953. */
  1954. wrapFunctions["bindBuffer"] = wrapFunctions["bindFramebuffer"] = wrapFunctions["bindRenderbuffer"] = function(target, obj)
  1955. {
  1956. this._resource._registerBoundResource("__bindBuffer_" + target, obj);
  1957. }
  1958. /**
  1959. * @param {number} target
  1960. * @param {WebGLTexture} obj
  1961. * @this Resource.WrapFunction
  1962. */
  1963. wrapFunctions["bindTexture"] = function(target, obj)
  1964. {
  1965. var gl = /** @type {WebGLRenderingContext} */ (this._originalObject);
  1966. var currentTextureBinding = /** @type {number} */ (gl.getParameter(gl.ACTIVE_TEXTURE));
  1967. this._resource._registerBoundResource("__bindTexture_" + target + "_" + currentTextureBinding, obj);
  1968. }
  1969. /**
  1970. * @param {WebGLProgram} program
  1971. * @this Resource.WrapFunction
  1972. */
  1973. wrapFunctions["useProgram"] = function(program)
  1974. {
  1975. this._resource._registerBoundResource("__useProgram", program);
  1976. }
  1977. /**
  1978. * @param {number} index
  1979. * @this Resource.WrapFunction
  1980. */
  1981. wrapFunctions["vertexAttribPointer"] = function(index)
  1982. {
  1983. var gl = /** @type {WebGLRenderingContext} */ (this._originalObject);
  1984. this._resource._registerBoundResource("__vertexAttribPointer_" + index, gl.getParameter(gl.ARRAY_BUFFER_BINDING));
  1985. }
  1986. WebGLRenderingContextResource._wrapFunctions = wrapFunctions;
  1987. }
  1988. return wrapFunctions;
  1989. },
  1990. __proto__: ContextResource.prototype
  1991. }
  1992. ////////////////////////////////////////////////////////////////////////////////
  1993. // 2D Canvas
  1994. ////////////////////////////////////////////////////////////////////////////////
  1995. /**
  1996. * @constructor
  1997. * @extends {ContextResource}
  1998. * @param {!CanvasRenderingContext2D} context
  1999. */
  2000. function CanvasRenderingContext2DResource(context)
  2001. {
  2002. ContextResource.call(this, context, "CanvasRenderingContext2D");
  2003. }
  2004. /**
  2005. * @const
  2006. * @type {!Array.<string>}
  2007. */
  2008. CanvasRenderingContext2DResource.AttributeProperties = [
  2009. "strokeStyle",
  2010. "fillStyle",
  2011. "globalAlpha",
  2012. "lineWidth",
  2013. "lineCap",
  2014. "lineJoin",
  2015. "miterLimit",
  2016. "shadowOffsetX",
  2017. "shadowOffsetY",
  2018. "shadowBlur",
  2019. "shadowColor",
  2020. "globalCompositeOperation",
  2021. "font",
  2022. "textAlign",
  2023. "textBaseline",
  2024. "lineDashOffset",
  2025. "webkitLineDash",
  2026. "webkitLineDashOffset"
  2027. ];
  2028. /**
  2029. * @const
  2030. * @type {!Array.<string>}
  2031. */
  2032. CanvasRenderingContext2DResource.PathMethods = [
  2033. "beginPath",
  2034. "moveTo",
  2035. "closePath",
  2036. "lineTo",
  2037. "quadraticCurveTo",
  2038. "bezierCurveTo",
  2039. "arcTo",
  2040. "arc",
  2041. "rect"
  2042. ];
  2043. /**
  2044. * @const
  2045. * @type {!Array.<string>}
  2046. */
  2047. CanvasRenderingContext2DResource.TransformationMatrixMethods = [
  2048. "scale",
  2049. "rotate",
  2050. "translate",
  2051. "transform",
  2052. "setTransform"
  2053. ];
  2054. /**
  2055. * @const
  2056. * @type {!Object.<string, boolean>}
  2057. */
  2058. CanvasRenderingContext2DResource.DrawingMethods = TypeUtils.createPrefixedPropertyNamesSet([
  2059. "clearRect",
  2060. "drawImage",
  2061. "drawImageFromRect",
  2062. "drawCustomFocusRing",
  2063. "drawSystemFocusRing",
  2064. "fill",
  2065. "fillRect",
  2066. "fillText",
  2067. "putImageData",
  2068. "putImageDataHD",
  2069. "stroke",
  2070. "strokeRect",
  2071. "strokeText"
  2072. ]);
  2073. CanvasRenderingContext2DResource.prototype = {
  2074. /**
  2075. * @override (overrides @return type)
  2076. * @return {CanvasRenderingContext2D}
  2077. */
  2078. wrappedObject: function()
  2079. {
  2080. return this._wrappedObject;
  2081. },
  2082. /**
  2083. * @override
  2084. * @return {string}
  2085. */
  2086. toDataURL: function()
  2087. {
  2088. return this.wrappedObject().canvas.toDataURL();
  2089. },
  2090. /**
  2091. * @override
  2092. * @param {!Object} data
  2093. * @param {!Cache} cache
  2094. */
  2095. _populateReplayableData: function(data, cache)
  2096. {
  2097. data.currentAttributes = this._currentAttributesState();
  2098. data.originalCanvasCloned = TypeUtils.cloneIntoCanvas(this.wrappedObject().canvas);
  2099. },
  2100. /**
  2101. * @override
  2102. * @param {!Object} data
  2103. * @param {!Cache} cache
  2104. */
  2105. _doReplayCalls: function(data, cache)
  2106. {
  2107. var canvas = TypeUtils.cloneIntoCanvas(data.originalCanvasCloned);
  2108. var ctx = /** @type {!CanvasRenderingContext2D} */ (Resource.wrappedObject(canvas.getContext("2d")));
  2109. this.setWrappedObject(ctx);
  2110. for (var i = 0, n = data.calls.length; i < n; ++i) {
  2111. var replayableCall = /** @type {ReplayableCall} */ (data.calls[i]);
  2112. if (replayableCall.functionName() === "save")
  2113. this._applyAttributesState(replayableCall.attachment("canvas2dAttributesState"));
  2114. this._calls.push(replayableCall.replay(cache));
  2115. }
  2116. this._applyAttributesState(data.currentAttributes);
  2117. },
  2118. /**
  2119. * @param {!Call} call
  2120. */
  2121. pushCall_setTransform: function(call)
  2122. {
  2123. var saveCallIndex = this._lastIndexOfMatchingSaveCall();
  2124. var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods);
  2125. index = Math.max(index, saveCallIndex);
  2126. if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1))
  2127. this._removeAllObsoleteCallsFromLog();
  2128. this.pushCall(call);
  2129. },
  2130. /**
  2131. * @param {!Call} call
  2132. */
  2133. pushCall_beginPath: function(call)
  2134. {
  2135. var index = this._lastIndexOfAnyCall(["clip"]);
  2136. if (this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1))
  2137. this._removeAllObsoleteCallsFromLog();
  2138. this.pushCall(call);
  2139. },
  2140. /**
  2141. * @param {!Call} call
  2142. */
  2143. pushCall_save: function(call)
  2144. {
  2145. call.setAttachment("canvas2dAttributesState", this._currentAttributesState());
  2146. this.pushCall(call);
  2147. },
  2148. /**
  2149. * @param {!Call} call
  2150. */
  2151. pushCall_restore: function(call)
  2152. {
  2153. var lastIndexOfSave = this._lastIndexOfMatchingSaveCall();
  2154. if (lastIndexOfSave === -1)
  2155. return;
  2156. this._calls[lastIndexOfSave].setAttachment("canvas2dAttributesState", null); // No longer needed, free memory.
  2157. var modified = false;
  2158. if (this._removeCallsFromLog(["clip"], lastIndexOfSave + 1))
  2159. modified = true;
  2160. var lastIndexOfAnyPathMethod = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods);
  2161. var index = Math.max(lastIndexOfSave, lastIndexOfAnyPathMethod);
  2162. if (this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1))
  2163. modified = true;
  2164. if (modified)
  2165. this._removeAllObsoleteCallsFromLog();
  2166. var lastCall = this._calls[this._calls.length - 1];
  2167. if (lastCall && lastCall.functionName() === "save")
  2168. this._calls.pop();
  2169. else
  2170. this.pushCall(call);
  2171. },
  2172. /**
  2173. * @param {number=} fromIndex
  2174. * @return {number}
  2175. */
  2176. _lastIndexOfMatchingSaveCall: function(fromIndex)
  2177. {
  2178. if (typeof fromIndex !== "number")
  2179. fromIndex = this._calls.length - 1;
  2180. else
  2181. fromIndex = Math.min(fromIndex, this._calls.length - 1);
  2182. var stackDepth = 1;
  2183. for (var i = fromIndex; i >= 0; --i) {
  2184. var functionName = this._calls[i].functionName();
  2185. if (functionName === "restore")
  2186. ++stackDepth;
  2187. else if (functionName === "save") {
  2188. --stackDepth;
  2189. if (!stackDepth)
  2190. return i;
  2191. }
  2192. }
  2193. return -1;
  2194. },
  2195. /**
  2196. * @param {!Array.<string>} functionNames
  2197. * @param {number=} fromIndex
  2198. * @return {number}
  2199. */
  2200. _lastIndexOfAnyCall: function(functionNames, fromIndex)
  2201. {
  2202. if (typeof fromIndex !== "number")
  2203. fromIndex = this._calls.length - 1;
  2204. else
  2205. fromIndex = Math.min(fromIndex, this._calls.length - 1);
  2206. for (var i = fromIndex; i >= 0; --i) {
  2207. if (functionNames.indexOf(this._calls[i].functionName()) !== -1)
  2208. return i;
  2209. }
  2210. return -1;
  2211. },
  2212. _removeAllObsoleteCallsFromLog: function()
  2213. {
  2214. // Remove all PATH methods between clip() and beginPath() calls.
  2215. var lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"]);
  2216. while (lastIndexOfBeginPath !== -1) {
  2217. var index = this._lastIndexOfAnyCall(["clip"], lastIndexOfBeginPath - 1);
  2218. this._removeCallsFromLog(CanvasRenderingContext2DResource.PathMethods, index + 1, lastIndexOfBeginPath);
  2219. lastIndexOfBeginPath = this._lastIndexOfAnyCall(["beginPath"], index - 1);
  2220. }
  2221. // Remove all TRASFORMATION MATRIX methods before restore() or setTransform() but after any PATH or corresponding save() method.
  2222. var lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform"]);
  2223. while (lastRestore !== -1) {
  2224. var saveCallIndex = this._lastIndexOfMatchingSaveCall(lastRestore - 1);
  2225. var index = this._lastIndexOfAnyCall(CanvasRenderingContext2DResource.PathMethods, lastRestore - 1);
  2226. index = Math.max(index, saveCallIndex);
  2227. this._removeCallsFromLog(CanvasRenderingContext2DResource.TransformationMatrixMethods, index + 1, lastRestore);
  2228. lastRestore = this._lastIndexOfAnyCall(["restore", "setTransform"], index - 1);
  2229. }
  2230. // Remove all save-restore consecutive pairs.
  2231. var restoreCalls = 0;
  2232. for (var i = this._calls.length - 1; i >= 0; --i) {
  2233. var functionName = this._calls[i].functionName();
  2234. if (functionName === "restore") {
  2235. ++restoreCalls;
  2236. continue;
  2237. }
  2238. if (functionName === "save" && restoreCalls > 0) {
  2239. var saveCallIndex = i;
  2240. for (var j = i - 1; j >= 0 && i - j < restoreCalls; --j) {
  2241. if (this._calls[j].functionName() === "save")
  2242. saveCallIndex = j;
  2243. else
  2244. break;
  2245. }
  2246. this._calls.splice(saveCallIndex, (i - saveCallIndex + 1) * 2);
  2247. i = saveCallIndex;
  2248. }
  2249. restoreCalls = 0;
  2250. }
  2251. },
  2252. /**
  2253. * @param {!Array.<string>} functionNames
  2254. * @param {number} fromIndex
  2255. * @param {number=} toIndex
  2256. * @return {boolean}
  2257. */
  2258. _removeCallsFromLog: function(functionNames, fromIndex, toIndex)
  2259. {
  2260. var oldLength = this._calls.length;
  2261. if (typeof toIndex !== "number")
  2262. toIndex = oldLength;
  2263. else
  2264. toIndex = Math.min(toIndex, oldLength);
  2265. var newIndex = Math.min(fromIndex, oldLength);
  2266. for (var i = newIndex; i < toIndex; ++i) {
  2267. var call = this._calls[i];
  2268. if (functionNames.indexOf(call.functionName()) === -1)
  2269. this._calls[newIndex++] = call;
  2270. }
  2271. if (newIndex >= toIndex)
  2272. return false;
  2273. this._calls.splice(newIndex, toIndex - newIndex);
  2274. return true;
  2275. },
  2276. /**
  2277. * @return {!Object.<string, string>}
  2278. */
  2279. _currentAttributesState: function()
  2280. {
  2281. var ctx = this.wrappedObject();
  2282. var state = {};
  2283. state.attributes = {};
  2284. CanvasRenderingContext2DResource.AttributeProperties.forEach(function(attribute) {
  2285. state.attributes[attribute] = ctx[attribute];
  2286. });
  2287. if (ctx.getLineDash)
  2288. state.lineDash = ctx.getLineDash();
  2289. return state;
  2290. },
  2291. /**
  2292. * @param {Object.<string, string>=} state
  2293. */
  2294. _applyAttributesState: function(state)
  2295. {
  2296. if (!state)
  2297. return;
  2298. var ctx = this.wrappedObject();
  2299. if (state.attributes) {
  2300. Object.keys(state.attributes).forEach(function(attribute) {
  2301. ctx[attribute] = state.attributes[attribute];
  2302. });
  2303. }
  2304. if (ctx.setLineDash)
  2305. ctx.setLineDash(state.lineDash);
  2306. },
  2307. /**
  2308. * @override
  2309. * @return {!Object.<string, Function>}
  2310. */
  2311. _customWrapFunctions: function()
  2312. {
  2313. var wrapFunctions = CanvasRenderingContext2DResource._wrapFunctions;
  2314. if (!wrapFunctions) {
  2315. wrapFunctions = Object.create(null);
  2316. wrapFunctions["createLinearGradient"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasGradient");
  2317. wrapFunctions["createRadialGradient"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasGradient");
  2318. wrapFunctions["createPattern"] = Resource.WrapFunction.resourceFactoryMethod(LogEverythingResource, "CanvasPattern");
  2319. /**
  2320. * @param {string} methodName
  2321. * @param {function(this:Resource, !Call)=} func
  2322. */
  2323. function stateModifyingWrapFunction(methodName, func)
  2324. {
  2325. if (func) {
  2326. /** @this Resource.WrapFunction */
  2327. wrapFunctions[methodName] = function()
  2328. {
  2329. func.call(this._resource, this.call());
  2330. }
  2331. } else {
  2332. /** @this Resource.WrapFunction */
  2333. wrapFunctions[methodName] = function()
  2334. {
  2335. this._resource.pushCall(this.call());
  2336. }
  2337. }
  2338. }
  2339. for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.TransformationMatrixMethods[i]; ++i)
  2340. stateModifyingWrapFunction(methodName, methodName === "setTransform" ? this.pushCall_setTransform : undefined);
  2341. for (var i = 0, methodName; methodName = CanvasRenderingContext2DResource.PathMethods[i]; ++i)
  2342. stateModifyingWrapFunction(methodName, methodName === "beginPath" ? this.pushCall_beginPath : undefined);
  2343. stateModifyingWrapFunction("save", this.pushCall_save);
  2344. stateModifyingWrapFunction("restore", this.pushCall_restore);
  2345. stateModifyingWrapFunction("clip");
  2346. CanvasRenderingContext2DResource._wrapFunctions = wrapFunctions;
  2347. }
  2348. return wrapFunctions;
  2349. },
  2350. __proto__: ContextResource.prototype
  2351. }
  2352. /**
  2353. * @constructor
  2354. * @param {!Object.<string, boolean>=} drawingMethodNames
  2355. */
  2356. function CallFormatter(drawingMethodNames)
  2357. {
  2358. this._drawingMethodNames = drawingMethodNames || Object.create(null);
  2359. }
  2360. CallFormatter.prototype = {
  2361. /**
  2362. * @param {!ReplayableCall} replayableCall
  2363. * @return {!Object}
  2364. */
  2365. formatCall: function(replayableCall)
  2366. {
  2367. var result = {};
  2368. var functionName = replayableCall.functionName();
  2369. if (functionName) {
  2370. result.functionName = functionName;
  2371. result.arguments = replayableCall.args().map(this.formatValue.bind(this));
  2372. if (replayableCall.result() !== undefined)
  2373. result.result = this.formatValue(replayableCall.result());
  2374. if (this._drawingMethodNames[functionName])
  2375. result.isDrawingCall = true;
  2376. } else {
  2377. result.property = replayableCall.args()[0];
  2378. result.value = this.formatValue(replayableCall.args()[1]);
  2379. }
  2380. return result;
  2381. },
  2382. /**
  2383. * @param {*} value
  2384. * @return {!Object}
  2385. */
  2386. formatValue: function(value)
  2387. {
  2388. if (value instanceof ReplayableResource)
  2389. var description = value.description();
  2390. else
  2391. var description = "" + value;
  2392. return { description: description };
  2393. }
  2394. }
  2395. /**
  2396. * @const
  2397. * @type {!Object.<string, !CallFormatter>}
  2398. */
  2399. CallFormatter._formatters = {};
  2400. /**
  2401. * @param {string} resourceName
  2402. * @param {!CallFormatter} callFormatter
  2403. */
  2404. CallFormatter.register = function(resourceName, callFormatter)
  2405. {
  2406. CallFormatter._formatters[resourceName] = callFormatter;
  2407. }
  2408. /**
  2409. * @param {!ReplayableCall} replayableCall
  2410. * @return {!Object}
  2411. */
  2412. CallFormatter.formatCall = function(replayableCall)
  2413. {
  2414. var resource = replayableCall.replayableResource();
  2415. var formatter = CallFormatter._formatters[resource.name()];
  2416. if (!formatter) {
  2417. var contextResource = resource.replayableContextResource();
  2418. formatter = CallFormatter._formatters[contextResource.name()] || new CallFormatter();
  2419. }
  2420. return formatter.formatCall(replayableCall);
  2421. }
  2422. CallFormatter.register("CanvasRenderingContext2D", new CallFormatter(CanvasRenderingContext2DResource.DrawingMethods));
  2423. CallFormatter.register("WebGLRenderingContext", new CallFormatter(WebGLRenderingContextResource.DrawingMethods));
  2424. /**
  2425. * @constructor
  2426. */
  2427. function TraceLog()
  2428. {
  2429. /** @type {!Array.<ReplayableCall>} */
  2430. this._replayableCalls = [];
  2431. /** @type {!Cache} */
  2432. this._replayablesCache = new Cache();
  2433. /** @type {!Object.<number, boolean>} */
  2434. this._frameEndCallIndexes = {};
  2435. }
  2436. TraceLog.prototype = {
  2437. /**
  2438. * @return {number}
  2439. */
  2440. size: function()
  2441. {
  2442. return this._replayableCalls.length;
  2443. },
  2444. /**
  2445. * @return {!Array.<ReplayableCall>}
  2446. */
  2447. replayableCalls: function()
  2448. {
  2449. return this._replayableCalls;
  2450. },
  2451. /**
  2452. * @param {number} id
  2453. * @return {ReplayableResource}
  2454. */
  2455. replayableResource: function(id)
  2456. {
  2457. return /** @type {ReplayableResource} */ (this._replayablesCache.get(id));
  2458. },
  2459. /**
  2460. * @param {!Resource} resource
  2461. */
  2462. captureResource: function(resource)
  2463. {
  2464. resource.toReplayable(this._replayablesCache);
  2465. },
  2466. /**
  2467. * @param {!Call} call
  2468. */
  2469. addCall: function(call)
  2470. {
  2471. this._replayableCalls.push(call.toReplayable(this._replayablesCache));
  2472. },
  2473. addFrameEndMark: function()
  2474. {
  2475. var index = this._replayableCalls.length - 1;
  2476. if (index >= 0)
  2477. this._frameEndCallIndexes[index] = true;
  2478. },
  2479. /**
  2480. * @param {number} index
  2481. * @return {boolean}
  2482. */
  2483. isFrameEndCallAt: function(index)
  2484. {
  2485. return !!this._frameEndCallIndexes[index];
  2486. }
  2487. }
  2488. /**
  2489. * @constructor
  2490. * @param {!TraceLog} traceLog
  2491. */
  2492. function TraceLogPlayer(traceLog)
  2493. {
  2494. /** @type {!TraceLog} */
  2495. this._traceLog = traceLog;
  2496. /** @type {number} */
  2497. this._nextReplayStep = 0;
  2498. /** @type {!Cache} */
  2499. this._replayWorldCache = new Cache();
  2500. }
  2501. TraceLogPlayer.prototype = {
  2502. /**
  2503. * @return {!TraceLog}
  2504. */
  2505. traceLog: function()
  2506. {
  2507. return this._traceLog;
  2508. },
  2509. /**
  2510. * @param {number} id
  2511. * @return {Resource}
  2512. */
  2513. replayWorldResource: function(id)
  2514. {
  2515. return /** @type {Resource} */ (this._replayWorldCache.get(id));
  2516. },
  2517. /**
  2518. * @return {number}
  2519. */
  2520. nextReplayStep: function()
  2521. {
  2522. return this._nextReplayStep;
  2523. },
  2524. reset: function()
  2525. {
  2526. this._nextReplayStep = 0;
  2527. this._replayWorldCache.reset();
  2528. },
  2529. /**
  2530. * @return {Call}
  2531. */
  2532. step: function()
  2533. {
  2534. return this.stepTo(this._nextReplayStep);
  2535. },
  2536. /**
  2537. * @param {number} stepNum
  2538. * @return {Call}
  2539. */
  2540. stepTo: function(stepNum)
  2541. {
  2542. stepNum = Math.min(stepNum, this._traceLog.size() - 1);
  2543. console.assert(stepNum >= 0);
  2544. if (this._nextReplayStep > stepNum)
  2545. this.reset();
  2546. // FIXME: Replay all the cached resources first to warm-up.
  2547. var lastCall = null;
  2548. var replayableCalls = this._traceLog.replayableCalls();
  2549. while (this._nextReplayStep <= stepNum)
  2550. lastCall = replayableCalls[this._nextReplayStep++].replay(this._replayWorldCache);
  2551. return lastCall;
  2552. },
  2553. /**
  2554. * @return {Call}
  2555. */
  2556. replay: function()
  2557. {
  2558. return this.stepTo(this._traceLog.size() - 1);
  2559. }
  2560. }
  2561. /**
  2562. * @constructor
  2563. */
  2564. function ResourceTrackingManager()
  2565. {
  2566. this._capturing = false;
  2567. this._stopCapturingOnFrameEnd = false;
  2568. this._lastTraceLog = null;
  2569. }
  2570. ResourceTrackingManager.prototype = {
  2571. /**
  2572. * @return {boolean}
  2573. */
  2574. capturing: function()
  2575. {
  2576. return this._capturing;
  2577. },
  2578. /**
  2579. * @return {TraceLog}
  2580. */
  2581. lastTraceLog: function()
  2582. {
  2583. return this._lastTraceLog;
  2584. },
  2585. /**
  2586. * @param {!Resource} resource
  2587. */
  2588. registerResource: function(resource)
  2589. {
  2590. resource.setManager(this);
  2591. },
  2592. startCapturing: function()
  2593. {
  2594. if (!this._capturing)
  2595. this._lastTraceLog = new TraceLog();
  2596. this._capturing = true;
  2597. this._stopCapturingOnFrameEnd = false;
  2598. },
  2599. /**
  2600. * @param {TraceLog=} traceLog
  2601. */
  2602. stopCapturing: function(traceLog)
  2603. {
  2604. if (traceLog && this._lastTraceLog !== traceLog)
  2605. return;
  2606. this._capturing = false;
  2607. this._stopCapturingOnFrameEnd = false;
  2608. if (this._lastTraceLog)
  2609. this._lastTraceLog.addFrameEndMark();
  2610. },
  2611. /**
  2612. * @param {!TraceLog} traceLog
  2613. */
  2614. dropTraceLog: function(traceLog)
  2615. {
  2616. this.stopCapturing(traceLog);
  2617. if (this._lastTraceLog === traceLog)
  2618. this._lastTraceLog = null;
  2619. },
  2620. captureFrame: function()
  2621. {
  2622. this._lastTraceLog = new TraceLog();
  2623. this._capturing = true;
  2624. this._stopCapturingOnFrameEnd = true;
  2625. },
  2626. /**
  2627. * @param {!Resource} resource
  2628. * @param {Array|Arguments} args
  2629. */
  2630. captureArguments: function(resource, args)
  2631. {
  2632. if (!this._capturing)
  2633. return;
  2634. this._lastTraceLog.captureResource(resource);
  2635. for (var i = 0, n = args.length; i < n; ++i) {
  2636. var res = Resource.forObject(args[i]);
  2637. if (res)
  2638. this._lastTraceLog.captureResource(res);
  2639. }
  2640. },
  2641. /**
  2642. * @param {!Call} call
  2643. */
  2644. captureCall: function(call)
  2645. {
  2646. if (!this._capturing)
  2647. return;
  2648. this._lastTraceLog.addCall(call);
  2649. },
  2650. markFrameEnd: function()
  2651. {
  2652. if (!this._lastTraceLog)
  2653. return;
  2654. this._lastTraceLog.addFrameEndMark();
  2655. if (this._stopCapturingOnFrameEnd && this._lastTraceLog.size())
  2656. this.stopCapturing(this._lastTraceLog);
  2657. }
  2658. }
  2659. /**
  2660. * @constructor
  2661. */
  2662. var InjectedCanvasModule = function()
  2663. {
  2664. /** @type {!ResourceTrackingManager} */
  2665. this._manager = new ResourceTrackingManager();
  2666. /** @type {number} */
  2667. this._lastTraceLogId = 0;
  2668. /** @type {!Object.<string, TraceLog>} */
  2669. this._traceLogs = {};
  2670. /** @type {!Object.<string, TraceLogPlayer>} */
  2671. this._traceLogPlayers = {};
  2672. }
  2673. InjectedCanvasModule.prototype = {
  2674. /**
  2675. * @param {!WebGLRenderingContext} glContext
  2676. * @return {Object}
  2677. */
  2678. wrapWebGLContext: function(glContext)
  2679. {
  2680. var resource = Resource.forObject(glContext) || new WebGLRenderingContextResource(glContext);
  2681. this._manager.registerResource(resource);
  2682. return resource.proxyObject();
  2683. },
  2684. /**
  2685. * @param {!CanvasRenderingContext2D} context
  2686. * @return {Object}
  2687. */
  2688. wrapCanvas2DContext: function(context)
  2689. {
  2690. var resource = Resource.forObject(context) || new CanvasRenderingContext2DResource(context);
  2691. this._manager.registerResource(resource);
  2692. return resource.proxyObject();
  2693. },
  2694. /**
  2695. * @return {CanvasAgent.TraceLogId}
  2696. */
  2697. captureFrame: function()
  2698. {
  2699. return this._callStartCapturingFunction(this._manager.captureFrame);
  2700. },
  2701. /**
  2702. * @return {CanvasAgent.TraceLogId}
  2703. */
  2704. startCapturing: function()
  2705. {
  2706. return this._callStartCapturingFunction(this._manager.startCapturing);
  2707. },
  2708. markFrameEnd: function()
  2709. {
  2710. this._manager.markFrameEnd();
  2711. },
  2712. /**
  2713. * @param {function(this:ResourceTrackingManager)} func
  2714. * @return {CanvasAgent.TraceLogId}
  2715. */
  2716. _callStartCapturingFunction: function(func)
  2717. {
  2718. var oldTraceLog = this._manager.lastTraceLog();
  2719. func.call(this._manager);
  2720. var traceLog = this._manager.lastTraceLog();
  2721. if (traceLog === oldTraceLog) {
  2722. for (var id in this._traceLogs) {
  2723. if (this._traceLogs[id] === traceLog)
  2724. return id;
  2725. }
  2726. }
  2727. var id = this._makeTraceLogId();
  2728. this._traceLogs[id] = traceLog;
  2729. return id;
  2730. },
  2731. /**
  2732. * @param {CanvasAgent.TraceLogId} id
  2733. */
  2734. stopCapturing: function(id)
  2735. {
  2736. var traceLog = this._traceLogs[id];
  2737. if (traceLog)
  2738. this._manager.stopCapturing(traceLog);
  2739. },
  2740. /**
  2741. * @param {CanvasAgent.TraceLogId} id
  2742. */
  2743. dropTraceLog: function(id)
  2744. {
  2745. var traceLog = this._traceLogs[id];
  2746. if (traceLog)
  2747. this._manager.dropTraceLog(traceLog);
  2748. delete this._traceLogs[id];
  2749. delete this._traceLogPlayers[id];
  2750. },
  2751. /**
  2752. * @param {CanvasAgent.TraceLogId} id
  2753. * @param {number=} startOffset
  2754. * @param {number=} maxLength
  2755. * @return {!CanvasAgent.TraceLog|string}
  2756. */
  2757. traceLog: function(id, startOffset, maxLength)
  2758. {
  2759. var traceLog = this._traceLogs[id];
  2760. if (!traceLog)
  2761. return "Error: Trace log with the given ID not found.";
  2762. // Ensure last call ends a frame.
  2763. traceLog.addFrameEndMark();
  2764. var replayableCalls = traceLog.replayableCalls();
  2765. if (typeof startOffset !== "number")
  2766. startOffset = 0;
  2767. if (typeof maxLength !== "number")
  2768. maxLength = replayableCalls.length;
  2769. var fromIndex = Math.max(0, startOffset);
  2770. var toIndex = Math.min(replayableCalls.length - 1, fromIndex + maxLength - 1);
  2771. var alive = this._manager.capturing() && this._manager.lastTraceLog() === traceLog;
  2772. var result = {
  2773. id: id,
  2774. /** @type {Array.<CanvasAgent.Call>} */
  2775. calls: [],
  2776. alive: alive,
  2777. startOffset: fromIndex,
  2778. totalAvailableCalls: replayableCalls.length
  2779. };
  2780. for (var i = fromIndex; i <= toIndex; ++i) {
  2781. var call = replayableCalls[i];
  2782. var contextResource = call.replayableResource().replayableContextResource();
  2783. var stackTrace = call.stackTrace();
  2784. var callFrame = stackTrace ? stackTrace.callFrame(0) || {} : {};
  2785. var item = CallFormatter.formatCall(call);
  2786. item.contextId = this._makeStringResourceId(contextResource.id());
  2787. item.sourceURL = callFrame.sourceURL;
  2788. item.lineNumber = callFrame.lineNumber;
  2789. item.columnNumber = callFrame.columnNumber;
  2790. item.isFrameEndCall = traceLog.isFrameEndCallAt(i);
  2791. result.calls.push(item);
  2792. }
  2793. return result;
  2794. },
  2795. /**
  2796. * @param {*} obj
  2797. * @return {!CanvasAgent.CallArgument}
  2798. */
  2799. _makeCallArgument: function(obj)
  2800. {
  2801. if (obj instanceof ReplayableResource)
  2802. var description = obj.description();
  2803. else
  2804. var description = "" + obj;
  2805. return { description: description };
  2806. },
  2807. /**
  2808. * @param {CanvasAgent.TraceLogId} traceLogId
  2809. * @param {number} stepNo
  2810. * @return {!CanvasAgent.ResourceState|string}
  2811. */
  2812. replayTraceLog: function(traceLogId, stepNo)
  2813. {
  2814. var traceLog = this._traceLogs[traceLogId];
  2815. if (!traceLog)
  2816. return "Error: Trace log with the given ID not found.";
  2817. this._traceLogPlayers[traceLogId] = this._traceLogPlayers[traceLogId] || new TraceLogPlayer(traceLog);
  2818. var lastCall = this._traceLogPlayers[traceLogId].stepTo(stepNo);
  2819. var resource = lastCall.resource();
  2820. var dataURL = resource.toDataURL();
  2821. if (!dataURL) {
  2822. resource = resource.contextResource();
  2823. dataURL = resource.toDataURL();
  2824. }
  2825. return this._makeResourceState(this._makeStringResourceId(resource.id()), traceLogId, dataURL);
  2826. },
  2827. /**
  2828. * @param {CanvasAgent.ResourceId} stringResourceId
  2829. * @return {!CanvasAgent.ResourceInfo|string}
  2830. */
  2831. resourceInfo: function(stringResourceId)
  2832. {
  2833. var resourceId = this._parseStringId(stringResourceId).resourceId;
  2834. if (!resourceId)
  2835. return "Error: Wrong resource ID: " + stringResourceId;
  2836. var replayableResource = null;
  2837. for (var id in this._traceLogs) {
  2838. replayableResource = this._traceLogs[id].replayableResource(resourceId);
  2839. if (replayableResource)
  2840. break;
  2841. }
  2842. if (!replayableResource)
  2843. return "Error: Resource with the given ID not found.";
  2844. return this._makeResourceInfo(stringResourceId, replayableResource.description());
  2845. },
  2846. /**
  2847. * @param {CanvasAgent.TraceLogId} traceLogId
  2848. * @param {CanvasAgent.ResourceId} stringResourceId
  2849. * @return {!CanvasAgent.ResourceState|string}
  2850. */
  2851. resourceState: function(traceLogId, stringResourceId)
  2852. {
  2853. var traceLog = this._traceLogs[traceLogId];
  2854. if (!traceLog)
  2855. return "Error: Trace log with the given ID not found.";
  2856. var traceLogPlayer = this._traceLogPlayers[traceLogId];
  2857. if (!traceLogPlayer)
  2858. return "Error: Trace log replay has not started yet.";
  2859. var parsedStringId1 = this._parseStringId(traceLogId);
  2860. var parsedStringId2 = this._parseStringId(stringResourceId);
  2861. if (parsedStringId1.injectedScriptId !== parsedStringId2.injectedScriptId)
  2862. return "Error: Both IDs must point to the same injected script.";
  2863. var resourceId = parsedStringId2.resourceId;
  2864. if (!resourceId)
  2865. return "Error: Wrong resource ID: " + stringResourceId;
  2866. var resource = traceLogPlayer.replayWorldResource(resourceId);
  2867. if (!resource)
  2868. return "Error: Resource with the given ID has not been replayed yet.";
  2869. return this._makeResourceState(stringResourceId, traceLogId, resource.toDataURL());
  2870. },
  2871. /**
  2872. * @return {CanvasAgent.TraceLogId}
  2873. */
  2874. _makeTraceLogId: function()
  2875. {
  2876. return "{\"injectedScriptId\":" + injectedScriptId + ",\"traceLogId\":" + (++this._lastTraceLogId) + "}";
  2877. },
  2878. /**
  2879. * @param {number} resourceId
  2880. * @return {CanvasAgent.ResourceId}
  2881. */
  2882. _makeStringResourceId: function(resourceId)
  2883. {
  2884. return "{\"injectedScriptId\":" + injectedScriptId + ",\"resourceId\":" + resourceId + "}";
  2885. },
  2886. /**
  2887. * @param {CanvasAgent.ResourceId} stringResourceId
  2888. * @param {string} description
  2889. * @return {!CanvasAgent.ResourceInfo}
  2890. */
  2891. _makeResourceInfo: function(stringResourceId, description)
  2892. {
  2893. return {
  2894. id: stringResourceId,
  2895. description: description
  2896. };
  2897. },
  2898. /**
  2899. * @param {CanvasAgent.ResourceId} stringResourceId
  2900. * @param {CanvasAgent.TraceLogId} traceLogId
  2901. * @param {string} imageURL
  2902. * @return {!CanvasAgent.ResourceState}
  2903. */
  2904. _makeResourceState: function(stringResourceId, traceLogId, imageURL)
  2905. {
  2906. return {
  2907. id: stringResourceId,
  2908. traceLogId: traceLogId,
  2909. imageURL: imageURL
  2910. };
  2911. },
  2912. /**
  2913. * @param {string} stringId
  2914. * @return {{injectedScriptId: number, traceLogId: ?number, resourceId: ?number}}
  2915. */
  2916. _parseStringId: function(stringId)
  2917. {
  2918. return InjectedScriptHost.evaluate("(" + stringId + ")");
  2919. }
  2920. }
  2921. var injectedCanvasModule = new InjectedCanvasModule();
  2922. return injectedCanvasModule;
  2923. })