Index: dart/pkg/shadow_dom/lib/shadow_dom.debug.js |
=================================================================== |
--- dart/pkg/shadow_dom/lib/shadow_dom.debug.js (revision 29808) |
+++ dart/pkg/shadow_dom/lib/shadow_dom.debug.js (working copy) |
@@ -1884,7 +1884,7 @@ |
// 1. |
if (isShadowRoot(node)) |
- return getInsertionParent(node) || scope.getHostForShadowRoot(node); |
+ return getInsertionParent(node) || node.host; |
// 2. |
var eventParents = scope.eventParentsTable.get(node); |
@@ -1980,7 +1980,7 @@ |
ancestor = calculateParents(ancestor, context, ancestors); // 3.4.7. |
} |
if (isShadowRoot(target)) // 3.5. |
- target = scope.getHostForShadowRoot(target); |
+ target = target.host; |
else |
target = target.parentNode; // 3.6. |
} |
@@ -2009,10 +2009,8 @@ |
function enclosedBy(a, b) { |
if (a === b) |
return true; |
- if (a instanceof wrappers.ShadowRoot) { |
- var host = scope.getHostForShadowRoot(a); |
- return enclosedBy(rootOfNode(host), b); |
- } |
+ if (a instanceof wrappers.ShadowRoot) |
+ return enclosedBy(rootOfNode(a.host), b); |
return false; |
} |
@@ -2434,7 +2432,7 @@ |
function getTargetToListenAt(wrapper) { |
if (wrapper instanceof wrappers.ShadowRoot) |
- wrapper = scope.getHostForShadowRoot(wrapper); |
+ wrapper = wrapper.host; |
return unwrap(wrapper); |
} |
@@ -2671,13 +2669,15 @@ |
} |
var nodes = []; |
- var firstChild; |
- while (firstChild = node.firstChild) { |
- node.removeChild(firstChild); |
- nodes.push(firstChild); |
- firstChild.parentNode_ = parentNode; |
+ for (var child = node.firstChild; child; child = child.nextSibling) { |
+ nodes.push(child); |
} |
+ for (var i = nodes.length - 1; i >= 0; i--) { |
+ node.removeChild(nodes[i]); |
+ nodes[i].parentNode_ = parentNode; |
+ } |
+ |
for (var i = 0; i < nodes.length; i++) { |
nodes[i].previousSibling_ = nodes[i - 1] || previousNode; |
nodes[i].nextSibling_ = nodes[i + 1] || nextNode; |
@@ -2825,13 +2825,29 @@ |
this.previousSibling_ = undefined; |
}; |
+ var OriginalDocumentFragment = window.DocumentFragment; |
var originalAppendChild = OriginalNode.prototype.appendChild; |
+ var originalCompareDocumentPosition = |
+ OriginalNode.prototype.compareDocumentPosition; |
var originalInsertBefore = OriginalNode.prototype.insertBefore; |
+ var originalRemoveChild = OriginalNode.prototype.removeChild; |
var originalReplaceChild = OriginalNode.prototype.replaceChild; |
- var originalRemoveChild = OriginalNode.prototype.removeChild; |
- var originalCompareDocumentPosition = |
- OriginalNode.prototype.compareDocumentPosition; |
+ var isIe = /Trident/.test(navigator.userAgent); |
+ |
+ var removeChildOriginalHelper = isIe ? |
+ function(parent, child) { |
+ try { |
+ originalRemoveChild.call(parent, child); |
+ } catch (ex) { |
+ if (!(parent instanceof OriginalDocumentFragment)) |
+ throw ex; |
+ } |
+ } : |
+ function(parent, child) { |
+ originalRemoveChild.call(parent, child); |
+ }; |
+ |
Node.prototype = Object.create(EventTarget.prototype); |
mixin(Node.prototype, { |
appendChild: function(childWrapper) { |
@@ -2906,8 +2922,20 @@ |
removeChild: function(childWrapper) { |
assertIsNodeWrapper(childWrapper); |
if (childWrapper.parentNode !== this) { |
- // TODO(arv): DOMException |
- throw new Error('NotFoundError'); |
+ // IE has invalid DOM trees at times. |
+ var found = false; |
+ var childNodes = this.childNodes; |
+ for (var ieChild = this.firstChild; ieChild; |
+ ieChild = ieChild.nextSibling) { |
+ if (ieChild === childWrapper) { |
+ found = true; |
+ break; |
+ } |
+ } |
+ if (!found) { |
+ // TODO(arv): DOMException |
+ throw new Error('NotFoundError'); |
+ } |
} |
var childNode = unwrap(childWrapper); |
@@ -2923,7 +2951,7 @@ |
var parentNode = childNode.parentNode; |
if (parentNode) |
- originalRemoveChild.call(parentNode, childNode); |
+ removeChildOriginalHelper(parentNode, childNode); |
if (thisFirstChild === childWrapper) |
this.firstChild_ = childWrapperNextSibling; |
@@ -2939,7 +2967,7 @@ |
childWrapper.previousSibling_ = childWrapper.nextSibling_ = |
childWrapper.parentNode_ = undefined; |
} else { |
- originalRemoveChild.call(this.impl, childNode); |
+ removeChildOriginalHelper(this.impl, childNode); |
} |
return childWrapper; |
@@ -3726,12 +3754,13 @@ |
} |
var node = unwrap(document.createElement('img')); |
+ HTMLElement.call(this, node); |
+ rewrap(node, this); |
+ |
if (width !== undefined) |
node.width = width; |
if (height !== undefined) |
node.height = height; |
- HTMLElement.call(this, node); |
- rewrap(node, this); |
} |
Image.prototype = HTMLImageElement.prototype; |
@@ -3858,6 +3887,136 @@ |
(function(scope) { |
'use strict'; |
+ var HTMLElement = scope.wrappers.HTMLElement; |
+ var registerWrapper = scope.registerWrapper; |
+ |
+ var OriginalHTMLMediaElement = window.HTMLMediaElement; |
+ |
+ function HTMLMediaElement(node) { |
+ HTMLElement.call(this, node); |
+ } |
+ HTMLMediaElement.prototype = Object.create(HTMLElement.prototype); |
+ |
+ registerWrapper(OriginalHTMLMediaElement, HTMLMediaElement, |
+ document.createElement('audio')); |
+ |
+ scope.wrappers.HTMLMediaElement = HTMLMediaElement; |
+})(this.ShadowDOMPolyfill); |
+ |
+// Copyright 2013 The Polymer Authors. All rights reserved. |
+// Use of this source code is goverened by a BSD-style |
+// license that can be found in the LICENSE file. |
+ |
+(function(scope) { |
+ 'use strict'; |
+ |
+ var HTMLMediaElement = scope.wrappers.HTMLMediaElement; |
+ var registerWrapper = scope.registerWrapper; |
+ var unwrap = scope.unwrap; |
+ var rewrap = scope.rewrap; |
+ |
+ var OriginalHTMLAudioElement = window.HTMLAudioElement; |
+ |
+ function HTMLAudioElement(node) { |
+ HTMLMediaElement.call(this, node); |
+ } |
+ HTMLAudioElement.prototype = Object.create(HTMLMediaElement.prototype); |
+ |
+ registerWrapper(OriginalHTMLAudioElement, HTMLAudioElement, |
+ document.createElement('audio')); |
+ |
+ function Audio(src) { |
+ if (!(this instanceof Audio)) { |
+ throw new TypeError( |
+ 'DOM object constructor cannot be called as a function.'); |
+ } |
+ |
+ var node = unwrap(document.createElement('audio')); |
+ HTMLMediaElement.call(this, node); |
+ rewrap(node, this); |
+ |
+ node.setAttribute('preload', 'auto'); |
+ if (src !== undefined) |
+ node.setAttribute('src', src); |
+ } |
+ |
+ Audio.prototype = HTMLAudioElement.prototype; |
+ |
+ scope.wrappers.HTMLAudioElement = HTMLAudioElement; |
+ scope.wrappers.Audio = Audio; |
+})(this.ShadowDOMPolyfill); |
+ |
+// Copyright 2013 The Polymer Authors. All rights reserved. |
+// Use of this source code is goverened by a BSD-style |
+// license that can be found in the LICENSE file. |
+ |
+(function(scope) { |
+ 'use strict'; |
+ |
+ var HTMLElement = scope.wrappers.HTMLElement; |
+ var mixin = scope.mixin; |
+ var registerWrapper = scope.registerWrapper; |
+ var rewrap = scope.rewrap; |
+ var unwrap = scope.unwrap; |
+ var wrap = scope.wrap; |
+ |
+ var OriginalHTMLOptionElement = window.HTMLOptionElement; |
+ |
+ function trimText(s) { |
+ return s.replace(/\s+/g, ' ').trim(); |
+ } |
+ |
+ function HTMLOptionElement(node) { |
+ HTMLElement.call(this, node); |
+ } |
+ HTMLOptionElement.prototype = Object.create(HTMLElement.prototype); |
+ mixin(HTMLOptionElement.prototype, { |
+ get text() { |
+ return trimText(this.textContent); |
+ }, |
+ set text(value) { |
+ this.textContent = trimText(String(value)); |
+ }, |
+ get form() { |
+ return wrap(unwrap(this).form); |
+ } |
+ }); |
+ |
+ registerWrapper(OriginalHTMLOptionElement, HTMLOptionElement, |
+ document.createElement('option')); |
+ |
+ function Option(text, value, defaultSelected, selected) { |
+ if (!(this instanceof Option)) { |
+ throw new TypeError( |
+ 'DOM object constructor cannot be called as a function.'); |
+ } |
+ |
+ var node = unwrap(document.createElement('option')); |
+ HTMLElement.call(this, node); |
+ rewrap(node, this); |
+ |
+ if (text !== undefined) |
+ node.text = text; |
+ if (value !== undefined) |
+ node.setAttribute('value', value); |
+ if (defaultSelected === true) |
+ node.setAttribute('selected', ''); |
+ node.selected = selected === true; |
+ } |
+ |
+ Option.prototype = HTMLOptionElement.prototype; |
+ |
+ scope.wrappers.HTMLOptionElement = HTMLOptionElement; |
+ scope.wrappers.Option = Option; |
+})(this.ShadowDOMPolyfill); |
+ |
+// Copyright 2013 The Polymer Authors. All rights reserved. |
+// Use of this source code is goverened by a BSD-style |
+// license that can be found in the LICENSE file. |
+ |
+(function(scope) { |
+ 'use strict'; |
+ |
var HTMLContentElement = scope.wrappers.HTMLContentElement; |
var HTMLElement = scope.wrappers.HTMLElement; |
var HTMLShadowElement = scope.wrappers.HTMLShadowElement; |
@@ -4037,6 +4196,10 @@ |
return nextOlderShadowTreeTable.get(this) || null; |
}, |
+ get host() { |
+ return shadowHostTable.get(this) || null; |
+ }, |
+ |
invalidateShadowRenderer: function() { |
return shadowHostTable.get(this).invalidateShadowRenderer(); |
}, |
@@ -4051,9 +4214,6 @@ |
}); |
scope.wrappers.ShadowRoot = ShadowRoot; |
- scope.getHostForShadowRoot = function(node) { |
- return shadowHostTable.get(node); |
- }; |
})(this.ShadowDOMPolyfill); |
// Copyright 2013 The Polymer Authors. All rights reserved. |
// Use of this source code is governed by a BSD-style |
@@ -4068,7 +4228,6 @@ |
var Node = scope.wrappers.Node; |
var ShadowRoot = scope.wrappers.ShadowRoot; |
var assert = scope.assert; |
- var getHostForShadowRoot = scope.getHostForShadowRoot; |
var mixin = scope.mixin; |
var muteMutationEvents = scope.muteMutationEvents; |
var oneOf = scope.oneOf; |
@@ -4310,7 +4469,7 @@ |
} |
function getRendererForShadowRoot(shadowRoot) { |
- return getRendererForHost(getHostForShadowRoot(shadowRoot)); |
+ return getRendererForHost(shadowRoot.host); |
} |
var spliceDiff = new ArraySplice(); |
@@ -4739,7 +4898,7 @@ |
'HTMLLabelElement', |
'HTMLLegendElement', |
'HTMLObjectElement', |
- 'HTMLOptionElement', |
+ // HTMLOptionElement is handled in HTMLOptionElement.js |
'HTMLOutputElement', |
'HTMLSelectElement', |
'HTMLTextAreaElement', |
@@ -4924,13 +5083,19 @@ |
}; |
}); |
- var nativeConstructor = originalRegister.call(unwrap(this), tagName, |
- object.extends ? {prototype: newPrototype, extends: object.extends} : |
- {prototype: newPrototype}); |
+ var p = {prototype: newPrototype}; |
+ if (object.extends) |
+ p.extends = object.extends; |
+ var nativeConstructor = originalRegister.call(unwrap(this), tagName, p); |
function GeneratedWrapper(node) { |
- if (!node) |
- return document.createElement(tagName); |
+ if (!node) { |
+ if (object.extends) { |
+ return document.createElement(object.extends, tagName); |
+ } else { |
+ return document.createElement(tagName); |
+ } |
+ } |
this.impl = node; |
} |
GeneratedWrapper.prototype = prototype; |
@@ -5304,7 +5469,6 @@ |
'a': 'HTMLAnchorElement', |
'applet': 'HTMLAppletElement', |
'area': 'HTMLAreaElement', |
- 'audio': 'HTMLAudioElement', |
'br': 'HTMLBRElement', |
'base': 'HTMLBaseElement', |
'body': 'HTMLBodyElement', |
@@ -5333,7 +5497,6 @@ |
'link': 'HTMLLinkElement', |
'map': 'HTMLMapElement', |
'marquee': 'HTMLMarqueeElement', |
- // 'media', Covered by audio and video |
'menu': 'HTMLMenuElement', |
'menuitem': 'HTMLMenuItemElement', |
'meta': 'HTMLMetaElement', |
@@ -5477,10 +5640,10 @@ |
/* |
This is a limited shim for ShadowDOM css styling. |
https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#styles |
- |
- The intention here is to support only the styling features which can be |
- relatively simply implemented. The goal is to allow users to avoid the |
- most obvious pitfalls and do so without compromising performance significantly. |
+ |
+ The intention here is to support only the styling features which can be |
+ relatively simply implemented. The goal is to allow users to avoid the |
+ most obvious pitfalls and do so without compromising performance significantly. |
For ShadowDOM styling that's not covered here, a set of best practices |
can be provided that should allow users to accomplish more complex styling. |
@@ -5489,57 +5652,57 @@ |
Shimmed features: |
- * @host: ShadowDOM allows styling of the shadowRoot's host element using the |
- @host rule. To shim this feature, the @host styles are reformatted and |
+ * @host: ShadowDOM allows styling of the shadowRoot's host element using the |
+ @host rule. To shim this feature, the @host styles are reformatted and |
prefixed with a given scope name and promoted to a document level stylesheet. |
For example, given a scope name of .foo, a rule like this: |
- |
+ |
@host { |
* { |
background: red; |
} |
} |
- |
+ |
becomes: |
- |
+ |
.foo { |
background: red; |
} |
- |
- * encapsultion: Styles defined within ShadowDOM, apply only to |
+ |
+ * encapsultion: Styles defined within ShadowDOM, apply only to |
dom inside the ShadowDOM. Polymer uses one of two techniques to imlement |
this feature. |
- |
- By default, rules are prefixed with the host element tag name |
+ |
+ By default, rules are prefixed with the host element tag name |
as a descendant selector. This ensures styling does not leak out of the 'top' |
of the element's ShadowDOM. For example, |
div { |
font-weight: bold; |
} |
- |
+ |
becomes: |
x-foo div { |
font-weight: bold; |
} |
- |
+ |
becomes: |
- Alternatively, if Platform.ShadowCSS.strictStyling is set to true then |
+ Alternatively, if Platform.ShadowCSS.strictStyling is set to true then |
selectors are scoped by adding an attribute selector suffix to each |
- simple selector that contains the host element tag name. Each element |
- in the element's ShadowDOM template is also given the scope attribute. |
+ simple selector that contains the host element tag name. Each element |
+ in the element's ShadowDOM template is also given the scope attribute. |
Thus, these rules match only elements that have the scope attribute. |
For example, given a scope name of x-foo, a rule like this: |
- |
+ |
div { |
font-weight: bold; |
} |
- |
+ |
becomes: |
- |
+ |
div[x-foo] { |
font-weight: bold; |
} |
@@ -5571,39 +5734,39 @@ |
becomes: |
- x-foo [part=special] { ... } |
- |
+ x-foo [part=special] { ... } |
+ |
Unaddressed ShadowDOM styling features: |
- |
+ |
* upper/lower bound encapsulation: Styles which are defined outside a |
shadowRoot should not cross the ShadowDOM boundary and should not apply |
inside a shadowRoot. |
- This styling behavior is not emulated. Some possible ways to do this that |
+ This styling behavior is not emulated. Some possible ways to do this that |
were rejected due to complexity and/or performance concerns include: (1) reset |
every possible property for every possible selector for a given scope name; |
(2) re-implement css in javascript. |
- |
+ |
As an alternative, users should make sure to use selectors |
specific to the scope in which they are working. |
- |
+ |
* ::distributed: This behavior is not emulated. It's often not necessary |
to style the contents of a specific insertion point and instead, descendants |
- of the host element can be styled selectively. Users can also create an |
+ of the host element can be styled selectively. Users can also create an |
extra node around an insertion point and style that node's contents |
via descendent selectors. For example, with a shadowRoot like this: |
- |
+ |
<style> |
content::-webkit-distributed(div) { |
background: red; |
} |
</style> |
<content></content> |
- |
+ |
could become: |
- |
+ |
<style> |
- / *@polyfill .content-container div * / |
+ / *@polyfill .content-container div * / |
content::-webkit-distributed(div) { |
background: red; |
} |
@@ -5611,9 +5774,9 @@ |
<div class="content-container"> |
<content></content> |
</div> |
- |
+ |
Note the use of @polyfill in the comment above a ShadowDOM specific style |
- declaration. This is a directive to the styling shim to use the selector |
+ declaration. This is a directive to the styling shim to use the selector |
in comments in lieu of the next selector when running under polyfill. |
*/ |
(function(scope) { |
@@ -5649,7 +5812,7 @@ |
root.shimmedStyle = def.shimmedStyle; |
} |
// remove existing style elements |
- for (var i=0, l=def.rootStyles.length, s; (i<l) && (s=def.rootStyles[i]); |
+ for (var i=0, l=def.rootStyles.length, s; (i<l) && (s=def.rootStyles[i]); |
i++) { |
s.parentNode.removeChild(s); |
} |
@@ -5693,14 +5856,14 @@ |
/* |
* Process styles to convert native ShadowDOM rules that will trip |
* up the css parser; we rely on decorating the stylesheet with comments. |
- * |
+ * |
* For example, we convert this rule: |
- * |
+ * |
* (comment start) @polyfill :host menu-item (comment end) |
* shadow::-webkit-distributed(menu-item) { |
- * |
+ * |
* to this: |
- * |
+ * |
* scopeName menu-item { |
* |
**/ |
@@ -5719,14 +5882,14 @@ |
}, |
/* |
* Process styles to add rules which will only apply under the polyfill |
- * |
+ * |
* For example, we convert this rule: |
- * |
- * (comment start) @polyfill-rule :host menu-item { |
+ * |
+ * (comment start) @polyfill-rule :host menu-item { |
* ... } (comment end) |
- * |
+ * |
* to this: |
- * |
+ * |
* scopeName menu-item {...} |
* |
**/ |
@@ -5745,15 +5908,15 @@ |
}, |
/* |
* Process styles to add rules which will only apply under the polyfill |
- * and do not process via CSSOM. (CSSOM is destructive to rules on rare |
+ * and do not process via CSSOM. (CSSOM is destructive to rules on rare |
* occasions, e.g. -webkit-calc on Safari.) |
* For example, we convert this rule: |
- * |
- * (comment start) @polyfill-unscoped-rule menu-item { |
+ * |
+ * (comment start) @polyfill-unscoped-rule menu-item { |
* ... } (comment end) |
- * |
+ * |
* to this: |
- * |
+ * |
* menu-item {...} |
* |
**/ |
@@ -5792,7 +5955,7 @@ |
return self.scopeHostCss(p1, name, typeExtension); |
}); |
cssText = rulesToCss(this.findAtHostRules(cssToRules(cssText), |
- new RegExp('^' + name + selectorReSuffix, 'm'))); |
+ this.makeScopeMatcher(name, typeExtension))); |
return cssText; |
}, |
scopeHostCss: function(cssText, name, typeExtension) { |
@@ -5819,10 +5982,10 @@ |
return r.join(', '); |
}, |
// consider styles that do not include component name in the selector to be |
- // unscoped and in need of promotion; |
+ // unscoped and in need of promotion; |
// for convenience, also consider keyframe rules this way. |
findAtHostRules: function(cssRules, matcher) { |
- return Array.prototype.filter.call(cssRules, |
+ return Array.prototype.filter.call(cssRules, |
this.isHostRule.bind(this, matcher)); |
}, |
isHostRule: function(matcher, cssRule) { |
@@ -5831,11 +5994,11 @@ |
(cssRule.type == CSSRule.WEBKIT_KEYFRAMES_RULE); |
}, |
/* Ensure styles are scoped. Pseudo-scoping takes a rule like: |
- * |
- * .foo {... } |
- * |
+ * |
+ * .foo {... } |
+ * |
* and converts this to |
- * |
+ * |
* scopeName .foo { ... } |
*/ |
shimScoping: function(styles, name, typeExtension) { |
@@ -5866,15 +6029,28 @@ |
* to |
* |
* scopeName.foo > .bar, .foo scopeName > .bar { } |
- * TODO(sorvell): file bug since native impl does not do the former yet. |
- * http://jsbin.com/OganOCI/2/edit |
+ * |
+ * and |
+ * |
+ * :host(.foo:host) .bar { ... } |
+ * |
+ * to |
+ * |
+ * scopeName.foo .bar { ... } |
*/ |
convertColonHost: function(cssText) { |
// p1 = :host, p2 = contents of (), p3 rest of rule |
return cssText.replace(cssColonHostRe, function(m, p1, p2, p3) { |
- return p2 ? polyfillHostNoCombinator + p2 + p3 + ', ' |
- + p2 + ' ' + p1 + p3 : |
- p1 + p3; |
+ p1 = polyfillHostNoCombinator; |
+ if (p2) { |
+ if (p2.match(polyfillHost)) { |
+ return p1 + p2.replace(polyfillHost, '') + p3; |
+ } else { |
+ return p1 + p2 + p3 + ', ' + p2 + ' ' + p1 + p3; |
+ } |
+ } else { |
+ return p1 + p3; |
+ } |
}); |
}, |
/* |
@@ -5888,7 +6064,7 @@ |
var cssText = ''; |
Array.prototype.forEach.call(cssRules, function(rule) { |
if (rule.selectorText && (rule.style && rule.style.cssText)) { |
- cssText += this.scopeSelector(rule.selectorText, name, typeExtension, |
+ cssText += this.scopeSelector(rule.selectorText, name, typeExtension, |
this.strictStyling) + ' {\n\t'; |
cssText += this.propertiesFromRule(rule) + '\n}\n\n'; |
} else if (rule.media) { |
@@ -5914,10 +6090,13 @@ |
return r.join(', '); |
}, |
selectorNeedsScoping: function(selector, name, typeExtension) { |
- var matchScope = typeExtension ? name : '\\[is=' + name + '\\]'; |
- var re = new RegExp('^(' + matchScope + ')' + selectorReSuffix, 'm'); |
+ var re = this.makeScopeMatcher(name, typeExtension); |
return !selector.match(re); |
}, |
+ makeScopeMatcher: function(name, typeExtension) { |
+ var matchScope = typeExtension ? '\\[is=[\'"]?' + name + '[\'"]?\\]' : name; |
+ return new RegExp('^(' + matchScope + ')' + selectorReSuffix, 'm'); |
+ }, |
// scope via name and [is=name] |
applySimpleSelectorScope: function(selector, name, typeExtension) { |
var scoper = typeExtension ? '[is=' + name + ']' : name; |
@@ -5956,7 +6135,7 @@ |
// TODO(sorvell): Chrome cssom incorrectly removes quotes from the content |
// property. (https://code.google.com/p/chromium/issues/detail?id=247231) |
if (rule.style.content && !rule.style.content.match(/['"]+/)) { |
- properties = 'content: \'' + rule.style.content + '\';\n' + |
+ properties = 'content: \'' + rule.style.content + '\';\n' + |
rule.style.cssText.replace(/content:[^;]*;/g, ''); |
} |
return properties; |
@@ -6042,5 +6221,4 @@ |
scope.ShadowCSS = ShadowCSS; |
})(window.Platform); |
- |
} |