468 lines
16 KiB
JavaScript
468 lines
16 KiB
JavaScript
/*
|
|
* 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.RecordingActionTreeElement = class RecordingActionTreeElement extends WI.GeneralTreeElement
|
|
{
|
|
constructor(representedObject, index, recordingType)
|
|
{
|
|
console.assert(representedObject instanceof WI.RecordingAction);
|
|
|
|
let {titleFragment, copyText} = WI.RecordingActionTreeElement._generateDOM(representedObject, recordingType);
|
|
let classNames = WI.RecordingActionTreeElement._getClassNames(representedObject);
|
|
|
|
const subtitle = null;
|
|
super(classNames, titleFragment, subtitle, representedObject);
|
|
|
|
this._index = index;
|
|
this._copyText = copyText;
|
|
|
|
if (this.representedObject.valid)
|
|
this.representedObject.singleFireEventListener(WI.RecordingAction.Event.ValidityChanged, this._handleValidityChanged, this);
|
|
}
|
|
|
|
// Static
|
|
|
|
static _generateDOM(recordingAction, recordingType)
|
|
{
|
|
let parameterCount = recordingAction.parameters.length;
|
|
|
|
function createParameterElement(parameter, swizzleType, index) {
|
|
let parameterCopyText = "";
|
|
let parameterElement = document.createElement("span");
|
|
parameterElement.classList.add("parameter");
|
|
|
|
switch (swizzleType) {
|
|
case WI.Recording.Swizzle.Number:
|
|
var constantNameForParameter = WI.RecordingAction.constantNameForParameter(recordingType, recordingAction.name, parameter, index, parameterCount);
|
|
var bitfieldNamesForParameter = WI.RecordingAction.bitfieldNamesForParameter(recordingType, recordingAction.name, parameter, index, parameterCount);
|
|
if (constantNameForParameter) {
|
|
parameterElement.classList.add("constant");
|
|
parameterElement.textContent = "context." + constantNameForParameter;
|
|
} else if (bitfieldNamesForParameter) {
|
|
parameterElement.classList.add("constant");
|
|
parameterElement.textContent = bitfieldNamesForParameter.map((p) => p.startsWith("0x") ? p : "context." + p).join(" | ");
|
|
} else
|
|
parameterElement.textContent = parameter.maxDecimals(2);
|
|
break;
|
|
|
|
case WI.Recording.Swizzle.Boolean:
|
|
parameterElement.textContent = parameter;
|
|
break;
|
|
|
|
case WI.Recording.Swizzle.String:
|
|
parameterElement.textContent = JSON.stringify(parameter);
|
|
break;
|
|
|
|
case WI.Recording.Swizzle.Array:
|
|
parameterElement.classList.add("swizzled");
|
|
parameterElement.textContent = JSON.stringify(parameter);
|
|
break;
|
|
|
|
case WI.Recording.Swizzle.TypedArray:
|
|
case WI.Recording.Swizzle.Image:
|
|
case WI.Recording.Swizzle.ImageBitmap:
|
|
case WI.Recording.Swizzle.ImageData:
|
|
case WI.Recording.Swizzle.DOMMatrix:
|
|
case WI.Recording.Swizzle.Path2D:
|
|
case WI.Recording.Swizzle.CanvasGradient:
|
|
case WI.Recording.Swizzle.CanvasPattern:
|
|
parameterElement.classList.add("swizzled");
|
|
parameterElement.textContent = WI.Recording.displayNameForSwizzleType(swizzleType);
|
|
break;
|
|
|
|
case WI.Recording.Swizzle.WebGLBuffer:
|
|
case WI.Recording.Swizzle.WebGLFramebuffer:
|
|
case WI.Recording.Swizzle.WebGLRenderbuffer:
|
|
case WI.Recording.Swizzle.WebGLTexture:
|
|
case WI.Recording.Swizzle.WebGLShader:
|
|
case WI.Recording.Swizzle.WebGLProgram:
|
|
case WI.Recording.Swizzle.WebGLUniformLocation:
|
|
case WI.Recording.Swizzle.WebGLQuery:
|
|
case WI.Recording.Swizzle.WebGLSampler:
|
|
case WI.Recording.Swizzle.WebGLSync:
|
|
case WI.Recording.Swizzle.WebGLTransformFeedback:
|
|
case WI.Recording.Swizzle.WebGLVertexArrayObject:
|
|
parameterCopyText = WI.Recording.displayNameForSwizzleType(swizzleType);
|
|
|
|
parameterElement.textContent = parameterCopyText;
|
|
if (parameter) {
|
|
let objectHandleElement = document.createElement("span");
|
|
objectHandleElement.classList.add("parameter");
|
|
objectHandleElement.classList.add("object-handle");
|
|
objectHandleElement.textContent = `@${parameter}`;
|
|
parameterElement.append(" ", objectHandleElement);
|
|
} else
|
|
parameterElement.classList.add("swizzled");
|
|
break;
|
|
}
|
|
|
|
if (!parameterElement.textContent) {
|
|
parameterElement.classList.add("invalid");
|
|
parameterElement.textContent = swizzleType === WI.Recording.Swizzle.None ? parameter : WI.Recording.displayNameForSwizzleType(swizzleType);
|
|
}
|
|
|
|
if (!parameterCopyText.length)
|
|
parameterCopyText = parameterElement.textContent;
|
|
|
|
return {parameterElement, parameterCopyText};
|
|
}
|
|
|
|
let titleFragment = document.createDocumentFragment();
|
|
let copyText = recordingAction.name;
|
|
|
|
let contextReplacer = recordingAction.contextReplacer;
|
|
if (contextReplacer) {
|
|
copyText = contextReplacer + "." + copyText;
|
|
|
|
let contextReplacerContainer = titleFragment.appendChild(document.createElement("span"));
|
|
contextReplacerContainer.classList.add("context-replacer");
|
|
contextReplacerContainer.textContent = contextReplacer;
|
|
}
|
|
|
|
let nameContainer = titleFragment.appendChild(document.createElement("span"));
|
|
nameContainer.classList.add("name");
|
|
nameContainer.textContent = recordingAction.name;
|
|
|
|
if (!parameterCount)
|
|
return {titleFragment, copyText};
|
|
|
|
let parametersContainer = titleFragment.appendChild(document.createElement("span"));
|
|
parametersContainer.classList.add("parameters");
|
|
|
|
if (recordingAction.isFunction)
|
|
copyText += "(";
|
|
else
|
|
copyText += " = ";
|
|
|
|
for (let i = 0; i < parameterCount; ++i) {
|
|
let parameter = recordingAction.parameters[i];
|
|
let swizzleType = recordingAction.swizzleTypes[i];
|
|
let {parameterElement, parameterCopyText} = createParameterElement(parameter, swizzleType, i);
|
|
parametersContainer.appendChild(parameterElement);
|
|
|
|
if (i)
|
|
copyText += ", ";
|
|
|
|
copyText += parameterCopyText;
|
|
}
|
|
|
|
if (recordingAction.isFunction)
|
|
copyText += ")";
|
|
|
|
let colorParameters = recordingAction.getColorParameters();
|
|
if (colorParameters.length) {
|
|
let swatch = WI.RecordingActionTreeElement._createSwatchForColorParameters(colorParameters);
|
|
if (swatch) {
|
|
let insertionIndex = recordingAction.parameters.indexOf(colorParameters[0]);
|
|
parametersContainer.insertBefore(swatch.element, parametersContainer.children[insertionIndex]);
|
|
}
|
|
}
|
|
|
|
let imageParameters = recordingAction.getImageParameters();
|
|
let isImage = imageParameters[0] instanceof HTMLImageElement;
|
|
let isImageBitmap = imageParameters[0] instanceof ImageBitmap;
|
|
let isImageData = imageParameters[0] instanceof ImageData;
|
|
let isCanvasGradient = imageParameters[0] instanceof CanvasGradient;
|
|
let isCanvasPattern = imageParameters[0] instanceof CanvasPattern;
|
|
if (imageParameters.length && (isImage || isImageBitmap || isImageData || isCanvasGradient || isCanvasPattern)) {
|
|
let image = imageParameters[0];
|
|
|
|
if (isImageBitmap)
|
|
image = WI.ImageUtilities.imageFromImageBitmap(image);
|
|
else if (isImageData)
|
|
image = WI.ImageUtilities.imageFromImageData(image);
|
|
else if (isCanvasGradient)
|
|
image = WI.ImageUtilities.imageFromCanvasGradient(image, 100, 100);
|
|
else if (isCanvasPattern)
|
|
image = image.__image;
|
|
|
|
if (image) {
|
|
let swatch = new WI.InlineSwatch(WI.InlineSwatch.Type.Image, image);
|
|
let insertionIndex = recordingAction.parameters.indexOf(imageParameters[0]);
|
|
let parameterElement = parametersContainer.children[insertionIndex];
|
|
parametersContainer.insertBefore(swatch.element, parameterElement);
|
|
}
|
|
}
|
|
|
|
return {titleFragment, copyText};
|
|
}
|
|
|
|
static _createSwatchForColorParameters(parameters)
|
|
{
|
|
let rgb = [];
|
|
let color = null;
|
|
|
|
switch (parameters.length) {
|
|
case 1:
|
|
case 2:
|
|
if (typeof parameters[0] === "string")
|
|
color = WI.Color.fromString(parameters[0].trim());
|
|
else if (!isNaN(parameters[0]))
|
|
rgb = WI.Color.normalized2rgb(parameters[0], parameters[0], parameters[0]);
|
|
break;
|
|
|
|
case 4:
|
|
rgb = WI.Color.normalized2rgb(parameters[0], parameters[1], parameters[2]);
|
|
break;
|
|
|
|
case 5:
|
|
rgb = WI.Color.cmyk2rgb(...parameters);
|
|
break;
|
|
|
|
default:
|
|
console.error("Unexpected number of color parameters.");
|
|
return null;
|
|
}
|
|
|
|
if (!color) {
|
|
if (rgb.length !== 3)
|
|
return null;
|
|
|
|
let alpha = parameters.length === 1 ? 1 : parameters.lastValue;
|
|
color = new WI.Color(WI.Color.Format.RGBA, [...rgb, alpha]);
|
|
if (!color)
|
|
return null;
|
|
}
|
|
|
|
return new WI.InlineSwatch(WI.InlineSwatch.Type.Color, color, {readOnly: true});
|
|
}
|
|
|
|
static _getClassNames(recordingAction)
|
|
{
|
|
let classNames = ["recording-action"];
|
|
|
|
if (recordingAction instanceof WI.RecordingInitialStateAction) {
|
|
classNames.push("initial-state");
|
|
return classNames;
|
|
}
|
|
|
|
if (!recordingAction.isFunction)
|
|
classNames.push("attribute");
|
|
|
|
let actionClassName = WI.RecordingActionTreeElement._classNameForAction(recordingAction);
|
|
if (actionClassName.length)
|
|
classNames.push(actionClassName);
|
|
|
|
if (recordingAction.isVisual)
|
|
classNames.push("visual");
|
|
|
|
if (!recordingAction.valid)
|
|
classNames.push("invalid");
|
|
|
|
return classNames;
|
|
}
|
|
|
|
static _classNameForAction(recordingAction)
|
|
{
|
|
if (recordingAction.contextReplacer)
|
|
return "has-context-replacer";
|
|
|
|
switch (recordingAction.name) {
|
|
case "arc":
|
|
case "arcTo":
|
|
return "arc";
|
|
|
|
case "globalAlpha":
|
|
case "globalCompositeOperation":
|
|
case "setAlpha":
|
|
case "setGlobalAlpha":
|
|
case "setCompositeOperation":
|
|
case "setGlobalCompositeOperation":
|
|
return "composite";
|
|
|
|
case "bezierCurveTo":
|
|
case "quadraticCurveTo":
|
|
return "curve";
|
|
|
|
case "clearRect":
|
|
case "fill":
|
|
case "fillRect":
|
|
case "fillText":
|
|
return "fill";
|
|
|
|
case "createImageData":
|
|
case "drawFocusIfNeeded":
|
|
case "drawImage":
|
|
case "drawImageFromRect":
|
|
case "filter":
|
|
case "getImageData":
|
|
case "imageSmoothingEnabled":
|
|
case "imageSmoothingQuality":
|
|
case "putImageData":
|
|
case "transferFromImageBitmap":
|
|
case "webkitImageSmoothingEnabled":
|
|
return "image";
|
|
|
|
case "getLineDash":
|
|
case "lineCap":
|
|
case "lineDashOffset":
|
|
case "lineJoin":
|
|
case "lineWidth":
|
|
case "miterLimit":
|
|
case "setLineCap":
|
|
case "setLineDash":
|
|
case "setLineJoin":
|
|
case "setLineWidth":
|
|
case "setMiterLimit":
|
|
case "webkitLineDash":
|
|
case "webkitLineDashOffset":
|
|
return "line-style";
|
|
|
|
case "closePath":
|
|
case "lineTo":
|
|
return "line-to";
|
|
|
|
case "beginPath":
|
|
case "moveTo":
|
|
return "move-to";
|
|
|
|
case "isPointInPath":
|
|
return "point-in-path";
|
|
|
|
case "isPointInStroke":
|
|
return "point-in-stroke";
|
|
|
|
case "clearShadow":
|
|
case "setShadow":
|
|
case "shadowBlur":
|
|
case "shadowColor":
|
|
case "shadowOffsetX":
|
|
case "shadowOffsetY":
|
|
return "shadow";
|
|
|
|
case "createConicGradient":
|
|
case "createLinearGradient":
|
|
case "createPattern":
|
|
case "createRadialGradient":
|
|
case "fillStyle":
|
|
case "setFillColor":
|
|
case "setStrokeColor":
|
|
case "strokeStyle":
|
|
return "style";
|
|
|
|
case "stroke":
|
|
case "strokeRect":
|
|
case "strokeText":
|
|
return "stroke";
|
|
|
|
case "direction":
|
|
case "font":
|
|
case "measureText":
|
|
case "textAlign":
|
|
case "textBaseline":
|
|
return "text";
|
|
|
|
case "getTransform":
|
|
case "resetTransform":
|
|
case "rotate":
|
|
case "scale":
|
|
case "setTransform":
|
|
case "transform":
|
|
case "translate":
|
|
return "transform";
|
|
|
|
case "clip":
|
|
case "ellipse":
|
|
case "rect":
|
|
case "restore":
|
|
case "save":
|
|
return recordingAction.name;
|
|
}
|
|
|
|
return "name-unknown";
|
|
}
|
|
|
|
// Public
|
|
|
|
get index() { return this._index; }
|
|
|
|
get filterableData()
|
|
{
|
|
let text = [];
|
|
|
|
function getText(stringOrElement) {
|
|
if (typeof stringOrElement === "string")
|
|
text.push(stringOrElement);
|
|
else if (stringOrElement instanceof Node)
|
|
text.push(stringOrElement.textContent);
|
|
}
|
|
|
|
getText(this._mainTitleElement || this._mainTitle);
|
|
getText(this._subtitleElement || this._subtitle);
|
|
|
|
return {text};
|
|
}
|
|
|
|
// Protected
|
|
|
|
customTitleTooltip()
|
|
{
|
|
return this._copyText;
|
|
}
|
|
|
|
onattach()
|
|
{
|
|
super.onattach();
|
|
|
|
this.element.dataset.index = this._index.toLocaleString();
|
|
|
|
if (this.representedObject.valid && this.representedObject.warning) {
|
|
this.addClassName("warning");
|
|
this.status = WI.ImageUtilities.useSVGSymbol("Images/Warning.svg", "warning", this.representedObject.warning);
|
|
}
|
|
}
|
|
|
|
populateContextMenu(contextMenu, event)
|
|
{
|
|
contextMenu.appendItem(WI.UIString("Copy Action"), () => {
|
|
InspectorFrontendHost.copyText("context." + this._copyText + ";");
|
|
});
|
|
|
|
contextMenu.appendSeparator();
|
|
|
|
let sourceCodeLocation = this.representedObject.stackTrace?.firstNonNativeNonAnonymousNotBlackboxedCallFrame;
|
|
if (sourceCodeLocation) {
|
|
contextMenu.appendItem(WI.UIString("Reveal in Sources Tab"), () => {
|
|
WI.showSourceCodeLocation(sourceCodeLocation, {
|
|
ignoreNetworkTab: true,
|
|
ignoreSearchTab: true,
|
|
initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
|
|
});
|
|
});
|
|
|
|
contextMenu.appendSeparator();
|
|
}
|
|
|
|
super.populateContextMenu(contextMenu, event);
|
|
}
|
|
|
|
// Private
|
|
|
|
_handleValidityChanged(event)
|
|
{
|
|
this.addClassName("invalid");
|
|
}
|
|
};
|