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)); |
apavlov
2013/05/29 15:44:36
Should you add something like
// Fall through.
aft
kenneth.r.christiansen
2013/05/29 15:52:56
I actually had that :-) Let me readd that
| |
132 } | 121 } |
133 | 122 case MediaQueryNormalMode: |
134 return adoptPtr(new MediaQuery(MediaQuery::None, "not all", nullptr)); | 123 return adoptPtr(new MediaQuery(MediaQuery::None, "not all", nullptr)); |
124 case MediaQueryStrictMode: | |
125 default: | |
apavlov
2013/05/29 15:44:36
Should we guard ourselves against the default-case
kenneth.r.christiansen
2013/05/29 15:52:56
Fine with me
| |
126 return nullptr; | |
127 } | |
135 } | 128 } |
136 | 129 |
137 bool MediaQuerySet::parse(const String& mediaString) | 130 void MediaQuerySet::parseMediaQueryList(const String& mediaString, MediaQueryPar serMode mode, Vector<OwnPtr<MediaQuery> >& result) |
138 { | 131 { |
139 if (mediaString.isEmpty()) { | 132 if (mediaString.isEmpty()) { |
140 m_queries.clear(); | 133 result.clear(); |
141 return true; | 134 return; |
142 } | 135 } |
143 | 136 |
144 Vector<String> list; | 137 Vector<String> list; |
145 // FIXME: This is too simple as it shouldn't split when the ',' is inside | 138 // FIXME: This is too simple as it shouldn't split when the ',' is inside |
146 // other allowed matching pairs such as (), [], {}, "", and ''. | 139 // other allowed matching pairs such as (), [], {}, "", and ''. |
147 mediaString.split(',', /* allowEmptyEntries */ true, list); | 140 mediaString.split(',', /* allowEmptyEntries */ true, list); |
148 | 141 |
149 Vector<OwnPtr<MediaQuery> > result; | |
150 result.reserveInitialCapacity(list.size()); | 142 result.reserveInitialCapacity(list.size()); |
151 | 143 |
152 for (unsigned i = 0; i < list.size(); ++i) { | 144 for (unsigned i = 0; i < list.size(); ++i) { |
153 String queryString = list[i].stripWhiteSpace(); | 145 String queryString = list[i].stripWhiteSpace(); |
154 if (OwnPtr<MediaQuery> parsedQuery = parseMediaQuery(queryString)) | 146 OwnPtr<MediaQuery> parsedQuery = parseMediaQuery(queryString, mode); |
147 if (parsedQuery) | |
155 result.uncheckedAppend(parsedQuery.release()); | 148 result.uncheckedAppend(parsedQuery.release()); |
156 } | 149 } |
150 } | |
157 | 151 |
152 bool MediaQuerySet::set(const String& mediaString) | |
153 { | |
154 Vector<OwnPtr<MediaQuery> > result; | |
155 parseMediaQueryList(mediaString, m_parserMode, result); | |
158 m_queries.swap(result); | 156 m_queries.swap(result); |
159 return true; | 157 return true; |
160 } | 158 } |
161 | 159 |
162 bool MediaQuerySet::add(const String& queryString) | 160 bool MediaQuerySet::add(const String& queryString) |
163 { | 161 { |
164 if (OwnPtr<MediaQuery> parsedQuery = parseMediaQuery(queryString)) { | 162 // To parse a media query for a given string means to follow the parse |
165 m_queries.append(parsedQuery.release()); | 163 // a media query list steps and return null if more than one media query |
apavlov
2013/05/29 15:44:36
Should the "parse a media query list" be quoted so
kenneth.r.christiansen
2013/05/29 15:52:56
Just quotes around that? That is fine with me.
| |
164 // is returned or a media query if a single media query is returned. | |
165 Vector<OwnPtr<MediaQuery> > queries; | |
166 parseMediaQueryList(queryString, MediaQueryStrictMode, queries); | |
167 | |
168 // If "null", terminate these steps. | |
apavlov
2013/05/29 15:44:36
Can it be more than 1, as opposed to 0?
kenneth.r.christiansen
2013/05/29 15:52:56
No, it cannot according to the spec. It has to be
| |
169 if (queries.size() != 1) | |
166 return true; | 170 return true; |
171 | |
172 OwnPtr<MediaQuery> newQuery = queries[0].release(); | |
173 ASSERT(newQuery); | |
174 | |
175 // If comparing with any of the media queries in the collection of media | |
176 // queries returns true terminate these steps. | |
177 for (size_t i = 0; i < m_queries.size(); ++i) { | |
178 MediaQuery* query = m_queries[i].get(); | |
179 if (*query == *newQuery) | |
180 return true; | |
167 } | 181 } |
168 return false; | 182 |
183 m_queries.append(newQuery.release()); | |
184 return true; | |
169 } | 185 } |
170 | 186 |
171 bool MediaQuerySet::remove(const String& queryStringToRemove) | 187 bool MediaQuerySet::remove(const String& queryStringToRemove) |
172 { | 188 { |
173 OwnPtr<MediaQuery> parsedQuery = parseMediaQuery(queryStringToRemove); | 189 // To parse a media query for a given string means to follow the parse |
apavlov
2013/05/29 15:44:36
Ditto for quoting
| |
174 if (!parsedQuery) | 190 // a media query list steps and return null if more than one media query |
175 return false; | 191 // is returned or a media query if a single media query is returned. |
192 Vector<OwnPtr<MediaQuery> > queries; | |
193 parseMediaQueryList(queryStringToRemove, MediaQueryStrictMode, queries); | |
176 | 194 |
195 // If "null", terminate these steps. | |
apavlov
2013/05/29 15:44:36
Ditto for the comment
kenneth.r.christiansen
2013/05/29 15:52:56
Suggestions for the comments?
| |
196 if (queries.size() != 1) | |
197 return true; | |
198 | |
199 OwnPtr<MediaQuery> newQuery = queries[0].release(); | |
200 ASSERT(newQuery); | |
201 | |
202 // Remove any media query from the collection of media queries for which | |
203 // comparing the media query with m returns true. | |
apavlov
2013/05/29 15:44:36
What is "m"?
kenneth.r.christiansen
2013/05/29 15:52:56
Copy paste from the algorithm, forgot to remove th
| |
204 bool found = false; | |
177 for (size_t i = 0; i < m_queries.size(); ++i) { | 205 for (size_t i = 0; i < m_queries.size(); ++i) { |
178 MediaQuery* query = m_queries[i].get(); | 206 MediaQuery* query = m_queries[i].get(); |
179 if (*query == *parsedQuery) { | 207 if (*query == *newQuery) { |
180 m_queries.remove(i); | 208 m_queries.remove(i); |
181 return true; | 209 --i; |
210 found = true; | |
182 } | 211 } |
183 } | 212 } |
184 return false; | 213 |
214 return found; | |
185 } | 215 } |
186 | 216 |
187 void MediaQuerySet::addMediaQuery(PassOwnPtr<MediaQuery> mediaQuery) | 217 void MediaQuerySet::addMediaQuery(PassOwnPtr<MediaQuery> mediaQuery) |
188 { | 218 { |
189 m_queries.append(mediaQuery); | 219 m_queries.append(mediaQuery); |
190 } | 220 } |
191 | 221 |
192 String MediaQuerySet::mediaText() const | 222 String MediaQuerySet::mediaText() const |
193 { | 223 { |
194 StringBuilder text; | 224 StringBuilder text; |
(...skipping 26 matching lines...) Expand all Loading... | |
221 : m_mediaQueries(mediaQueries) | 251 : m_mediaQueries(mediaQueries) |
222 , m_parentStyleSheet(0) | 252 , m_parentStyleSheet(0) |
223 , m_parentRule(parentRule) | 253 , m_parentRule(parentRule) |
224 { | 254 { |
225 } | 255 } |
226 | 256 |
227 MediaList::~MediaList() | 257 MediaList::~MediaList() |
228 { | 258 { |
229 } | 259 } |
230 | 260 |
231 void MediaList::setMediaText(const String& value, ExceptionCode& ec) | 261 void MediaList::setMediaText(const String& value) |
232 { | 262 { |
233 CSSStyleSheet::RuleMutationScope mutationScope(m_parentRule); | 263 CSSStyleSheet::RuleMutationScope mutationScope(m_parentRule); |
234 | 264 |
235 bool success = m_mediaQueries->parse(value); | 265 m_mediaQueries->set(value); |
236 if (!success) { | 266 |
237 ec = SYNTAX_ERR; | |
238 return; | |
239 } | |
240 if (m_parentStyleSheet) | 267 if (m_parentStyleSheet) |
241 m_parentStyleSheet->didMutate(); | 268 m_parentStyleSheet->didMutate(); |
242 } | 269 } |
243 | 270 |
244 String MediaList::item(unsigned index) const | 271 String MediaList::item(unsigned index) const |
245 { | 272 { |
246 const Vector<OwnPtr<MediaQuery> >& queries = m_mediaQueries->queryVector(); | 273 const Vector<OwnPtr<MediaQuery> >& queries = m_mediaQueries->queryVector(); |
247 if (index < queries.size()) | 274 if (index < queries.size()) |
248 return queries[index]->cssText(); | 275 return queries[index]->cssText(); |
249 return String(); | 276 return String(); |
(...skipping 11 matching lines...) Expand all Loading... | |
261 if (m_parentStyleSheet) | 288 if (m_parentStyleSheet) |
262 m_parentStyleSheet->didMutate(); | 289 m_parentStyleSheet->didMutate(); |
263 } | 290 } |
264 | 291 |
265 void MediaList::appendMedium(const String& medium, ExceptionCode& ec) | 292 void MediaList::appendMedium(const String& medium, ExceptionCode& ec) |
266 { | 293 { |
267 CSSStyleSheet::RuleMutationScope mutationScope(m_parentRule); | 294 CSSStyleSheet::RuleMutationScope mutationScope(m_parentRule); |
268 | 295 |
269 bool success = m_mediaQueries->add(medium); | 296 bool success = m_mediaQueries->add(medium); |
270 if (!success) { | 297 if (!success) { |
271 // FIXME: Should this really be INVALID_CHARACTER_ERR? | |
272 ec = INVALID_CHARACTER_ERR; | 298 ec = INVALID_CHARACTER_ERR; |
273 return; | 299 return; |
274 } | 300 } |
301 | |
275 if (m_parentStyleSheet) | 302 if (m_parentStyleSheet) |
276 m_parentStyleSheet->didMutate(); | 303 m_parentStyleSheet->didMutate(); |
277 } | 304 } |
278 | 305 |
279 void MediaList::reattach(MediaQuerySet* mediaQueries) | 306 void MediaList::reattach(MediaQuerySet* mediaQueries) |
280 { | 307 { |
281 ASSERT(mediaQueries); | 308 ASSERT(mediaQueries); |
282 m_mediaQueries = mediaQueries; | 309 m_mediaQueries = mediaQueries; |
283 } | 310 } |
284 | 311 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
346 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(cssV alue); | 373 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(cssV alue); |
347 if (primitiveValue->isDotsPerInch() || primitiveValue->isDot sPerCentimeter()) | 374 if (primitiveValue->isDotsPerInch() || primitiveValue->isDot sPerCentimeter()) |
348 addResolutionWarningMessageToConsole(document, mediaQuer ySet->mediaText(), primitiveValue); | 375 addResolutionWarningMessageToConsole(document, mediaQuer ySet->mediaText(), primitiveValue); |
349 } | 376 } |
350 } | 377 } |
351 } | 378 } |
352 } | 379 } |
353 } | 380 } |
354 | 381 |
355 } | 382 } |
OLD | NEW |