com.google.common.css.compiler.passes.StrictCss3 Maven / Gradle / Ivy
Show all versions of closure-stylesheets Show documentation
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.css.compiler.passes;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.css.compiler.ast.*;
import com.google.common.css.compiler.ast.CssCombinatorNode.Combinator;
import java.util.Set;
/**
* This compiler pass enforces that only correct CSS level 3 is used. Be aware
* that there is no final specification yet and that the draft is distributed
* over several documents.
*
* The official W3C drafts used here:
* Selectors Level 3,
* CSS3 Basic User Interface Module
*
*
Wikipedia gives a good overview:
*
* Comparison of layout engines (Cascading Style Sheets)
*
*
TODO(fbenz): The ProcessRefiners and ProcessKeyframes passes should
* run before.
*
* @author [email protected] (Florian Benz)
*/
public class StrictCss3 extends StrictCssBase {
private static final ImmutableSet
ALLOWED_COMBINATORS = ImmutableSet.of(Combinator.DESCENDANT,
Combinator.CHILD, Combinator.ADJACENT_SIBLING, Combinator.GENERAL_SIBLING);
private static final ImmutableSet PSEUDO_CLASSES = ImmutableSet.of(
"root", "first-child", "last-child", "first-of-type", "last-of-type",
"only-child", "only-of-type", "empty", "link", "visited", "active",
"hover", "focus", "target", "enabled", "disabled", "checked",
"indeterminate", "default", "valid", "invalid", "in-range",
"out-of-range", "required", "optional", "read-only", "read-write");
private static final ImmutableSet PSEUDO_CLASSES_NTH =
ImmutableSet.of("nth-child", "nth-last-child", "nth-of-type",
"nth-last-of-type");
private static final ImmutableSet PSEUDO_ELEMENTS = ImmutableSet.of(
"first-line", "first-letter", "before", "after", "value", "choices",
"repeat-item", "repeat-index");
/**
* Length units.
* See Section 5 of
* http://www.w3.org/TR/css3-values/#lengths
*/
private static final Set UNITS = Sets.newHashSet(
"",
// Relative measures
"em",
"ex",
"%",
"ch",
"rem",
"vw",
"vh",
"vm",
// Absolute measures
"in",
"cm",
"mm",
"pt",
"pc",
"px",
// angles
"deg",
"grad",
"rad",
"turn",
// time
"s",
"ms",
// frequency
"hz",
"khz");
@VisibleForTesting
static final String UNSUPPORTED_COMBINATOR_ERROR_MESSAGE =
"An unsupported combinator is used.";
@VisibleForTesting
static final String UNSUPPORTED_PESUDO_CLASS_ERROR_MESSAGE =
"An unsupported pseudo-class is used.";
@VisibleForTesting
static final String UNSUPPORTED_PESUDO_CLASS_NTH_ERROR_MESSAGE =
"An unsupported pseudo-class for the nth-pattern is used.";
@VisibleForTesting
static final String UNSUPPORTED_PESUDO_ELEMENT_ERROR_MESSAGE =
"An unsupported pseudo-element is used.";
@VisibleForTesting
static final String MISSING_FUNCTION_PESUDO_CLASS_NTH_ERROR_MESSAGE =
"A pseudo-class for the nth-pattern is used without an argument.";
public StrictCss3(VisitController visitController,
ErrorManager errorManager) {
super(visitController, errorManager);
}
/**
* Idenitifies valid units of 'width', 'height', etc.
*/
@Override
Set getValidCssUnits() {
return UNITS;
}
/**
* Ensures that the combinator '/deep/' (not strict CSS 3) is not used.
*/
@Override
public boolean enterCombinator(CssCombinatorNode combinator) {
if (!ALLOWED_COMBINATORS.contains(combinator.getCombinatorType())) {
errorManager.report(new GssError(UNSUPPORTED_COMBINATOR_ERROR_MESSAGE,
combinator.getSourceCodeLocation()));
return false;
}
return true;
}
@Override
public boolean enterPseudoClass(CssPseudoClassNode node) {
switch (node.getFunctionType()) {
case NONE:
return checkNonFunctionPseudoClass(node);
case NTH:
return checkNthFunctionPseudoClass(node);
}
return true;
}
/**
* Checks whether the pseudo-class is in the list of standard pseudo-classes
* for CSS level 3. Note that this means that pseudo-elements, like
* {@code after}, that are normally accepted using the pseudo-class style for
* backwards compatibility are rejected.
*/
private boolean checkNonFunctionPseudoClass(CssRefinerNode refiner) {
if (PSEUDO_CLASSES_NTH.contains(refiner.getRefinerName())) {
errorManager.report(new GssError(
MISSING_FUNCTION_PESUDO_CLASS_NTH_ERROR_MESSAGE,
refiner.getSourceCodeLocation()));
return false;
}
if (!PSEUDO_CLASSES.contains(refiner.getRefinerName())) {
reportUnsupported(refiner, UNSUPPORTED_PESUDO_CLASS_ERROR_MESSAGE,
PSEUDO_CLASSES);
return false;
}
return true;
}
private boolean checkNthFunctionPseudoClass(CssRefinerNode refiner) {
// If name is an nth function pseudo class, then it should be of the form:
// "nth-child(".
String name = refiner.getRefinerName();
if (!name.endsWith("(")
|| !PSEUDO_CLASSES_NTH.contains(name.substring(0, name.length() - 1))) {
reportUnsupported(refiner, UNSUPPORTED_PESUDO_CLASS_NTH_ERROR_MESSAGE,
PSEUDO_CLASSES_NTH);
return false;
}
return true;
}
/**
* Ensures that only pseudo-elements valid in CSS 3 are used.
*/
@Override
public boolean enterPseudoElement(CssPseudoElementNode node) {
if (!PSEUDO_ELEMENTS.contains(node.getRefinerName())) {
reportUnsupported(node, UNSUPPORTED_PESUDO_ELEMENT_ERROR_MESSAGE,
PSEUDO_ELEMENTS);
return false;
}
return true;
}
}