1 /**
2 * This file is part of the DOM implementation for KDE.
3 *
4 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5 * (C) 2004 Apple Computer, Inc.
6 * (C) 2008 Germain Garand <germain@ebooksfrance.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25 //#define CSS_STYLESHEET_DEBUG
26
27 #include "css_stylesheetimpl.h"
28 #include "css_ruleimpl.h"
29 #include "css_valueimpl.h"
30 #include "cssparser.h"
31 #include "css_mediaquery.h"
32
33 #include <dom/dom_string.h>
34 #include <dom/dom_exception.h>
35 #include <dom/css_stylesheet.h>
36 #include <dom/css_rule.h>
37 #include <dom/dom_exception.h>
38
39 #include <xml/dom_nodeimpl.h>
40 #include <html/html_documentimpl.h>
41 #include <misc/loader.h>
42
43 #include "khtml_debug.h"
44
45 using namespace DOM;
46 using namespace khtml;
47 // --------------------------------------------------------------------------------
48
StyleSheetImpl(StyleSheetImpl * parentSheet,DOMString href)49 StyleSheetImpl::StyleSheetImpl(StyleSheetImpl *parentSheet, DOMString href)
50 : StyleListImpl(parentSheet)
51 {
52 m_disabled = false;
53 m_media = nullptr;
54 m_parentNode = nullptr;
55 m_strHref = href;
56 }
57
StyleSheetImpl(DOM::NodeImpl * parentNode,DOMString href)58 StyleSheetImpl::StyleSheetImpl(DOM::NodeImpl *parentNode, DOMString href)
59 : StyleListImpl()
60 {
61 m_parentNode = parentNode;
62 m_disabled = false;
63 m_media = nullptr;
64 m_strHref = href;
65 }
66
StyleSheetImpl(StyleBaseImpl * owner,DOMString href)67 StyleSheetImpl::StyleSheetImpl(StyleBaseImpl *owner, DOMString href)
68 : StyleListImpl(owner)
69 {
70 m_disabled = false;
71 m_media = nullptr;
72 m_parentNode = nullptr;
73 m_strHref = href;
74 }
75
~StyleSheetImpl()76 StyleSheetImpl::~StyleSheetImpl()
77 {
78 if (m_media) {
79 m_media->setParent(nullptr);
80 m_media->deref();
81 }
82 }
83
parentStyleSheet() const84 StyleSheetImpl *StyleSheetImpl::parentStyleSheet() const
85 {
86 if (!m_parent) {
87 return nullptr;
88 }
89 if (m_parent->isStyleSheet()) {
90 return static_cast<StyleSheetImpl *>(m_parent);
91 }
92 if (m_parent->isRule()) {
93 return m_parent->stylesheet();
94 }
95 return nullptr;
96 }
97
setMedia(MediaListImpl * media)98 void StyleSheetImpl::setMedia(MediaListImpl *media)
99 {
100 if (media) {
101 media->ref();
102 }
103 if (m_media) {
104 m_media->setParent(nullptr);
105 m_media->deref();
106 }
107 m_media = media;
108 if (m_media) {
109 m_media->setParent(this);
110 }
111 }
112
setDisabled(bool disabled)113 void StyleSheetImpl::setDisabled(bool disabled)
114 {
115 bool updateStyle = isCSSStyleSheet() && m_parentNode && disabled != m_disabled;
116 m_disabled = disabled;
117 if (updateStyle) {
118 m_parentNode->document()->updateStyleSelector();
119 }
120 }
121
122 // -----------------------------------------------------------------------
123
CSSStyleSheetImpl(CSSStyleSheetImpl * parentSheet,DOMString href)124 CSSStyleSheetImpl::CSSStyleSheetImpl(CSSStyleSheetImpl *parentSheet, DOMString href)
125 : StyleSheetImpl(parentSheet, href)
126 {
127 m_lstChildren = new QList<StyleBaseImpl *>;
128 m_doc = parentSheet ? parentSheet->doc() : nullptr;
129 m_implicit = false;
130 m_namespaces = nullptr;
131 m_defaultNamespace = NamespaceName::fromId(anyNamespace);
132 m_loadedHint = false;
133 }
134
CSSStyleSheetImpl(DOM::NodeImpl * parentNode,DOMString href,bool _implicit)135 CSSStyleSheetImpl::CSSStyleSheetImpl(DOM::NodeImpl *parentNode, DOMString href, bool _implicit)
136 : StyleSheetImpl(parentNode, href)
137 {
138 m_lstChildren = new QList<StyleBaseImpl *>;
139 m_doc = parentNode->document();
140 m_implicit = _implicit;
141 m_namespaces = nullptr;
142 m_defaultNamespace = NamespaceName::fromId(anyNamespace);
143 m_loadedHint = false;
144 }
145
CSSStyleSheetImpl(CSSRuleImpl * ownerRule,DOMString href)146 CSSStyleSheetImpl::CSSStyleSheetImpl(CSSRuleImpl *ownerRule, DOMString href)
147 : StyleSheetImpl(ownerRule, href)
148 {
149 m_lstChildren = new QList<StyleBaseImpl *>;
150 m_doc = static_cast<CSSStyleSheetImpl *>(ownerRule->stylesheet())->doc();
151 m_implicit = false;
152 m_namespaces = nullptr;
153 m_defaultNamespace = NamespaceName::fromId(anyNamespace);
154 m_loadedHint = false;
155 }
156
CSSStyleSheetImpl(DOM::NodeImpl * parentNode,CSSStyleSheetImpl * orig)157 CSSStyleSheetImpl::CSSStyleSheetImpl(DOM::NodeImpl *parentNode, CSSStyleSheetImpl *orig)
158 : StyleSheetImpl(parentNode, orig->m_strHref)
159 {
160 m_lstChildren = new QList<StyleBaseImpl *>;
161 StyleBaseImpl *rule;
162 QListIterator<StyleBaseImpl *> it(*orig->m_lstChildren);
163 while (it.hasNext()) {
164 rule = it.next();
165 m_lstChildren->append(rule);
166 rule->setParent(this);
167 }
168 m_doc = parentNode->document();
169 m_implicit = false;
170 m_namespaces = nullptr;
171 m_defaultNamespace = NamespaceName::fromId(anyNamespace);
172 m_loadedHint = false;
173
174 recomputeNamespaceInfo(); // as we cloned kids
175 }
176
CSSStyleSheetImpl(CSSRuleImpl * ownerRule,CSSStyleSheetImpl * orig)177 CSSStyleSheetImpl::CSSStyleSheetImpl(CSSRuleImpl *ownerRule, CSSStyleSheetImpl *orig)
178 : StyleSheetImpl(ownerRule, orig->m_strHref)
179 {
180 // m_lstChildren is deleted in StyleListImpl
181 m_lstChildren = new QList<StyleBaseImpl *>;
182 StyleBaseImpl *rule;
183 QListIterator<StyleBaseImpl *> it(*orig->m_lstChildren);
184 while (it.hasNext()) {
185 rule = it.next();
186 m_lstChildren->append(rule);
187 rule->setParent(this);
188 }
189 m_doc = static_cast<CSSStyleSheetImpl *>(ownerRule->stylesheet())->doc();
190 m_implicit = false;
191 m_namespaces = nullptr;
192 m_defaultNamespace = NamespaceName::fromId(anyNamespace);
193 m_loadedHint = false;
194
195 recomputeNamespaceInfo(); // as we cloned kids
196 }
197
ownerRule() const198 CSSRuleImpl *CSSStyleSheetImpl::ownerRule() const
199 {
200 if (!m_parent) {
201 return nullptr;
202 }
203 if (m_parent->isRule()) {
204 return static_cast<CSSRuleImpl *>(m_parent);
205 }
206 return nullptr;
207 }
208
insertRule(const DOMString & rule,unsigned long index,int & exceptioncode)209 unsigned long CSSStyleSheetImpl::insertRule(const DOMString &rule, unsigned long index, int &exceptioncode)
210 {
211 exceptioncode = 0;
212 if (index > (unsigned) m_lstChildren->count()) {
213 exceptioncode = DOMException::INDEX_SIZE_ERR;
214 return 0;
215 }
216 CSSParser p(strictParsing);
217 CSSRuleImpl *r = p.parseRule(this, rule);
218
219 if (!r) {
220 exceptioncode = CSSException::SYNTAX_ERR + CSSException::_EXCEPTION_OFFSET;
221 return 0;
222 }
223 // ###
224 // HIERARCHY_REQUEST_ERR: Raised if the rule cannot be inserted at the specified index e.g. if an
225 //@import rule is inserted after a standard rule set or other at-rule.
226 m_lstChildren->insert(index, r);
227 if (m_doc) {
228 m_doc->updateStyleSelector(true /*shallow*/);
229 }
230
231 if (r->type() == DOM::CSSRule::NAMESPACE_RULE) {
232 dirtyNamespaceInfo();
233 if (static_cast<CSSNamespaceRuleImpl *>(r)->isDefault()) {
234 recomputeNamespaceInfo(); // default may have changed
235 }
236 // ### too late for some rules?
237 }
238
239 return index;
240 }
241
appendNamespaceRule(CSSNamespaceRuleImpl * ns)242 void CSSStyleSheetImpl::appendNamespaceRule(CSSNamespaceRuleImpl *ns)
243 {
244 append(ns);
245 dirtyNamespaceInfo();
246 if (ns->isDefault()) {
247 recomputeNamespaceInfo();
248 }
249 }
250
cssRules(bool omitCharsetRules)251 CSSRuleListImpl *CSSStyleSheetImpl::cssRules(bool omitCharsetRules)
252 {
253 return new CSSRuleListImpl(this, omitCharsetRules);
254 }
255
deleteRule(unsigned long index,int & exceptioncode)256 void CSSStyleSheetImpl::deleteRule(unsigned long index, int &exceptioncode)
257 {
258 exceptioncode = 0;
259 if (index + 1 > (unsigned) m_lstChildren->count()) {
260 exceptioncode = DOMException::INDEX_SIZE_ERR;
261 return;
262 }
263 StyleBaseImpl *b = m_lstChildren->takeAt(index);
264
265 if (b->isRule() && static_cast<CSSRuleImpl *>(b)->type() == DOM::CSSRule::NAMESPACE_RULE) {
266 dirtyNamespaceInfo();
267 if (static_cast<CSSNamespaceRuleImpl *>(b)->isDefault()) {
268 recomputeNamespaceInfo(); // default may have changed
269 }
270 // ### too late for some rules?
271 }
272
273 // TreeShared requires delete not deref when removed from tree
274 b->setParent(nullptr);
275 if (!b->refCount()) {
276 delete b;
277 }
278 if (m_doc) {
279 m_doc->updateStyleSelector(true /*shallow*/);
280 }
281 }
282
recomputeNamespaceInfo()283 void CSSStyleSheetImpl::recomputeNamespaceInfo()
284 {
285 assert(!m_namespaces);
286
287 m_namespaces = new QList<CSSNamespaceRuleImpl *>;
288 m_defaultNamespace = NamespaceName::fromId(anyNamespace);
289
290 // Compute list of all the @namespace nodes, as well as the default one.
291 for (int i = 0; i < m_lstChildren->count(); ++i) {
292 StyleBaseImpl *b = m_lstChildren->at(i);
293 if (b->isRule() && static_cast<CSSRuleImpl *>(b)->type() == DOM::CSSRule::NAMESPACE_RULE) {
294 CSSNamespaceRuleImpl *nr = static_cast<CSSNamespaceRuleImpl *>(b);
295 DOM::DOMString prefix = nr->prefix();
296 DOM::DOMString uri = nr->namespaceURI();
297
298 if (uri.isNull()) {
299 continue;
300 }
301
302 if (nr->isDefault()) {
303 m_defaultNamespace = NamespaceName::fromString(uri);
304 }
305
306 m_namespaces->append(nr);
307 }
308 }
309 }
310
determineNamespace(NamespaceName & namespacename,const DOM::DOMString & prefix)311 void CSSStyleSheetImpl::determineNamespace(NamespaceName &namespacename, const DOM::DOMString &prefix)
312 {
313 if (prefix.isEmpty()) {
314 namespacename = NamespaceName::fromId(emptyNamespace); // No namespace. If an element/attribute has a namespace, we won't match it.
315 } else if (prefix == "*") {
316 namespacename = NamespaceName::fromId(anyNamespace); // We'll match any namespace.
317 } else {
318 if (!m_namespaces) {
319 recomputeNamespaceInfo();
320 }
321
322 // To lookup the name, we go backwards, so the latest one wins
323 for (int p = m_namespaces->count() - 1; p >= 0; --p) {
324 CSSNamespaceRuleImpl *ns = m_namespaces->at(p);
325 if (ns->prefix() == prefix) {
326 namespacename = NamespaceName::fromString(ns->namespaceURI());
327 return;
328 }
329 }
330 }
331 }
332
parseString(const DOMString & string,bool strict)333 bool CSSStyleSheetImpl::parseString(const DOMString &string, bool strict)
334 {
335 #ifdef CSS_STYLESHEET_DEBUG
336 qCDebug(KHTML_LOG) << "parsing sheet, len=" << string.length() << ", sheet is " << string.string();
337 #endif
338
339 strictParsing = strict;
340 CSSParser p(strict);
341 p.parseSheet(this, string);
342 return true;
343 }
344
isLoading() const345 bool CSSStyleSheetImpl::isLoading() const
346 {
347 StyleBaseImpl *rule;
348 QListIterator<StyleBaseImpl *> it(*m_lstChildren);
349 while (it.hasNext()) {
350 rule = it.next();
351 if (rule->isImportRule()) {
352 CSSImportRuleImpl *import = static_cast<CSSImportRuleImpl *>(rule);
353 #ifdef CSS_STYLESHEET_DEBUG
354 qCDebug(KHTML_LOG) << "found import";
355 #endif
356 if (import->isLoading()) {
357 #ifdef CSS_STYLESHEET_DEBUG
358 qCDebug(KHTML_LOG) << "--> not loaded";
359 #endif
360 m_loadedHint = false;
361 return true;
362 }
363 }
364 }
365 m_loadedHint = true;
366 return false;
367 }
368
checkLoaded() const369 void CSSStyleSheetImpl::checkLoaded() const
370 {
371 if (isLoading()) {
372 return;
373 }
374 if (m_parent) {
375 m_parent->checkLoaded();
376 }
377 if (m_parentNode) {
378 m_loadedHint = m_parentNode->checkRemovePendingSheet();
379 } else if (parentStyleSheet() && parentStyleSheet()->isCSSStyleSheet()) {
380 m_loadedHint = static_cast<CSSStyleSheetImpl *>(parentStyleSheet())->loadedHint();
381 } else {
382 m_loadedHint = true;
383 }
384 }
385
checkPending() const386 void CSSStyleSheetImpl::checkPending() const
387 {
388 if (!m_loadedHint) {
389 return;
390 }
391 if (m_parent) {
392 m_parent->checkPending();
393 } else if (m_parentNode) {
394 m_parentNode->checkAddPendingSheet();
395 }
396 }
397
398 // ---------------------------------------------------------------------------
399
~StyleSheetListImpl()400 StyleSheetListImpl::~StyleSheetListImpl()
401 {
402 foreach (StyleSheetImpl *sh, styleSheets) {
403 sh->deref();
404 }
405 }
406
add(StyleSheetImpl * s)407 void StyleSheetListImpl::add(StyleSheetImpl *s)
408 {
409 if (managerDocument) {
410 managerDocument->ensureStyleSheetListUpToDate();
411 }
412
413 // ### in cases this is document.styleSheets, maybe
414 // we should route to DocumentImpl::addStyleSheets?
415
416 if (!styleSheets.contains(s)) {
417 s->ref();
418 styleSheets.append(s);
419 }
420 }
421
remove(StyleSheetImpl * s)422 void StyleSheetListImpl::remove(StyleSheetImpl *s)
423 {
424 if (managerDocument) {
425 managerDocument->ensureStyleSheetListUpToDate();
426 }
427
428 if (styleSheets.removeAll(s)) {
429 s->deref();
430 }
431 }
432
length() const433 unsigned long StyleSheetListImpl::length() const
434 {
435 if (managerDocument) {
436 managerDocument->ensureStyleSheetListUpToDate();
437 }
438
439 // hack so implicit BODY stylesheets don't get counted here
440 unsigned long l = 0;
441 foreach (StyleSheetImpl *sh, styleSheets) {
442 if (!sh->isCSSStyleSheet() || !static_cast<CSSStyleSheetImpl *>(sh)->implicit()) {
443 ++l;
444 }
445 }
446 return l;
447 }
448
item(unsigned long index)449 StyleSheetImpl *StyleSheetListImpl::item(unsigned long index)
450 {
451 if (managerDocument) {
452 managerDocument->ensureStyleSheetListUpToDate();
453 }
454
455 unsigned long l = 0;
456 foreach (StyleSheetImpl *sh, styleSheets) {
457 if (!sh->isCSSStyleSheet() || !static_cast<CSSStyleSheetImpl *>(sh)->implicit()) {
458 if (l == index) {
459 return sh;
460 }
461 ++l;
462 }
463 }
464 return nullptr;
465 }
466
467 // --------------------------------------------------------------------------------------------
468
469 /* MediaList is used to store 3 types of media related entities which mean the same:
470 * Media Queries, Media Types and Media Descriptors.
471 * Currently MediaList always tries to parse media queries and if parsing fails,
472 * tries to fallback to Media Descriptors if m_fallback flag is set.
473 * Slight problem with syntax error handling:
474 * CSS 2.1 Spec (https://www.w3.org/TR/CSS21/media.html)
475 * specifies that failing media type parsing is a syntax error
476 * CSS 3 Media Queries Spec (https://www.w3.org/TR/css3-mediaqueries/)
477 * specifies that failing media query is a syntax error
478 * HTML 4.01 spec (https://www.w3.org/TR/REC-html40/present/styles.html#adef-media)
479 * specifies that Media Descriptors should be parsed with forward-compatible syntax
480 * DOM Level 2 Style Sheet spec (https://www.w3.org/TR/DOM-Level-2-Style/)
481 * talks about MediaList.mediaText and refers
482 * - to Media Descriptors of HTML 4.0 in context of StyleSheet
483 * - to Media Types of CSS 2.0 in context of CSSMediaRule and CSSImportRule
484 *
485 * These facts create situation where same (illegal) media specification may result in
486 * different parses depending on whether it is media attr of style element or part of
487 * css @media rule.
488 * <style media="screen and resolution > 40dpi"> ..</style> will be enabled on screen devices where as
489 * @media screen and resolution > 40dpi {..} will not.
490 * This gets more counter-intuitive in JavaScript:
491 * document.styleSheets[0].media.mediaText = "screen and resolution > 40dpi" will be ok and
492 * enabled, while
493 * document.styleSheets[0].cssRules[0].media.mediaText = "screen and resolution > 40dpi" will
494 * throw SYNTAX_ERR exception.
495 */
496
MediaListImpl(CSSStyleSheetImpl * parentSheet,const DOMString & media,bool fallbackToDescriptor)497 MediaListImpl::MediaListImpl(CSSStyleSheetImpl *parentSheet,
498 const DOMString &media, bool fallbackToDescriptor)
499 : StyleBaseImpl(parentSheet)
500 , m_fallback(fallbackToDescriptor)
501 {
502 int ec = 0;
503 setMediaText(media, ec);
504 // FIXME: parsing can fail. The problem with failing constructor is that
505 // we would need additional flag saying MediaList is not valid
506 // Parse can fail only when fallbackToDescriptor == false, i.e when HTML4 media descriptor
507 // forward-compatible syntax is not in use.
508 // DOMImplementationCSS seems to mandate that media descriptors are used
509 // for both html and svg, even though svg:style doesn't use media descriptors
510 // Currently the only places where parsing can fail are
511 // creating <svg:style>, creating css media / import rules from js
512 if (ec) {
513 setMediaText("invalid", ec);
514 }
515 }
516
MediaListImpl(CSSRuleImpl * parentRule,const DOMString & media,bool fallbackToDescriptor)517 MediaListImpl::MediaListImpl(CSSRuleImpl *parentRule, const DOMString &media, bool fallbackToDescriptor)
518 : StyleBaseImpl(parentRule)
519 , m_fallback(fallbackToDescriptor)
520 {
521 int ec = 0;
522 setMediaText(media, ec);
523 // FIXME: parsing can fail. The problem with failing constructor is that
524 // we would need additional flag saying MediaList is not valid
525 // Parse can fail only when fallbackToDescriptor == false, i.e when HTML4 media descriptor
526 // forward-compatible syntax is not in use.
527 // DOMImplementationCSS seems to mandate that media descriptors are used
528 // for both html and svg, even though svg:style doesn't use media descriptors
529 // Currently the only places where parsing can fail are
530 // creating <svg:style>, creating css media / import rules from js
531 if (ec) {
532 setMediaText("invalid", ec);
533 }
534 }
535
~MediaListImpl()536 MediaListImpl::~MediaListImpl()
537 {
538 qDeleteAll(m_queries);
539 }
540
parentStyleSheet() const541 CSSStyleSheetImpl *MediaListImpl::parentStyleSheet() const
542 {
543 if (m_parent->isCSSStyleSheet()) {
544 return static_cast<CSSStyleSheetImpl *>(m_parent);
545 }
546 return nullptr;
547 }
548
parentRule() const549 CSSRuleImpl *MediaListImpl::parentRule() const
550 {
551 if (m_parent->isRule()) {
552 return static_cast<CSSRuleImpl *>(m_parent);
553 }
554 return nullptr;
555 }
556
parseMediaDescriptor(const DOMString & s)557 static DOMString parseMediaDescriptor(const DOMString &s)
558 {
559 int len = s.length();
560
561 // https://www.w3.org/TR/REC-html40/types.html#type-media-descriptors
562 // "Each entry is truncated just before the first character that isn't a
563 // US ASCII letter [a-zA-Z] (ISO 10646 hex 41-5a, 61-7a), digit [0-9] (hex 30-39),
564 // or hyphen (hex 2d)."
565 int i;
566 unsigned short c;
567 for (i = 0; i < len; ++i) {
568 c = s[i].unicode();
569 if (!((c >= 'a' && c <= 'z')
570 || (c >= 'A' && c <= 'Z')
571 || (c >= '1' && c <= '9')
572 || (c == '-'))) {
573 break;
574 }
575 }
576 return s.implementation()->substring(0, i);
577 }
578
deleteMedium(const DOMString & oldMedium,int & ec)579 void MediaListImpl::deleteMedium(const DOMString &oldMedium, int &ec)
580 {
581 MediaListImpl tempMediaList;
582 CSSParser p(true);
583
584 MediaQuery *oldQuery = nullptr;
585 bool deleteOldQuery = false;
586
587 if (p.parseMediaQuery(&tempMediaList, oldMedium)) {
588 if (tempMediaList.m_queries.size() > 0) {
589 oldQuery = tempMediaList.m_queries[0];
590 }
591 } else if (m_fallback) {
592 DOMString medium = parseMediaDescriptor(oldMedium);
593 if (!medium.isNull()) {
594 oldQuery = new MediaQuery(MediaQuery::None, medium, nullptr);
595 deleteOldQuery = true;
596 }
597 }
598
599 // DOM Style Sheets spec doesn't allow SYNTAX_ERR to be thrown in deleteMedium
600 ec = DOMException::NOT_FOUND_ERR;
601
602 if (oldQuery) {
603 for (int i = 0; i < m_queries.size(); ++i) {
604 MediaQuery *a = m_queries[i];
605 if (*a == *oldQuery) {
606 m_queries.removeAt(i);
607 delete a;
608 ec = 0;
609 break;
610 }
611 }
612 if (deleteOldQuery) {
613 delete oldQuery;
614 }
615 }
616 }
617
mediaText() const618 DOM::DOMString MediaListImpl::mediaText() const
619 {
620 DOMString text;
621 bool first = true;
622 const QList<MediaQuery *>::ConstIterator itEnd = m_queries.end();
623
624 for (QList<MediaQuery *>::ConstIterator it = m_queries.begin(); it != itEnd; ++it) {
625 if (!first) {
626 text += ", ";
627 }
628 text += (*it)->cssText();
629 first = false;
630 }
631 return text;
632 }
633
setMediaText(const DOM::DOMString & value,int & ec)634 void MediaListImpl::setMediaText(const DOM::DOMString &value, int &ec)
635 {
636 MediaListImpl tempMediaList;
637 CSSParser p(true);
638
639 const QString val = value.string();
640 const QStringList list = val.split(',');
641
642 const QStringList::ConstIterator itEnd = list.end();
643
644 for (QStringList::ConstIterator it = list.begin(); it != itEnd; ++it) {
645 const DOMString medium = (*it).trimmed();
646 if (!medium.isEmpty()) {
647 if (!p.parseMediaQuery(&tempMediaList, medium)) {
648 if (m_fallback) {
649 DOMString mediaDescriptor = parseMediaDescriptor(medium);
650 if (!mediaDescriptor.isNull()) {
651 tempMediaList.m_queries.append(new MediaQuery(MediaQuery::None, mediaDescriptor, nullptr));
652 }
653 } else {
654 ec = CSSException::SYNTAX_ERR;
655 return;
656 }
657 }
658 } else if (!m_fallback) {
659 ec = CSSException::SYNTAX_ERR;
660 return;
661 }
662 }
663 // ",,,," falls straight through, but is not valid unless fallback
664 if (!m_fallback && list.begin() == list.end()) {
665 DOMString s = value.string().trimmed();
666 if (!s.isEmpty()) {
667 ec = CSSException::SYNTAX_ERR;
668 return;
669 }
670 }
671
672 ec = 0;
673 qDeleteAll(m_queries);
674 m_queries = tempMediaList.m_queries;
675 tempMediaList.m_queries.clear();
676 }
677
item(unsigned long index) const678 DOMString MediaListImpl::item(unsigned long index) const
679 {
680 if (index < (unsigned)m_queries.size()) {
681 MediaQuery *query = m_queries[index];
682 return query->cssText();
683 }
684
685 return DOMString();
686 }
687
appendMedium(const DOMString & newMedium,int & ec)688 void MediaListImpl::appendMedium(const DOMString &newMedium, int &ec)
689 {
690 ec = DOMException::INVALID_CHARACTER_ERR;
691 CSSParser p(true);
692 if (p.parseMediaQuery(this, newMedium)) {
693 ec = 0;
694 } else if (m_fallback) {
695 DOMString medium = parseMediaDescriptor(newMedium);
696 if (!medium.isNull()) {
697 m_queries.append(new MediaQuery(MediaQuery::None, medium, nullptr));
698 ec = 0;
699 }
700 }
701 }
702
appendMediaQuery(MediaQuery * mediaQuery)703 void MediaListImpl::appendMediaQuery(MediaQuery *mediaQuery)
704 {
705 m_queries.append(mediaQuery);
706 }
707
708