Skip to content
Permalink
Browse files
Web Inspector: show warning when recorded Canvas action caused no vis…
…ual change

https://bugs.webkit.org/show_bug.cgi?id=175282

Reviewed by Joseph Pecoraro.

* Localizations/en.lproj/localizedStrings.js:

* UserInterface/Models/RecordingAction.js:
(WI.RecordingAction):
(WI.RecordingAction.prototype.get valid):
(WI.RecordingAction.prototype.get hasVisibleEffect):
(WI.RecordingAction.prototype.markInvalid):
(WI.RecordingAction.prototype.apply):
(WI.RecordingAction.prototype.async._swizzle):
If the selected action is visual, save a copy of the preview canvas' dataURL before
applying the action and compare it to its dataURL after. If there is no difference, the
action had no visual effect.

* UserInterface/Models/Recording.js:
(WI.Recording):

* UserInterface/Views/RecordingActionTreeElement.js:
(WI.RecordingActionTreeElement):
(WI.RecordingActionTreeElement.prototype._handleValidityChanged):
(WI.RecordingActionTreeElement.prototype._handleHasVisibleEffectChanged):
* UserInterface/Views/RecordingActionTreeElement.css:
(.item.action.visual.no-visible-effect:not(.invalid) > .status > .warning):

* UserInterface/Views/RecordingContentView.js:
(WI.RecordingContentView):
(WI.RecordingContentView.prototype.async._generateContentCanvas2D):
(WI.RecordingContentView.prototype._applyAction): Deleted.


Canonical link: https://commits.webkit.org/194542@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223335 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
dcrousso committed Oct 16, 2017
1 parent 70fe920 commit bc0213f3516a6e55b75fe64976f288318e18c14c
@@ -1,3 +1,38 @@
2017-10-15 Devin Rousso <webkit@devinrousso.com>

Web Inspector: show warning when recorded Canvas action caused no visual change
https://bugs.webkit.org/show_bug.cgi?id=175282

Reviewed by Joseph Pecoraro.

* Localizations/en.lproj/localizedStrings.js:

* UserInterface/Models/RecordingAction.js:
(WI.RecordingAction):
(WI.RecordingAction.prototype.get valid):
(WI.RecordingAction.prototype.get hasVisibleEffect):
(WI.RecordingAction.prototype.markInvalid):
(WI.RecordingAction.prototype.apply):
(WI.RecordingAction.prototype.async._swizzle):
If the selected action is visual, save a copy of the preview canvas' dataURL before
applying the action and compare it to its dataURL after. If there is no difference, the
action had no visual effect.

* UserInterface/Models/Recording.js:
(WI.Recording):

* UserInterface/Views/RecordingActionTreeElement.js:
(WI.RecordingActionTreeElement):
(WI.RecordingActionTreeElement.prototype._handleValidityChanged):
(WI.RecordingActionTreeElement.prototype._handleHasVisibleEffectChanged):
* UserInterface/Views/RecordingActionTreeElement.css:
(.item.action.visual.no-visible-effect:not(.invalid) > .status > .warning):

* UserInterface/Views/RecordingContentView.js:
(WI.RecordingContentView):
(WI.RecordingContentView.prototype.async._generateContentCanvas2D):
(WI.RecordingContentView.prototype._applyAction): Deleted.

2017-10-15 Nikita Vasilyev <nvasilyev@apple.com>

