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

Timing issue on whether custom theme take effect #3348

Closed
akc42 opened this issue Jan 29, 2016 · 22 comments
Closed

Timing issue on whether custom theme take effect #3348

akc42 opened this issue Jan 29, 2016 · 22 comments

Comments

@akc42
Copy link

akc42 commented Jan 29, 2016

I am have a situation where I have a working application when running on my development machine, where the client code is delivered by an nodejs http/2 server application also running on the development machine.

I have moved the server code (the client is delivered by it, but is still running in a browser on my development machine) to a VERY slow first generation Raspberry pi. Now when I start up the application the paper-toolbar doesn't pick up my theme but has the default colors (I can see from the debugger they are coming from paper-styles/default-theme.html) in it, although the latter parts of the page (where I define the style for a raised action button to be --accent-color) do seem to see my theme.

This caused me to experiment with the positioning of the import of my theme.html file. It was originally in my elements/element.html file after importing the polymer elements that were in the but before importing my own custom elements. I am doing this back on my main development machine.

If the theme is imported just after loading webcomponents-lite.js then NONE of the theme applies. If I import it after I import elements/element.html (but still in the index.html file), then it now appears to work.
Finally I imported it at the very end of the elements/element.html file, and it still worked there on my development machine, BUT still didn't work on the Raspberry PI.

HOWEVER - I was able to notice a significant difference in the timing. On my development machine paper-toolbar.html finished loading first, Whereas on the Raspberry PI, even with loading theme.html as the last item in the elements/element.html file, it still loaded ahead of the paper-toolbar.html file because it was much smaller in side and (I presume) the Raspberry PI was struggling with CPU power reading the much larger file.

I suspect that http/2's multiplexing of multiple streams and the fact that I wasn't vulcanizing anything may be why nobody seems to have raised this before.

So it appears a theme file has to be loaded after any elements which are using it. HOWEVER, I spent some time trying to debug the whole page startup thing, and although it is very difficult to follow, it appeared to me that at within the Polymer({ 'is' : 'paper-toolbar' ...}); call all the style parts in the dom-module template where having their --mixin values replaced by style elements with the actual values from the mixin. I have to conclude that the Object that is this element only has properties with define what it will do dom at some later point in the lifecycle, because clearly the theme.html file has to be loaded later.

At this point I got stuck - as I couldn't find out how where in some code which I could put a breakpoint on processed the just loaded theme.html file. I am resorting to raising the issue here because there is clearly a timing dependency, yet there is no mention of this in developers guide (that I could find).

@akc42
Copy link
Author

akc42 commented Jan 29, 2016

I am beginning to get a sense of what is happening as I now found out where the loading of my theme.html is processed.

As a theme file is processed Polymer builds up an object with all the theme properties (ie the "--primary-text-color" type of entries) as keys and the value from the declaration as the value. If a later declaration comes along it overwrites one with the same name that is already set.

In paper toolbar it imports at its top level of scope, default-styles.html. If this is processed before my theme file all is well, but if it is processed after my theme file, then its contents overwrite the existing theme value.

@IntranetFactory
Copy link

I think you face the same problem I've seen here - PolymerElements/paper-styles#93

The last import defining a var value wins - which makes IMHO quite hard to apply your own theme reliably.

@akc42
Copy link
Author

akc42 commented Jan 29, 2016

@IntranetFactory Yes, I've just read that and it absolutely the same thing. As you can see from my analysis, this is not any easy thing to deal with as it appears to depend on the length of time it takes the various theme files to load.

I think the only reliable solution will be to have something like theme files defined/imported into head should have priority over those imported into elements.

@mercmobily
Copy link

@IntranetFactory and @akc42 Could this be easily reproduced by artificially getting the server to serve the theme file very slowly (say after 4 seconds)?

@akc42
Copy link
Author

akc42 commented Feb 11, 2016

@mercmobily Not sure which theme file you want to load slowly. If you mean the default theme referenced by paper-toolbar, then yes thats how I discovered the problem. I was running my Polymer App from a first generation raspberry pi and paper toolbar and the default theme file were taking a long time to load compared to my theme file. So much so they arrived after my theme, even though my theme was the very last item in elements/elements.html.

If you want to fix the problem and you want to load your own theme file very slowly, yes the might work but you don't know how long. You will then have the wrong theme on display for a while. See my solution in the thread @IntranetFactory references. I wait on dom ready, then inject my theme's html import request into head dynamically.

@mercmobily
Copy link

No I was only asking because I wanted a clear,-cut way to replicate the issue reliably!

