Skip to content

Commit

Permalink
Allow style elements to be separate in the element template.
Browse files Browse the repository at this point in the history
Previously Polymer collapsed any included shared styles into 1 style element. This prevents the browser from recognizing shared style elements as similar and optimizing them. With this change, we no longer collapse styles. Note, included styles are still currently collapsed by ShadyCSS but the polyfill will likely not do this in the future.
  • Loading branch information
Steven Orvell committed Oct 19, 2017
1 parent 982d28c commit 819652e
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 54 deletions.
4 changes: 2 additions & 2 deletions lib/mixins/dir-mixin.html
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@
* @override
* @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
*/
static _processStyleText(is, template, baseURI) {
let cssText = super._processStyleText(is, template, baseURI);
static _processStyleText(cssText, baseURI) {
cssText = super._processStyleText(cssText, baseURI);
cssText = this._replaceDirInCssText(cssText);
return cssText;
}
Expand Down
55 changes: 38 additions & 17 deletions lib/mixins/element-mixin.html
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,37 @@
}
}

/**
* Process all style elements in the element template. Styles with the
* `include` attribute are processed such that any styles in
* the associated "style modules" are included in the element template.
* @param {PolymerElementConstructor} klass Element class
* @param {!HTMLTemplateElement} template Template to process
* @param {string} is Name of element
* @param {string} ext Name of native element
* @param {string} baseURI Base URI for element
* @private
*/
function processElementStyles(klass, template, is, ext, baseURI) {
const styles = Polymer.StyleGather.stylesFromModuleImports(is).concat(
Polymer.StyleGather.stylesFromTemplate(template));
let templateStyles = template.content.querySelectorAll('style');
let lastStyle = templateStyles[templateStyles.length-1];
for (let i=0; i < styles.length; i++) {
let s = styles[i];
// if the style is not in this template, it's been "included" and
// we put a clone of it in the template.
if (s.getRootNode() != template.content) {
s = s.cloneNode(true);
template.content.insertBefore(s, lastStyle);
}
s.textContent = klass._processStyleText(s.textContent, baseURI);
}
if (window.ShadyCSS) {
window.ShadyCSS.prepareTemplate(template, is, ext);
}
}

