Skip to content

Commit be74f7e

Browse files
authored
Merge pull request Polymer#3807 from Polymer/invalidating-apply-shim
Rerun Apply Shim when mixins with consumers are redefined
2 parents 19d9f3c + 0859803 commit be74f7e

File tree

6 files changed

+194
-20
lines changed

6 files changed

+194
-20
lines changed

polymer.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
this._setupShady();
7373
this._registerHost();
7474
if (this._template) {
75+
this._validateApplyShim();
7576
// manage local dom
7677
this._poolContent();
7778
// host stack
@@ -88,7 +89,7 @@
8889
// acquire instance behaviors
8990
this._marshalBehaviors();
9091
/*
91-
TODO(sorvell): It's *slightly() more efficient to marshal attributes prior
92+
TODO(sorvell): It's *slightly() more efficient to marshal attributes prior
9293
to installing hostAttributes, but then hostAttributes must be separately
9394
funneled to configure, which is cumbersome.
9495
Since this delta seems hard to measure we will not bother atm.

src/lib/apply-shim.html

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,15 @@
8686
var MIXIN_VAR_SEP = '_-_';
8787

8888
// map of mixin to property names
89-
// --foo: {border: 2px} -> (--foo, ['border'])
89+
// --foo: {border: 2px} -> {properties: {(--foo, ['border'])}, dependants: {'element-name': proto}}
9090
var mixinMap = {};
9191

92-
function mapSet(name, prop) {
92+
function mapSet(name, props) {
9393
name = name.trim();
94-
mixinMap[name] = prop;
94+
mixinMap[name] = {
95+
properties: props,
96+
dependants: {}
97+
};
9598
}
9699

97100
function mapGet(name) {
@@ -118,6 +121,16 @@
118121
return out;
119122
}
120123

124+
function invalidateMixinEntry(mixinEntry) {
125+
var currentProto = ApplyShim.__currentElementProto;
126+
var currentElementName = currentProto && currentProto.is;
127+
for (var elementName in mixinEntry.dependants) {
128+
if (elementName !== currentElementName) {
129+
mixinEntry.dependants[elementName].__applyShimInvalid = true;
130+
}
131+
}
132+
}
133+
121134
function produceCssProperties(matchText, propertyName, valueProperty, valueMixin) {
122135
// handle case where property value is a mixin
123136
if (valueProperty) {
@@ -133,26 +146,38 @@
133146
var mixinAsProperties = consumeCssProperties(valueMixin);
134147
var prefix = matchText.slice(0, matchText.indexOf('--'));
135148
var mixinValues = cssTextToMap(mixinAsProperties);
136-
var oldProperties = mapGet(propertyName);
137149
var combinedProps = mixinValues;
138-
if (oldProperties) {
150+
var mixinEntry = mapGet(propertyName);
151+
var oldProps = mixinEntry && mixinEntry.properties;
152+
if (oldProps) {
139153
// NOTE: since we use mixin, the map of properties is updated here
140154
// and this is what we want.
141-
combinedProps = Polymer.Base.mixin(oldProperties, mixinValues);
155+
combinedProps = Object.create(oldProps);
156+
combinedProps = Polymer.Base.mixin(combinedProps, mixinValues);
142157
} else {
143158
mapSet(propertyName, combinedProps);
144159
}
145160
var out = [];
146161
var p, v;
147162
// set variables defined by current mixin
163+
var needToInvalidate = false;
148164
for (p in combinedProps) {
149165
v = mixinValues[p];
150166
// if property not defined by current mixin, set initial
151167
if (v === undefined) {
152168
v = 'initial';
153169
}
170+
if (oldProps && !(p in oldProps)) {
171+
needToInvalidate = true;
172+
}
154173
out.push(propertyName + MIXIN_VAR_SEP + p + ': ' + v);
155174
}
175+
if (needToInvalidate) {
176+
invalidateMixinEntry(mixinEntry);
177+
}
178+
if (mixinEntry) {
179+
mixinEntry.properties = combinedProps;
180+
}
156181
return prefix + out.join('; ') + ';';
157182
}
158183

@@ -170,10 +195,14 @@
170195
function atApplyToCssProperties(mixinName, fallbacks) {
171196
mixinName = mixinName.replace(APPLY_NAME_CLEAN, '');
172197
var vars = [];
173-
var mixinProperties = mapGet(mixinName);
174-
if (mixinProperties) {
198+
var mixinEntry = mapGet(mixinName);
199+
if (mixinEntry) {
200+
var currentProto = ApplyShim.__currentElementProto;
201+
if (currentProto) {
202+
mixinEntry.dependants[currentProto.is] = currentProto;
203+
}
175204
var p, parts, f;
176-
for (p in mixinProperties) {
205+
for (p in mixinEntry.properties) {
177206
f = fallbacks && fallbacks[p];
178207
parts = [p, ': var(', mixinName, MIXIN_VAR_SEP, p];
179208
if (f) {
@@ -214,8 +243,11 @@
214243
var ApplyShim = {
215244
_map: mixinMap,
216245
_separator: MIXIN_VAR_SEP,
217-
transform: function(styles) {
246+
transform: function(styles, elementProto) {
247+
this.__currentElementProto = elementProto;
218248
styleUtil.forRulesInStyles(styles, this._boundTransformRule);
249+
elementProto.__applyShimInvalid = false;
250+
this.__currentElementProto = null;
219251
},
220252
transformRule: function(rule) {
221253
rule.cssText = this.transformCssText(rule.parsedCssText);

src/lib/custom-style.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
// TODO(sorvell): we could only do this if and only if
133133
// this.ownerDocument != document;
134134
// however, if we do that, we also have to change the `attached`
135-
// code to go at `_beforeAttached` time because this is when
135+
// code to go at `_beforeAttached` time because this is when
136136
// elements produce styles (otherwise this breaks @apply shim)
137137
this._tryApply();
138138
},
@@ -153,7 +153,7 @@
153153
var e = this.__appliedElement;
154154
// used applied element from HTMLImports polyfill or this
155155
if (!settings.useNativeCSSProperties) {
156-
// if default style properties exist when
156+
// if default style properties exist when
157157
// this element tries to apply styling then,
158158
// it has been loaded async and needs to trigger a full updateStyles
159159
// to guarantee properties it provides update correctly.
@@ -256,7 +256,7 @@
256256
_flushCustomProperties: function() {
257257
// if this style has not yet applied at all and it was loaded asynchronously
258258
// (detected by Polymer being ready when this element tried to apply), then
259-
// do a full updateStyles to ensure that
259+
// do a full updateStyles to ensure that
260260
if (this.__needsUpdateStyles) {
261261
this.__needsUpdateStyles = false;
262262
updateDebouncer = debounce(updateDebouncer, this._updateStyles);

src/standard/styling.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
// We can avoid *all* shimming if native properties are used
6464
// and there is a shadow css build and we are using native shadow.
6565
var hasTargetedCssBuild = styleUtil.isTargetedBuild(this.__cssBuild);
66-
if (settings.useNativeCSSProperties && this.__cssBuild === 'shadow'
66+
if (settings.useNativeCSSProperties && this.__cssBuild === 'shadow'
6767
&& hasTargetedCssBuild) {
6868
return;
6969
}
@@ -72,12 +72,12 @@
7272
// css is calculated.
7373
// css build takes care of apply shim, so avoid doing this work.
7474
if (settings.useNativeCSSProperties && !this.__cssBuild) {
75-
applyShim.transform(this._styles);
75+
applyShim.transform(this._styles, this);
7676
}
7777
// calculate element static styling (with a targeted build and native
7878
// properties, there's only 1 style and no need to parse it!
79-
var cssText = settings.useNativeCSSProperties && hasTargetedCssBuild ?
80-
(this._styles.length && this._styles[0].textContent.trim()) :
79+
var cssText = settings.useNativeCSSProperties && hasTargetedCssBuild ?
80+
(this._styles.length && this._styles[0].textContent.trim()) :
8181
styleTransformer.elementStyles(this);
8282
// prepare to shim style properties.
8383
this._prepStyleProperties();

src/standard/x-styling.html

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,31 @@
7474
this._ownStylePropertyNames.length);
7575
},
7676

77+
_validateApplyShim: function() {
78+
if (this.__applyShimInvalid) {
79+
// rerun apply shim
80+
Polymer.ApplyShim.transform(this._styles, this.__proto__);
81+
var cssText = styleTransformer.elementStyles(this);
82+
if (nativeShadow) {
83+
// replace style in template
84+
var templateStyle = this._template.content.querySelector('style');
85+
if (templateStyle) {
86+
templateStyle.textContent = cssText;
87+
}
88+
} else {
89+
// replace scoped style
90+
var shadyStyle = this._scopeStyle && this._scopeStyle.nextSibling;
91+
if (shadyStyle) {
92+
shadyStyle.textContent = cssText;
93+
}
94+
}
95+
}
96+
},
97+
7798
_beforeAttached: function() {
7899
// note: do this once automatically,
79100
// then requires calling `updateStyles`
80-
if ((!this._scopeSelector || this.__stylePropertiesInvalid) &&
101+
if ((!this._scopeSelector || this.__stylePropertiesInvalid) &&
81102
this._needsStyleProperties()) {
82103
this.__stylePropertiesInvalid = false;
83104
this._updateStyleProperties();
@@ -273,7 +294,7 @@
273294
} else {
274295
this._styleProperties = null;
275296
}
276-
// if called when an element is not attached, invalidate
297+
// if called when an element is not attached, invalidate
277298
// styling by unsetting scopeSelector.
278299
} else {
279300
this.__stylePropertiesInvalid = true;

test/unit/styling-cross-scope-apply.html

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,92 @@
447447
</script>
448448
</dom-module>
449449

450+
<dom-module id="x-consume-mixin">
451+
<template>
452+
<style>
453+
:host {
454+
@apply --above;
455+
}
456+
</style>
457+
<span>X</span>
458+
</template>
459+
<script>
460+
HTMLImports.whenReady(function() {
461+
Polymer({
462+
is: 'x-consume-mixin'
463+
})
464+
})
465+
</script>
466+
</dom-module>
467+
468+
<dom-module id="x-produce-mixin-1">
469+
<template>
470+
<style>
471+
:host {
472+
--above: {
473+
color: rgb(255, 0, 0);
474+
};
475+
}
476+
</style>
477+
<x-consume-mixin id="child"></x-consume-mixin>
478+
</template>
479+
<script>
480+
HTMLImports.whenReady(function() {
481+
Polymer({
482+
is: 'x-produce-mixin-1'
483+
})
484+
})
485+
</script>
486+
</dom-module>
487+
488+
<dom-module id="x-produce-mixin-2">
489+
<template>
490+
<style>
491+
:host {
492+
--above: {
493+
background-color: rgb(0, 255, 0);
494+
};
495+
}
496+
</style>
497+
<x-consume-mixin id="child"></x-consume-mixin>
498+
</template>
499+
<script>
500+
HTMLImports.whenReady(function() {
501+
Polymer({
502+
is: 'x-produce-mixin-2'
503+
})
504+
})
505+
</script>
506+
</dom-module>
507+
508+
<dom-module id="x-produce-mixin-3">
509+
<template>
510+
<style>
511+
:host {
512+
--above: {
513+
color: rgb(255, 0, 0);
514+
};
515+
}
516+
</style>
517+
<x-consume-mixin id="child"></x-consume-mixin>
518+
</template>
519+
<script>
520+
HTMLImports.whenReady(function() {
521+
Polymer({
522+
is: 'x-produce-mixin-3'
523+
})
524+
})
525+
</script>
526+
</dom-module>
527+
528+
<style is="custom-style">
529+
:root {
530+
--above: {
531+
border: 8px solid blue;
532+
}
533+
}
534+
</style>
535+
450536
<script>
451537
suite('scoped-styling-apply', function() {
452538
function assertComputed(element, value, property) {
@@ -616,6 +702,40 @@
616702
assertComputed(div, '2px');
617703
assertComputed(div, '0px', 'height');
618704
});
705+
706+
test('Redefined mixins apply new properties for future elements', function() {
707+
function getStyleText(element) {
708+
if (Polymer.Settings.useNativeShadow) {
709+
return element.shadowRoot.querySelector('style').textContent;
710+
} else {
711+
var shadyStyle = element._scopeStyle && element._scopeStyle.nextSibling;
712+
if (shadyStyle) {
713+
return shadyStyle.textContent;
714+
}
715+
return '';
716+
}
717+
}
718+
var parent1 = document.createElement('x-produce-mixin-1');
719+
var parent2 = document.createElement('x-produce-mixin-2');
720+
var parent3 = document.createElement('x-produce-mixin-3');
721+
document.body.appendChild(parent1);
722+
document.body.appendChild(parent2);
723+
document.body.appendChild(parent3);
724+
CustomElements.takeRecords();
725+
assertComputed(parent1.$.child, 'rgb(255, 0, 0)', 'color');
726+
assertComputed(parent2.$.child, 'rgb(0, 255, 0)', 'background-color');
727+
assertComputed(parent1.$.child, '0px');
728+
assertComputed(parent2.$.child, '0px');
729+
assertComputed(parent3.$.child, 'rgb(255, 0, 0)', 'color');
730+
assertComputed(parent3.$.child, '0px');
731+
if (Polymer.Settings.useNativeCSSProperties && Polymer.Settings.useNativeShadow) {
732+
var parent1Text = getStyleText(parent1.$.child);
733+
var parent2Text = getStyleText(parent2.$.child);
734+
var parent3Text = getStyleText(parent3.$.child);
735+
assert.notEqual(parent1Text, parent2Text, 'x-produce-mixin-2 should invalidate x-consume-mixin');
736+
assert.equal(parent2Text, parent3Text, 'x-produce-mixin-3 should not have invalidated x-consume-mixin');
737+
}
738+
});
619739
});
620740

621741
</script>

0 commit comments

Comments
 (0)