From b829f2a3aa85b106800ed2be29168506a591b407 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Thu, 12 Nov 2015 15:09:48 -0800 Subject: [PATCH] Fixes #2692. Ensures that custom-style properties are applied async but 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). --- src/lib/custom-style.html | 6 +++- src/lib/dom-module.html | 18 +++++++--- src/lib/render-status.html | 44 +++++++++++++++++-------- test/runner.html | 1 + test/unit/custom-style-late-import.html | 19 +++++++++++ test/unit/custom-style-late.html | 42 +++++++++++++++++++++++ test/unit/custom-style.html | 17 ++++++---- 7 files changed, 121 insertions(+), 26 deletions(-) create mode 100644 test/unit/custom-style-late-import.html create mode 100644 test/unit/custom-style-late.html diff --git a/src/lib/custom-style.html b/src/lib/custom-style.html index 4c78e526d9..ff791d650b 100644 --- a/src/lib/custom-style.html +++ b/src/lib/custom-style.html @@ -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]); } }, diff --git a/src/lib/dom-module.html b/src/lib/dom-module.html index 36903984f2..4420bca61f 100644 --- a/src/lib/dom-module.html +++ b/src/lib/dom-module.html @@ -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) { @@ -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); + } } } } diff --git a/src/lib/render-status.html b/src/lib/render-status.html index eb687cf377..493c76dd74 100644 --- a/src/lib/render-status.html +++ b/src/lib/render-status.html @@ -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 = []; } }; diff --git a/test/runner.html b/test/runner.html index b2431e9acf..f92b70f780 100644 --- a/test/runner.html +++ b/test/runner.html @@ -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', diff --git a/test/unit/custom-style-late-import.html b/test/unit/custom-style-late-import.html new file mode 100644 index 0000000000..fa6f62beeb --- /dev/null +++ b/test/unit/custom-style-late-import.html @@ -0,0 +1,19 @@ + + + \ No newline at end of file diff --git a/test/unit/custom-style-late.html b/test/unit/custom-style-late.html new file mode 100644 index 0000000000..92a28df75b --- /dev/null +++ b/test/unit/custom-style-late.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + diff --git a/test/unit/custom-style.html b/test/unit/custom-style.html index 7c7b702e00..65630f2ac2 100644 --- a/test/unit/custom-style.html +++ b/test/unit/custom-style.html @@ -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() { @@ -409,7 +409,7 @@ 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'); @@ -417,9 +417,12 @@ 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() {