Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Side by Side Diff: pkg/shadow_dom/lib/shadow_dom.debug.js

Issue 22951003: Build shadow_dom package in dart/pkg (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Use ShadowDOM test file Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 if ((!HTMLElement.prototype.createShadowRoot && 1 if ((!HTMLElement.prototype.createShadowRoot &&
2 !HTMLElement.prototype.webkitCreateShadowRoot) || 2 !HTMLElement.prototype.webkitCreateShadowRoot) ||
3 window.__forceShadowDomPolyfill) { 3 window.__forceShadowDomPolyfill) {
4 4
5 /* 5 /*
6 * Copyright 2013 The Polymer Authors. All rights reserved. 6 * Copyright 2013 The Polymer Authors. All rights reserved.
7 * Use of this source code is governed by a BSD-style 7 * Use of this source code is governed by a BSD-style
8 * license that can be found in the LICENSE file. 8 * license that can be found in the LICENSE file.
9 */ 9 */
10 (function() { 10 (function() {
(...skipping 3186 matching lines...) Expand 10 before | Expand all | Expand 10 after
3197 return name; 3197 return name;
3198 } 3198 }
3199 } 3199 }
3200 return originalGetTag(obj); 3200 return originalGetTag(obj);
3201 } 3201 }
3202 3202
3203 return getTag; 3203 return getTag;
3204 }; 3204 };
3205 })(); 3205 })();
3206 3206
3207 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
3208 // for details. All rights reserved. Use of this source code is governed by a
3209 // BSD-style license that can be found in the LICENSE file.
3210
3211 var Platform = {};
3212
3213 /*
3214 * Copyright 2012 The Polymer Authors. All rights reserved.
3215 * Use of this source code is governed by a BSD-style
3216 * license that can be found in the LICENSE file.
3217 */
3218
3219 /*
3220 This is a limited shim for ShadowDOM css styling.
3221 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#style s
3222
3223 The intention here is to support only the styling features which can be
3224 relatively simply implemented. The goal is to allow users to avoid the
3225 most obvious pitfalls and do so without compromising performance significantly .
3226 For ShadowDOM styling that's not covered here, a set of best practices
3227 can be provided that should allow users to accomplish more complex styling.
3228
3229 The following is a list of specific ShadowDOM styling features and a brief
3230 discussion of the approach used to shim.
3231
3232 Shimmed features:
3233
3234 * @host: ShadowDOM allows styling of the shadowRoot's host element using the
3235 @host rule. To shim this feature, the @host styles are reformatted and
3236 prefixed with a given scope name and promoted to a document level stylesheet.
3237 For example, given a scope name of .foo, a rule like this:
3238
3239 @host {
3240 * {
3241 background: red;
3242 }
3243 }
3244
3245 becomes:
3246
3247 .foo {
3248 background: red;
3249 }
3250
3251 * encapsultion: Styles defined within ShadowDOM, apply only to
3252 dom inside the ShadowDOM. Polymer uses one of two techniques to imlement
3253 this feature.
3254
3255 By default, rules are prefixed with the host element tag name
3256 as a descendant selector. This ensures styling does not leak out of the 'top'
3257 of the element's ShadowDOM. For example,
3258
3259 div {
3260 font-weight: bold;
3261 }
3262
3263 becomes:
3264
3265 x-foo div {
3266 font-weight: bold;
3267 }
3268
3269 becomes:
3270
3271
3272 Alternatively, if Platform.ShadowCSS.strictStyling is set to true then
3273 selectors are scoped by adding an attribute selector suffix to each
3274 simple selector that contains the host element tag name. Each element
3275 in the element's ShadowDOM template is also given the scope attribute.
3276 Thus, these rules match only elements that have the scope attribute.
3277 For example, given a scope name of x-foo, a rule like this:
3278
3279 div {
3280 font-weight: bold;
3281 }
3282
3283 becomes:
3284
3285 div[x-foo] {
3286 font-weight: bold;
3287 }
3288
3289 Note that elements that are dynamically added to a scope must have the scope
3290 selector added to them manually.
3291
3292 * ::pseudo: These rules are converted to rules that take advantage of the
3293 pseudo attribute. For example, a shadowRoot like this inside an x-foo
3294
3295 <div pseudo="x-special">Special</div>
3296
3297 with a rule like this:
3298
3299 x-foo::x-special { ... }
3300
3301 becomes:
3302
3303 x-foo [pseudo=x-special] { ... }
3304
3305 Unaddressed ShadowDOM styling features:
3306
3307 * upper/lower bound encapsulation: Styles which are defined outside a
3308 shadowRoot should not cross the ShadowDOM boundary and should not apply
3309 inside a shadowRoot.
3310
3311 This styling behavior is not emulated. Some possible ways to do this that
3312 were rejected due to complexity and/or performance concerns include: (1) reset
3313 every possible property for every possible selector for a given scope name;
3314 (2) re-implement css in javascript.
3315
3316 As an alternative, users should make sure to use selectors
3317 specific to the scope in which they are working.
3318
3319 * ::distributed: This behavior is not emulated. It's often not necessary
3320 to style the contents of a specific insertion point and instead, descendants
3321 of the host element can be styled selectively. Users can also create an
3322 extra node around an insertion point and style that node's contents
3323 via descendent selectors. For example, with a shadowRoot like this:
3324
3325 <style>
3326 content::-webkit-distributed(div) {
3327 background: red;
3328 }
3329 </style>
3330 <content></content>
3331
3332 could become:
3333
3334 <style>
3335 / *@polyfill .content-container div * /
3336 content::-webkit-distributed(div) {
3337 background: red;
3338 }
3339 </style>
3340 <div class="content-container">
3341 <content></content>
3342 </div>
3343
3344 Note the use of @polyfill in the comment above a ShadowDOM specific style
3345 declaration. This is a directive to the styling shim to use the selector
3346 in comments in lieu of the next selector when running under polyfill.
3347 */
3348 (function(scope) {
3349
3350 var ShadowCSS = {
3351 strictStyling: false,
3352 registry: {},
3353 // Shim styles for a given root associated with a name and extendsName
3354 // 1. cache root styles by name
3355 // 2. optionally tag root nodes with scope name
3356 // 3. shim polyfill directives /* @polyfill */
3357 // 4. shim @host and scoping
3358 shimStyling: function(root, name, extendsName) {
3359 if (root) {
3360 // use caching to make working with styles nodes easier and to facilitate
3361 // lookup of extendee
3362 var def = this.registerDefinition(root, name, extendsName);
3363 // find styles and apply shimming...
3364 if (this.strictStyling) {
3365 this.applyScopeToContent(root, name);
3366 }
3367 this.shimPolyfillDirectives(def.rootStyles, name);
3368 this.applyShimming(def.scopeStyles, name);
3369 }
3370 },
3371 // Shim styles to be placed inside a shadowRoot.
3372 // 1. shim polyfill directives /* @polyfill */
3373 // 2. shim @host and scoping
3374 shimShadowDOMStyling: function(styles, name) {
3375 this.shimPolyfillDirectives(styles, name);
3376 this.applyShimming(styles, name);
3377 },
3378 registerDefinition: function(root, name, extendsName) {
3379 var def = this.registry[name] = {
3380 root: root,
3381 name: name,
3382 extendsName: extendsName
3383 }
3384 var styles = root.querySelectorAll('style');
3385 styles = styles ? Array.prototype.slice.call(styles, 0) : [];
3386 def.rootStyles = styles;
3387 def.scopeStyles = def.rootStyles;
3388 var extendee = this.registry[def.extendsName];
3389 if (extendee) {
3390 def.scopeStyles = def.scopeStyles.concat(extendee.scopeStyles);
3391 }
3392 return def;
3393 },
3394 applyScopeToContent: function(root, name) {
3395 if (root) {
3396 // add the name attribute to each node in root.
3397 Array.prototype.forEach.call(root.querySelectorAll('*'),
3398 function(node) {
3399 node.setAttribute(name, '');
3400 });
3401 // and template contents too
3402 Array.prototype.forEach.call(root.querySelectorAll('template'),
3403 function(template) {
3404 this.applyScopeToContent(template.content, name);
3405 },
3406 this);
3407 }
3408 },
3409 /*
3410 * Process styles to convert native ShadowDOM rules that will trip
3411 * up the css parser; we rely on decorating the stylesheet with comments.
3412 *
3413 * For example, we convert this rule:
3414 *
3415 * (comment start) @polyfill @host g-menu-item (comment end)
3416 * shadow::-webkit-distributed(g-menu-item) {
3417 *
3418 * to this:
3419 *
3420 * scopeName g-menu-item {
3421 *
3422 **/
3423 shimPolyfillDirectives: function(styles, name) {
3424 if (styles) {
3425 Array.prototype.forEach.call(styles, function(s) {
3426 s.textContent = this.convertPolyfillDirectives(s.textContent, name);
3427 }, this);
3428 }
3429 },
3430 convertPolyfillDirectives: function(cssText, name) {
3431 var r = '', l = 0, matches, selector;
3432 while (matches = cssPolyfillCommentRe.exec(cssText)) {
3433 r += cssText.substring(l, matches.index);
3434 // remove end comment delimiter (*/)
3435 selector = matches[1].slice(0, -2).replace(hostRe, name);
3436 r += this.scopeSelector(selector, name) + '{';
3437 l = cssPolyfillCommentRe.lastIndex;
3438 }
3439 r += cssText.substring(l, cssText.length);
3440 return r;
3441 },
3442 // apply @host and scope shimming
3443 applyShimming: function(styles, name) {
3444 var cssText = this.shimAtHost(styles, name);
3445 cssText += this.shimScoping(styles, name);
3446 addCssToDocument(cssText);
3447 },
3448 // form: @host { .foo { declarations } }
3449 // becomes: scopeName.foo { declarations }
3450 shimAtHost: function(styles, name) {
3451 if (styles) {
3452 return this.convertAtHostStyles(styles, name);
3453 }
3454 },
3455 convertAtHostStyles: function(styles, name) {
3456 var cssText = stylesToCssText(styles);
3457 var r = '', l=0, matches;
3458 while (matches = hostRuleRe.exec(cssText)) {
3459 r += cssText.substring(l, matches.index);
3460 r += this.scopeHostCss(matches[1], name);
3461 l = hostRuleRe.lastIndex;
3462 }
3463 r += cssText.substring(l, cssText.length);
3464 var re = new RegExp('^' + name + selectorReSuffix, 'm');
3465 var cssText = rulesToCss(this.findAtHostRules(cssToRules(r),
3466 re));
3467 return cssText;
3468 },
3469 scopeHostCss: function(cssText, name) {
3470 var r = '', matches;
3471 while (matches = selectorRe.exec(cssText)) {
3472 r += this.scopeHostSelector(matches[1], name) +' ' + matches[2] + '\n\t';
3473 }
3474 return r;
3475 },
3476 // supports scopig by name and [is=name] syntax
3477 scopeHostSelector: function(selector, name) {
3478 var r = [], parts = selector.split(','), is = '[is=' + name + ']';
3479 parts.forEach(function(p) {
3480 p = p.trim();
3481 // selector: *|:scope -> name
3482 if (p.match(hostElementRe)) {
3483 p = p.replace(hostElementRe, name + '$1$3, ' + is + '$1$3');
3484 // selector: .foo -> name.foo, [bar] -> name[bar]
3485 } else if (p.match(hostFixableRe)) {
3486 p = name + p + ', ' + is + p;
3487 }
3488 r.push(p);
3489 }, this);
3490 return r.join(', ');
3491 },
3492 // consider styles that do not include component name in the selector to be
3493 // unscoped and in need of promotion;
3494 // for convenience, also consider keyframe rules this way.
3495 findAtHostRules: function(cssRules, matcher) {
3496 return Array.prototype.filter.call(cssRules,
3497 this.isHostRule.bind(this, matcher));
3498 },
3499 isHostRule: function(matcher, cssRule) {
3500 return (cssRule.selectorText && cssRule.selectorText.match(matcher)) ||
3501 (cssRule.cssRules && this.findAtHostRules(cssRule.cssRules, matcher).lengt h) ||
3502 (cssRule.type == CSSRule.WEBKIT_KEYFRAMES_RULE);
3503 },
3504 /* Ensure styles are scoped. Pseudo-scoping takes a rule like:
3505 *
3506 * .foo {... }
3507 *
3508 * and converts this to
3509 *
3510 * scopeName .foo { ... }
3511 */
3512 shimScoping: function(styles, name) {
3513 if (styles) {
3514 return this.convertScopedStyles(styles, name);
3515 }
3516 },
3517 convertScopedStyles: function(styles, name) {
3518 Array.prototype.forEach.call(styles, function(s) {
3519 if (s.parentNode) {
3520 s.parentNode.removeChild(s);
3521 }
3522 });
3523 var cssText = stylesToCssText(styles).replace(hostRuleRe, '');
3524 cssText = this.convertPseudos(cssText);
3525 var rules = cssToRules(cssText);
3526 cssText = this.scopeRules(rules, name);
3527 return cssText;
3528 },
3529 convertPseudos: function(cssText) {
3530 return cssText.replace(cssPseudoRe, ' [pseudo=$1]');
3531 },
3532 // change a selector like 'div' to 'name div'
3533 scopeRules: function(cssRules, name) {
3534 var cssText = '';
3535 Array.prototype.forEach.call(cssRules, function(rule) {
3536 if (rule.selectorText && (rule.style && rule.style.cssText)) {
3537 cssText += this.scopeSelector(rule.selectorText, name,
3538 this.strictStyling) + ' {\n\t';
3539 cssText += this.propertiesFromRule(rule) + '\n}\n\n';
3540 } else if (rule.media) {
3541 cssText += '@media ' + rule.media.mediaText + ' {\n';
3542 cssText += this.scopeRules(rule.cssRules, name);
3543 cssText += '\n}\n\n';
3544 } else if (rule.cssText) {
3545 cssText += rule.cssText + '\n\n';
3546 }
3547 }, this);
3548 return cssText;
3549 },
3550 scopeSelector: function(selector, name, strict) {
3551 var r = [], parts = selector.split(',');
3552 parts.forEach(function(p) {
3553 p = p.trim();
3554 if (this.selectorNeedsScoping(p, name)) {
3555 p = strict ? this.applyStrictSelectorScope(p, name) :
3556 this.applySimpleSelectorScope(p, name);
3557 }
3558 r.push(p);
3559 }, this);
3560 return r.join(', ');
3561 },
3562 selectorNeedsScoping: function(selector, name) {
3563 var matchScope = '(' + name + '|\\[is=' + name + '\\])';
3564 var re = new RegExp('^' + matchScope + selectorReSuffix, 'm');
3565 return !selector.match(re);
3566 },
3567 // scope via name and [is=name]
3568 applySimpleSelectorScope: function(selector, name) {
3569 return name + ' ' + selector + ', ' + '[is=' + name + '] ' + selector;
3570 },
3571 // return a selector with [name] suffix on each simple selector
3572 // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name]
3573 applyStrictSelectorScope: function(selector, name) {
3574 var splits = [' ', '>', '+', '~'],
3575 scoped = selector,
3576 attrName = '[' + name + ']';
3577 splits.forEach(function(sep) {
3578 var parts = scoped.split(sep);
3579 scoped = parts.map(function(p) {
3580 var t = p.trim();
3581 if (t && (splits.indexOf(t) < 0) && (t.indexOf(attrName) < 0)) {
3582 p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3')
3583 }
3584 return p;
3585 }).join(sep);
3586 });
3587 return scoped;
3588 },
3589 propertiesFromRule: function(rule) {
3590 var properties = rule.style.cssText;
3591 // TODO(sorvell): Chrome cssom incorrectly removes quotes from the content
3592 // property. (https://code.google.com/p/chromium/issues/detail?id=247231)
3593 if (rule.style.content && !rule.style.content.match(/['"]+/)) {
3594 properties = 'content: \'' + rule.style.content + '\';\n' +
3595 rule.style.cssText.replace(/content:[^;]*;/g, '');
3596 }
3597 return properties;
3598 }
3599 };
3600
3601 var hostRuleRe = /@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,
3602 selectorRe = /([^{]*)({[\s\S]*?})/gim,
3603 hostElementRe = /(.*)((?:\*)|(?:\:scope))(.*)/,
3604 hostFixableRe = /^[.\[:]/,
3605 cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
3606 cssPolyfillCommentRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*? ){/gim,
3607 cssPseudoRe = /::(x-[^\s{,(]*)/gim,
3608 selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$',
3609 hostRe = /@host/gim;
3610
3611 function stylesToCssText(styles, preserveComments) {
3612 var cssText = '';
3613 Array.prototype.forEach.call(styles, function(s) {
3614 cssText += s.textContent + '\n\n';
3615 });
3616 // strip comments for easier processing
3617 if (!preserveComments) {
3618 cssText = cssText.replace(cssCommentRe, '');
3619 }
3620 return cssText;
3621 }
3622
3623 function cssToRules(cssText) {
3624 var style = document.createElement('style');
3625 style.textContent = cssText;
3626 document.head.appendChild(style);
3627 var rules = style.sheet.cssRules;
3628 style.parentNode.removeChild(style);
3629 return rules;
3630 }
3631
3632 function rulesToCss(cssRules) {
3633 for (var i=0, css=[]; i < cssRules.length; i++) {
3634 css.push(cssRules[i].cssText);
3635 }
3636 return css.join('\n\n');
3637 }
3638
3639 function addCssToDocument(cssText) {
3640 if (cssText) {
3641 getSheet().appendChild(document.createTextNode(cssText));
3642 }
3643 }
3644
3645 var sheet;
3646 function getSheet() {
3647 if (!sheet) {
3648 sheet = document.createElement("style");
3649 sheet.setAttribute('ShadowCSSShim', '');
3650 }
3651 return sheet;
3652 }
3653
3654 // add polyfill stylesheet to document
3655 if (window.ShadowDOMPolyfill) {
3656 addCssToDocument('style { display: none !important; }\n');
3657 var head = document.querySelector('head');
3658 head.insertBefore(getSheet(), head.childNodes[0]);
3659 }
3660
3661 // exports
3662 scope.ShadowCSS = ShadowCSS;
3663
3664 })(window.Platform);
3665 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
3666 // for details. All rights reserved. Use of this source code is governed by a
3667 // BSD-style license that can be found in the LICENSE file.
3668
3669 (function(scope) {
3670 // TODO(terry): Remove shimShadowDOMStyling2 until wrap/unwrap from a
3671 // dart:html Element to a JS DOM node is available.
3672 /**
3673 * Given the content of a STYLE tag and the name of a component shim the CSS
3674 * and return the new scoped CSS to replace the STYLE's content. The content
3675 * is replaced in Dart's implementation of PolymerElement.
3676 */
3677 function shimShadowDOMStyling2(styleContent, name) {
3678 if (window.ShadowDOMPolyfill) {
3679 var content = this.convertPolyfillDirectives(styleContent, name);
3680
3681 // applyShimming calls shimAtHost and shipScoping
3682 // shimAtHost code:
3683 var r = '', l=0, matches;
3684 while (matches = hostRuleRe.exec(content)) {
3685 r += content.substring(l, matches.index);
3686 r += this.scopeHostCss(matches[1], name);
3687 l = hostRuleRe.lastIndex;
3688 }
3689 r += content.substring(l, content.length);
3690 var re = new RegExp('^' + name + selectorReSuffix, 'm');
3691 var atHostCssText = rulesToCss(this.findAtHostRules(cssToRules(r), re));
3692
3693 // shimScoping code:
3694 // strip comments for easier processing
3695 content = content.replace(cssCommentRe, '');
3696
3697 content = this.convertPseudos(content);
3698 var rules = cssToRules(content);
3699 var cssText = this.scopeRules(rules, name);
3700
3701 return atHostCssText + cssText;
3702 }
3703 }
3704
3705 // Minimal copied code from ShadowCSS, that is not exposed in
3706 // PlatForm.ShadowCSS (local code).
3707 var hostRuleRe = /@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,
3708 cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
3709 selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$';
3710
3711 function cssToRules(cssText) {
3712 var style = document.createElement('style');
3713 style.textContent = cssText;
3714 document.head.appendChild(style);
3715 var rules = style.sheet.cssRules;
3716 style.parentNode.removeChild(style);
3717 return rules;
3718 }
3719
3720 function rulesToCss(cssRules) {
3721 for (var i=0, css=[]; i < cssRules.length; i++) {
3722 css.push(cssRules[i].cssText);
3723 }
3724 return css.join('\n\n');
3725 }
3726
3727 // exports
3728 scope.ShadowCSS.shimShadowDOMStyling2 = shimShadowDOMStyling2;
3729 })(window.Platform);
3730
3207 } 3731 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698