blob: 34a684a65e2bd1c9c74664de41e865802c30c07a [file] [log] [blame]
/**
* Copyright 2010 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.
*/
// Copyright 2007 Google Inc. All Rights Reserved.
// Author: yian@google.com (Yi-An Huang)
#include "base/logging.h"
#include "base/macros.h"
#include "util/gtl/map-util.h"
#include "util/gtl/stl_util-inl.h"
#include "webutil/css/valuevalidator.h"
namespace Css {
const Property::Prop kEndProp = static_cast<Property::Prop>(-1);
const Value::ValueType kEndType = static_cast<Value::ValueType>(-1);
const Identifier::Ident kEndIdent = static_cast<Identifier::Ident>(-1);
// For each CSS property (or a group of properties), the valid value types and
// valid IDENT values. We do not list the IDENT, DEFAULT and UNKOWN types
// because they are always valid, we also do not list INHERIT values explicitly
// for the same reason. Arrays are terminated with -1.
static struct valid_prop_info_t {
Property::Prop props[10];
Value::ValueType types[10];
Identifier::Ident idents[30];
// the following properties make sense for numbers.
bool accept_percent;
bool accept_no_unit;
bool accept_length;
bool accept_negative;
} kValidPropInfo[] = {
// Chapter 8: Box model
{ { Property::BORDER_COLOR, Property::BORDER_TOP_COLOR,
Property::BORDER_RIGHT_COLOR, Property::BORDER_BOTTOM_COLOR,
Property::BORDER_LEFT_COLOR, kEndProp },
{ Value::COLOR, kEndType },
{ Identifier::TRANSPARENT, Identifier::GOOG_INITIAL, kEndIdent },
},
{ { Property::BORDER_STYLE, Property::BORDER_TOP_STYLE,
Property::BORDER_RIGHT_STYLE, Property::BORDER_BOTTOM_STYLE,
Property::BORDER_LEFT_STYLE, kEndProp },
{ kEndType },
{ Identifier::NONE, Identifier::HIDDEN, Identifier::DOTTED,
Identifier::DASHED, Identifier::SOLID, Identifier::DOUBLE,
Identifier::GROOVE, Identifier::RIDGE, Identifier::INSET,
Identifier::OUTSET, kEndIdent },
},
{ { Property::BORDER_WIDTH, Property::BORDER_TOP_WIDTH,
Property::BORDER_RIGHT_WIDTH, Property::BORDER_BOTTOM_WIDTH,
Property::BORDER_LEFT_WIDTH, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::THIN, Identifier::MEDIUM, Identifier::THICK, kEndIdent },
false, false, true, false,
},
{ { Property::BORDER, Property::BORDER_TOP, Property::BORDER_RIGHT,
Property::BORDER_BOTTOM, Property::BORDER_LEFT, kEndProp },
{ Value::COLOR, Value::NUMBER, kEndType },
{ Identifier::TRANSPARENT, Identifier::GOOG_INITIAL,
Identifier::NONE, Identifier::HIDDEN, Identifier::DOTTED,
Identifier::DASHED, Identifier::SOLID, Identifier::DOUBLE,
Identifier::GROOVE, Identifier::RIDGE, Identifier::INSET,
Identifier::OUTSET,
Identifier::THIN, Identifier::MEDIUM, Identifier::THICK, kEndIdent },
false, false, true, false,
},
{ { Property::MARGIN, Property::MARGIN_RIGHT, Property::MARGIN_LEFT,
Property::MARGIN_TOP, Property::MARGIN_BOTTOM, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::AUTO, kEndIdent },
true, false, true, true,
},
{ { Property::PADDING, Property::PADDING_RIGHT, Property::PADDING_LEFT,
Property::PADDING_TOP, Property::PADDING_BOTTOM, kEndProp },
{ Value::NUMBER, kEndType },
{ kEndIdent },
true, false, true, false,
},
// Chapter 9: Visual formatting model
{ { Property::BOTTOM, Property::LEFT, Property::RIGHT, Property::TOP,
kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::AUTO, kEndIdent },
true, false, true, true,
},
{ { Property::CLEAR, kEndProp },
{ kEndType },
{ Identifier::NONE, Identifier::LEFT, Identifier::RIGHT, Identifier::BOTH,
kEndIdent },
},
{ { Property::DIRECTION, kEndProp },
{ kEndType },
{ Identifier::LTR, Identifier::RTL, kEndIdent },
},
{ { Property::DISPLAY, kEndProp },
{ kEndType },
{ Identifier::INLINE, Identifier::BLOCK, Identifier::LIST_ITEM,
Identifier::RUN_IN, Identifier::INLINE_BLOCK, Identifier::TABLE,
Identifier::INLINE_TABLE, Identifier::TABLE_ROW_GROUP,
Identifier::TABLE_HEADER_GROUP, Identifier::TABLE_FOOTER_GROUP,
Identifier::TABLE_ROW, Identifier::TABLE_COLUMN_GROUP,
Identifier::TABLE_COLUMN, Identifier::TABLE_CELL,
Identifier::TABLE_CAPTION, Identifier::NONE, kEndIdent },
},
{ { Property::FLOAT, kEndProp },
{ kEndType },
{ Identifier::LEFT, Identifier::RIGHT, Identifier::NONE, kEndIdent },
},
{ { Property::POSITION, kEndProp },
{ kEndType },
{ Identifier::STATIC, Identifier::RELATIVE, Identifier::ABSOLUTE,
Identifier::FIXED, kEndIdent },
},
{ { Property::UNICODE_BIDI, kEndProp },
{ kEndType },
{ Identifier::NORMAL, Identifier::EMBED, Identifier::BIDI_OVERRIDE,
kEndIdent },
},
{ { Property::Z_INDEX, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::AUTO, kEndIdent },
false, true, false, true,
},
// Chapter 10: Visual formatting model details
{ { Property::HEIGHT, Property::WIDTH, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::AUTO, kEndIdent },
true, false, true, false,
},
{ { Property::LINE_HEIGHT, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::NORMAL, kEndIdent },
true, true, true, false,
},
{ { Property::MAX_HEIGHT, Property::MAX_WIDTH, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::NONE, kEndIdent },
true, false, true, false,
},
{ { Property::MIN_HEIGHT, Property::MIN_WIDTH, kEndProp },
{ Value::NUMBER, kEndType },
{ kEndIdent },
true, false, true, false,
},
{ { Property::VERTICAL_ALIGN, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::BASELINE, Identifier::SUB, Identifier::SUPER,
Identifier::TOP, Identifier::TEXT_TOP, Identifier::MIDDLE,
Identifier::BOTTOM, Identifier::TEXT_BOTTOM, kEndIdent },
true, false, true, true,
},
// Chapter 11: Visual effects
{ { Property::CLIP, kEndProp },
{ Value::RECT, kEndType },
{ Identifier::AUTO, kEndIdent },
false, false, true, true,
},
{ { Property::OVERFLOW, kEndProp },
{ kEndType },
{ Identifier::VISIBLE, Identifier::HIDDEN, Identifier::SCROLL,
Identifier::AUTO, kEndIdent },
},
{ { Property::VISIBILITY, kEndProp },
{ kEndType },
{ Identifier::VISIBLE, Identifier::HIDDEN, Identifier::COLLAPSE, kEndIdent
},
},
// Chapter 12: Generated content, automatic numbering, and lists
{ { Property::CONTENT, kEndProp },
{ Value::STRING, Value::URI, Value::COUNTER, Value::FUNCTION, kEndType },
{ Identifier::NORMAL, Identifier::NONE, Identifier::OPEN_QUOTE,
Identifier::NO_OPEN_QUOTE, Identifier::NO_CLOSE_QUOTE, kEndIdent },
},
{ { Property::COUNTER_INCREMENT, Property::COUNTER_RESET, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::NONE, Identifier::OTHER, kEndIdent },
false, true, false, true,
},
{ { Property::LIST_STYLE_IMAGE, kEndProp },
{ Value::URI, kEndType },
{ Identifier::NONE, kEndIdent },
},
{ { Property::LIST_STYLE_POSITION, kEndProp },
{ kEndType },
{ Identifier::INSIDE, Identifier::OUTSIDE, kEndIdent },
},
{ { Property::LIST_STYLE_TYPE, kEndProp },
{ kEndType },
{ Identifier::DISC, Identifier::CIRCLE, Identifier::SQUARE,
Identifier::DECIMAL, Identifier::DECIMAL_LEADING_ZERO,
Identifier::LOWER_ROMAN, Identifier::UPPER_ROMAN,
Identifier::LOWER_GREEK, Identifier::LOWER_LATIN,
Identifier::UPPER_LATIN, Identifier::ARMENIAN, Identifier::GEORGIAN,
Identifier::LOWER_ALPHA, Identifier::UPPER_ALPHA, kEndIdent },
},
{ { Property::LIST_STYLE, kEndProp },
{ Value::URI, kEndType },
{ Identifier::NONE, Identifier::INSIDE, Identifier::OUTSIDE,
Identifier::DISC, Identifier::CIRCLE, Identifier::SQUARE,
Identifier::DECIMAL, Identifier::DECIMAL_LEADING_ZERO,
Identifier::LOWER_ROMAN, Identifier::UPPER_ROMAN,
Identifier::LOWER_GREEK, Identifier::LOWER_LATIN,
Identifier::UPPER_LATIN, Identifier::ARMENIAN, Identifier::GEORGIAN,
Identifier::LOWER_ALPHA, Identifier::UPPER_ALPHA, kEndIdent },
},
{ { Property::QUOTES, kEndProp },
{ Value::STRING, kEndType },
{ Identifier::NONE, kEndIdent },
},
// Chapter 13: Paged media
{ { Property::ORPHANS, kEndProp },
{ Value::NUMBER, kEndType },
{ kEndIdent },
false, true, false, false,
},
{ { Property::PAGE_BREAK_AFTER, Property::PAGE_BREAK_BEFORE, kEndProp },
{ kEndType },
{ Identifier::AUTO, Identifier::ALWAYS, Identifier::AVOID,
Identifier::LEFT, Identifier::RIGHT, kEndIdent },
},
{ { Property::PAGE_BREAK_INSIDE, kEndProp },
{ kEndType },
{ Identifier::AVOID, Identifier::AUTO, kEndIdent },
},
{ { Property::WIDOWS, kEndProp },
{ Value::NUMBER, kEndType },
{ kEndIdent },
false, true, false, false,
},
// Chapter 14: Colors and Backgrounds
{ { Property::BACKGROUND_ATTACHMENT, kEndProp },
{ kEndType },
{ Identifier::SCROLL, Identifier::FIXED, kEndIdent },
},
{ { Property::BACKGROUND_COLOR, kEndProp },
{ Value::COLOR, kEndType },
{ Identifier::TRANSPARENT, kEndIdent },
},
{ { Property::BACKGROUND_IMAGE, kEndProp },
{ Value::URI, kEndType },
{ Identifier::NONE, kEndIdent },
},
{ { Property::BACKGROUND_POSITION, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::LEFT, Identifier::CENTER, Identifier::RIGHT, Identifier::TOP,
Identifier::BOTTOM, kEndIdent },
true, false, true, true,
},
{ { Property::BACKGROUND_REPEAT, kEndProp },
{ kEndType },
{ Identifier::REPEAT, Identifier::REPEAT_X, Identifier::REPEAT_Y,
Identifier::NO_REPEAT, kEndIdent },
},
{ { Property::BACKGROUND, kEndProp },
{ Value::COLOR, Value::URI, Value::NUMBER, kEndType },
{ Identifier::SCROLL, Identifier::FIXED, Identifier::TRANSPARENT,
Identifier::NONE, Identifier::LEFT, Identifier::CENTER,
Identifier::RIGHT, Identifier::TOP, Identifier::BOTTOM,
Identifier::REPEAT, Identifier::REPEAT_X, Identifier::REPEAT_Y,
Identifier::NO_REPEAT, kEndIdent },
true, false, true, true,
},
{ { Property::COLOR, kEndProp },
{ Value::COLOR, kEndType },
{ Identifier::GOOG_BODY_COLOR, Identifier::GOOG_BODY_LINK_COLOR,
kEndIdent },
},
// Chapter 15: Fonts
{ { Property::FONT_FAMILY, kEndProp },
{ Value::STRING, kEndType },
{ Identifier::SERIF, Identifier::SANS_SERIF, Identifier::CURSIVE,
Identifier::FANTASY, Identifier::MONOSPACE, Identifier::OTHER, kEndIdent
},
},
{ { Property::FONT_SIZE, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::XX_SMALL, Identifier::X_SMALL, Identifier::SMALL,
Identifier::MEDIUM, Identifier::LARGE, Identifier::X_LARGE,
Identifier::XX_LARGE, Identifier::LARGER, Identifier::SMALLER,
Identifier::GOOG_BIG, Identifier::GOOG_SMALL, kEndIdent
},
true, false, true, false,
},
{ { Property::FONT_STYLE, kEndProp },
{ kEndType },
{ Identifier::NORMAL, Identifier::ITALIC, Identifier::OBLIQUE, kEndIdent },
},
{ { Property::FONT_VARIANT, kEndProp },
{ kEndType },
{ Identifier::NORMAL, Identifier::SMALL_CAPS, kEndIdent },
},
{ { Property::FONT_WEIGHT, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::NORMAL, Identifier::BOLD, Identifier::BOLDER,
Identifier::LIGHTER, kEndIdent },
false, true, true, false,
},
{ { Property::FONT, kEndProp },
{ Value::STRING, Value::NUMBER, kEndType },
{ Identifier::SERIF, Identifier::SANS_SERIF, Identifier::CURSIVE,
Identifier::FANTASY, Identifier::MONOSPACE, Identifier::OTHER,
Identifier::XX_SMALL, Identifier::X_SMALL, Identifier::SMALL,
Identifier::MEDIUM, Identifier::LARGE, Identifier::X_LARGE,
Identifier::XX_LARGE, Identifier::LARGER, Identifier::SMALLER,
Identifier::NORMAL, Identifier::ITALIC, Identifier::OBLIQUE,
Identifier::SMALL_CAPS, Identifier::BOLD, Identifier::BOLDER,
Identifier::LIGHTER,
Identifier::CAPTION, Identifier::ICON, Identifier::MENU,
Identifier::MESSAGE_BOX, Identifier::MESSAGE_BOX,
Identifier::SMALL_CAPTION, Identifier::STATUS_BAR, kEndIdent },
true, true, true, false,
},
// Chapter 16: Text
{ { Property::LETTER_SPACING, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::NORMAL, kEndIdent },
false, false, true, true,
},
{ { Property::TEXT_ALIGN, kEndProp },
{ kEndType },
{ Identifier::LEFT, Identifier::RIGHT, Identifier::CENTER,
Identifier::JUSTIFY, Identifier::GOOG_INITIAL, kEndIdent },
},
{ { Property::TEXT_DECORATION, kEndProp },
{ kEndType },
{ Identifier::NONE, Identifier::UNDERLINE, Identifier::OVERLINE,
Identifier::LINE_THROUGH, Identifier::BLINK, kEndIdent },
},
{ { Property::TEXT_INDENT, kEndProp },
{ Value::NUMBER, kEndType },
{ kEndIdent },
true, false, true, true,
},
{ { Property::TEXT_TRANSFORM, kEndProp },
{ kEndType },
{ Identifier::CAPITALIZE, Identifier::UPPERCASE, Identifier::LOWERCASE,
Identifier::NONE, kEndIdent },
},
{ { Property::WHITE_SPACE, kEndProp },
{ kEndType },
{ Identifier::NORMAL, Identifier::PRE, Identifier::NOWRAP,
Identifier::PRE_WRAP, Identifier::PRE_LINE, kEndIdent },
},
{ { Property::WORD_SPACING, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::NORMAL, kEndIdent },
false, false, true, true,
},
// Chapter 17: Tables
{ { Property::BORDER_COLLAPSE, kEndProp },
{ kEndType },
{ Identifier::COLLAPSE, Identifier::SEPARATE, kEndIdent },
},
{ { Property::BORDER_SPACING, kEndProp },
{ Value::NUMBER, kEndType },
{ kEndIdent },
false, false, true, false,
},
{ { Property::CAPTION_SIDE, kEndProp },
{ kEndType },
{ Identifier::TOP, Identifier::BOTTOM, kEndIdent },
},
{ { Property::EMPTY_CELLS, kEndProp },
{ kEndType },
{ Identifier::SHOW, Identifier::HIDE, kEndIdent },
},
{ { Property::TABLE_LAYOUT, kEndProp },
{ kEndType },
{ Identifier::AUTO, Identifier::FIXED, kEndIdent },
},
// Chapter 18: User interface
{ { Property::CURSOR, kEndProp },
{ Value::URI, kEndType },
{ Identifier::AUTO, Identifier::CROSSHAIR, Identifier::DEFAULT,
Identifier::POINTER, Identifier::MOVE, Identifier::E_RESIZE,
Identifier::NE_RESIZE, Identifier::NW_RESIZE, Identifier::N_RESIZE,
Identifier::SE_RESIZE, Identifier::SW_RESIZE, Identifier::S_RESIZE,
Identifier::W_RESIZE, Identifier::TEXT, Identifier::WAIT,
Identifier::HELP, Identifier::PROGRESS, kEndIdent },
},
{ { Property::OUTLINE_COLOR, kEndProp },
{ Value::COLOR, kEndType },
{ Identifier::INVERT, kEndIdent },
},
{ { Property::OUTLINE_STYLE, kEndProp },
{ kEndType },
{ Identifier::NONE, Identifier::DOTTED, Identifier::DASHED,
Identifier::SOLID, Identifier::DOUBLE, Identifier::GROOVE,
Identifier::RIDGE, Identifier::INSET, Identifier::OUTSET, kEndIdent },
},
{ { Property::OUTLINE_WIDTH, kEndProp },
{ Value::NUMBER, kEndType },
{ Identifier::THIN, Identifier::MEDIUM, Identifier::THICK, kEndIdent },
false, false, true, false,
},
{ { Property::OUTLINE, kEndProp },
{ Value::COLOR, Value::NUMBER, kEndType },
{ Identifier::INVERT, Identifier::NONE, Identifier::DOTTED,
Identifier::DASHED, Identifier::SOLID, Identifier::DOUBLE,
Identifier::GROOVE, Identifier::RIDGE, Identifier::INSET,
Identifier::OUTSET, Identifier::THIN, Identifier::MEDIUM,
Identifier::THICK, kEndIdent },
false, false, true, false,
}
};
struct PropertyValidationInfo {
std::set<Value::ValueType> valid_types;
std::set<Identifier::Ident> valid_idents;
bool accept_percent;
bool accept_no_unit;
bool accept_length;
bool accept_negative;
};
ValueValidator::ValueValidator() {
validation_info_.resize(Property::OTHER + 1);
for (int i = 0, n = arraysize(kValidPropInfo); i < n; ++i) {
std::set<Value::ValueType> t;
t.insert(Value::IDENT);
t.insert(Value::UNKNOWN);
t.insert(Value::DEFAULT);
for (int j = 0; kValidPropInfo[i].types[j] != -1; ++j) {
CHECK_LT(j, arraysize(kValidPropInfo[i].types));
t.insert(kValidPropInfo[i].types[j]);
}
std::set<Identifier::Ident> s;
s.insert(Identifier::INHERIT);
for (int j = 0; kValidPropInfo[i].idents[j] != -1; ++j) {
CHECK_LT(j, arraysize(kValidPropInfo[i].idents));
s.insert(kValidPropInfo[i].idents[j]);
}
for (int j = 0; kValidPropInfo[i].props[j] != -1; ++j) {
CHECK_LT(j, arraysize(kValidPropInfo[i].props));
PropertyValidationInfo* info = new PropertyValidationInfo;
validation_info_[kValidPropInfo[i].props[j]] = info;
info->valid_types = t;
info->valid_idents = s;
info->accept_percent = kValidPropInfo[i].accept_percent;
info->accept_no_unit = kValidPropInfo[i].accept_no_unit;
info->accept_length = kValidPropInfo[i].accept_length;
info->accept_negative = kValidPropInfo[i].accept_negative;
}
}
}
ValueValidator::~ValueValidator() {
STLDeleteElements(&validation_info_);
}
bool ValueValidator::IsValidValue(Property::Prop prop,
const Value& value,
bool quirks_mode) const {
if (!IsValidType(prop, value.GetLexicalUnitType()))
return false;
if (value.GetLexicalUnitType() == Value::IDENT &&
!IsValidIdentifier(prop, value.GetIdentifier().ident()))
return false;
if (value.GetLexicalUnitType() == Value::NUMBER &&
!IsValidNumber(prop, value, quirks_mode))
return false;
if (value.GetLexicalUnitType() == Value::RECT) {
const Values* params = value.GetParameters();
CHECK(params != NULL && params->size() == 4);
for (Values::const_iterator iter = params->begin();
iter < params->end(); ++iter) {
const Value* param = *iter;
if (param->GetLexicalUnitType() == Value::IDENT) {
if (!IsValidIdentifier(prop, param->GetIdentifier().ident()))
return false;
} else if (param->GetLexicalUnitType() == Value::NUMBER) {
if (!IsValidNumber(prop, *param, quirks_mode))
return false;
} else {
return false;
}
}
}
return true;
}
bool ValueValidator::IsValidType(Property::Prop prop,
Value::ValueType type) const {
const PropertyValidationInfo* info = validation_info_[prop];
// If we don't have information about prop, then all values are accepted.
return info == NULL || ContainsKey(info->valid_types, type);
}
bool ValueValidator::IsValidIdentifier(Property::Prop prop,
Identifier::Ident ident) const {
const PropertyValidationInfo* info = validation_info_[prop];
// If we don't have information about prop, then all values are accepted.
return info == NULL || ContainsKey(info->valid_idents, ident);
}
bool ValueValidator::IsValidNumber(Property::Prop prop, const Value& value,
bool quirks_mode) const {
const PropertyValidationInfo* info = validation_info_[prop];
// If we don't have information about prop, then all values are accepted.
if (info == NULL) return true;
switch (value.GetDimension()) {
case Value::OTHER:
return false;
case Value::DEG:
case Value::RAD:
case Value::GRAD:
case Value::HZ:
case Value::KHZ:
case Value::MS:
case Value::S:
return false; // unless we handle aural properties
case Value::PERCENT:
if (!info->accept_percent)
return false;
break;
case Value::NO_UNIT:
// We accept no-unit numbers if any of
// 1) accepted by the property,
// 2) 0 is always accepted,
// 3) in quirks mode.
if (!info->accept_no_unit && !quirks_mode &&
value.GetFloatValue() != 0.0)
return false;
break;
default:
if (!info->accept_length)
return false;
break;
}
if (value.GetFloatValue() < 0 && !info->accept_negative)
return false;
return true;
}
} // namespace