1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright 1999-2003 Lars Knoll (knoll@kde.org)
5  * Copyright 2002-2003 Dirk Mueller (mueller@kde.org)
6  * Copyright 2002-2008 Apple Computer, Inc.
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 #include "css_ruleimpl.h"
25 #include "css_stylesheetimpl.h"
26 #include "css_valueimpl.h"
27 #include "cssparser.h"
28 
29 #include <dom/css_rule.h>
30 #include <dom/css_stylesheet.h>
31 #include <dom/dom_exception.h>
32 #include <dom/dom_string.h>
33 
34 #include <xml/dom_docimpl.h>
35 
36 using namespace DOM;
37 
parentStyleSheet() const38 CSSStyleSheetImpl *CSSRuleImpl::parentStyleSheet() const
39 {
40     return (m_parent && m_parent->isCSSStyleSheet())  ?
41            static_cast<CSSStyleSheetImpl *>(m_parent) : nullptr;
42 }
43 
parentRule() const44 CSSRuleImpl *CSSRuleImpl::parentRule() const
45 {
46     return (m_parent && m_parent->isRule())  ?
47            static_cast<CSSRuleImpl *>(m_parent) : nullptr;
48 }
49 
cssText() const50 DOM::DOMString CSSRuleImpl::cssText() const
51 {
52     // ###
53     return DOMString();
54 }
55 
setCssText(DOM::DOMString)56 void CSSRuleImpl::setCssText(DOM::DOMString /*str*/)
57 {
58     // ###
59 }
60 
61 // ---------------------------------------------------------------------------
62 
CSSFontFaceRuleImpl(StyleBaseImpl * parent)63 CSSFontFaceRuleImpl::CSSFontFaceRuleImpl(StyleBaseImpl *parent)
64     : CSSRuleImpl(parent)
65 {
66     m_type = CSSRule::FONT_FACE_RULE;
67     m_style = nullptr;
68 }
69 
~CSSFontFaceRuleImpl()70 CSSFontFaceRuleImpl::~CSSFontFaceRuleImpl()
71 {
72     if (m_style) {
73         m_style->deref();
74     }
75 }
76 
setDeclaration(CSSStyleDeclarationImpl * decl)77 void CSSFontFaceRuleImpl::setDeclaration(CSSStyleDeclarationImpl *decl)
78 {
79     assert(!m_style);
80     m_style = decl;
81     if (m_style) {
82         m_style->ref();
83     }
84 }
85 
cssText() const86 DOMString CSSFontFaceRuleImpl::cssText() const
87 {
88     DOMString result("@font-face");
89 
90     result += " { ";
91     result += m_style->cssText();
92     result += "}";
93 
94     return result;
95 }
96 
97 // --------------------------------------------------------------------------
98 
CSSImportRuleImpl(StyleBaseImpl * parent,const DOM::DOMString & href,MediaListImpl * media)99 CSSImportRuleImpl::CSSImportRuleImpl(StyleBaseImpl *parent,
100                                      const DOM::DOMString &href,
101                                      MediaListImpl *media)
102     : CSSRuleImpl(parent)
103 {
104     m_type = CSSRule::IMPORT_RULE;
105 
106     m_lstMedia = media;
107     if (!m_lstMedia) {
108         m_lstMedia = new MediaListImpl(this, DOMString());
109     }
110     m_lstMedia->setParent(this);
111     m_lstMedia->ref();
112 
113     m_strHref = href;
114     m_styleSheet = nullptr;
115 
116     m_cachedSheet = nullptr;
117 
118     init();
119 }
CSSImportRuleImpl(StyleBaseImpl * parent,const DOM::DOMString & href,const DOM::DOMString & media)120 CSSImportRuleImpl::CSSImportRuleImpl(StyleBaseImpl *parent,
121                                      const DOM::DOMString &href,
122                                      const DOM::DOMString &media)
123     : CSSRuleImpl(parent)
124 {
125     m_type = CSSRule::IMPORT_RULE;
126 
127     m_lstMedia = new MediaListImpl(this, media);
128     m_lstMedia->ref();
129 
130     m_strHref = href;
131     m_styleSheet = nullptr;
132 
133     m_cachedSheet = nullptr;
134 
135     init();
136 }
137 
~CSSImportRuleImpl()138 CSSImportRuleImpl::~CSSImportRuleImpl()
139 {
140     if (m_lstMedia) {
141         m_lstMedia->setParent(nullptr);
142         m_lstMedia->deref();
143     }
144     if (m_styleSheet) {
145         m_styleSheet->setParent(nullptr);
146         m_styleSheet->deref();
147     }
148 
149     if (m_cachedSheet) {
150         m_cachedSheet->deref(this);
151     }
152 }
153 
checkLoaded() const154 void CSSImportRuleImpl::checkLoaded() const
155 {
156     if (isLoading()) {
157         return;
158     }
159     CSSRuleImpl::checkLoaded();
160 }
161 
setStyleSheet(const DOM::DOMString & url,const DOM::DOMString & sheetStr,const DOM::DOMString & charset,const DOM::DOMString & mimetype)162 void CSSImportRuleImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheetStr, const DOM::DOMString &charset, const DOM::DOMString &mimetype)
163 {
164     if (m_styleSheet) {
165         m_styleSheet->setParent(nullptr);
166         m_styleSheet->deref();
167     }
168     m_styleSheet = new CSSStyleSheetImpl(this, url);
169     m_styleSheet->setCharset(charset);
170     m_styleSheet->ref();
171 
172     CSSStyleSheetImpl *parent = parentStyleSheet();
173     bool strict = parent ? parent->useStrictParsing() : true;
174     DOMString sheet = sheetStr;
175     if (strict && !khtml::isAcceptableCSSMimetype(mimetype)) {
176         sheet = "";
177     }
178     m_styleSheet->parseString(sheet, strict);
179     m_loading = false;
180     m_done = true;
181 
182     checkLoaded();
183 }
184 
error(int,const QString &)185 void CSSImportRuleImpl::error(int /*err*/, const QString &/*text*/)
186 {
187     if (m_styleSheet) {
188         m_styleSheet->setParent(nullptr);
189         m_styleSheet->deref();
190     }
191     m_styleSheet = nullptr;
192 
193     m_loading = false;
194     m_done = true;
195 
196     checkLoaded();
197 }
198 
isLoading() const199 bool CSSImportRuleImpl::isLoading() const
200 {
201     return (m_loading || (m_styleSheet && m_styleSheet->isLoading()));
202 }
203 
init()204 void CSSImportRuleImpl::init()
205 {
206     m_loading = 0;
207     m_done = false;
208     khtml::DocLoader *docLoader = nullptr;
209     StyleBaseImpl *root = this;
210     StyleBaseImpl *parent;
211     while ((parent = root->parent())) {
212         root = parent;
213     }
214     if (root->isCSSStyleSheet()) {
215         docLoader = static_cast<CSSStyleSheetImpl *>(root)->docLoader();
216     }
217 
218     DOMString absHref = m_strHref;
219     CSSStyleSheetImpl *parentSheet = parentStyleSheet();
220     if (!parentSheet->href().isNull()) {
221         // use parent styleheet's URL as the base URL
222         absHref = QUrl(parentSheet->href().string()).resolved(QUrl(m_strHref.string())).toString();
223     }
224     /*
225         else {
226           // use documents's URL as the base URL
227           DocumentImpl *doc = static_cast<CSSStyleSheetImpl*>(root)->doc();
228           absHref = QUrl(doc->URL()).resolved(QUrl(m_strHref.string())).toString();
229         }
230     */
231     // Check for a cycle in our import chain.  If we encounter a stylesheet
232     // in our parent chain with the same URL, then just bail.
233     for (parent = static_cast<StyleBaseImpl *>(this)->parent();
234             parent;
235             parent = parent->parent())
236         if (absHref == parent->baseURL().url()) {
237             return;
238         }
239 
240     m_cachedSheet = docLoader->requestStyleSheet(absHref, parentStyleSheet()->charset().string());
241 
242     if (m_cachedSheet) {
243         // if the import rule is issued dynamically, the sheet may have already been
244         // removed from the pending sheet count, so let the doc know
245         // the sheet being imported is pending.
246         checkPending();
247 
248         m_loading = true;
249         m_cachedSheet->ref(this);
250     }
251 }
252 
cssText() const253 DOMString CSSImportRuleImpl::cssText() const
254 {
255     DOMString result = "@import url(\"";
256     result += m_strHref;
257     result += "\")";
258 
259     if (m_lstMedia) {
260         result += " ";
261         result += m_lstMedia->mediaText();
262     }
263     result += ";";
264 
265     return result;
266 }
267 
268 // --------------------------------------------------------------------------
CSSMediaRuleImpl(StyleBaseImpl * parent,MediaListImpl * mediaList,CSSRuleListImpl * ruleList)269 CSSMediaRuleImpl::CSSMediaRuleImpl(StyleBaseImpl *parent, MediaListImpl *mediaList, CSSRuleListImpl *ruleList)
270     :   CSSRuleImpl(parent)
271 {
272     m_type = CSSRule::MEDIA_RULE;
273     m_lstMedia = mediaList;
274     if (m_lstMedia) {
275         m_lstMedia->ref();
276     }
277     m_lstCSSRules = ruleList;
278     m_lstCSSRules->ref();
279 }
280 
CSSMediaRuleImpl(StyleBaseImpl * parent)281 CSSMediaRuleImpl::CSSMediaRuleImpl(StyleBaseImpl *parent)
282     :   CSSRuleImpl(parent)
283 {
284     m_type = CSSRule::MEDIA_RULE;
285     m_lstMedia = nullptr;
286     m_lstCSSRules = new CSSRuleListImpl();
287     m_lstCSSRules->ref();
288 }
289 
CSSMediaRuleImpl(StyleBaseImpl * parent,const DOM::DOMString & media)290 CSSMediaRuleImpl::CSSMediaRuleImpl(StyleBaseImpl *parent, const DOM::DOMString &media)
291     :   CSSRuleImpl(parent)
292 {
293     m_type = CSSRule::MEDIA_RULE;
294     m_lstMedia = new MediaListImpl(this, media);
295     m_lstMedia->ref();
296     m_lstCSSRules = new CSSRuleListImpl();
297     m_lstCSSRules->ref();
298 }
299 
~CSSMediaRuleImpl()300 CSSMediaRuleImpl::~CSSMediaRuleImpl()
301 {
302     if (m_lstMedia) {
303         m_lstMedia->setParent(nullptr);
304         m_lstMedia->deref();
305     }
306     for (unsigned int i = 0; i < m_lstCSSRules->length(); ++i) {
307         m_lstCSSRules->item(i)->setParent(nullptr);
308     }
309     m_lstCSSRules->deref();
310 }
311 
append(CSSRuleImpl * rule)312 unsigned long CSSMediaRuleImpl::append(CSSRuleImpl *rule)
313 {
314     return rule ? m_lstCSSRules->insertRule(rule, m_lstCSSRules->length()) : 0;
315 }
316 
insertRule(const DOMString & rule,unsigned long index)317 unsigned long CSSMediaRuleImpl::insertRule(const DOMString &rule,
318         unsigned long index)
319 {
320     CSSParser p(strictParsing);
321     CSSRuleImpl *newRule = p.parseRule(parentStyleSheet(), rule);
322 
323     return newRule ? m_lstCSSRules->insertRule(newRule, index) : 0;
324 }
325 
cssText() const326 DOM::DOMString CSSMediaRuleImpl::cssText() const
327 {
328     DOMString result("@media ");
329     if (m_lstMedia) {
330         result += m_lstMedia->mediaText();
331         result += " ";
332     }
333     result += "{ \n";
334 
335     if (m_lstCSSRules) {
336         unsigned len = m_lstCSSRules->length();
337         for (unsigned i = 0; i < len; i++) {
338             result += "  ";
339             result += m_lstCSSRules->item(i)->cssText();
340             result += "\n";
341         }
342     }
343 
344     result += "}";
345     return result;
346 }
347 
348 // ---------------------------------------------------------------------------
349 
CSSPageRuleImpl(StyleBaseImpl * parent)350 CSSPageRuleImpl::CSSPageRuleImpl(StyleBaseImpl *parent)
351     : CSSRuleImpl(parent)
352 {
353     m_type = CSSRule::PAGE_RULE;
354     m_style = nullptr;
355 }
356 
~CSSPageRuleImpl()357 CSSPageRuleImpl::~CSSPageRuleImpl()
358 {
359     if (m_style) {
360         m_style->deref();
361     }
362 }
363 
selectorText() const364 DOM::DOMString CSSPageRuleImpl::selectorText() const
365 {
366     // ###
367     return DOMString();
368 }
369 
setSelectorText(DOM::DOMString)370 void CSSPageRuleImpl::setSelectorText(DOM::DOMString /*str*/)
371 {
372     // ###
373 }
374 
375 // --------------------------------------------------------------------------
376 
CSSStyleRuleImpl(StyleBaseImpl * parent)377 CSSStyleRuleImpl::CSSStyleRuleImpl(StyleBaseImpl *parent)
378     : CSSRuleImpl(parent)
379 {
380     m_type = CSSRule::STYLE_RULE;
381     m_style = nullptr;
382     m_selector = nullptr;
383 }
384 
~CSSStyleRuleImpl()385 CSSStyleRuleImpl::~CSSStyleRuleImpl()
386 {
387     if (m_style) {
388         m_style->setParent(nullptr);
389         m_style->deref();
390     }
391     qDeleteAll(*m_selector);
392     delete m_selector;
393 }
394 
cssText() const395 DOMString CSSStyleRuleImpl::cssText() const
396 {
397     DOMString result(selectorText());
398 
399     result += " { ";
400     result += m_style->cssText();
401     result += "}";
402 
403     return result;
404 }
405 
selectorText() const406 DOM::DOMString CSSStyleRuleImpl::selectorText() const
407 {
408     if (m_selector) {
409         DOMString str;
410         foreach (CSSSelector *s, *m_selector) {
411             if (s != m_selector->at(0)) {
412                 str += ", ";
413             }
414             str += s->selectorText();
415         }
416         return str;
417     }
418     return DOMString();
419 }
420 
setSelectorText(DOM::DOMString)421 void CSSStyleRuleImpl::setSelectorText(DOM::DOMString /*str*/)
422 {
423     // ###
424 }
425 
parseString(const DOMString &,bool)426 bool CSSStyleRuleImpl::parseString(const DOMString &/*string*/, bool)
427 {
428     // ###
429     return false;
430 }
431 
setDeclaration(CSSStyleDeclarationImpl * style)432 void CSSStyleRuleImpl::setDeclaration(CSSStyleDeclarationImpl *style)
433 {
434     if (m_style != style) {
435         if (m_style) {
436             m_style->deref();
437         }
438         m_style = style;
439         if (m_style) {
440             m_style->ref();
441         }
442     }
443 }
444 
445 // --------------------------------------------------------------------
446 
CSSNamespaceRuleImpl(StyleBaseImpl * parent,const DOMString & prefix,const DOMString & ns)447 CSSNamespaceRuleImpl::CSSNamespaceRuleImpl(StyleBaseImpl *parent, const DOMString &prefix, const DOMString &ns)
448     : CSSRuleImpl(parent)
449 {
450     m_type      = CSSRule::NAMESPACE_RULE;
451     m_prefix    = prefix;
452     m_namespace = ns;
453 }
454 
455 // --------------------------------------------------------------------
456 
CSSRuleListImpl(StyleListImpl * const list,bool omitCharsetRules)457 CSSRuleListImpl::CSSRuleListImpl(StyleListImpl *const list, bool omitCharsetRules)
458 {
459     m_list = list;
460     if (list && omitCharsetRules) {
461         m_list = nullptr;
462         unsigned len = list->length();
463         for (unsigned i = 0; i < len; ++i) {
464             StyleBaseImpl *rule = list->item(i);
465             if (rule->isRule() && !rule->isCharsetRule()) {
466                 append(static_cast<CSSRuleImpl *>(rule));
467             }
468         }
469     } else if (m_list) {
470         m_list->ref();
471     }
472 }
473 
~CSSRuleListImpl()474 CSSRuleListImpl::~CSSRuleListImpl()
475 {
476     CSSRuleImpl *rule;
477     while (!m_lstCSSRules.isEmpty() && (rule = m_lstCSSRules.takeFirst())) {
478         rule->deref();
479     }
480     if (m_list) {
481         m_list->deref();
482     }
483 }
484 
length() const485 unsigned long CSSRuleListImpl::length() const
486 {
487     return m_list ? m_list->length() : m_lstCSSRules.count();
488 }
489 
item(unsigned long index)490 CSSRuleImpl *CSSRuleListImpl::item(unsigned long index)
491 {
492     if (m_list) {
493         StyleBaseImpl *rule = m_list->item(index);
494         assert(!rule || rule->isRule());
495         return static_cast<CSSRuleImpl *>(rule);
496     }
497     return index < length() ? m_lstCSSRules.at(index) : nullptr;
498 }
499 
deleteRule(unsigned long index)500 void CSSRuleListImpl::deleteRule(unsigned long index)
501 {
502     assert(!m_list);
503     if (index + 1 > (unsigned) m_lstCSSRules.size()) {
504         return;
505         // ### Throw INDEX_SIZE_ERR exception here (TODO)
506     }
507     CSSRuleImpl *rule = m_lstCSSRules.takeAt(index);
508     rule->deref();
509 }
510 
append(CSSRuleImpl * rule)511 void CSSRuleListImpl::append(CSSRuleImpl *rule)
512 {
513     assert(!m_list);
514     rule->ref();
515     m_lstCSSRules.append(rule);
516 }
517 
insertRule(CSSRuleImpl * rule,unsigned long index)518 unsigned long CSSRuleListImpl::insertRule(CSSRuleImpl *rule,
519         unsigned long index)
520 {
521     assert(!m_list);
522     if (index > (unsigned) m_lstCSSRules.size()) {
523         return 0;
524         // ### Throw INDEX_SIZE_ERR exception here (TODO)
525     }
526 
527     if (rule) {
528         m_lstCSSRules.insert(index, rule);
529         rule->ref();
530         return index;
531     }
532 
533     return 0;
534 }
535 
536