Bug 1226245 - Make CallWatcher generic, r=jsantell

This commit is contained in:
Victor Porof 2016-01-20 14:11:14 +01:00
parent ed87fa4de9
commit 9e259bd7f9
8 changed files with 277 additions and 155 deletions

View File

@ -81,8 +81,6 @@ var CallsListView = Heritage.extend(WidgetMethods, {
gutter.appendChild(index);
contents.appendChild(gutter);
// Not all function calls have a caller that was stringified (e.g.
// context calls have a "gl" or "ctx" caller preview).
if (call.callerPreview) {
let context = document.createElement("label");
context.className = "plain call-item-context";

View File

@ -40,7 +40,7 @@ function* ifTestingSupported() {
is(functionCalls[0].line, 25,
"The called function's line is correct.");
is(functionCalls[0].callerPreview, "ctx",
is(functionCalls[0].callerPreview, "Object",
"The called function's caller preview is correct.");
is(functionCalls[0].argsPreview, "0, 0, 128, 128",
"The called function's args preview is correct.");

View File

@ -41,7 +41,7 @@ function* ifTestingSupported() {
"The first called function's line is correct.");
is(functionCalls[0].argsPreview, "0, 0, 128, 128",
"The first called function's args preview is correct.");
is(functionCalls[0].callerPreview, "ctx",
is(functionCalls[0].callerPreview, "Object",
"The first called function's caller preview is correct.");
is(functionCalls[6].type, CallWatcherFront.METHOD_FUNCTION,
@ -54,7 +54,7 @@ function* ifTestingSupported() {
"The penultimate called function's line is correct.");
is(functionCalls[6].argsPreview, "10, 10, 55, 50",
"The penultimate called function's args preview is correct.");
is(functionCalls[6].callerPreview, "ctx",
is(functionCalls[6].callerPreview, "Object",
"The penultimate called function's caller preview is correct.");
is(functionCalls[7].type, CallWatcherFront.METHOD_FUNCTION,
@ -67,7 +67,7 @@ function* ifTestingSupported() {
"The last called function's line is correct.");
ok(functionCalls[7].argsPreview.includes("Function"),
"The last called function's args preview is correct.");
is(functionCalls[7].callerPreview, "",
is(functionCalls[7].callerPreview, "Object",
"The last called function's caller preview is correct.");
yield removeTab(target.tab);

View File

@ -41,7 +41,7 @@ function* ifTestingSupported() {
"The first called function's line is correct.");
is(functionCalls[0].argsPreview, "0, 0, 128, 128",
"The first called function's args preview is correct.");
is(functionCalls[0].callerPreview, "ctx",
is(functionCalls[0].callerPreview, "Object",
"The first called function's caller preview is correct.");
is(functionCalls[6].type, CallWatcherFront.METHOD_FUNCTION,
@ -54,7 +54,7 @@ function* ifTestingSupported() {
"The penultimate called function's line is correct.");
is(functionCalls[6].argsPreview, "10, 10, 55, 50",
"The penultimate called function's args preview is correct.");
is(functionCalls[6].callerPreview, "ctx",
is(functionCalls[6].callerPreview, "Object",
"The penultimate called function's caller preview is correct.");
is(functionCalls[7].type, CallWatcherFront.METHOD_FUNCTION,
@ -67,7 +67,7 @@ function* ifTestingSupported() {
"The last called function's line is correct.");
ok(functionCalls[7].argsPreview.includes("Function"),
"The last called function's args preview is correct.");
is(functionCalls[7].callerPreview, "",
is(functionCalls[7].callerPreview, "Object",
"The last called function's caller preview is correct.");
let firstNonDrawCall = yield functionCalls[1].getDetails();

View File

@ -21,22 +21,22 @@ function* ifTestingSupported() {
"All the function calls should now be displayed in the UI.");
testItem(CallsListView.getItemAtIndex(0),
"1", "ctx", "clearRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:25");
"1", "Object", "clearRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:25");
testItem(CallsListView.getItemAtIndex(1),
"2", "ctx", "fillStyle", " = rgb(192, 192, 192)", "doc_simple-canvas.html:20");
"2", "Object", "fillStyle", " = rgb(192, 192, 192)", "doc_simple-canvas.html:20");
testItem(CallsListView.getItemAtIndex(2),
"3", "ctx", "fillRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:21");
"3", "Object", "fillRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:21");
testItem(CallsListView.getItemAtIndex(3),
"4", "ctx", "fillStyle", " = rgba(0, 0, 192, 0.5)", "doc_simple-canvas.html:20");
"4", "Object", "fillStyle", " = rgba(0, 0, 192, 0.5)", "doc_simple-canvas.html:20");
testItem(CallsListView.getItemAtIndex(4),
"5", "ctx", "fillRect", "(30, 30, 55, 50)", "doc_simple-canvas.html:21");
"5", "Object", "fillRect", "(30, 30, 55, 50)", "doc_simple-canvas.html:21");
testItem(CallsListView.getItemAtIndex(5),
"6", "ctx", "fillStyle", " = rgba(192, 0, 0, 0.5)", "doc_simple-canvas.html:20");
"6", "Object", "fillStyle", " = rgba(192, 0, 0, 0.5)", "doc_simple-canvas.html:20");
testItem(CallsListView.getItemAtIndex(6),
"7", "ctx", "fillRect", "(10, 10, 55, 50)", "doc_simple-canvas.html:21");
"7", "Object", "fillRect", "(10, 10, 55, 50)", "doc_simple-canvas.html:21");
testItem(CallsListView.getItemAtIndex(7),
"8", "", "requestAnimationFrame", "(Function)", "doc_simple-canvas.html:30");
@ -53,7 +53,7 @@ function* ifTestingSupported() {
is($(".call-item-context", item.target).getAttribute("value"), context,
"The item's context label has the correct text.");
} else {
is($(".call-item-context", item.target), null,
is($(".call-item-context", item.target) + "", "[object XULElement]",
"The item's context label should not be available.");
}

View File

@ -40,7 +40,7 @@ function* ifTestingSupported() {
"The visible item's line has the expected value.");
is(CallsListView.visibleItems[0].attachment.actor.argsPreview, "0, 0, 128, 128",
"The visible item's args have the expected value.");
is(CallsListView.visibleItems[0].attachment.actor.callerPreview, "ctx",
is(CallsListView.visibleItems[0].attachment.actor.callerPreview, "Object",
"The visible item's caller has the expected value.");
let secondRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);

View File

@ -56,7 +56,7 @@ var FunctionCallActor = protocol.ActorClass({
* @param array stack
* The called function's stack, as a list of { name, file, line } objects.
* @param number timestamp
* The timestamp of draw-related functions
* The performance.now() timestamp when the function was called.
* @param array args
* The called function's arguments.
* @param any result
@ -69,6 +69,7 @@ var FunctionCallActor = protocol.ActorClass({
protocol.Actor.prototype.initialize.call(this, conn);
this.details = {
global: global,
type: type,
name: name,
stack: stack,
@ -77,49 +78,39 @@ var FunctionCallActor = protocol.ActorClass({
// Store a weak reference to all objects so we don't
// prevent natural GC if `holdWeak` was passed into
// setup as truthy. Used in the Web Audio Editor.
// setup as truthy.
if (holdWeak) {
let weakRefs = {
window: Cu.getWeakReference(window),
caller: Cu.getWeakReference(caller),
args: Cu.getWeakReference(args),
result: Cu.getWeakReference(result),
args: Cu.getWeakReference(args)
};
Object.defineProperties(this.details, {
window: { get: () => weakRefs.window.get() },
caller: { get: () => weakRefs.caller.get() },
result: { get: () => weakRefs.result.get() },
args: { get: () => weakRefs.args.get() },
timestamp: { get: () => weakRefs.timestamp.get() },
result: { get: () => weakRefs.result.get() },
});
}
// Otherwise, hold strong references to the objects.
else {
this.details.window = window;
this.details.caller = caller;
this.details.result = result;
this.details.args = args;
this.details.timestamp = timestamp;
this.details.result = result;
}
this.meta = {
global: -1,
previews: { caller: "", args: "" }
// The caller, args and results are string names for now. It would
// certainly be nicer if they were Object actors. Make this smarter, so
// that the frontend can inspect each argument, be it object or primitive.
// Bug 978960.
this.details.previews = {
caller: this._generateStringPreview(caller),
args: this._generateArgsPreview(args),
result: this._generateStringPreview(result)
};
if (global == "WebGLRenderingContext") {
this.meta.global = CallWatcherFront.CANVAS_WEBGL_CONTEXT;
} else if (global == "CanvasRenderingContext2D") {
this.meta.global = CallWatcherFront.CANVAS_2D_CONTEXT;
} else if (global == "window") {
this.meta.global = CallWatcherFront.UNKNOWN_SCOPE;
} else {
this.meta.global = CallWatcherFront.GLOBAL_SCOPE;
}
this.meta.previews.caller = this._generateCallerPreview();
this.meta.previews.args = this._generateArgsPreview();
},
/**
@ -134,8 +125,9 @@ var FunctionCallActor = protocol.ActorClass({
file: this.details.stack[0].file,
line: this.details.stack[0].line,
timestamp: this.details.timestamp,
callerPreview: this.meta.previews.caller,
argsPreview: this.meta.previews.args
callerPreview: this.details.previews.caller,
argsPreview: this.details.previews.args,
resultPreview: this.details.previews.result
};
},
@ -169,45 +161,37 @@ var FunctionCallActor = protocol.ActorClass({
response: { info: RetVal("call-details") }
}),
/**
* Serializes the caller's name so that it can be easily be transferred
* as a string, but still be useful when displayed in a potential UI.
*
* @return string
* The caller's name as a string.
*/
_generateCallerPreview: function() {
let global = this.meta.global;
if (global == CallWatcherFront.CANVAS_WEBGL_CONTEXT) {
return "gl";
}
if (global == CallWatcherFront.CANVAS_2D_CONTEXT) {
return "ctx";
}
return "";
},
/**
* Serializes the arguments so that they can be easily be transferred
* as a string, but still be useful when displayed in a potential UI.
*
* @param array args
* The source arguments.
* @return string
* The arguments as a string.
*/
_generateArgsPreview: function() {
let { caller, args, name } = this.details;
let { global } = this.meta;
_generateArgsPreview: function(args) {
let { global, name, caller } = this.details;
// Get method signature to determine if there are any enums
// used in this method.
let enumArgs = (CallWatcherFront.ENUM_METHODS[global] || {})[name];
if (typeof enumArgs === "function") {
enumArgs = enumArgs(args);
let methodSignatureEnums;
let knownGlobal = CallWatcherFront.KNOWN_METHODS[global];
if (knownGlobal) {
let knownMethod = knownGlobal[name];
if (knownMethod) {
let isOverloaded = typeof knownMethod.enums === "function";
if (isOverloaded) {
methodSignatureEnums = methodSignatureEnums(args);
} else {
methodSignatureEnums = knownMethod.enums;
}
}
}
// XXX: All of this sucks. Make this smarter, so that the frontend
// can inspect each argument, be it object or primitive. Bug 978960.
let serializeArgs = () => args.map((arg, i) => {
// XXX: Bug 978960.
if (arg === undefined) {
return "undefined";
}
@ -222,13 +206,39 @@ var FunctionCallActor = protocol.ActorClass({
}
// If this argument matches the method's signature
// and is an enum, change it to its constant name.
if (enumArgs && enumArgs.indexOf(i) !== -1) {
if (methodSignatureEnums && methodSignatureEnums.has(i)) {
return getBitToEnumValue(global, caller, arg);
}
return arg;
return arg + "";
});
return serializeArgs().join(", ");
},
/**
* Serializes the data so that it can be easily be transferred
* as a string, but still be useful when displayed in a potential UI.
*
* @param object data
* The source data.
* @return string
* The arguments as a string.
*/
_generateStringPreview: function(data) {
// XXX: Bug 978960.
if (data === undefined) {
return "undefined";
}
if (data === null) {
return "null";
}
if (typeof data == "function") {
return "Function";
}
if (typeof data == "object") {
return "Object";
}
return data + "";
}
});
@ -253,6 +263,7 @@ var FunctionCallFront = protocol.FrontClass(FunctionCallActor, {
this.timestamp = form.timestamp;
this.callerPreview = form.callerPreview;
this.argsPreview = form.argsPreview;
this.resultPreview = form.resultPreview;
}
});
@ -283,6 +294,7 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
return;
}
this._initialized = true;
this._timestampEpoch = 0;
this._functionCalls = [];
this._tracedGlobals = tracedGlobals || [];
@ -342,10 +354,10 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
}),
/**
* Initialize frame start timestamp for measuring
* Initialize the timestamp epoch used to offset function call timestamps.
*/
initFrameStartTimestamp: method(function() {
this._frameStartTimestamp = this.tabActor.window.performance.now();
initTimestampEpoch: method(function() {
this._timestampEpoch = this.tabActor.window.performance.now();
}),
/**
@ -378,18 +390,18 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
* while recording. We're doing this to avoid the event emitter overhead,
* since this is expected to be a very hot function.
*/
onCall: function() {},
onCall: null,
/**
* Invoked whenever the current tab actor's document global is created.
*/
_onGlobalCreated: function({window, id, isTopLevel}) {
let self = this;
// TODO: bug 981748, support more than just the top-level documents.
if (!isTopLevel) {
return;
}
let self = this;
this._tracedWindowId = id;
let unwrappedWindow = XPCNativeWrapper.unwrap(window);
@ -439,9 +451,9 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
}
if (self._recording) {
let timestamp = self.tabActor.window.performance.now() - self._frameStartTimestamp;
let stack = getStack(name);
let type = CallWatcherFront.METHOD_FUNCTION;
let stack = getStack(name);
let timestamp = self.tabActor.window.performance.now() - self._timestampEpoch;
callback(unwrappedWindow, global, this, type, name, stack, timestamp, args, result);
}
return result;
@ -469,9 +481,9 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
let result = Cu.waiveXrays(originalGetter.apply(this, args));
if (self._recording) {
let timestamp = self.tabActor.window.performance.now() - self._frameStartTimestamp;
let stack = getStack(name);
let type = CallWatcherFront.GETTER_FUNCTION;
let stack = getStack(name);
let timestamp = self.tabActor.window.performance.now() - self._timestampEpoch;
callback(unwrappedWindow, global, this, type, name, stack, timestamp, args, result);
}
return result;
@ -481,9 +493,9 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
originalSetter.apply(this, args);
if (self._recording) {
let timestamp = self.tabActor.window.performance.now() - self._frameStartTimestamp;
let stack = getStack(name);
let type = CallWatcherFront.SETTER_FUNCTION;
let stack = getStack(name);
let timestamp = self.tabActor.window.performance.now() - self._timestampEpoch;
callback(unwrappedWindow, global, this, type, name, stack, timestamp, args, undefined);
}
},
@ -554,6 +566,7 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
if (this._tracedWindowId == id) {
this.pauseRecording();
this.eraseRecording();
this._timestampEpoch = 0;
}
},
@ -566,13 +579,18 @@ var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
if (this._finalized) {
return;
}
let functionCall = new FunctionCallActor(this.conn, details, this._holdWeak);
if (this._storeCalls) {
this._functionCalls.push(functionCall);
}
this.onCall(functionCall);
if (this.onCall) {
this.onCall(functionCall);
} else {
emit(this, "call", functionCall);
}
}
});
@ -593,71 +611,177 @@ CallWatcherFront.METHOD_FUNCTION = 0;
CallWatcherFront.GETTER_FUNCTION = 1;
CallWatcherFront.SETTER_FUNCTION = 2;
CallWatcherFront.GLOBAL_SCOPE = 0;
CallWatcherFront.UNKNOWN_SCOPE = 1;
CallWatcherFront.CANVAS_WEBGL_CONTEXT = 2;
CallWatcherFront.CANVAS_2D_CONTEXT = 3;
CallWatcherFront.KNOWN_METHODS = {};
CallWatcherFront.ENUM_METHODS = {};
CallWatcherFront.ENUM_METHODS[CallWatcherFront.CANVAS_2D_CONTEXT] = {
asyncDrawXULElement: [6],
drawWindow: [6]
CallWatcherFront.KNOWN_METHODS["CanvasRenderingContext2D"] = {
asyncDrawXULElement: {
enums: new Set([6]),
},
drawWindow: {
enums: new Set([6])
},
};
CallWatcherFront.ENUM_METHODS[CallWatcherFront.CANVAS_WEBGL_CONTEXT] = {
activeTexture: [0],
bindBuffer: [0],
bindFramebuffer: [0],
bindRenderbuffer: [0],
bindTexture: [0],
blendEquation: [0],
blendEquationSeparate: [0, 1],
blendFunc: [0, 1],
blendFuncSeparate: [0, 1, 2, 3],
bufferData: [0, 1, 2],
bufferSubData: [0, 1],
checkFramebufferStatus: [0],
clear: [0],
compressedTexImage2D: [0, 2],
compressedTexSubImage2D: [0, 6],
copyTexImage2D: [0, 2],
copyTexSubImage2D: [0],
createShader: [0],
cullFace: [0],
depthFunc: [0],
disable: [0],
drawArrays: [0],
drawElements: [0, 2],
enable: [0],
framebufferRenderbuffer: [0, 1, 2],
framebufferTexture2D: [0, 1, 2],
frontFace: [0],
generateMipmap: [0],
getBufferParameter: [0, 1],
getParameter: [0],
getFramebufferAttachmentParameter: [0, 1, 2],
getProgramParameter: [1],
getRenderbufferParameter: [0, 1],
getShaderParameter: [1],
getShaderPrecisionFormat: [0, 1],
getTexParameter: [0, 1],
getVertexAttrib: [1],
getVertexAttribOffset: [1],
hint: [0, 1],
isEnabled: [0],
pixelStorei: [0],
readPixels: [4, 5],
renderbufferStorage: [0, 1],
stencilFunc: [0],
stencilFuncSeparate: [0, 1],
stencilMaskSeparate: [0],
stencilOp: [0, 1, 2],
stencilOpSeparate: [0, 1, 2, 3],
texImage2D: (args) => args.length > 6 ? [0, 2, 6, 7] : [0, 2, 3, 4],
texParameterf: [0, 1],
texParameteri: [0, 1, 2],
texSubImage2D: (args) => args.length === 9 ? [0, 6, 7] : [0, 4, 5],
vertexAttribPointer: [2]
CallWatcherFront.KNOWN_METHODS["WebGLRenderingContext"] = {
activeTexture: {
enums: new Set([0]),
},
bindBuffer: {
enums: new Set([0]),
},
bindFramebuffer: {
enums: new Set([0]),
},
bindRenderbuffer: {
enums: new Set([0]),
},
bindTexture: {
enums: new Set([0]),
},
blendEquation: {
enums: new Set([0]),
},
blendEquationSeparate: {
enums: new Set([0, 1]),
},
blendFunc: {
enums: new Set([0, 1]),
},
blendFuncSeparate: {
enums: new Set([0, 1, 2, 3]),
},
bufferData: {
enums: new Set([0, 1, 2]),
},
bufferSubData: {
enums: new Set([0, 1]),
},
checkFramebufferStatus: {
enums: new Set([0]),
},
clear: {
enums: new Set([0]),
},
compressedTexImage2D: {
enums: new Set([0, 2]),
},
compressedTexSubImage2D: {
enums: new Set([0, 6]),
},
copyTexImage2D: {
enums: new Set([0, 2]),
},
copyTexSubImage2D: {
enums: new Set([0]),
},
createShader: {
enums: new Set([0]),
},
cullFace: {
enums: new Set([0]),
},
depthFunc: {
enums: new Set([0]),
},
disable: {
enums: new Set([0]),
},
drawArrays: {
enums: new Set([0]),
},
drawElements: {
enums: new Set([0, 2]),
},
enable: {
enums: new Set([0]),
},
framebufferRenderbuffer: {
enums: new Set([0, 1, 2]),
},
framebufferTexture2D: {
enums: new Set([0, 1, 2]),
},
frontFace: {
enums: new Set([0]),
},
generateMipmap: {
enums: new Set([0]),
},
getBufferParameter: {
enums: new Set([0, 1]),
},
getParameter: {
enums: new Set([0]),
},
getFramebufferAttachmentParameter: {
enums: new Set([0, 1, 2]),
},
getProgramParameter: {
enums: new Set([1]),
},
getRenderbufferParameter: {
enums: new Set([0, 1]),
},
getShaderParameter: {
enums: new Set([1]),
},
getShaderPrecisionFormat: {
enums: new Set([0, 1]),
},
getTexParameter: {
enums: new Set([0, 1]),
},
getVertexAttrib: {
enums: new Set([1]),
},
getVertexAttribOffset: {
enums: new Set([1]),
},
hint: {
enums: new Set([0, 1]),
},
isEnabled: {
enums: new Set([0]),
},
pixelStorei: {
enums: new Set([0]),
},
readPixels: {
enums: new Set([4, 5]),
},
renderbufferStorage: {
enums: new Set([0, 1]),
},
stencilFunc: {
enums: new Set([0]),
},
stencilFuncSeparate: {
enums: new Set([0, 1]),
},
stencilMaskSeparate: {
enums: new Set([0]),
},
stencilOp: {
enums: new Set([0, 1, 2]),
},
stencilOpSeparate: {
enums: new Set([0, 1, 2, 3]),
},
texImage2D: {
enums: args => args.length > 6 ? new Set([0, 2, 6, 7]) : new Set([0, 2, 3, 4]),
},
texParameterf: {
enums: new Set([0, 1]),
},
texParameteri: {
enums: new Set([0, 1, 2]),
},
texSubImage2D: {
enums: args => args.length === 9 ? new Set([0, 6, 7]) : new Set([0, 4, 5]),
},
vertexAttribPointer: {
enums: new Set([2])
},
};
/**

View File

@ -147,7 +147,7 @@ var FrameSnapshotActor = protocol.ActorClass({
*/
generateScreenshotFor: method(function(functionCall) {
let caller = functionCall.details.caller;
let global = functionCall.meta.global;
let global = functionCall.details.global;
let canvas = this._contentCanvas;
let calls = this._functionCalls;
@ -170,10 +170,10 @@ var FrameSnapshotActor = protocol.ActorClass({
// Depending on the canvas' context, generating a screenshot is done
// in different ways.
if (global == CallWatcherFront.CANVAS_WEBGL_CONTEXT) {
if (global == "WebGLRenderingContext") {
screenshot = ContextUtils.getPixelsForWebGL(replayContext, left, top, width, height);
screenshot.flipped = true;
} else if (global == CallWatcherFront.CANVAS_2D_CONTEXT) {
} else if (global == "CanvasRenderingContext2D") {
screenshot = ContextUtils.getPixelsFor2D(replayContext, left, top, width, height);
screenshot.flipped = false;
}
@ -326,7 +326,7 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClass({
this._recordingContainsDrawCall = false;
this._callWatcher.eraseRecording();
this._callWatcher.initFrameStartTimestamp();
this._callWatcher.initTimestampEpoch();
this._webGLPrimitiveCounter.resetCounts();
this._callWatcher.resumeRecording();
@ -463,7 +463,7 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClass({
_handleDrawCall: function(functionCall) {
let functionCalls = this._callWatcher.pauseRecording();
let caller = functionCall.details.caller;
let global = functionCall.meta.global;
let global = functionCall.details.global;
let contentCanvas = this._lastDrawCallCanvas = caller.canvas;
let index = this._lastDrawCallIndex = functionCalls.indexOf(functionCall);
@ -478,7 +478,7 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClass({
// Create a thumbnail on every draw call on the canvas context, to augment
// the respective function call actor with this additional data.
if (global == CallWatcherFront.CANVAS_WEBGL_CONTEXT) {
if (global == "WebGLRenderingContext") {
// Check if drawing to a custom framebuffer (when rendering to texture).
// Don't create a thumbnail in this particular case.
let framebufferBinding = caller.getParameter(caller.FRAMEBUFFER_BINDING);
@ -487,7 +487,7 @@ var CanvasActor = exports.CanvasActor = protocol.ActorClass({
thumbnail.flipped = this._lastThumbnailFlipped = true;
thumbnail.index = index;
}
} else if (global == CallWatcherFront.CANVAS_2D_CONTEXT) {
} else if (global == "CanvasRenderingContext2D") {
thumbnail = ContextUtils.getPixelsFor2D(caller, 0, 0, w, h, dimensions);
thumbnail.flipped = this._lastThumbnailFlipped = false;
thumbnail.index = index;
@ -673,7 +673,7 @@ var ContextUtils = {
// required GL state (like recompiling shaders, setting global flags, etc.)
// in an entirely new canvas. However, special care is needed to not
// permanently affect the existing GL state in the process.
if (contextType == CallWatcherFront.CANVAS_WEBGL_CONTEXT) {
if (contextType == "WebGLRenderingContext") {
// To keep things fast, replay the context calls on a framebuffer
// of smaller dimensions than the actual canvas (maximum 256x256 pixels).
let scaling = Math.min(CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, h) / h;
@ -697,7 +697,7 @@ var ContextUtils = {
};
}
// In case of 2D contexts, draw everything on a separate canvas context.
else if (contextType == CallWatcherFront.CANVAS_2D_CONTEXT) {
else if (contextType == "CanvasRenderingContext2D") {
let contentDocument = canvas.ownerDocument;
let replayCanvas = contentDocument.createElement("canvas");
replayCanvas.width = w;