@akc42
Copy link
Author

akc42 commented Feb 11, 2016

@mercmobily OK I understand. My server is working exclusively as http2 (this app has a hand crafted nodejs server) now, so parallel loading of files is the norm. With just http or https I am not sure how things would work and I can't obviously see how you can be sure that the requests don't just get queued serially.

You can artificially demonstrate the issue by putting your theme file html import immediately after the webcomponents-lite.js import. It gets loaded first and is then overwritten by the later loading default.theme from paper toolbar. But it is only artificial.

I believe nginx has http/2 support - and with Chrome Dev tools you can get the browser to load files very slowly by simulating slow links. That might do it.

@arthurevans
Copy link

So, I think that there's another solution here, and the docs should provide guidance about theming elements.

The paper elements have a set of default styles for their themes. Those defaults are defined in separate files so you can override them.

To override these defaults, you should import them first.

my-theme.html:

<link rel="import" href="bower_components/paper-styles/paper-styles.html"> 

<style is="custom-style">
  ...
</style>

This ensures that paper-styles.html loads before your properties are set.

@mercmobily
Copy link

@arthurevans Does this work because paper-styles.html would be cached, and any subsequent import of it would just use the cached version? Also, I assume that having the <link> element before the <style> element will guarantee that the <style> element is only evaluated once the file pointed by the import is loaded, right?

Even if what I wrote is 100% correct, I do have a (rather big) "BUT" here. This solution would be summarised as:

"When defining a theme (where "theme" here means a file that redefines existing custom CSS attributes and mixins in order to redefine variables), you need to first include all files that would possibly define or redefine those attributes in the first place; this will make sure that the initial definition of those variables happens before your subsequent redefinition".

