зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1403938 - remove Canvas Debugger actor from the server; r=vporof
Depends on D14730 Differential Revision: https://phabricator.services.mozilla.com/D14731 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
45147b86ec
Коммит
0d4cd33f2f
|
@ -1,731 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
/* global XPCNativeWrapper */
|
||||
|
||||
const defer = require("devtools/shared/defer");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const {CallWatcher} = require("devtools/server/actors/utils/call-watcher");
|
||||
const {METHOD_FUNCTION, SETTER_FUNCTION} = require("devtools/shared/fronts/function-call");
|
||||
const {WebGLPrimitiveCounter} = require("devtools/server/actors/canvas/primitive");
|
||||
const {
|
||||
frameSnapshotSpec,
|
||||
canvasSpec,
|
||||
CANVAS_CONTEXTS,
|
||||
ANIMATION_GENERATORS,
|
||||
LOOP_GENERATORS,
|
||||
} = require("devtools/shared/specs/canvas");
|
||||
const {CanvasFront} = require("devtools/shared/fronts/canvas");
|
||||
|
||||
/**
|
||||
* This actor represents a recorded animation frame snapshot, along with
|
||||
* all the corresponding canvas' context methods invoked in that frame,
|
||||
* thumbnails for each draw call and a screenshot of the end result.
|
||||
*/
|
||||
var FrameSnapshotActor = protocol.ActorClassWithSpec(frameSnapshotSpec, {
|
||||
/**
|
||||
* Creates the frame snapshot call actor.
|
||||
*
|
||||
* @param DebuggerServerConnection conn
|
||||
* The server connection.
|
||||
* @param HTMLCanvasElement canvas
|
||||
* A reference to the content canvas.
|
||||
* @param array calls
|
||||
* An array of "function-call" actor instances.
|
||||
* @param object screenshot
|
||||
* A single "snapshot-image" type instance.
|
||||
*/
|
||||
initialize: function(conn, { canvas, calls, screenshot, primitive }) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this._contentCanvas = canvas;
|
||||
this._functionCalls = calls;
|
||||
this._animationFrameEndScreenshot = screenshot;
|
||||
this._primitive = primitive;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets as much data about this snapshot without computing anything costly.
|
||||
*/
|
||||
getOverview: function() {
|
||||
return {
|
||||
calls: this._functionCalls,
|
||||
thumbnails: this._functionCalls.map(e => e._thumbnail).filter(e => !!e),
|
||||
screenshot: this._animationFrameEndScreenshot,
|
||||
primitive: {
|
||||
tris: this._primitive.tris,
|
||||
vertices: this._primitive.vertices,
|
||||
points: this._primitive.points,
|
||||
lines: this._primitive.lines,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a screenshot of the canvas's contents after the specified
|
||||
* function was called.
|
||||
*/
|
||||
generateScreenshotFor: function(functionCall) {
|
||||
const global = functionCall.details.global;
|
||||
|
||||
const canvas = this._contentCanvas;
|
||||
const calls = this._functionCalls;
|
||||
const index = calls.indexOf(functionCall);
|
||||
|
||||
// To get a screenshot, replay all the steps necessary to render the frame,
|
||||
// by invoking the context calls up to and including the specified one.
|
||||
// This will be done in a custom framebuffer in case of a WebGL context.
|
||||
const replayData = ContextUtils.replayAnimationFrame({
|
||||
contextType: global,
|
||||
canvas: canvas,
|
||||
calls: calls,
|
||||
first: 0,
|
||||
last: index,
|
||||
});
|
||||
|
||||
const {
|
||||
replayContext,
|
||||
replayContextScaling,
|
||||
lastDrawCallIndex,
|
||||
doCleanup,
|
||||
} = replayData;
|
||||
const [left, top, width, height] = replayData.replayViewport;
|
||||
let screenshot;
|
||||
|
||||
// Depending on the canvas' context, generating a screenshot is done
|
||||
// in different ways.
|
||||
if (global == "WebGLRenderingContext") {
|
||||
screenshot = ContextUtils.getPixelsForWebGL(replayContext, left, top,
|
||||
width, height);
|
||||
screenshot.flipped = true;
|
||||
} else if (global == "CanvasRenderingContext2D") {
|
||||
screenshot = ContextUtils.getPixelsFor2D(replayContext, left, top, width, height);
|
||||
screenshot.flipped = false;
|
||||
}
|
||||
|
||||
// In case of the WebGL context, we also need to reset the framebuffer
|
||||
// binding to the original value, after generating the screenshot.
|
||||
doCleanup();
|
||||
|
||||
screenshot.scaling = replayContextScaling;
|
||||
screenshot.index = lastDrawCallIndex;
|
||||
return screenshot;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* This Canvas Actor handles simple instrumentation of all the methods
|
||||
* of a 2D or WebGL context, to provide information regarding all the calls
|
||||
* made when drawing frame inside an animation loop.
|
||||
*/
|
||||
exports.CanvasActor = protocol.ActorClassWithSpec(canvasSpec, {
|
||||
// Reset for each recording, boolean indicating whether or not
|
||||
// any draw calls were called for a recording.
|
||||
_animationContainsDrawCall: false,
|
||||
|
||||
initialize: function(conn, targetActor) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.targetActor = targetActor;
|
||||
this._webGLPrimitiveCounter = new WebGLPrimitiveCounter(targetActor);
|
||||
this._onContentFunctionCall = this._onContentFunctionCall.bind(this);
|
||||
},
|
||||
destroy: function(conn) {
|
||||
protocol.Actor.prototype.destroy.call(this, conn);
|
||||
this._webGLPrimitiveCounter.destroy();
|
||||
this.finalize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts listening for function calls.
|
||||
*/
|
||||
setup: function({ reload }) {
|
||||
if (this._initialized) {
|
||||
if (reload) {
|
||||
this.targetActor.window.location.reload();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
|
||||
this._callWatcher = new CallWatcher(this.conn, this.targetActor);
|
||||
this._callWatcher.onCall = this._onContentFunctionCall;
|
||||
this._callWatcher.setup({
|
||||
tracedGlobals: CANVAS_CONTEXTS,
|
||||
tracedFunctions: [...ANIMATION_GENERATORS, ...LOOP_GENERATORS],
|
||||
performReload: reload,
|
||||
storeCalls: true,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops listening for function calls.
|
||||
*/
|
||||
finalize: function() {
|
||||
if (!this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = false;
|
||||
|
||||
this._callWatcher.finalize();
|
||||
this._callWatcher = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether this actor has been set up.
|
||||
*/
|
||||
isInitialized: function() {
|
||||
return !!this._initialized;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether or not the CanvasActor is recording an animation.
|
||||
* Used in tests.
|
||||
*/
|
||||
isRecording: function() {
|
||||
return !!this._callWatcher.isRecording();
|
||||
},
|
||||
|
||||
/**
|
||||
* Records a snapshot of all the calls made during the next animation frame.
|
||||
* The animation should be implemented via the de-facto requestAnimationFrame
|
||||
* utility, or inside recursive `setTimeout`s. `setInterval` at this time are
|
||||
* not supported.
|
||||
*/
|
||||
recordAnimationFrame: function() {
|
||||
if (this._callWatcher.isRecording()) {
|
||||
return this._currentAnimationFrameSnapshot.promise;
|
||||
}
|
||||
|
||||
this._recordingContainsDrawCall = false;
|
||||
this._callWatcher.eraseRecording();
|
||||
this._callWatcher.initTimestampEpoch();
|
||||
this._webGLPrimitiveCounter.resetCounts();
|
||||
this._callWatcher.resumeRecording();
|
||||
|
||||
const deferred = this._currentAnimationFrameSnapshot = defer();
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cease attempts to record an animation frame.
|
||||
*/
|
||||
stopRecordingAnimationFrame: function() {
|
||||
if (!this._callWatcher.isRecording()) {
|
||||
return;
|
||||
}
|
||||
this._animationStarted = false;
|
||||
this._callWatcher.pauseRecording();
|
||||
this._callWatcher.eraseRecording();
|
||||
this._currentAnimationFrameSnapshot.resolve(null);
|
||||
this._currentAnimationFrameSnapshot = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever an instrumented function is called, be it on a
|
||||
* 2d or WebGL context, or an animation generator like requestAnimationFrame.
|
||||
*/
|
||||
_onContentFunctionCall: function(functionCall) {
|
||||
const { window, name, args } = functionCall.details;
|
||||
|
||||
// The function call arguments are required to replay animation frames,
|
||||
// in order to generate screenshots. However, simply storing references to
|
||||
// every kind of object is a bad idea, since their properties may change.
|
||||
// Consider transformation matrices for example, which are typically
|
||||
// Float32Arrays whose values can easily change across context calls.
|
||||
// They need to be cloned.
|
||||
inplaceShallowCloneArrays(args, window);
|
||||
|
||||
// Handle animations generated using requestAnimationFrame
|
||||
if (CanvasFront.ANIMATION_GENERATORS.has(name)) {
|
||||
this._handleAnimationFrame(functionCall);
|
||||
return;
|
||||
}
|
||||
// Handle animations generated using setTimeout. While using
|
||||
// those timers is considered extremely poor practice, they're still widely
|
||||
// used on the web, especially for old demos; it's nice to support them as well.
|
||||
if (CanvasFront.LOOP_GENERATORS.has(name)) {
|
||||
this._handleAnimationFrame(functionCall);
|
||||
return;
|
||||
}
|
||||
if (CanvasFront.DRAW_CALLS.has(name) && this._animationStarted) {
|
||||
this._handleDrawCall(functionCall);
|
||||
this._webGLPrimitiveCounter.handleDrawPrimitive(functionCall);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle animations generated using requestAnimationFrame.
|
||||
*/
|
||||
_handleAnimationFrame: function(functionCall) {
|
||||
if (!this._animationStarted) {
|
||||
this._handleAnimationFrameBegin();
|
||||
} else if (this._animationContainsDrawCall) {
|
||||
// Check to see if draw calls occurred yet, as it could be future frames,
|
||||
// like in the scenario where requestAnimationFrame is called to trigger
|
||||
// an animation, and rAF is at the beginning of the animate loop.
|
||||
this._handleAnimationFrameEnd(functionCall);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called whenever an animation frame rendering begins.
|
||||
*/
|
||||
_handleAnimationFrameBegin: function() {
|
||||
this._callWatcher.eraseRecording();
|
||||
this._animationStarted = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called whenever an animation frame rendering ends.
|
||||
*/
|
||||
_handleAnimationFrameEnd: function() {
|
||||
// Get a hold of all the function calls made during this animation frame.
|
||||
// Since only one snapshot can be recorded at a time, erase all the
|
||||
// previously recorded calls.
|
||||
const functionCalls = this._callWatcher.pauseRecording();
|
||||
this._callWatcher.eraseRecording();
|
||||
this._animationContainsDrawCall = false;
|
||||
|
||||
// Since the animation frame finished, get a hold of the (already retrieved)
|
||||
// canvas pixels to conveniently create a screenshot of the final rendering.
|
||||
const index = this._lastDrawCallIndex;
|
||||
const width = this._lastContentCanvasWidth;
|
||||
const height = this._lastContentCanvasHeight;
|
||||
// undefined -> false
|
||||
const flipped = !!this._lastThumbnailFlipped;
|
||||
const pixels = ContextUtils.getPixelStorage()["8bit"];
|
||||
const primitiveResult = this._webGLPrimitiveCounter.getCounts();
|
||||
const animationFrameEndScreenshot = {
|
||||
index: index,
|
||||
width: width,
|
||||
height: height,
|
||||
scaling: 1,
|
||||
flipped: flipped,
|
||||
pixels: pixels.subarray(0, width * height * 4),
|
||||
};
|
||||
|
||||
// Wrap the function calls and screenshot in a FrameSnapshotActor instance,
|
||||
// which will resolve the promise returned by `recordAnimationFrame`.
|
||||
const frameSnapshot = new FrameSnapshotActor(this.conn, {
|
||||
canvas: this._lastDrawCallCanvas,
|
||||
calls: functionCalls,
|
||||
screenshot: animationFrameEndScreenshot,
|
||||
primitive: {
|
||||
tris: primitiveResult.tris,
|
||||
vertices: primitiveResult.vertices,
|
||||
points: primitiveResult.points,
|
||||
lines: primitiveResult.lines,
|
||||
},
|
||||
});
|
||||
|
||||
this._currentAnimationFrameSnapshot.resolve(frameSnapshot);
|
||||
this._currentAnimationFrameSnapshot = null;
|
||||
this._animationStarted = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a draw call is detected in the animation frame which is
|
||||
* currently being recorded.
|
||||
*/
|
||||
_handleDrawCall: function(functionCall) {
|
||||
const functionCalls = this._callWatcher.pauseRecording();
|
||||
const caller = functionCall.details.caller;
|
||||
const global = functionCall.details.global;
|
||||
|
||||
const contentCanvas = this._lastDrawCallCanvas = caller.canvas;
|
||||
const index = this._lastDrawCallIndex = functionCalls.indexOf(functionCall);
|
||||
const w = this._lastContentCanvasWidth = contentCanvas.width;
|
||||
const h = this._lastContentCanvasHeight = contentCanvas.height;
|
||||
|
||||
// To keep things fast, generate images of small and fixed dimensions.
|
||||
const dimensions = CanvasFront.THUMBNAIL_SIZE;
|
||||
let thumbnail;
|
||||
|
||||
this._animationContainsDrawCall = true;
|
||||
|
||||
// Create a thumbnail on every draw call on the canvas context, to augment
|
||||
// the respective function call actor with this additional data.
|
||||
if (global == "WebGLRenderingContext") {
|
||||
// Check if drawing to a custom framebuffer (when rendering to texture).
|
||||
// Don't create a thumbnail in this particular case.
|
||||
const framebufferBinding = caller.getParameter(caller.FRAMEBUFFER_BINDING);
|
||||
if (framebufferBinding == null) {
|
||||
thumbnail = ContextUtils.getPixelsForWebGL(caller, 0, 0, w, h, dimensions);
|
||||
thumbnail.flipped = this._lastThumbnailFlipped = true;
|
||||
thumbnail.index = index;
|
||||
}
|
||||
} else if (global == "CanvasRenderingContext2D") {
|
||||
thumbnail = ContextUtils.getPixelsFor2D(caller, 0, 0, w, h, dimensions);
|
||||
thumbnail.flipped = this._lastThumbnailFlipped = false;
|
||||
thumbnail.index = index;
|
||||
}
|
||||
|
||||
functionCall._thumbnail = thumbnail;
|
||||
this._callWatcher.resumeRecording();
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* A collection of methods for manipulating canvas contexts.
|
||||
*/
|
||||
var ContextUtils = {
|
||||
/**
|
||||
* WebGL contexts are sensitive to how they're queried. Use this function
|
||||
* to make sure the right context is always retrieved, if available.
|
||||
*
|
||||
* @param HTMLCanvasElement canvas
|
||||
* The canvas element for which to get a WebGL context.
|
||||
* @param WebGLRenderingContext gl
|
||||
* The queried WebGL context, or null if unavailable.
|
||||
*/
|
||||
getWebGLContext: function(canvas) {
|
||||
return canvas.getContext("webgl") ||
|
||||
canvas.getContext("experimental-webgl");
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a hold of the rendered pixels in the most efficient way possible for
|
||||
* a canvas with a WebGL context.
|
||||
*
|
||||
* @param WebGLRenderingContext gl
|
||||
* The WebGL context to get a screenshot from.
|
||||
* @param number srcX [optional]
|
||||
* The first left pixel that is read from the framebuffer.
|
||||
* @param number srcY [optional]
|
||||
* The first top pixel that is read from the framebuffer.
|
||||
* @param number srcWidth [optional]
|
||||
* The number of pixels to read on the X axis.
|
||||
* @param number srcHeight [optional]
|
||||
* The number of pixels to read on the Y axis.
|
||||
* @param number dstHeight [optional]
|
||||
* The desired generated screenshot height.
|
||||
* @return object
|
||||
* An objet containing the screenshot's width, height and pixel data,
|
||||
* represented as an 8-bit array buffer of r, g, b, a values.
|
||||
*/
|
||||
getPixelsForWebGL: function(gl,
|
||||
srcX = 0, srcY = 0,
|
||||
srcWidth = gl.canvas.width,
|
||||
srcHeight = gl.canvas.height,
|
||||
dstHeight = srcHeight
|
||||
) {
|
||||
const contentPixels = ContextUtils.getPixelStorage(srcWidth, srcHeight);
|
||||
const { "8bit": charView, "32bit": intView } = contentPixels;
|
||||
gl.readPixels(srcX, srcY, srcWidth, srcHeight, gl.RGBA, gl.UNSIGNED_BYTE, charView);
|
||||
return this.resizePixels(intView, srcWidth, srcHeight, dstHeight);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a hold of the rendered pixels in the most efficient way possible for
|
||||
* a canvas with a 2D context.
|
||||
*
|
||||
* @param CanvasRenderingContext2D ctx
|
||||
* The 2D context to get a screenshot from.
|
||||
* @param number srcX [optional]
|
||||
* The first left pixel that is read from the canvas.
|
||||
* @param number srcY [optional]
|
||||
* The first top pixel that is read from the canvas.
|
||||
* @param number srcWidth [optional]
|
||||
* The number of pixels to read on the X axis.
|
||||
* @param number srcHeight [optional]
|
||||
* The number of pixels to read on the Y axis.
|
||||
* @param number dstHeight [optional]
|
||||
* The desired generated screenshot height.
|
||||
* @return object
|
||||
* An objet containing the screenshot's width, height and pixel data,
|
||||
* represented as an 8-bit array buffer of r, g, b, a values.
|
||||
*/
|
||||
getPixelsFor2D: function(ctx,
|
||||
srcX = 0, srcY = 0,
|
||||
srcWidth = ctx.canvas.width,
|
||||
srcHeight = ctx.canvas.height,
|
||||
dstHeight = srcHeight
|
||||
) {
|
||||
const { data } = ctx.getImageData(srcX, srcY, srcWidth, srcHeight);
|
||||
const { "32bit": intView } = ContextUtils.usePixelStorage(data.buffer);
|
||||
return this.resizePixels(intView, srcWidth, srcHeight, dstHeight);
|
||||
},
|
||||
|
||||
/**
|
||||
* Resizes the provided pixels to fit inside a rectangle with the specified
|
||||
* height and the same aspect ratio as the source.
|
||||
*
|
||||
* @param Uint32Array srcPixels
|
||||
* The source pixel data, assuming 32bit/pixel and 4 color components.
|
||||
* @param number srcWidth
|
||||
* The source pixel data width.
|
||||
* @param number srcHeight
|
||||
* The source pixel data height.
|
||||
* @param number dstHeight [optional]
|
||||
* The desired resized pixel data height.
|
||||
* @return object
|
||||
* An objet containing the resized pixels width, height and data,
|
||||
* represented as an 8-bit array buffer of r, g, b, a values.
|
||||
*/
|
||||
resizePixels: function(srcPixels, srcWidth, srcHeight, dstHeight) {
|
||||
const screenshotRatio = dstHeight / srcHeight;
|
||||
const dstWidth = (srcWidth * screenshotRatio) | 0;
|
||||
const dstPixels = new Uint32Array(dstWidth * dstHeight);
|
||||
|
||||
// If the resized image ends up being completely transparent, returning
|
||||
// an empty array will skip some redundant serialization cycles.
|
||||
let isTransparent = true;
|
||||
|
||||
for (let dstX = 0; dstX < dstWidth; dstX++) {
|
||||
for (let dstY = 0; dstY < dstHeight; dstY++) {
|
||||
const srcX = (dstX / screenshotRatio) | 0;
|
||||
const srcY = (dstY / screenshotRatio) | 0;
|
||||
const cPos = srcX + srcWidth * srcY;
|
||||
const dPos = dstX + dstWidth * dstY;
|
||||
const color = dstPixels[dPos] = srcPixels[cPos];
|
||||
if (color) {
|
||||
isTransparent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
width: dstWidth,
|
||||
height: dstHeight,
|
||||
pixels: isTransparent ? [] : new Uint8Array(dstPixels.buffer),
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Invokes a series of canvas context calls, to "replay" an animation frame
|
||||
* and generate a screenshot.
|
||||
*
|
||||
* In case of a WebGL context, an offscreen framebuffer is created for
|
||||
* the respective canvas, and the rendering will be performed into it.
|
||||
* This is necessary because some state (like shaders, textures etc.) can't
|
||||
* be shared between two different WebGL contexts.
|
||||
* - Hopefully, once SharedResources are a thing this won't be necessary:
|
||||
* http://www.khronos.org/webgl/wiki/SharedResouces
|
||||
* - Alternatively, we could pursue the idea of using the same context
|
||||
* for multiple canvases, instead of trying to share resources:
|
||||
* https://www.khronos.org/webgl/public-mailing-list/archives/1210/msg00058.html
|
||||
*
|
||||
* In case of a 2D context, a new canvas is created, since there's no
|
||||
* intrinsic state that can't be easily duplicated.
|
||||
*
|
||||
* @param number contextType
|
||||
* The type of context to use. See the FunctionCallFront constants.
|
||||
* @param HTMLCanvasElement canvas
|
||||
* The canvas element which is the source of all context calls.
|
||||
* @param array calls
|
||||
* An array of function call actors.
|
||||
* @param number first
|
||||
* The first function call to start from.
|
||||
* @param number last
|
||||
* The last (inclusive) function call to end at.
|
||||
* @return object
|
||||
* The context on which the specified calls were invoked, the
|
||||
* last registered draw call's index and a cleanup function, which
|
||||
* needs to be called whenever any potential followup work is finished.
|
||||
*/
|
||||
replayAnimationFrame: function({ contextType, canvas, calls, first, last }) {
|
||||
let w = canvas.width;
|
||||
let h = canvas.height;
|
||||
|
||||
let replayContext;
|
||||
let replayContextScaling;
|
||||
let customViewport;
|
||||
let customFramebuffer;
|
||||
let lastDrawCallIndex = -1;
|
||||
let doCleanup = () => {};
|
||||
|
||||
// In case of WebGL contexts, rendering will be done offscreen, in a
|
||||
// custom framebuffer, but using the same provided context. This is
|
||||
// necessary because it's very memory-unfriendly to rebuild all the
|
||||
// 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 == "WebGLRenderingContext") {
|
||||
// To keep things fast, replay the context calls on a framebuffer
|
||||
// of smaller dimensions than the actual canvas (maximum 256x256 pixels).
|
||||
const scaling = Math.min(CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, h) / h;
|
||||
replayContextScaling = scaling;
|
||||
w = (w * scaling) | 0;
|
||||
h = (h * scaling) | 0;
|
||||
|
||||
// Fetch the same WebGL context and bind a new framebuffer.
|
||||
const gl = replayContext = this.getWebGLContext(canvas);
|
||||
const { newFramebuffer, oldFramebuffer } = this.createBoundFramebuffer(gl, w, h);
|
||||
customFramebuffer = newFramebuffer;
|
||||
|
||||
// Set the viewport to match the new framebuffer's dimensions.
|
||||
const { newViewport, oldViewport } = this.setCustomViewport(gl, w, h);
|
||||
customViewport = newViewport;
|
||||
|
||||
// Revert the framebuffer and viewport to the original values.
|
||||
doCleanup = () => {
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, oldFramebuffer);
|
||||
gl.viewport.apply(gl, oldViewport);
|
||||
};
|
||||
} else if (contextType == "CanvasRenderingContext2D") {
|
||||
// In case of 2D contexts, draw everything on a separate canvas context.
|
||||
const contentDocument = canvas.ownerDocument;
|
||||
const replayCanvas = contentDocument.createElement("canvas");
|
||||
replayCanvas.width = w;
|
||||
replayCanvas.height = h;
|
||||
replayContext = replayCanvas.getContext("2d");
|
||||
replayContextScaling = 1;
|
||||
customViewport = [0, 0, w, h];
|
||||
}
|
||||
|
||||
// Replay all the context calls up to and including the specified one.
|
||||
for (let i = first; i <= last; i++) {
|
||||
const { type, name, args } = calls[i].details;
|
||||
|
||||
// Prevent WebGL context calls that try to reset the framebuffer binding
|
||||
// to the default value, since we want to perform the rendering offscreen.
|
||||
if (name == "bindFramebuffer" && args[1] == null) {
|
||||
replayContext.bindFramebuffer(replayContext.FRAMEBUFFER, customFramebuffer);
|
||||
continue;
|
||||
}
|
||||
// Also prevent WebGL context calls that try to change the viewport
|
||||
// while our custom framebuffer is bound.
|
||||
if (name == "viewport") {
|
||||
const framebufferBinding = replayContext.getParameter(
|
||||
replayContext.FRAMEBUFFER_BINDING);
|
||||
if (framebufferBinding == customFramebuffer) {
|
||||
replayContext.viewport.apply(replayContext, customViewport);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (type == METHOD_FUNCTION) {
|
||||
replayContext[name].apply(replayContext, args);
|
||||
} else if (type == SETTER_FUNCTION) {
|
||||
replayContext[name] = args;
|
||||
}
|
||||
if (CanvasFront.DRAW_CALLS.has(name)) {
|
||||
lastDrawCallIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
replayContext: replayContext,
|
||||
replayContextScaling: replayContextScaling,
|
||||
replayViewport: customViewport,
|
||||
lastDrawCallIndex: lastDrawCallIndex,
|
||||
doCleanup: doCleanup,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets an object containing a buffer large enough to hold width * height
|
||||
* pixels, assuming 32bit/pixel and 4 color components.
|
||||
*
|
||||
* This method avoids allocating memory and tries to reuse a common buffer
|
||||
* as much as possible.
|
||||
*
|
||||
* @param number w
|
||||
* The desired pixel array storage width.
|
||||
* @param number h
|
||||
* The desired pixel array storage height.
|
||||
* @return object
|
||||
* The requested pixel array buffer.
|
||||
*/
|
||||
getPixelStorage: function(w = 0, h = 0) {
|
||||
const storage = this._currentPixelStorage;
|
||||
if (storage && storage["32bit"].length >= w * h) {
|
||||
return storage;
|
||||
}
|
||||
return this.usePixelStorage(new ArrayBuffer(w * h * 4));
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates and saves the array buffer views used by `getPixelStorage`.
|
||||
*
|
||||
* @param ArrayBuffer buffer
|
||||
* The raw buffer used as storage for various array buffer views.
|
||||
*/
|
||||
usePixelStorage: function(buffer) {
|
||||
const array8bit = new Uint8Array(buffer);
|
||||
const array32bit = new Uint32Array(buffer);
|
||||
this._currentPixelStorage = {
|
||||
"8bit": array8bit,
|
||||
"32bit": array32bit,
|
||||
};
|
||||
return this._currentPixelStorage;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a framebuffer of the specified dimensions for a WebGL context,
|
||||
* assuming a RGBA color buffer, a depth buffer and no stencil buffer.
|
||||
*
|
||||
* @param WebGLRenderingContext gl
|
||||
* The WebGL context to create and bind a framebuffer for.
|
||||
* @param number width
|
||||
* The desired width of the renderbuffers.
|
||||
* @param number height
|
||||
* The desired height of the renderbuffers.
|
||||
* @return WebGLFramebuffer
|
||||
* The generated framebuffer object.
|
||||
*/
|
||||
createBoundFramebuffer: function(gl, width, height) {
|
||||
const oldFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
|
||||
const oldRenderbufferBinding = gl.getParameter(gl.RENDERBUFFER_BINDING);
|
||||
const oldTextureBinding = gl.getParameter(gl.TEXTURE_BINDING_2D);
|
||||
|
||||
const newFramebuffer = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, newFramebuffer);
|
||||
|
||||
// Use a texture as the color renderbuffer attachment, since consumers of
|
||||
// this function will most likely want to read the rendered pixels back.
|
||||
const colorBuffer = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, colorBuffer);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA,
|
||||
gl.UNSIGNED_BYTE, null);
|
||||
|
||||
const depthBuffer = gl.createRenderbuffer();
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
|
||||
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
|
||||
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D,
|
||||
colorBuffer, 0);
|
||||
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER,
|
||||
depthBuffer);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, oldTextureBinding);
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, oldRenderbufferBinding);
|
||||
|
||||
return { oldFramebuffer, newFramebuffer };
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the viewport of the drawing buffer for a WebGL context.
|
||||
* @param WebGLRenderingContext gl
|
||||
* @param number width
|
||||
* @param number height
|
||||
*/
|
||||
setCustomViewport: function(gl, width, height) {
|
||||
const oldViewport = XPCNativeWrapper.unwrap(gl.getParameter(gl.VIEWPORT));
|
||||
const newViewport = [0, 0, width, height];
|
||||
gl.viewport.apply(gl, newViewport);
|
||||
|
||||
return { oldViewport, newViewport };
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Goes through all the arguments and creates a one-level shallow copy
|
||||
* of all arrays and array buffers.
|
||||
*/
|
||||
function inplaceShallowCloneArrays(functionArguments, contentWindow) {
|
||||
const { Object, Array, ArrayBuffer } = contentWindow;
|
||||
|
||||
functionArguments.forEach((arg, index, store) => {
|
||||
if (arg instanceof Array) {
|
||||
store[index] = arg.slice();
|
||||
}
|
||||
if (arg instanceof Object && arg.buffer instanceof ArrayBuffer) {
|
||||
store[index] = new arg.constructor(arg);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'primitive.js',
|
||||
)
|
|
@ -1,161 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const WebGLPrimitivesType = {
|
||||
"POINTS": 0,
|
||||
"LINES": 1,
|
||||
"LINE_LOOP": 2,
|
||||
"LINE_STRIP": 3,
|
||||
"TRIANGLES": 4,
|
||||
"TRIANGLE_STRIP": 5,
|
||||
"TRIANGLE_FAN": 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility for monitoring WebGL primitive draws. Takes a `targetActor`
|
||||
* and monitors primitive draws over time.
|
||||
*/
|
||||
const WebGLDrawArrays = "drawArrays";
|
||||
const WebGLDrawElements = "drawElements";
|
||||
|
||||
exports.WebGLPrimitiveCounter = class WebGLPrimitiveCounter {
|
||||
constructor(targetActor) {
|
||||
this.targetActor = targetActor;
|
||||
}
|
||||
|
||||
destroy() {}
|
||||
|
||||
/**
|
||||
* Starts monitoring primitive draws, storing the primitives count per tick.
|
||||
*/
|
||||
resetCounts() {
|
||||
this._tris = 0;
|
||||
this._vertices = 0;
|
||||
this._points = 0;
|
||||
this._lines = 0;
|
||||
this._startTime = this.targetActor.docShell.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops monitoring primitive draws, returning the recorded values.
|
||||
*/
|
||||
getCounts() {
|
||||
const result = {
|
||||
tris: this._tris,
|
||||
vertices: this._vertices,
|
||||
points: this._points,
|
||||
lines: this._lines,
|
||||
};
|
||||
|
||||
this._tris = 0;
|
||||
this._vertices = 0;
|
||||
this._points = 0;
|
||||
this._lines = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles WebGL draw primitive functions to catch primitive info.
|
||||
*/
|
||||
handleDrawPrimitive(functionCall) {
|
||||
const { name, args } = functionCall.details;
|
||||
|
||||
if (name === WebGLDrawArrays) {
|
||||
this._processDrawArrays(args);
|
||||
} else if (name === WebGLDrawElements) {
|
||||
this._processDrawElements(args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes WebGL drawArrays method to count primitve numbers
|
||||
*/
|
||||
_processDrawArrays(args) {
|
||||
const mode = args[0];
|
||||
const count = args[2];
|
||||
|
||||
switch (mode) {
|
||||
case WebGLPrimitivesType.POINTS:
|
||||
this._vertices += count;
|
||||
this._points += count;
|
||||
break;
|
||||
case WebGLPrimitivesType.LINES:
|
||||
this._vertices += count;
|
||||
this._lines += (count / 2);
|
||||
break;
|
||||
case WebGLPrimitivesType.LINE_LOOP:
|
||||
this._vertices += count;
|
||||
this._lines += count;
|
||||
break;
|
||||
case WebGLPrimitivesType.LINE_STRIP:
|
||||
this._vertices += count;
|
||||
this._lines += (count - 1);
|
||||
break;
|
||||
case WebGLPrimitivesType.TRIANGLES:
|
||||
this._tris += (count / 3);
|
||||
this._vertices += count;
|
||||
break;
|
||||
case WebGLPrimitivesType.TRIANGLE_STRIP:
|
||||
this._tris += (count - 2);
|
||||
this._vertices += count;
|
||||
break;
|
||||
case WebGLPrimitivesType.TRIANGLE_FAN:
|
||||
this._tris += (count - 2);
|
||||
this._vertices += count;
|
||||
break;
|
||||
default:
|
||||
console.error("_processDrawArrays doesn't define this type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes WebGL drawElements method to count primitve numbers
|
||||
*/
|
||||
_processDrawElements(args) {
|
||||
const mode = args[0];
|
||||
const count = args[1];
|
||||
|
||||
switch (mode) {
|
||||
case WebGLPrimitivesType.POINTS:
|
||||
this._vertices += count;
|
||||
this._points += count;
|
||||
break;
|
||||
case WebGLPrimitivesType.LINES:
|
||||
this._vertices += count;
|
||||
this._lines += (count / 2);
|
||||
break;
|
||||
case WebGLPrimitivesType.LINE_LOOP:
|
||||
this._vertices += count;
|
||||
this._lines += count;
|
||||
break;
|
||||
case WebGLPrimitivesType.LINE_STRIP:
|
||||
this._vertices += count;
|
||||
this._lines += (count - 1);
|
||||
break;
|
||||
case WebGLPrimitivesType.TRIANGLES:
|
||||
const tris = count / 3;
|
||||
let vertex = tris * 3;
|
||||
|
||||
if (tris > 1) {
|
||||
vertex = tris * 2;
|
||||
}
|
||||
this._tris += tris;
|
||||
this._vertices += vertex;
|
||||
break;
|
||||
case WebGLPrimitivesType.TRIANGLE_STRIP:
|
||||
this._tris += (count - 2);
|
||||
this._vertices += count;
|
||||
break;
|
||||
case WebGLPrimitivesType.TRIANGLE_FAN:
|
||||
this._tris += (count - 2);
|
||||
this._vertices += count;
|
||||
break;
|
||||
default:
|
||||
console.error("_processDrawElements doesn't define this type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -7,7 +7,6 @@
|
|||
DIRS += [
|
||||
'accessibility',
|
||||
'addon',
|
||||
'canvas',
|
||||
'emulation',
|
||||
'highlighters',
|
||||
'inspector',
|
||||
|
@ -26,7 +25,6 @@ DevToolsModules(
|
|||
'animation.js',
|
||||
'array-buffer.js',
|
||||
'breakpoint.js',
|
||||
'canvas.js',
|
||||
'changes.js',
|
||||
'common.js',
|
||||
'css-properties.js',
|
||||
|
|
|
@ -163,11 +163,6 @@ const ActorRegistry = {
|
|||
constructor: "InspectorActor",
|
||||
type: { target: true },
|
||||
});
|
||||
this.registerModule("devtools/server/actors/canvas", {
|
||||
prefix: "canvas",
|
||||
constructor: "CanvasActor",
|
||||
type: { target: true },
|
||||
});
|
||||
this.registerModule("devtools/server/actors/webgl", {
|
||||
prefix: "webgl",
|
||||
constructor: "WebGLActor",
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
frameSnapshotSpec,
|
||||
canvasSpec,
|
||||
CANVAS_CONTEXTS,
|
||||
ANIMATION_GENERATORS,
|
||||
LOOP_GENERATORS,
|
||||
DRAW_CALLS,
|
||||
INTERESTING_CALLS,
|
||||
} = require("devtools/shared/specs/canvas");
|
||||
const { FrontClassWithSpec, registerFront } = require("devtools/shared/protocol");
|
||||
const promise = require("promise");
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the FrameSnapshotActor.
|
||||
*/
|
||||
class FrameSnapshotFront extends FrontClassWithSpec(frameSnapshotSpec) {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this._animationFrameEndScreenshot = null;
|
||||
this._cachedScreenshots = new WeakMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation caches the animation frame end screenshot to optimize
|
||||
* frontend requests to `generateScreenshotFor`.
|
||||
*/
|
||||
getOverview() {
|
||||
return super.getOverview().then(data => {
|
||||
this._animationFrameEndScreenshot = data.screenshot;
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation saves a roundtrip to the backend if the screenshot
|
||||
* was already generated and retrieved once.
|
||||
*/
|
||||
generateScreenshotFor(functionCall) {
|
||||
if (CanvasFront.ANIMATION_GENERATORS.has(functionCall.name) ||
|
||||
CanvasFront.LOOP_GENERATORS.has(functionCall.name)) {
|
||||
return promise.resolve(this._animationFrameEndScreenshot);
|
||||
}
|
||||
const cachedScreenshot = this._cachedScreenshots.get(functionCall);
|
||||
if (cachedScreenshot) {
|
||||
return cachedScreenshot;
|
||||
}
|
||||
const screenshot = super.generateScreenshotFor(functionCall);
|
||||
this._cachedScreenshots.set(functionCall, screenshot);
|
||||
return screenshot;
|
||||
}
|
||||
}
|
||||
|
||||
exports.FrameSnapshotFront = FrameSnapshotFront;
|
||||
registerFront(FrameSnapshotFront);
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the CanvasActor.
|
||||
*/
|
||||
class CanvasFront extends FrontClassWithSpec(canvasSpec) {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
|
||||
// Attribute name from which to retrieve the actorID out of the target actor's form
|
||||
this.formAttributeName = "canvasActor";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
*/
|
||||
CanvasFront.CANVAS_CONTEXTS = new Set(CANVAS_CONTEXTS);
|
||||
CanvasFront.ANIMATION_GENERATORS = new Set(ANIMATION_GENERATORS);
|
||||
CanvasFront.LOOP_GENERATORS = new Set(LOOP_GENERATORS);
|
||||
CanvasFront.DRAW_CALLS = new Set(DRAW_CALLS);
|
||||
CanvasFront.INTERESTING_CALLS = new Set(INTERESTING_CALLS);
|
||||
CanvasFront.THUMBNAIL_SIZE = 50;
|
||||
CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT = 256;
|
||||
CanvasFront.INVALID_SNAPSHOT_IMAGE = {
|
||||
index: -1,
|
||||
width: 0,
|
||||
height: 0,
|
||||
pixels: [],
|
||||
};
|
||||
|
||||
exports.CanvasFront = CanvasFront;
|
||||
registerFront(CanvasFront);
|
|
@ -15,7 +15,6 @@ DevToolsModules(
|
|||
'accessibility.js',
|
||||
'actor-registry.js',
|
||||
'animation.js',
|
||||
'canvas.js',
|
||||
'changes.js',
|
||||
'css-properties.js',
|
||||
'csscoverage.js',
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const {Arg, Option, RetVal, generateActorSpec} = protocol;
|
||||
|
||||
/**
|
||||
* Type representing an ArrayBufferView, serialized fast(er).
|
||||
*
|
||||
* Don't create a new array buffer view from the parsed array on the frontend.
|
||||
* Consumers may copy the data into an existing buffer, or create a new one if
|
||||
* necesasry. For example, this avoids the need for a redundant copy when
|
||||
* populating ImageData objects, at the expense of transferring char views
|
||||
* of a pixel buffer over the protocol instead of a packed int view.
|
||||
*
|
||||
* XXX: It would be nice if on local connections (only), we could just *give*
|
||||
* the buffer directly to the front, instead of going through all this
|
||||
* serialization redundancy.
|
||||
*/
|
||||
protocol.types.addType("array-buffer-view", {
|
||||
write: (v) => "[" + Array.join(v, ",") + "]",
|
||||
read: (v) => JSON.parse(v),
|
||||
});
|
||||
|
||||
/**
|
||||
* Type describing a thumbnail or screenshot in a recorded animation frame.
|
||||
*/
|
||||
protocol.types.addDictType("snapshot-image", {
|
||||
index: "number",
|
||||
width: "number",
|
||||
height: "number",
|
||||
scaling: "number",
|
||||
flipped: "boolean",
|
||||
pixels: "array-buffer-view",
|
||||
});
|
||||
|
||||
/**
|
||||
* Type describing an overview of a recorded animation frame.
|
||||
*/
|
||||
protocol.types.addDictType("snapshot-overview", {
|
||||
calls: "array:function-call",
|
||||
thumbnails: "array:snapshot-image",
|
||||
screenshot: "snapshot-image",
|
||||
});
|
||||
|
||||
exports.CANVAS_CONTEXTS = [
|
||||
"CanvasRenderingContext2D",
|
||||
"WebGLRenderingContext",
|
||||
];
|
||||
|
||||
exports.ANIMATION_GENERATORS = [
|
||||
"requestAnimationFrame",
|
||||
];
|
||||
|
||||
exports.LOOP_GENERATORS = [
|
||||
"setTimeout",
|
||||
];
|
||||
|
||||
exports.DRAW_CALLS = [
|
||||
// 2D canvas
|
||||
"fill",
|
||||
"stroke",
|
||||
"clearRect",
|
||||
"fillRect",
|
||||
"strokeRect",
|
||||
"fillText",
|
||||
"strokeText",
|
||||
"drawImage",
|
||||
|
||||
// WebGL
|
||||
"clear",
|
||||
"drawArrays",
|
||||
"drawElements",
|
||||
"finish",
|
||||
"flush",
|
||||
];
|
||||
|
||||
exports.INTERESTING_CALLS = [
|
||||
// 2D canvas
|
||||
"save",
|
||||
"restore",
|
||||
|
||||
// WebGL
|
||||
"useProgram",
|
||||
];
|
||||
|
||||
const frameSnapshotSpec = generateActorSpec({
|
||||
typeName: "frame-snapshot",
|
||||
|
||||
methods: {
|
||||
getOverview: {
|
||||
response: { overview: RetVal("snapshot-overview") },
|
||||
},
|
||||
generateScreenshotFor: {
|
||||
request: { call: Arg(0, "function-call") },
|
||||
response: { screenshot: RetVal("snapshot-image") },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
exports.frameSnapshotSpec = frameSnapshotSpec;
|
||||
|
||||
const canvasSpec = generateActorSpec({
|
||||
typeName: "canvas",
|
||||
|
||||
methods: {
|
||||
setup: {
|
||||
request: { reload: Option(0, "boolean") },
|
||||
oneway: true,
|
||||
},
|
||||
finalize: {
|
||||
oneway: true,
|
||||
},
|
||||
isInitialized: {
|
||||
response: { initialized: RetVal("boolean") },
|
||||
},
|
||||
isRecording: {
|
||||
response: { recording: RetVal("boolean") },
|
||||
},
|
||||
recordAnimationFrame: {
|
||||
response: { snapshot: RetVal("nullable:frame-snapshot") },
|
||||
},
|
||||
stopRecordingAnimationFrame: {
|
||||
oneway: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
exports.canvasSpec = canvasSpec;
|
|
@ -42,11 +42,6 @@ const Types = exports.__TypesForTests = [
|
|||
spec: "devtools/shared/specs/animation",
|
||||
front: "devtools/shared/fronts/animation",
|
||||
},
|
||||
{
|
||||
types: ["frame-snapshot", "canvas"],
|
||||
spec: "devtools/shared/specs/canvas",
|
||||
front: "devtools/shared/fronts/canvas",
|
||||
},
|
||||
{
|
||||
types: ["changes"],
|
||||
spec: "devtools/shared/specs/changes",
|
||||
|
|
|
@ -14,7 +14,6 @@ DevToolsModules(
|
|||
'accessibility.js',
|
||||
'actor-registry.js',
|
||||
'animation.js',
|
||||
'canvas.js',
|
||||
'changes.js',
|
||||
'css-properties.js',
|
||||
'csscoverage.js',
|
||||
|
|
Загрузка…
Ссылка в новой задаче