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