/*
* 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.CssAttributeSelectorNode;
import com.google.common.css.compiler.ast.CssAttributeSelectorNode.MatchType;
import com.google.common.css.compiler.ast.CssCombinatorNode;
import com.google.common.css.compiler.ast.CssCombinatorNode.Combinator;
import com.google.common.css.compiler.ast.CssPseudoClassNode;
import com.google.common.css.compiler.ast.CssPseudoElementNode;
import com.google.common.css.compiler.ast.CssRefinerNode;
import com.google.common.css.compiler.ast.ErrorManager;
import com.google.common.css.compiler.ast.GssError;
import com.google.common.css.compiler.ast.VisitController;
import java.util.Set;
/**
* This compiler pass enforces that only correct CSS 2.1 is used.
* See {@link "http://www.w3.org/TR/CSS21/"} for the specification.
*
* @author fbenz@google.com (Florian Benz)
*/
public class StrictCss2 extends StrictCssBase {
private static final ImmutableSet<String> PSEUDO_CLASSES_OR_ELEMENT =
ImmutableSet.of("first-child", "link", "visited", "active", "hover",
"focus", "first-line", "first-letter", "before", "after");
private static final ImmutableSet<Combinator>
ALLOWED_COMBINATORS = ImmutableSet.of(Combinator.DESCENDANT,
Combinator.CHILD, Combinator.ADJACENT_SIBLING);
private static final ImmutableSet<MatchType>
ALLOWED_ATTRIBUTE_SELECTORS = ImmutableSet.of(MatchType.ANY,
MatchType.EXACT, MatchType.EXACT_OR_DASH, MatchType.ONE_WORD);
/**
* Units.
* See Section 4.3.2 of
* http://www.w3.org/TR/CSS2/syndata.html#value-def-length
*/
private static final Set<String> UNITS = Sets.newHashSet(
"",
// Relative measures
"em",
"ex",
"%",
// Absolute measures
"in",
"cm",
"mm",
"pt",
"pc",
"px");
@VisibleForTesting
static final String UNSUPPORTED_COMBINATOR_ERROR_MESSAGE =
"An unsupported combinator is used.";
@VisibleForTesting
static final String UNSUPPORTED_PESUDO_CLASS_OR_ELEMENT_ERROR_MESSAGE =
"An unsupported pseudo-class or pseudo-element is used.";
@VisibleForTesting
static final String NEW_PESUDO_ELEMENTS_NOT_ALLOWED_ERROR_MESSAGE =
"Pseudo-elements starting with '::' are not allowed in CSS 2.1.";
@VisibleForTesting
static final String FORBIDDEN_ATTRIBUTE_COMPARER_ERROR_MESSAGE =
"An operator for matching attributes not allowed in CSS 2.1 is used.";
public StrictCss2(VisitController visitController,
ErrorManager errorManager) {
super(visitController, errorManager);
}
/**
* Idenitifies valid units of 'width', 'height', etc.
*/
@Override
Set<String> getValidCssUnits() {
return UNITS;
}
/**
* Ensures that the combinator '~' (introduced in 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;
}
/**
* Ensures that only pseudo-classes valid in CSS 2.1 are used. Especially, the
* new pseudo-classes introduced in CSS 3 are not valid.
*/
@Override
public boolean enterPseudoClass(CssPseudoClassNode node) {
switch (node.getFunctionType()) {
case NONE:
return checkNonFunctionPseudoClass(node);
case NTH:
case NOT:
reportUnsupported(node,
UNSUPPORTED_PESUDO_CLASS_OR_ELEMENT_ERROR_MESSAGE,
PSEUDO_CLASSES_OR_ELEMENT);
return false;
}
return true;
}
/**
* Ensures that only pseudo-classes and pseudo-elements listed in the
* specification are used.
*/
private boolean checkNonFunctionPseudoClass(CssRefinerNode refiner) {
if (!PSEUDO_CLASSES_OR_ELEMENT.contains(refiner.getRefinerName())) {
reportUnsupported(refiner,
UNSUPPORTED_PESUDO_CLASS_OR_ELEMENT_ERROR_MESSAGE,
PSEUDO_CLASSES_OR_ELEMENT);
return false;
}
return true;
}
/**
* Ensures that the new pseudo-element notation (::) is not used, because
* the notation has been introduced in CSS 3.
*/
@Override
public boolean enterPseudoElement(CssPseudoElementNode node) {
errorManager.report(new GssError(
NEW_PESUDO_ELEMENTS_NOT_ALLOWED_ERROR_MESSAGE,
node.getSourceCodeLocation()));
return false;
}
/**
* Ensures that attribute selectors are valid. If the equal sign is preceded
* by a special character, only '~' and '|' are allowed.
* Valid CSS 2.1 examples: {@code [att]}, {@code [att=val]},
* {@code [att~=val]}, {@code [att|=val]}
*/
@Override
public boolean enterAttributeSelector(CssAttributeSelectorNode node) {
if (!ALLOWED_ATTRIBUTE_SELECTORS.contains(node.getMatchType())) {
errorManager.report(new GssError(
FORBIDDEN_ATTRIBUTE_COMPARER_ERROR_MESSAGE,
node.getSourceCodeLocation()));
return false;
}
return true;
}
}