File indexing completed on 2024-05-05 16:10:12

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
0005  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
0006  *           (C) 2001 Dirk Mueller (mueller@kde.org)
0007  *           (C) 2003, 2004, 2005, 2006, 2007 Apple Computer, Inc.
0008  *
0009  * This library is free software; you can redistribute it and/or
0010  * modify it under the terms of the GNU Library General Public
0011  * License as published by the Free Software Foundation; either
0012  * version 2 of the License, or (at your option) any later version.
0013  *
0014  * This library is distributed in the hope that it will be useful,
0015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017  * Library General Public License for more details.
0018  *
0019  * You should have received a copy of the GNU Library General Public License
0020  * along with this library; see the file COPYING.LIB.  If not, write to
0021  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022  * Boston, MA 02110-1301, USA.
0023  */
0024 // -------------------------------------------------------------------------
0025 
0026 #include "html/html_headimpl.h"
0027 #include "html/html_documentimpl.h"
0028 #include "xml/dom_textimpl.h"
0029 #include "xml/dom2_eventsimpl.h"
0030 
0031 #include "khtmlview.h"
0032 #include "khtml_part.h"
0033 
0034 #include "misc/loader.h"
0035 
0036 #include "css/cssstyleselector.h"
0037 #include "css/css_mediaquery.h"
0038 
0039 #include "ecma/kjs_proxy.h"
0040 
0041 #include <QUrl>
0042 #include "khtml_debug.h"
0043 
0044 using namespace khtml;
0045 using namespace DOM;
0046 
0047 NodeImpl::Id HTMLBaseElementImpl::id() const
0048 {
0049     return ID_BASE;
0050 }
0051 
0052 void HTMLBaseElementImpl::parseAttribute(AttributeImpl *attr)
0053 {
0054     switch (attr->id()) {
0055     case ATTR_HREF:
0056         m_href = attr->value().trimSpaces().string();
0057         process();
0058         break;
0059     case ATTR_TARGET:
0060         m_target = attr->value();
0061         process();
0062         break;
0063     default:
0064         HTMLElementImpl::parseAttribute(attr);
0065     }
0066 }
0067 
0068 void HTMLBaseElementImpl::insertedIntoDocument()
0069 {
0070     HTMLElementImpl::insertedIntoDocument();
0071     process();
0072 }
0073 
0074 void HTMLBaseElementImpl::removedFromDocument()
0075 {
0076     HTMLElementImpl::removedFromDocument();
0077 
0078     // Since the document doesn't have a base element...
0079     // (This will break in the case of multiple base elements, but that's not valid anyway (?))
0080     document()->setBaseURL(QUrl());
0081     document()->setBaseTarget(QString());
0082 }
0083 
0084 void HTMLBaseElementImpl::process()
0085 {
0086     if (!inDocument()) {
0087         return;
0088     }
0089 
0090     if (!m_href.isEmpty() && document()->part()) {
0091         document()->setBaseURL(QUrl(document()->part()->url()).resolved(QUrl(m_href)));
0092     }
0093 
0094     if (!m_target.isEmpty()) {
0095         document()->setBaseTarget(m_target.string());
0096     }
0097 
0098     // ### should changing a document's base URL dynamically automatically update all images, stylesheets etc?
0099 }
0100 
0101 // -------------------------------------------------------------------------
0102 
0103 HTMLLinkElementImpl::~HTMLLinkElementImpl()
0104 {
0105     if (m_sheet) {
0106         m_sheet->deref();
0107     }
0108     if (m_cachedSheet) {
0109         m_cachedSheet->deref(this);
0110     }
0111 }
0112 
0113 NodeImpl::Id HTMLLinkElementImpl::id() const
0114 {
0115     return ID_LINK;
0116 }
0117 
0118 void HTMLLinkElementImpl::parseAttribute(AttributeImpl *attr)
0119 {
0120     switch (attr->id()) {
0121     case ATTR_HREF: {
0122         const DOMString hrefUrl = attr->value().trimSpaces();
0123         if (!hrefUrl.isEmpty()) {
0124             m_url = document()->completeURL(hrefUrl.string());
0125         }
0126         process();
0127         break;
0128     }
0129     case ATTR_REL:
0130     case ATTR_TYPE:
0131         process();
0132         break;
0133     case ATTR_TITLE:
0134         // ### when title changes we have to reconsider our alternative
0135         // stylesheet choice
0136         if (m_sheet) {
0137             m_sheet->setTitle(attr->value());
0138         }
0139         break;
0140     case ATTR_MEDIA:
0141         m_media = attr->value().string().toLower();
0142         process();
0143         break;
0144     case ATTR_DISABLED: {
0145         bool m_oldisDisabled = m_isDisabled;
0146         m_isDisabled = attr->val();
0147         if (m_oldisDisabled != m_isDisabled) {
0148             if (isLoading()) {
0149                 if (m_oldisDisabled) {
0150                     document()->addPendingSheet();
0151                 } else if (!m_alternate) {
0152                     document()->styleSheetLoaded();
0153                 }
0154             }
0155             if (m_oldisDisabled) {
0156                 // enabling: if it's an alternate sheet, pretend it's not.
0157                 m_alternate = false;
0158             } else if (!m_alternate) {
0159                 // disabling: recheck alternate status
0160                 QString rel =  getAttribute(ATTR_REL).string().toLower();
0161                 QString type = getAttribute(ATTR_TYPE).string().toLower();
0162                 m_alternate = (type.contains("text/css") || rel.contains("stylesheet")) && rel.contains("alternate");
0163             }
0164             if (isLoading()) {
0165                 break;
0166             }
0167             if (!m_sheet && !m_isDisabled) {
0168                 process();
0169                 if (isLoading() && m_alternate) {
0170                     document()->addPendingSheet();
0171                 }
0172                 m_alternate = false;
0173             } else {
0174                 document()->updateStyleSelector();    // Update the style selector.
0175             }
0176         }
0177         break;
0178     }
0179     default:
0180         HTMLElementImpl::parseAttribute(attr);
0181     }
0182 }
0183 
0184 static inline bool isIconLink(const QString &relAttribute)
0185 {
0186     // https://www.w3.org/TR/html5/links.html#linkTypes:
0187     // split rel attribute on spaces and search for "icon" keyword.
0188     // note: relAttribute is supposed to be a lower case string
0189     const QLatin1String icon("icon");
0190     int iconPos = relAttribute.indexOf(icon);
0191     while (iconPos != -1) {
0192         const bool found = (iconPos == 0 || relAttribute.at(iconPos - 1).isSpace()) &&
0193                            (((iconPos + 4) == relAttribute.length()) || relAttribute.at(iconPos + 4).isSpace());
0194         if (found) {
0195             return true;
0196         }
0197         iconPos = relAttribute.indexOf(icon, iconPos + 5);
0198     }
0199 
0200     return false;
0201 }
0202 
0203 void HTMLLinkElementImpl::process()
0204 {
0205     if (!inDocument()) {
0206         return;
0207     }
0208 
0209     //QString type = getAttribute(ATTR_TYPE).string().toLower();
0210     const QString rel = getAttribute(ATTR_REL).string().toLower();
0211 
0212     KHTMLPart *part = document()->part();
0213 
0214     // Location of small icon for locationbar / bookmarks (aka "favicon")
0215     if (isIconLink(rel) && !m_url.isEmpty() && part && !part->parentPart()) {
0216         part->browserExtension()->setIconUrl(QUrl(m_url.string()));
0217     } // Stylesheet
0218     else if (rel.contains("stylesheet") && !m_url.isEmpty() && !m_isDisabled) {
0219         // no need to load style sheets which aren't for the screen output
0220         // ### there may be in some situations e.g. for an editor or script to manipulate
0221         khtml::MediaQueryEvaluator allEval(true);
0222         khtml::MediaQueryEvaluator screenEval("screen", true);
0223         khtml::MediaQueryEvaluator printEval("print", true);
0224         MediaListImpl *media = new MediaListImpl((CSSStyleSheetImpl *)nullptr, m_media, true);
0225         media->ref();
0226         if (allEval.eval(media) || screenEval.eval(media) || printEval.eval(media)) {
0227             // Add ourselves as a pending sheet, but only if we aren't an alternate
0228             // stylesheet.  Alternate stylesheets don't hold up render tree construction.
0229             m_alternate = rel.contains("alternate");
0230             if (!isAlternate()) {
0231                 document()->addPendingSheet();
0232             }
0233 
0234             QString chset = getAttribute(ATTR_CHARSET).string();
0235             // set chset to charset of referring document when attribute CHARSET is absent.
0236             // https://www.w3.org/TR/CSS21/syndata.html(4.4)
0237             if (chset.isEmpty() && part) {
0238                 chset = part->encoding();
0239             }
0240             if (m_cachedSheet) {
0241                 if (m_loading) {
0242                     document()->styleSheetLoaded();
0243                 }
0244                 m_cachedSheet->deref(this);
0245             }
0246             m_loading = true;
0247             m_cachedSheet = document()->docLoader()->requestStyleSheet(m_url, chset);
0248             if (m_cachedSheet) {
0249                 m_isCSSSheet = true;
0250                 m_cachedSheet->ref(this);
0251             } else if (!isAlternate()) {
0252                 // Error requesting sheet; decrement pending sheet count
0253                 m_loading = false;
0254                 document()->styleSheetLoaded();
0255             }
0256         }
0257         media->deref();
0258     } else if (m_sheet) {
0259         // we no longer contain a stylesheet, e.g. perhaps rel or type was changed
0260         m_sheet->deref();
0261         m_sheet = nullptr;
0262         m_isCSSSheet = false;
0263         document()->updateStyleSelector();
0264     }
0265 }
0266 
0267 void HTMLLinkElementImpl::insertedIntoDocument()
0268 {
0269     HTMLElementImpl::insertedIntoDocument();
0270     process();
0271 }
0272 
0273 void HTMLLinkElementImpl::removedFromDocument()
0274 {
0275     HTMLElementImpl::removedFromDocument();
0276     document()->updateStyleSelector();
0277 }
0278 
0279 void HTMLLinkElementImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheetStr, const DOM::DOMString &charset, const DOM::DOMString &mimetype)
0280 {
0281     if (m_sheet) {
0282         m_sheet->deref();
0283     }
0284     bool strict = !document()->inCompatMode();
0285     DOMString sheet = sheetStr;
0286     if (strict && !khtml::isAcceptableCSSMimetype(mimetype)) {
0287         sheet = "";
0288     }
0289     m_sheet = new CSSStyleSheetImpl(this, url);
0290     m_sheet->ref();
0291     m_sheet->setCharset(charset);
0292     m_sheet->parseString(sheet, strict);
0293     m_sheet->setTitle(getAttribute(ATTR_TITLE));
0294 
0295     MediaListImpl *media = new MediaListImpl((CSSStyleSheetImpl *)nullptr, m_media);
0296     m_sheet->setMedia(media);
0297 
0298     finished();
0299 }
0300 
0301 void HTMLLinkElementImpl::finished()
0302 {
0303     m_loading = false;
0304 
0305     // Tell the doc about the sheet.
0306     if (!isLoading() && !isDisabled() && !isAlternate()) {
0307         document()->styleSheetLoaded();
0308     }
0309 
0310     // ### major inefficiency, but currently necessary for proper
0311     // alternate styles support. don't recalc the styleselector
0312     // when nothing actually changed!
0313     if (isAlternate() && m_sheet && !isDisabled()) {
0314         document()->updateStyleSelector();
0315     }
0316 }
0317 
0318 void HTMLLinkElementImpl::error(int, const QString &)
0319 {
0320     finished();
0321 }
0322 
0323 bool HTMLLinkElementImpl::isLoading() const
0324 {
0325 //    qCDebug(KHTML_LOG) << "link: checking if loading!";
0326     if (m_loading) {
0327         return true;
0328     }
0329     if (!m_sheet) {
0330         return false;
0331     }
0332     //if(!m_sheet->isCSSStyleSheet()) return false;
0333     return static_cast<CSSStyleSheetImpl *>(m_sheet)->isLoading();
0334 }
0335 
0336 bool HTMLLinkElementImpl::checkAddPendingSheet()
0337 {
0338     if (!isLoading() && !isDisabled() && !isAlternate()) {
0339         document()->addPendingSheet();
0340         return true;
0341     }
0342     return false;
0343 }
0344 
0345 bool HTMLLinkElementImpl::checkRemovePendingSheet()
0346 {
0347     if (!isLoading() && !isDisabled() && !isAlternate()) {
0348         document()->styleSheetLoaded();
0349         return true;
0350     }
0351     return false;
0352 }
0353 
0354 // -------------------------------------------------------------------------
0355 
0356 NodeImpl::Id HTMLMetaElementImpl::id() const
0357 {
0358     return ID_META;
0359 }
0360 
0361 void HTMLMetaElementImpl::parseAttribute(AttributeImpl *attr)
0362 {
0363     switch (attr->id()) {
0364     case ATTR_HTTP_EQUIV:
0365         m_equiv = attr->value();
0366         process();
0367         break;
0368     case ATTR_CONTENT:
0369         m_content = attr->value();
0370         process();
0371         break;
0372     default:
0373         HTMLElementImpl::parseAttribute(attr);
0374     }
0375 }
0376 
0377 void HTMLMetaElementImpl::insertedIntoDocument()
0378 {
0379     HTMLElementImpl::insertedIntoDocument();
0380     process();
0381 }
0382 
0383 void HTMLMetaElementImpl::process()
0384 {
0385     // Get the document to process the tag, but only if we're actually part of DOM tree (changing a meta tag while
0386     // it's not in the tree shouldn't have any effect on the document)
0387     if (inDocument() && !m_equiv.isNull() && !m_content.isNull()) {
0388         document()->processHttpEquiv(m_equiv, m_content);
0389     }
0390 }
0391 
0392 // -------------------------------------------------------------------------
0393 
0394 HTMLScriptElementImpl::HTMLScriptElementImpl(DocumentImpl *doc)
0395     : HTMLElementImpl(doc), m_cachedScript(nullptr), m_createdByParser(false), m_evaluated(false), m_hasNonEmptyForAttribute(false)
0396 {
0397 }
0398 
0399 HTMLScriptElementImpl::~HTMLScriptElementImpl()
0400 {
0401     if (m_cachedScript) {
0402         m_cachedScript->deref(this);
0403     }
0404 }
0405 
0406 NodeImpl::Id HTMLScriptElementImpl::id() const
0407 {
0408     return ID_SCRIPT;
0409 }
0410 
0411 void HTMLScriptElementImpl::parseAttribute(AttributeImpl *attr)
0412 {
0413     switch (attr->id()) {
0414     case ATTR_ONLOAD:
0415         setHTMLEventListener(EventImpl::LOAD_EVENT,
0416                              document()->createHTMLEventListener(attr->value().string(), "onload", this));
0417         break;
0418     case ATTR_SRC: {
0419         // We want to evaluate scripts on src attr change when a fresh script element
0420         // is inserted into document, and then has its source changed -after-.
0421         // If the source is manipulated while we're outside the document,
0422         // we'll only start doing things once we get insertedIntoDocument()
0423         if (m_evaluated || m_cachedScript || m_createdByParser || !inDocument()) {
0424             return;
0425         }
0426         const DOMString url = attr->value().trimSpaces();
0427         if (!url.isEmpty()) {
0428             loadFromUrl(url);
0429         }
0430         break;
0431     }
0432     case ATTR_FOR: {
0433         m_hasNonEmptyForAttribute = !attr->value().isEmpty();
0434         break;
0435     }
0436     default:
0437         HTMLElementImpl::parseAttribute(attr);
0438     }
0439 }
0440 
0441 bool HTMLScriptElementImpl::isURLAttribute(AttributeImpl *attr) const
0442 {
0443     return attr->id() == ATTR_SRC;
0444 }
0445 
0446 bool HTMLScriptElementImpl::isValidScript() const
0447 {
0448     // HTML5 draft 4.3.1 : script elements with non-empty for attribute
0449     // must not be executed.
0450     if (m_hasNonEmptyForAttribute) {
0451         return false;
0452     }
0453 
0454     // Check type before language, since language is deprecated
0455     /*
0456         Mozilla 1.5 doesn't accept the text/javascript1.x formats, but WinIE 6 does.
0457         Mozilla 1.5 doesn't accept text/jscript, text/ecmascript, and text/livescript, but WinIE 6 does.
0458         Mozilla 1.5 accepts application/x-javascript, WinIE 6 doesn't.
0459         Mozilla 1.5 allows leading and trailing whitespace, but WinIE 6 doesn't.
0460         Mozilla 1.5 and WinIE 6 both accept the empty string, but neither accept a whitespace-only string.
0461         We want to accept all the values that either of these browsers accept, but not other values.
0462     */
0463     QString type = getAttribute(ATTR_TYPE).string().toLower();
0464 
0465     // Gecko accepts initial/trailing whitespace around the mimetype.
0466     // Whitespace only, however, musn't trigger execution.
0467     int length = type.length();
0468     type = type.trimmed();
0469     if (length)
0470         return !(type.compare("text/javascript") != 0 &&
0471                  type.compare("text/javascript1.0") != 0 &&
0472                  type.compare("text/javascript1.1") != 0 &&
0473                  type.compare("text/javascript1.2") != 0 &&
0474                  type.compare("text/javascript1.3") != 0 &&
0475                  type.compare("text/javascript1.4") != 0 &&
0476                  type.compare("text/javascript1.5") != 0 &&
0477                  type.compare("text/jscript") != 0 &&
0478                  type.compare("text/ecmascript") != 0 &&
0479                  type.compare("text/livescript") != 0 &&
0480                  type.compare("application/x-javascript") != 0 &&
0481                  type.compare("application/x-ecmascript") != 0 &&
0482                  type.compare("application/javascript") != 0 &&
0483                  type.compare("application/ecmascript") != 0);
0484 
0485     /*
0486         Mozilla 1.5 doesn't accept jscript or ecmascript, but WinIE 6 does.
0487         Mozilla 1.5 accepts javascript1.0, javascript1.4, and javascript1.5, but WinIE 6 accepts only 1.1 - 1.3.
0488         Neither Mozilla 1.5 nor WinIE 6 accept leading or trailing whitespace.
0489         We want to accept all the values that either of these browsers accept, but not other values.
0490     */
0491     QString lang = getAttribute(ATTR_LANGUAGE).string().toLower();
0492     if (!lang.isEmpty())
0493         return !(lang.compare("javascript") != 0 &&
0494                  lang.compare("javascript1.0") != 0 &&
0495                  lang.compare("javascript1.1") != 0 &&
0496                  lang.compare("javascript1.2") != 0 &&
0497                  lang.compare("javascript1.3") != 0 &&
0498                  lang.compare("javascript1.4") != 0 &&
0499                  lang.compare("javascript1.5") != 0 &&
0500                  lang.compare("ecmascript") != 0 &&
0501                  lang.compare("livescript") != 0 &&
0502                  lang.compare("jscript"));
0503 
0504     return true;
0505 }
0506 
0507 void HTMLScriptElementImpl::childrenChanged()
0508 {
0509     // If a node is inserted as a child of the script element
0510     // and the script element has been inserted in the document
0511     // we evaluate the script.
0512     if (!m_createdByParser && inDocument() && firstChild()) {
0513         evaluateScript(document()->URL().url(), text());
0514     }
0515 }
0516 
0517 void HTMLScriptElementImpl::loadFromUrl(const DOMString &url)
0518 {
0519     QString charset = getAttribute(ATTR_CHARSET).string();
0520     m_cachedScript = document()->docLoader()->requestScript(url, charset);
0521     if (m_cachedScript) {
0522         m_cachedScript->ref(this);
0523     }
0524 }
0525 
0526 void HTMLScriptElementImpl::insertedIntoDocument()
0527 {
0528     HTMLElementImpl::insertedIntoDocument();
0529 
0530     assert(!m_cachedScript);
0531 
0532     if (m_createdByParser) {
0533         return;
0534     }
0535 
0536     const DOMString url = getAttribute(ATTR_SRC).trimSpaces();
0537     if (!url.isEmpty()) {
0538         loadFromUrl(url);
0539         return;
0540     }
0541 
0542     // If there's an empty script node, we shouldn't evaluate the script
0543     // because if a script is inserted afterwards (by setting text or innerText)
0544     // it should be evaluated, and evaluateScript only evaluates a script once.
0545     DOMString scriptString = text();
0546     if (!scriptString.isEmpty()) {
0547         evaluateScript(document()->URL().url(), scriptString);
0548     }
0549 }
0550 
0551 void HTMLScriptElementImpl::removedFromDocument()
0552 {
0553     HTMLElementImpl::removedFromDocument();
0554 
0555     if (m_cachedScript) {
0556         m_cachedScript->deref(this);
0557         m_cachedScript = nullptr;
0558     }
0559 }
0560 
0561 void HTMLScriptElementImpl::notifyFinished(CachedObject *o)
0562 {
0563     CachedScript *cs = static_cast<CachedScript *>(o);
0564 
0565     assert(cs == m_cachedScript);
0566 
0567     QString   URL    = cs->url().string();
0568     DOMString script = cs->script();
0569     cs->deref(this);
0570     m_cachedScript = nullptr;
0571 
0572     ref(); // Pin so we don't destroy oursleves.
0573     if (!cs->hadError()) {
0574         evaluateScript(URL, script);
0575         dispatchHTMLEvent(EventImpl::LOAD_EVENT, false, false);
0576     }
0577     deref();
0578 }
0579 
0580 void HTMLScriptElementImpl::evaluateScript(const QString &URL, const DOMString &script)
0581 {
0582     if (m_evaluated || !isValidScript()) {
0583         return;
0584     }
0585 
0586     KHTMLPart *part = document()->part();
0587     if (part) {
0588         KJSProxy *proxy = KJSProxy::proxy(part);
0589         if (proxy) {
0590             m_evaluated = true;
0591             proxy->evaluate(URL, 0, script.string(), nullptr);
0592             DocumentImpl::updateDocumentsRendering();
0593         }
0594     }
0595 }
0596 
0597 DOMString HTMLScriptElementImpl::text() const
0598 {
0599     DOMString val = "";
0600 
0601     for (NodeImpl *n = firstChild(); n; n = n->nextSibling()) {
0602         if (n->isTextNode()) {
0603             val += static_cast<TextImpl *>(n)->data();
0604         }
0605     }
0606 
0607     return val;
0608 }
0609 
0610 void HTMLScriptElementImpl::setText(const DOMString &value)
0611 {
0612     int exceptioncode = 0;
0613     int numChildren = childNodeCount();
0614 
0615     if (numChildren == 1 && firstChild()->isTextNode()) {
0616         static_cast<DOM::TextImpl *>(firstChild())->setData(value, exceptioncode);
0617         return;
0618     }
0619 
0620     if (numChildren > 0) {
0621         removeChildren();
0622     }
0623 
0624     appendChild(document()->createTextNode(value.implementation()), exceptioncode);
0625 }
0626 
0627 DOMString HTMLScriptElementImpl::htmlFor() const
0628 {
0629     // DOM Level 1 says: reserved for future use.
0630     return DOMString();
0631 }
0632 
0633 void HTMLScriptElementImpl::setHtmlFor(const DOMString &/*value*/)
0634 {
0635     // DOM Level 1 says: reserved for future use.
0636 }
0637 
0638 DOMString HTMLScriptElementImpl::event() const
0639 {
0640     // DOM Level 1 says: reserved for future use.
0641     return DOMString();
0642 }
0643 
0644 void HTMLScriptElementImpl::setEvent(const DOMString &/*value*/)
0645 {
0646     // DOM Level 1 says: reserved for future use.
0647 }
0648 
0649 DOMString HTMLScriptElementImpl::charset() const
0650 {
0651     return getAttribute(ATTR_CHARSET);
0652 }
0653 
0654 void HTMLScriptElementImpl::setCharset(const DOMString &value)
0655 {
0656     setAttribute(ATTR_CHARSET, value);
0657 }
0658 
0659 bool HTMLScriptElementImpl::defer() const
0660 {
0661     return !getAttribute(ATTR_DEFER).isNull();
0662 }
0663 
0664 void HTMLScriptElementImpl::setDefer(bool defer)
0665 {
0666     setAttribute(ATTR_DEFER, defer ? "" : nullptr);
0667 }
0668 
0669 DOMString HTMLScriptElementImpl::src() const
0670 {
0671     return document()->completeURL(getAttribute(ATTR_SRC).trimSpaces().string());
0672 }
0673 
0674 void HTMLScriptElementImpl::setSrc(const DOMString &value)
0675 {
0676     setAttribute(ATTR_SRC, value);
0677 }
0678 
0679 DOMString HTMLScriptElementImpl::type() const
0680 {
0681     return getAttribute(ATTR_TYPE);
0682 }
0683 
0684 void HTMLScriptElementImpl::setType(const DOMString &value)
0685 {
0686     setAttribute(ATTR_TYPE, value);
0687 }
0688 
0689 // -------------------------------------------------------------------------
0690 
0691 HTMLStyleElementImpl::~HTMLStyleElementImpl()
0692 {
0693     if (m_sheet) {
0694         m_sheet->deref();
0695     }
0696 }
0697 
0698 NodeImpl::Id HTMLStyleElementImpl::id() const
0699 {
0700     return ID_STYLE;
0701 }
0702 
0703 // other stuff...
0704 void HTMLStyleElementImpl::parseAttribute(AttributeImpl *attr)
0705 {
0706     switch (attr->id()) {
0707     case ATTR_TYPE:
0708         m_type = attr->value().lower();
0709         break;
0710     case ATTR_MEDIA:
0711         m_media = attr->value().string().toLower();
0712         break;
0713     case ATTR_TITLE:
0714         if (m_sheet) {
0715             m_sheet->setTitle(attr->value());
0716         }
0717         break;
0718     default:
0719         HTMLElementImpl::parseAttribute(attr);
0720     }
0721 }
0722 
0723 void HTMLStyleElementImpl::insertedIntoDocument()
0724 {
0725     HTMLElementImpl::insertedIntoDocument();
0726 
0727     // If we're empty, we have to call parseText here, since we won't get childrenChanged();
0728     // but we still want a CSSOM object
0729     if (!firstChild()) {
0730         parseText();
0731     }
0732 
0733     if (m_sheet) {
0734         document()->updateStyleSelector();
0735     }
0736 }
0737 
0738 void HTMLStyleElementImpl::removedFromDocument()
0739 {
0740     HTMLElementImpl::removedFromDocument();
0741     if (m_sheet) {
0742         document()->updateStyleSelector();
0743     }
0744 }
0745 
0746 void HTMLStyleElementImpl::childrenChanged()
0747 {
0748     HTMLElementImpl::childrenChanged();
0749 
0750     parseText();
0751 }
0752 
0753 void HTMLStyleElementImpl::parseText()
0754 {
0755     DOMString text = "";
0756 
0757     for (NodeImpl *c = firstChild(); c != nullptr; c = c->nextSibling()) {
0758         if ((c->nodeType() == Node::TEXT_NODE) ||
0759                 (c->nodeType() == Node::CDATA_SECTION_NODE) ||
0760                 (c->nodeType() == Node::COMMENT_NODE)) {
0761             text += c->nodeValue();
0762         }
0763     }
0764 
0765     if (m_sheet) {
0766         m_sheet->deref();
0767         m_sheet = nullptr;
0768     }
0769 
0770     m_loading = false;
0771     if (m_type.isEmpty() || m_type == "text/css") { // Type must be empty or CSS
0772         MediaListImpl *media = new MediaListImpl((CSSStyleSheetImpl *)nullptr, m_media, true);
0773         media->ref();
0774         khtml::MediaQueryEvaluator screenEval("screen", true);
0775         khtml::MediaQueryEvaluator printEval("print", true);
0776         if (screenEval.eval(media) || printEval.eval(media)) {
0777             document()->addPendingSheet();
0778             m_loading = true;
0779             m_sheet = new CSSStyleSheetImpl(this);
0780             m_sheet->ref();
0781             m_sheet->parseString(text, !document()->inCompatMode());
0782             m_sheet->setMedia(media);
0783             m_sheet->setTitle(getAttribute(ATTR_TITLE));
0784             m_loading = false;
0785         }
0786         media->deref();
0787     }
0788 
0789     if (!isLoading() && m_sheet) {
0790         document()->styleSheetLoaded();
0791     }
0792 }
0793 
0794 bool HTMLStyleElementImpl::isLoading() const
0795 {
0796     if (m_loading) {
0797         return true;
0798     }
0799     if (!m_sheet) {
0800         return false;
0801     }
0802     return static_cast<CSSStyleSheetImpl *>(m_sheet)->isLoading();
0803 }
0804 
0805 bool HTMLStyleElementImpl::checkRemovePendingSheet()
0806 {
0807     if (!isLoading()) {
0808         document()->styleSheetLoaded();
0809         return true;
0810     }
0811     return false;
0812 }
0813 
0814 bool HTMLStyleElementImpl::checkAddPendingSheet()
0815 {
0816     if (!isLoading()) {
0817         document()->addPendingSheet();
0818         return true;
0819     }
0820     return false;
0821 }
0822 
0823 // -------------------------------------------------------------------------
0824 
0825 NodeImpl::Id HTMLTitleElementImpl::id() const
0826 {
0827     return ID_TITLE;
0828 }
0829 
0830 void HTMLTitleElementImpl::childrenChanged()
0831 {
0832     HTMLElementImpl::childrenChanged();
0833 
0834     m_title = "";
0835     for (NodeImpl *c = firstChild(); c != nullptr; c = c->nextSibling()) {
0836         if ((c->nodeType() == Node::TEXT_NODE) || (c->nodeType() == Node::CDATA_SECTION_NODE)) {
0837             m_title += c->nodeValue();
0838         }
0839     }
0840     if (!m_title.isEmpty() && inDocument()) {
0841         document()->setTitle(m_title);
0842     }
0843 }
0844 
0845 DOMString HTMLTitleElementImpl::text()
0846 {
0847     if (firstChild() && firstChild()->nodeType() == Node::TEXT_NODE) {
0848         return firstChild()->nodeValue();
0849     }
0850     return "";
0851 }
0852 
0853 void HTMLTitleElementImpl::setText(const DOMString &str)
0854 {
0855     int exceptioncode = 0;
0856     RefPtr<DOM::NodeListImpl> nl = childNodes();
0857     const unsigned int length = nl->length();
0858     for (unsigned int i = 0; i < length; ++i) {
0859         if (nl->item(i)->nodeType() == DOM::Node::TEXT_NODE) {
0860             static_cast<DOM::TextImpl *>(nl->item(i))->setData(str, exceptioncode);
0861             return;
0862         }
0863     }
0864 
0865     // No child text node found, creating one
0866     DOM::TextImpl *t = document()->createTextNode(str.implementation());
0867     appendChild(t, exceptioncode);
0868 }