/** * This file is part of the DOM implementation for KDE. * * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) * (C) 2002-2006 Apple Computer, Inc. * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "dom/dom_exception.h" #include "xml/dom_textimpl.h" #include "xml/dom_xmlimpl.h" #include "xml/dom2_rangeimpl.h" #include "xml/dom2_eventsimpl.h" #include "xml/xml_tokenizer.h" #include "html/htmltokenizer.h" #include "xml/dom_restyler.h" #include "css/csshelper.h" #include "css/cssstyleselector.h" #include "css/css_stylesheetimpl.h" #include "misc/htmlhashes.h" #include "misc/helper.h" #include "misc/seed.h" #include "misc/loader.h" #include "ecma/kjs_proxy.h" #include "ecma/kjs_binding.h" #include #include #include #include #include #include "rendering/counter_tree.h" #include "rendering/render_canvas.h" #include "rendering/render_replaced.h" #include "rendering/render_arena.h" #include "rendering/render_layer.h" #include "rendering/render_frames.h" #include "rendering/render_image.h" #include "khtmlview.h" #include "khtml_part.h" #include #include #include #include "khtml_settings.h" #include "khtmlpart_p.h" #include "html/html_baseimpl.h" #include "html/html_blockimpl.h" #include "html/html_documentimpl.h" #include "html/html_formimpl.h" #include "html/html_headimpl.h" #include "html/html_imageimpl.h" #include "html/html_listimpl.h" #include "html/html_miscimpl.h" #include "html/html_tableimpl.h" #include "html/html_objectimpl.h" #include #include #include #include "dom_docimpl.h" using namespace DOM; using namespace khtml; // ------------------------------------------------------------------------ DOMImplementationImpl *DOMImplementationImpl::m_instance = 0; DOMImplementationImpl::DOMImplementationImpl() { } DOMImplementationImpl::~DOMImplementationImpl() { } bool DOMImplementationImpl::hasFeature ( const DOMString &feature, const DOMString &version ) { // ### update when we (fully) support the relevant features TQString lower = feature.string().lower(); if ((lower == "html" || lower == "xml") && (version.isEmpty() || version == "1.0" || version == "2.0" || version == "null")) return true; // ## Do we support Core Level 3 ? if ((lower == "core" ) && (version.isEmpty() || version == "2.0" || version == "null")) return true; if ((lower == "events" || lower == "uievents" || lower == "mouseevents" || lower == "mutationevents" || lower == "htmlevents" || lower == "textevents" ) && (version.isEmpty() || version == "2.0" || version == "3.0" || version == "null")) return true; return false; } DocumentTypeImpl *DOMImplementationImpl::createDocumentType( const DOMString &qualifiedName, const DOMString &publicId, const DOMString &systemId, int &exceptioncode ) { // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied if (qualifiedName.isNull()) { exceptioncode = DOMException::NAMESPACE_ERR; return 0; } // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character. if (!Element::khtmlValidQualifiedName(qualifiedName)) { exceptioncode = DOMException::INVALID_CHARACTER_ERR; return 0; } // NAMESPACE_ERR: Raised if the qualifiedName is malformed. // Added special case for the empty string, which seems to be a common pre-DOM2 misuse if (!qualifiedName.isEmpty() && Element::khtmlMalformedQualifiedName(qualifiedName)) { exceptioncode = DOMException::NAMESPACE_ERR; return 0; } return new DocumentTypeImpl(this,0,qualifiedName,publicId,systemId); } DOMImplementationImpl* DOMImplementationImpl::getInterface(const DOMString& /*feature*/) const { // ### return 0; } DocumentImpl *DOMImplementationImpl::createDocument( const DOMString &namespaceURI, const DOMString &qualifiedName, const DocumentType &doctype, int &exceptioncode ) { exceptioncode = 0; if (!checkQualifiedName(qualifiedName, namespaceURI, 0, true/*nameCanBeNull*/, true /*nameCanBeEmpty, see #61650*/, &exceptioncode) ) return 0; DocumentTypeImpl *dtype = static_cast(doctype.handle()); // WRONG_DOCUMENT_ERR: Raised if doctype has already been used with a different document or was // created from a different implementation. if (dtype && (dtype->getDocument() || dtype->implementation() != this)) { exceptioncode = DOMException::WRONG_DOCUMENT_ERR; return 0; } // ### this is completely broken.. without a view it will not work (Dirk) DocumentImpl *doc = new DocumentImpl(this, 0); // now get the interesting parts of the doctype // ### create new one if not there (currently always there) if (doc->doctype() && dtype) doc->doctype()->copyFrom(*dtype); // the document must be created empty if all parameters are null // (or empty for qName/nsURI as a tolerance) - see DOM 3 Core. if (dtype || !qualifiedName.isEmpty() || !namespaceURI.isEmpty()) { ElementImpl *element = doc->createElementNS(namespaceURI,qualifiedName); doc->appendChild(element,exceptioncode); if (exceptioncode) { delete element; delete doc; return 0; } } return doc; } CSSStyleSheetImpl *DOMImplementationImpl::createCSSStyleSheet(DOMStringImpl* /*title*/, DOMStringImpl *media, int &/*exceptioncode*/) { // ### TODO : title should be set, and media could have wrong syntax, in which case we should // generate an exception. CSSStyleSheetImpl *parent = 0L; CSSStyleSheetImpl *sheet = new CSSStyleSheetImpl(parent, DOMString()); sheet->setMedia(new MediaListImpl(sheet, media)); return sheet; } DocumentImpl *DOMImplementationImpl::createDocument( KHTMLView *v ) { return new DocumentImpl(this, v); } HTMLDocumentImpl *DOMImplementationImpl::createHTMLDocument( KHTMLView *v ) { return new HTMLDocumentImpl(this, v); } DOMImplementationImpl *DOMImplementationImpl::instance() { if (!m_instance) { m_instance = new DOMImplementationImpl(); m_instance->ref(); } return m_instance; } // ------------------------------------------------------------------------ ElementMappingCache::ElementMappingCache():m_dict(257) { m_dict.setAutoDelete(true); } void ElementMappingCache::add(const TQString& id, ElementImpl* nd) { if (id.isEmpty()) return; ItemInfo* info = m_dict.find(id); if (info) { info->ref++; info->nd = 0; //Now ambigous } else { ItemInfo* info = new ItemInfo(); info->ref = 1; info->nd = nd; m_dict.insert(id, info); } } void ElementMappingCache::set(const TQString& id, ElementImpl* nd) { if (id.isEmpty()) return; ItemInfo* info = m_dict.find(id); info->nd = nd; } void ElementMappingCache::remove(const TQString& id, ElementImpl* nd) { if (id.isEmpty()) return; ItemInfo* info = m_dict.find(id); info->ref--; if (info->ref == 0) { m_dict.take(id); delete info; } else { if (info->nd == nd) info->nd = 0; } } bool ElementMappingCache::contains(const TQString& id) { if (id.isEmpty()) return false; return m_dict.find(id); } ElementMappingCache::ItemInfo* ElementMappingCache::get(const TQString& id) { if (id.isEmpty()) return 0; return m_dict.find(id); } static KStaticDeleter< TQPtrList > s_changedDocumentsDeleter; TQPtrList * DocumentImpl::changedDocuments; // KHTMLView might be 0 DocumentImpl::DocumentImpl(DOMImplementationImpl *_implementation, KHTMLView *v) : NodeBaseImpl( 0 ), m_domtree_version(0), m_counterDict(257), m_imageLoadEventTimer(0) { m_document.resetSkippingRef(this); //Make getDocument return us.. m_selfOnlyRefCount = 0; m_paintDeviceMetrics = 0; m_paintDevice = 0; m_decoderMibEnum = 0; m_textColor = Qt::black; m_view = v; m_renderArena.reset(); KHTMLFactory::ref(); if ( v ) { m_docLoader = new DocLoader(v->part(), this ); setPaintDevice( TQT_TQPAINTDEVICE(m_view) ); } else m_docLoader = new DocLoader( 0, this ); visuallyOrdered = false; m_bParsing = false; m_docChanged = false; m_elemSheet = 0; m_tokenizer = 0; // ### this should be created during parsing a // not during construction. Not sure who added that and why (Dirk) m_doctype = new DocumentTypeImpl(_implementation, getDocument(), DOMString() /* qualifiedName */, DOMString() /* publicId */, DOMString() /* systemId */); m_doctype->ref(); m_implementation = _implementation; m_implementation->ref(); pMode = Strict; hMode = XHtml; m_textColor = "#000000"; m_attrMap = new IdNameMapping(ATTR_LAST_ATTR+1); m_elementMap = new IdNameMapping(ID_LAST_TAG+1); m_namespaceMap = new IdNameMapping(1); TQString xhtml(XHTML_NAMESPACE); m_namespaceMap->names.insert(emptyNamespace, new DOMStringImpl("")); m_namespaceMap->names.insert(xhtmlNamespace, new DOMStringImpl(xhtml.unicode(), xhtml.length())); m_namespaceMap->names[emptyNamespace]->ref(); m_namespaceMap->names[xhtmlNamespace]->ref(); m_namespaceMap->count+=2; m_focusNode = 0; m_hoverNode = 0; m_activeNode = 0; m_defaultView = new AbstractViewImpl(this); m_defaultView->ref(); m_listenerTypes = 0; m_styleSheets = new StyleSheetListImpl; m_styleSheets->ref(); m_addedStyleSheets = 0; m_inDocument = true; m_styleSelectorDirty = false; m_styleSelector = 0; m_counterDict.setAutoDelete(true); m_inStyleRecalc = false; m_pendingStylesheets = 0; m_ignorePendingStylesheets = false; m_async = true; m_hadLoadError = false; m_docLoading = false; m_inSyncLoad = false; m_loadingXMLDoc = 0; m_cssTarget = 0; m_dynamicDomRestyler = new khtml::DynamicDomRestyler(); } void DocumentImpl::removedLastRef() { if (m_selfOnlyRefCount) { /* In this case, the only references to us are from children, so we have a cycle. We'll try to break it by disconnecting the children from us; this sucks/is wrong, but it's pretty much the best we can do without tracing. Of course, if dumping the children causes the refcount from them to drop to 0 we can get killed right here, so better hold a temporary reference, too */ DocPtr guard(this); // we must make sure not to be retaining any of our children through // these extra pointers or we will create a reference cycle if (m_doctype) { m_doctype->deref(); m_doctype = 0; } if (m_cssTarget) { m_cssTarget->deref(); m_cssTarget = 0; } if (m_focusNode) { m_focusNode->deref(); m_focusNode = 0; } if (m_hoverNode) { m_hoverNode->deref(); m_hoverNode = 0; } if (m_activeNode) { m_activeNode->deref(); m_activeNode = 0; } removeChildren(); delete m_tokenizer; m_tokenizer = 0; } else { delete this; } } DocumentImpl::~DocumentImpl() { //Important: if you need to remove stuff here, //you may also have to fix removedLastRef() above - M.O. assert( !m_render ); TQIntDictIterator it(m_nodeListCache); for (; it.current(); ++it) it.current()->deref(); if (m_loadingXMLDoc) m_loadingXMLDoc->deref(this); if (changedDocuments && m_docChanged) changedDocuments->remove(this); delete m_tokenizer; m_document.resetSkippingRef(0); delete m_styleSelector; delete m_docLoader; if (m_elemSheet ) m_elemSheet->deref(); if (m_doctype) m_doctype->deref(); m_implementation->deref(); delete m_paintDeviceMetrics; delete m_elementMap; delete m_attrMap; delete m_namespaceMap; delete m_dynamicDomRestyler; m_defaultView->deref(); m_styleSheets->deref(); if (m_addedStyleSheets) m_addedStyleSheets->deref(); if (m_cssTarget) m_cssTarget->deref(); if (m_focusNode) m_focusNode->deref(); if ( m_hoverNode ) m_hoverNode->deref(); if (m_activeNode) m_activeNode->deref(); m_renderArena.reset(); KHTMLFactory::deref(); } DocumentTypeImpl *DocumentImpl::doctype() const { return m_doctype; } DOMImplementationImpl *DocumentImpl::implementation() const { return m_implementation; } ElementImpl *DocumentImpl::documentElement() const { NodeImpl *n = firstChild(); while (n && n->nodeType() != Node::ELEMENT_NODE) n = n->nextSibling(); return static_cast(n); } ElementImpl *DocumentImpl::createElement( const DOMString &name, int* pExceptioncode ) { Id id = getId( NodeImpl::ElementId, name.implementation(), false /* allocate */, false /*HTMLDocumentImpl::createElement looked for HTML elements already*/, pExceptioncode); if ( pExceptioncode && *pExceptioncode ) return 0; XMLElementImpl* e = new XMLElementImpl( getDocument(), id ); e->setHTMLCompat( htmlMode() != XHtml ); // Not a real HTML element, but inside an html-compat doc all tags are uppercase. return e; } AttrImpl *DocumentImpl::createAttribute( const DOMString &tagName, int* pExceptioncode ) { Id id = getId( NodeImpl::AttributeId, tagName.implementation(), false /* allocate */, isHTMLDocument(), pExceptioncode); if ( pExceptioncode && *pExceptioncode ) return 0; AttrImpl* attr = new AttrImpl( 0, getDocument(), id, DOMString("").implementation()); attr->setHTMLCompat( htmlMode() != XHtml ); return attr; } DocumentFragmentImpl *DocumentImpl::createDocumentFragment( ) { return new DocumentFragmentImpl( docPtr() ); } CommentImpl *DocumentImpl::createComment ( DOMStringImpl* data ) { return new CommentImpl( docPtr(), data ); } CDATASectionImpl *DocumentImpl::createCDATASection ( DOMStringImpl* data ) { return new CDATASectionImpl( docPtr(), data ); } ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction ( const DOMString &target, DOMStringImpl* data ) { return new ProcessingInstructionImpl( docPtr(),target,data); } EntityReferenceImpl *DocumentImpl::createEntityReference ( const DOMString &name ) { return new EntityReferenceImpl(docPtr(), name.implementation()); } NodeImpl *DocumentImpl::importNode(NodeImpl *importedNode, bool deep, int &exceptioncode) { NodeImpl *result = 0; // Not mentioned in spec: throw NOT_FOUND_ERR if evt is null if (!importedNode) { exceptioncode = DOMException::NOT_FOUND_ERR; return 0; } if(importedNode->nodeType() == Node::ELEMENT_NODE) { // Why not use cloneNode? ElementImpl *otherElem = static_cast(importedNode); NamedAttrMapImpl *otherMap = static_cast(importedNode)->attributes(true); ElementImpl *tempElementImpl; if (!importedNode->localName().isNull()) tempElementImpl = createElementNS(otherElem->namespaceURI(),otherElem->nodeName()); else tempElementImpl = createElement(otherElem->nodeName()); result = tempElementImpl; if(otherMap) { for(unsigned long i = 0; i < otherMap->length(); i++) { AttrImpl *otherAttr = otherMap->attrAt(i)->createAttr(otherElem,otherElem->docPtr()); if (!otherAttr->localName().isNull()) { // attr was created via createElementNS() tempElementImpl->setAttributeNS(otherAttr->namespaceURI(), otherAttr->name(), otherAttr->nodeValue(), exceptioncode); } else { // attr was created via createElement() tempElementImpl->setAttribute(otherAttr->id(), otherAttr->nodeValue(), otherAttr->name(), exceptioncode); } if(exceptioncode != 0) break; // ### properly cleanup here } } } else if(importedNode->nodeType() == Node::TEXT_NODE) { result = createTextNode(static_cast(importedNode)->string()); deep = false; } else if(importedNode->nodeType() == Node::CDATA_SECTION_NODE) { result = createCDATASection(static_cast(importedNode)->string()); deep = false; } else if(importedNode->nodeType() == Node::ENTITY_REFERENCE_NODE) result = createEntityReference(importedNode->nodeName()); else if(importedNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { result = createProcessingInstruction(importedNode->nodeName(), importedNode->nodeValue().implementation()); deep = false; } else if(importedNode->nodeType() == Node::COMMENT_NODE) { result = createComment(static_cast(importedNode)->string()); deep = false; } else if (importedNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) result = createDocumentFragment(); else exceptioncode = DOMException::NOT_SUPPORTED_ERR; //### FIXME: This should handle Attributes, and a few other things if(deep && result) { for(Node n = importedNode->firstChild(); !n.isNull(); n = n.nextSibling()) result->appendChild(importNode(n.handle(), true, exceptioncode), exceptioncode); } return result; } ElementImpl *DocumentImpl::createElementNS( const DOMString &_namespaceURI, const DOMString &_qualifiedName, int* pExceptioncode ) { ElementImpl *e = 0; int colonPos = -2; // check NAMESPACE_ERR/INVALID_CHARACTER_ERR if (pExceptioncode && !checkQualifiedName(_qualifiedName, _namespaceURI, &colonPos, false/*nameCanBeNull*/, false/*nameCanBeEmpty*/, pExceptioncode)) return 0; DOMString prefix, localName; splitPrefixLocalName(_qualifiedName.implementation(), prefix, localName, colonPos); if ((isHTMLDocument() && _namespaceURI.isNull()) || (strcasecmp(_namespaceURI, XHTML_NAMESPACE) == 0 && localName == localName.lower())) { e = createHTMLElement(localName); if (e) { int _exceptioncode = 0; if (!prefix.isNull()) e->setPrefix(prefix, _exceptioncode); if ( _exceptioncode ) { if ( pExceptioncode ) *pExceptioncode = _exceptioncode; delete e; return 0; } e->setHTMLCompat( _namespaceURI.isNull() && htmlMode() != XHtml ); } } if (!e) { Id id = getId(NodeImpl::ElementId, _namespaceURI.implementation(), prefix.implementation(), localName.implementation(), false, false /*HTML already looked up*/); e = new XMLElementImpl( getDocument(), id, prefix.implementation() ); } return e; } AttrImpl *DocumentImpl::createAttributeNS( const DOMString &_namespaceURI, const DOMString &_qualifiedName, int* pExceptioncode) { int colonPos = -2; // check NAMESPACE_ERR/INVALID_CHARACTER_ERR if (pExceptioncode && !checkQualifiedName(_qualifiedName, _namespaceURI, &colonPos, false/*nameCanBeNull*/, false/*nameCanBeEmpty*/, pExceptioncode)) return 0; DOMString prefix, localName; splitPrefixLocalName(_qualifiedName.implementation(), prefix, localName, colonPos); Id id = getId(NodeImpl::AttributeId, _namespaceURI.implementation(), prefix.implementation(), localName.implementation(), false, true /*lookupHTML*/); AttrImpl* attr = new AttrImpl(0, getDocument(), id, DOMString("").implementation(), prefix.implementation()); attr->setHTMLCompat( _namespaceURI.isNull() && htmlMode() != XHtml ); return attr; } ElementImpl *DocumentImpl::getElementById( const DOMString &elementId ) const { TQString stringKey = elementId.string(); ElementMappingCache::ItemInfo* info = m_getElementByIdCache.get(stringKey); if (!info) return 0; //See if cache has an unambiguous answer. if (info->nd) return info->nd; //Now we actually have to walk. TQPtrStack nodeStack; NodeImpl *current = _first; while(1) { if(!current) { if(nodeStack.isEmpty()) break; current = nodeStack.pop(); current = current->nextSibling(); } else { if(current->isElementNode()) { ElementImpl *e = static_cast(current); if(e->getAttribute(ATTR_ID) == elementId) { info->nd = e; return e; } } NodeImpl *child = current->firstChild(); if(child) { nodeStack.push(current); current = child; } else { current = current->nextSibling(); } } } assert(0); //If there is no item with such an ID, we should never get here //kdDebug() << "WARNING: *DocumentImpl::getElementById not found " << elementId.string() << endl; return 0; } void DocumentImpl::setTitle(const DOMString& _title) { if (_title == m_title && !m_title.isNull()) return; m_title = _title; TQString titleStr = m_title.string(); for (unsigned int i = 0; i < titleStr.length(); ++i) if (titleStr[i] < ' ') titleStr[i] = ' '; titleStr = titleStr.simplifyWhiteSpace(); titleStr.compose(); if ( view() && !view()->part()->parentPart() ) { if (titleStr.isNull() || titleStr.isEmpty()) { // empty title... set window caption as the URL KURL url = m_url; url.setRef(TQString::null); url.setQuery(TQString::null); titleStr = url.prettyURL(); } emit view()->part()->setWindowCaption( KStringHandler::csqueeze( titleStr, 128 ) ); } } DOMString DocumentImpl::nodeName() const { return "#document"; } unsigned short DocumentImpl::nodeType() const { return Node::DOCUMENT_NODE; } DOMStringImpl* DocumentImpl::textContent() const { return 0; } void DocumentImpl::setTextContent( const DOMString&, int& ) {} ElementImpl *DocumentImpl::createHTMLElement( const DOMString &name ) { uint id = khtml::getTagID( name.string().lower().latin1(), name.string().length() ); // id = makeId(xhtmlNamespace, id); ElementImpl *n = 0; switch(id) { case ID_HTML: n = new HTMLHtmlElementImpl(docPtr()); break; case ID_HEAD: n = new HTMLHeadElementImpl(docPtr()); break; case ID_BODY: n = new HTMLBodyElementImpl(docPtr()); break; // head elements case ID_BASE: n = new HTMLBaseElementImpl(docPtr()); break; case ID_LINK: n = new HTMLLinkElementImpl(docPtr()); break; case ID_META: n = new HTMLMetaElementImpl(docPtr()); break; case ID_STYLE: n = new HTMLStyleElementImpl(docPtr()); break; case ID_TITLE: n = new HTMLTitleElementImpl(docPtr()); break; // frames case ID_FRAME: n = new HTMLFrameElementImpl(docPtr()); break; case ID_FRAMESET: n = new HTMLFrameSetElementImpl(docPtr()); break; case ID_IFRAME: n = new HTMLIFrameElementImpl(docPtr()); break; // form elements // ### FIXME: we need a way to set form dependency after we have made the form elements case ID_FORM: n = new HTMLFormElementImpl(docPtr(), false); break; case ID_BUTTON: n = new HTMLButtonElementImpl(docPtr()); break; case ID_FIELDSET: n = new HTMLFieldSetElementImpl(docPtr()); break; case ID_INPUT: n = new HTMLInputElementImpl(docPtr()); break; case ID_ISINDEX: n = new HTMLIsIndexElementImpl(docPtr()); break; case ID_LABEL: n = new HTMLLabelElementImpl(docPtr()); break; case ID_LEGEND: n = new HTMLLegendElementImpl(docPtr()); break; case ID_OPTGROUP: n = new HTMLOptGroupElementImpl(docPtr()); break; case ID_OPTION: n = new HTMLOptionElementImpl(docPtr()); break; case ID_SELECT: n = new HTMLSelectElementImpl(docPtr()); break; case ID_TEXTAREA: n = new HTMLTextAreaElementImpl(docPtr()); break; // lists case ID_DL: n = new HTMLDListElementImpl(docPtr()); break; case ID_DD: n = new HTMLGenericElementImpl(docPtr(), id); break; case ID_DT: n = new HTMLGenericElementImpl(docPtr(), id); break; case ID_UL: n = new HTMLUListElementImpl(docPtr()); break; case ID_OL: n = new HTMLOListElementImpl(docPtr()); break; case ID_DIR: n = new HTMLDirectoryElementImpl(docPtr()); break; case ID_MENU: n = new HTMLMenuElementImpl(docPtr()); break; case ID_LI: n = new HTMLLIElementImpl(docPtr()); break; // formatting elements (block) case ID_DIV: case ID_P: n = new HTMLDivElementImpl( docPtr(), id ); break; case ID_BLOCKQUOTE: case ID_H1: case ID_H2: case ID_H3: case ID_H4: case ID_H5: case ID_H6: n = new HTMLGenericElementImpl(docPtr(), id); break; case ID_HR: n = new HTMLHRElementImpl(docPtr()); break; case ID_PLAINTEXT: case ID_XMP: case ID_PRE: n = new HTMLPreElementImpl(docPtr(), id); break; // font stuff case ID_BASEFONT: n = new HTMLBaseFontElementImpl(docPtr()); break; case ID_FONT: n = new HTMLFontElementImpl(docPtr()); break; // ins/del case ID_DEL: case ID_INS: n = new HTMLGenericElementImpl(docPtr(), id); break; // anchor case ID_A: n = new HTMLAnchorElementImpl(docPtr()); break; // images case ID_IMG: n = new HTMLImageElementImpl(docPtr()); break; case ID_MAP: n = new HTMLMapElementImpl(docPtr()); /*n = map;*/ break; case ID_AREA: n = new HTMLAreaElementImpl(docPtr()); break; // objects, applets and scripts case ID_APPLET: n = new HTMLAppletElementImpl(docPtr()); break; case ID_OBJECT: n = new HTMLObjectElementImpl(docPtr()); break; case ID_EMBED: n = new HTMLEmbedElementImpl(docPtr()); break; case ID_PARAM: n = new HTMLParamElementImpl(docPtr()); break; case ID_SCRIPT: n = new HTMLScriptElementImpl(docPtr()); break; // tables case ID_TABLE: n = new HTMLTableElementImpl(docPtr()); break; case ID_CAPTION: n = new HTMLTableCaptionElementImpl(docPtr()); break; case ID_COLGROUP: case ID_COL: n = new HTMLTableColElementImpl(docPtr(), id); break; case ID_TR: n = new HTMLTableRowElementImpl(docPtr()); break; case ID_TD: case ID_TH: n = new HTMLTableCellElementImpl(docPtr(), id); break; case ID_THEAD: case ID_TBODY: case ID_TFOOT: n = new HTMLTableSectionElementImpl(docPtr(), id, false); break; // inline elements case ID_BR: n = new HTMLBRElementImpl(docPtr()); break; case ID_Q: n = new HTMLGenericElementImpl(docPtr(), id); break; // elements with no special representation in the DOM // block: case ID_ADDRESS: case ID_CENTER: n = new HTMLGenericElementImpl(docPtr(), id); break; // inline // %fontstyle case ID_TT: case ID_U: case ID_B: case ID_I: case ID_S: case ID_STRIKE: case ID_BIG: case ID_SMALL: // %phrase case ID_EM: case ID_STRONG: case ID_DFN: case ID_CODE: case ID_SAMP: case ID_KBD: case ID_VAR: case ID_CITE: case ID_ABBR: case ID_ACRONYM: // %special case ID_SUB: case ID_SUP: case ID_SPAN: case ID_NOBR: case ID_WBR: case ID_BDO: case ID_NOFRAMES: n = new HTMLGenericElementImpl(docPtr(), id); break; case ID_MARQUEE: n = new HTMLMarqueeElementImpl(docPtr()); break; // text case ID_TEXT: kdDebug( 6020 ) << "Use document->createTextNode()" << endl; break; default: break; } return n; } TQString DocumentImpl::nextState() { TQString state; if (!m_state.isEmpty()) { state = m_state.first(); m_state.remove(m_state.begin()); } return state; } TQStringList DocumentImpl::docState() { TQStringList s; for (TQPtrListIterator it(m_maintainsState); it.current(); ++it) s.append(it.current()->state()); return s; } bool DocumentImpl::unsubmittedFormChanges() { for (TQPtrListIterator it(m_maintainsState); it.current(); ++it) if (it.current()->state().right(1)=="M") return true; return false; } RangeImpl *DocumentImpl::createRange() { return new RangeImpl( docPtr() ); } NodeIteratorImpl *DocumentImpl::createNodeIterator(NodeImpl *root, unsigned long whatToShow, NodeFilter &filter, bool entityReferenceExpansion, int &exceptioncode) { if (!root) { exceptioncode = DOMException::NOT_SUPPORTED_ERR; return 0; } return new NodeIteratorImpl(root,whatToShow,filter,entityReferenceExpansion); } TreeWalkerImpl *DocumentImpl::createTreeWalker(NodeImpl *root, unsigned long whatToShow, NodeFilterImpl *filter, bool entityReferenceExpansion, int &exceptioncode) { if (!root) { exceptioncode = DOMException::NOT_SUPPORTED_ERR; return 0; } return new TreeWalkerImpl( root, whatToShow, filter, entityReferenceExpansion ); } void DocumentImpl::setDocumentChanged(bool b) { if (!changedDocuments) changedDocuments = s_changedDocumentsDeleter.setObject( changedDocuments, new TQPtrList() ); if (b && !m_docChanged) changedDocuments->append(this); else if (!b && m_docChanged) changedDocuments->remove(this); m_docChanged = b; } void DocumentImpl::recalcStyle( StyleChange change ) { // qDebug("recalcStyle(%p)", this); // TQTime qt; // qt.start(); if (m_inStyleRecalc) return; // Guard against re-entrancy. -dwh m_inStyleRecalc = true; if( !m_render ) goto bail_out; if ( change == Force ) { RenderStyle* oldStyle = m_render->style(); if ( oldStyle ) oldStyle->ref(); RenderStyle* _style = new RenderStyle(); _style->setDisplay(BLOCK); _style->setVisuallyOrdered( visuallyOrdered ); // ### make the font stuff _really_ work!!!! khtml::FontDef fontDef; TQFont f = KGlobalSettings::generalFont(); fontDef.family = f.family(); fontDef.italic = f.italic(); fontDef.weight = f.weight(); if (m_view) { const KHTMLSettings *settings = m_view->part()->settings(); TQString stdfont = settings->stdFontName(); if ( !stdfont.isEmpty() ) fontDef.family = stdfont; fontDef.size = m_styleSelector->fontSizes()[3]; } //kdDebug() << "DocumentImpl::attach: setting to charset " << settings->charset() << endl; _style->setFontDef(fontDef); _style->htmlFont().update( paintDeviceMetrics() ); if ( inCompatMode() ) _style->setHtmlHacks(true); // enable html specific rendering tricks StyleChange ch = diff( _style, oldStyle ); if(m_render && ch != NoChange) m_render->setStyle(_style); else delete _style; if ( change != Force ) change = ch; if (oldStyle) oldStyle->deref(); } NodeImpl *n; for (n = _first; n; n = n->nextSibling()) if ( change>= Inherit || n->hasChangedChild() || n->changed() ) n->recalcStyle( change ); //kdDebug( 6020 ) << "TIME: recalcStyle() dt=" << qt.elapsed() << endl; if (changed() && m_view) m_view->layout(); bail_out: setChanged( false ); setHasChangedChild( false ); setDocumentChanged( false ); m_inStyleRecalc = false; } void DocumentImpl::updateRendering() { if (!hasChangedChild()) return; // TQTime time; // time.start(); // kdDebug() << "UPDATERENDERING: "<")); } m_tokenizer->write(text, false); } void DocumentImpl::writeln( const DOMString &text ) { write(text); write(DOMString("\n")); } void DocumentImpl::finishParsing ( ) { if(m_tokenizer) m_tokenizer->finish(); } void DocumentImpl::setUserStyleSheet( const TQString& sheet ) { if ( m_usersheet != sheet ) { m_usersheet = sheet; updateStyleSelector(); } } CSSStyleSheetImpl* DocumentImpl::elementSheet() { if (!m_elemSheet) { m_elemSheet = new CSSStyleSheetImpl(this, baseURL().url() ); m_elemSheet->ref(); } return m_elemSheet; } void DocumentImpl::determineParseMode( const TQString &/*str*/ ) { // For XML documents, use strict parse mode pMode = Strict; hMode = XHtml; kdDebug(6020) << " using strict parseMode" << endl; } NodeImpl *DocumentImpl::nextFocusNode(NodeImpl *fromNode) { unsigned short fromTabIndex; if (!fromNode) { // No starting node supplied; begin with the top of the document NodeImpl *n; int lowestTabIndex = 65535; for (n = this; n != 0; n = n->traverseNextNode()) { if (n->isTabFocusable()) { if ((n->tabIndex() > 0) && (n->tabIndex() < lowestTabIndex)) lowestTabIndex = n->tabIndex(); } } if (lowestTabIndex == 65535) lowestTabIndex = 0; // Go to the first node in the document that has the desired tab index for (n = this; n != 0; n = n->traverseNextNode()) { if (n->isTabFocusable() && (n->tabIndex() == lowestTabIndex)) return n; } return 0; } else { fromTabIndex = fromNode->tabIndex(); } if (fromTabIndex == 0) { // Just need to find the next selectable node after fromNode (in document order) that doesn't have a tab index NodeImpl *n = fromNode->traverseNextNode(); while (n && !(n->isTabFocusable() && n->tabIndex() == 0)) n = n->traverseNextNode(); return n; } else { // Find the lowest tab index out of all the nodes except fromNode, that is greater than or equal to fromNode's // tab index. For nodes with the same tab index as fromNode, we are only interested in those that come after // fromNode in document order. // If we don't find a suitable tab index, the next focus node will be one with a tab index of 0. unsigned short lowestSuitableTabIndex = 65535; NodeImpl *n; bool reachedFromNode = false; for (n = this; n != 0; n = n->traverseNextNode()) { if (n->isTabFocusable() && ((reachedFromNode && (n->tabIndex() >= fromTabIndex)) || (!reachedFromNode && (n->tabIndex() > fromTabIndex))) && (n->tabIndex() < lowestSuitableTabIndex) && (n != fromNode)) { // We found a selectable node with a tab index at least as high as fromNode's. Keep searching though, // as there may be another node which has a lower tab index but is still suitable for use. lowestSuitableTabIndex = n->tabIndex(); } if (n == fromNode) reachedFromNode = true; } if (lowestSuitableTabIndex == 65535) { // No next node with a tab index -> just take first node with tab index of 0 NodeImpl *n = this; while (n && !(n->isTabFocusable() && n->tabIndex() == 0)) n = n->traverseNextNode(); return n; } // Search forwards from fromNode for (n = fromNode->traverseNextNode(); n != 0; n = n->traverseNextNode()) { if (n->isTabFocusable() && (n->tabIndex() == lowestSuitableTabIndex)) return n; } // The next node isn't after fromNode, start from the beginning of the document for (n = this; n != fromNode; n = n->traverseNextNode()) { if (n->isTabFocusable() && (n->tabIndex() == lowestSuitableTabIndex)) return n; } assert(false); // should never get here return 0; } } NodeImpl *DocumentImpl::previousFocusNode(NodeImpl *fromNode) { NodeImpl *lastNode = this; while (lastNode->lastChild()) lastNode = lastNode->lastChild(); if (!fromNode) { // No starting node supplied; begin with the very last node in the document NodeImpl *n; int highestTabIndex = 0; for (n = lastNode; n != 0; n = n->traversePreviousNode()) { if (n->isTabFocusable()) { if (n->tabIndex() == 0) return n; else if (n->tabIndex() > highestTabIndex) highestTabIndex = n->tabIndex(); } } // No node with a tab index of 0; just go to the last node with the highest tab index for (n = lastNode; n != 0; n = n->traversePreviousNode()) { if (n->isTabFocusable() && (n->tabIndex() == highestTabIndex)) return n; } return 0; } else { unsigned short fromTabIndex = fromNode->tabIndex(); if (fromTabIndex == 0) { // Find the previous selectable node before fromNode (in document order) that doesn't have a tab index NodeImpl *n = fromNode->traversePreviousNode(); while (n && !(n->isTabFocusable() && n->tabIndex() == 0)) n = n->traversePreviousNode(); if (n) return n; // No previous nodes with a 0 tab index, go to the last node in the document that has the highest tab index int highestTabIndex = 0; for (n = this; n != 0; n = n->traverseNextNode()) { if (n->isTabFocusable() && (n->tabIndex() > highestTabIndex)) highestTabIndex = n->tabIndex(); } if (highestTabIndex == 0) return 0; for (n = lastNode; n != 0; n = n->traversePreviousNode()) { if (n->isTabFocusable() && (n->tabIndex() == highestTabIndex)) return n; } assert(false); // should never get here return 0; } else { // Find the lowest tab index out of all the nodes except fromNode, that is less than or equal to fromNode's // tab index. For nodes with the same tab index as fromNode, we are only interested in those before // fromNode. // If we don't find a suitable tab index, then there will be no previous focus node. unsigned short highestSuitableTabIndex = 0; NodeImpl *n; bool reachedFromNode = false; for (n = this; n != 0; n = n->traverseNextNode()) { if (n->isTabFocusable() && ((!reachedFromNode && (n->tabIndex() <= fromTabIndex)) || (reachedFromNode && (n->tabIndex() < fromTabIndex))) && (n->tabIndex() > highestSuitableTabIndex) && (n != fromNode)) { // We found a selectable node with a tab index no higher than fromNode's. Keep searching though, as // there may be another node which has a higher tab index but is still suitable for use. highestSuitableTabIndex = n->tabIndex(); } if (n == fromNode) reachedFromNode = true; } if (highestSuitableTabIndex == 0) { // No previous node with a tab index. Since the order specified by HTML is nodes with tab index > 0 // first, this means that there is no previous node. return 0; } // Search backwards from fromNode for (n = fromNode->traversePreviousNode(); n != 0; n = n->traversePreviousNode()) { if (n->isTabFocusable() && (n->tabIndex() == highestSuitableTabIndex)) return n; } // The previous node isn't before fromNode, start from the end of the document for (n = lastNode; n != fromNode; n = n->traversePreviousNode()) { if (n->isTabFocusable() && (n->tabIndex() == highestSuitableTabIndex)) return n; } assert(false); // should never get here return 0; } } } ElementImpl* DocumentImpl::findAccessKeyElement(TQChar c) { c = c.upper(); for( NodeImpl* n = this; n != NULL; n = n->traverseNextNode()) { if( n->isElementNode()) { ElementImpl* en = static_cast< ElementImpl* >( n ); DOMString s = en->getAttribute( ATTR_ACCESSKEY ); if( s.length() == 1 && s[ 0 ].upper() == c ) return en; } } return NULL; } int DocumentImpl::nodeAbsIndex(NodeImpl *node) { assert(node->getDocument() == this); int absIndex = 0; for (NodeImpl *n = node; n && n != this; n = n->traversePreviousNode()) absIndex++; return absIndex; } NodeImpl *DocumentImpl::nodeWithAbsIndex(int absIndex) { NodeImpl *n = this; for (int i = 0; n && (i < absIndex); i++) { n = n->traverseNextNode(); } return n; } void DocumentImpl::processHttpEquiv(const DOMString &equiv, const DOMString &content) { assert(!equiv.isNull() && !content.isNull()); KHTMLView *v = getDocument()->view(); if(strcasecmp(equiv, "refresh") == 0 && v && v->part()->metaRefreshEnabled()) { // get delay and url TQString str = content.string().stripWhiteSpace(); int pos = str.find(TQRegExp("[;,]")); if ( pos == -1 ) pos = str.find(TQRegExp("[ \t]")); bool ok = false; int delay = kMax( 0, content.implementation()->toInt(&ok) ); if ( !ok && str.length() && str[0] == '.' ) ok = true; if (pos == -1) // There can be no url (David) { if(ok) v->part()->scheduleRedirection(delay, v->part()->url().url() ); } else { pos++; while(pos < (int)str.length() && str[pos].isSpace()) pos++; str = str.mid(pos); if(str.find("url", 0, false ) == 0) str = str.mid(3); str = str.stripWhiteSpace(); if ( str.length() && str[0] == '=' ) str = str.mid( 1 ).stripWhiteSpace(); while(str.length() && (str[str.length()-1] == ';' || str[str.length()-1] == ',')) str.setLength(str.length()-1); str = parseURL( DOMString(str) ).string(); TQString newURL = getDocument()->completeURL( str ); if ( ok ) v->part()->scheduleRedirection(delay, getDocument()->completeURL( str ), delay < 2 || newURL == URL().url()); } } else if(strcasecmp(equiv, "expires") == 0) { bool relative = false; TQString str = content.string().stripWhiteSpace(); time_t expire_date = KRFCDate::parseDate(str); if (!expire_date) { expire_date = str.toULong(); relative = true; } if (!expire_date) expire_date = 1; // expire now if (m_docLoader) m_docLoader->setExpireDate(expire_date, relative); } else if(v && (strcasecmp(equiv, "pragma") == 0 || strcasecmp(equiv, "cache-control") == 0)) { TQString str = content.string().lower().stripWhiteSpace(); KURL url = v->part()->url(); if ((str == "no-cache") && url.protocol().startsWith("http")) { KIO::http_update_cache(url, true, 0); } } else if( (strcasecmp(equiv, "set-cookie") == 0)) { // ### make setCookie work on XML documents too; e.g. in case of HTMLDocumentImpl *d = static_cast(this); d->setCookie(content); } else if (strcasecmp(equiv, "default-style") == 0) { // HTML 4.0 14.3.2 // http://www.hixie.ch/tests/evil/css/import/main/preferred.html m_preferredStylesheetSet = content; updateStyleSelector(); } else if (strcasecmp(equiv, "content-language") == 0) { m_contentLanguage = content.string(); } } bool DocumentImpl::prepareMouseEvent( bool readonly, int _x, int _y, MouseEvent *ev ) { if ( m_render ) { assert(m_render->isCanvas()); RenderObject::NodeInfo renderInfo(readonly, ev->type == MousePress); bool isInside = m_render->layer()->nodeAtPoint(renderInfo, _x, _y); ev->innerNode = renderInfo.innerNode(); ev->innerNonSharedNode = renderInfo.innerNonSharedNode(); if (renderInfo.URLElement()) { assert(renderInfo.URLElement()->isElementNode()); //qDebug("urlnode: %s (%d)", getTagName(renderInfo.URLElement()->id()).string().latin1(), renderInfo.URLElement()->id()); ElementImpl* e = static_cast(renderInfo.URLElement()); DOMString href = khtml::parseURL(e->getAttribute(ATTR_HREF)); DOMString target = e->getAttribute(ATTR_TARGET); if (!target.isNull() && !href.isNull()) { ev->target = target; ev->url = href; } else ev->url = href; } if (!readonly) updateRendering(); return isInside; } return false; } // DOM Section 1.1.1 bool DocumentImpl::childTypeAllowed( unsigned short type ) { switch (type) { case Node::ATTRIBUTE_NODE: case Node::CDATA_SECTION_NODE: case Node::DOCUMENT_FRAGMENT_NODE: case Node::DOCUMENT_NODE: case Node::ENTITY_NODE: case Node::ENTITY_REFERENCE_NODE: case Node::NOTATION_NODE: case Node::TEXT_NODE: // case Node::XPATH_NAMESPACE_NODE: return false; case Node::COMMENT_NODE: case Node::PROCESSING_INSTRUCTION_NODE: return true; case Node::DOCUMENT_TYPE_NODE: case Node::ELEMENT_NODE: // Documents may contain no more than one of each of these. // (One Element and one DocumentType.) for (NodeImpl* c = firstChild(); c; c = c->nextSibling()) if (c->nodeType() == type) return false; return true; } return false; } NodeImpl *DocumentImpl::cloneNode ( bool /*deep*/ ) { // Spec says cloning Document nodes is "implementation dependent" // so we do not support it... return 0; } typedef const char* (*NameLookupFunction)(unsigned short id); typedef int (*IdLookupFunction)(const char *tagStr, int len); NodeImpl::Id DocumentImpl::getId( NodeImpl::IdType _type, DOMStringImpl* _nsURI, DOMStringImpl *_prefix, DOMStringImpl *_name, bool readonly, bool /*lookupHTML*/, int *pExceptioncode) { /*kdDebug() << "DocumentImpl::getId( type: " << _type << ", uri: " << DOMString(_nsURI).string() << ", prefix: " << DOMString(_prefix).string() << ", name: " << DOMString(_name).string() << ", readonly: " << readonly << ", lookupHTML: " << lookupHTML << ", exceptions: " << (pExceptioncode ? "yes" : "no") << " )" << endl;*/ if(!_name) return 0; IdNameMapping *map; IdLookupFunction lookup; switch (_type) { case NodeImpl::ElementId: map = m_elementMap; lookup = getTagID; break; case NodeImpl::AttributeId: map = m_attrMap; lookup = getAttrID; break; case NodeImpl::NamespaceId: if( strcasecmp(_name, XHTML_NAMESPACE) == 0) return xhtmlNamespace; if( _name->l == 0) return emptyNamespace; // defaultNamespace handled by "if (!_name) return 0" map = m_namespaceMap; lookup = 0; break; default: return 0; } // Names and attributes with "" if (_name->l == 0) return 0; NodeImpl::Id id, nsid = 0; TQConstString n(_name->s, _name->l); bool cs = true; // case sensitive if (_type != NodeImpl::NamespaceId) { if (_nsURI) nsid = getId( NodeImpl::NamespaceId, 0, 0, _nsURI, false, false, 0 ) << 16; // Each document maintains a mapping of tag name -> id for every tag name encountered // in the document. cs = (htmlMode() == XHtml) || (_nsURI && _type != NodeImpl::AttributeId); // First see if it's a HTML element name // xhtml is lower case - case sensitive, easy to implement if ( cs && (id = lookup(n.string().ascii(), _name->l)) ) { map->addAlias(_prefix, _name, cs, id); return nsid + id; } // compatibility: upper case - case insensitive if ( !cs && (id = lookup(n.string().lower().ascii(), _name->l )) ) { map->addAlias(_prefix, _name, cs, id); return nsid + id; } } // Look in the names array for the name // compatibility mode has to lookup upper case TQString name = cs ? n.string() : n.string().upper(); if (!_nsURI) { id = (NodeImpl::Id)(long) map->ids.find( name ); if (!id && _type != NodeImpl::NamespaceId) { id = (NodeImpl::Id)(long) map->ids.find( "aliases: " + name ); } } else { id = (NodeImpl::Id)(long) map->ids.find( name ); if (!readonly && id && _prefix && _prefix->l) { // we were called in registration mode... check if the alias exists TQConstString px( _prefix->s, _prefix->l ); TQString qn("aliases: " + (cs ? px.string() : px.string().upper()) + ":" + name); if (!map->ids.find( qn )) { map->ids.insert( qn, (void*)id ); } } } if (id) return nsid + id; // unknown if (readonly) return 0; if ( pExceptioncode && _type != NodeImpl::NamespaceId && !Element::khtmlValidQualifiedName(_name)) { *pExceptioncode = DOMException::INVALID_CHARACTER_ERR; return 0; } // Name not found, so let's add it NodeImpl::Id cid = map->count++ + map->idStart; map->names.insert( cid, _name ); _name->ref(); map->ids.insert( name, (void*)cid ); // and register an alias if needed for DOM1 methods compatibility map->addAlias(_prefix, _name, cs, cid); return nsid + cid; } NodeImpl::Id DocumentImpl::getId( NodeImpl::IdType _type, DOMStringImpl *_nodeName, bool readonly, bool lookupHTML, int *pExceptioncode) { return getId(_type, 0, 0, _nodeName, readonly, lookupHTML, pExceptioncode); } DOMString DocumentImpl::getName( NodeImpl::IdType _type, NodeImpl::Id _id ) const { IdNameMapping *map; NameLookupFunction lookup; bool hasNS = (namespacePart(_id) != defaultNamespace); switch (_type) { case NodeImpl::ElementId: map = m_elementMap; lookup = getTagName; break; case NodeImpl::AttributeId: map = m_attrMap; lookup = getAttrName; break; case NodeImpl::NamespaceId: if( _id == xhtmlNamespace ) return XHTML_NAMESPACE; else if( _id == emptyNamespace ) return DOMString(""); else if ( _id == defaultNamespace ) return DOMString(); map = m_namespaceMap; lookup = 0; break; default: return DOMString();; } _id = localNamePart(_id) ; if (_id >= map->idStart) { return map->names[_id]; } else if (lookup) { // ### put them in a cache if (hasNS) return DOMString(lookup(_id)).lower(); else return lookup(_id); } else return DOMString(); } // This method is called whenever a top-level stylesheet has finished loading. void DocumentImpl::styleSheetLoaded() { // Make sure we knew this sheet was pending, and that our count isn't out of sync. assert(m_pendingStylesheets > 0); m_pendingStylesheets--; updateStyleSelector(); } DOMString DocumentImpl::selectedStylesheetSet() const { if (!view()) return DOMString(); return view()->part()->d->m_sheetUsed; } void DocumentImpl::setSelectedStylesheetSet(const DOMString& s) { // this code is evil if (view() && view()->part()->d->m_sheetUsed != s.string()) { view()->part()->d->m_sheetUsed = s.string(); updateStyleSelector(); } } void DocumentImpl::addStyleSheet(StyleSheetImpl *sheet, int *exceptioncode) { int excode = 0; if (!m_addedStyleSheets) { m_addedStyleSheets = new StyleSheetListImpl; m_addedStyleSheets->ref(); } m_addedStyleSheets->add(sheet); if (sheet->isCSSStyleSheet()) updateStyleSelector(); if (exceptioncode) *exceptioncode = excode; } void DocumentImpl::removeStyleSheet(StyleSheetImpl *sheet, int *exceptioncode) { int excode = 0; bool removed = false; bool is_css = sheet->isCSSStyleSheet(); if (m_addedStyleSheets) { bool in_main_list = !sheet->hasOneRef(); removed = m_addedStyleSheets->styleSheets.removeRef(sheet); sheet->deref(); if (m_addedStyleSheets->styleSheets.count() == 0) { bool reset = m_addedStyleSheets->hasOneRef(); m_addedStyleSheets->deref(); if (reset) m_addedStyleSheets = 0; } // remove from main list, too if (in_main_list) m_styleSheets->remove(sheet); } if (removed) { if (is_css) updateStyleSelector(); } else excode = DOMException::NOT_FOUND_ERR; if (exceptioncode) *exceptioncode = excode; } void DocumentImpl::updateStyleSelector(bool shallow) { // kdDebug() << "PENDING " << m_pendingStylesheets << endl; // Don't bother updating, since we haven't loaded all our style info yet. if (m_pendingStylesheets > 0) return; if (shallow) rebuildStyleSelector(); else recalcStyleSelector(); recalcStyle(Force); #if 0 m_styleSelectorDirty = true; #endif if ( renderer() ) renderer()->setNeedsLayoutAndMinMaxRecalc(); } void DocumentImpl::recalcStyleSelector() { if ( !m_render || !attached() ) return; assert(m_pendingStylesheets==0); TQPtrList oldStyleSheets = m_styleSheets->styleSheets; m_styleSheets->styleSheets.clear(); TQString sheetUsed = view() ? view()->part()->d->m_sheetUsed.replace("&&", "&") : TQString(); bool autoselect = sheetUsed.isEmpty(); if (autoselect && !m_preferredStylesheetSet.isEmpty()) sheetUsed = m_preferredStylesheetSet.string(); NodeImpl *n; for (int i=0 ; i<2 ; i++) { m_availableSheets.clear(); m_availableSheets << i18n("Basic Page Style"); bool canResetSheet = false; for (n = this; n; n = n->traverseNextNode()) { StyleSheetImpl *sheet = 0; if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { // Processing instruction (XML documents only) ProcessingInstructionImpl* pi = static_cast(n); sheet = pi->sheet(); if (!sheet && !pi->localHref().isEmpty()) { // Processing instruction with reference to an element in this document - e.g. // , with the element // heading { color: red; } at some location in // the document ElementImpl* elem = getElementById(pi->localHref()); if (elem) { DOMString sheetText(""); NodeImpl *c; for (c = elem->firstChild(); c; c = c->nextSibling()) { if (c->nodeType() == Node::TEXT_NODE || c->nodeType() == Node::CDATA_SECTION_NODE) sheetText += c->nodeValue(); } CSSStyleSheetImpl *cssSheet = new CSSStyleSheetImpl(this); cssSheet->parseString(sheetText); pi->setStyleSheet(cssSheet); sheet = cssSheet; } } } else if (n->isHTMLElement() && ( n->id() == ID_LINK || n->id() == ID_STYLE) ) { TQString title; if ( n->id() == ID_LINK ) { HTMLLinkElementImpl* l = static_cast(n); if (l->isCSSStyleSheet()) { sheet = l->sheet(); if (sheet || l->isLoading() || l->isAlternate() ) title = l->getAttribute(ATTR_TITLE).string(); if ((autoselect || title != sheetUsed) && l->isDisabled()) { sheet = 0; } else if (!title.isEmpty() && !l->isAlternate() && sheetUsed.isEmpty()) { sheetUsed = title; l->setDisabled(false); } } } else { //