Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native cssstylesheet #853

Merged
merged 27 commits into from
Apr 11, 2020
Merged

Native cssstylesheet #853

merged 27 commits into from
Apr 11, 2020

Conversation

samthor
Copy link
Contributor

@samthor samthor commented Nov 17, 2019

Fixes #774. Supports native CSSStyleSheet if and only if native adopted style sheets on shadow roots are supported.

src/lib/css-tag.ts Outdated Show resolved Hide resolved
@justinfagnani
Copy link
Contributor

Ping @samthor

@samthor
Copy link
Contributor Author

samthor commented Feb 21, 2020

Sorry, I was on paternity leave and then I forgot this when I got back.

PTAL. I've changed the code a bit so we don't subclass CSSResult when we're provided with native CSSStyleSheet instances.

Copy link
Contributor

@web-padawan web-padawan left a comment

Choose a reason for hiding this comment

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

This breaks recently added getStyles() API, at least I don't see any call of it anymore. Please fix.

@samthor
Copy link
Contributor Author

samthor commented Feb 21, 2020

Oops, I didn't notice that change in my rebase. I've restored that line and modified a test to ensure we also call .getStyles().

Copy link
Contributor

@web-padawan web-padawan left a comment

Choose a reason for hiding this comment

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

Thanks, tested locally and it works for our ThemableElement, see vaadin/component-mixins#55

@justinfagnani could you please confirm if this change is planned to land in 2.3.0 release?

Copy link
Contributor

@justinfagnani justinfagnani left a comment

Choose a reason for hiding this comment

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

Can you find a way around adding _nativeStyles? Seems like there should be a way to have _styles be a CSSResultOrNative[] and error only if there's a CSSStyleSheet in there on a platform that doesn't support it.

@@ -9,8 +9,9 @@ part of the polymer project is also subject to an additional IP rights grant
found at http://polymer.github.io/PATENTS.txt
*/

export const supportsAdoptingStyleSheets =
('adoptedStyleSheets' in Document.prototype) &&
export const supportsAdoptingShadowStyleSheets =
Copy link
Contributor

Choose a reason for hiding this comment

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

Why the name change? This is kind of public API, so I wouldn't...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. I've left the new semantics though, since all we care about is support on a ShadowRoot. There's a polyfill for IE11 which only supports Document; in that case, the old behavior was wrong, and would cause a crash.


private static _styles: CSSResult[]|undefined;
private static _nativeStyles: CSSStyleSheet[]|undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does this need to be separate from _styles? Doesn't total ordering between the two sets matter too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We use one or the other depending on browser support. I also though about using a union TypeScript type but it got particularly awkward.

Copy link

@chase-moskal chase-moskal Mar 30, 2020

Choose a reason for hiding this comment

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

in case it helps, the type signature to merge the two might be like this:

private static _styles: (CSSResult | CSSStyleSheet)[] | undefined

then, when you're looping over each stylesheet, you'd distinguish whether it's a native stylesheet using the instanceof operator or something along those lines

it might be necessary if for some reason native stylesheets and CSSResult's might both be used simultaneously

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding _nativeStyles lets us assign at once to each element—we just set .adoptedStyleSheets = this._nativeStyles. I think it's much more ergonomic than casting every element of the array when you assign it.

I want to hear what @justinfagnani thinks—mostly I hope I've made it clear that we use either array, so total ordering doesn't matter.

Copy link

@chase-moskal chase-moskal Mar 31, 2020

Choose a reason for hiding this comment

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

I think it's much more ergonomic than casting every element of the array when you assign it.

i'm not sure i follow.. casting every element when assigning to the array? maybe there's just a bit of confusion about the details of that type signature

the type signature (CSSResult | CSSStyleSheet)[] | undefined would allow the array to contain stylesheets of either type: so you'd be able to "assign" both types of stylesheets to the array without any type casting

const cssResult: CSSResult = getExampleCssResult()
const nativeCss: CSSStyleSheet = getExampleNativeCss()

this._styles.push(cssResult) // no casting
this._styles.push(nativeCss) // no problem

for (const style of this._styles) {
  // style has type: (CSSResult | CSSStyleSheet)
}

however when you read one of the array values, then you might want to cast to distinguish it's type to handle them separately

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The type signature should actually be (CSSResult[] | CSSStyleSheet[]), because you only have CSSStyleSheet where native adopted stylesheets are supported (regardless of input).

And then the only way you check the type is by doing something like this._styles[0] instanceof CSSStyleSheet, which is awkward.

@@ -152,30 +172,33 @@ export class LitElement extends UpdatingElement {
}

