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

Unified Diff: lib/src/html_css_fixup.dart

Issue 22962005: Merge pull request #581 from kevmoo/polymer (Closed) Base URL: https://github.com/dart-lang/web-ui.git@polymer
Patch Set: 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/files.dart ('k') | lib/src/info.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/html_css_fixup.dart
diff --git a/lib/src/html_css_fixup.dart b/lib/src/html_css_fixup.dart
deleted file mode 100644
index bf5f708927e04ca95becae342751529a8e54d2da..0000000000000000000000000000000000000000
--- a/lib/src/html_css_fixup.dart
+++ /dev/null
@@ -1,590 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library html_css_fixup;
-
-import 'dart:json' as json;
-
-import 'package:csslib/parser.dart' as css;
-import 'package:csslib/visitor.dart';
-import 'package:html5lib/dom.dart';
-import 'package:html5lib/dom_parsing.dart';
-
-import 'compiler.dart';
-import 'emitters.dart';
-import 'info.dart';
-import 'messages.dart';
-import 'compiler_options.dart';
-import 'paths.dart';
-import 'utils.dart';
-
-/** Enum for type of polyfills supported. */
-class CssPolyfillKind {
- final _index;
- const CssPolyfillKind._internal(this._index);
-
- /** Emit CSS selectors as seen (no polyfill). */
- static const NO_POLYFILL = const CssPolyfillKind._internal(0);
-
- /** Emit CSS selectors scoped to the "is" attribute of the component. */
- static const SCOPED_POLYFILL = const CssPolyfillKind._internal(1);
-
- /** Emit CSS selectors mangled. */
- static const MANGLED_POLYFILL = const CssPolyfillKind._internal(2);
-
- static CssPolyfillKind of(CompilerOptions options, ComponentInfo component) {
- if (!options.processCss || !component.scoped) return NO_POLYFILL;
- if (options.mangleCss) return MANGLED_POLYFILL;
- if (!component.hasAuthorStyles && !options.hasCssReset) {
- return MANGLED_POLYFILL;
- }
- return SCOPED_POLYFILL;
- }
-}
-
-
-/**
- * If processCss is enabled, prefix any component's HTML attributes for id or
- * class to reference the mangled CSS class name or id.
- */
-void fixupHtmlCss(FileInfo fileInfo, CompilerOptions options) {
- // Walk the HTML tree looking for class names or id that are in our parsed
- // stylesheet selectors and making those CSS classes and ids unique to that
- // component.
- if (options.verbose) {
- print(" CSS fixup ${path.basename(fileInfo.inputUrl.resolvedPath)}");
- }
- for (var component in fileInfo.declaredComponents) {
- // Mangle class names and element ids in the HTML to match the stylesheet.
- // TODO(terry): Allow more than one style sheet per component.
- if (component.styleSheets.length == 1) {
- // For components only 1 stylesheet allowed.
- var styleSheet = component.styleSheets[0];
- var prefix = CssPolyfillKind.of(options, component) ==
- CssPolyfillKind.MANGLED_POLYFILL ? component.tagName : null;
-
- // List of referenced #id and .class in CSS.
- var knownCss = new IdClassVisitor()..visitTree(styleSheet);
- // Prefix all id and class refs in CSS selectors and HTML attributes.
- new _ScopedStyleRenamer(knownCss, prefix, options.debugCss)
- .visit(component.element);
- }
- }
-}
-
-/** Build list of every CSS class name and id selector in a stylesheet. */
-class IdClassVisitor extends Visitor {
- final Set<String> classes = new Set();
- final Set<String> ids = new Set();
-
- void visitClassSelector(ClassSelector node) {
- classes.add(node.name);
- }
-
- void visitIdSelector(IdSelector node) {
- ids.add(node.name);
- }
-}
-
-/** Build the Dart map of managled class/id names and component tag name. */
-Map _createCssSimpleSelectors(IdClassVisitor visitedCss, ComponentInfo info,
- CssPolyfillKind kind) {
- bool mangleNames = kind == CssPolyfillKind.MANGLED_POLYFILL;
- Map selectors = {};
- if (visitedCss != null) {
- for (var cssClass in visitedCss.classes) {
- selectors['.$cssClass'] =
- mangleNames ? '${info.tagName}_$cssClass' : cssClass;
- }
- for (var id in visitedCss.ids) {
- selectors['#$id'] = mangleNames ? '${info.tagName}_$id' : id;
- }
- }
-
- // Add tag name selector x-comp == [is="x-comp"].
- var componentName = info.tagName;
- selectors['$componentName'] = '[is="$componentName"]';
-
- return selectors;
-}
-
-/**
- * Return a map of simple CSS selectors (class and id selectors) as a Dart map
- * definition.
- */
-String createCssSelectorsExpression(ComponentInfo info, CssPolyfillKind kind) {
- var cssVisited = new IdClassVisitor();
-
- // For components only 1 stylesheet allowed.
- if (!info.styleSheets.isEmpty && info.styleSheets.length == 1) {
- var styleSheet = info.styleSheets[0];
- cssVisited..visitTree(styleSheet);
- }
-
- return json.stringify(_createCssSimpleSelectors(cssVisited, info, kind));
-}
-
-// TODO(terry): Need to handle other selectors than IDs/classes like tag name
-// e.g., DIV { color: red; }
-// TODO(terry): Would be nice if we didn't need to mangle names; requires users
-// to be careful in their code and makes it more than a "polyfill".
-// Maybe mechanism that generates CSS class name for scoping. This
-// would solve tag name selectors (see above TODO).
-/**
- * Fix a component's HTML to implement scoped stylesheets.
- *
- * We do this by renaming all element class and id attributes to be globally
- * unique to a component.
- */
-class _ScopedStyleRenamer extends TreeVisitor {
- final bool _debugCss;
-
- /** Set of classes and ids defined for this component. */
- final IdClassVisitor _knownCss;
-
- /** Prefix to apply to each class/id reference. */
- final String _prefix;
-
- _ScopedStyleRenamer(this._knownCss, this._prefix, this._debugCss);
-
- void visitElement(Element node) {
- // Walk the HTML elements mangling any references to id or class attributes.
- _mangleClassAttribute(node, _knownCss.classes, _prefix);
- _mangleIdAttribute(node, _knownCss.ids, _prefix);
-
- super.visitElement(node);
- }
-
- /**
- * Mangles HTML class reference that matches a CSS class name defined in the
- * component's style sheet.
- */
- void _mangleClassAttribute(Node node, Set<String> classes, String prefix) {
- if (node.attributes.containsKey('class')) {
- var refClasses = node.attributes['class'].trim().split(" ");
-
- bool changed = false;
- var len = refClasses.length;
- for (var i = 0; i < len; i++) {
- var refClass = refClasses[i];
- if (classes.contains(refClass)) {
- if (prefix != null) {
- refClasses[i] = '${prefix}_$refClass';
- changed = true;
- }
- }
- }
-
- if (changed) {
- StringBuffer newClasses = new StringBuffer();
- refClasses.forEach((String className) {
- newClasses.write("${(newClasses.length > 0) ? ' ' : ''}$className");
- });
- var mangledClasses = newClasses.toString();
- if (_debugCss) {
- print(" class = ${node.attributes['class'].trim()} => "
- "$mangledClasses");
- }
- node.attributes['class'] = mangledClasses;
- }
- }
- }
-
- /**
- * Mangles an HTML id reference that matches a CSS id selector name defined
- * in the component's style sheet.
- */
- void _mangleIdAttribute(Node node, Set<String> ids, String prefix) {
- if (prefix != null) {
- var id = node.attributes['id'];
- if (id != null && ids.contains(id)) {
- var mangledName = '${prefix}_$id';
- if (_debugCss) {
- print(" id = ${node.attributes['id'].toString()} => $mangledName");
- }
- node.attributes['id'] = mangledName;
- }
- }
- }
-}
-
-
-/**
- * Find var- definitions in a style sheet.
- * [found] list of known definitions.
- */
-class VarDefinitions extends Visitor {
- final Map<String, VarDefinition> found = new Map();
-
- void visitTree(StyleSheet tree) {
- visitStyleSheet(tree);
- }
-
- visitVarDefinition(VarDefinition node) {
- // Replace with latest variable definition.
- found[node.definedName] = node;
- super.visitVarDefinition(node);
- }
-
- void visitVarDefinitionDirective(VarDefinitionDirective node) {
- visitVarDefinition(node.def);
- }
-}
-
-/**
- * Resolve any CSS expression which contains a var() usage to the ultimate real
- * CSS expression value e.g.,
- *
- * var-one: var(two);
- * var-two: #ff00ff;
- *
- * .test {
- * color: var(one);
- * }
- *
- * then .test's color would be #ff00ff
- */
-class ResolveVarUsages extends Visitor {
- final Map<String, VarDefinition> varDefs;
- bool inVarDefinition = false;
- bool inUsage = false;
- Expressions currentExpressions;
-
- ResolveVarUsages(this.varDefs);
-
- void visitTree(StyleSheet tree) {
- visitStyleSheet(tree);
- }
-
- void visitVarDefinition(VarDefinition varDef) {
- inVarDefinition = true;
- super.visitVarDefinition(varDef);
- inVarDefinition = false;
- }
-
- void visitExpressions(Expressions node) {
- currentExpressions = node;
- super.visitExpressions(node);
- currentExpressions = null;
- }
-
- void visitVarUsage(VarUsage node) {
- // Don't process other var() inside of a varUsage. That implies that the
- // default is a var() too. Also, don't process any var() inside of a
- // varDefinition (they're just place holders until we've resolved all real
- // usages.
- if (!inUsage && !inVarDefinition && currentExpressions != null) {
- var expressions = currentExpressions.expressions;
- var index = expressions.indexOf(node);
- assert(index >= 0);
- var def = varDefs[node.name];
- if (def != null) {
- // Found a VarDefinition use it.
- _resolveVarUsage(currentExpressions.expressions, index, def);
- } else if (node.defaultValues.any((e) => e is VarUsage)) {
- // Don't have a VarDefinition need to use default values resolve all
- // default values.
- var terminalDefaults = [];
- for (var defaultValue in node.defaultValues) {
- terminalDefaults.addAll(resolveUsageTerminal(defaultValue));
- }
- expressions.replaceRange(index, index + 1, terminalDefaults);
- } else {
- // No VarDefinition but default value is a terminal expression; use it.
- expressions.replaceRange(index, index + 1, node.defaultValues);
- }
- }
-
- inUsage = true;
- super.visitVarUsage(node);
- inUsage = false;
- }
-
- List<Expression> resolveUsageTerminal(VarUsage usage) {
- var result = [];
-
- var varDef = varDefs[usage.name];
- var expressions;
- if (varDef == null) {
- // VarDefinition not found try the defaultValues.
- expressions = usage.defaultValues;
- } else {
- // Use the VarDefinition found.
- expressions = (varDef.expression as Expressions).expressions;
- }
-
- for (var expr in expressions) {
- if (expr is VarUsage) {
- // Get terminal value.
- result.addAll(resolveUsageTerminal(expr));
- }
- }
-
- // We're at a terminal just return the VarDefinition expression.
- if (result.isEmpty && varDef != null) {
- result = (varDef.expression as Expressions).expressions;
- }
-
- return result;
- }
-
- _resolveVarUsage(List<Expressions> expressions, int index,
- VarDefinition def) {
- var defExpressions = (def.expression as Expressions).expressions;
- expressions.replaceRange(index, index + 1, defExpressions);
- }
-}
-
-/** Remove all var definitions. */
-class RemoveVarDefinitions extends Visitor {
- void visitTree(StyleSheet tree) {
- visitStyleSheet(tree);
- }
-
- void visitStyleSheet(StyleSheet ss) {
- ss.topLevels.removeWhere((e) => e is VarDefinitionDirective);
- super.visitStyleSheet(ss);
- }
-
- void visitDeclarationGroup(DeclarationGroup node) {
- node.declarations.removeWhere((e) => e is VarDefinition);
- super.visitDeclarationGroup(node);
- }
-}
-
-/**
- * Process all selectors looking for a pseudo-element in a selector. If the
- * name is found in our list of known pseudo-elements. Known pseudo-elements
- * are built when parsing a component looking for an attribute named "pseudo".
- * The value of the pseudo attribute is the name of the custom pseudo-element.
- * The name is mangled so Dart/JS can't directly access the pseudo-element only
- * CSS can access a custom pseudo-element (and see issue #510, querying needs
- * access to custom pseudo-elements).
- *
- * Change the custom pseudo-element to be a child of the pseudo attribute's
- * mangled custom pseudo element name. e.g,
- *
- * .test::x-box
- *
- * would become:
- *
- * .test > *[pseudo="x-box_2"]
- */
-class PseudoElementExpander extends Visitor {
- final Map<String, String> _pseudoElements;
-
- PseudoElementExpander(this._pseudoElements);
-
- void visitTree(StyleSheet tree) => visitStyleSheet(tree);
-
- visitSelector(Selector node) {
- var selectors = node.simpleSelectorSequences;
- for (var index = 0; index < selectors.length; index++) {
- var selector = selectors[index].simpleSelector;
- if (selector is PseudoElementSelector) {
- if (_pseudoElements.containsKey(selector.name)) {
- // Pseudo Element is a custom element.
- var mangledName = _pseudoElements[selector.name];
-
- var span = selectors[index].span;
-
- var attrSelector = new AttributeSelector(
- new Identifier('pseudo', span), css.TokenKind.EQUALS,
- mangledName, span);
- // The wildcard * namespace selector.
- var wildCard = new ElementSelector(new Wildcard(span), span);
- selectors[index] = new SimpleSelectorSequence(wildCard, span,
- css.TokenKind.COMBINATOR_GREATER);
- selectors.insert(++index,
- new SimpleSelectorSequence(attrSelector, span));
- }
- }
- }
- }
-}
-
-/** Compute each CSS URI resource relative from the generated CSS file. */
-class UriVisitor extends Visitor {
- /**
- * Relative path from the output css file to the location of the original
- * css file that contained the URI to each resource.
- */
- final String _pathToOriginalCss;
-
- factory UriVisitor(PathMapper pathMapper, String cssPath, bool rewriteUrl) {
- var cssDir = path.dirname(cssPath);
- var outCssDir = rewriteUrl ? pathMapper.outputDirPath(cssPath)
- : path.dirname(cssPath);
- return new UriVisitor._internal(path.relative(cssDir, from: outCssDir));
- }
-
- UriVisitor._internal(this._pathToOriginalCss);
-
- void visitUriTerm(UriTerm node) {
- // Don't touch URIs that have any scheme (http, etc.).
- var uri = Uri.parse(node.text);
- if (uri.host != '') return;
- if (uri.scheme != '' && uri.scheme != 'package') return;
-
- node.text = pathToUrl(
- path.normalize(path.join(_pathToOriginalCss, node.text)));
- }
-}
-
-List<UrlInfo> findImportsInStyleSheet(StyleSheet styleSheet,
- String packageRoot, UrlInfo inputUrl, Messages messages) {
- var visitor = new CssImports(packageRoot, inputUrl, messages);
- visitor.visitTree(styleSheet);
- return visitor.urlInfos;
-}
-
-/**
- * Find any imports in the style sheet; normalize the style sheet href and
- * return a list of all fully qualified CSS files.
- */
-class CssImports extends Visitor {
- final String packageRoot;
-
- /** Input url of the css file, used to normalize relative import urls. */
- final UrlInfo inputUrl;
-
- /** List of all imported style sheets. */
- final List<UrlInfo> urlInfos = [];
-
- final Messages _messages;
-
- CssImports(this.packageRoot, this.inputUrl, this._messages);
-
- void visitTree(StyleSheet tree) {
- visitStyleSheet(tree);
- }
-
- void visitImportDirective(ImportDirective node) {
- var urlInfo = UrlInfo.resolve(node.import, inputUrl,
- node.span, packageRoot, _messages, ignoreAbsolute: true);
- if (urlInfo == null) return;
- urlInfos.add(urlInfo);
- }
-}
-
-StyleSheet parseCss(String content, Messages messages,
- CompilerOptions options) {
- if (content.trim().isEmpty) return null;
-
- var errors = [];
-
- // TODO(terry): Add --checked when fully implemented and error handling.
- var stylesheet = css.parse(content, errors: errors, options:
- [options.warningsAsErrors ? '--warnings_as_errors' : '', 'memory']);
-
- // Note: errors aren't fatal in HTML (unless strict mode is on).
- // So just print them as warnings.
- for (var e in errors) {
- messages.warning(e.message, e.span);
- }
-
- return stylesheet;
-}
-
-/** Find terminal definition (non VarUsage implies real CSS value). */
-VarDefinition findTerminalVarDefinition(Map<String, VarDefinition> varDefs,
- VarDefinition varDef) {
- var expressions = varDef.expression as Expressions;
- for (var expr in expressions.expressions) {
- if (expr is VarUsage) {
- var usageName = (expr as VarUsage).name;
- var foundDef = varDefs[usageName];
-
- // If foundDef is unknown check if defaultValues; if it exist then resolve
- // to terminal value.
- if (foundDef == null) {
- // We're either a VarUsage or terminal definition if in varDefs;
- // either way replace VarUsage with it's default value because the
- // VarDefinition isn't found.
- var defaultValues = (expr as VarUsage).defaultValues;
- var replaceExprs = expressions.expressions;
- assert(replaceExprs.length == 1);
- replaceExprs.replaceRange(0, 1, defaultValues);
- return varDef;
- }
- if (foundDef is VarDefinition) {
- return findTerminalVarDefinition(varDefs, foundDef);
- }
- } else {
- // Return real CSS property.
- return varDef;
- }
- }
-
- // Didn't point to a var definition that existed.
- return varDef;
-}
-
-/**
- * Find urls imported inside style tags under [info]. If [info] is a FileInfo
- * then process only style tags in the body (don't process any style tags in a
- * component). If [info] is a ComponentInfo only process style tags inside of
- * the element are processed. For an [info] of type FileInfo [node] is the
- * file's document and for an [info] of type ComponentInfo then [node] is the
- * component's element tag.
- */
-List<UrlInfo> findUrlsImported(LibraryInfo info, UrlInfo inputUrl,
- String packageRoot, Node node, Messages messages, CompilerOptions options) {
- // Process any @imports inside of the <style> tag.
- var styleProcessor =
- new CssStyleTag(packageRoot, info, inputUrl, messages, options);
- styleProcessor.visit(node);
- return styleProcessor.imports;
-}
-
-/* Process CSS inside of a style tag. */
-class CssStyleTag extends TreeVisitor {
- final String _packageRoot;
-
- /** Either a FileInfo or ComponentInfo. */
- final LibraryInfo _info;
- final Messages _messages;
- final CompilerOptions _options;
-
- /**
- * Path of the declaring file, for a [_info] of type FileInfo it's the file's
- * path for a type ComponentInfo it's the declaring file path.
- */
- final UrlInfo _inputUrl;
-
- /** List of @imports found. */
- List<UrlInfo> imports = [];
-
- CssStyleTag(this._packageRoot, this._info, this._inputUrl, this._messages,
- this._options);
-
- void visitElement(Element node) {
- // Don't process any style tags inside of element if we're processing a
- // FileInfo. The style tags inside of a component defintion will be
- // processed when _info is a ComponentInfo.
- if (node.tagName == 'polymer-element' && _info is FileInfo) return;
- if (node.tagName == 'style') {
- // Parse the contents of the scoped style tag.
- var styleSheet = parseCss(node.nodes.single.value, _messages, _options);
- if (styleSheet != null) {
- _info.styleSheets.add(styleSheet);
-
- // TODO(terry): Check on scoped attribute there's a rumor that styles
- // might always be scoped in a component.
- // TODO(terry): May need to handle multiple style tags some with scoped
- // and some without for now first style tag determines how
- // CSS is emitted.
- if (node.attributes.containsKey('scoped') && _info is ComponentInfo) {
- (_info as ComponentInfo).scoped = true;
- }
-
- // Find all imports return list of @imports in this style tag.
- var urlInfos = findImportsInStyleSheet(styleSheet, _packageRoot,
- _inputUrl, _messages);
- imports.addAll(urlInfos);
- }
- }
- super.visitElement(node);
- }
-}
« no previous file with comments | « lib/src/files.dart ('k') | lib/src/info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698