blob: 7f7f3b35be364d98b35052848245f2125f592994 [file] [log] [blame]
/*
* Copyright (C) 2018 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.RecordingState = class RecordingState
{
constructor(data, {source} = {})
{
this._data = data;
this._source = source || null;
}
// Static
static fromContext(type, context, options = {})
{
if (type !== WI.Recording.Type.Canvas2D)
return null;
let matrix = context.getTransform();
let data = {};
if (WI.ImageUtilities.supportsCanvasPathDebugging()) {
data.currentX = context.currentX;
data.currentY = context.currentY;
}
data.direction = context.direction;
data.fillStyle = context.fillStyle;
data.font = context.font;
data.globalAlpha = context.globalAlpha;
data.globalCompositeOperation = context.globalCompositeOperation;
data.imageSmoothingEnabled = context.imageSmoothingEnabled;
data.imageSmoothingQuality = context.imageSmoothingQuality;
data.lineCap = context.lineCap;
data.lineDash = context.getLineDash();
data.lineDashOffset = context.lineDashOffset;
data.lineJoin = context.lineJoin;
data.lineWidth = context.lineWidth;
data.miterLimit = context.miterLimit;
data.shadowBlur = context.shadowBlur;
data.shadowColor = context.shadowColor;
data.shadowOffsetX = context.shadowOffsetX;
data.shadowOffsetY = context.shadowOffsetY;
data.strokeStyle = context.strokeStyle;
data.textAlign = context.textAlign;
data.textBaseline = context.textBaseline;
data.transform = [matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f];
data.webkitImageSmoothingEnabled = context.webkitImageSmoothingEnabled;
data.webkitLineDash = context.webkitLineDash;
data.webkitLineDashOffset = context.webkitLineDashOffset;
if (WI.ImageUtilities.supportsCanvasPathDebugging())
data.setPath = [context.getPath()];
return new WI.RecordingState(data, options);
}
static async swizzleInitialState(recording, initialState)
{
if (recording.type === WI.Recording.Type.Canvas2D) {
let swizzledState = {};
for (let [name, value] of Object.entries(initialState)) {
// COMPATIBILITY (iOS 12.0): Recording.InitialState.states did not exist yet
let nameIndex = parseInt(name);
if (!isNaN(nameIndex))
name = await recording.swizzle(nameIndex, WI.Recording.Swizzle.String);
switch (name) {
case "setTransform":
value = [await recording.swizzle(value, WI.Recording.Swizzle.DOMMatrix)];
break;
case "fillStyle":
case "strokeStyle":
var [gradient, pattern, string] = await Promise.all([
recording.swizzle(value, WI.Recording.Swizzle.CanvasGradient),
recording.swizzle(value, WI.Recording.Swizzle.CanvasPattern),
recording.swizzle(value, WI.Recording.Swizzle.String),
]);
if (gradient && !pattern)
value = gradient;
else if (pattern && !gradient)
value = pattern;
else
value = string;
break;
case "direction":
case "font":
case "globalCompositeOperation":
case "imageSmoothingQuality":
case "lineCap":
case "lineJoin":
case "shadowColor":
case "textAlign":
case "textBaseline":
value = await recording.swizzle(value, WI.Recording.Swizzle.String);
break;
case "globalAlpha":
case "lineWidth":
case "miterLimit":
case "shadowOffsetX":
case "shadowOffsetY":
case "shadowBlur":
case "lineDashOffset":
value = await recording.swizzle(value, WI.Recording.Swizzle.Number);
break;
case "setPath":
value = [await recording.swizzle(value[0], WI.Recording.Swizzle.Path2D)];
break;
}
if (value === undefined || (Array.isArray(value) && value.includes(undefined)))
continue;
swizzledState[name] = value;
}
return new WI.RecordingState(swizzledState);
}
return null;
}
// Public
get source() { return this._source; }
has(name)
{
return name in this._data;
}
get(name)
{
return this._data[name];
}
apply(type, context)
{
for (let [name, value] of this) {
if (!(name in context))
continue;
// Skip internal state used for path debugging.
if (name === "currentX" || name === "currentY")
continue;
try {
if (WI.RecordingAction.isFunctionForType(type, name))
context[name](...value);
else
context[name] = value;
} catch { }
}
}
toJSON()
{
return this._data;
}
[Symbol.iterator]()
{
return Object.entries(this._data)[Symbol.iterator]();
}
};