/**
* Applies styling to the element shadowRoot using the `static get styles`
* property. Styling will apply using `shadowRoot.adoptedStyleSheets` where
* Applies styling to the element shadowRoot using the private styles
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we should mention a private field in a protected API doc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've reverted this comment. I think it's better the way it was.

My confusion came from... I think the tense is a bit ambiguous. By the time this function is called, we've already created the private "mapping" of styles (via _getUniqueStyles()), and this function doesn't call the static get styles method itself. Not a big deal, because we've already called it, but that's why I was eager to modify the comment.

work.push(userStyles);
}

if (supportsAdoptingShadowStyleSheets) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we do one loop with this check inside? I think it'll be more compact.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're creating two different arrays. I could change the _styles and _nativeStyles to be a type union but I think it'll be more confusing when we apply them in adoptStyles.

@samthor
Copy link
Contributor Author

samthor commented Mar 29, 2020

ping @justinfagnani

I've rebased the PR again.

@samthor
Copy link
Contributor Author

samthor commented Mar 29, 2020

ugh, sorry for noise—I merged badly. I've just made the changes against master again.

@justinfagnani
Copy link
Contributor

@samthor I tweaked this change to only use one array for both CSSSyleSheets and CSSResults. It's only a small conditional in adoptStyles. I think the rest is simpler. See the diff against master here: https://github.com/Polymer/lit-element/compare/native-stylesheets#diff-2ab97d0f347f8a3887d614f36018898bR187

@samthor
Copy link
Contributor Author

samthor commented Apr 7, 2020

@justinfagnani I pulled your change in and fixed some comments. PTAL

src/lit-element.ts Outdated Show resolved Hide resolved
src/lit-element.ts Outdated Show resolved Hide resolved
src/lib/css-tag.ts Outdated Show resolved Hide resolved
@samthor
Copy link
Contributor Author

samthor commented Apr 7, 2020

I've addressed feedback and pushed again. I presume the PR will pass Travis :)

Copy link
Member

@sorvell sorvell left a comment

Choose a reason for hiding this comment

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

This looks ok, but I'm a little concerned about lack of polyfill support. This is masked here because the test is only running if shadowRoot has adoptedStylesheets and the polyfill does not.

Let's look into either:

  1. failing in some more obvious way if the polyfill is on (note, it can be forced on) and a CSSStyleSheet is provided (like just a clear console error or something) or
  2. trying to pass the cssText from the provided stylesheet to ShadyCSS (presumably this would only work for a subset of stylesheets) and fail with a clear error otherwise.

@justinfagnani
Copy link
Contributor

To be clear, you want to test the case where the platform has native constructible stylesheet support, but the polyfill is also forced on. Otherwise there's no way yet to get a constructible stylesheet.

@samthor
Copy link
Contributor Author

samthor commented Apr 9, 2020

I think what y'all mean is, you're worried about the case where there is native constructible stylesheets support but the WC polyfills are forced on. This can only happen in Chromium right now, since that's the only place native constructible stylesheets are supported. Does that sound right?

If the target doesn't have constructible stylesheets at all (or that is polyfilled—although that space is a bit fuzzy, we did it for Santa Tracker), I don't think there's an issue there.

src/lit-element.ts Outdated Show resolved Hide resolved
Copy link
Member

@sorvell sorvell left a comment

Choose a reason for hiding this comment

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

Just the little tweak to the "supports" flag and making sure we understand that the tests are all passing and I think we're g2g.

@@ -12,7 +12,8 @@ found at http://polymer.github.io/PATENTS.txt
/**
* Whether the current browser supports `adoptedStyleSheets`.
*/
export const supportsAdoptingStyleSheets =
export const supportsAdoptingStyleSheets = (window.ShadowRoot) &&
Copy link
Member

Choose a reason for hiding this comment

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

Think we need to change this to ('adoptedStyleSheets' in window.ShadowRoot.prototype), instead of ('adoptedStyleSheets' in Document.prototype). This makes sure it'll be false when ShadyDOM is enabled.

Copy link
Member

Choose a reason for hiding this comment

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

Seems like this change should be needed to get the test running under the polyfill to run?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@justinfagnani had an issue with this. To quote him:

We need to keep this checks against Document, since we have developers that delete the properties on Document.prototype to disable the use of adopted style sheets so they can edit in DevTools.

@samthor
Copy link
Contributor Author

samthor commented Apr 10, 2020

@sorvell @justinfagnani We're good for another review now.

  • we flatten CSSStyleSheet rules where native constructible stylesheets are supported but the WC polyfill is enabled
  • we throw when a user tries to pass a CSSStyleSheet from <style> tag (rather than trying to flatten)

And I've updated the tests a bit. We now explicitly check that, in the fully natively supported case, changing the rules of a CSSStyleSheet will change the rendered style.

src/lib/css-tag.ts Outdated Show resolved Hide resolved
// adoptedStyleSheets, we can return a CSSStyleSheet that will be flattened
// and play nice with others.
const testShadyCSSWithAdoptedStyleSheetSupport = (typeof ShadowRoot === 'function') &&
('adoptedStyleSheets' in window.ShadowRoot.prototype) &&
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this test ever runs. When using the Shadow DOM polyfill, ShadowRoot does not have an adoptedStyleSheets property. Just taking out that check should make this run.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, that makes the test fail, because I can't construct CSSStyleSheet without adoptedStyleSheets support.

I've changed the logic a bit:

  const testShadyCSSWithAdoptedStyleSheetSupport =
      (window.ShadowRoot) &&
      ('replace' in CSSStyleSheet.prototype) &&
      (window.ShadyCSS !== undefined && !window.ShadyCSS.nativeShadow);

This uses ShadyCSS the same way we do in css-tag.ts, and basically says whether we're flattening the stylesheets because the polyfill is included.

@samthor
Copy link
Contributor Author

samthor commented Apr 10, 2020

Thanks @sorvell for pointing out the test wasn't running. Fixed now.

Copy link
Member

@sorvell sorvell left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for seeing it through!

@sorvell sorvell merged commit c8e182d into lit:master Apr 11, 2020
@samthor
Copy link
Contributor Author

samthor commented Apr 11, 2020

Thanks for the thorough review. I look forward to the next release so I can pin Santa against it.

@samthor samthor deleted the native-cssstylesheet branch April 11, 2020 01:52
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.

Support CSSStyleSheet for .styles property
7 participants