/**
* @polymer
* @mixinClass
Expand Down Expand Up @@ -578,17 +609,15 @@
}

/**
* Gather style text for the template
* Gather style text for a style element in the template.
*
* @param {string} is Tag name for this element
* @param {!HTMLTemplateElement} template Template to process
* @param {string} cssText Text containing styling to process
* @param {string} baseURI Base URI to rebase CSS paths against
* @return {string} The combined CSS text
* @return {string} The processed CSS text
* @protected
*/
static _processStyleText(is, template, baseURI) {
return Polymer.StyleGather.cssFromModuleImports(is) +
Polymer.StyleGather.cssFromTemplate(template, baseURI);
static _processStyleText(cssText, baseURI) {
return Polymer.ResolveUrl.resolveCss(cssText, baseURI);
}

/**
Expand All @@ -607,16 +636,8 @@
template.__polymerFinalized = true;
const importPath = this.importPath;
const baseURI = importPath ? Polymer.ResolveUrl.resolveUrl(importPath) : '';
// support `include="module-name"`
let cssText = this._processStyleText(is, template, baseURI);
if (cssText) {
let style = document.createElement('style');
style.textContent = cssText;
template.content.insertBefore(style, template.content.firstChild);
}
if (window.ShadyCSS) {
window.ShadyCSS.prepareTemplate(template, is, ext);
}
// e.g. support `include="module-name"`, and ShadyCSS
processElementStyles(this, template, is, ext, baseURI);
this.prototype._bindTemplate(template);
}
}
Expand Down
192 changes: 158 additions & 34 deletions lib/utils/style-gather.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,154 @@
const StyleGather = {

/**
* Returns a list of <style> elements in a space-separated list of `dom-module`s.
*
* @memberof Polymer.StyleGather
* @param {string} moduleIds List of dom-module id's within which to
* search for css.
* @return {array} Array of contained <style> elements
* @this {StyleGather}
*/
stylesFromModules(moduleIds) {
const modules = moduleIds.trim().split(/\s+/);
const styles = [];
for (let i=0; i < modules.length; i++) {
styles.push(...this.stylesFromModule(modules[i]));
}
return styles;
},

/**
* Returns a list of <style> elements in a given `dom-module`.
* Styles in a `dom-module` can come either from `<style>`s within the
* first `<template>`, or else from one or more
* `<link rel="import" type="css">` links outside the template.
*
* @memberof Polymer.StyleGather
* @param {string} moduleId dom-module id to gather styles from
* @return {array} Array of contained styles.
* @this {StyleGather}
*/
stylesFromModule(moduleId) {
const m = importModule(moduleId);
if (m && m._styles === undefined) {
const styles = [];
// module imports: <link rel="import" type="css">
styles.push(...this._stylesFromModuleImports(m));
// include css from the first template in the module
const template = m.querySelector('template');
if (template) {
styles.push(...this.stylesFromTemplate(template,
/** @type {templateWithAssetPath} */(m).assetpath));
}
m._styles = styles;
}
if (!m) {
console.warn('Could not find style data in module named', moduleId);
}
return m ? m._styles : [];
},

/**
* Returns the `<style>` elements within a given template.
*
* @memberof Polymer.StyleGather
* @param {HTMLTemplateElement} template Template to gather styles from
* @return {array} Array of styles
* @this {StyleGather}
*/
stylesFromTemplate(template, baseURI) {
if (!template._styles) {
const styles = [];
// if element is a template, get content from its .content
const e$ = template.content.querySelectorAll('style');
for (let i=0; i < e$.length; i++) {
let e = e$[i];
// support style sharing by allowing styles to "include"
// other dom-modules that contain styling
let include = e.getAttribute(INCLUDE_ATTR);
if (include) {
styles.push(...this.stylesFromModules(include));
}
if (window.ShadyDOM && e.hasAttribute(SHADY_UNSCOPED_ATTR)) {
e.textContent = baseURI ?
Polymer.ResolveUrl.resolveCss(e.textContent, baseURI) : e.textContent;
document.head.insertBefore(e, document.head.firstChild);
} else {
styles.push(e);
}
}
template._styles = styles;
}
return template._styles;
},

/**
* Returns a list of <style> elements from stylesheets loaded via `<link rel="import" type="css">` links within the specified `dom-module`.
*
* @memberof Polymer.StyleGather
* @param {string} moduleId Id of `dom-module` to gather CSS from
* @return {array} Array of contained styles.
* @this {StyleGather}
*/
stylesFromModuleImports(moduleId) {
let m = importModule(moduleId);
return m ? this._stylesFromModuleImports(m) : [];
},

/**
* @memberof Polymer.StyleGather
* @this {StyleGather}
* @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles
* @return {array} Array of contained styles
*/
_stylesFromModuleImports(module) {
const styles = [];
const p$ = module.querySelectorAll(MODULE_STYLE_LINK_SELECTOR);
for (let i=0; i < p$.length; i++) {
let p = p$[i];
if (p.import) {
const importDoc = p.import;
if (!importDoc._style) {
// NOTE: polyfill affordance.
// under the HTMLImports polyfill, there will be no 'body',
// but the import pseudo-doc can be used directly.
let container = importDoc.body ? importDoc.body : importDoc;
const importCss = Polymer.ResolveUrl.resolveCss(container.textContent,
importDoc.baseURI);
const style = document.createElement('style');
if (p.hasAttribute(SHADY_UNSCOPED_ATTR)) {
style.setAttribute(SHADY_UNSCOPED_ATTR, '');
}
style.textContent = importCss;
importDoc._style = style;
}
// support the shady-unscoped promoting styles to main document
if (window.ShadyDOM &&
importDoc._style.hasAttribute(SHADY_UNSCOPED_ATTR) &&
!importDoc._style.parentNode) {
document.head.insertBefore(importDoc._style, document.head.firstChild);
} else {
styles.push(importDoc._style);
}
}
}
return styles;
},

/**
*
* Returns CSS text of styles in a space-separated list of `dom-module`s.
* Note: This method is deprecated, use `stylesFromModules` instead.
*
* @deprecated
* @memberof Polymer.StyleGather
* @param {string} moduleIds List of dom-module id's within which to
* search for css.
* @return {string} Concatenated CSS content from specified `dom-module`s
* @this {StyleGather}
*/
cssFromModules(moduleIds) {
cssFromModules(moduleIds) {
let modules = moduleIds.trim().split(/\s+/);
let cssText = '';
for (let i=0; i < modules.length; i++) {
Expand All @@ -63,7 +202,9 @@
* template.
*
* Any `<styles>` processed are removed from their original location.
* Note: This method is deprecated, use `styleFromModule` instead.
*
* @deprecated
* @memberof Polymer.StyleGather
* @param {string} moduleId dom-module id to gather styles from
* @return {string} Concatenated CSS content from specified `dom-module`
Expand Down Expand Up @@ -91,7 +232,9 @@
* Returns CSS text of `<styles>` within a given template.
*
* Any `<styles>` processed are removed from their original location.
* Note: This method is deprecated, use `styleFromTemplate` instead.
*
* @deprecated
* @memberof Polymer.StyleGather
* @param {HTMLTemplateElement} template Template to gather styles from
* @param {string} baseURI Base URI to resolve the URL against
Expand All @@ -100,17 +243,13 @@
*/
cssFromTemplate(template, baseURI) {
let cssText = '';
const e$ = this.stylesFromTemplate(template, baseURI);
// if element is a template, get content from its .content
let e$ = template.content.querySelectorAll('style');
for (let i=0; i < e$.length; i++) {
let e = e$[i];
// support style sharing by allowing styles to "include"
// other dom-modules that contain styling
let include = e.getAttribute(INCLUDE_ATTR);
if (include) {
cssText += this.cssFromModules(include);
if (e.parentNode) {
e.parentNode.removeChild(e);
}
e.parentNode.removeChild(e);
const styleCss = baseURI ?
Polymer.ResolveUrl.resolveCss(e.textContent, baseURI) : e.textContent;
if (window.ShadyDOM && e.hasAttribute(SHADY_UNSCOPED_ATTR)) {
Expand All @@ -127,6 +266,10 @@
* Returns CSS text from stylesheets loaded via `<link rel="import" type="css">`
* links within the specified `dom-module`.
*
* Note: This method is deprecated, use `stylesFromModuleImports` instead.
*
* @deprecated
*
* @memberof Polymer.StyleGather
* @param {string} moduleId Id of `dom-module` to gather CSS from
* @return {string} Concatenated CSS content from links in specified `dom-module`
Expand All @@ -136,41 +279,22 @@
let m = importModule(moduleId);
return m ? this._cssFromModuleImports(m) : '';
},

/**
* @deprecated
* @memberof Polymer.StyleGather
* @this {StyleGather}
* @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles
* @return {string} Concatenated CSS content from links in the dom-module
*/
_cssFromModuleImports(module) {
_cssFromModuleImports(module) {
let cssText = '';
let p$ = module.querySelectorAll(MODULE_STYLE_LINK_SELECTOR);
for (let i=0; i < p$.length; i++) {
let p = p$[i];
if (p.import) {
let importDoc = p.import;
// NOTE: polyfill affordance.
// under the HTMLImports polyfill, there will be no 'body',
// but the import pseudo-doc can be used directly.
let container = importDoc.body ? importDoc.body : importDoc;
const importCss = Polymer.ResolveUrl.resolveCss(container.textContent,
importDoc.baseURI);
// support the shady-unscoped promoting styles to main document
if (window.ShadyDOM && p.hasAttribute(SHADY_UNSCOPED_ATTR)) {
if (!unscopedStyleImportsMap.has(importDoc)) {
unscopedStyleImportsMap.set(importDoc);
const style = document.createElement('style');
style.setAttribute(SHADY_UNSCOPED_ATTR, '');
style.textContent = importCss;
document.head.insertBefore(style, document.head.firstChild);
}
} else {
cssText += importCss;
}
}
let styles = this._stylesFromModuleImports(module);
for (let i=0; i < styles.length; i++) {
cssText += styles[i].textContent;
}
return cssText;
}
},
};

Polymer.StyleGather = StyleGather;
Expand Down
3 changes: 2 additions & 1 deletion test/runner.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
'unit/mixin-behaviors.html',
'unit/render-status.html',
'unit/dir.html',
'unit/shady-unscoped-style.html'
'unit/shady-unscoped-style.html',
'unit/multi-style.html'
];

// http://eddmann.com/posts/cartesian-product-in-javascript/
Expand Down
Loading

0 comments on commit 819652e

Please sign in to comment.