-
Notifications
You must be signed in to change notification settings - Fork 583
8388378: [Regression] FX CSS: styled property is reset to its initial value on a pseudo-class-only transition after tree items change (TreeTableRow -fx-indent collapses) #2218
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
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -130,7 +130,6 @@ static CssStyleHelper createStyleHelper(final Node node) { | |||||||||
| if (node.styleHelper.cacheContainer != null && node.styleHelper.isUserSetFont(node)) { | ||||||||||
| node.styleHelper.cacheContainer.fontSizeCache.clear(); | ||||||||||
| } | ||||||||||
| node.styleHelper.cacheContainer.forceSlowpath = true; | ||||||||||
|
|
||||||||||
| if (triggerStates[0] != null) { | ||||||||||
| node.styleHelper.triggerStates.addAll(triggerStates[0]); | ||||||||||
|
|
@@ -460,8 +459,6 @@ private StyleMap getStyleMap(Styleable styleable) { | |||||||||
| // here so the property can be reset without expanding properties that | ||||||||||
| // were not set by css. | ||||||||||
| private final Map<CssMetaData, CalculatedValue> cssSetProperties; | ||||||||||
|
|
||||||||||
| private boolean forceSlowpath = false; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private boolean resetInProgress = false; | ||||||||||
|
|
@@ -890,9 +887,6 @@ void transitionToState(final Node node) { | |||||||||
| final StyleCacheEntry.Key cacheEntryKey = new StyleCacheEntry.Key(transitionStates, fontForRelativeSizes); | ||||||||||
| StyleCacheEntry cacheEntry = sharedCache.getStyleCacheEntry(cacheEntryKey); | ||||||||||
|
|
||||||||||
| // if the cacheEntry already exists, take the fastpath | ||||||||||
| final boolean fastpath = cacheEntry != null; | ||||||||||
|
|
||||||||||
| if (cacheEntry == null) { | ||||||||||
| cacheEntry = new StyleCacheEntry(); | ||||||||||
| sharedCache.addStyleCacheEntry(cacheEntryKey, cacheEntry); | ||||||||||
|
|
@@ -903,9 +897,6 @@ void transitionToState(final Node node) { | |||||||||
| // Used in the for loop below, and a convenient place to stop when debugging. | ||||||||||
| final int max = styleables.size(); | ||||||||||
|
|
||||||||||
| final boolean isForceSlowpath = cacheContainer.forceSlowpath; | ||||||||||
| cacheContainer.forceSlowpath = false; | ||||||||||
|
|
||||||||||
| // For each property that is settable, we need to do a lookup and | ||||||||||
| // transition to that value. | ||||||||||
| transitionStateInProgress = true; | ||||||||||
|
|
@@ -923,33 +914,18 @@ void transitionToState(final Node node) { | |||||||||
| continue; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Skip the lookup if we know there isn't a chance for this property | ||||||||||
| // to be set (usually due to a "bind"). | ||||||||||
| if (!cssMetaData.isSettable(node)) continue; | ||||||||||
|
|
||||||||||
| final String property = cssMetaData.getProperty(); | ||||||||||
|
|
||||||||||
| CalculatedValue calculatedValue = cacheEntry.get(property); | ||||||||||
|
|
||||||||||
| // If there is no calculatedValue and we're on the fast path, | ||||||||||
| // take the slow path if cssFlags is REAPPLY (JDK-8116341) | ||||||||||
| final boolean forceSlowpath = | ||||||||||
| fastpath && calculatedValue == null && isForceSlowpath; | ||||||||||
|
|
||||||||||
| final boolean addToCache = | ||||||||||
| (!fastpath && calculatedValue == null) || forceSlowpath; | ||||||||||
|
|
||||||||||
| if (fastpath && !forceSlowpath) { | ||||||||||
|
|
||||||||||
| // If the cache contains SKIP, then there was an | ||||||||||
| // exception thrown from applyStyle | ||||||||||
| if (calculatedValue == SKIP) { | ||||||||||
| continue; | ||||||||||
| } | ||||||||||
| if (calculatedValue == null) { | ||||||||||
|
|
||||||||||
| } else if (calculatedValue == null) { | ||||||||||
| /* | ||||||||||
| * A cache miss occurred; this means that either we're the first to evaluate | ||||||||||
| * this property, or that the CssMetaData didn't include this property yet | ||||||||||
| * (not all styleables have stable CssMetaData, most notably Control). | ||||||||||
| */ | ||||||||||
|
|
||||||||||
| // slowpath! | ||||||||||
| calculatedValue = lookup(node, cssMetaData, styleMap, transitionStates[0], | ||||||||||
| node, cachedFont); | ||||||||||
|
|
||||||||||
|
|
@@ -959,33 +935,40 @@ void transitionToState(final Node node) { | |||||||||
| continue; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| cacheEntry.put(property, calculatedValue); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /* | ||||||||||
| * Skip this property (after caching) if it can't be set (usually because it is bound). | ||||||||||
| * The cached value is still useful for others sharing this entry. | ||||||||||
| */ | ||||||||||
|
|
||||||||||
| if (!cssMetaData.isSettable(node)) continue; | ||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer this style, but again, minor and is all of the place in this file anyway
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the contract for
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's an odd amount of detail that is rather implementation specific. I would suggest we remove it, as I don't think there are good reasons for this other than "that's how it works currently", and it actually is an active source of problems. A property being unsettable does not mean you can't evaluate the stylesheet to find what value it would have gotten (and that's what we do now). The only reason this method exists is that is faster than catching "RuntimeException: A bound value cannot be set.". There are two good reasons to still evaluate the CSS value (even if we won't set it):
If there are good reasons to keep it, then the only other option we have is to NOT create a cache entry at all when a Node has any CSS property bound (a sibling that is in a better state may create it still though). This could be a performance hazard though, because if it is the only node of its kind, it would always need to go through the slow
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 for removing it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you mean removing the spec "This method is called before any styles are looked up for the given property." ?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I don't think that should have been in the spec in the first place, as it reveals basically the inner workings of the CSS system. Also, we're still respecting the return value of So I suggest rewriting the docs to: Changing the sentence "This method is called before any styles are looked up for the given property." to "This method is called before any call to {@code applyStyle} is made." -- in both cases this ensures the property is not expanded unnecessarily, but gives the CSS system the freedom to still evaluate the stylesheet so it can create a shared cache entry that is complete. |
||||||||||
|
|
||||||||||
| // StyleableProperty#applyStyle might throw an exception and it is called | ||||||||||
| // from two places in this try block. | ||||||||||
| try { | ||||||||||
|
|
||||||||||
| // | ||||||||||
| // JDK-8127435 | ||||||||||
| // If the current value of the property was set by CSS | ||||||||||
| // and there is no style for the property, then reset this | ||||||||||
| // property to its initial value. If it was not set by CSS | ||||||||||
| // then leave the property alone. | ||||||||||
| // | ||||||||||
| if (calculatedValue == null || calculatedValue == SKIP) { | ||||||||||
| /* | ||||||||||
| * JDK-8127435: If there is no style for the property (SKIP), then check if it must be reset | ||||||||||
| * to its initial value. Otherwise, continue with CSS application. | ||||||||||
| */ | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. minor: I would have used |
||||||||||
|
|
||||||||||
| if (calculatedValue == SKIP) { // calculatedValue is never null here | ||||||||||
|
|
||||||||||
| // cssSetProperties keeps track of the StyleableProperty's that were set by CSS in the previous state. | ||||||||||
| // If this property is not in cssSetProperties map, then the property was not set in the previous state. | ||||||||||
| // This accomplishes two things. First, it lets us know if the property was set in the previous state | ||||||||||
| // so it can be reset in this state if there is no value for it. Second, it calling | ||||||||||
| // so it can be reset in this state if there is no value for it. Second, it avoids calling | ||||||||||
| // CssMetaData#getStyleableProperty which is rather expensive as it may cause expansion of lazy | ||||||||||
| // properties. | ||||||||||
| CalculatedValue initialValue = cacheContainer.cssSetProperties.get(cssMetaData); | ||||||||||
|
|
||||||||||
| // if the current value was set by CSS and there | ||||||||||
| // is no calculated value for the property, then | ||||||||||
| // there was no style for the property in the current | ||||||||||
| // state, so reset the property to its initial value. | ||||||||||
| /* | ||||||||||
| * If the initial value is not null, then the property was set by CSS | ||||||||||
| * on this node, and so it must be reset: | ||||||||||
| */ | ||||||||||
|
|
||||||||||
| if (initialValue != null) { | ||||||||||
| resetToInitialValue(node, cssMetaData, initialValue); | ||||||||||
| } | ||||||||||
|
|
@@ -994,13 +977,6 @@ void transitionToState(final Node node) { | |||||||||
|
|
||||||||||
| } | ||||||||||
|
|
||||||||||
| if (addToCache) { | ||||||||||
|
|
||||||||||
| // If we're not on the fastpath, then add the calculated | ||||||||||
| // value to cache. | ||||||||||
| cacheEntry.put(property, calculatedValue); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| StyleableProperty styleableProperty = cssMetaData.getStyleableProperty(node); | ||||||||||
|
|
||||||||||
| // need to know who set the current value - CSS, the user, or init | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like the value is stored by property name, which means if the SKIP value was stored and then the metadata was replaced (by setting a skin, for example, which uses the same property names), the old SKIP would prevent re-evaluation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A property always evaluates the same, as this is dictated by the stylesheet, not by the presence or absence of a property in the CSS metadata. So the stylesheet says:
xyzmust be5here, and if you replace the skin and it has the same property, that evaluation is still correct.There may be a problem if that new property is of a different type (I didn't test), but I consider that beyond the scope of this fix as that has never worked. If you see an easy solution, I can consider adding it -- my primary goal was to fix the customer found regression that seemed to have been due to #1076 but in reality has been there much longer, only less visible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit it's a rather contrived scenario, so I am not concerned. The devs can always work around this by using different property names.
Maybe just add a comment pointing to this scenario?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this to
StyleCacheEntry: