Closed Bug 1801789 Opened 3 years ago Closed 3 years ago

JS1k demo doesnt draw anything on the screen (Chrome does)

Categories

(Core :: Graphics: Canvas2D, defect)

defect

Tracking

()

RESOLVED INVALID
Tracking Status
firefox-esr102 --- wontfix
firefox107 --- wontfix
firefox108 --- wontfix
firefox109 --- wontfix

People

(Reporter: mayankleoboy1, Unassigned)

References

(Regression)

Details

(Keywords: regression)

Go to https://js1k.com/2018-coins/demo/3209

ER: demo
AR: only a purple-ish background is shown. Nothing else draws.

Uncaught TypeError: c.r is not a function
y https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript line 2 > eval:1
<anonymous> https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript line 2 > eval:1
b https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript line 2 > eval:1
<anonymous> https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript line 2 > eval:1
<anonymous> https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript:2
reload https://js1k.com/2018-coins/demo/3209:113
<anonymous> https://js1k.com/2018-coins/demo/3209:115
3209 line 113 > injectedScript line 2 > eval:1:531
y https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript line 2 > eval:1
<anonymous> https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript line 2 > eval:1
forEach self-hosted:203
b https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript line 2 > eval:1
<anonymous> https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript line 2 > eval:1
<anonymous> https://js1k.com/2018-coins/demo/3209 line 113 > injectedScript:2
reload https://js1k.com/2018-coins/demo/3209:113
<anonymous> https://js1k.com/2018-coins/demo/3209:115

Regression2 : Nothing is drawn on the screen.

2022-11-22T11:36:08.992000: DEBUG : Found commit message:
Bug 1728999 - Add simple WPT testcases for the CanvasRenderingContext2D.direction property. r=lsalzman

And remove failure annotations for a couple of existing tests that we now pass.

Depends on D142701

Differential Revision: https://phabricator.services.mozilla.com/D142702

2022-11-22T11:36:08.992000: DEBUG : Did not find a branch, checking all integration branches
2022-11-22T11:36:08.992000: INFO : The bisection is done.
2022-11-22T11:36:09.002000: INFO : Stopped

Component: General → Graphics: Canvas2D
Product: Firefox → Core
Regressed by: 1728999

Set release status flags based on info from the regressing bug 1728999

:jfkthame, since you are the author of the regressor, bug 1728999, could you take a look? Also, could you set the severity field?

For more information, please visit auto_nag documentation.

Flags: needinfo?(jfkthame)

Re: "Regression2 : Nothing is drawn on the screen."

Uncaught TypeError: c.r is not a function

This is clearly what stops the drawing. But it's very strange; I don't know what the demo is doing that would be affected by bug 1728999.

It seems that the (bizarre, compressed) JS is setting up a bunch of one- and two-letter aliases for canvas2d methods/attributes, and for some reason after bug 1728999 it ends up with .r being an alias for .direction, whereas before it was an alias for .stroke().

If I simply comment out the webidl declaration of the direction attribute in CanvasTextDrawingStyles, and make no other changes, the regression is fixed.

So apparently, even though the example doesn't (as far as I can see) try to make use of .direction in any way, the act of exposing it on the canvas2d context somehow affects the "decoding" of the compressed/obfuscated code. Maybe it embodies some fragile assumptions about the properties that it expects to exist on the object? I'm afraid deciphering this looks way above my limited JS knowledge...

Flags: needinfo?(jfkthame)

