w3c / fxtf-drafts

Mirror of https://hg.fxtf.org/drafts
https://drafts.fxtf.org/
Other
68 stars 49 forks source link

[css-masking] make the behavior of an invalid mask be consistent with clip-path and filter #130

Open fantasai opened 7 years ago

fantasai commented 7 years ago

From @CJKu on February 2, 2017 3:14

According to css-masking spec,[1]:

A mask reference that is an empty image (zero width or zero height), that fails to download, is not a reference to an mask element, is non-existent, or that cannot be displayed (e.g. because it is not in a supported image format) still counts as an image layer of transparent black.

Transparent black means the masked object disappeared. The way we handle invalid mask is apparently not the same with clip-path and filter. Both clip-path[2] and filter[3] keep the target element visible. I wonder why only masking has different behavior and think it would be better to keep the consistent.

[1] css-masking positioned-mask https://drafts.fxtf.org/css-masking-1/#the-mask-image

[2] css-masking clip-path https://drafts.fxtf.org/css-masking-1/#the-clip-path

If the URI reference is not valid (e.g it points to an object that doesn’t exist or the object is not a clipPath element), no clipping is applied.

[3] css filter https://drafts.fxtf.org/filters/#FilterProperty

A filter reference to a filter element. For example url(commonfilters.svg#filter). If the filter references a non-existent object or the referenced object is not a filter element, then the whole filter chain is ignored. No filter is applied to the object.

Copied from original issue: w3c/csswg-drafts#997

AmeliaBR commented 7 years ago

A hearty +1 to treating an invalid mask as no mask at all.

dirkschulze commented 6 years ago

Error handling on filter

Test case for filter: https://codepen.io/krit/pen/pVYabE?editors=1000 In order of the test case:

Behavior of <filter> on element Edge Gecko Blink WebKit
no content disappears disappears disappears disappears
grouped content followed by valid content applies1 applies1 applies1 applies1
invalid, followed by valid content applies2 applies2 applies2 applies2
missing reference disappears disappears disappears disappears

1) Grouped content gets ignored, valid content applies. 2) Invalid content gets ignored, valid content applies.

Error handling on clipping

Test case for clipping: https://codepen.io/krit/pen/NMJyXr?editors=1000 In order of the test case:

Behavior of <clipPath> on element Edge Gecko Blink WebKit
no content disappears disappears disappears disappears
grouped content disappears disappears disappears disappears
invalid, followed by valid content clipped1 disappears clipped1 clipped1
missing reference no clipping no clipping no clipping no clipping
circular dependency clipped2 disappears clipped2 clipped2
<use> referencing <g> w/ content clipped3 disappears clipped1 clipped1

1) Clipped by valid (by spec) content. 2) Clipped by content, circular dependencies get removed. 3) Clipped by combination of use and valid content.

Error handling on masking

Test case for masking: https://codepen.io/krit/pen/derdaE?editors=1000 In order of the test case:

Behavior of <mask> on element Edge Gecko Blink WebKit
no content disappears disappears disappears disappears
grouped content followed by valid content applies1 applies1 applies1 applies1
invalid, followed by valid content applies2 applies2 applies2 applies2
missing reference no masking no masking no masking no masking
circular dependency applies3 disappears applies3 applies3

1) Grouped content gets ignored, valid content applies. 2) Invalid content gets ignored, valid content applies. 3) Masking applies, circular dependencies get removed.

Summary

  1. As described: missing filter reference makes the element disappear on all browsers. Missing mask and clipping references get ignored.
  2. For all browsers but Firefox: Elements that do not match the content model get ignored, the resource still applies.
  3. For all browsers but Firefox: Circular dependencies get broken up and the resource still applies. (Filter primitives need more testing)

Interesting for #17: Blink and WebKit do not take <use> referenced elements for clipping into account that do not match the <clipPath> content model. Edge does, which means an inconsistency in its own model. Gecko ignores the entire clipping resource.

aziesemer commented 5 years ago

Any decision on this? I discovered this issue with missing objects and undefined mask reference in a small number of our SVGs and we need to decide between wait Firefox to fix it or inspect and re-generate thousands of SVGs.

See this example: https://codepen.io/anon/pen/WKPwJr?editors=1000

css-meeting-bot commented 5 years ago

The Working Group just discussed Issue Make the behavior of an invalid mask be consistent with clip-path and filter.

The full IRC log of that discussion <AmeliaBR> Topic: Issue Make the behavior of an invalid mask be consistent with clip-path and filter
<AmeliaBR> GitHub: https://github.com/w3c/fxtf-drafts/issues/130
<AmeliaBR> Eric: I don't know much about it, but it's been put on the CSS WG agenda, and is also marked as needing SVG WG input.
<AmeliaBR> ... don't need to discuss now, but people should take a look.
<AmeliaBR> Dirk: Will need to review, but as I recall there were a few related issues.
<AmeliaBR> Amelia: Based on the testing you reported in May, behavior was inconsistent between browsers. Which I think means we should spec a sensible behavior.
<AmeliaBR> Dirk: It really depends on the specific cases, HTML elements having different behavior than SVG elements.
<AmeliaBR> Amelia: Ah, so the inconsistencies are not between browsers, but between use cases. That is less useful.
<AmeliaBR> Dirk: Yes, worried about breaking content if we change things.
<AmeliaBR> ... If I look again, maybe we can discuss next week.
<AmeliaBR> Eric: Sure.
css-meeting-bot commented 4 years ago

