File indexing completed on 2024-04-28 15:22:41

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
0005  *           (C) 2004 Apple Computer, Inc.
0006  *           (C) 2008 Germain Garand <germain@ebooksfrance.org>
0007  *
0008  * This library is free software; you can redistribute it and/or
0009  * modify it under the terms of the GNU Library General Public
0010  * License as published by the Free Software Foundation; either
0011  * version 2 of the License, or (at your option) any later version.
0012  *
0013  * This library is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016  * Library General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU Library General Public License
0019  * along with this library; see the file COPYING.LIB.  If not, write to
0020  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021  * Boston, MA 02110-1301, USA.
0022  *
0023  */
0024 
0025 //#define CSS_STYLESHEET_DEBUG
0026 
0027 #include "css_stylesheetimpl.h"
0028 #include "css_ruleimpl.h"
0029 #include "css_valueimpl.h"
0030 #include "cssparser.h"
0031 #include "css_mediaquery.h"
0032 
0033 #include <dom/dom_string.h>
0034 #include <dom/dom_exception.h>
0035 #include <dom/css_stylesheet.h>
0036 #include <dom/css_rule.h>
0037 #include <dom/dom_exception.h>
0038 
0039 #include <xml/dom_nodeimpl.h>
0040 #include <html/html_documentimpl.h>
0041 #include <misc/loader.h>
0042 
0043 #include "khtml_debug.h"
0044 
0045 using namespace DOM;
0046 using namespace khtml;
0047 // --------------------------------------------------------------------------------
0048 
0049 StyleSheetImpl::StyleSheetImpl(StyleSheetImpl *parentSheet, DOMString href)
0050     : StyleListImpl(parentSheet)
0051 {
0052     m_disabled = false;
0053     m_media = nullptr;
0054     m_parentNode = nullptr;
0055     m_strHref = href;
0056 }
0057 
0058 StyleSheetImpl::StyleSheetImpl(DOM::NodeImpl *parentNode, DOMString href)
0059     : StyleListImpl()
0060 {
0061     m_parentNode = parentNode;
0062     m_disabled = false;
0063     m_media = nullptr;
0064     m_strHref = href;
0065 }
0066 
0067 StyleSheetImpl::StyleSheetImpl(StyleBaseImpl *owner, DOMString href)
0068     : StyleListImpl(owner)
0069 {
0070     m_disabled = false;
0071     m_media = nullptr;
0072     m_parentNode = nullptr;
0073     m_strHref = href;
0074 }
0075 
0076 StyleSheetImpl::~StyleSheetImpl()
0077 {
0078     if (m_media) {
0079         m_media->setParent(nullptr);
0080         m_media->deref();
0081     }
0082 }
0083 
0084 StyleSheetImpl *StyleSheetImpl::parentStyleSheet() const
0085 {
0086     if (!m_parent) {
0087         return nullptr;
0088     }
0089     if (m_parent->isStyleSheet()) {
0090         return static_cast<StyleSheetImpl *>(m_parent);
0091     }
0092     if (m_parent->isRule()) {
0093         return m_parent->stylesheet();
0094     }
0095     return nullptr;
0096 }
0097 
0098 void StyleSheetImpl::setMedia(MediaListImpl *media)
0099 {
0100     if (media) {
0101         media->ref();
0102     }
0103     if (m_media) {
0104         m_media->setParent(nullptr);
0105         m_media->deref();
0106     }
0107     m_media = media;
0108     if (m_media) {
0109         m_media->setParent(this);
0110     }
0111 }
0112 
0113 void StyleSheetImpl::setDisabled(bool disabled)
0114 {
0115     bool updateStyle = isCSSStyleSheet() && m_parentNode && disabled != m_disabled;
0116     m_disabled = disabled;
0117     if (updateStyle) {
0118         m_parentNode->document()->updateStyleSelector();
0119     }
0120 }
0121 
0122 // -----------------------------------------------------------------------
0123 
0124 CSSStyleSheetImpl::CSSStyleSheetImpl(CSSStyleSheetImpl *parentSheet, DOMString href)
0125     : StyleSheetImpl(parentSheet, href)
0126 {
0127     m_lstChildren = new QList<StyleBaseImpl *>;
0128     m_doc = parentSheet ? parentSheet->doc() : nullptr;
0129     m_implicit = false;
0130     m_namespaces = nullptr;
0131     m_defaultNamespace = NamespaceName::fromId(anyNamespace);
0132     m_loadedHint = false;
0133 }
0134 
0135 CSSStyleSheetImpl::CSSStyleSheetImpl(DOM::NodeImpl *parentNode, DOMString href, bool _implicit)
0136     : StyleSheetImpl(parentNode, href)
0137 {
0138     m_lstChildren = new QList<StyleBaseImpl *>;
0139     m_doc = parentNode->document();
0140     m_implicit = _implicit;
0141     m_namespaces = nullptr;
0142     m_defaultNamespace = NamespaceName::fromId(anyNamespace);
0143     m_loadedHint = false;
0144 }
0145 
0146 CSSStyleSheetImpl::CSSStyleSheetImpl(CSSRuleImpl *ownerRule, DOMString href)
0147     : StyleSheetImpl(ownerRule, href)
0148 {
0149     m_lstChildren = new QList<StyleBaseImpl *>;
0150     m_doc = static_cast<CSSStyleSheetImpl *>(ownerRule->stylesheet())->doc();
0151     m_implicit = false;
0152     m_namespaces = nullptr;
0153     m_defaultNamespace = NamespaceName::fromId(anyNamespace);
0154     m_loadedHint = false;
0155 }
0156 
0157 CSSStyleSheetImpl::CSSStyleSheetImpl(DOM::NodeImpl *parentNode, CSSStyleSheetImpl *orig)
0158     : StyleSheetImpl(parentNode, orig->m_strHref)
0159 {
0160     m_lstChildren = new QList<StyleBaseImpl *>;
0161     StyleBaseImpl *rule;
0162     QListIterator<StyleBaseImpl *> it(*orig->m_lstChildren);
0163     while (it.hasNext()) {
0164         rule = it.next();
0165         m_lstChildren->append(rule);
0166         rule->setParent(this);
0167     }
0168     m_doc = parentNode->document();
0169     m_implicit = false;
0170     m_namespaces = nullptr;
0171     m_defaultNamespace = NamespaceName::fromId(anyNamespace);
0172     m_loadedHint = false;
0173 
0174     recomputeNamespaceInfo(); // as we cloned kids
0175 }
0176 
0177 CSSStyleSheetImpl::CSSStyleSheetImpl(CSSRuleImpl *ownerRule, CSSStyleSheetImpl *orig)
0178     : StyleSheetImpl(ownerRule, orig->m_strHref)
0179 {
0180     // m_lstChildren is deleted in StyleListImpl
0181     m_lstChildren = new QList<StyleBaseImpl *>;
0182     StyleBaseImpl *rule;
0183     QListIterator<StyleBaseImpl *> it(*orig->m_lstChildren);
0184     while (it.hasNext()) {
0185         rule = it.next();
0186         m_lstChildren->append(rule);
0187         rule->setParent(this);
0188     }
0189     m_doc = static_cast<CSSStyleSheetImpl *>(ownerRule->stylesheet())->doc();
0190     m_implicit = false;
0191     m_namespaces = nullptr;
0192     m_defaultNamespace = NamespaceName::fromId(anyNamespace);
0193     m_loadedHint = false;
0194 
0195     recomputeNamespaceInfo(); // as we cloned kids
0196 }
0197 
0198 CSSRuleImpl *CSSStyleSheetImpl::ownerRule() const
0199 {
0200     if (!m_parent) {
0201         return nullptr;
0202     }
0203     if (m_parent->isRule()) {
0204         return static_cast<CSSRuleImpl *>(m_parent);
0205     }
0206     return nullptr;
0207 }
0208 
0209 unsigned long CSSStyleSheetImpl::insertRule(const DOMString &rule, unsigned long index, int &exceptioncode)
0210 {
0211     exceptioncode = 0;
0212     if (index > (unsigned) m_lstChildren->count()) {
0213         exceptioncode = DOMException::INDEX_SIZE_ERR;
0214         return 0;
0215     }
0216     CSSParser p(strictParsing);
0217     CSSRuleImpl *r = p.parseRule(this, rule);
0218 
0219     if (!r) {
0220         exceptioncode = CSSException::SYNTAX_ERR + CSSException::_EXCEPTION_OFFSET;
0221         return 0;
0222     }
0223     // ###
0224     // HIERARCHY_REQUEST_ERR: Raised if the rule cannot be inserted at the specified index e.g. if an
0225     //@import rule is inserted after a standard rule set or other at-rule.
0226     m_lstChildren->insert(index, r);
0227     if (m_doc) {
0228         m_doc->updateStyleSelector(true /*shallow*/);
0229     }
0230 
0231     if (r->type() == DOM::CSSRule::NAMESPACE_RULE) {
0232         dirtyNamespaceInfo();
0233         if (static_cast<CSSNamespaceRuleImpl *>(r)->isDefault()) {
0234             recomputeNamespaceInfo();    // default may have changed
0235         }
0236         // ### too late for some rules?
0237     }
0238 
0239     return index;
0240 }
0241 
0242 void CSSStyleSheetImpl::appendNamespaceRule(CSSNamespaceRuleImpl *ns)
0243 {
0244     append(ns);
0245     dirtyNamespaceInfo();
0246     if (ns->isDefault()) {
0247         recomputeNamespaceInfo();
0248     }
0249 }
0250 
0251 CSSRuleListImpl *CSSStyleSheetImpl::cssRules(bool omitCharsetRules)
0252 {
0253     return new CSSRuleListImpl(this, omitCharsetRules);
0254 }
0255 
0256 void CSSStyleSheetImpl::deleteRule(unsigned long index, int &exceptioncode)
0257 {
0258     exceptioncode = 0;
0259     if (index + 1 > (unsigned) m_lstChildren->count()) {
0260         exceptioncode = DOMException::INDEX_SIZE_ERR;
0261         return;
0262     }
0263     StyleBaseImpl *b = m_lstChildren->takeAt(index);
0264 
0265     if (b->isRule() && static_cast<CSSRuleImpl *>(b)->type() == DOM::CSSRule::NAMESPACE_RULE) {
0266         dirtyNamespaceInfo();
0267         if (static_cast<CSSNamespaceRuleImpl *>(b)->isDefault()) {
0268             recomputeNamespaceInfo();    // default may have changed
0269         }
0270         // ### too late for some rules?
0271     }
0272 
0273     // TreeShared requires delete not deref when removed from tree
0274     b->setParent(nullptr);
0275     if (!b->refCount()) {
0276         delete b;
0277     }
0278     if (m_doc) {
0279         m_doc->updateStyleSelector(true /*shallow*/);
0280     }
0281 }
0282 
0283 void CSSStyleSheetImpl::recomputeNamespaceInfo()
0284 {
0285     assert(!m_namespaces);
0286 
0287     m_namespaces = new QList<CSSNamespaceRuleImpl *>;
0288     m_defaultNamespace = NamespaceName::fromId(anyNamespace);
0289 
0290     // Compute list of all the @namespace nodes, as well as the default one.
0291     for (int i = 0; i < m_lstChildren->count(); ++i) {
0292         StyleBaseImpl *b = m_lstChildren->at(i);
0293         if (b->isRule() && static_cast<CSSRuleImpl *>(b)->type() == DOM::CSSRule::NAMESPACE_RULE) {
0294             CSSNamespaceRuleImpl *nr = static_cast<CSSNamespaceRuleImpl *>(b);
0295             DOM::DOMString prefix = nr->prefix();
0296             DOM::DOMString uri    = nr->namespaceURI();
0297 
0298             if (uri.isNull()) {
0299                 continue;
0300             }
0301 
0302             if (nr->isDefault()) {
0303                 m_defaultNamespace = NamespaceName::fromString(uri);
0304             }
0305 
0306             m_namespaces->append(nr);
0307         }
0308     }
0309 }
0310 
0311 void CSSStyleSheetImpl::determineNamespace(NamespaceName &namespacename, const DOM::DOMString &prefix)
0312 {
0313     if (prefix.isEmpty()) {
0314         namespacename = NamespaceName::fromId(emptyNamespace);    // No namespace. If an element/attribute has a namespace, we won't match it.
0315     } else if (prefix == "*") {
0316         namespacename = NamespaceName::fromId(anyNamespace);    // We'll match any namespace.
0317     } else {
0318         if (!m_namespaces) {
0319             recomputeNamespaceInfo();
0320         }
0321 
0322         // To lookup the name, we go backwards, so the latest one wins
0323         for (int p = m_namespaces->count() - 1; p >= 0; --p) {
0324             CSSNamespaceRuleImpl *ns = m_namespaces->at(p);
0325             if (ns->prefix() == prefix) {
0326                 namespacename = NamespaceName::fromString(ns->namespaceURI());
0327                 return;
0328             }
0329         }
0330     }
0331 }
0332 
0333 bool CSSStyleSheetImpl::parseString(const DOMString &string, bool strict)
0334 {
0335 #ifdef CSS_STYLESHEET_DEBUG
0336     qCDebug(KHTML_LOG) << "parsing sheet, len=" << string.length() << ", sheet is " << string.string();
0337 #endif
0338 
0339     strictParsing = strict;
0340     CSSParser p(strict);
0341     p.parseSheet(this, string);
0342     return true;
0343 }
0344 
0345 bool CSSStyleSheetImpl::isLoading() const
0346 {
0347     StyleBaseImpl *rule;
0348     QListIterator<StyleBaseImpl *> it(*m_lstChildren);
0349     while (it.hasNext()) {
0350         rule = it.next();
0351         if (rule->isImportRule()) {
0352             CSSImportRuleImpl *import = static_cast<CSSImportRuleImpl *>(rule);
0353 #ifdef CSS_STYLESHEET_DEBUG
0354             qCDebug(KHTML_LOG) << "found import";
0355 #endif
0356             if (import->isLoading()) {
0357 #ifdef CSS_STYLESHEET_DEBUG
0358                 qCDebug(KHTML_LOG) << "--> not loaded";
0359 #endif
0360                 m_loadedHint = false;
0361                 return true;
0362             }
0363         }
0364     }
0365     m_loadedHint = true;
0366     return false;
0367 }
0368 
0369 void CSSStyleSheetImpl::checkLoaded() const
0370 {
0371     if (isLoading()) {
0372         return;
0373     }
0374     if (m_parent) {
0375         m_parent->checkLoaded();
0376     }
0377     if (m_parentNode) {
0378         m_loadedHint = m_parentNode->checkRemovePendingSheet();
0379     } else if (parentStyleSheet() && parentStyleSheet()->isCSSStyleSheet()) {
0380         m_loadedHint = static_cast<CSSStyleSheetImpl *>(parentStyleSheet())->loadedHint();
0381     } else {
0382         m_loadedHint = true;
0383     }
0384 }
0385 
0386 void CSSStyleSheetImpl::checkPending() const
0387 {
0388     if (!m_loadedHint) {
0389         return;
0390     }
0391     if (m_parent) {
0392         m_parent->checkPending();
0393     } else if (m_parentNode) {
0394         m_parentNode->checkAddPendingSheet();
0395     }
0396 }
0397 
0398 // ---------------------------------------------------------------------------
0399 
0400 StyleSheetListImpl::~StyleSheetListImpl()
0401 {
0402     foreach (StyleSheetImpl *sh, styleSheets) {
0403         sh->deref();
0404     }
0405 }
0406 
0407 void StyleSheetListImpl::add(StyleSheetImpl *s)
0408 {
0409     if (managerDocument) {
0410         managerDocument->ensureStyleSheetListUpToDate();
0411     }
0412 
0413     // ### in cases this is document.styleSheets, maybe
0414     // we should route to DocumentImpl::addStyleSheets?
0415 
0416     if (!styleSheets.contains(s)) {
0417         s->ref();
0418         styleSheets.append(s);
0419     }
0420 }
0421 
0422 void StyleSheetListImpl::remove(StyleSheetImpl *s)
0423 {
0424     if (managerDocument) {
0425         managerDocument->ensureStyleSheetListUpToDate();
0426     }
0427 
0428     if (styleSheets.removeAll(s)) {
0429         s->deref();
0430     }
0431 }
0432 
0433 unsigned long StyleSheetListImpl::length() const
0434 {
0435     if (managerDocument) {
0436         managerDocument->ensureStyleSheetListUpToDate();
0437     }
0438 
0439     // hack so implicit BODY stylesheets don't get counted here
0440     unsigned long l = 0;
0441     foreach (StyleSheetImpl *sh, styleSheets) {
0442         if (!sh->isCSSStyleSheet() || !static_cast<CSSStyleSheetImpl *>(sh)->implicit()) {
0443             ++l;
0444         }
0445     }
0446     return l;
0447 }
0448 
0449 StyleSheetImpl *StyleSheetListImpl::item(unsigned long index)
0450 {
0451     if (managerDocument) {
0452         managerDocument->ensureStyleSheetListUpToDate();
0453     }
0454 
0455     unsigned long l = 0;
0456     foreach (StyleSheetImpl *sh, styleSheets) {
0457         if (!sh->isCSSStyleSheet() || !static_cast<CSSStyleSheetImpl *>(sh)->implicit()) {
0458             if (l == index) {
0459                 return sh;
0460             }
0461             ++l;
0462         }
0463     }
0464     return nullptr;
0465 }
0466 
0467 // --------------------------------------------------------------------------------------------
0468 
0469 /* MediaList is used to store 3 types of media related entities which mean the same:
0470  * Media Queries, Media Types and Media Descriptors.
0471  * Currently MediaList always tries to parse media queries and if parsing fails,
0472  * tries to fallback to Media Descriptors if m_fallback flag is set.
0473  * Slight problem with syntax error handling:
0474  * CSS 2.1 Spec (https://www.w3.org/TR/CSS21/media.html)
0475  * specifies that failing media type parsing is a syntax error
0476  * CSS 3 Media Queries Spec (https://www.w3.org/TR/css3-mediaqueries/)
0477  * specifies that failing media query is a syntax error
0478  * HTML 4.01 spec (https://www.w3.org/TR/REC-html40/present/styles.html#adef-media)
0479  * specifies that Media Descriptors should be parsed with forward-compatible syntax
0480  * DOM Level 2 Style Sheet spec (https://www.w3.org/TR/DOM-Level-2-Style/)
0481  * talks about MediaList.mediaText and refers
0482  *   -  to Media Descriptors of HTML 4.0 in context of StyleSheet
0483  *   -  to Media Types of CSS 2.0 in context of CSSMediaRule and CSSImportRule
0484  *
0485  * These facts create situation where same (illegal) media specification may result in
0486  * different parses depending on whether it is media attr of style element or part of
0487  * css @media rule.
0488  * <style media="screen and resolution > 40dpi"> ..</style> will be enabled on screen devices where as
0489  * @media screen and resolution > 40dpi {..} will not.
0490  * This gets more counter-intuitive in JavaScript:
0491  * document.styleSheets[0].media.mediaText = "screen and resolution > 40dpi" will be ok and
0492  * enabled, while
0493  * document.styleSheets[0].cssRules[0].media.mediaText = "screen and resolution > 40dpi" will
0494  * throw SYNTAX_ERR exception.
0495  */
0496 
0497 MediaListImpl::MediaListImpl(CSSStyleSheetImpl *parentSheet,
0498                              const DOMString &media, bool fallbackToDescriptor)
0499     : StyleBaseImpl(parentSheet)
0500     , m_fallback(fallbackToDescriptor)
0501 {
0502     int ec = 0;
0503     setMediaText(media, ec);
0504     // FIXME: parsing can fail. The problem with failing constructor is that
0505     // we would need additional flag saying MediaList is not valid
0506     // Parse can fail only when fallbackToDescriptor == false, i.e when HTML4 media descriptor
0507     // forward-compatible syntax is not in use.
0508     // DOMImplementationCSS seems to mandate that media descriptors are used
0509     // for both html and svg, even though svg:style doesn't use media descriptors
0510     // Currently the only places where parsing can fail are
0511     // creating <svg:style>, creating css media / import rules from js
0512     if (ec) {
0513         setMediaText("invalid", ec);
0514     }
0515 }
0516 
0517 MediaListImpl::MediaListImpl(CSSRuleImpl *parentRule, const DOMString &media, bool fallbackToDescriptor)
0518     : StyleBaseImpl(parentRule)
0519     , m_fallback(fallbackToDescriptor)
0520 {
0521     int ec = 0;
0522     setMediaText(media, ec);
0523     // FIXME: parsing can fail. The problem with failing constructor is that
0524     // we would need additional flag saying MediaList is not valid
0525     // Parse can fail only when fallbackToDescriptor == false, i.e when HTML4 media descriptor
0526     // forward-compatible syntax is not in use.
0527     // DOMImplementationCSS seems to mandate that media descriptors are used
0528     // for both html and svg, even though svg:style doesn't use media descriptors
0529     // Currently the only places where parsing can fail are
0530     // creating <svg:style>, creating css media / import rules from js
0531     if (ec) {
0532         setMediaText("invalid", ec);
0533     }
0534 }
0535 
0536 MediaListImpl::~MediaListImpl()
0537 {
0538     qDeleteAll(m_queries);
0539 }
0540 
0541 CSSStyleSheetImpl *MediaListImpl::parentStyleSheet() const
0542 {
0543     if (m_parent->isCSSStyleSheet()) {
0544         return static_cast<CSSStyleSheetImpl *>(m_parent);
0545     }
0546     return nullptr;
0547 }
0548 
0549 CSSRuleImpl *MediaListImpl::parentRule() const
0550 {
0551     if (m_parent->isRule()) {
0552         return static_cast<CSSRuleImpl *>(m_parent);
0553     }
0554     return nullptr;
0555 }
0556 
0557 static DOMString parseMediaDescriptor(const DOMString &s)
0558 {
0559     int len = s.length();
0560 
0561     // https://www.w3.org/TR/REC-html40/types.html#type-media-descriptors
0562     // "Each entry is truncated just before the first character that isn't a
0563     // US ASCII letter [a-zA-Z] (ISO 10646 hex 41-5a, 61-7a), digit [0-9] (hex 30-39),
0564     // or hyphen (hex 2d)."
0565     int i;
0566     unsigned short c;
0567     for (i = 0; i < len; ++i) {
0568         c = s[i].unicode();
0569         if (!((c >= 'a' && c <= 'z')
0570                 || (c >= 'A' && c <= 'Z')
0571                 || (c >= '1' && c <= '9')
0572                 || (c == '-'))) {
0573             break;
0574         }
0575     }
0576     return s.implementation()->substring(0, i);
0577 }
0578 
0579 void MediaListImpl::deleteMedium(const DOMString &oldMedium, int &ec)
0580 {
0581     MediaListImpl tempMediaList;
0582     CSSParser p(true);
0583 
0584     MediaQuery *oldQuery = nullptr;
0585     bool deleteOldQuery = false;
0586 
0587     if (p.parseMediaQuery(&tempMediaList, oldMedium)) {
0588         if (tempMediaList.m_queries.size() > 0) {
0589             oldQuery = tempMediaList.m_queries[0];
0590         }
0591     } else if (m_fallback) {
0592         DOMString medium = parseMediaDescriptor(oldMedium);
0593         if (!medium.isNull()) {
0594             oldQuery = new MediaQuery(MediaQuery::None, medium, nullptr);
0595             deleteOldQuery = true;
0596         }
0597     }
0598 
0599     // DOM Style Sheets spec doesn't allow SYNTAX_ERR to be thrown in deleteMedium
0600     ec = DOMException::NOT_FOUND_ERR;
0601 
0602     if (oldQuery) {
0603         for (int i = 0; i < m_queries.size(); ++i) {
0604             MediaQuery *a = m_queries[i];
0605             if (*a == *oldQuery) {
0606                 m_queries.removeAt(i);
0607                 delete a;
0608                 ec = 0;
0609                 break;
0610             }
0611         }
0612         if (deleteOldQuery) {
0613             delete oldQuery;
0614         }
0615     }
0616 }
0617 
0618 DOM::DOMString MediaListImpl::mediaText() const
0619 {
0620     DOMString text;
0621     bool first = true;
0622     const QList<MediaQuery *>::ConstIterator itEnd = m_queries.end();
0623 
0624     for (QList<MediaQuery *>::ConstIterator it = m_queries.begin(); it != itEnd; ++it) {
0625         if (!first) {
0626             text += ", ";
0627         }
0628         text += (*it)->cssText();
0629         first = false;
0630     }
0631     return text;
0632 }
0633 
0634 void MediaListImpl::setMediaText(const DOM::DOMString &value, int &ec)
0635 {
0636     MediaListImpl tempMediaList;
0637     CSSParser p(true);
0638 
0639     const QString val = value.string();
0640     const QStringList list = val.split(',');
0641 
0642     const QStringList::ConstIterator itEnd = list.end();
0643 
0644     for (QStringList::ConstIterator it = list.begin(); it != itEnd; ++it) {
0645         const DOMString medium = (*it).trimmed();
0646         if (!medium.isEmpty()) {
0647             if (!p.parseMediaQuery(&tempMediaList, medium)) {
0648                 if (m_fallback) {
0649                     DOMString mediaDescriptor = parseMediaDescriptor(medium);
0650                     if (!mediaDescriptor.isNull()) {
0651                         tempMediaList.m_queries.append(new MediaQuery(MediaQuery::None, mediaDescriptor, nullptr));
0652                     }
0653                 } else {
0654                     ec = CSSException::SYNTAX_ERR;
0655                     return;
0656                 }
0657             }
0658         } else if (!m_fallback) {
0659             ec = CSSException::SYNTAX_ERR;
0660             return;
0661         }
0662     }
0663     // ",,,," falls straight through, but is not valid unless fallback
0664     if (!m_fallback && list.begin() == list.end()) {
0665         DOMString s = value.string().trimmed();
0666         if (!s.isEmpty()) {
0667             ec = CSSException::SYNTAX_ERR;
0668             return;
0669         }
0670     }
0671 
0672     ec = 0;
0673     qDeleteAll(m_queries);
0674     m_queries = tempMediaList.m_queries;
0675     tempMediaList.m_queries.clear();
0676 }
0677 
0678 DOMString MediaListImpl::item(unsigned long index) const
0679 {
0680     if (index < (unsigned)m_queries.size()) {
0681         MediaQuery *query = m_queries[index];
0682         return query->cssText();
0683     }
0684 
0685     return DOMString();
0686 }
0687 
0688 void MediaListImpl::appendMedium(const DOMString &newMedium, int &ec)
0689 {
0690     ec = DOMException::INVALID_CHARACTER_ERR;
0691     CSSParser p(true);
0692     if (p.parseMediaQuery(this, newMedium)) {
0693         ec = 0;
0694     } else if (m_fallback) {
0695         DOMString medium = parseMediaDescriptor(newMedium);
0696         if (!medium.isNull()) {
0697             m_queries.append(new MediaQuery(MediaQuery::None, medium, nullptr));
0698             ec = 0;
0699         }
0700     }
0701 }
0702 
0703 void MediaListImpl::appendMediaQuery(MediaQuery *mediaQuery)
0704 {
0705     m_queries.append(mediaQuery);
0706 }
0707