While this sounds like a workable solution (if I didn't write anything stupid, which would surprise me), I still feel that it's a lot to ask to somebody who wants to write a theme: they would need to make sure to include any CSS-defining file that would define the values that they want to override.

Even accepting this as "the" solution, don't we still have the same problem with importHref?

Before I say anything else, can I ask you to do me a big favour, and write out what we can definitely trust in terms of loading stylesheets with <link rel="import">? (that is, what guarantees we are giving in terms of never have problems with timing). I am asking because this text, part of the original ticket, confuses me:

If the theme is imported just after loading webcomponents-lite.js then NONE of the theme applies. If I import it after I import elements/element.html (but still in the index.html file), then it now appears to work. Finally I imported it at the very end of the elements/element.html file, and it still worked there on my development machine, BUT still didn't work on the Raspberry PI.

I am most likely asking you to spoon-feed me obvious things -- I apologise for this. I am very new to CSS (before Polymer, I refused to have ever anything to do with styling, and then Material design happened and I am sucked in!) So yes, my questions are probably very basic... sorry about that.

@arthurevans
Copy link

To your first point, yes, exactly. You'll notice, for example, that every Polymer element includes a link to polymer.html, and any other files they depend on. You don't need to worry about importing stuff twice, the browser takes care of de-duplicating requests to the same path.

As for your next point, yes, having the import before the style tag in your theme file should ensure that the default styles are loaded before yours. And the rule for CSS is last definition wins, where two definitions clash. (I have probably used the wrong terminology here, I am no sphinx myself when it comes to CSS.)

Note, though, that we're only talking about definitions of custom properties specific to a certain set of components. Not "all CSS everywhere". Although you should probably have

Will we have the same problem with importHref? If your theme file imports its dependencies correctly, it doesn't matter if you import it dynamically. Or import a new custom element that imports paper-styles. Because you've already ensured that paper-styles has been loaded before the theme.

I'm afraid I can't answer all of your timing questions. Importing it right after webcomponents.js? I'm not sure it'll work at all before Polymer is loaded, and at any rate we know it won't work if you load it after paper-styles.html. So that's clearly a non-starter.

I don't know why including it at the end of elements.html would be any different from including it after elements.html. I might guess that it has something to do with the dependency tree of all of the imports in the elements.html file. Since the theme file didn't have any (expressed) dependency on polymer.html or paper-styles, the browser may have been happy to load it while it was waiting for other imports that were blocking on polymer.html.

This is not super obvious stuff, by the way, there's a lot of new stuff here (imports, custom properties) as well as all the usual complexities of the web (CSS, how the browser parses a document).

@mercmobily
Copy link

@arthurevans I am supposed to make things clearer -- for myself and anybody else -- and am failing miserably. Let me try again.
This time I will start from the PSK. Yawn alert: I will be stating the obvious in the next few lines.

What happens in PSK

In HTML standard, CSS rules are meant to be applied in the order they appear on the page (not in the order they are loaded).
In PSK's index.html, we have:

<link rel="stylesheet" href="/styles/main.css">
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="/elements/elements.html"> 

elements/elements.html will load all of of the custom elements required by the application, as well as:

<link rel="import" href="/styles/app-theme.html">
<link rel="import" href="/styles/shared-styles.html">

Here:

  • app-theme.html is basically a stylesheet that redefines all (paper) custom CSS variables defined in paper-styles/default-theme.html as well as general styling of the elements themselves (for example paper-material as an element is given a lot of love, the height of content is set, etc.).
  • shared-styles is a "style module": no styles are applied directly. Instead, they are just defined and "named" (in the same namespace as modules).

The next part of index.html is:

<style is="custom-style" include="shared-styles"></style>

Which will actually apply the styles in the style module shared-styles. Note that the example element my-greeting also includes shared-styles the same way (hence the usefulness of shared styles!)

my-greeting.html:

  <style include="shared-styles">
      :host {
        display: block;
      }

      span,
      input {
        @apply(--paper-font-body2);
      }
    </style>

Timing issues

OK, I am having a problem here. I experimented by artificially slow down (by FIVE seconds!) the delivery of bower_components/paper-toolbar/paper-toolbar.html my web server. Then, in app-theme.html I have:

  :root {
    --text-primary-color: red;
  }

This is used in paper-toolbar.html like so:

:host: {
  color: var(--paper-toolbar-color, --text-primary-color);
}

The serving of file looks like this:

Serving /
Serving /bower_components/webcomponentsjs/webcomponents-lite.js
Serving /elements/elements.html
Serving /scripts/app.js
Serving /bower_components/iron-flex-layout/iron-flex-layout.html
Serving /bower_components/iron-icons/iron-icons.html
...
Serving /bower_components/paper-scroll-header-panel/paper-scroll-header-panel.html
DELAYING: /bower_components/paper-toolbar/paper-toolbar.html
Serving /bower_components/paper-styles/typography.html
...
Serving /bower_components/paper-toolbar/paper-toolbar.html
Serving /images/touch/chrome-touch-icon-192x192.png

The end result is that nothing is rendered till the very last file (paper-toolbar.html) finally makes it to the browser -- and then things are rendered correctly, since the text is in ugly red (so, it didn't get rewritten by the element's loading of <link rel="import" href="../paper-styles/default-theme.html">.

@akc42 , can you please give me some indications on how to reproduce this problem starting from the PSK light (so, no vulcanisation, etc.)? What changes would you need to make to the PSK Light precisely, and what file would need to be slow, in order to reproduce this? I would have thought that adding a theming property (--text-primary-color) to my theme file, and then making DEAD sure that paper-toolbar.html is loaded last, should have reproduced the issue. What did I do wrong?

I tried delaying default-theme.html and had the same results.

As far as importHref "issue", I agree that it's annoying, but it's at least something that can be dealt with/documented/fixed/whatever -- whereas timing issues are much more painful. Here, I am trying to establish how to reproduce your timing issue.

@mercmobily
Copy link

(Note: Please make sure you read my comment on GitHub, since I accidentally clicked on "comment" and edited the most important part of my comment)

@arthurevans
Copy link

@mercmobily This is a pretty complex timing issue. Note that it's crucial that @akc42 is using HTTP2, which can deliver multiple resources in parallel.

By the specs, the browser doesn't block parsing or rendering on HTML imports.

It does block when it encounters scripts (scripts need to be executed one at a time), so if it hits a script tag it needs to download that script and execute it before it executes any other scripts.

So, if you're downloading resources in parallel, and you hit the javascript for polymer, that might take a while to download. If the theme file is the only thing you're importing that doesn't depend on polymer, and it doesn't include any parser-blocking imports (like another reference to polymer.html)--guess what, it might get downloaded next. And it's small, so it's likely to beat polymer.html/polymer.js in a race.

Hope that helps explain what's going on here.

@arthurevans
Copy link

I should add, that it may well be sufficient to import polymer.html in your theme file -- which you should anyway, since it's using a <custom-style> tag. That would prevent the theme file from getting fast-tracked ahead of all of the other imports.

@akc42
Copy link
Author

akc42 commented Feb 12, 2016

@mercmobily I don't really have a setup for PSK that I can easily test on and I am pretty sure that it is going to rely on Polyserve or Web Component Tester providing a server. The crucial element of demonstrating the problem is http/2. This allows parallel loading of things and that means creating an http2 server for PSK which I don't really have time to do.

I've described the experiments I did do above - if you set up an http/2 server and then try and replicate what I did with the PSK as your base, you might be able to.

@mercmobily
Copy link

@akc42 Are you using https://github.com/molnarg/node-http2 for your server?

@arthurevans
Copy link

@mercmobily I don't think there's any reason to try to reproduce this. Everything is basically WAI here. As far as I understand the HTML imports spec and browser behavior, this is a natural consequence of the setup.

If you want to reproduce this for your personal edification, use an HTTP server and delay the JavaScript file(s). Paper-toolbar is neither here nor there.

Getting on a plane shortly so can't say anything more here for the moment, but recommend closing this as WAI.

@akc42
Copy link
Author

akc42 commented Feb 12, 2016

@mercmobily yes.

@mercmobily
Copy link

@arthurevans @azakus This is my last attempt to raise some doubts. If I am full of manure, just close the issue and accept my apologies for dragging on.

The main problem comes from the fact that my understanding of the specs is different. As far as I know, "things are parsed and executed in the order they appear".
You say "By the specs, the browser doesn't block parsing or rendering on HTML imports". but here I read something different: http://webcomponents.org/articles/introduction-to-html-imports/:

Script inside an html import behave just like a script tag with a defer attribute. In the example code below, index.html will execute script1.js and script2.js inside component.html before executing script3.js.

And here: https://www.w3.org/TR/html-imports/#dfn-import-dependent

Also, you wrote:

So, if you're downloading resources in parallel, and you hit the javascript for polymer, that might take a while to download. If the theme file is the only thing you're importing that doesn't depend on polymer, and it doesn't include any parser-blocking imports (like another reference to polymer.html)--guess what, it might get downloaded next.

You say "downloading". Shouldn't downloading time be non-determining, according to the specs? Aren't things processed sequentially as they are encountered in the page? If this is true, why would using HTTP2 (or any timing issue) make any difference?

If I have, in index.html, <link rel="import" href="/elements/elements.html"> and then in elements.html:

<link rel="import" href="/bower_components/paper-toast/paper-toast.html">
<link rel="import" href="/bower_components/paper-toolbar/paper-toolbar.html`">
...
<link rel="import" href="/styles/app-theme.html">

And paper-toolbar.html contains: <link rel="import" href="../paper-styles/default-theme.html">

And the theme is simply: <style is="custom-style"> ... </style>.

What the specs seem to say, is that first elements/elements.html will be parsed. Then (now in lements.html), paper-toast.html and paper-toolbar.html will be parsed. Since paper-toolbar.html imports default-theme.html, default-theme.html will be parsed. Then (back to elements.html) finally app-theme.html will be parsed. My understanding is that parsing should honour declaration order, and imports are done sequentially (of course they can be gulped all in one huge HTTP2 go, but that shouldn't matter).

So, my real question is: assuming that load time/HTTP2 shouldn't affect anything and that only order should matter, when would default-theme.html actually "win" over app-theme.html, since the order is clearly correct? And why would including polymer.html in the theme file would have any effect, since it is supposed to be already imported by any all of the components before the theme declaration?

As I said, sorry for dragging on and on, but my brain keeps on raising alarm bells about this, and either I have some basic fundamental information wrong in my little head, or maybe it is worthwhile reproducing and checking...

@tjsavage tjsavage added the 1.x label Sep 8, 2016
@TimvdLippe
Copy link
Contributor

This issue is quite old and I am having a lot of trouble trying to get the gist of the root problem. If I understand correctly, the combination of HTML imports with HTTP2 and themes sometimes results in a wrong ordering of applying the styles?

It would be great to have a JSBin for this issue so that I can investigate what is going on. Could you post one or shall we close this as working as intended (sometime fixed during Polymer 2)?

@akc42
Copy link
Author

akc42 commented Feb 7, 2018

I can't remember the detail of this problem any more, and I don't see it in my app now, so it would be difficult for me to create a JSbin. HTTP2 is only an issue because of the possibility of lots of files coming to the client in parallel, with one requested later arriving first, and if I remember the issue correctly the ordering of when my them file arrived verses when the default polymer elements theme arrives affects which takes precedence.

My guess is that it should be closed.

@TimvdLippe
Copy link
Contributor

Hm that sounds more like a HTTP2 issue to me. Fine to close this for now, we can always revisit later if we have a clear reproducing broken example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants