Skip to content

Commit

Permalink
Fixes #2692. Ensures that custom-style properties are applied async b…
Browse files Browse the repository at this point in the history
…ut before next render so that all properties are defined before any are consumed by custom-styles. Also refines dom-module's early upgrade code so that it does not affect other elements (corrects for example, custom-styles upgrading before expected).
  • Loading branch information
Steven Orvell committed Nov 12, 2015
1 parent cb68ce5 commit b829f2a
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 26 deletions.
6 changes: 5 additions & 1 deletion src/lib/custom-style.html
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,11 @@
styleUtil.forEachStyleRule(styleUtil.rulesForStyle(e), function(rule) {
styleTransformer.documentRule(rule);
});
this._applyCustomProperties(e);
// Allow all custom-styles defined in this turn to register
// before applying any properties. This helps ensure that all properties
// are defined before any are consumed.
Polymer.RenderStatus.beforeNextRender(this,
this._applyCustomProperties, [e]);
}
},

Expand Down
18 changes: 13 additions & 5 deletions src/lib/dom-module.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@
if (!m) {
// If polyfilling, a script can run before a dom-module element
// is upgraded. We force the containing document to upgrade
// and try again to workaround this polyfill limitation.
forceDocumentUpgrade();
// dom-modules and try again to workaround this polyfill limitation.
forceDomModulesUpgrade();
m = findModule(id);
}
if (m && selector) {
Expand All @@ -97,12 +97,20 @@
var cePolyfill = window.CustomElements && !CustomElements.useNative;
document.registerElement('dom-module', DomModule);

function forceDocumentUpgrade() {
function forceDomModulesUpgrade() {
if (cePolyfill) {
var script = document._currentScript || document.currentScript;
var doc = script && script.ownerDocument || document;
if (doc) {
CustomElements.upgradeAll(doc);
// find all dom-modules
var modules = doc.querySelectorAll('dom-module');
// minimize work by going backwards and stopping if we find an
// upgraded module.
for (var i= modules.length-1, m; (i >=0) && (m=modules[i]); i--) {
if (m.__upgraded__) {
return;
} else {
CustomElements.upgrade(m);
}
}
}
}
Expand Down
44 changes: 31 additions & 13 deletions src/lib/render-status.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,33 +50,51 @@
},

_afterNextRenderQueue: [],
_beforeNextRenderQueue: [],

_waitingNextRender: false,

afterNextRender: function(element, fn, args) {
this._watchNextRender();
this._afterNextRenderQueue.push([element, fn, args]);
},

beforeNextRender: function(element, fn, args) {
this._watchNextRender();
this._beforeNextRenderQueue.push([element, fn, args]);
},

_watchNextRender: function() {
if (!this._waitingNextRender) {
this._waitingNextRender = true;
this.whenReady(this._flushAfterNextRender);
var fn = function() {
Polymer.RenderStatus._flushNextRender();
}
if (!this._ready) {
this.whenReady(fn);
} else {
requestAnimationFrame(fn);
}
}
this._afterNextRenderQueue.push([element, fn, args]);
},

_flushAfterNextRender: function() {
// we want to defer flush until just after the next paint.
requestAnimationFrame(function() {
setTimeout(Polymer.RenderStatus.__flushAfterNextRender);
_flushNextRender: function() {
this._waitingNextRender = false;
this._flushRenderCallbacks(this._beforeNextRenderQueue);
this._beforeNextRenderQueue = [];
var self = this;
// we want to defer after render until just after the paint.
setTimeout(function() {
self._flushRenderCallbacks(self._afterNextRenderQueue);
self._afterNextRenderQueue = [];
});
},

__flushAfterNextRender: function() {
// called without context so force here.
var self = Polymer.RenderStatus;
self._waitingNextRender = false;
for (var i=0, h; i < self._afterNextRenderQueue.length; i++) {
h = self._afterNextRenderQueue[i];
_flushRenderCallbacks: function(callbacks) {
for (var i=0, h; i < callbacks.length; i++) {
h = callbacks[i];
h[1].apply(h[0], h[2] || Polymer.nar);
};
self._afterNextRenderQueue = [];
}
};

Expand Down
1 change: 1 addition & 0 deletions test/runner.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
// 'unit/styling-cross-scope-apply.html?dom=shadow',
'unit/styling-cross-scope-unknown-host.html',
'unit/custom-style.html',
'unit/custom-style-late.html',
'unit/dynamic-import.html',
'unit/templatizer.html',
'unit/dom-repeat.html',
Expand Down
19 changes: 19 additions & 0 deletions test/unit/custom-style-late-import.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
Polymer({
is: 'x-input',
extends : 'input'
});
</script>
<style is="custom-style">
input[is=x-input] {
border : 4px solid red;
@apply (--cs-blue);
}
</style>
<style is="custom-style">
:root {
--cs-blue: {
border : 8px solid blue;
};
}
</style>
42 changes: 42 additions & 0 deletions test/unit/custom-style-late.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!doctype html>
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
<meta charset="utf-8">
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../../web-component-tester/browser.js"></script>
<link rel="import" href="../../polymer.html">
<link rel="import" href="custom-style-late-import.html">

</head>
<body>
<input id="input" is="x-input">

<script>

suite('custom-style late property definition', function() {

test('late defined properties applied to custom-style', function() {
assertComputed(input, '8px');
});

});

function assertComputed(element, value, property, pseudo) {
var computed = getComputedStyle(element, pseudo);
property = property || 'border-top-width';
assert.equal(computed[property], value, 'computed style incorrect for ' + property);
}

</script>

</body>
</html>
17 changes: 10 additions & 7 deletions test/unit/custom-style.html
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,11 @@
var ds = document.createElement('style', 'custom-style');
ds.textContent = ':root { --dynamic: 11px solid orange; }';
document.head.appendChild(ds);
setTimeout(function() {
Polymer.updateStyles();
Polymer.updateStyles();
Polymer.RenderStatus.afterNextRender(null, function() {
assertComputed(dynamic, '11px');
done();
}, 0);
});
});

test('custom-styles apply normal and property values to elements and cannot be late bound via inheritance', function() {
Expand Down Expand Up @@ -409,17 +409,20 @@
document.body.removeChild(style);
});

test('imperative custom style with include', function() {
test('imperative custom style with include', function(done) {
var style = document.createElement('style', 'custom-style');
style.include = 'shared-style2';
var d = document.createElement('div');
d.classList.add('zazz');
document.body.appendChild(d);
document.body.appendChild(style);
CustomElements.takeRecords();
assertComputed(d, '16px');
document.body.removeChild(d);
document.body.removeChild(style);
Polymer.RenderStatus.afterNextRender(null, function() {
assertComputed(d, '16px');
document.body.removeChild(d);
document.body.removeChild(style);
done();
});
});

test('imperative custom style with non-existent include', function() {
Expand Down

0 comments on commit b829f2a

Please sign in to comment.