Page MenuHomePhabricator

mw.util.parseImageUrl() returns invalid thumb URLs for images where original size is under requested width
Open, Needs TriagePublic

Description

It is not valid to request a thumbnail width beyond the orignal's width, for bitmap images.

If you ask thumb.php?f=Example.jpg&w=1280 for a local upload where Example.jpg is less than 1280px wide, it responds HTTP 400 Bad Request, with the message:

Error creating thumbnail: Image was not scaled, is the requested width bigger than the source?

This hasn't been an issue historically because all URLs and images were generated server-side:

  • via wikitext [[File:]] syntax,
  • via the imageinfo API,
  • via Special:Filepath?file=&width= (which redirects to either a thumb or the original),
  • via thumb.php?f=&w= (internal; which allows/denies accordingly, caller responsibility)

With the introduction of mw.util.parseImageUrl() we are supposing that a client can do this. Due to a bug in Thumbor this sometimes works albeit in an inefficient manner (T411076), and is probably why this was able to take off despite being incompatible with MediaWiki design requirements.

Impact

When viewing an article with an image in it, where the original is relatively small, features like Popups or Mediaviewer will serve a broken or inflated thumbnail. Any other extension or gadget calling mw.util.parseImageUrl() is also impacted.

In MediaWiki by default, these are deterministically broken, including in local development. In production, they sometimes work, although with inflated bandwidth consumption (server-side upscale instead of browser-side rendering).

Ref: T414338

Event Timeline

One solution might be to accept the latency hit and always use Special:Filepath (Special:Redirect/file) in this JavaScript function.

If we take that route, we'll need to ensure that route is highly cachable, including for logged-in users. Alternatively, we might be able to find a fast-path for cases where 1) we can pass the max width from an HTML attribute, and 2) when config.GenerateThumbnailOnParse is true. Although it might be easier for T414338 if we reduce this pattern rather than encourage it further.

/**
 * @typedef {Object} ResizeableThumbnailUrl
...
 * @property {function(number):string} [resizeUrl] A function that takes a width
 *   parameter and returns a thumbnail URL (URL-encoded) with that width. The width
     parameter must be smaller than the width of the original image

I guess we could make it more explicit by making the original width a required parameter to mw.util.parseImageUrl(), but then the method would have to know which file types can be scaled beyond the original size.