[HiDPI] nytimes.com Election 2012 map is too big on Retina display

RESOLVED WONTFIX

Status

Tech Evangelism
Desktop
RESOLVED WONTFIX
5 years ago
3 years ago

People

(Reporter: cpeterson, Unassigned)

Tracking

(Blocks: 1 bug, {regression})

unspecified
x86
Mac OS X
regression
Dependency tree / graph

Firefox Tracking Flags

(firefox17 unaffected, firefox18 affected, firefox19 affected)

Details

(URL)

Attachments

(4 attachments)

(Reporter)

Description

5 years ago
Created attachment 679045 [details]
2012-09-29_screenshot.png

Starting in Nightly 19.0a1 2012-09-29 build, the nytimes.com Election 2012 is too big:

http://elections.nytimes.com/2012/results/president

If I change the gfx.hidpi.enabled pref from 2 to 0 and restart Firefox, then the map is the correct size. The map looks correct on Safari and Chrome.

Here is the pushlog from build 09-28 to 09-29:
https://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=895f66c4eada&tochange=85f561c755f6

Please see the attached screenshots.
(Reporter)

Comment 1

5 years ago
Created attachment 679046 [details]
2012-09-28_screenshot.png
Weird. If you zoom the page (either larger or smaller - Cmd-= or Cmd--) and then hit Reload, it displays fine. And if you then zoom back to default size, it's still fine... until you reload again. The problem only seems to occur when the page is at 100% zoom _at the time of loading_.
Clearly this is related to HiDPI support in some way (originally landed in bug 674373, but now tracking remaining issues in bug 785330); it's not yet clear to me whether it's a bug on our side, or a bad assumption somewhere in the NYT's code that is drawing the map, though.
Blocks: 785330
Further observation: if you disable HiDPI support (by setting gfx.hidpi.enabled to zero, and restart), you can still reproduce the problem by zooming in to 200% of the default scale (press Cmd-= six times in succession), then reload.
No matter what I suspect that we need to support whatever code patterns the nytimes site is doing. Us switching to a higher resolution rendering should not be visible to websites.
For the most part, that's true. But in this case the site is specifically wanting to know - looks like it uses window.devicePixelRatio to find out, so that it can render differently on retina vs non-retina displays.

What I don't understand is why the problem only occurs at one specific zoom level - seems like that's an anomaly either in our code or in the NYT code, but I haven't understood anywhere near enough of it to tell where.
Another weird thing about this bug is that it happens only once per FF session -- the first time you load this bug's URL.
For me it reproduces no matter how many times I reload. But only when the zoom level is 100% at the time when the page is loaded.

What I noticed is that changing the zoom level changes window.devicePixelRatio to non-integer values.

