| 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 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 |