Web Inspector: [PARITY] Styles Redesign: clicking on the go-to arrow in Computed tab should work
@@ -916,6 +916,7 @@ localizedStrings["The “%s”\ntable is empty."] = "The “%s”\ntable is empt
localizedStrings["The “webkit” prefix is needed for this property.\nClick to insert a duplicate with the prefix."] = "The “webkit” prefix is needed for this property.\nClick to insert a duplicate with the prefix.";
localizedStrings["The “webkit” prefix is not necessary.\nClick to insert a duplicate without the prefix."] = "The “webkit” prefix is not necessary.\nClick to insert a duplicate without the prefix.";
localizedStrings["This Element"] = "This Element";
localizedStrings["This action causes no visual change"] = "This action causes no visual change";
localizedStrings["This object is a root"] = "This object is a root";
localizedStrings["This object is referenced by internal objects"] = "This object is referenced by internal objects";
localizedStrings["This property needs a value.\nClick to open autocomplete."] = "This property needs a value.\nClick to open autocomplete.";
@@ -53,9 +53,9 @@ WI.Recording = class Recording
let validName = action.name in prototype;
let validFunction = !action.isFunction || typeof prototype[action.name] === "function";
if (!validName || !validFunction) {
action.valid = false;
action.markInvalid();

WI.Recording.synthesizeError(WI.UIString("“%s” is invalid.").format(action.name));
WI.Recording.synthesizeError(WI.UIString("“%s” is invalid.").format(this._name));
}
}
}
@@ -23,10 +23,12 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/

WI.RecordingAction = class RecordingAction
WI.RecordingAction = class RecordingAction extends WI.Object
{
constructor(name, parameters, swizzleTypes, trace, snapshot)
{
super();

this._payloadName = name;
this._payloadParameters = parameters;
this._payloadSwizzleTypes = swizzleTypes;
@@ -44,6 +46,7 @@ WI.RecordingAction = class RecordingAction
this._isFunction = false;
this._isGetter = false;
this._isVisual = false;
this._hasVisibleEffect = undefined;
this._stateModifiers = new Set;
}

@@ -89,22 +92,62 @@ WI.RecordingAction = class RecordingAction
get swizzleTypes() { return this._payloadSwizzleTypes; }
get trace() { return this._trace; }
get snapshot() { return this._snapshot; }

get valid() { return this._valid; }
set valid(valid) { this._valid = !!valid; }

get isFunction() { return this._isFunction; }
get isGetter() { return this._isGetter; }
get isVisual() { return this._isVisual; }
get hasVisibleEffect() { return this._hasVisibleEffect; }
get stateModifiers() { return this._stateModifiers; }

markInvalid()
{
let wasValid = this._valid;
this._valid = false;

if (wasValid)
this.dispatchEventToListeners(WI.RecordingAction.Event.ValidityChanged);
}

swizzle(recording)
{
if (!this._swizzledPromise)
this._swizzledPromise = this._swizzle(recording);
return this._swizzledPromise;
}

apply(context, options = {})
{
if (!this.valid)
return;

let contentBefore = null;
let shouldCheckForChange = this._isVisual && this._hasVisibleEffect === undefined;
if (shouldCheckForChange)
contentBefore = context.canvas.toDataURL();

try {
let name = options.nameOverride || this._name;
if (this.isFunction)
context[name](...this._parameters);
else {
if (this.isGetter)
context[name];
else
context[name] = this._parameters[0];
}

if (shouldCheckForChange) {
this._hasVisibleEffect = contentBefore !== context.canvas.toDataURL();
if (!this._hasVisibleEffect)
this.dispatchEventToListeners(WI.RecordingAction.Event.HasVisibleEffectChanged);
}
} catch {
this.markInvalid();

WI.Recording.synthesizeError(WI.UIString("“%s” threw an error.").format(this._name));
}
}

toJSON()
{
let json = [this._payloadName, this._payloadParameters, this._payloadSwizzleTypes, this._payloadTrace];
@@ -150,8 +193,12 @@ WI.RecordingAction = class RecordingAction
if (this._payloadSnapshot >= 0)
this._snapshot = snapshot;

if (this._valid)
this._valid = this._parameters.every((parameter) => parameter !== undefined) && this._payloadSwizzleTypes.every((swizzleType) => swizzleType !== WI.Recording.Swizzle.None);
if (this._valid) {
let parametersSpecified = this._parameters.every((parameter) => parameter !== undefined);
let parametersCanBeSwizzled = this._payloadSwizzleTypes.every((swizzleType) => swizzleType !== WI.Recording.Swizzle.None);
if (!parametersSpecified || !parametersCanBeSwizzled)
this.markInvalid();
}

this._isFunction = WI.RecordingAction.isFunctionForType(recording.type, this._name);
this._isGetter = !this._isFunction && !this._parameters.length;
@@ -192,6 +239,11 @@ WI.RecordingAction = class RecordingAction
}
};