So I think that what's happening here is that the webpage at loadtime checks if window.devicePixelRatio == 2 and when that's the case it takes a separate code path. This codepath does not work in Firefox. If that code path is buggy, or if it's exposing a bug in Firefox I don't know.
(Following up comment #7)

> Another weird thing about this bug is that it happens only once per
> FF session -- the first time you load this bug's URL.

OK, I stand corrected.

I tried again with a fresh profile, and no longer see this.
> So I think that what's happening here is that the webpage at
> loadtime checks if window.devicePixelRatio == 2 and when that's the
> case it takes a separate code path. This codepath does not work in
> Firefox.

This does seem to be what's happening.	I confirmed it by adding
logging to nsGlobalWindow::GetDevicePixelRatio() and breaking on it in
gdb.  Then (inside gdb) I did 'bt' and 'call (void) DumpJSStack()'.

> If that code path is buggy, or if it's exposing a bug in Firefox I
> don't know.

It's very difficult to tell -- the NYT's JavaScript is heavily
obfuscated.  But it does contain the following code snippet:

D.devicePixelRatio=2==window.devicePixelRatio?2:1;
D.canvasPixelRatio=D.devicePixelRatio;

Because their JavaScript code *is* obfuscated, I'm very strongly
inclined to say this is their bug -- at least until new evidence shows
otherwise.
Created attachment 679424 [details]
Gzipped NYT JavaScript file (very large)
The map does work in Chrome though, even though chrome returns window.devicePixelRatio = 2
The big JavaScript file I uploaded in comment #11 does contain "-moz-..." and "-webkit-..." CSS properties.  So I suppose it must do some kind of browser sniffing.

But setting FF's user-agent string to Safari's (by setting general.useragent.override in about:config) doesn't make the bug go away.
This isn't a CSS bug, so it's unlikely caused by -moz- vs. -webkit- css prefixes.
That's not what I was saying.

I think the presence of these prefixes is evidence of browser sniffing.  Is it?

If the NYT site is browser sniffing (and using different code paths in different browsers), that (by itself) might explain this bug.

Note that current FF releases don't (yet) support HiDPI mode.  And it's quite unlikely the NYT's web developers have tested with the developer versions that do support it (and have this bug).
I don't think that just because they are doing sniffing in one place, that we should assume that the breakage here is due to sniffing someplace else. Virtually every major website does sniffing someplace.

Technically this could actually be a CSS issue though. It appears that the canvas here *does* render the whole map, but the canvas is way too big and ends up getting clipped. Viewing the DOM in the inspector shows that there's quite a few canvases here and many of them are 2x larger (in each dimention) than what is seen on screen.
FWIW, the NYT map is similarly broken (incorrectly scaled) on the current version of Opera on OS X with Retina display. In this case, however, it remains broken regardless of the zoom level.
(In reply to Jonas Sicking (:sicking) from comment #16)
> Technically this could actually be a CSS issue though. It appears that the
> canvas here *does* render the whole map, but the canvas is way too big and
> ends up getting clipped. Viewing the DOM in the inspector shows that there's
> quite a few canvases here and many of them are 2x larger (in each dimention)
> than what is seen on screen.

The site is trying to achieve "retina-resolution" canvas drawing when it's on a retina display by creating a canvas sized according to the number of device pixels (rather than CSS pixels) available in the window, and rendering the map to this. By then scaling this down to 50%, it should be possible to make the map appear fully sharp. But I guess they're not setting whatever CSS properties would be needed to achieve that scaling in the Gecko case.

So this bug raises several issues, I think. When they decide to render the "double-size" map, why isn't it then being scaled to the desired viewport by Gecko (and Opera), although it is by Safari? This could well be a CSS issue on their side - e.g. they're using some webkit-only stuff. I believe this technique of creating a double-sized canvas and then rendering it scaled to 50% *can* be used successfully in Gecko - the pdf.js guys have been experimenting with it to achieve sharp PDF rendering on retina.

Another question, though, is how window.devicePixelRatio is supposed to behave in relation to zoom. Apparently, in Safari it is *not* affected by zoom, but always returns 1.0 or 2.0 (non-retina vs retina), whereas our implementation varies with page zoom. The NYT page tests specifically for devicePixelRatio==2 as the condition for doing its double-size canvas stuff. That's why the problem only appears at 100% zoom on a retina display, and only at 200% zoom on a non-retina display.

To confirm that the problem is triggered by a devicePixelRatio of precisely 2.0, I hacked nsGlobalWindow::GetDevicePixelRatio by adding "if (*aRatio == 2.0) *aRatio = 2.0001;" before the return. As expected, this "fixes" the problem; the NYT site never tries to use the double-size canvas trick, and the map displays properly (although blurrier than desired).

According to http://www.quirksmode.org/blog/archives/2012/07/more_about_devi.html, "Turns out Opera's value depends on the zoom level", which sounds like it would match ours. However, I think that information is out of date; testing with the current Opera version (12.10), I see devicePixelRatio fixed at 2.0, regardless of zoom. That's why Opera has the NYT map scaling problem at all zoom levels, while we only have it at 100%.

Given the Safari behavior (devicePixelRatio is independent of page zoom), and the fact that Opera has apparently updated their implementation to match, I think we should probably do the same. I'll file a separate bug for that. Doing so will not "solve" the NYT map issue, of course; it'll just make it more consistent, ensuring that it occurs at zoom levels.

I'm guessing that the NYT problem actually needs to be fixed on their side, in their CSS/JS; the fact that it fails similarly in both Opera and Gecko suggests that they have some webkit-specific stuff in there, and didn't test sufficiently across other browsers.
Depends on: 809788
By adding "transform: scale(0.5, 0.5); transform-origin: 0px 0px 0px;" to all the double-sized canvases using the element inspector, I get the desired "sharp" and properly-scaled rendering of the map. (I had to force the window to refresh, by clicking on the map or zooming; but then as long as I don't reload the page, so my added properties stay on the elements, it behaves fine across zooming and other interactions.)
Better than using transform (which I guess would probably mess up rendering in Safari, as it's already scaling the canvas of its own accord), just setting height and width in the CSS of the canvas elements will solve the problem.
Created attachment 679623 [details]
screenshot showing inspection and CSS fixup of the NYT map

I think the attached picture (large, as it's a retina-display screenshot) indicates the source of the problem, and how it could be fixed on the NYT end.

Note in the element inspector how the page uses a <div> with "overflow: hidden; height: 550px; width: 852px;", and within this, it has a number of <canvas>es that have height="1100" width="1704". These are the "double-size" canvases it's using for hi-res drawing.

However, the original page did not have any CSS to control the displayed size of these canvases, so they are displayed at their intrinsic size of 1100x1704 CSS pixels, which means that only a quarter of them will be visible within the containing <div>.

In this screenshot, I've used the inspector to add "height: 100%; width: 100%;" to the style of each of these <canvas> elements. This forces them to be scaled on display to match the size of their containing <div>, and all's well.

I tried the same thing in Opera, with their Dragonfly tool, and it solves the map scaling problem there as well.

Do we have any webdev contacts at NYT who could look into this?
Oh, and the reason it displays at the proper scale in webkit is because the version served to webkit browsers adds "-webkit-transform: scale3d(0.5, 0.5, 1) translate3d(-50%, -50%, 0px);" to the style of all those double-sized canvas elements. For non-webkit browsers, they still create the double-sized canvas, but they don't provide any CSS to scale them down to the final desired size.

IMO, simply setting "width:100%; height:100%;" would be a much better approach.
Moving this to Tech Evangelism, as it's a problem with the NYT site's code.
Assignee: nobody → english-us
Component: Graphics → English US
Product: Core → Tech Evangelism
Version: 19 Branch → unspecified
I do not have a retina display, but I haven't found the previous properties and it is working for me.
Assignee: english-us → nobody
Status: NEW → RESOLVED
Last Resolved: 3 years ago
Component: English US → Desktop
Resolution: --- → WORKSFORME
(Reporter)

Comment 25

3 years ago
I have a Retina display and I can still reproduce this problem. But since this is apparently a bug on the dated website, we can probably close this report as WONTFIX.
Resolution: WORKSFORME → WONTFIX
You need to log in before you can comment on or make changes to this bug.