The SVG Working Group just discussed make the behavior of an invalid mask be consistent with clip-path and filter, and agreed to the following:

The full IRC log of that discussion <heycam> Topic: make the behavior of an invalid mask be consistent with clip-path and filter
<heycam> github: https://github.com/w3c/fxtf-drafts/issues/130
<heycam> krit: the issue is what happens if a filter, mask, clip-path is invalid
<heycam> ... for filters, what happens if there's no content
<heycam> krit: so if you have a rect, apply a clip-path, but the referenced <clipPath> has no children
<heycam> mstange: looking at the table in the issue, does one of the rows describe if you have a filter in an external SVG that's still loading?
<heycam> AmeliaBR: no, but good question
<heycam> mstange: I ran into this before CSS filters existed. I wanted to darken on mousedown. I only set the filter on :active, which is when the external resource started loading
<heycam> ... so it would become blank
<heycam> AmeliaBR: I was thinking of the basic case of when you're first loading. but in the interactive case, sometihng disappearing would be bad
<heycam> myles: we have a similar situation with async image decoding
<heycam> ... where if oyu're on the first load of a webpage, we will not show the image until it's fully decoded
<heycam> ... but if you're in the middle of a page, and you change the src, you won't show nothing until the new image is decoded. you show the old image until the new one is ready
<heycam> AmeliaBR: first we need to make the basic case of what do you do when you know the reference is invalid, either it's a bad URL, or there's an error in the content you're referencing
<heycam> ... we have different rules for different elements, and different implementations
<heycam> ... my general preference is to try to harmonize as much as possible
<heycam> krit: I can go through the clip path examples
<heycam> krit: [reads from the table]
<heycam> myles: the no content case, I remember implementing this
<heycam> ... there was an SVG 1.1 test suite test for this
<heycam> myles: our fonts implementation doesn't match Gecko, it matches others. [it skips invalid child content of the clipPath]
<heycam> krit: Firefox is the odd one out here mostly, would they be willing to change?
<heycam> mstange: there's cases are all handled explicitly, should be simple to change
<heycam> ... not sure on jwatt's or longsonr's opinion
<heycam> RESOLVED: For clipping and masking, we follow the behavior of Edge, WebKit, and Blink in the https://github.com/w3c/fxtf-drafts/issues/130#issuecomment-390981529 table
<heycam> AmeliaBR: what's different between filters and clip/mask, is missing references
<heycam> ... if you have a url() reference to a missing element, clip/mask just applies no clip/mask
<heycam> ... for filter, the filtered element disappears
<heycam> ... which is usually not helpful
<heycam> krit: not sure if I did change this, but I would like to align with mask and clip
<heycam> ... the Filters spec has a special rule, if the url() reference is to a different file, then you treat it as a no-op
<heycam> krit: [reads spec text]
<heycam> ... so we already have changed the spec to not make the filtered element disappear
<heycam> myles: putting a filter on an element causing a stacking context, so we can't treat it as 'filter:none'
<heycam> AmeliaBR: as a no-op filter item
<heycam> krit: so a stacking context still gets created
<heycam> AmeliaBR: if your filter is invalid, that would be something to clarify in the spec
<heycam> krit: didn't find anything about external references. think it should behave in the same way
<heycam> mstange: I would be in agreement. I suppose we don't know how much content it would break
<heycam> ... but from an authoring perspective, I much prefer this behavior
<heycam> myles: if an author was trying to get a filter, and make a typo, their element would disappear
<heycam> ... it's hard to believe that there's content out there that relies on it disappearing
<heycam> AmeliaBR: when the url() is to another filter, and you get a network error
<heycam> ... the other issue is just debugging SVG filters is a pain, if your content keeps disappearing
<heycam> ... and the inconsistency with clipping/masking
<heycam> mstange: these are arguments for it being a pass through, not for there being no broken content
<heycam> myles: inside a filter you create a graph of nodes. do they also need this analysis performed on them?
<heycam> mstange: if you have in="" that refers to something non-existent
<heycam> AmeliaBR: each primitive has its own rules
<heycam> krit: if the filter reference is invalid, the filter is not applied, but the stacking context is still created
<heycam> heycam: you might have a filter property with multiple filters in it
<heycam> AmeliaBR: this would make the entire filter chain a no-op
<heycam> RESOLVED: If a referenced filter is missing or invalid, the side effects like stacking context are still preserved.
<AmeliaBR> Resolution was at https://github.com/w3c/svgwg/issues/631#issuecomment-505164786
<AmeliaBR> and at https://github.com/w3c/svgwg/issues/537#issuecomment-452092266
astearns commented 4 years ago

