Rebase inclusion of URL polyfill

This commit is contained in:
Addy Osmani
2015-03-06 15:07:46 +00:00
30 changed files with 346 additions and 33 deletions

19
LICENSE.md Normal file
View File

@@ -0,0 +1,19 @@
# License
Everything in this repo is BSD style license unless otherwise specified.
Copyright (c) 2015 The Polymer Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,6 +1,8 @@
webcomponents.js
================
[![Join the chat at https://gitter.im/webcomponents/webcomponentsjs](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/webcomponents/webcomponentsjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
A suite of polyfills supporting the [Web Components](http://webcomponents.org) specs:
**Custom Elements**: allows authors to define their own custom tags ([spec](https://w3c.github.io/webcomponents/spec/custom/)).
@@ -21,6 +23,28 @@ Pre-built (concatenated & minified) versions of the polyfills are maintained in
`webcomponents-lite.js` includes all polyfills except for shadow DOM.
## Browser Support
Our polyfills are intended to work in the latest versions of evergreen browsers. See below
for our complete browser support matrix:
| Polyfill | IE10 | IE11+ | Chrome* | Firefox* | Safari 7+* | Chrome Android* | Mobile Safari* |
| ---------- |:----:|:-----:|:-------:|:--------:|:----------:|:---------------:|:--------------:|
| Custom Elements | ~ | ✓ | ✓ | ✓ | ✓ | ✓| ✓ |
| HTML Imports | ~ | ✓ | ✓ | ✓ | ✓| ✓| ✓ |
| Shadow DOM | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Templates | ✓ | ✓ | ✓ | ✓| ✓ | ✓ | ✓ |
*Indicates the current version of the browser
~Indicates support may be flaky. If using Custom Elements or HTML Imports with Shadow DOM,
you will get the non-flaky Mutation Observer polyfill that Shadow DOM includes.
The polyfills may work in older browsers, however require additional polyfills (such as classList)
to be used. We cannot guarantee support for browsers outside of our compatibility matrix.
### Manually Building
If you wish to build the polyfills yourself, you'll need `node` and `gulp` on your system:
@@ -41,4 +65,9 @@ The builds will be placed into the `dist/` directory.
See the [contributing guide](CONTRIBUTING.md)
## License
Everything in this repository is BSD style license unless otherwise specified.
Copyright (c) 2015 The Polymer Authors. All rights reserved.

View File

@@ -1,7 +1,7 @@
{
"name": "webcomponentsjs",
"main": "webcomponents.js",
"version": "0.5.4",
"version": "0.5.5",
"homepage": "http://webcomponents.org",
"authors": [
"The Polymer Authors"

View File

@@ -1,6 +1,6 @@
{
"name": "webcomponents.js",
"version": "0.5.4",
"version": "0.5.5",
"description": "webcomponents.js",
"main": "webcomponents.js",
"directories": {

View File

@@ -28,7 +28,6 @@ var file = 'CustomElements.js';
var modules = [
'../WeakMap/WeakMap.js',
'../MutationObserver/MutationObserver.js',
'base.js',
'traverse.js',
'observe.js',

View File

@@ -1,6 +1,5 @@
[
"../WeakMap/WeakMap.js",
"../MutationObserver/MutationObserver.js",
"base.js",
"traverse.js",
"observe.js",

View File

@@ -163,7 +163,7 @@ function inDocument(element) {
if (p == doc) {
return true;
}
p = p.parentNode || p.host;
p = p.parentNode || ((p.nodeType === Node.DOCUMENT_FRAGMENT_NODE) && p.host);
}
}
@@ -293,7 +293,6 @@ function upgradeDocumentTree(doc) {
// undefined to aid feature detection of Shadow DOM.
var originalCreateShadowRoot = Element.prototype.createShadowRoot;
if (originalCreateShadowRoot) {
// ensure that all ShadowRoots watch for CustomElements.
Element.prototype.createShadowRoot = function() {
var root = originalCreateShadowRoot.call(this);
CustomElements.watchShadow(this);

View File

@@ -27,7 +27,6 @@ var file = 'HTMLImports.js';
var modules = [
'../WeakMap/WeakMap.js',
'../MutationObserver/MutationObserver.js',
'base.js',
'module.js',
'path.js',

View File

@@ -115,24 +115,35 @@ function markTargetLoaded(event) {
// call <callback> when we ensure all imports have loaded
function watchImportsLoad(callback, doc) {
var imports = doc.querySelectorAll('link[rel=import]');
var loaded = 0, l = imports.length;
function checkDone(d) {
if ((loaded == l) && callback) {
callback();
var parsedCount = 0, importCount = imports.length, newImports = [], errorImports = [];
function checkDone() {
if (parsedCount == importCount && callback) {
callback({
allImports: imports,
loadedImports: newImports,
errorImports: errorImports
});
}
}
function loadedImport(e) {
markTargetLoaded(e);
loaded++;
newImports.push(this);
parsedCount++;
checkDone();
}
if (l) {
for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
function errorLoadingImport(e) {
errorImports.push(this);
parsedCount++;
checkDone();
}
if (importCount) {
for (var i=0, imp; i<importCount && (imp=imports[i]); i++) {
if (isImportLoaded(imp)) {
loadedImport.call(imp, {target: imp});
parsedCount++;
checkDone();
} else {
imp.addEventListener('load', loadedImport);
imp.addEventListener('error', loadedImport);
imp.addEventListener('error', errorLoadingImport);
}
}
} else {
@@ -211,11 +222,11 @@ if (useNative) {
// have loaded. This event is required to simulate the script blocking
// behavior of native imports. A main document script that needs to be sure
// imports have loaded should wait for this event.
whenReady(function() {
whenReady(function(detail) {
HTMLImports.ready = true;
HTMLImports.readyTime = new Date().getTime();
var evt = rootDocument.createEvent("CustomEvent");
evt.initCustomEvent("HTMLImportsLoaded", true, true, {});
evt.initCustomEvent("HTMLImportsLoaded", true, true, detail);
rootDocument.dispatchEvent(evt);
});

View File

@@ -1,6 +1,5 @@
[
"../WeakMap/WeakMap.js",
"../MutationObserver/MutationObserver.js",
"base.js",
"module.js",
"path.js",

View File

@@ -119,6 +119,10 @@ function isLinkRel(elt, rel) {
return elt.localName === 'link' && elt.getAttribute('rel') === rel;
}
function hasBaseURIAccessor(doc) {
return !! Object.getOwnPropertyDescriptor(doc, 'baseURI');
}
function makeDocument(resource, url) {
// create a new HTML document
var doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE);
@@ -128,7 +132,7 @@ function makeDocument(resource, url) {
var base = doc.createElement('base');
base.setAttribute('href', url);
// add baseURI support to browsers (IE) that lack it.
if (!doc.baseURI) {
if (!doc.baseURI && !hasBaseURIAccessor(doc)) {
// Use defineProperty since Safari throws an exception when using assignment.
Object.defineProperty(doc, 'baseURI', {value:url});
}

View File

@@ -150,6 +150,7 @@ var importParser = {
// TODO(sorvell): style element load event can just not fire so clone styles
var src = elt;
elt = cloneStyle(elt);
src.__appliedElement = elt;
elt.__importElement = src;
this.parseGeneric(elt);
},

View File

@@ -16,22 +16,25 @@ var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
// document. We fixup url's in url() and @import.
var path = {
resolveUrlsInStyle: function(style) {
resolveUrlsInStyle: function(style, linkUrl) {
var doc = style.ownerDocument;
var resolver = doc.createElement('a');
style.textContent = this.resolveUrlsInCssText(style.textContent, resolver);
style.textContent = this.resolveUrlsInCssText(style.textContent, linkUrl, resolver);
return style;
},
resolveUrlsInCssText: function(cssText, urlObj) {
var r = this.replaceUrls(cssText, urlObj, CSS_URL_REGEXP);
r = this.replaceUrls(r, urlObj, CSS_IMPORT_REGEXP);
resolveUrlsInCssText: function(cssText, linkUrl, urlObj) {
var r = this.replaceUrls(cssText, urlObj, linkUrl, CSS_URL_REGEXP);
r = this.replaceUrls(r, urlObj, linkUrl, CSS_IMPORT_REGEXP);
return r;
},
replaceUrls: function(text, urlObj, regexp) {
replaceUrls: function(text, urlObj, linkUrl, regexp) {
return text.replace(regexp, function(m, pre, url, post) {
var urlPath = url.replace(/["']/g, '');
if (linkUrl) {
urlPath = (new URL(urlPath, linkUrl)).href;
}
urlObj.href = urlPath;
urlPath = urlObj.href;
return pre + '\'' + urlPath + '\'' + post;

View File

@@ -524,7 +524,6 @@
// Fall through.
case 'DOMNodeInserted':
// http://dom.spec.whatwg.org/#concept-mo-queue-childlist
var target = e.relatedNode;
var changedNode = e.target;
var addedNodes, removedNodes;
if (e.type === 'DOMNodeInserted') {
@@ -539,13 +538,13 @@
var nextSibling = changedNode.nextSibling;
// 1.
var record = getRecord('childList', target);
var record = getRecord('childList', e.target.parentNode);
record.addedNodes = addedNodes;
record.removedNodes = removedNodes;
record.previousSibling = previousSibling;
record.nextSibling = nextSibling;
forEachAncestorAndObserverEnqueueRecord(target, function(options) {
forEachAncestorAndObserverEnqueueRecord(e.relatedNode, function(options) {
// 2.1, 3.2
if (!options.childList)
return;

View File

@@ -457,7 +457,7 @@ var ShadowCSS = {
return !selector.match(re);
},
makeScopeMatcher: function(scopeSelector) {
scopeSelector = scopeSelector.replace(/\[/g, '\\[').replace(/\[/g, '\\]');
scopeSelector = scopeSelector.replace(/\[/g, '\\[').replace(/\]/g, '\\]');
return new RegExp('^(' + scopeSelector + ')' + selectorReSuffix, 'm');
},
applySelectorScope: function(selector, selectorScope) {
@@ -495,7 +495,7 @@ var ShadowCSS = {
// remove :host since it should be unnecessary
var t = p.trim().replace(polyfillHostRe, '');
if (t && (splits.indexOf(t) < 0) && (t.indexOf(attrName) < 0)) {
p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3')
p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3');
}
return p;
}).join(sep);
@@ -742,7 +742,7 @@ if (window.ShadowDOMPolyfill) {
style.textContent = elt.__resource;
}
// relay on HTMLImports for path fixup
HTMLImports.path.resolveUrlsInStyle(style);
HTMLImports.path.resolveUrlsInStyle(style, elt.href);
style.textContent = ShadowCSS.shimStyle(style);
style.removeAttribute(SHIM_ATTRIBUTE, '');
style.setAttribute(SHIMMED_ATTRIBUTE, '');

View File

@@ -103,6 +103,17 @@ window.ShadowDOMPolyfill = {};
function getWrapperConstructor(node) {
var nativePrototype = node.__proto__ || Object.getPrototypeOf(node);
if (isFirefox) {
// HTMLEmbedElements will sometimes be [NS Object wrapper class]
// which throws an error when getOwnPropertyNames is called on it.
// Mozilla handily includes a second HTMLEmbedElementPrototype in
// the chain, so we use that one if available.
try {
getOwnPropertyNames(nativePrototype);
} catch (error) {
nativePrototype = nativePrototype.__proto__;
}
}
var wrapperConstructor = constructorTable.get(nativePrototype);
if (wrapperConstructor)
return wrapperConstructor;

View File

@@ -14,6 +14,12 @@
(function(scope) {
'use strict';
if (!window.DOMTokenList) {
console.warn('Missing DOMTokenList prototype, please include a ' +
'compatible classList polyfill such as http://goo.gl/uTcepH.');
return;
}
var unsafeUnwrap = scope.unsafeUnwrap;
var enqueueMutation = scope.enqueueMutation;

View File

@@ -103,6 +103,7 @@
var list = classListTable.get(this);
if (!list) {
list = unsafeUnwrap(this).classList;
if (!list) return;
list.ownerElement_ = this;
classListTable.set(this, list);
}

View File

@@ -1,6 +1,7 @@
[
"build/boot.js",
"../URL/URL.js",
"../MutationObserver/MutationObserver.js",
"../HTMLImports/build.json",
"../CustomElements/build.json",
"../Template/Template.js",

View File

@@ -0,0 +1,49 @@
<!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>
<title>HTML Imports Dynamic</title>
<script src="../../tools/htmltest.js"></script>
<script src="../../tools/chai/chai.js"></script>
<script src="../../../src/HTMLImports/HTMLImports.js"></script>
</head>
<body>
<script>
document.addEventListener('DOMContentLoaded', function() {
// some time later
setTimeout(function() {
var div = document.createElement('div');
div.innerHTML = '<link rel="import" href="imports/load-1.html">' +
'<link rel="import" href="imports/load-2.html">';
document.body.appendChild(div);
var ports = document.querySelectorAll('link[rel=import]');
var loads = 0;
for (var i=0, l=ports.length, n; (i<l) && (n=ports[i]); i++) {
n.addEventListener('load', function(e) {
loads++;
chai.assert.ok(e.target.import);
});
}
HTMLImports.whenReady(function(detail) {
chai.assert.equal(detail.allImports.length, 2);
chai.assert.equal(detail.loadedImports.length, 2);
chai.expect(detail.allImports[0].href).to.contain('imports/load-1.html');
chai.expect(detail.allImports[1].href).to.contain('imports/load-2.html');
done();
});
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,50 @@
<!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>
<title>HTML Imports Dynamic</title>
<script src="../../tools/htmltest.js"></script>
<script src="../../tools/chai/chai.js"></script>
<script src="../../../src/HTMLImports/HTMLImports.js"></script>
</head>
<body>
<script>
document.addEventListener('DOMContentLoaded', function() {
// some time later
setTimeout(function() {
var div = document.createElement('div');
div.innerHTML = '<link rel="import" href="imports/load-1.html">' +
'<link rel="import" href="imports/load-does-not-exist.html">';
document.body.appendChild(div);
var ports = document.querySelectorAll('link[rel=import]');
var loads = 0;
for (var i=0, l=ports.length, n; (i<l) && (n=ports[i]); i++) {
n.addEventListener('load', function(e) {
loads++;
chai.assert.ok(e.target.import);
});
}
HTMLImports.whenReady(function(detail) {
chai.assert.equal(detail.allImports.length, 2);
chai.assert.equal(detail.errorImports.length, 1);
chai.assert.equal(detail.loadedImports.length, 1);
var errorImport = detail.errorImports[0];
chai.expect(errorImport.href).to.contain('imports/load-does-not-exist.html');
done();
});
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,50 @@
<!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>
<title>HTML Imports Dynamic</title>
<script src="../../tools/htmltest.js"></script>
<script src="../../tools/chai/chai.js"></script>
<script src="../../../src/HTMLImports/HTMLImports.js"></script>
</head>
<body>
<script>
document.addEventListener('DOMContentLoaded', function() {
// some time later
setTimeout(function() {
var div = document.createElement('div');
div.innerHTML = '<link rel="import" href="imports/load-1.html">' +
'<link rel="import" href="imports/load-2.html">';
document.body.appendChild(div);
var ports = document.querySelectorAll('link[rel=import]');
var loads = 0;
for (var i=0, l=ports.length, n; (i<l) && (n=ports[i]); i++) {
n.addEventListener('load', function(e) {
loads++;
chai.assert.ok(e.target.import);
});
}
HTMLImports.whenReady(function(detail) {
chai.assert.equal(detail.allImports.length, 2);
chai.assert.equal(detail.errorImports.length, 0);
chai.assert.equal(detail.loadedImports.length, 2);
chai.expect(detail.loadedImports[0].href).to.contain('imports/load-1.html');
chai.expect(detail.loadedImports[1].href).to.contain('imports/load-2.html');
done();
});
});
});
</script>
</body>
</html>

View File

@@ -21,6 +21,9 @@ htmlSuite('HTMLImports', function() {
htmlTest('html/currentScript.html');
htmlTest('html/dedupe.html');
htmlTest('html/dynamic.html');
htmlTest('html/dynamic-all-imports-detail.html');
htmlTest('html/dynamic-errors-detail.html');
htmlTest('html/dynamic-loaded-detail.html');
htmlTest('html/csp.html');
htmlTest('html/customevent-detail.html');
htmlTest('html/encoding.html');

View File

@@ -378,4 +378,25 @@ suite('JsMutationObserver childList', function() {
});
});
test('Append child in child', function() {
var div = testDiv.appendChild(document.createElement('div'));
var observer = new JsMutationObserver(function() {});
observer.observe(div, {
childList: true,
subtree: true
});
var div2 = document.createElement('div')
var div3 = div2.appendChild(document.createElement('div'));
div.appendChild(div2);
var records = observer.takeRecords();
if(records.length == 1) {
assert.strictEqual(records[0].target, div);
assert.strictEqual(records[0].addedNodes[0].firstChild, div3);
} else {
assert.strictEqual(records[0].target, div);
assert.strictEqual(records[1].target, div2);
}
});
});

View File

@@ -17,6 +17,7 @@
<script src="../tools/mocha-htmltest.js"></script>
<!-- MutationObserver -->
<script src="../../src/WeakMap/WeakMap.js"></script>
<script src="../../src/MutationObserver/MutationObserver.js"></script>
<script>

View File

@@ -0,0 +1,11 @@
/**
* @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
*/
@import "../imports/sheet1.css";

View File

@@ -0,0 +1,40 @@
<!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>
<title>Imports style loading with base tag</title>
<base href="/">
<script src="tests/tools/chai/chai.js"></script>
<script src="tests/tools/htmltest.js"></script>
<script src="webcomponents.js" shadow></script>
<link rel="stylesheet" href="tests/ShadowCSS/html/imports/style-import-base-tag.css" shim-shadowdom>
</head>
<body>
<div id="test1" class="red">red</div>
<script>
document.addEventListener('WebComponentsReady', function() {
function getComputed(selector) {
return getComputedStyle(document.querySelector(selector));
}
var link = document.querySelector('link[rel=stylesheet]');
//Tricky interval, because WebComponentsReady is fired before all link[rel=stylesheet] are processed
var interval = setInterval(function () {
if (link.__importParsed) {
clearInterval(interval);
chai.assert.equal(getComputed('#test1').backgroundColor, 'rgb(255, 0, 0)', 'shimmed imported style is loaded');
done();
}
}, 50);
});
</script>
</body>
</html>

View File

@@ -25,5 +25,6 @@ htmlSuite('ShadowCss', function() {
htmlTest('html/before-content.html?shadow');
htmlTest('html/before-content.html');
htmlTest('html/style-import.html');
htmlTest('html/style-import-base-tag.html');
htmlTest('html/css-animation.html');
});

View File

@@ -9,6 +9,13 @@
*/
suite('DOMTokenList', function() {
if (!window.DOMTokenList) {
test('classList returns undefined if not supported', function() {
var div = document.createElement('div');
assert.isUndefined(div.classList);
});
return;
}
test('instanceof', function() {
var div = document.createElement('div');

View File

@@ -34,7 +34,7 @@
}
}
// log flags
if (flags.log) {
if (flags.log && flags.log.split) {
var parts = flags.log.split(',');
flags.log = {};
parts.forEach(function(f) {