X Tutup
Skip to content

Commit d8775e0

Browse files
committed
fix(shadow_css): strip comments and fix logic for parsing rules.
Closes #5037 Closes #5011
1 parent 53bddec commit d8775e0

File tree

2 files changed

+151
-110
lines changed

2 files changed

+151
-110
lines changed

modules/angular2/src/core/compiler/shadow_css.ts

Lines changed: 93 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,6 @@ export class ShadowCss {
139139

140140
constructor() {}
141141

142-
/*
143-
* Shim a style element with the given selector. Returns cssText that can
144-
* be included in the document via WebComponents.ShadowCSS.addCssToDocument(css).
145-
*/
146-
shimStyle(cssText: string, selector: string, hostSelector: string = ''): string {
147-
return this.shimCssText(cssText, selector, hostSelector);
148-
}
149-
150142
/*
151143
* Shim some cssText with the given selector. Returns cssText that can
152144
* be included in the document via WebComponents.ShadowCSS.addCssToDocument(css).
@@ -156,12 +148,12 @@ export class ShadowCss {
156148
* - hostSelector is the attribute added to the host itself.
157149
*/
158150
shimCssText(cssText: string, selector: string, hostSelector: string = ''): string {
151+
cssText = stripComments(cssText);
159152
cssText = this._insertDirectives(cssText);
160153
return this._scopeCssText(cssText, selector, hostSelector);
161154
}
162155

163-
/** @internal */
164-
_insertDirectives(cssText: string): string {
156+
private _insertDirectives(cssText: string): string {
165157
cssText = this._insertPolyfillDirectivesInCssText(cssText);
166158
return this._insertPolyfillRulesInCssText(cssText);
167159
}
@@ -180,8 +172,7 @@ export class ShadowCss {
180172
* scopeName menu-item {
181173
*
182174
**/
183-
/** @internal */
184-
_insertPolyfillDirectivesInCssText(cssText: string): string {
175+
private _insertPolyfillDirectivesInCssText(cssText: string): string {
185176
// Difference with webcomponents.js: does not handle comments
186177
return StringWrapper.replaceAllMapped(cssText, _cssContentNextSelectorRe,
187178
function(m) { return m[1] + '{'; });
@@ -202,8 +193,7 @@ export class ShadowCss {
202193
* scopeName menu-item {...}
203194
*
204195
**/
205-
/** @internal */
206-
_insertPolyfillRulesInCssText(cssText: string): string {
196+
private _insertPolyfillRulesInCssText(cssText: string): string {
207197
// Difference with webcomponents.js: does not handle comments
208198
return StringWrapper.replaceAllMapped(cssText, _cssContentRuleRe, function(m) {
209199
var rule = m[0];
@@ -221,8 +211,7 @@ export class ShadowCss {
221211
*
222212
* scopeName .foo { ... }
223213
*/
224-
/** @internal */
225-
_scopeCssText(cssText: string, scopeSelector: string, hostSelector: string): string {
214+
private _scopeCssText(cssText: string, scopeSelector: string, hostSelector: string): string {
226215
var unscoped = this._extractUnscopedRulesFromCssText(cssText);
227216
cssText = this._insertPolyfillHostInCssText(cssText);
228217
cssText = this._convertColonHost(cssText);
@@ -250,8 +239,7 @@ export class ShadowCss {
250239
* menu-item {...}
251240
*
252241
**/
253-
/** @internal */
254-
_extractUnscopedRulesFromCssText(cssText: string): string {
242+
private _extractUnscopedRulesFromCssText(cssText: string): string {
255243
// Difference with webcomponents.js: does not handle comments
256244
var r = '', m;
257245
var matcher = RegExpWrapper.matcher(_cssContentUnscopedRuleRe, cssText);
@@ -271,8 +259,7 @@ export class ShadowCss {
271259
*
272260
* scopeName.foo > .bar
273261
*/
274-
/** @internal */
275-
_convertColonHost(cssText: string): string {
262+
private _convertColonHost(cssText: string): string {
276263
return this._convertColonRule(cssText, _cssColonHostRe, this._colonHostPartReplacer);
277264
}
278265

@@ -291,14 +278,12 @@ export class ShadowCss {
291278
*
292279
* scopeName.foo .bar { ... }
293280
*/
294-
/** @internal */
295-
_convertColonHostContext(cssText: string): string {
281+
private _convertColonHostContext(cssText: string): string {
296282
return this._convertColonRule(cssText, _cssColonHostContextRe,
297283
this._colonHostContextPartReplacer);
298284
}
299285

300-
/** @internal */
301-
_convertColonRule(cssText: string, regExp: RegExp, partReplacer: Function): string {
286+
private _convertColonRule(cssText: string, regExp: RegExp, partReplacer: Function): string {
302287
// p1 = :host, p2 = contents of (), p3 rest of rule
303288
return StringWrapper.replaceAllMapped(cssText, regExp, function(m) {
304289
if (isPresent(m[2])) {
@@ -316,70 +301,46 @@ export class ShadowCss {
316301
});
317302
}
318303

319-
/** @internal */
320-
_colonHostContextPartReplacer(host: string, part: string, suffix: string): string {
304+
private _colonHostContextPartReplacer(host: string, part: string, suffix: string): string {
321305
if (StringWrapper.contains(part, _polyfillHost)) {
322306
return this._colonHostPartReplacer(host, part, suffix);
323307
} else {
324308
return host + part + suffix + ', ' + part + ' ' + host + suffix;
325309
}
326310
}
327311

328-
/** @internal */
329-
_colonHostPartReplacer(host: string, part: string, suffix: string): string {
312+
private _colonHostPartReplacer(host: string, part: string, suffix: string): string {
330313
return host + StringWrapper.replace(part, _polyfillHost, '') + suffix;
331314
}
332315

333316
/*
334317
* Convert combinators like ::shadow and pseudo-elements like ::content
335318
* by replacing with space.
336319
*/
337-
/** @internal */
338-
_convertShadowDOMSelectors(cssText: string): string {
320+
private _convertShadowDOMSelectors(cssText: string): string {
339321
for (var i = 0; i < _shadowDOMSelectorsRe.length; i++) {
340322
cssText = StringWrapper.replaceAll(cssText, _shadowDOMSelectorsRe[i], ' ');
341323
}
342324
return cssText;
343325
}
344326

345327
// change a selector like 'div' to 'name div'
346-
/** @internal */
347-
_scopeSelectors(cssText: string, scopeSelector: string, hostSelector: string): string {
348-
var parts = splitCurlyBlocks(cssText);
349-
var result = [];
350-
for (var i = 0; i < parts.length; i += 2) {
351-
var selectorTextWithCommands = parts[i];
352-
var selectorStart = selectorTextWithCommands.lastIndexOf(';') + 1;
353-
var selectorText =
354-
selectorTextWithCommands.substring(selectorStart, selectorTextWithCommands.length);
355-
var ruleContent = parts[i + 1];
356-
var selectorMatch = RegExpWrapper.firstMatch(_singleSelectorRe, selectorText);
357-
if (isPresent(selectorMatch) && ruleContent.length > 0) {
358-
var selPrefix = selectorMatch[1];
359-
var selAt = isPresent(selectorMatch[2]) ? selectorMatch[2] : '';
360-
var selector = selectorMatch[3];
361-
var selSuffix = selectorMatch[4];
362-
if (selAt.length === 0 || selAt == '@page') {
363-
var scopedSelector =
364-
this._scopeSelector(selector, scopeSelector, hostSelector, this.strictStyling);
365-
selectorText = `${selPrefix}${selAt}${scopedSelector}${selSuffix}`;
366-
} else if (selAt == '@media' && ruleContent[0] == OPEN_CURLY &&
367-
ruleContent[ruleContent.length - 1] == CLOSE_CURLY) {
368-
var scopedContent = this._scopeSelectors(ruleContent.substring(1, ruleContent.length - 1),
369-
scopeSelector, hostSelector);
370-
ruleContent = `${OPEN_CURLY}${scopedContent}${CLOSE_CURLY}`;
371-
}
328+
private _scopeSelectors(cssText: string, scopeSelector: string, hostSelector: string): string {
329+
return processRules(cssText, (rule: CssRule) => {
330+
var selector = rule.selector;
331+
var content = rule.content;
332+
if (rule.selector[0] != '@' || rule.selector.startsWith('@page')) {
333+
selector =
334+
this._scopeSelector(rule.selector, scopeSelector, hostSelector, this.strictStyling);
335+
} else if (rule.selector.startsWith('@media')) {
336+
content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
372337
}
373-
result.push(selectorTextWithCommands.substring(0, selectorStart));
374-
result.push(selectorText);
375-
result.push(ruleContent);
376-
}
377-
return result.join('');
338+
return new CssRule(selector, content);
339+
});
378340
}
379341

380-
/** @internal */
381-
_scopeSelector(selector: string, scopeSelector: string, hostSelector: string,
382-
strict: boolean): string {
342+
private _scopeSelector(selector: string, scopeSelector: string, hostSelector: string,
343+
strict: boolean): string {
383344
var r = [], parts = selector.split(',');
384345
for (var i = 0; i < parts.length; i++) {
385346
var p = parts[i];
@@ -394,30 +355,28 @@ export class ShadowCss {
394355
return r.join(', ');
395356
}
396357

397-
/** @internal */
398-
_selectorNeedsScoping(selector: string, scopeSelector: string): boolean {
358+
private _selectorNeedsScoping(selector: string, scopeSelector: string): boolean {
399359
var re = this._makeScopeMatcher(scopeSelector);
400360
return !isPresent(RegExpWrapper.firstMatch(re, selector));
401361
}
402362

403-
/** @internal */
404-
_makeScopeMatcher(scopeSelector: string): RegExp {
363+
private _makeScopeMatcher(scopeSelector: string): RegExp {
405364
var lre = /\[/g;
406365
var rre = /\]/g;
407366
scopeSelector = StringWrapper.replaceAll(scopeSelector, lre, '\\[');
408367
scopeSelector = StringWrapper.replaceAll(scopeSelector, rre, '\\]');
409368
return RegExpWrapper.create('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');
410369
}
411370

412-
/** @internal */
413-
_applySelectorScope(selector: string, scopeSelector: string, hostSelector: string): string {
371+
private _applySelectorScope(selector: string, scopeSelector: string,
372+
hostSelector: string): string {
414373
// Difference from webcomponentsjs: scopeSelector could not be an array
415374
return this._applySimpleSelectorScope(selector, scopeSelector, hostSelector);
416375
}
417376

418377
// scope via name and [is=name]
419-
/** @internal */
420-
_applySimpleSelectorScope(selector: string, scopeSelector: string, hostSelector: string): string {
378+
private _applySimpleSelectorScope(selector: string, scopeSelector: string,
379+
hostSelector: string): string {
421380
if (isPresent(RegExpWrapper.firstMatch(_polyfillHostRe, selector))) {
422381
var replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
423382
selector = StringWrapper.replace(selector, _polyfillHostNoCombinator, replaceBy);
@@ -428,9 +387,8 @@ export class ShadowCss {
428387
}
429388

430389
// return a selector with [name] suffix on each simple selector
431-
// e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name]
432-
/** @internal */
433-
_applyStrictSelectorScope(selector: string, scopeSelector: string): string {
390+
// e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] /** @internal */
391+
private _applyStrictSelectorScope(selector: string, scopeSelector: string): string {
434392
var isRe = /\[is=([^\]]*)\]/g;
435393
scopeSelector = StringWrapper.replaceAllMapped(scopeSelector, isRe, (m) => m[1]);
436394
var splits = [' ', '>', '+', '~'], scoped = selector, attrName = '[' + scopeSelector + ']';
@@ -455,8 +413,7 @@ export class ShadowCss {
455413
return scoped;
456414
}
457415

458-
/** @internal */
459-
_insertPolyfillHostInCssText(selector: string): string {
416+
private _insertPolyfillHostInCssText(selector: string): string {
460417
selector = StringWrapper.replaceAll(selector, _colonHostContextRe, _polyfillHostContext);
461418
selector = StringWrapper.replaceAll(selector, _colonHostRe, _polyfillHost);
462419
return selector;
@@ -493,34 +450,72 @@ var _polyfillHostRe = RegExpWrapper.create(_polyfillHost, 'im');
493450
var _colonHostRe = /:host/gim;
494451
var _colonHostContextRe = /:host-context/gim;
495452

496-
var _singleSelectorRe = /^(\s*)(@\S+)?(.*?)(\s*)$/g;
453+
var _commentRe = /\/\*[\s\S]*?\*\//g;
454+
455+
function stripComments(input:string):string {
456+
return StringWrapper.replaceAllMapped(input, _commentRe, (_) => '');
457+
}
497458

459+
var _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g;
498460
var _curlyRe = /([{}])/g;
499-
var OPEN_CURLY = '{';
500-
var CLOSE_CURLY = '}';
461+
const OPEN_CURLY = '{';
462+
const CLOSE_CURLY = '}';
463+
const BLOCK_PLACEHOLDER = '%BLOCK%';
501464

502-
export function splitCurlyBlocks(cssText:string):string[] {
503-
var parts = StringWrapper.split(cssText, _curlyRe);
504-
var result = [];
465+
export class CssRule {
466+
constructor(public selector:string, public content:string) {}
467+
}
468+
469+
export function processRules(input:string, ruleCallback:Function):string {
470+
var inputWithEscapedBlocks = escapeBlocks(input);
471+
var nextBlockIndex = 0;
472+
return StringWrapper.replaceAllMapped(inputWithEscapedBlocks.escapedString, _ruleRe, function(m) {
473+
var selector = m[2];
474+
var content = '';
475+
var suffix = m[4];
476+
var contentPrefix = '';
477+
if (isPresent(m[4]) && m[4].startsWith('{'+BLOCK_PLACEHOLDER)) {
478+
content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
479+
suffix = m[4].substring(BLOCK_PLACEHOLDER.length+1);
480+
contentPrefix = '{';
481+
}
482+
var rule = ruleCallback(new CssRule(selector, content));
483+
return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;
484+
});
485+
}
486+
487+
class StringWithEscapedBlocks {
488+
constructor(public escapedString:string, public blocks:string[]) {}
489+
}
490+
491+
function escapeBlocks(input:string):StringWithEscapedBlocks {
492+
var inputParts = StringWrapper.split(input, _curlyRe);
493+
var resultParts = [];
494+
var escapedBlocks = [];
505495
var bracketCount = 0;
506-
var currentCurlyParts = [];
507-
for (var partIndex = 0; partIndex<parts.length; partIndex++) {
508-
var part = parts[partIndex];
509-
currentCurlyParts.push(part);
510-
if (part == OPEN_CURLY) {
511-
bracketCount++;
512-
} else if (part == CLOSE_CURLY) {
496+
var currentBlockParts = [];
497+
for (var partIndex = 0; partIndex<inputParts.length; partIndex++) {
498+
var part = inputParts[partIndex];
499+
if (part == CLOSE_CURLY) {
513500
bracketCount--;
514501
}
515-
if (bracketCount === 0) {
516-
result.push(currentCurlyParts.join(''));
517-
currentCurlyParts = [];
502+
if (bracketCount > 0) {
503+
currentBlockParts.push(part);
504+
} else {
505+
if (currentBlockParts.length > 0) {
506+
escapedBlocks.push(currentBlockParts.join(''));
507+
resultParts.push(BLOCK_PLACEHOLDER);
508+
currentBlockParts = [];
509+
}
510+
resultParts.push(part);
511+
}
512+
if (part == OPEN_CURLY) {
513+
bracketCount++;
518514
}
519515
}
520-
result.push(currentCurlyParts.join(''));
521-
if (result.length >= 2 && result[result.length-1] == '' && result[result.length-2] == '') {
522-
result = result.slice(0, result.length-2);
516+
if (currentBlockParts.length > 0) {
517+
escapedBlocks.push(currentBlockParts.join(''));
518+
resultParts.push(BLOCK_PLACEHOLDER);
523519
}
524-
return result;
520+
return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks);
525521
}
526-

0 commit comments

Comments
 (0)
X Tutup