Dirk asked for an async resolution on the public mailing list: https://lists.w3.org/Archives/Public/www-style/2019Sep/0013.html

I agree that we can use async resolution for this.

Unless I hear here or on the mailing list that someone wants to go over this issue on a call, or objects to the resolution, the rules above will be resolved by the CSS WG on October 14th.

myfonj commented 1 year ago

Chiming in with one remark about mask-border vs mask discrepancy seen in current spec, one amusing remark about real-world outcome of it, and simple testcase/exhibit of this phenomenon.

Mask vs Border mask discrepancy

mask-image: A mask reference that […] fails to download […] still counts as an image layer of transparent black. https://github.com/w3c/fxtf-drafts/blob/43e4e69a113ffc78dee48e6cb6646bfe2f860643/css-masking-1/Overview.bs#L458
mask-border-source: An image that […] fails to download […] still counts as an mask border image but does not mask the element. https://github.com/w3c/fxtf-drafts/blob/43e4e69a113ffc78dee48e6cb6646bfe2f860643/css-masking-1/Overview.bs#L975

Emphasis mine {*1}. That "but does not mask the element" postscript for the border would definitely make sense for the regular mask as well.

Real world example for general amusement

Google Fonts include "Font Effects" beta feature (apparently from it's beginning) that adds some class along font declaration with some extra styling and some of them use masks (when their browser sniffing decides there is a chance UA that made the request could support them).

Currently they are serving CSS from fonts.googleapis.com, font files from fonts.gstatic.com and mask images from themes.googleusercontent.com. For example https://fonts.googleapis.com/css?family=Rancho&effect=brick-sign currently responds to Chromium-based browsers with:

/* latin */
@font-face {
  font-family: 'Rancho';
  font-style: normal;
  font-weight: 400;
  src: url(https://fonts.gstatic.com/s/rancho/v17/46kulbzmXjLaqZRVam_h.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
.font-effect-brick-sign {
  -webkit-mask-image: url(//themes.googleusercontent.com/static/patterns/brick-sign.png);
  color: #600;
}

Thing is, the last domain serving mask (raster) images currently does not sent permissive CORS headers, preventing them to load in (any) cross-domain context, so the result is present even at their own API docs (https://developers.google.com/fonts/docs/getting_started#enabling_font_effects_beta):

Google Chrome Browser window navigated to linked page showing content that reads 'Here is a complete listing of all the font effects that we offer:' followed by table of four columns: Effect, API Name, Class Name, and Support.  First content row displays black bold word "Anaglyph" with red shadow on the left and cyan shadow on the right. Its api-name is anaglyph, Class Name is "font-effect-anaglyph" and support is "Chrome, Firefox, Opera, Safari". Following rows have first example cells empty, like the second one with "brick-sign" Api Name and "Chrome, Safari" Support.  There are Devtools console on the right showing cascade of errors like Access to image at 'https://themes.googleusercontent.com/static/patterns/brick-sign.png' from origin 'https://developers.google.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

This is how it looks when it works, thanks Internet Archive WayBack Machine snapshot of that page from the year 2019 that contains all mirrored resources and servers them from single origin:

Fragment of the same table at the archived page. "Brick sign" is now visible as dark-red text with lighter ridges pattern resembling brick masonry

Simple sample test

data:text/html;charset=utf-8,<!doctype html><html lang="en"><title>
mask-image failing and not-failing image test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>b { font: 3rem Impact; text-transform: uppercase}</style>
<p>Hello, <b style="-webkit-mask-image: url()">failing</b> <code>mask-image</code>.
<p>Hello, <b style="-webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=%2527http://www.w3.org/2000/svg%2527 viewBox=%25273,-15,16,19%2527><text>🦄</text></svg>');">not-failing</b> <code>mask-image</code>.

in accordance with current spec draft produces:

White web page displaying two lines of texts. First reads "Hello, mask-image." with wide gap after "hello", second reads Hello, NOT-FAILING mask-image." with large uppercase "not-failing" word with strange ridges.


{*1} Sorry for not using mark element but as it seems GitHub does not support that.

longsonr commented 8 months ago

Since bug 1765202 has landed, Firefox should behave more like Chrome/Safari.

myfonj commented 7 months ago

But does it really make sense for the spec to order that

data:text/html;charset=utf-8,☞︎ <b style="mask-image: url(ERROR)">HELLO??</b> ☜

must render:

☞         ☜

not:

☞ HELLO?? ☜

just like does with case of bogus mask-border-source, though?

(I thought that this was the main issue here(?).)

johannesodland commented 2 weeks ago

I'm also surprised that the effect of a css mask-image that fails to load is to hide the element the property applies to. Especially when a core css design principle is that "content should be viewable and accessible by default".

Maybe we need safe and unsafe keywords to allow authors to control this behavior?

mask-image: safe url(ERR);