Skip to content

Commit da49964

Browse files
author
Steve Orvell
authored
Merge pull request #4360 from Polymer/2.0-fix-4357
Fixes #4357.
2 parents 8dc2886 + 9cd7d80 commit da49964

12 files changed

+386
-186
lines changed

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,18 @@ id is to use `id`.
270270
* <a name="breaking-attribute-property-timing"></a>Any attribute values will take priority over property values set prior to upgrade due to V1 `attributeChangedCallback` timing semantics. In 1.x properties set prior to upgrade overrode attributes.
271271
* <a name="breaking-transpiling"></a>Polymer 2.0 uses ES2015 syntax, and can be run without transpilation in current Chrome, Safari 10, Safari Technology Preview, Firefox, and Edge. Transpilation is required to run in IE11 and Safari 9. We will be releasing tooling for development and production time to support this need in the future.
272272
* <a name="breaking-hostAttributes-class"></a>In Polymer 1.x, the `class` attribute was explicitly blacklisted from `hostAttributes` and never serialized. This is no longer the case using the 2.0 legacy API.
273+
* <a name="breaking-url-changes"></a>In Polymer 1.x, URLs in attributes and styles inside element templates were re-written to be relative to the element HTMLImport. Based on user feedback, we are changing this behavior.
274+
275+
Two new properties are being added to `Polymer.Element`: `importPath` and `rootPath`. The `importPath` property is a static getter on the element class that defaults to the element HTMLImport document URL and is overridable. It may be useful to override `importPath` when an element `template` is not retrieved from a `dom-module` or the element is not defined using an HTMLImport. The `rootPath` property is set to the value of `Polymer.rootPath` which is globally settable and defaults to the main document URL. It may be useful to set `Polymer.rootPath` to provide a stable application mount path when using client side routing. URL's in styles are re-written to be relative to the `importPath` property. Inside element templates, URLs in element attributes are *no longer* re-written. Instead, they should be bound using `importPath` and `rootPath` where appropriate. For example:
276+
277+
A Polymer 1.x template that included:
278+
279+
```html
280+
<img src="foo.jpg">
281+
```
282+
283+
in Polymer 2.x should be:
284+
285+
```html
286+
<img src$="[[importPath]]foo.jpg">
287+
```

lib/elements/dom-module.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@
8888
// element's location; accomodate polyfilled HTMLImports
8989
const owner = window.HTMLImports && HTMLImports.importForElement ?
9090
HTMLImports.importForElement(this) || document : this.ownerDocument;
91-
this.__assetpath = Polymer.ResolveUrl.resolveUrl(
91+
const url = Polymer.ResolveUrl.resolveUrl(
9292
this.getAttribute('assetpath') || '', owner.baseURI);
93+
this.__assetpath = Polymer.ResolveUrl.pathFromUrl(url);
9394
}
9495
return this.__assetpath;
9596
}

lib/mixins/element-mixin.html

