blob: 58238cdf3f69b004875c7724388d4696a339fe31 [file] [log] [blame]
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
WI.Recording = class Recording
{
constructor(version, type, initialState, frames, data)
{
this._version = version;
this._type = type;
this._initialState = initialState;
this._frames = frames;
this._data = data;
this._displayName = WI.UIString("Recording");
this._swizzle = [];
this._visualActionIndexes = [];
this._source = null;
let actions = [new WI.RecordingInitialStateAction].concat(...this._frames.map((frame) => frame.actions));
this._actions = Promise.all(actions.map((action) => action.swizzle(this))).then(() => {
actions.forEach((action, index) => {
if (!action.valid)
return;
let prototype = null;
if (this._type === WI.Recording.Type.Canvas2D)
prototype = CanvasRenderingContext2D.prototype;
else if (this._type === WI.Recording.Type.CanvasWebGL)
prototype = WebGLRenderingContext.prototype;
if (prototype) {
let validName = action.name in prototype;
let validFunction = !action.isFunction || typeof prototype[action.name] === "function";
if (!validName || !validFunction) {
action.markInvalid();
WI.Recording.synthesizeError(WI.UIString("ā€œ%sā€ is invalid.").format(this._name));
}
}
if (action.isVisual)
this._visualActionIndexes.push(index);
});
return actions;
});
}
static fromPayload(payload, frames)
{
if (typeof payload !== "object" || payload === null)
return null;
if (isNaN(payload.version) || payload.version <= 0)
return null;
let type = null;
switch (payload.type) {
case RecordingAgent.Type.Canvas2D:
type = WI.Recording.Type.Canvas2D;
break;
case RecordingAgent.Type.CanvasWebGL:
type = WI.Recording.Type.CanvasWebGL;
break;
default:
type = String(payload.type);
break;
}
if (typeof payload.initialState !== "object" || payload.initialState === null)
payload.initialState = {};
if (typeof payload.initialState.attributes !== "object" || payload.initialState.attributes === null)
payload.initialState.attributes = {};
if (!Array.isArray(payload.initialState.parameters))
payload.initialState.parameters = [];
if (typeof payload.initialState.content !== "string")
payload.initialState.content = "";
if (!Array.isArray(payload.frames))
payload.frames = [];
if (!Array.isArray(payload.data))
payload.data = [];
if (!frames)
frames = payload.frames.map(WI.RecordingFrame.fromPayload)
return new WI.Recording(payload.version, type, payload.initialState, frames, payload.data);
}
static displayNameForSwizzleType(swizzleType)
{
switch (swizzleType) {
case WI.Recording.Swizzle.None:
return WI.unlocalizedString("None");
case WI.Recording.Swizzle.Number:
return WI.unlocalizedString("Number");
case WI.Recording.Swizzle.Boolean:
return WI.unlocalizedString("Boolean");
case WI.Recording.Swizzle.String:
return WI.unlocalizedString("String");
case WI.Recording.Swizzle.Array:
return WI.unlocalizedString("Array");
case WI.Recording.Swizzle.TypedArray:
return WI.unlocalizedString("TypedArray");
case WI.Recording.Swizzle.Image:
return WI.unlocalizedString("Image");
case WI.Recording.Swizzle.ImageData:
return WI.unlocalizedString("ImageData");
case WI.Recording.Swizzle.DOMMatrix:
return WI.unlocalizedString("DOMMatrix");
case WI.Recording.Swizzle.Path2D:
return WI.unlocalizedString("Path2D");
case WI.Recording.Swizzle.CanvasGradient:
return WI.unlocalizedString("CanvasGradient");
case WI.Recording.Swizzle.CanvasPattern:
return WI.unlocalizedString("CanvasPattern");
case WI.Recording.Swizzle.WebGLBuffer:
return WI.unlocalizedString("WebGLBuffer");
case WI.Recording.Swizzle.WebGLFramebuffer:
return WI.unlocalizedString("WebGLFramebuffer");
case WI.Recording.Swizzle.WebGLRenderbuffer:
return WI.unlocalizedString("WebGLRenderbuffer");
case WI.Recording.Swizzle.WebGLTexture:
return WI.unlocalizedString("WebGLTexture");
case WI.Recording.Swizzle.WebGLShader:
return WI.unlocalizedString("WebGLShader");
case WI.Recording.Swizzle.WebGLProgram:
return WI.unlocalizedString("WebGLProgram");
case WI.Recording.Swizzle.WebGLUniformLocation:
return WI.unlocalizedString("WebGLUniformLocation");
case WI.Recording.Swizzle.ImageBitmap:
return WI.unlocalizedString("ImageBitmap");
default:
console.error("Unknown swizzle type", swizzleType);
return null;
}
}
static synthesizeError(message)
{
const target = WI.mainTarget;
const source = WI.ConsoleMessage.MessageSource.Other;
const level = WI.ConsoleMessage.MessageLevel.Error;
let consoleMessage = new WI.ConsoleMessage(target, source, level, WI.UIString("Recording error: %s").format(message));
consoleMessage.shouldRevealConsole = true;
WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
}
// Public
get displayName() { return this._displayName; }
get type() { return this._type; }
get initialState() { return this._initialState; }
get frames() { return this._frames; }
get data() { return this._data; }
get visualActionIndexes() { return this._visualActionIndexes; }
get actions() { return this._actions; }
get source() { return this._source; }
set source(source) { this._source = source; }
createDisplayName(suggestedName)
{
let recordingNameSet;
if (this._source) {
recordingNameSet = this._source[WI.Recording.CanvasRecordingNamesSymbol];
if (!recordingNameSet)
this._source[WI.Recording.CanvasRecordingNamesSymbol] = recordingNameSet = new Set;
} else
recordingNameSet = WI.Recording._importedRecordingNameSet;
let name;
if (suggestedName) {
name = suggestedName;
let duplicateNumber = 2;
while (recordingNameSet.has(name))
name = `${suggestedName} (${duplicateNumber++})`;
} else {
let recordingNumber = 1;
do {
name = WI.UIString("Recording %d").format(recordingNumber++);
} while (recordingNameSet.has(name));
}
recordingNameSet.add(name);
this._displayName = name;
}
async swizzle(index, type)
{
if (typeof this._swizzle[index] !== "object")
this._swizzle[index] = {};
if (type === WI.Recording.Swizzle.Number)
return parseFloat(index);
if (type === WI.Recording.Swizzle.Boolean)
return !!index;
if (type === WI.Recording.Swizzle.Array)
return Array.isArray(index) ? index : [];
if (type === WI.Recording.Swizzle.DOMMatrix)
return new DOMMatrix(index);
// FIXME: <https://webkit.org/b/176009> Web Inspector: send data for WebGL objects during a recording instead of a placeholder string
if (type === WI.Recording.Swizzle.TypedArray
|| type === WI.Recording.Swizzle.WebGLBuffer
|| type === WI.Recording.Swizzle.WebGLFramebuffer
|| type === WI.Recording.Swizzle.WebGLRenderbuffer
|| type === WI.Recording.Swizzle.WebGLTexture
|| type === WI.Recording.Swizzle.WebGLShader
|| type === WI.Recording.Swizzle.WebGLProgram
|| type === WI.Recording.Swizzle.WebGLUniformLocation) {
return index;
}
if (!(type in this._swizzle[index])) {
try {
let data = this._data[index];
switch (type) {
case WI.Recording.Swizzle.None:
this._swizzle[index][type] = data;
break;
case WI.Recording.Swizzle.String:
this._swizzle[index][type] = String(data);
break;
case WI.Recording.Swizzle.Image:
this._swizzle[index][type] = await WI.ImageUtilities.promisifyLoad(data);
break;
case WI.Recording.Swizzle.ImageData:
this._swizzle[index][type] = new ImageData(new Uint8ClampedArray(data[0]), parseInt(data[1]), parseInt(data[2]));
break;
case WI.Recording.Swizzle.Path2D:
this._swizzle[index][type] = new Path2D(data);
break;
case WI.Recording.Swizzle.CanvasGradient:
var gradientType = await this.swizzle(data[0], WI.Recording.Swizzle.String);
WI.ImageUtilities.scratchCanvasContext2D((context) => {
this._swizzle[index][type] = gradientType === "radial-gradient" ? context.createRadialGradient(...data[1]) : context.createLinearGradient(...data[1]);
});
for (let stop of data[2]) {
let color = await this.swizzle(stop[1], WI.Recording.Swizzle.String);
this._swizzle[index][type].addColorStop(stop[0], color);
}
break;
case WI.Recording.Swizzle.CanvasPattern:
var [image, repeat] = await Promise.all([
this.swizzle(data[0], WI.Recording.Swizzle.Image),
this.swizzle(data[1], WI.Recording.Swizzle.String),
]);
WI.ImageUtilities.scratchCanvasContext2D((context) => {
this._swizzle[index][type] = context.createPattern(image, repeat);
this._swizzle[index][type].__image = image;
});
break;
case WI.Recording.Swizzle.ImageBitmap:
var image = await this.swizzle(index, WI.Recording.Swizzle.Image);
this._swizzle[index][type] = await createImageBitmap(image);
break;
}
} catch { }
}
return this._swizzle[index][type];
}
toJSON()
{
let initialState = {};
if (!isEmptyObject(this._initialState.attributes))
initialState.attributes = this._initialState.attributes;
if (this._initialState.parameters.length)
initialState.parameters = this._initialState.parameters;
if (this._initialState.content && this._initialState.content.length)
initialState.content = this._initialState.content;
return {
version: this._version,
type: this._type,
initialState,
frames: this._frames.map((frame) => frame.toJSON()),
data: this._data,
};
}
};
WI.Recording._importedRecordingNameSet = new Set;
WI.Recording.CanvasRecordingNamesSymbol = Symbol("canvas-recording-names");
WI.Recording.Type = {
Canvas2D: "canvas-2d",
CanvasWebGL: "canvas-webgl",
};
// Keep this in sync with WebCore::RecordingSwizzleTypes.
WI.Recording.Swizzle = {
None: 0,
Number: 1,
Boolean: 2,
String: 3,
Array: 4,
TypedArray: 5,
Image: 6,
ImageData: 7,
DOMMatrix: 8,
Path2D: 9,
CanvasGradient: 10,
CanvasPattern: 11,
WebGLBuffer: 12,
WebGLFramebuffer: 13,
WebGLRenderbuffer: 14,
WebGLTexture: 15,
WebGLShader: 16,
WebGLProgram: 17,
WebGLUniformLocation: 18,
ImageBitmap: 19,
};