2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2001 Dirk Mueller (mueller@kde.org)
7 * Copyright (C) 2003 Apple Computer, Inc.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
24 // -------------------------------------------------------------------------
26 #include "html/html_headimpl.h"
27 #include "html/html_documentimpl.h"
28 #include "xml/dom_textimpl.h"
30 #include "khtmlview.h"
31 #include "khtml_part.h"
32 #include "kjs_proxy.h"
34 #include "misc/htmlhashes.h"
35 #include "misc/loader.h"
36 #include "misc/helper.h"
38 #include "css/cssstyleselector.h"
39 #include "css/css_stylesheetimpl.h"
40 #include "css/csshelper.h"
46 using namespace khtml;
48 HTMLBaseElementImpl::HTMLBaseElementImpl(DocumentPtr *doc)
49 : HTMLElementImpl(doc)
53 HTMLBaseElementImpl::~HTMLBaseElementImpl()
57 NodeImpl::Id HTMLBaseElementImpl::id() const
62 void HTMLBaseElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
67 m_href = khtml::parseURL(attr->value());
71 m_target = attr->value();
75 HTMLElementImpl::parseHTMLAttribute(attr);
79 void HTMLBaseElementImpl::insertedIntoDocument()
81 HTMLElementImpl::insertedIntoDocument();
85 void HTMLBaseElementImpl::removedFromDocument()
87 HTMLElementImpl::removedFromDocument();
89 // Since the document doesn't have a base element...
90 // (This will break in the case of multiple base elements, but that's not valid anyway (?))
91 getDocument()->setBaseURL( QString::null );
92 getDocument()->setBaseTarget( QString::null );
95 void HTMLBaseElementImpl::process()
100 if(!m_href.isEmpty() && getDocument()->part())
101 getDocument()->setBaseURL( KURL( getDocument()->part()->url(), m_href.string() ).url() );
103 if(!m_target.isEmpty())
104 getDocument()->setBaseTarget( m_target.string() );
106 // ### should changing a document's base URL dynamically automatically update all images, stylesheets etc?
109 // -------------------------------------------------------------------------
111 HTMLLinkElementImpl::HTMLLinkElementImpl(DocumentPtr *doc)
112 : HTMLElementImpl(doc)
117 m_isStyleSheet = m_isIcon = m_alternate = false;
121 HTMLLinkElementImpl::~HTMLLinkElementImpl()
123 if(m_sheet) m_sheet->deref();
124 if(m_cachedSheet) m_cachedSheet->deref(this);
127 NodeImpl::Id HTMLLinkElementImpl::id() const
132 void HTMLLinkElementImpl::setDisabledState(bool _disabled)
134 int oldDisabledState = m_disabledState;
135 m_disabledState = _disabled ? 2 : 1;
136 if (oldDisabledState != m_disabledState) {
137 // If we change the disabled state while the sheet is still loading, then we have to
138 // perform three checks:
140 // Check #1: If the sheet becomes disabled while it was loading, and if it was either
141 // a main sheet or a sheet that was previously enabled via script, then we need
142 // to remove it from the list of pending sheets.
143 if (m_disabledState == 2 && (!m_alternate || oldDisabledState == 1))
144 getDocument()->stylesheetLoaded();
146 // Check #2: An alternate sheet becomes enabled while it is still loading.
147 if (m_alternate && m_disabledState == 1)
148 getDocument()->addPendingSheet();
150 // Check #3: A main sheet becomes enabled while it was still loading and
151 // after it was disabled via script. It takes really terrible code to make this
152 // happen (a double toggle for no reason essentially). This happens on
153 // virtualplastic.net, which manages to do about 12 enable/disables on only 3
155 if (!m_alternate && m_disabledState == 1 && oldDisabledState == 2)
156 getDocument()->addPendingSheet();
158 // If the sheet is already loading just bail.
162 // Load the sheet, since it's never been loaded before.
163 if (!m_sheet && m_disabledState == 1)
166 getDocument()->updateStyleSelector(); // Update the style selector.
170 void HTMLLinkElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
175 tokenizeRelAttribute(attr->value());
179 m_url = getDocument()->completeURL( khtml::parseURL(attr->value()).string() );
183 m_type = attr->value();
187 m_media = attr->value().string().lower();
191 setDisabledState(!attr->isNull());
194 HTMLElementImpl::parseHTMLAttribute(attr);
198 void HTMLLinkElementImpl::tokenizeRelAttribute(const AtomicString& relStr)
200 m_isStyleSheet = m_isIcon = m_alternate = false;
201 QString rel = relStr.string().lower();
202 if (rel == "stylesheet")
203 m_isStyleSheet = true;
204 else if (rel == "icon" || rel == "shortcut icon")
206 else if (rel == "alternate stylesheet" || rel == "stylesheet alternate")
207 m_isStyleSheet = m_alternate = true;
209 // Tokenize the rel attribute and set bits based on specific keywords that we find.
210 rel.replace('\n', ' ');
211 QStringList list = QStringList::split(' ', rel);
212 for (QStringList::Iterator i = list.begin(); i != list.end(); ++i) {
213 if (*i == "stylesheet")
214 m_isStyleSheet = true;
215 else if (*i == "alternate")
217 else if (*i == "icon")
223 void HTMLLinkElementImpl::process()
228 QString type = m_type.string().lower();
230 KHTMLPart* part = getDocument()->part();
232 // IE extension: location of small icon for locationbar / bookmarks
233 if (part && m_isIcon && !m_url.isEmpty() && !part->parentPart()) {
234 if (!type.isEmpty()) // Mozilla extension to IE extension: icon specified with type
235 part->browserExtension()->setTypedIconURL(KURL(m_url.string()), type);
237 part->browserExtension()->setIconURL(KURL(m_url.string()));
241 // This was buggy and would incorrectly match <link rel="alternate">, which has a different specified meaning. -dwh
242 if (m_disabledState != 2 && (type.contains("text/css") || m_isStyleSheet) && getDocument()->part()) {
243 // no need to load style sheets which aren't for the screen output
244 // ### there may be in some situations e.g. for an editor or script to manipulate
245 // also, don't load style sheets for standalone documents
246 if (m_media.isNull() || m_media.contains("screen") || m_media.contains("all") || m_media.contains("print")) {
249 // Add ourselves as a pending sheet, but only if we aren't an alternate
250 // stylesheet. Alternate stylesheets don't hold up render tree construction.
252 getDocument()->addPendingSheet();
254 QString chset = getAttribute( ATTR_CHARSET ).string();
256 m_cachedSheet->deref(this);
257 m_cachedSheet = getDocument()->docLoader()->requestStyleSheet(m_url, chset);
259 m_cachedSheet->ref(this);
263 // we no longer contain a stylesheet, e.g. perhaps rel or type was changed
266 getDocument()->updateStyleSelector();
270 void HTMLLinkElementImpl::insertedIntoDocument()
272 HTMLElementImpl::insertedIntoDocument();
276 void HTMLLinkElementImpl::removedFromDocument()
278 HTMLElementImpl::removedFromDocument();
282 void HTMLLinkElementImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheetStr)
284 // kdDebug( 6030 ) << "HTMLLinkElement::setStyleSheet()" << endl;
285 // kdDebug( 6030 ) << "**** current medium: " << m_media << endl;
289 m_sheet = new CSSStyleSheetImpl(this, url);
290 kdDebug( 6030 ) << "style sheet parse mode strict = " << ( !getDocument()->inCompatMode() ) << endl;
292 m_sheet->parseString( sheetStr, !getDocument()->inCompatMode() );
294 MediaListImpl *media = new MediaListImpl( m_sheet, m_media );
295 m_sheet->setMedia( media );
299 // Tell the doc about the sheet.
300 if (!isLoading() && m_sheet && !isDisabled() && !isAlternate())
301 getDocument()->stylesheetLoaded();
304 bool HTMLLinkElementImpl::isLoading() const
306 // kdDebug( 6030 ) << "link: checking if loading!" << endl;
307 if(m_loading) return true;
308 if(!m_sheet) return false;
309 //if(!m_sheet->isCSSStyleSheet()) return false;
310 return static_cast<CSSStyleSheetImpl *>(m_sheet)->isLoading();
313 void HTMLLinkElementImpl::sheetLoaded()
315 if (!isLoading() && !isDisabled() && !isAlternate())
316 getDocument()->stylesheetLoaded();
319 bool HTMLLinkElementImpl::isURLAttribute(AttributeImpl *attr) const
321 return attr->id() == ATTR_HREF;
324 // -------------------------------------------------------------------------
326 HTMLMetaElementImpl::HTMLMetaElementImpl(DocumentPtr *doc) : HTMLElementImpl(doc)
330 HTMLMetaElementImpl::~HTMLMetaElementImpl()
334 NodeImpl::Id HTMLMetaElementImpl::id() const
339 void HTMLMetaElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
343 case ATTR_HTTP_EQUIV:
344 m_equiv = attr->value();
348 m_content = attr->value();
354 HTMLElementImpl::parseHTMLAttribute(attr);
358 void HTMLMetaElementImpl::insertedIntoDocument()
360 HTMLElementImpl::insertedIntoDocument();
364 void HTMLMetaElementImpl::process()
366 // Get the document to process the tag, but only if we're actually part of DOM tree (changing a meta tag while
367 // it's not in the tree shouldn't have any effect on the document)
368 if (inDocument() && !m_equiv.isNull() && !m_content.isNull())
369 getDocument()->processHttpEquiv(m_equiv,m_content);
372 // -------------------------------------------------------------------------
374 HTMLScriptElementImpl::HTMLScriptElementImpl(DocumentPtr *doc)
375 : HTMLElementImpl(doc), m_cachedScript(0), m_createdByParser(false)
379 HTMLScriptElementImpl::~HTMLScriptElementImpl()
382 m_cachedScript->deref(this);
385 void HTMLScriptElementImpl::insertedIntoDocument()
387 HTMLElementImpl::insertedIntoDocument();
389 assert(!m_cachedScript);
391 if (m_createdByParser)
394 QString url = getAttribute(ATTR_SRC).string();
395 if (!url.isEmpty()) {
396 QString charset = getAttribute(ATTR_CHARSET).string();
397 m_cachedScript = getDocument()->docLoader()->requestScript(DOMString(url), charset);
398 m_cachedScript->ref(this);
402 DOMString scriptString = "";
403 for (NodeImpl *n = firstChild(); n; n = n->nextSibling())
405 scriptString += static_cast<TextImpl*>(n)->data();
407 DocumentImpl *doc = getDocument();
408 KHTMLPart *part = doc->part();
411 KJSProxy *proxy = KJSProxy::proxy(part);
415 proxy->evaluate(doc->URL(), 0, scriptString.string(), Node());
416 DocumentImpl::updateDocumentsRendering();
419 void HTMLScriptElementImpl::removedFromDocument()
421 HTMLElementImpl::removedFromDocument();
423 if (m_cachedScript) {
424 m_cachedScript->deref(this);
429 void HTMLScriptElementImpl::notifyFinished(CachedObject* o)
431 CachedScript *cs = static_cast<CachedScript *>(o);
433 assert(cs == m_cachedScript);
435 KHTMLPart *part = getDocument()->part();
437 KJSProxy *proxy = KJSProxy::proxy(part);
439 proxy->evaluate(cs->url().string(), 0, cs->script().string(), Node());
440 DocumentImpl::updateDocumentsRendering();
448 NodeImpl::Id HTMLScriptElementImpl::id() const
453 bool HTMLScriptElementImpl::isURLAttribute(AttributeImpl *attr) const
455 return attr->id() == ATTR_SRC;
458 // -------------------------------------------------------------------------
460 HTMLStyleElementImpl::HTMLStyleElementImpl(DocumentPtr *doc) : HTMLElementImpl(doc)
466 HTMLStyleElementImpl::~HTMLStyleElementImpl()
468 if(m_sheet) m_sheet->deref();
471 NodeImpl::Id HTMLStyleElementImpl::id() const
477 void HTMLStyleElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
482 m_type = attr->value().domString().lower();
485 m_media = attr->value().string().lower();
488 HTMLElementImpl::parseHTMLAttribute(attr);
492 void HTMLStyleElementImpl::insertedIntoDocument()
494 HTMLElementImpl::insertedIntoDocument();
496 getDocument()->updateStyleSelector();
499 void HTMLStyleElementImpl::removedFromDocument()
501 HTMLElementImpl::removedFromDocument();
503 getDocument()->updateStyleSelector();
506 void HTMLStyleElementImpl::childrenChanged()
510 for (NodeImpl *c = firstChild(); c != 0; c = c->nextSibling()) {
511 if ((c->nodeType() == Node::TEXT_NODE) ||
512 (c->nodeType() == Node::CDATA_SECTION_NODE) ||
513 (c->nodeType() == Node::COMMENT_NODE))
514 text += c->nodeValue();
518 if (static_cast<CSSStyleSheetImpl *>(m_sheet)->isLoading())
519 getDocument()->stylesheetLoaded(); // Remove ourselves from the sheet list.
525 if ((m_type.isEmpty() || m_type == "text/css") // Type must be empty or CSS
526 && (m_media.isNull() || m_media.contains("screen") || m_media.contains("all") || m_media.contains("print"))) {
527 getDocument()->addPendingSheet();
529 m_sheet = new CSSStyleSheetImpl(this);
531 m_sheet->parseString( text, !getDocument()->inCompatMode() );
532 MediaListImpl *media = new MediaListImpl( m_sheet, m_media );
533 m_sheet->setMedia( media );
537 if (!isLoading() && m_sheet)
538 getDocument()->stylesheetLoaded();
541 bool HTMLStyleElementImpl::isLoading() const
543 if (m_loading) return true;
544 if(!m_sheet) return false;
545 return static_cast<CSSStyleSheetImpl *>(m_sheet)->isLoading();
548 void HTMLStyleElementImpl::sheetLoaded()
551 getDocument()->stylesheetLoaded();
554 // -------------------------------------------------------------------------
556 HTMLTitleElementImpl::HTMLTitleElementImpl(DocumentPtr *doc)
557 : HTMLElementImpl(doc)
561 HTMLTitleElementImpl::~HTMLTitleElementImpl()
565 NodeImpl::Id HTMLTitleElementImpl::id() const
570 void HTMLTitleElementImpl::insertedIntoDocument()
572 HTMLElementImpl::insertedIntoDocument();
574 // Only allow title to be set by first <title> encountered.
575 if (getDocument()->title().isEmpty())
576 getDocument()->setTitle(m_title);
578 getDocument()->setTitle(m_title);
582 void HTMLTitleElementImpl::removedFromDocument()
584 HTMLElementImpl::removedFromDocument();
585 // Title element removed, so we have no title... we ignore the case of multiple title elements, as it's invalid
587 getDocument()->setTitle(DOMString());
590 void HTMLTitleElementImpl::childrenChanged()
592 HTMLElementImpl::childrenChanged();
594 for (NodeImpl *c = firstChild(); c != 0; c = c->nextSibling()) {
595 if ((c->nodeType() == Node::TEXT_NODE) || (c->nodeType() == Node::CDATA_SECTION_NODE))
596 m_title += c->nodeValue();
599 // Only allow title to be set by first <title> encountered.
600 if (inDocument() && getDocument()->title().isEmpty())
604 getDocument()->setTitle(m_title);