+39-8
Original file line numberDiff line numberDiff line change
@@ -392,13 +392,16 @@
392392
* style scoping.
393393
* @param {HTMLElement} proto
394394
* @param {HTMLTemplateElement} template
395+
* @param {string} baseURI URL against which to resolve urls in
396+
* style element cssText.
395397
* @param {string} is
396398
* @param {string} ext
397399
* @private
398400
*/
399-
function finalizeTemplate(proto, template, is, ext) {
401+
function finalizeTemplate(proto, template, baseURI, is, ext) {
400402
// support `include="module-name"`
401-
let cssText = Polymer.StyleGather.cssFromTemplate(template) +
403+
let cssText =
404+
Polymer.StyleGather.cssFromTemplate(template, baseURI) +
402405
Polymer.StyleGather.cssFromModuleImports(is);
403406
if (cssText) {
404407
let style = document.createElement('style');
@@ -489,6 +492,8 @@
489492
* return memoizedTemplate;
490493
* }
491494
* }
495+
*
496+
* @returns {HTMLTemplateElement|string}
492497
*/
493498
static get template() {
494499
if (!this.hasOwnProperty(goog.reflect.objectProperty('_template', this))) {
@@ -501,6 +506,26 @@
501506
return this._template;
502507
}
503508

509+
/**
510+
* Path matching the url from which the element was imported.
511+
* This path is used to resolve url's in template style cssText.
512+
* The `importPath` property is also set on element instances and can be
513+
* used to create bindings relative to the import path.
514+
* Defaults to the path matching the url containing a `dom-module` element
515+
* matching this element's static `is` property.
516+
* Note, this path should contain a trailing `/`.
517+
*
518+
* @returns {string}
519+
*/
520+
static get importPath() {
521+
if (!this.hasOwnProperty(goog.reflect.objectProperty('_importPath', this))) {
522+
const module = Polymer.DomModule.import(this.is);
523+
this._importPath = module ? module.assetpath : '' ||
524+
Object.getPrototypeOf(this.prototype).constructor.importPath;
525+
}
526+
return this._importPath;
527+
}
528+
504529
constructor() {
505530
super();
506531
Polymer.telemetry.instanceCount++;
@@ -524,13 +549,20 @@
524549
*/
525550
_initializeProperties() {
526551
this.constructor.finalize();
552+
const importPath = this.constructor.importPath;
527553
// note: finalize template when we have access to `localName` to
528554
// avoid dependence on `is` for polyfilling styling.
529555
if (this._template && !this._template.__polymerFinalized) {
530556
this._template.__polymerFinalized = true;
531-
finalizeTemplate(this.__proto__, this._template, this.localName);
557+
const baseURI =
558+
importPath ? Polymer.ResolveUrl.resolveUrl(importPath) : '';
559+
finalizeTemplate(this.__proto__, this._template, baseURI,
560+
this.localName);
532561
}
533562
super._initializeProperties();
563+
// set path defaults
564+
this.rootPath = Polymer.rootPath;
565+
this.importPath = importPath;
534566
// apply property defaults...
535567
let p$ = propertyDefaultsForClass(this.constructor);
536568
if (!p$) {
@@ -673,13 +705,12 @@
673705
*
674706
* @param {string} url URL to resolve.
675707
* @param {string=} base Optional base URL to resolve against, defaults
676-
* to the element template's ownerDocument baseURI.
677-
* @return {string} Rewritten URL relative to the import
708+
* to the element's `importPath`
709+
* @return {string} Rewritten URL relative to base
678710
*/
679711
resolveUrl(url, base) {
680-
if (!base) {
681-
const module = Polymer.DomModule.import(this.constructor.is);
682-
base = module ? module.assetpath : document.baseURI;
712+
if (!base && this.importPath) {
713+
base = Polymer.ResolveUrl.resolveUrl(this.importPath);
683714
}
684715
return Polymer.ResolveUrl.resolveUrl(url, base);
685716
}

lib/mixins/property-effects.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,8 @@
850850
* @param {Function} effectFn Function to run when arguments change
851851
* @param {*=} methodInfo
852852
* @param {Object=} dynamicFns Map indicating whether method names should
853-
* be included as a dependency to the effect.
853+
* be included as a dependency to the effect. Note, defaults to true
854+
* if the signature is statci (sig.static is true).
854855
* @private
855856
*/
856857
function createMethodEffect(model, sig, type, effectFn, methodInfo, dynamicFns) {

lib/mixins/template-stamp.html

-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,6 @@
201201
parseChildNodesAnnotations(element, note, list, stripWhiteSpace, ownerDocument);
202202
if (element.attributes) {
203203
parseNodeAttributeAnnotations(element, note);
204-
Polymer.ResolveUrl.resolveAttrs(element, ownerDocument);
205204
}
206205
if (note.bindings.length || note.events.length || note.id) {
207206
list.push(note);

lib/utils/boot.html

+12-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
99
-->
1010
<script>
11+
(function() {
12+
13+
const userPolymer = window.Polymer;
1114

1215
/**
1316
* @namespace Polymer
@@ -18,6 +21,12 @@
1821
window.Polymer = function(info) {
1922
return window.Polymer._polymerFn(info);
2023
}
24+
25+
// support user settings on the Polymer object
26+
if (userPolymer) {
27+
Object.assign(Polymer, userPolymer);
28+
}
29+
2130
// To be plugged by legacy implementation if loaded
2231
window.Polymer._polymerFn = function() {
2332
throw new Error('Load polymer.html to use the Polymer() function.');
@@ -29,12 +38,14 @@
2938
When using Closure Compiler, goog.reflect.objectProperty(property, object) is replaced by the munged name for object[property]
3039
We cannot alias this function, so we have to use a small shim that has the same behavior when not compiling.
3140
*/
32-
var goog = {
41+
window.goog = {
3342
reflect: {
3443
objectProperty(s, o) {
3544
return s;
3645
}
3746
}
3847
}
3948
/* eslint-enable */
49+
50+
})();
4051
</script>

lib/utils/resolve-url.html

+61-76
Original file line numberDiff line numberDiff line change
@@ -13,99 +13,82 @@
1313

1414
(function() {
1515

16+
let CSS_URL_RX = /(url\()([^)]*)(\))/g;
17+
let ABS_URL = /(^\/)|(^#)|(^[\w-\d]*:)/;
18+
let workingURL;
19+
let resolveDoc;
20+
/**
21+
* Resolves the given URL against the provided `baseUri'.
22+
*
23+
* @memberof Polymer.ResolveUrl
24+
* @param {string} url Input URL to resolve
25+
* @param {string} baseURI Base URI to resolve the URL against
26+
* @return {string} resolved URL
27+
*/
28+
function resolveUrl(url, baseURI) {
29+
if (url && ABS_URL.test(url)) {
30+
return url;
31+
}
32+
// Lazy feature detection.
33+
if (workingURL === undefined) {
34+
workingURL = false;
35+
try {
36+
const u = new URL('b', 'http://a');
37+
u.pathname = 'c%20d';
38+
workingURL = (u.href === 'http://a/c%20d');
39+
} catch (e) {
40+
// silently fail
41+
}
42+
}
43+
if (!baseURI) {
44+
baseURI = document.baseURI || window.location.href;
45+
}
46+
if (workingURL) {
47+
return (new URL(url, baseURI)).href;
48+
}
49+
// Fallback to creating an anchor into a disconnected document.
50+
if (!resolveDoc) {
51+
resolveDoc = document.implementation.createHTMLDocument('temp');
52+
resolveDoc.base = resolveDoc.createElement('base');
53+
resolveDoc.head.appendChild(resolveDoc.base);
54+
resolveDoc.anchor = resolveDoc.createElement('a');
55+
resolveDoc.body.appendChild(resolveDoc.anchor);
56+
}
57+
resolveDoc.base.href = baseURI;
58+
resolveDoc.anchor.href = url;
59+
return resolveDoc.anchor.href || url;
60+
61+
}
62+
1663
/**
1764
* Resolves any relative URL's in the given CSS text against the provided
1865
* `ownerDocument`'s `baseURI`.
1966
*
2067
* @memberof Polymer.ResolveUrl
2168
* @param {string} cssText CSS text to process
22-
* @param {Document} ownerDocument Owner document to base URL's on
69+
* @param {string} baseURI Base URI to resolve the URL against
2370
* @return {string} Processed CSS text with resolved URL's
2471
*/
25-
function resolveCss(cssText, ownerDocument) {
72+
function resolveCss(cssText, baseURI) {
2673
return cssText.replace(CSS_URL_RX, function(m, pre, url, post) {
2774
return pre + '\'' +
28-
resolve(url.replace(/["']/g, ''), ownerDocument) +
75+
resolveUrl(url.replace(/["']/g, ''), baseURI) +
2976
'\'' + post;
3077
});
3178
}
3279

3380
/**
34-
* Resolves any relative URL's in `src` or `style` attributes of the given
35-
* `element` against the provided `ownerDocument`'s `baseURI`.
81+
* Returns a path from a given `url`. The path includes the trailing
82+
* `/` from the url.
3683
*
3784
* @memberof Polymer.ResolveUrl
38-
* @param {HTMLElement} element Element whose attributes will be processed
39-
* @param {Document} ownerDocument Owner document to base URL's on
85+
* @param {string} url Input URL to transform
86+
* @return {string} resolved path
4087
*/
41-
function resolveAttrs(element, ownerDocument) {
42-
for (var name in URL_ATTRS) {
43-
var a$ = URL_ATTRS[name];
44-
for (var i=0, l=a$.length, a, at, v; (i<l) && (a=a$[i]); i++) {
45-
if (name === '*' || element.localName === name) {
46-
at = element.attributes[a];
47-
v = at && at.value;
48-
if (v && (v.search(BINDING_RX) < 0)) {
49-
at.value = (a === 'style') ?
50-
resolveCss(v, ownerDocument) :
51-
resolve(v, ownerDocument);
52-
}
53-
}
54-
}
55-
}
88+
function pathFromUrl(url) {
89+
return url.substring(0, url.lastIndexOf('/') + 1);
5690
}
5791

58-
/**
59-
* Resolves the given URL against the provided `ownerDocument`'s `baseURI'.
60-
*
61-
* Does not modify `#` links or absolute URL's.
62-
*
63-
* @private
64-
*/
65-
function resolve(url, ownerDocument) {
66-
// do not resolve '#' links, they are used for routing
67-
if (url && ABS_URL.test(url)) {
68-
return url;
69-
}
70-
var resolver = getUrlResolver(ownerDocument);
71-
resolver.href = url;
72-
return resolver.href || url;
73-
}
74-
75-
var tempDoc;
76-
var tempDocBase;
77-
78-
/**
79-
* Resolves the given URL against the provided `baseUri'.
80-
*
81-
* @memberof Polymer.ResolveUrl
82-
* @param {string} url Input URL to resolve
83-
* @param {string} baseUri Base URI to resolve the URL against
84-
* @param {type} name Description
85-
*/
86-
function resolveUrl(url, baseUri) {
87-
if (!tempDoc) {
88-
tempDoc = document.implementation.createHTMLDocument('temp');
89-
tempDocBase = tempDoc.createElement('base');
90-
tempDoc.head.appendChild(tempDocBase);
91-
}
92-
tempDocBase.href = baseUri;
93-
return resolve(url, tempDoc);
94-
}
95-
96-
function getUrlResolver(ownerDocument) {
97-
return ownerDocument.__urlResolver ||
98-
(ownerDocument.__urlResolver = ownerDocument.createElement('a'));
99-
}
100-
101-
var CSS_URL_RX = /(url\()([^)]*)(\))/g;
102-
var URL_ATTRS = {
103-
'*': ['href', 'src', 'style', 'url'],
104-
form: ['action']
105-
};
106-
var ABS_URL = /(^\/)|(^#)|(^[\w-\d]*:)/;
107-
var BINDING_RX = /\{\{|\[\[/;
108-
10992
/**
11093
* Module with utilities for resolving relative URL's.
11194
*
@@ -114,12 +97,14 @@
11497
* @summary Module with utilities for resolving relative URL's.
11598
*/
11699
Polymer.ResolveUrl = {
117-
// exports
118100
resolveCss: resolveCss,
119-
resolveAttrs: resolveAttrs,
120-
resolveUrl: resolveUrl
101+
resolveUrl: resolveUrl,
102+
pathFromUrl: pathFromUrl
121103
};
122104

105+
// NOTE: baseURI is not supported on IE?
106+
Polymer.rootPath = pathFromUrl(document.baseURI || window.location.href);
107+
123108
})();
124109

125110
</script>

lib/utils/style-gather.html

+7-5
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
// include css from the first template in the module
6868
let t = m.querySelector('template');
6969
if (t) {
70-
cssText += this.cssFromTemplate(t);
70+
cssText += this.cssFromTemplate(t, m.assetpath);
7171
}
7272
// module imports: <link rel="import" type="css">
7373
cssText += this.cssFromModuleImports(moduleId);
@@ -86,9 +86,10 @@
8686
*
8787
* @memberof Polymer.StyleGather
8888
* @param {HTMLTemplateElement} template Template to gather styles from
89+
* @param {string} baseURI Base URI to resolve the URL against
8990
* @return {string} Concatenated CSS content from specified template
9091
*/
91-
cssFromTemplate(template) {
92+
cssFromTemplate(template, baseURI) {
9293
let cssText = '';
9394
// if element is a template, get content from its .content
9495
let e$ = template.content.querySelectorAll('style');
@@ -101,8 +102,8 @@
101102
cssText += this.cssFromModules(include);
102103
}
103104
e.parentNode.removeChild(e);
104-
cssText +=
105-
Polymer.ResolveUrl.resolveCss(e.textContent, template.ownerDocument);
105+
cssText += baseURI ?
106+
Polymer.ResolveUrl.resolveCss(e.textContent, baseURI) : e.textContent;
106107
}
107108
return cssText;
108109
},
@@ -131,7 +132,8 @@
131132
// but the import pseudo-doc can be used directly.
132133
let container = importDoc.body ? importDoc.body : importDoc;
133134
cssText +=
134-
Polymer.ResolveUrl.resolveCss(container.textContent, importDoc);
135+
Polymer.ResolveUrl.resolveCss(container.textContent,
136+
importDoc.baseURI);
135137
}
136138
}
137139
return cssText;

0 commit comments

Comments
 (0)