OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/css/CSSSyntaxDescriptor.h" | 5 #include "core/css/CSSSyntaxDescriptor.h" |
6 | 6 |
7 #include "core/css/CSSCustomPropertyDeclaration.h" | 7 #include "core/css/CSSCustomPropertyDeclaration.h" |
8 #include "core/css/CSSURIValue.h" | 8 #include "core/css/CSSURIValue.h" |
9 #include "core/css/CSSValueList.h" | 9 #include "core/css/CSSValueList.h" |
10 #include "core/css/CSSVariableReferenceValue.h" | 10 #include "core/css/CSSVariableReferenceValue.h" |
| 11 #include "core/css/parser/CSSParserIdioms.h" |
11 #include "core/css/parser/CSSPropertyParserHelpers.h" | 12 #include "core/css/parser/CSSPropertyParserHelpers.h" |
12 #include "core/css/parser/CSSTokenizer.h" | 13 #include "core/css/parser/CSSTokenizer.h" |
13 #include "core/css/parser/CSSVariableParser.h" | 14 #include "core/css/parser/CSSVariableParser.h" |
| 15 #include "core/html/parser/HTMLParserIdioms.h" |
14 | 16 |
15 namespace blink { | 17 namespace blink { |
16 | 18 |
| 19 void consumeWhitespace(const String& string, size_t& offset) |
| 20 { |
| 21 while (isHTMLSpace(string[offset])) |
| 22 offset++; |
| 23 } |
| 24 |
| 25 bool consumeCharacterAndWhitespace(const String& string, char character, size_t&
offset) |
| 26 { |
| 27 if (string[offset] != character) |
| 28 return false; |
| 29 offset++; |
| 30 consumeWhitespace(string, offset); |
| 31 return true; |
| 32 } |
| 33 |
| 34 CSSSyntaxType parseSyntaxType(String type) |
| 35 { |
| 36 // TODO(timloh): Are these supposed to be case sensitive? |
| 37 if (type == "length") |
| 38 return CSSSyntaxType::Length; |
| 39 if (type == "number") |
| 40 return CSSSyntaxType::Number; |
| 41 if (type == "percentage") |
| 42 return CSSSyntaxType::Percentage; |
| 43 if (type == "length-percentage") |
| 44 return CSSSyntaxType::LengthPercentage; |
| 45 if (type == "color") |
| 46 return CSSSyntaxType::Color; |
| 47 if (type == "image") |
| 48 return CSSSyntaxType::Image; |
| 49 if (type == "url") |
| 50 return CSSSyntaxType::Url; |
| 51 if (type == "integer") |
| 52 return CSSSyntaxType::Integer; |
| 53 if (type == "angle") |
| 54 return CSSSyntaxType::Angle; |
| 55 if (type == "time") |
| 56 return CSSSyntaxType::Time; |
| 57 if (type == "resolution") |
| 58 return CSSSyntaxType::Resolution; |
| 59 if (type == "transform-function") |
| 60 return CSSSyntaxType::TransformFunction; |
| 61 if (type == "custom-ident") |
| 62 return CSSSyntaxType::CustomIdent; |
| 63 // Not an Ident, just used to indicate failure |
| 64 return CSSSyntaxType::Ident; |
| 65 } |
| 66 |
| 67 bool consumeSyntaxType(const String& input, size_t& offset, CSSSyntaxType& type) |
| 68 { |
| 69 DCHECK_EQ(input[offset], '<'); |
| 70 offset++; |
| 71 size_t typeStart = offset; |
| 72 while (offset < input.length() && input[offset] != '>') |
| 73 offset++; |
| 74 if (offset == input.length()) |
| 75 return false; |
| 76 type = parseSyntaxType(input.substring(typeStart, offset - typeStart)); |
| 77 if (type == CSSSyntaxType::Ident) |
| 78 return false; |
| 79 offset++; |
| 80 return true; |
| 81 } |
| 82 |
| 83 bool consumeSyntaxIdent(const String& input, size_t& offset, String& ident) |
| 84 { |
| 85 // TODO(timloh): Are CSS-wide keywords allowed here? |
| 86 size_t identStart = offset; |
| 87 while (isNameCodePoint(input[offset])) |
| 88 offset++; |
| 89 if (offset == identStart) |
| 90 return false; |
| 91 ident = input.substring(identStart, offset - identStart); |
| 92 return true; |
| 93 } |
| 94 |
17 CSSSyntaxDescriptor::CSSSyntaxDescriptor(String input) | 95 CSSSyntaxDescriptor::CSSSyntaxDescriptor(String input) |
18 { | 96 { |
19 // TODO(timloh): Implement proper parsing | 97 size_t offset = 0; |
20 if (input.contains('*')) | 98 consumeWhitespace(input, offset); |
21 m_syntaxComponents.append(CSSSyntaxComponent(CSSSyntaxType::TokenStream)
); | 99 |
22 else | 100 if (consumeCharacterAndWhitespace(input, '*', offset)) { |
23 m_syntaxComponents.append(CSSSyntaxComponent(CSSSyntaxType::Length)); | 101 if (offset != input.length()) |
| 102 return; |
| 103 m_syntaxComponents.append(CSSSyntaxComponent(CSSSyntaxType::TokenStream,
emptyString(), false)); |
| 104 return; |
| 105 } |
| 106 |
| 107 do { |
| 108 CSSSyntaxType type; |
| 109 String ident; |
| 110 bool success; |
| 111 |
| 112 if (input[offset] == '<') { |
| 113 success = consumeSyntaxType(input, offset, type); |
| 114 } else { |
| 115 type = CSSSyntaxType::Ident; |
| 116 success = consumeSyntaxIdent(input, offset, ident); |
| 117 } |
| 118 |
| 119 if (!success) { |
| 120 m_syntaxComponents.clear(); |
| 121 return; |
| 122 } |
| 123 |
| 124 bool repeatable = consumeCharacterAndWhitespace(input, '+', offset); |
| 125 consumeWhitespace(input, offset); |
| 126 m_syntaxComponents.append(CSSSyntaxComponent(type, ident, repeatable)); |
| 127 |
| 128 } while (consumeCharacterAndWhitespace(input, '|', offset)); |
| 129 |
| 130 if (offset != input.length()) |
| 131 m_syntaxComponents.clear(); |
24 } | 132 } |
25 | 133 |
26 const CSSValue* consumeSingleType(const CSSSyntaxComponent& syntax, CSSParserTok
enRange& range) | 134 const CSSValue* consumeSingleType(const CSSSyntaxComponent& syntax, CSSParserTok
enRange& range) |
27 { | 135 { |
28 using namespace CSSPropertyParserHelpers; | 136 using namespace CSSPropertyParserHelpers; |
29 | 137 |
30 // TODO(timloh): Calc values need to be normalized | 138 // TODO(timloh): Calc values need to be normalized |
31 switch (syntax.m_type) { | 139 switch (syntax.m_type) { |
| 140 case CSSSyntaxType::Ident: |
| 141 if (range.peek().type() == IdentToken |
| 142 && range.peek().value() == syntax.m_string) { |
| 143 range.consumeIncludingWhitespace(); |
| 144 return CSSCustomIdentValue::create(AtomicString(syntax.m_string)); |
| 145 } |
| 146 return nullptr; |
32 case CSSSyntaxType::Length: | 147 case CSSSyntaxType::Length: |
33 return consumeLength(range, HTMLStandardMode, ValueRange::ValueRangeAll)
; | 148 return consumeLength(range, HTMLStandardMode, ValueRange::ValueRangeAll)
; |
| 149 case CSSSyntaxType::Number: |
| 150 return consumeNumber(range, ValueRange::ValueRangeAll); |
| 151 case CSSSyntaxType::Percentage: |
| 152 return consumePercent(range, ValueRange::ValueRangeAll); |
| 153 case CSSSyntaxType::LengthPercentage: |
| 154 return consumeLengthOrPercent(range, HTMLStandardMode, ValueRange::Value
RangeAll); |
| 155 case CSSSyntaxType::Color: |
| 156 return consumeColor(range, HTMLStandardMode); |
| 157 case CSSSyntaxType::Image: |
| 158 // TODO(timloh): This probably needs a proper parser context for relativ
e URL resolution. |
| 159 return consumeImage(range, strictCSSParserContext()); |
| 160 case CSSSyntaxType::Url: |
| 161 return consumeUrl(range); |
| 162 case CSSSyntaxType::Integer: |
| 163 return consumeInteger(range); |
| 164 case CSSSyntaxType::Angle: |
| 165 return consumeAngle(range); |
| 166 case CSSSyntaxType::Time: |
| 167 return consumeTime(range, ValueRange::ValueRangeAll); |
| 168 case CSSSyntaxType::Resolution: |
| 169 return nullptr; // TODO(timloh): Implement this. |
| 170 case CSSSyntaxType::TransformFunction: |
| 171 return nullptr; // TODO(timloh): Implement this. |
| 172 case CSSSyntaxType::CustomIdent: |
| 173 return consumeCustomIdent(range); |
34 default: | 174 default: |
35 NOTREACHED(); | 175 NOTREACHED(); |
36 return nullptr; | 176 return nullptr; |
37 } | 177 } |
38 } | 178 } |
39 | 179 |
40 const CSSValue* consumeSyntaxComponent(const CSSSyntaxComponent& syntax, CSSPars
erTokenRange range) | 180 const CSSValue* consumeSyntaxComponent(const CSSSyntaxComponent& syntax, CSSPars
erTokenRange range) |
41 { | 181 { |
42 // CSS-wide keywords are already handled by the CSSPropertyParser | 182 // CSS-wide keywords are already handled by the CSSPropertyParser |
| 183 if (syntax.m_repeatable) { |
| 184 CSSValueList* list = CSSValueList::createSpaceSeparated(); |
| 185 while (!range.atEnd()) { |
| 186 const CSSValue* value = consumeSingleType(syntax, range); |
| 187 if (!value) |
| 188 return nullptr; |
| 189 list->append(*value); |
| 190 } |
| 191 return list; |
| 192 } |
43 const CSSValue* result = consumeSingleType(syntax, range); | 193 const CSSValue* result = consumeSingleType(syntax, range); |
44 if (!range.atEnd()) | 194 if (!range.atEnd()) |
45 return nullptr; | 195 return nullptr; |
46 return result; | 196 return result; |
47 } | 197 } |
48 | 198 |
49 const CSSValue* CSSSyntaxDescriptor::parse(const String& value) const | 199 const CSSValue* CSSSyntaxDescriptor::parse(const String& value) const |
50 { | 200 { |
51 CSSTokenizer::Scope scope(value); | 201 CSSTokenizer::Scope scope(value); |
52 return parse(scope.tokenRange()); | 202 return parse(scope.tokenRange()); |
53 } | 203 } |
54 | 204 |
55 const CSSValue* CSSSyntaxDescriptor::parse(CSSParserTokenRange range) const | 205 const CSSValue* CSSSyntaxDescriptor::parse(CSSParserTokenRange range) const |
56 { | 206 { |
57 if (isTokenStream()) | 207 if (isTokenStream()) |
58 return CSSVariableParser::parseRegisteredPropertyValue(range, false); | 208 return CSSVariableParser::parseRegisteredPropertyValue(range, false); |
59 range.consumeWhitespace(); | 209 range.consumeWhitespace(); |
60 for (const CSSSyntaxComponent& component : m_syntaxComponents) { | 210 for (const CSSSyntaxComponent& component : m_syntaxComponents) { |
61 if (const CSSValue* result = consumeSyntaxComponent(component, range)) | 211 if (const CSSValue* result = consumeSyntaxComponent(component, range)) |
62 return result; | 212 return result; |
63 } | 213 } |
64 return CSSVariableParser::parseRegisteredPropertyValue(range, true); | 214 return CSSVariableParser::parseRegisteredPropertyValue(range, true); |
65 } | 215 } |
66 | 216 |
67 } // namespace blink | 217 } // namespace blink |
OLD | NEW |