blob: 587a2d1e880a8b3f73cf8f03eb33fbfa65a805f5 [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 <string>
#include <vector>
#include "base/stringprintf.h"
#include "strings/join.h"
#include "strings/strutil.h"
#include "webutil/css/parser.h"
#include "webutil/css/string.h"
namespace Css {
// Escape [(), \t\r\n\\'"]
// Based on CEscape/CEscapeString from strings/strutil.cc.
static string CSSEscapeString(const StringPiece& src) {
const int dest_length = src.size() * 2 + 1; // Maximum possible expansion
scoped_array<char> dest(new char[dest_length]);
const char* src_end = src.data() + src.size();
int used = 0;
for (const char* p = src.data(); p < src_end; p++) {
switch (*p) {
case '\n': dest[used++] = '\\'; dest[used++] = 'n'; break;
case '\r': dest[used++] = '\\'; dest[used++] = 'r'; break;
case '\t': dest[used++] = '\\'; dest[used++] = 't'; break;
case '\"': case '\'': case '\\': case ',': case '(': case ')':
dest[used++] = '\\';
dest[used++] = *p;
break;
default: dest[used++] = *p; break;
}
}
return string(dest.get(), used);
}
static string CSSEscapeString(const UnicodeText& src) {
return CSSEscapeString(StringPiece(src.utf8_data(), src.utf8_length()));
}
template <typename Container>
static string JoinElementStrings(const Container& c, const char* delim) {
std::vector<string> vals;
vals.reserve(c.size());
for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it)
vals.push_back((*it)->ToString());
string result;
JoinStrings(vals, delim, &result);
return result;
}
static string JoinMediaStrings(const std::vector<UnicodeText>& media,
const char* delim) {
std::vector<string> vals;
vals.reserve(media.size());
for (std::vector<UnicodeText>::const_iterator
it = media.begin(); it != media.end(); ++it)
vals.push_back(CSSEscapeString(*it));
string result;
JoinStrings(vals, delim, &result);
return result;
}
static string StylesheetTypeString(Stylesheet::StylesheetType type) {
switch (type) {
CONSIDER_IN_CLASS(Stylesheet, AUTHOR);
CONSIDER_IN_CLASS(Stylesheet, USER);
CONSIDER_IN_CLASS(Stylesheet, SYSTEM);
default:
LOG(FATAL) << "Invalid type";
}
}
string Value::ToString() const {
switch (GetLexicalUnitType()) {
case NUMBER:
return StringPrintf("%g%s",
GetFloatValue(),
GetDimensionUnitText().c_str());
case URI:
return StringPrintf("url(%s)",
CSSEscapeString(GetStringValue()).c_str());
case COUNTER:
return StringPrintf("counter(%s)",
GetParameters()->ToString().c_str());
case FUNCTION:
return StringPrintf("%s(%s)",
CSSEscapeString(GetFunctionName()).c_str(),
GetParameters()->ToString().c_str());
case RECT:
return StringPrintf("rect(%s)",
GetParameters()->ToString().c_str());
case COLOR:
if (GetColorValue().IsDefined())
return GetColorValue().ToString();
else
return "bad";
case STRING:
return StringPrintf("\"%s\"",
CSSEscapeString(GetStringValue()).c_str());
case IDENT:
return CSSEscapeString(GetIdentifierText()).c_str();
case UNKNOWN:
return "UNKNOWN";
case DEFAULT:
return "";
default:
LOG(FATAL) << "Invalid type";
}
}
string Values::ToString() const {
return JoinElementStrings(*this, " ");
}
string SimpleSelector::ToString() const {
switch (type()) {
case ELEMENT_TYPE:
return UnicodeTextToUTF8(element_text());
case UNIVERSAL:
return "*";
case EXIST_ATTRIBUTE:
return StringPrintf("[%s]",
CSSEscapeString(attribute()).c_str());
case EXACT_ATTRIBUTE:
return StringPrintf("[%s=%s]",
CSSEscapeString(attribute()).c_str(),
CSSEscapeString(value()).c_str());
case ONE_OF_ATTRIBUTE:
return StringPrintf("[%s~=%s]",
CSSEscapeString(attribute()).c_str(),
CSSEscapeString(value()).c_str());
case BEGIN_HYPHEN_ATTRIBUTE:
return StringPrintf("[%s|=%s]",
CSSEscapeString(attribute()).c_str(),
CSSEscapeString(value()).c_str());
case SUBSTRING_ATTRIBUTE:
return StringPrintf("[%s*=%s]",
CSSEscapeString(attribute()).c_str(),
CSSEscapeString(value()).c_str());
case BEGIN_WITH_ATTRIBUTE:
return StringPrintf("[%s^=%s]",
CSSEscapeString(attribute()).c_str(),
CSSEscapeString(value()).c_str());
case END_WITH_ATTRIBUTE:
return StringPrintf("[%s$=%s]",
CSSEscapeString(attribute()).c_str(),
CSSEscapeString(value()).c_str());
case CLASS:
return StringPrintf(".%s",
CSSEscapeString(value()).c_str());
case ID:
return StringPrintf("#%s",
CSSEscapeString(value()).c_str());
case PSEUDOCLASS:
return StringPrintf(":%s",
CSSEscapeString(pseudoclass()).c_str());
case LANG:
return StringPrintf(":lang(%s)",
CSSEscapeString(lang()).c_str());
default:
LOG(FATAL) << "Invalid type";
}
}
string SimpleSelectors::ToString() const {
string prefix;
switch (combinator()) {
case CHILD:
prefix = "> ";
break;
case SIBLING:
prefix = "+ ";
break;
default:
break;
}
return prefix + JoinElementStrings(*this, "");
}
string Selector::ToString() const {
return JoinElementStrings(*this, " ");
}
string Selectors::ToString() const {
return JoinElementStrings(*this, ", ");
}
string Declaration::ToString() const {
string result = prop_text() + ": ";
switch (prop()) {
case Property::FONT_FAMILY:
result += JoinElementStrings(*values(), ",");
break;
case Property::FONT:
if (values()->size() < 5) {
result += "bad";
} else {
string tmp;
tmp = values()->get(0)->ToString();
if (tmp != "normal") result += tmp + " ";
tmp = values()->get(1)->ToString();
if (tmp != "normal") result += tmp + " ";
tmp = values()->get(2)->ToString();
if (tmp != "normal") result += tmp + " ";
result += values()->get(3)->ToString();
tmp = values()->get(4)->ToString();
if (tmp != "normal") result += "/" + tmp;
for (int i = 5, n = values()->size(); i < n; ++i)
result += (i == 5 ? " " : ",") + values()->get(i)->ToString();
}
break;
default:
result += values()->ToString();
break;
}
if (IsImportant())
result += " !important";
return result;
}
string Declarations::ToString() const {
return JoinElementStrings(*this, "; ");
}
string Ruleset::ToString() const {
string result;
if (!media().empty())
result += StringPrintf("@media %s { ",
JoinMediaStrings(media(), ",").c_str());
result += selectors().ToString() + " {" + declarations().ToString() + "}";
if (!media().empty())
result += " }";
return result;
}
string Import::ToString() const {
return StringPrintf("@import url(\"%s\") %s;",
CSSEscapeString(link).c_str(),
JoinMediaStrings(media, ",").c_str());
}
string Stylesheet::ToString() const {
string result;
result += "/* " + StylesheetTypeString(type()) + " */\n";
result += JoinElementStrings(imports(), "\n") + "\n";
result += JoinElementStrings(rulesets(), "\n") + "\n";
return result;
}
} // namespace