Skip to content

Load images that redirect - #26296

Merged
crazytonyli merged 3 commits into
trunkfrom
bugfix/cmm-2595-reader-thumbnail-redirects
Oct 30, 2026
Merged

crazytonyli merged 3 commits into
trunkfrom
bugfix/cmm-2595-reader-thumbnail-redirects

Conversation

@crazytonyli

Copy link
Copy Markdown
Contributor

Description

Fixes https://linear.app/a8c/issue/CMM-2595

To reproduce the issue: in Reader, open "Stop Paying Extra to Keep Your Site Safe" (WordPress.com Blog) and scroll to "More from WordPress.com Blog". The two thumbnails stay gray.

Their images are on en-blog.files.wordpress.com, which redirects to en.blog.wordpress.com/wp-content/uploads/.... downloadImage rejected any image whose final URL was not the requested one, so every redirected image failed, in Reader and anywhere else that uses it.

Why the URL check exists

a3c9668 (#15252, 2020) added the check so CollapsableHeaderCollectionViewCell can poll mShots previews. While mShots generates a screenshot it answers 307 to https://s0.wp.com/mshots/v1/default, a placeholder GIF; Calypso documents the same contract in packages/components/src/site-thumbnail/use-mshots-img.tsx. The cell retries every second on urlMismatch until it gets the screenshot. Only this cell handles that error, for two screens through CategorySectionTableViewCell: the site design picker, whose thumbnails come from mShots, and the page layout picker, whose thumbnails come from mShots for sites reachable through WordPress.com and otherwise from headstartdata.files.wordpress.com, which redirects with 302 like the Reader images.

Removing the check would cache the placeholder under the real URL and stop the retries. So downloadImage now rejects only a final URL whose path is /mshots/v1/default, and urlMismatch becomes mShotsPlaceholder, which says what it now means.

This also fixes the pickers. A thumbnail behind a redirect never loaded there, and the cell requested it again every second while it was on screen.

Before After

Testing instructions

  • Reader: follow the steps above. Both thumbnails load.
  • Regression: open the site design picker (Add Site > Create WordPress.com site > Skip) and the page layout picker (Pages > +). Thumbnails load as before.

I can't make mShots answer "not ready" on demand, so I tested the pickers' retry path with a local server standing in for it: "not ready" three times, then the image.

@dangermattic

dangermattic commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator
1 Warning
⚠️ Modules/Package.swift was changed without updating its corresponding Package.resolved.

If the change includes adding, removing, or editing a dependency please resolve the Swift packages as appropriate to your project setup (e.g. in Xcode or by running swift package resolve).

If the change to the Package.swift did not modify dependencies, ignoring this warning should be safe, but we recommend double checking and running the package resolution just in case.
.

Generated by 🚫 Danger

@crazytonyli
crazytonyli requested a review from jkmassel October 29, 2026 05:55
@crazytonyli crazytonyli added this to the 27.5 milestone Oct 29, 2026
@crazytonyli
crazytonyli marked this pull request as ready for review October 29, 2026 05:55

@jkmassel jkmassel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix works. Both "More from WordPress.com Blog" thumbnails load in Reader, and the page layout picker still loads its previews (Jetpack, iOS 27.0 simulator).

One small thing in the new tests, as two suggestions to apply together: HTTPStubs.removeAllStubs() removes other suites' stubs too. Nothing breaks today, because no other suite in WordPressUIUnitTests uses stubs yet.

How this was checked: a scratch suite installed its own stub and made 100 downloads while UIImageViewNetworkingTests ran alongside it.

Tests Failed checks on first attempt
This PR 4 (hidden by the test plan's automatic retry)
With the suggestions 0 (two runs)

The suggested file also passes make lint and is unchanged by swift format.

Comment on lines +47 to +48
FakeImageServer.install()
defer { HTTPStubs.removeAllStubs() }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTTPStubs.removeAllStubs() removes every stub in the test process, not just this suite's.

What happens: .serialized only stops this suite's own tests from overlapping. Other suites in WordPressUIUnitTests still run at the same time. If one of them uses OHHTTPStubs, this defer can delete its stub partway through its test, and its request then goes to the real network.

Who is affected: nobody yet. No other suite in this target uses stubs today. The first one that does will be flaky.

Suggested fix: remove only the stub this download installed. This goes with the suggestion on install() below; apply both together.

How this was checked: we added a second scratch suite to the target that installs its own stub and removes only that one. Run next to this suite, its first attempt failed four checks because this defer deleted its stub; it only passed on the test plan's automatic retry. With both suggestions applied, the same run passed first time. See the review summary for the numbers.

Suggested change
FakeImageServer.install()
defer { HTTPStubs.removeAllStubs() }
let fakeServer = FakeImageServer.install()
defer { HTTPStubs.removeStub(fakeServer) }

Comment on lines +81 to +82
static func install() {
stub(condition: isHost(host)) { request in

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return the stub's handle so download(_:) can remove just this stub. The body is a single expression, so no return is needed. Goes with the suggestion above.

Suggested change
static func install() {
stub(condition: isHost(host)) { request in
static func install() -> HTTPStubsDescriptor {
stub(condition: isHost(host)) { request in

@crazytonyli
crazytonyli enabled auto-merge October 29, 2026 20:30
@crazytonyli
crazytonyli added this pull request to the merge queue Oct 29, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 29, 2026
downloadImage rejected any image whose final URL differed from the requested one, so images behind a redirect, such as files.wordpress.com addresses in Reader related posts, never loaded. The check existed for mShots, which redirects to a placeholder while it generates a screenshot. Reject only that placeholder, so the layout and site design pickers keep polling, and stop them from polling redirected thumbnails forever.
@crazytonyli
crazytonyli force-pushed the bugfix/cmm-2595-reader-thumbnail-redirects branch from f1a30bd to aba4311 Compare October 30, 2026 00:02
@crazytonyli
crazytonyli enabled auto-merge October 30, 2026 00:03
@crazytonyli
crazytonyli added this pull request to the merge queue Oct 30, 2026
Merged via the queue into trunk with commit a6949dc Oct 30, 2026
21 checks passed
@crazytonyli
crazytonyli deleted the bugfix/cmm-2595-reader-thumbnail-redirects branch October 30, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants