OLD | NEW |
---|---|
1 /* | 1 /* |
2 * (C) 1999-2003 Lars Knoll (knoll@kde.org) | 2 * (C) 1999-2003 Lars Knoll (knoll@kde.org) |
3 * Copyright (C) 2004, 2006, 2010, 2012 Apple Inc. All rights reserved. | 3 * Copyright (C) 2004, 2006, 2010, 2012 Apple Inc. All rights reserved. |
4 * | 4 * |
5 * This library is free software; you can redistribute it and/or | 5 * This library is free software; you can redistribute it and/or |
6 * modify it under the terms of the GNU Library General Public | 6 * modify it under the terms of the GNU Library General Public |
7 * License as published by the Free Software Foundation; either | 7 * License as published by the Free Software Foundation; either |
8 * version 2 of the License, or (at your option) any later version. | 8 * version 2 of the License, or (at your option) any later version. |
9 * | 9 * |
10 * This library is distributed in the hope that it will be useful, | 10 * This library is distributed in the hope that it will be useful, |
(...skipping 17 matching lines...) Expand all Loading... | |
28 #include "core/dom/Document.h" | 28 #include "core/dom/Document.h" |
29 #include "core/dom/ExceptionCode.h" | 29 #include "core/dom/ExceptionCode.h" |
30 #include "core/dom/WebCoreMemoryInstrumentation.h" | 30 #include "core/dom/WebCoreMemoryInstrumentation.h" |
31 #include "core/page/DOMWindow.h" | 31 #include "core/page/DOMWindow.h" |
32 #include "wtf/MemoryInstrumentationVector.h" | 32 #include "wtf/MemoryInstrumentationVector.h" |
33 #include "wtf/text/StringBuilder.h" | 33 #include "wtf/text/StringBuilder.h" |
34 | 34 |
35 namespace WebCore { | 35 namespace WebCore { |
36 | 36 |
37 /* MediaList is used to store 3 types of media related entities which mean the s ame: | 37 /* MediaList is used to store 3 types of media related entities which mean the s ame: |
38 * | |
38 * Media Queries, Media Types and Media Descriptors. | 39 * Media Queries, Media Types and Media Descriptors. |
39 * Currently MediaList always tries to parse media queries and if parsing fails, | 40 * |
40 * tries to fallback to Media Descriptors if m_fallbackToDescriptor flag is set. | |
41 * Slight problem with syntax error handling: | 41 * Slight problem with syntax error handling: |
42 * CSS 2.1 Spec (http://www.w3.org/TR/CSS21/media.html) | 42 * CSS 2.1 Spec (http://www.w3.org/TR/CSS21/media.html) |
43 * specifies that failing media type parsing is a syntax error | 43 * specifies that failing media type parsing is a syntax error |
44 * CSS 3 Media Queries Spec (http://www.w3.org/TR/css3-mediaqueries/) | 44 * CSS 3 Media Queries Spec (http://www.w3.org/TR/css3-mediaqueries/) |
45 * specifies that failing media query is a syntax error | 45 * specifies that failing media query is a syntax error |
46 * HTML 4.01 spec (http://www.w3.org/TR/REC-html40/present/styles.html#adef-medi a) | 46 * HTML 4.01 spec (http://www.w3.org/TR/REC-html40/present/styles.html#adef-medi a) |
47 * specifies that Media Descriptors should be parsed with forward-compatible syn tax | 47 * specifies that Media Descriptors should be parsed with forward-compatible syn tax |
48 * DOM Level 2 Style Sheet spec (http://www.w3.org/TR/DOM-Level-2-Style/) | 48 * DOM Level 2 Style Sheet spec (http://www.w3.org/TR/DOM-Level-2-Style/) |
49 * talks about MediaList.mediaText and refers | 49 * talks about MediaList.mediaText and refers |
50 * - to Media Descriptors of HTML 4.0 in context of StyleSheet | 50 * - to Media Descriptors of HTML 4.0 in context of StyleSheet |
51 * - to Media Types of CSS 2.0 in context of CSSMediaRule and CSSImportRule | 51 * - to Media Types of CSS 2.0 in context of CSSMediaRule and CSSImportRule |
52 * | 52 * |
53 * These facts create situation where same (illegal) media specification may res ult in | 53 * These facts create situation where same (illegal) media specification may res ult in |
54 * different parses depending on whether it is media attr of style element or pa rt of | 54 * different parses depending on whether it is media attr of style element or pa rt of |
55 * css @media rule. | 55 * css @media rule. |
56 * <style media="screen and resolution > 40dpi"> ..</style> will be enabled on s creen devices where as | 56 * <style media="screen and resolution > 40dpi"> ..</style> will be enabled on s creen devices where as |
57 * @media screen and resolution > 40dpi {..} will not. | 57 * @media screen and resolution > 40dpi {..} will not. |
58 * This gets more counter-intuitive in JavaScript: | 58 * This gets more counter-intuitive in JavaScript: |
59 * document.styleSheets[0].media.mediaText = "screen and resolution > 40dpi" wil l be ok and | 59 * document.styleSheets[0].media.mediaText = "screen and resolution > 40dpi" wil l be ok and |
60 * enabled, while | 60 * enabled, while |
61 * document.styleSheets[0].cssRules[0].media.mediaText = "screen and resolution > 40dpi" will | 61 * document.styleSheets[0].cssRules[0].media.mediaText = "screen and resolution > 40dpi" will |
62 * throw SYNTAX_ERR exception. | 62 * throw SYNTAX_ERR exception. |
63 */ | 63 */ |
64 | 64 |
65 MediaQuerySet::MediaQuerySet() | 65 MediaQuerySet::MediaQuerySet() |
66 : m_fallbackToDescriptor(false) | 66 : m_parserMode(MediaQueryNormalMode) |
67 , m_lastLine(0) | 67 , m_lastLine(0) |
68 { | 68 { |
69 } | 69 } |
70 | 70 |
71 MediaQuerySet::MediaQuerySet(const String& mediaString, bool fallbackToDescripto r) | 71 MediaQuerySet::MediaQuerySet(const String& mediaString, MediaQueryParserMode mod e) |
72 : m_fallbackToDescriptor(fallbackToDescriptor) | 72 : m_parserMode(mode) |
73 , m_lastLine(0) | 73 , m_lastLine(0) |
74 { | 74 { |
75 bool success = parse(mediaString); | 75 set(mediaString); |
76 // FIXME: parsing can fail. The problem with failing constructor is that | |
77 // we would need additional flag saying MediaList is not valid | |
78 // Parse can fail only when fallbackToDescriptor == false, i.e when HTML4 me dia descriptor | |
79 // forward-compatible syntax is not in use. | |
80 // DOMImplementationCSS seems to mandate that media descriptors are used | |
81 // for both html and svg, even though svg:style doesn't use media descriptor s | |
82 // Currently the only places where parsing can fail are | |
83 // creating <svg:style>, creating css media / import rules from js | |
84 | |
85 // FIXME: This doesn't make much sense. | |
86 if (!success) | |
87 parse("invalid"); | |
88 } | 76 } |
89 | 77 |
90 MediaQuerySet::MediaQuerySet(const MediaQuerySet& o) | 78 MediaQuerySet::MediaQuerySet(const MediaQuerySet& o) |
91 : RefCounted<MediaQuerySet>() | 79 : RefCounted<MediaQuerySet>() |
92 , m_fallbackToDescriptor(o.m_fallbackToDescriptor) | 80 , m_parserMode(o.m_parserMode) |
93 , m_lastLine(o.m_lastLine) | 81 , m_lastLine(o.m_lastLine) |
94 , m_queries(o.m_queries.size()) | 82 , m_queries(o.m_queries.size()) |
95 { | 83 { |
96 for (unsigned i = 0; i < m_queries.size(); ++i) | 84 for (unsigned i = 0; i < m_queries.size(); ++i) |
97 m_queries[i] = o.m_queries[i]->copy(); | 85 m_queries[i] = o.m_queries[i]->copy(); |
98 } | 86 } |
99 | 87 |
100 MediaQuerySet::~MediaQuerySet() | 88 MediaQuerySet::~MediaQuerySet() |
101 { | 89 { |
102 } | 90 } |
103 | 91 |
104 static String parseMediaDescriptor(const String& string) | 92 static String parseMediaDescriptor(const String& string) |
105 { | 93 { |
106 // http://www.w3.org/TR/REC-html40/types.html#type-media-descriptors | 94 // http://www.w3.org/TR/REC-html40/types.html#type-media-descriptors |
107 // "Each entry is truncated just before the first character that isn't a | 95 // "Each entry is truncated just before the first character that isn't a |
108 // US ASCII letter [a-zA-Z] (ISO 10646 hex 41-5a, 61-7a), digit [0-9] (hex 3 0-39), | 96 // US ASCII letter [a-zA-Z] (ISO 10646 hex 41-5a, 61-7a), digit [0-9] (hex 3 0-39), |
109 // or hyphen (hex 2d)." | 97 // or hyphen (hex 2d)." |
110 unsigned length = string.length(); | 98 unsigned length = string.length(); |
111 unsigned i = 0; | 99 unsigned i = 0; |
112 for (; i < length; ++i) { | 100 for (; i < length; ++i) { |
113 unsigned short c = string[i]; | 101 unsigned short c = string[i]; |
114 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '1' && c <= '9') || (c == '-'))) | 102 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '1' && c <= '9') || (c == '-'))) |
115 break; | 103 break; |
116 } | 104 } |
117 return string.left(i); | 105 return string.left(i); |
118 } | 106 } |
119 | 107 |
120 PassOwnPtr<MediaQuery> MediaQuerySet::parseMediaQuery(const String& queryString) | 108 PassOwnPtr<MediaQuery> MediaQuerySet::parseMediaQuery(const String& queryString, MediaQueryParserMode mode) |
121 { | 109 { |
122 CSSParser parser(CSSStrictMode); | 110 CSSParser parser(CSSStrictMode); |
123 OwnPtr<MediaQuery> parsedQuery = parser.parseMediaQuery(queryString); | 111 OwnPtr<MediaQuery> parsedQuery = parser.parseMediaQuery(queryString); |
124 | 112 |
125 if (parsedQuery) | 113 if (parsedQuery) |
126 return parsedQuery.release(); | 114 return parsedQuery.release(); |
127 | 115 |
128 if (m_fallbackToDescriptor) { | 116 switch (mode) { |
117 case MediaQueryForwardCompatibleSyntaxMode: { | |
129 String medium = parseMediaDescriptor(queryString); | 118 String medium = parseMediaDescriptor(queryString); |
130 if (!medium.isNull()) | 119 if (!medium.isNull()) |
131 return adoptPtr(new MediaQuery(MediaQuery::None, medium, nullptr)); | 120 return adoptPtr(new MediaQuery(MediaQuery::None, medium, nullptr)); |
121 // Fall throught. | |
apavlov
2013/05/30 09:49:56
typo: should be "through"
| |
132 } | 122 } |
133 | 123 case MediaQueryNormalMode: |
134 return adoptPtr(new MediaQuery(MediaQuery::None, "not all", nullptr)); | 124 return adoptPtr(new MediaQuery(MediaQuery::None, "not all", nullptr)); |
125 case MediaQueryStrictMode: | |
126 break; | |
127 default: | |
128 ASSERT_NOT_REACHED(); | |
129 break; | |
130 } | |
131 return nullptr; | |
135 } | 132 } |
136 | 133 |
137 bool MediaQuerySet::parse(const String& mediaString) | 134 void MediaQuerySet::parseMediaQueryList(const String& mediaString, MediaQueryPar serMode mode, Vector<OwnPtr<MediaQuery> >& result) |
138 { | 135 { |
139 if (mediaString.isEmpty()) { | 136 if (mediaString.isEmpty()) { |
140 m_queries.clear(); | 137 result.clear(); |
141 return true; | 138 return; |
142 } | 139 } |
143 | 140 |
144 Vector<String> list; | 141 Vector<String> list; |
145 // FIXME: This is too simple as it shouldn't split when the ',' is inside | 142 // FIXME: This is too simple as it shouldn't split when the ',' is inside |
146 // other allowed matching pairs such as (), [], {}, "", and ''. | 143 // other allowed matching pairs such as (), [], {}, "", and ''. |
147 mediaString.split(',', /* allowEmptyEntries */ true, list); | 144 mediaString.split(',', /* allowEmptyEntries */ true, list); |
148 | 145 |
149 Vector<OwnPtr<MediaQuery> > result; | |
150 result.reserveInitialCapacity(list.size()); | 146 result.reserveInitialCapacity(list.size()); |
151 | 147 |
152 for (unsigned i = 0; i < list.size(); ++i) { | 148 for (unsigned i = 0; i < list.size(); ++i) { |
153 String queryString = list[i].stripWhiteSpace(); | 149 String queryString = list[i].stripWhiteSpace(); |
154 if (OwnPtr<MediaQuery> parsedQuery = parseMediaQuery(queryString)) | 150 OwnPtr<MediaQuery> parsedQuery = parseMediaQuery(queryString, mode); |
151 if (parsedQuery) | |
155 result.uncheckedAppend(parsedQuery.release()); | 152 result.uncheckedAppend(parsedQuery.release()); |
156 } | 153 } |
154 } | |
157 | 155 |
156 bool MediaQuerySet::set(const String& mediaString) | |
157 { | |
158 Vector<OwnPtr<MediaQuery> > result; | |
159 parseMediaQueryList(mediaString, parserMode(), result); | |
158 m_queries.swap(result); | 160 m_queries.swap(result); |
159 return true; | 161 return true; |
160 } | 162 } |
161 | 163 |
162 bool MediaQuerySet::add(const String& queryString) | 164 bool MediaQuerySet::add(const String& queryString) |
163 { | 165 { |
164 if (OwnPtr<MediaQuery> parsedQuery = parseMediaQuery(queryString)) { | 166 // To "parse a media query" for a given string means to follow "the parse |
165 m_queries.append(parsedQuery.release()); | 167 // a media query list" steps and return "null" if more than one media query |
168 // is returned, or else the returned media query. | |
169 Vector<OwnPtr<MediaQuery> > queries; | |
170 parseMediaQueryList(queryString, MediaQueryStrictMode, queries); | |
171 | |
172 // Only continue if exactly one media query is found, as described above. | |
173 if (queries.size() != 1) | |
166 return true; | 174 return true; |
175 | |
176 OwnPtr<MediaQuery> newQuery = queries[0].release(); | |
177 ASSERT(newQuery); | |
178 | |
179 // If comparing with any of the media queries in the collection of media | |
180 // queries returns true terminate these steps. | |
181 for (size_t i = 0; i < m_queries.size(); ++i) { | |
182 MediaQuery* query = m_queries[i].get(); | |
183 if (*query == *newQuery) | |
184 return true; | |
167 } | 185 } |
168 return false; | 186 |
187 m_queries.append(newQuery.release()); | |
188 return true; | |
169 } | 189 } |
170 | 190 |
171 bool MediaQuerySet::remove(const String& queryStringToRemove) | 191 bool MediaQuerySet::remove(const String& queryStringToRemove) |
172 { | 192 { |
173 OwnPtr<MediaQuery> parsedQuery = parseMediaQuery(queryStringToRemove); | 193 // To "parse a media query" for a given string means to follow "the parse |
174 if (!parsedQuery) | 194 // a media query list" steps and return "null" if more than one media query |
175 return false; | 195 // is returned, or else the returned media query. |
196 Vector<OwnPtr<MediaQuery> > queries; | |
197 parseMediaQueryList(queryStringToRemove, MediaQueryStrictMode, queries); | |
176 | 198 |
199 // Only continue if exactly one media query is found, as described above. | |
200 if (queries.size() != 1) | |
201 return true; | |
202 | |
203 OwnPtr<MediaQuery> newQuery = queries[0].release(); | |
204 ASSERT(newQuery); | |
205 | |
206 // Remove any media query from the collection of media queries for which | |
207 // comparing with the media query returns true. | |
208 bool found = false; | |
177 for (size_t i = 0; i < m_queries.size(); ++i) { | 209 for (size_t i = 0; i < m_queries.size(); ++i) { |
178 MediaQuery* query = m_queries[i].get(); | 210 MediaQuery* query = m_queries[i].get(); |
179 if (*query == *parsedQuery) { | 211 if (*query == *newQuery) { |
180 m_queries.remove(i); | 212 m_queries.remove(i); |
181 return true; | 213 --i; |
214 found = true; | |
182 } | 215 } |
183 } | 216 } |
184 return false; | 217 |
218 return found; | |
185 } | 219 } |
186 | 220 |
187 void MediaQuerySet::addMediaQuery(PassOwnPtr<MediaQuery> mediaQuery) | 221 void MediaQuerySet::addMediaQuery(PassOwnPtr<MediaQuery> mediaQuery) |
188 { | 222 { |
189 m_queries.append(mediaQuery); | 223 m_queries.append(mediaQuery); |
190 } | 224 } |
191 | 225 |
192 String MediaQuerySet::mediaText() const | 226 String MediaQuerySet::mediaText() const |
193 { | 227 { |
194 StringBuilder text; | 228 StringBuilder text; |
(...skipping 26 matching lines...) Expand all Loading... | |
221 : m_mediaQueries(mediaQueries) | 255 : m_mediaQueries(mediaQueries) |
222 , m_parentStyleSheet(0) | 256 , m_parentStyleSheet(0) |
223 , m_parentRule(parentRule) | 257 , m_parentRule(parentRule) |
224 { | 258 { |
225 } | 259 } |
226 | 260 |
227 MediaList::~MediaList() | 261 MediaList::~MediaList() |
228 { | 262 { |
229 } | 263 } |
230 | 264 |
231 void MediaList::setMediaText(const String& value, ExceptionCode& ec) | 265 void MediaList::setMediaText(const String& value) |
232 { | 266 { |
233 CSSStyleSheet::RuleMutationScope mutationScope(m_parentRule); | 267 CSSStyleSheet::RuleMutationScope mutationScope(m_parentRule); |
234 | 268 |
235 bool success = m_mediaQueries->parse(value); | 269 m_mediaQueries->set(value); |
236 if (!success) { | 270 |
237 ec = SYNTAX_ERR; | |
238 return; | |
239 } | |
240 if (m_parentStyleSheet) | 271 if (m_parentStyleSheet) |
241 m_parentStyleSheet->didMutate(); | 272 m_parentStyleSheet->didMutate(); |
242 } | 273 } |
243 | 274 |
244 String MediaList::item(unsigned index) const | 275 String MediaList::item(unsigned index) const |
245 { | 276 { |
246 const Vector<OwnPtr<MediaQuery> >& queries = m_mediaQueries->queryVector(); | 277 const Vector<OwnPtr<MediaQuery> >& queries = m_mediaQueries->queryVector(); |
247 if (index < queries.size()) | 278 if (index < queries.size()) |
248 return queries[index]->cssText(); | 279 return queries[index]->cssText(); |
249 return String(); | 280 return String(); |
(...skipping 11 matching lines...) Expand all Loading... | |
261 if (m_parentStyleSheet) | 292 if (m_parentStyleSheet) |
262 m_parentStyleSheet->didMutate(); | 293 m_parentStyleSheet->didMutate(); |
263 } | 294 } |
264 | 295 |
265 void MediaList::appendMedium(const String& medium, ExceptionCode& ec) | 296 void MediaList::appendMedium(const String& medium, ExceptionCode& ec) |
266 { | 297 { |
267 CSSStyleSheet::RuleMutationScope mutationScope(m_parentRule); | 298 CSSStyleSheet::RuleMutationScope mutationScope(m_parentRule); |
268 | 299 |
269 bool success = m_mediaQueries->add(medium); | 300 bool success = m_mediaQueries->add(medium); |
270 if (!success) { | 301 if (!success) { |
271 // FIXME: Should this really be INVALID_CHARACTER_ERR? | |
272 ec = INVALID_CHARACTER_ERR; | 302 ec = INVALID_CHARACTER_ERR; |
273 return; | 303 return; |
274 } | 304 } |
305 | |
275 if (m_parentStyleSheet) | 306 if (m_parentStyleSheet) |
276 m_parentStyleSheet->didMutate(); | 307 m_parentStyleSheet->didMutate(); |
277 } | 308 } |
278 | 309 |
279 void MediaList::reattach(MediaQuerySet* mediaQueries) | 310 void MediaList::reattach(MediaQuerySet* mediaQueries) |
280 { | 311 { |
281 ASSERT(mediaQueries); | 312 ASSERT(mediaQueries); |
282 m_mediaQueries = mediaQueries; | 313 m_mediaQueries = mediaQueries; |
283 } | 314 } |
284 | 315 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
346 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(cssV alue); | 377 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(cssV alue); |
347 if (primitiveValue->isDotsPerInch() || primitiveValue->isDot sPerCentimeter()) | 378 if (primitiveValue->isDotsPerInch() || primitiveValue->isDot sPerCentimeter()) |
348 addResolutionWarningMessageToConsole(document, mediaQuer ySet->mediaText(), primitiveValue); | 379 addResolutionWarningMessageToConsole(document, mediaQuer ySet->mediaText(), primitiveValue); |
349 } | 380 } |
350 } | 381 } |
351 } | 382 } |
352 } | 383 } |
353 } | 384 } |
354 | 385 |
355 } | 386 } |
OLD | NEW |