(In reply to Mayank Bansal from comment #1)

Regression1 : The translucent circle becomes opaque white:
https://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=02d875d4f7d769cd03be3175490377dc600355fe&tochange=cdd65df84242fa01e9b2c690fb9078d7fba46cb6

This is a result of bug 1627014 "Implement CanvasRenderingContext2D.createConicGradient", which I guess the demo is trying to use, but something isn't working right. Setting canvas.createConicGradient.enabled to false should help.

Ideally, we'd have a non-obfuscated testcase showing what kind of conic gradient we're mishandling here, assuming that's what is going on.

Using https://lelinhtinh.github.io/de4js/ , this is the unobfuscated code i get:

for (v in c) c[v[2] + [v[9]]] = c[v];
a.style.backgroundColor = "rgb(0, 0, 26)";
var E, l, I, S, C, O, A, M, w = a.width,
h = a.height,
e = w / 2,
t = h / 2;
T = new AudioContext, P = T.createOscillator(), P.connect(G = T.createGain()), G.connect(T.destination), P.start(), G.gain.value = 0.1;
function y(b, d, f, g, k, m, n, q, s, u) {
4 > s && (c.g(), c.strokeStyle = "rgb(140, 26, 255)", c.lineWidth = 1, c.moveTo(b, d), 2 < s && (c.strokeStyle = "rgb(255, 102, 254)"), (2 == s || 0) && (n /= 5, q /= 5), 0 == s && (c.strokeStyle = "rgb(255, 255, 254)"), c.aC(f, g, k, m), u && (c.strokeStyle = "rgb(255, 255, 254)", c.lineWidth = 4), c.r(), y(k, m, k + n, m + q, k + 2 * (n * Math.random()), m + 2 * (q * Math.random()), n, q, s + 1), y(k, m, k + n, m + q, k + 2 * (n * Math.random()), m + 2 * (q * Math.random()), n, q, s + 1))
}(function b() {
c.e(0, 0, w, h), E = [];
for (var d = 0; 25 > d; d++) A = 70 * Math.random() - 35, M = 70 * Math.random() - 35, E.push([e + A, t + M, e + A, t + M, A, M]);
C ? (S = E[0], P.frequency.value = 8500, y(e, t, O.pageX * Math.random(), O.pageY * Math.random(), O.pageX, O.pageY, S[4], S[5], 2, 1)) : P.frequency.value = 300, E.forEach(function (g) {
y(e, t, g[0], g[1], g[2] + g[4] * Math.random(), g[3] + g[5] * Math.random(), g[4], g[5], 0)
}), setTimeout(b, 150), W(e, t, 5, e, t, 25, c1 = "rgb(255, 255, 255)", c2 = "rgba(255, 25, 255,0.7)", 25), C ? W(O.pageX, O.pageY, 10, O.pageX, O.pageY, 55, c1 = "rgb(255, 255, 255)", c2 = "rgba(255, 25, 255,0.1)", 250) : W(e, t, 5, e, t, 250, c1 = "rgba(0, 0, 5,0.1)", c2 = "rgba(255, 255, 255,0.2)", 250)
})();
function W(b, d, f, g, k, m, n, q, s) {
I = c.ei(b, d, f, g, k, m), I.addColorStop(0, n), I.addColorStop(1, q), c.fillStyle = I, c.g(), c.arc(e, t, s, 0, 2 * Math.PI, false), c.fill()
}
a.onmousemove = b => {
O = b, C = c.PP(b.pageX, b.pageY)
};

Huh, I think both "regressions" here are actually the same kind of underlying issue. The "Regression1 : The translucent circle becomes opaque white" occurs because when createConicGradient is enabled, the demo ends up calling that function instead of its intended createRadialGradient (and of course the parameters it passes are not at all sensible for that).

Again, this call is happening through a shorthand "alias" that has apparently been defined by the obfuscated JS here, and when we expose the additional API createConicGradient, the alias accidentally ends up referring to that instead of the expected method. I'm guessing there's some kind of assumption about the properties present on the canvas2d context, and they're being indexed instead of looked-up by name, or something like that.

So I'm not sure this is actually a Firefox regression at all; I think it may simply be a script that makes too many fragile assumptions about the APIs.

This looks like the problem:

for (v in c) c[v[2] + [v[9]]] = c[v];

If I'm understanding correctly, this creates the "aliases" for all the properties of the canvas2d context, using character 3 and character 10 (if present) of the property name as the abbreviation.

So "stroke" is abbreviated as r.

But so is "direction", if it exists.

Similarly, "createRadialGradient" gets abbreviated as ei, but so does "createConicGradient".

Severity: -- → S3

I believe this is INVALID; the problem arises because the demo script creates abbreviated "aliases" of all the canvas2d attributes/methods in an unreliable way (comment 8).

Running

for (i in document.createElement("canvas").getContext("2d")) console.log(i[2] + [i[9]] + " => " + i)

generates a list of all the "abbreviations" that the demo script will end up creating, and comparing the results of this across Safari, Chrome and Firefox we can see that the exact content of the list, and the order in which they're generated, varies considerably:

Safari:                             Chrome:                         Firefox:
n => canvas                         n => canvas                     a => drawImage
bk => webkitBackingStorePixelRatio  oh => globalAlpha               g => beginPath
bg => webkitImageSmoothingEnabled   op => globalCompositeOperation  l => fill
be => webkitLineDash                l => filter                     r => stroke
be => webkitLineDashOffset          at => imageSmoothingEnabled     i => clip
oh => globalAlpha                   at => imageSmoothingQuality     PP => isPointInPath
op => globalCompositeOperation      rl => strokeStyle               PS => isPointInStroke
rl => strokeStyle                   l => fillStyle                  ee => createLinearGradient
l => fillStyle                      as => shadowOffsetX             ei => createRadialGradient
at => imageSmoothingEnabled         as => shadowOffsetY             ei => createConicGradient
at => imageSmoothingQuality         ar => shadowBlur                et => createPattern
n => lineWidth                      ao => shadowColor               eg => createImageData
n => lineCap                        n => lineWidth                  ta => getImageData
n => lineJoin                       n => lineCap                    ta => putImageData
tt => miterLimit                    n => lineJoin                   ts => setLineDash
nf => lineDashOffset                tt => miterLimit                ts => getLineDash
as => shadowOffsetX                 nf => lineDashOffset            o => closePath
as => shadowOffsetY                 n => font                       v => moveTo
ar => shadowBlur                    x => textAlign                  n => lineTo
ao => shadowColor                   xi => textBaseline              aC => quadraticCurveTo
n => font                           r => direction                  zv => bezierCurveTo
x => textAlign                      nn => fontKerning               c => arcTo
xi => textBaseline                  nc => fontStretch               c => rect
r => direction                      nn => fontVariantCaps           c => arc
tt => getContextAttributes          tc => letterSpacing             l => ellipse
t => setAlpha                       xr => textRendering             e => clearRect
ti => setCompositeOperation         rn => wordSpacing               l => fillRect
aF => drawImageFromRect             i => clip                       rt => strokeRect
tC => setStrokeColor                ei => createConicGradient       v => save
tl => setFillColor                  eg => createImageData           s => restore
td => setLineWidth                  ee => createLinearGradient      l => fillText
tp => setLineCap                    et => createPattern             rt => strokeText
ti => setLineJoin                   ei => createRadialGradient      ax => measureText
ti => setMiterLimit                 aI => drawFocusIfNeeded         a => scale
t => setShadow                      a => drawImage                  t => rotate
eo => clearShadow                   l => fill                       a => translate
a => drawImage                      l => fillText                   a => transform
g => beginPath                      tt => getContextAttributes      to => getTransform
l => fill                           ta => getImageData              to => setTransform
r => stroke                         ts => getLineDash               ss => resetTransform
i => clip                           to => getTransform              aI => drawFocusIfNeeded
PP => isPointInPath                 CL => isContextLost             n => canvas
PS => isPointInStroke               PP => isPointInPath             zm => mozImageSmoothingEnabled
ee => createLinearGradient          PS => isPointInStroke           oh => globalAlpha
ei => createRadialGradient          ax => measureText               op => globalCompositeOperation
ei => createConicGradient           ta => putImageData              rl => strokeStyle
et => createPattern                 s => reset                      l => fillStyle
eg => createImageData               u => roundRect                  l => filter
ta => getImageData                  v => save                       at => imageSmoothingEnabled
ta => putImageData                  a => scale                      n => lineWidth
o => closePath                      ts => setLineDash               n => lineCap
v => moveTo                         to => setTransform              n => lineJoin
n => lineTo                         r => stroke                     tt => miterLimit
aC => quadraticCurveTo              rt => strokeText                nf => lineDashOffset
zv => bezierCurveTo                 a => transform                  as => shadowOffsetX
c => arcTo                          a => translate                  as => shadowOffsetY
c => rect                           c => arc                        ar => shadowBlur
c => arc                            c => arcTo                      ao => shadowColor
l => ellipse                        g => beginPath                  n => font
ts => setLineDash                   zv => bezierCurveTo             x => textAlign
ts => getLineDash                   e => clearRect                  xi => textBaseline
e => clearRect                      o => closePath                  r => direction
l => fillRect                       l => ellipse                    nn => fontKerning
rt => strokeRect                    l => fillRect
v => save                           n => lineTo
s => restore                        v => moveTo
l => fillText                       aC => quadraticCurveTo
rt => strokeText                    c => rect
ax => measureText                   ss => resetTransform
a => scale                          s => restore
t => rotate                         t => rotate
a => translate                      rt => strokeRect
a => transform
to => getTransform
to => setTransform
ss => resetTransform
aI => drawFocusIfNeeded

Note that in all cases, there are a number of duplicates, and the order in which they occur varies. I can't find any spec that requires a particular ordering here; it seems to be quite arbitrary.

So it seems to me like this was a "neat hack" for a minimized demo that happened to work for the symbols the script actually uses, but has always been at risk of breaking any time the canvas API is updated; and indeed, adding createConicGradient in Firefox caused the abbreviation for createRadialGradient to be overwritten, and then adding direction caused the abbreviation for stroke to be overwritten.

At some point, as APIs get updated, it'll probably break in other browsers as well.

Status: NEW → RESOLVED
Closed: 3 years ago
Resolution: --- → INVALID
See Also: → 1801805, 1801792
You need to log in before you can comment on or make changes to this bug.