WI.RecordingAction.Event = {
ValidityChanged: "recording-action-marked-invalid",
HasVisibleEffectChanged: "recording-action-has-visible-effect-changed",
};

WI.RecordingAction._functionNames = {
[WI.Recording.Type.Canvas2D]: new Set([
"arc",
@@ -217,3 +217,9 @@ body[dir=rtl] .tree-outline:not(.hide-disclosure-buttons) .item.action:not(.init
height: 12px;
content: url(../Images/Error.svg);
}

.item.action.visual.no-visible-effect:not(.invalid) > .status > .warning {
width: 12px;
height: 12px;
margin-top: 2px;
}
@@ -37,6 +37,9 @@ WI.RecordingActionTreeElement = class RecordingActionTreeElement extends WI.Gene

this._index = index;
this._copyText = copyText;

this.representedObject.addEventListener(WI.RecordingAction.Event.ValidityChanged, this._handleValidityChanged, this);
this.representedObject.addEventListener(WI.RecordingAction.Event.HasVisibleEffectChanged, this._handleHasVisibleEffectChanged, this);
}

// Static
@@ -390,6 +393,21 @@ WI.RecordingActionTreeElement = class RecordingActionTreeElement extends WI.Gene

super.populateContextMenu(contextMenu, event);
}

// Private

_handleValidityChanged(event)
{
this.addClassName("invalid");
}

_handleHasVisibleEffectChanged(event)
{
this.addClassName("no-visible-effect");

this.status = useSVGSymbol("Images/Warning.svg", "warning");
this.status.title = WI.UIString("This action causes no visual change");
}
};

WI.RecordingActionTreeElement._memoizedActionClassNames = new Map;
@@ -56,6 +56,9 @@ WI.RecordingContentView = class RecordingContentView extends WI.ContentView

this._previewContainer = this.element.appendChild(document.createElement("div"));
this._previewContainer.classList.add("preview-container");

this._messageElement = this.element.appendChild(document.createElement("div"));
this._messageElement.classList.add("message");
}

// Static
@@ -238,7 +241,7 @@ WI.RecordingContentView = class RecordingContentView extends WI.ContentView
--saveCount;
}

this._applyAction(snapshot.context, actions[i]);
actions[i].apply(snapshot.context);

if (shouldDrawCanvasPath && i >= indexOfLastBeginPathAction && WI.RecordingContentView._actionModifiesPath(actions[i])) {
lastPathPoint = {x: this._pathContext.currentX, y: this._pathContext.currentY};
@@ -259,7 +262,7 @@ WI.RecordingContentView = class RecordingContentView extends WI.ContentView
this._pathContext.lineTo(subPathStartPoint.x, subPathStartPoint.y);
subPathStartPoint = {};
} else {
this._applyAction(this._pathContext, actions[i], {nameOverride: isMoveTo ? "lineTo" : null});
actions[i].apply(this._pathContext, {nameOverride: isMoveTo ? "lineTo" : null});
if (isMoveTo)
subPathStartPoint = {x: this._pathContext.currentX, y: this._pathContext.currentY};
}
@@ -393,6 +396,7 @@ WI.RecordingContentView = class RecordingContentView extends WI.ContentView
}

this._previewContainer.removeChildren();
this._messageElement.removeChildren();

if (showCanvasPath) {
indexOfLastBeginPathAction = this._index;
@@ -455,28 +459,6 @@ WI.RecordingContentView = class RecordingContentView extends WI.ContentView
options.actionCompletedCallback(actions[this._index]);
}

_applyAction(context, action, options = {})
{
if (!action.valid)
return;

try {
let name = options.nameOverride || action.name;
if (action.isFunction)
context[name](...action.parameters);
else {
if (action.isGetter)
context[name];
else
context[name] = action.parameters[0];
}
} catch (e) {
WI.Recording.synthesizeError(WI.UIString("“%s” threw an error.").format(action.name));

action.valid = false;
}
}

_updateCanvasPath()
{
let activated = WI.settings.showCanvasPath.value;

0 comments on commit bc0213f

Please sign in to comment.