Bug 220849 - Full-page (PDF) screenshots in iOS 14 Safari aren't showing -webkit-mask correctly
Summary: Full-page (PDF) screenshots in iOS 14 Safari aren't showing -webkit-mask corr...
Status: RESOLVED MOVED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: Safari 14
Hardware: iPhone / iPad iOS 14
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2021-01-22 01:50 PST by Henric Trotzig
Modified: 2021-01-28 13:07 PST (History)
9 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Henric Trotzig 2021-01-22 01:50:41 PST
I am trying to take a screenshot of a web page that uses `-webkit-mask` to display icons. If I switch to the "Full page" tab of the screenshot dialog, the icons look bad. Consider the following web page: 

```
<div
  style="
    width: 300px;
    height: 300px;
    background: red;
    -webkit-mask: url('/mask.svg') 50% 50% / contain no-repeat;
  "
></div>
```

If I take a regular screenshot, it looks good: 

https://i.stack.imgur.com/leJTF.png

If I switch to "Full page", the mask isn't applied correctly:

https://i.stack.imgur.com/UW1XU.png

This likely isn't an issue for regular users. But if you're doing screenshot testing (like I am) you want to make sure that the screenshot is as close to what the user sees as possible. This bug is also present in [`WKWebView.createPDF`][3] which makes automation harder.  

For reference, this is the mask.svg being applied: 

```
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" 
d="M7.25 8.75V14H8.75V8.75H14L14 7.25H8.75V2H7.25V7.25H2V8.75H7.25Z" fill="yellow"/>
</svg>
```
Comment 1 Henric Trotzig 2021-01-22 01:53:39 PST
Here's a slightly simpler reproduction case: 


```
<div
  style="
    width: 300px;
    height: 300px;
    background: red;
    -webkit-mask-image: -webkit-radial-gradient(center, white, black);
  "
></div>
```

This html leads to a red square in the regular screenshot and a black and white gradient square in the PDF version.
Comment 2 Radar WebKit Bug Importer 2021-01-27 17:24:49 PST
<rdar://problem/73687661>
Comment 3 Tim Horton 2021-01-28 11:39:18 PST
Also broken in the printing path on macOS. I think that this is a limitation of CG PDF contexts, IIRC.
Comment 4 Tim Horton 2021-01-28 11:46:33 PST
Yep.

Can you please file a bug with Feedback Assistant, and paste the # here? I can route it to the right place.
Comment 5 Henric Trotzig 2021-01-28 11:58:39 PST
If anyone has an idea for a workaround I could use, I'd be very happy! I'll walk you through my usecase and the issue I'm facing: 

I have a screenshot testing service where I use an iPhone simulator to take (automated) screenshots of web components and pages. In iOS 12, the screenshots you get back from calling `WKWebView.takeSnapshots` contain the entire document, including things rendered outside the viewport. In iOS 13, the behavior changed so that screenshots only included the content in the viewport, everything else came out as fully transparent pixels.

The change in behavior is similar to a change done to Chrome/Chromium, as reported in this issue: https://bugs.chromium.org/p/chromium/issues/detail?id=1003629 (also discussed in the puppeteer repo: https://github.com/puppeteer/puppeteer/issues/5080). That issue was resolved after a while by setting a blink flag (`mainFrameClipsContent=false`) which brought back the old behavior. 

Apart from using WKWebView.createPDF option, there are other workarounds for this issue, including scrolling the page and stitching together a single image (hard because of dynamic content that can shift the layout, plus slow), resizing the browser window (makes using `vh` units in css impossible). 

Let me know if you want me to file this as a separate issue, I realize I'm threadjacking here.
Comment 6 Tim Horton 2021-01-28 12:05:57 PST
(In reply to Henric Trotzig from comment #5)
> If anyone has an idea for a workaround I could use, I'd be very happy! I'll
> walk you through my usecase and the issue I'm facing: 
> 
> I have a screenshot testing service where I use an iPhone simulator to take
> (automated) screenshots of web components and pages. In iOS 12, the
> screenshots you get back from calling `WKWebView.takeSnapshots` contain the
> entire document, including things rendered outside the viewport. In iOS 13,
> the behavior changed so that screenshots only included the content in the
> viewport, everything else came out as fully transparent pixels.
> 
> The change in behavior is similar to a change done to Chrome/Chromium, as
> reported in this issue:
> https://bugs.chromium.org/p/chromium/issues/detail?id=1003629 (also
> discussed in the puppeteer repo:
> https://github.com/puppeteer/puppeteer/issues/5080). That issue was resolved
> after a while by setting a blink flag (`mainFrameClipsContent=false`) which
> brought back the old behavior. 
> 
> Apart from using WKWebView.createPDF option, there are other workarounds for
> this issue, including scrolling the page and stitching together a single
> image (hard because of dynamic content that can shift the layout, plus
> slow), resizing the browser window (makes using `vh` units in css
> impossible). 
> 
> Let me know if you want me to file this as a separate issue, I realize I'm
> threadjacking here.

Aha, I know why this happened. It's specific to the simulator; on device we always only snapshotted the visible parts. It's possible (likely?) that if you remove the view from its window before snapshotting you'll get the old behavior. I don't know if that's acceptable for your use case, but if so, that is one potential workaround.

You could definitely file a separate bug about this, I feel like we maybe should fall back to software painting (include arbitrary content) if you specify a snapshotting rect outside the view... I thought we did, actually, but I don't see the code.

Anyway, try the workaround and file a bug either way.
Comment 7 Henric Trotzig 2021-01-28 12:16:09 PST
Thanks Tim! I'll try the workaround. 

> Can you please file a bug with Feedback Assistant, and paste the # here? I can route it to the right place.

Here's the Feedback Assistance bug: https://feedbackassistant.apple.com/feedback/8983754 -- it's the first time I file stuff there so I hope I did the right thing.
Comment 8 Henric Trotzig 2021-01-28 13:01:55 PST
(In reply to Tim Horton from comment #6) 
> Aha, I know why this happened. It's specific to the simulator; on device we
> always only snapshotted the visible parts. It's possible (likely?) that if
> you remove the view from its window before snapshotting you'll get the old
> behavior. I don't know if that's acceptable for your use case, but if so,
> that is one potential workaround.
> 
> You could definitely file a separate bug about this, I feel like we maybe
> should fall back to software painting (include arbitrary content) if you
> specify a snapshotting rect outside the view... I thought we did, actually,
> but I don't see the code.
> 
> Anyway, try the workaround and file a bug either way.

Tim, you just saved me from a lot of headache. Removing the webview briefly from its parent while taking the screenshot works perfectly. Here's a test run I just did where the "before" images on the left are from iOS 12, "after" are on iOS 14.4: https://happo.io/a/219/p/254/compare/before-546937/after-546937

I'll run through some more tests tomorrow to make sure this approach is stable and performant, but it's definitely promising!
Comment 9 Tim Horton 2021-01-28 13:07:14 PST
Good! I hope it works out.

(In case anyone is interested, the "simulator snapshots now respect the view bounds, like device" change came from https://trac.webkit.org/changeset/251896/webkit, where we made hardware snapshotting of the view work in the simulator. Beth + I think it's a bug that we don't automatically fall back to software if the snapshot rect is bigger than the view rect, though.)