diff options
author | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
---|---|---|
committer | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
commit | d8543bb6618c17b12da906afa77d216f58cf4058 (patch) | |
tree | c58dc05ed86825bd0ef8d305d58c8205106b540f /WebCore/dom | |
download | external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.zip external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.gz external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.bz2 |
external/webkit r30707
Diffstat (limited to 'WebCore/dom')
176 files changed, 29256 insertions, 0 deletions
diff --git a/WebCore/dom/Attr.cpp b/WebCore/dom/Attr.cpp new file mode 100644 index 0000000..52a2581 --- /dev/null +++ b/WebCore/dom/Attr.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "Attr.h" + +#include "Document.h" +#include "Element.h" +#include "ExceptionCode.h" +#include "Text.h" + +namespace WebCore { + +Attr::Attr(Element* element, Document* docPtr, PassRefPtr<Attribute> a) + : ContainerNode(docPtr), + m_element(element), + m_attribute(a), + m_ignoreChildrenChanged(0) +{ + ASSERT(!m_attribute->attr()); + m_attribute->m_impl = this; + m_attrWasSpecifiedOrElementHasRareData = true; +} + +Attr::~Attr() +{ + ASSERT(m_attribute->attr() == this); + m_attribute->m_impl = 0; +} + +void Attr::createTextChild() +{ + ASSERT(refCount()); + if (!m_attribute->value().isEmpty()) { + RefPtr<Text> textNode = document()->createTextNode(m_attribute->value().string()); + + // This does everything appendChild() would do in this situation (assuming m_ignoreChildrenChanged was set), + // but much more efficiently. + textNode->setParent(this); + setFirstChild(textNode.get()); + setLastChild(textNode.get()); + } +} + +String Attr::nodeName() const +{ + return name(); +} + +Node::NodeType Attr::nodeType() const +{ + return ATTRIBUTE_NODE; +} + +const AtomicString& Attr::localName() const +{ + return m_attribute->localName(); +} + +const AtomicString& Attr::namespaceURI() const +{ + return m_attribute->namespaceURI(); +} + +const AtomicString& Attr::prefix() const +{ + return m_attribute->prefix(); +} + +void Attr::setPrefix(const AtomicString &_prefix, ExceptionCode& ec) +{ + ec = 0; + checkSetPrefix(_prefix, ec); + if (ec) + return; + + m_attribute->setPrefix(_prefix); +} + +String Attr::nodeValue() const +{ + return value(); +} + +void Attr::setValue( const String& v, ExceptionCode& ec) +{ + ec = 0; + + // do not interprete entities in the string, its literal! + + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + int e = 0; + m_ignoreChildrenChanged++; + removeChildren(); + appendChild(document()->createTextNode(v), e); + m_ignoreChildrenChanged--; + + m_attribute->setValue(v.impl()); + if (m_element) + m_element->attributeChanged(m_attribute.get()); +} + +void Attr::setNodeValue(const String& v, ExceptionCode& ec) +{ + // NO_MODIFICATION_ALLOWED_ERR: taken care of by setValue() + setValue(v, ec); +} + +PassRefPtr<Node> Attr::cloneNode(bool /*deep*/) +{ + RefPtr<Attr> clone = new Attr(0, document(), m_attribute->clone()); + cloneChildNodes(clone.get()); + return clone.release(); +} + +// DOM Section 1.1.1 +bool Attr::childTypeAllowed(NodeType type) +{ + switch (type) { + case TEXT_NODE: + case ENTITY_REFERENCE_NODE: + return true; + default: + return false; + } +} + +void Attr::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + if (m_ignoreChildrenChanged > 0) + return; + + Node::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + + // FIXME: We should include entity references in the value + + String val = ""; + for (Node *n = firstChild(); n; n = n->nextSibling()) { + if (n->isTextNode()) + val += static_cast<Text *>(n)->data(); + } + + m_attribute->setValue(val.impl()); + if (m_element) + m_element->attributeChanged(m_attribute.get()); +} + +String Attr::toString() const +{ + String result; + + result += nodeName(); + + // FIXME: substitute entities for any instances of " or ' -- + // maybe easier to just use text value and ignore existing + // entity refs? + + if (firstChild() != NULL) { + result += "=\""; + + for (Node *child = firstChild(); child != NULL; child = child->nextSibling()) { + result += child->toString(); + } + + result += "\""; + } + + return result; +} + +} diff --git a/WebCore/dom/Attr.h b/WebCore/dom/Attr.h new file mode 100644 index 0000000..d71a79e --- /dev/null +++ b/WebCore/dom/Attr.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef Attr_h +#define Attr_h + +#include "ContainerNode.h" +#include "Attribute.h" + +namespace WebCore { + +// this has no counterpart in DOM, purely internal +// representation of the nodevalue of an Attr. +// the actual Attr (Attr) with its value as textchild +// is only allocated on demand by the DOM bindings. +// Any use of Attr inside khtml should be avoided. + +// Attr can have Text and EntityReference children +// therefore it has to be a fullblown Node. The plan +// is to dynamically allocate a textchild and store the +// resulting nodevalue in the Attribute upon +// destruction. however, this is not yet implemented. + +class Attr : public ContainerNode { + friend class NamedAttrMap; + +public: + Attr(Element*, Document*, PassRefPtr<Attribute>); + ~Attr(); + + // Call this after calling the constructor so the + // Attr node isn't floating when we append the text node. + void createTextChild(); + + // DOM methods & attributes for Attr + String name() const { return qualifiedName().toString(); } + bool specified() const { return m_attrWasSpecifiedOrElementHasRareData; } + Element* ownerElement() const { return m_element; } + + String value() const { return m_attribute->value(); } + void setValue(const String&, ExceptionCode&); + + // DOM methods overridden from parent classes + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual const AtomicString& localName() const; + virtual const AtomicString& namespaceURI() const; + virtual const AtomicString& prefix() const; + virtual void setPrefix(const AtomicString&, ExceptionCode&); + + virtual String nodeValue() const; + virtual void setNodeValue(const String&, ExceptionCode&); + virtual PassRefPtr<Node> cloneNode(bool deep); + + // Other methods (not part of DOM) + virtual bool isAttributeNode() const { return true; } + virtual bool childTypeAllowed(NodeType); + + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + virtual String toString() const; + + Attribute* attr() const { return m_attribute.get(); } + const QualifiedName& qualifiedName() const { return m_attribute->name(); } + + // An extension to get presentational information for attributes. + CSSStyleDeclaration* style() { return m_attribute->style(); } + + void setSpecified(bool specified) { m_attrWasSpecifiedOrElementHasRareData = specified; } + +private: + Element* m_element; + RefPtr<Attribute> m_attribute; + int m_ignoreChildrenChanged; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/Attr.idl b/WebCore/dom/Attr.idl new file mode 100644 index 0000000..eeb064b --- /dev/null +++ b/WebCore/dom/Attr.idl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=EEE8E22B-22C3-4e50-95F4-5E0B8AAD8231, + ImplementationUUID=41B16348-D8E7-4d21-BFDB-125705B7E91F + ] Attr : EventTargetNode { + + // DOM Level 1 + + readonly attribute [ConvertNullStringTo=Null] DOMString name; + readonly attribute boolean specified; + attribute [ConvertNullStringTo=Null, ConvertNullToNullString, CustomSetter] DOMString value + setter raises(DOMException); + + // DOM Level 2 + + readonly attribute Element ownerElement; + + // extensions +#if !defined(LANGUAGE_COM) + readonly attribute CSSStyleDeclaration style; +#endif + }; + +} diff --git a/WebCore/dom/Attribute.cpp b/WebCore/dom/Attribute.cpp new file mode 100644 index 0000000..351b55a --- /dev/null +++ b/WebCore/dom/Attribute.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "Attribute.h" + +#include "Attr.h" +#include "Element.h" + +namespace WebCore { + +Attribute* Attribute::clone(bool) const +{ + return new Attribute(m_name, m_value); +} + +PassRefPtr<Attr> Attribute::createAttrIfNeeded(Element* e) +{ + RefPtr<Attr> r = m_impl; + if (!r) { + r = new Attr(e, e->document(), this); + r->createTextChild(); + } + return r.release(); +} + +} diff --git a/WebCore/dom/Attribute.h b/WebCore/dom/Attribute.h new file mode 100644 index 0000000..d515ea7 --- /dev/null +++ b/WebCore/dom/Attribute.h @@ -0,0 +1,89 @@ +/* + * 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 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef Attribute_h +#define Attribute_h + +#include "QualifiedName.h" + +namespace WebCore { + +class Attr; +class CSSStyleDeclaration; +class Element; +class NamedAttrMap; + +// this has no counterpart in DOM, purely internal +// representation of the nodevalue of an Attr. +// the actual Attr (Attr) with its value as textchild +// is only allocated on demand by the DOM bindings. +// Any use of Attr inside khtml should be avoided. +class Attribute : public RefCounted<Attribute> { + friend class Attr; + friend class Element; + friend class NamedAttrMap; +public: + // null value is forbidden ! + Attribute(const QualifiedName& name, const AtomicString& value) + : RefCounted<Attribute>(0), m_name(name), m_value(value), m_impl(0) + {} + + Attribute(const AtomicString& name, const AtomicString& value) + : RefCounted<Attribute>(0), m_name(nullAtom, name, nullAtom), m_value(value), m_impl(0) + {} + + virtual ~Attribute() { } + + const AtomicString& value() const { return m_value; } + const AtomicString& prefix() const { return m_name.prefix(); } + const AtomicString& localName() const { return m_name.localName(); } + const AtomicString& namespaceURI() const { return m_name.namespaceURI(); } + + const QualifiedName& name() const { return m_name; } + + Attr* attr() const { return m_impl; } + PassRefPtr<Attr> createAttrIfNeeded(Element*); + + bool isNull() const { return m_value.isNull(); } + bool isEmpty() const { return m_value.isEmpty(); } + + virtual Attribute* clone(bool preserveDecl = true) const; + + // An extension to get the style information for presentational attributes. + virtual CSSStyleDeclaration* style() const { return 0; } + + void setValue(const AtomicString& value) { m_value = value; } + void setPrefix(const AtomicString& prefix) { m_name.setPrefix(prefix); } + +private: + QualifiedName m_name; + AtomicString m_value; + Attr* m_impl; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/BeforeTextInsertedEvent.cpp b/WebCore/dom/BeforeTextInsertedEvent.cpp new file mode 100644 index 0000000..be27adc --- /dev/null +++ b/WebCore/dom/BeforeTextInsertedEvent.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "BeforeTextInsertedEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +using namespace EventNames; + +BeforeTextInsertedEvent::BeforeTextInsertedEvent(const String& text) + : Event(webkitBeforeTextInsertedEvent, false, true), m_text(text) +{ +} + +} diff --git a/WebCore/dom/BeforeTextInsertedEvent.h b/WebCore/dom/BeforeTextInsertedEvent.h new file mode 100644 index 0000000..dacfd71 --- /dev/null +++ b/WebCore/dom/BeforeTextInsertedEvent.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BeforeTextInsertedEvent_h +#define BeforeTextInsertedEvent_h + +#include "Event.h" + +namespace WebCore { + +class BeforeTextInsertedEvent : public Event { +public: + BeforeTextInsertedEvent(const String&); + + virtual bool isBeforeTextInsertedEvent() const { return true; } + + const String& text() const { return m_text; } + void setText(const String& s) { m_text = s; } + +private: + String m_text; +}; + +} // namespace + +#endif diff --git a/WebCore/dom/BeforeUnloadEvent.cpp b/WebCore/dom/BeforeUnloadEvent.cpp new file mode 100644 index 0000000..b1cafe1 --- /dev/null +++ b/WebCore/dom/BeforeUnloadEvent.cpp @@ -0,0 +1,49 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "BeforeUnloadEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +using namespace EventNames; + +BeforeUnloadEvent::BeforeUnloadEvent() + : Event(beforeunloadEvent, false, true) +{ +} + +bool BeforeUnloadEvent::storesResultAsString() const +{ + return true; +} + +void BeforeUnloadEvent::storeResult(const String& s) +{ + m_result = s; +} + +} // namespace WebCore diff --git a/WebCore/dom/BeforeUnloadEvent.h b/WebCore/dom/BeforeUnloadEvent.h new file mode 100644 index 0000000..6eecb73 --- /dev/null +++ b/WebCore/dom/BeforeUnloadEvent.h @@ -0,0 +1,48 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef BeforeUnloadEvent_h +#define BeforeUnloadEvent_h + +#include "Event.h" + +namespace WebCore { + + class BeforeUnloadEvent : public Event { + public: + BeforeUnloadEvent(); + + virtual bool storesResultAsString() const; + virtual void storeResult(const String&); + + String result() const { return m_result; } + + private: + String m_result; + }; + +} // namespace WebCore + +#endif // BeforeUnloadEvent_h diff --git a/WebCore/dom/CDATASection.cpp b/WebCore/dom/CDATASection.cpp new file mode 100644 index 0000000..3f3d472 --- /dev/null +++ b/WebCore/dom/CDATASection.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "CDATASection.h" + +#include "Document.h" + +namespace WebCore { + +CDATASection::CDATASection(Document* document, const String& text) + : Text(document, text) +{ +} + +CDATASection::~CDATASection() +{ +} + +String CDATASection::nodeName() const +{ + return "#cdata-section"; +} + +Node::NodeType CDATASection::nodeType() const +{ + return CDATA_SECTION_NODE; +} + +PassRefPtr<Node> CDATASection::cloneNode(bool /*deep*/) +{ + return new CDATASection(document(), m_data); +} + +// DOM Section 1.1.1 +bool CDATASection::childTypeAllowed(NodeType) +{ + return false; +} + +PassRefPtr<Text> CDATASection::createNew(PassRefPtr<StringImpl> string) +{ + return new CDATASection(document(), string); +} + +String CDATASection::toString() const +{ + // FIXME: We need to substitute entity references. + return "<![CDATA[" + data() + "]]>"; +} + +} // namespace WebCore diff --git a/WebCore/dom/CDATASection.h b/WebCore/dom/CDATASection.h new file mode 100644 index 0000000..7a21a36 --- /dev/null +++ b/WebCore/dom/CDATASection.h @@ -0,0 +1,49 @@ +/* + * 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) + * Copyright (C) 2003 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef CDATASection_h +#define CDATASection_h + +#include "Text.h" + +namespace WebCore { + +class CDATASection : public Text { +public: + CDATASection(Document*, const String&); + virtual ~CDATASection(); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); + virtual String toString() const; + +protected: + virtual PassRefPtr<Text> createNew(PassRefPtr<StringImpl>); +}; + +} // namespace WebCore + +#endif // CDATASection_h diff --git a/WebCore/dom/CDATASection.idl b/WebCore/dom/CDATASection.idl new file mode 100644 index 0000000..7c6c1e6 --- /dev/null +++ b/WebCore/dom/CDATASection.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=DC8E30FD-42DD-4a12-9B74-78D634321B41, + ImplementationUUID=10A5D70C-D93E-409c-A6BA-9D7CB4E3D06A + ] CDATASection : Text { + }; + +} diff --git a/WebCore/dom/CSSMappedAttributeDeclaration.cpp b/WebCore/dom/CSSMappedAttributeDeclaration.cpp new file mode 100644 index 0000000..7fe0915 --- /dev/null +++ b/WebCore/dom/CSSMappedAttributeDeclaration.cpp @@ -0,0 +1,38 @@ +/** + * 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 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "CSSMappedAttributeDeclaration.h" + +#include "StyledElement.h" + +namespace WebCore { + +CSSMappedAttributeDeclaration::~CSSMappedAttributeDeclaration() +{ + if (m_entryType != ePersistent) + StyledElement::removeMappedAttributeDecl(m_entryType, m_attrName, m_attrValue); +} + +} diff --git a/WebCore/dom/CSSMappedAttributeDeclaration.h b/WebCore/dom/CSSMappedAttributeDeclaration.h new file mode 100644 index 0000000..cacb7de --- /dev/null +++ b/WebCore/dom/CSSMappedAttributeDeclaration.h @@ -0,0 +1,60 @@ +/* + * 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 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef CSSMappedAttributeDeclaration_h +#define CSSMappedAttributeDeclaration_h + +#include "CSSMutableStyleDeclaration.h" +#include "MappedAttributeEntry.h" +#include "QualifiedName.h" + +namespace WebCore { + +class CSSMappedAttributeDeclaration : public CSSMutableStyleDeclaration { +public: + CSSMappedAttributeDeclaration(CSSRule* parentRule) + : CSSMutableStyleDeclaration(parentRule) + , m_entryType(eNone) + , m_attrName(anyQName()) { } + + virtual ~CSSMappedAttributeDeclaration(); + + void setMappedState(MappedAttributeEntry type, const QualifiedName& name, const AtomicString& val) + { + m_entryType = type; + m_attrName = name; + m_attrValue = val; + } + +private: + MappedAttributeEntry m_entryType; + QualifiedName m_attrName; + AtomicString m_attrValue; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/CharacterData.cpp b/WebCore/dom/CharacterData.cpp new file mode 100644 index 0000000..1228c98 --- /dev/null +++ b/WebCore/dom/CharacterData.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "CharacterData.h" + +#include "CString.h" +#include "Document.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "MutationEvent.h" +#include "RenderText.h" + +namespace WebCore { + +using namespace EventNames; + +CharacterData::CharacterData(Document *doc) + : EventTargetNode(doc) +{ +} + +CharacterData::CharacterData(Document *doc, const String &_text) + : EventTargetNode(doc) +{ + m_data = _text.impl() ? _text.impl() : StringImpl::empty(); +} + +CharacterData::~CharacterData() +{ +} + +void CharacterData::setData(const String& data, ExceptionCode& ec) +{ + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + if (equal(m_data.get(), data.impl())) + return; + + RefPtr<StringImpl> oldStr = m_data; + m_data = data.impl(); + + if ((!renderer() || !rendererIsNeeded(renderer()->style())) && attached()) { + detach(); + attach(); + } else if (renderer()) + static_cast<RenderText*>(renderer())->setText(m_data); + + dispatchModifiedEvent(oldStr.get()); + + document()->removeMarkers(this); +} + +String CharacterData::substringData( const unsigned offset, const unsigned count, ExceptionCode& ec) +{ + ec = 0; + checkCharDataOperation(offset, ec); + if (ec) + return String(); + + return m_data->substring(offset, count); +} + +void CharacterData::appendData( const String &arg, ExceptionCode& ec) +{ + ec = 0; + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + String newStr = m_data; + newStr.append(arg); + + RefPtr<StringImpl> oldStr = m_data; + m_data = newStr.impl(); + + if ((!renderer() || !rendererIsNeeded(renderer()->style())) && attached()) { + detach(); + attach(); + } else if (renderer()) + static_cast<RenderText*>(renderer())->setTextWithOffset(m_data, oldStr->length(), 0); + + dispatchModifiedEvent(oldStr.get()); +} + +void CharacterData::insertData( const unsigned offset, const String &arg, ExceptionCode& ec) +{ + ec = 0; + checkCharDataOperation(offset, ec); + if (ec) + return; + + String newStr = m_data; + newStr.insert(arg, offset); + + RefPtr<StringImpl> oldStr = m_data; + m_data = newStr.impl(); + + if ((!renderer() || !rendererIsNeeded(renderer()->style())) && attached()) { + detach(); + attach(); + } else if (renderer()) + static_cast<RenderText*>(renderer())->setTextWithOffset(m_data, offset, 0); + + dispatchModifiedEvent(oldStr.get()); + + // update the markers for spell checking and grammar checking + unsigned length = arg.length(); + document()->shiftMarkers(this, offset, length); +} + +void CharacterData::deleteData( const unsigned offset, const unsigned count, ExceptionCode& ec) +{ + ec = 0; + checkCharDataOperation(offset, ec); + if (ec) + return; + + String newStr = m_data; + newStr.remove(offset, count); + + RefPtr<StringImpl> oldStr = m_data; + m_data = newStr.impl(); + + if ((!renderer() || !rendererIsNeeded(renderer()->style())) && attached()) { + detach(); + attach(); + } else if (renderer()) + static_cast<RenderText*>(renderer())->setTextWithOffset(m_data, offset, count); + + dispatchModifiedEvent(oldStr.get()); + + // update the markers for spell checking and grammar checking + document()->removeMarkers(this, offset, count); + document()->shiftMarkers(this, offset + count, -static_cast<int>(count)); +} + +void CharacterData::replaceData( const unsigned offset, const unsigned count, const String &arg, ExceptionCode& ec) +{ + ec = 0; + checkCharDataOperation(offset, ec); + if (ec) + return; + + unsigned realCount; + if (offset + count > m_data->length()) + realCount = m_data->length()-offset; + else + realCount = count; + + String newStr = m_data; + newStr.remove(offset, realCount); + newStr.insert(arg, offset); + + RefPtr<StringImpl> oldStr = m_data; + m_data = newStr.impl(); + + if ((!renderer() || !rendererIsNeeded(renderer()->style())) && attached()) { + detach(); + attach(); + } else if (renderer()) + static_cast<RenderText*>(renderer())->setTextWithOffset(m_data, offset, count); + + dispatchModifiedEvent(oldStr.get()); + + // update the markers for spell checking and grammar checking + int diff = arg.length() - count; + document()->removeMarkers(this, offset, count); + document()->shiftMarkers(this, offset + count, diff); +} + +String CharacterData::nodeValue() const +{ + return m_data; +} + +bool CharacterData::containsOnlyWhitespace() const +{ + if (m_data) + return m_data->containsOnlyWhitespace(); + return true; +} + +void CharacterData::setNodeValue( const String &_nodeValue, ExceptionCode& ec) +{ + // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData() + setData(_nodeValue, ec); +} + +void CharacterData::dispatchModifiedEvent(StringImpl *prevValue) +{ + if (parentNode()) + parentNode()->childrenChanged(); + if (document()->hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) { + ExceptionCode ec; + dispatchEvent(new MutationEvent(DOMCharacterDataModifiedEvent, true, false, 0, prevValue, m_data, String(), 0), ec); + } + dispatchSubtreeModifiedEvent(); +} + +void CharacterData::checkCharDataOperation( const unsigned offset, ExceptionCode& ec) +{ + ec = 0; + + // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than the number of 16-bit + // units in data. + if (offset > m_data->length()) { + ec = INDEX_SIZE_ERR; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } +} + +int CharacterData::maxCharacterOffset() const +{ + return (int)length(); +} + +bool CharacterData::rendererIsNeeded(RenderStyle *style) +{ + if (!m_data || m_data->length() == 0) + return false; + return EventTargetNode::rendererIsNeeded(style); +} + +bool CharacterData::offsetInCharacters() const +{ + return true; +} + +} // namespace WebCore diff --git a/WebCore/dom/CharacterData.h b/WebCore/dom/CharacterData.h new file mode 100644 index 0000000..da89a95 --- /dev/null +++ b/WebCore/dom/CharacterData.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef CharacterData_h +#define CharacterData_h + +#include "EventTargetNode.h" + +namespace WebCore { + +class CharacterData : public EventTargetNode { +public: + CharacterData(Document*, const String& text); + CharacterData(Document*); + virtual ~CharacterData(); + + // DOM methods & attributes for CharacterData + + String data() const { return m_data; } + void setData(const String&, ExceptionCode&); + unsigned length() const { return m_data->length(); } + String substringData(unsigned offset, unsigned count, ExceptionCode&); + void appendData(const String&, ExceptionCode&); + void insertData(unsigned offset, const String&, ExceptionCode&); + void deleteData(unsigned offset, unsigned count, ExceptionCode&); + void replaceData(unsigned offset, unsigned count, const String &arg, ExceptionCode&); + + bool containsOnlyWhitespace() const; + + // DOM methods overridden from parent classes + + virtual String nodeValue() const; + virtual void setNodeValue(const String&, ExceptionCode&); + + // Other methods (not part of DOM) + + virtual bool isCharacterDataNode() const { return true; } + virtual int maxCharacterOffset() const; + StringImpl* string() { return m_data.get(); } + virtual void checkCharDataOperation(unsigned offset, ExceptionCode&); + + virtual bool offsetInCharacters() const; + virtual bool rendererIsNeeded(RenderStyle*); + +protected: + RefPtr<StringImpl> m_data; + + void dispatchModifiedEvent(StringImpl* oldValue); +}; + +} // namespace WebCore + +#endif // CharacterData_h + diff --git a/WebCore/dom/CharacterData.idl b/WebCore/dom/CharacterData.idl new file mode 100644 index 0000000..74dc483 --- /dev/null +++ b/WebCore/dom/CharacterData.idl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=149159F4-D2BA-4040-8137-6BF6424C972A, + ImplementationUUID=E2095280-B9BD-446a-8C03-79F78417CDFF + ] CharacterData : EventTargetNode { + + attribute [ConvertNullToNullString] DOMString data + setter raises(DOMException); + + readonly attribute unsigned long length; + + [ConvertNullStringTo=Null, OldStyleObjC] DOMString substringData(in [IsIndex] unsigned long offset, + in [IsIndex] unsigned long length) + raises(DOMException); + + void appendData(in DOMString data) + raises(DOMException); + + [OldStyleObjC] void insertData(in [IsIndex] unsigned long offset, + in DOMString data) + raises(DOMException); + + [OldStyleObjC] void deleteData(in [IsIndex] unsigned long offset, + in [IsIndex] unsigned long length) + raises(DOMException); + + [OldStyleObjC] void replaceData(in [IsIndex] unsigned long offset, + in [IsIndex] unsigned long length, + in DOMString data) + raises(DOMException); + + }; + +} diff --git a/WebCore/dom/ChildNodeList.cpp b/WebCore/dom/ChildNodeList.cpp new file mode 100644 index 0000000..4befe40 --- /dev/null +++ b/WebCore/dom/ChildNodeList.cpp @@ -0,0 +1,112 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2007 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "ChildNodeList.h" + +#include "Node.h" + +namespace WebCore { + +ChildNodeList::ChildNodeList(PassRefPtr<Node> rootNode, DynamicNodeList::Caches* info) + : DynamicNodeList(rootNode, info, false) +{ +} + +unsigned ChildNodeList::length() const +{ + if (m_caches->isLengthCacheValid) + return m_caches->cachedLength; + + unsigned len = 0; + for (Node* n = m_rootNode->firstChild(); n; n = n->nextSibling()) + len++; + + m_caches->cachedLength = len; + m_caches->isLengthCacheValid = true; + + return len; +} + +Node* ChildNodeList::item(unsigned index) const +{ + unsigned int pos = 0; + Node* n = m_rootNode->firstChild(); + + if (m_caches->isItemCacheValid) { + if (index == m_caches->lastItemOffset) + return m_caches->lastItem; + + int diff = index - m_caches->lastItemOffset; + unsigned dist = abs(diff); + if (dist < index) { + n = m_caches->lastItem; + pos = m_caches->lastItemOffset; + } + } + + if (m_caches->isLengthCacheValid) { + if (index >= m_caches->cachedLength) + return 0; + + int diff = index - pos; + unsigned dist = abs(diff); + if (dist > m_caches->cachedLength - 1 - index) { + n = m_rootNode->lastChild(); + pos = m_caches->cachedLength - 1; + } + } + + if (pos <= index) { + while (n && pos < index) { + n = n->nextSibling(); + ++pos; + } + } else { + while (n && pos > index) { + n = n->previousSibling(); + --pos; + } + } + + if (n) { + m_caches->lastItem = n; + m_caches->lastItemOffset = pos; + m_caches->isItemCacheValid = true; + return n; + } + + return 0; +} + +bool ChildNodeList::nodeMatches(Node* testNode) const +{ + return testNode->parentNode() == m_rootNode; +} + +void ChildNodeList::rootNodeChildrenChanged() +{ + // For child node lists, the common cache is reset in Node::notifyLocalNodeListsChildrenChanged() + ASSERT(!m_ownsCaches); +} + +} // namespace WebCore diff --git a/WebCore/dom/ChildNodeList.h b/WebCore/dom/ChildNodeList.h new file mode 100644 index 0000000..efdc606 --- /dev/null +++ b/WebCore/dom/ChildNodeList.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef ChildNodeList_h +#define ChildNodeList_h + +#include "DynamicNodeList.h" + +namespace WebCore { + + class ChildNodeList : public DynamicNodeList { + public: + ChildNodeList(PassRefPtr<Node> rootNode, DynamicNodeList::Caches*); + + virtual unsigned length() const; + virtual Node* item(unsigned index) const; + + virtual void rootNodeChildrenChanged(); + + protected: + virtual bool nodeMatches(Node*) const; + }; + +} // namespace WebCore + +#endif // ChildNodeList_h diff --git a/WebCore/dom/ClassNames.cpp b/WebCore/dom/ClassNames.cpp new file mode 100644 index 0000000..d406198 --- /dev/null +++ b/WebCore/dom/ClassNames.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007 David Smith (catfish.man@gmail.com) + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "ClassNames.h" + +namespace WebCore { + +void ClassNames::parseClassAttribute(const String& classStr, const bool inCompatMode) +{ + if (!m_nameVector) + m_nameVector.set(new ClassNameVector); + else + m_nameVector->clear(); + + if (classStr.isEmpty()) + return; + + String classAttr = inCompatMode ? classStr.foldCase() : classStr; + + const UChar* str = classAttr.characters(); + const int length = classAttr.length(); + int start = 0; + while (true) { + while (start < length && isClassWhitespace(str[start])) + ++start; + if (start >= length) + break; + int end = start + 1; + while (end < length && !isClassWhitespace(str[end])) + ++end; + + m_nameVector->append(AtomicString(str + start, end - start)); + + start = end + 1; + } +} + +} // namespace WebCore diff --git a/WebCore/dom/ClassNames.h b/WebCore/dom/ClassNames.h new file mode 100644 index 0000000..2bee7bd --- /dev/null +++ b/WebCore/dom/ClassNames.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef ClassNames_h +#define ClassNames_h + +#include "AtomicString.h" +#include <wtf/Vector.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + + class ClassNames { + typedef Vector<AtomicString, 8> ClassNameVector; + public: + ClassNames() + { + } + + bool contains(const AtomicString& str) const + { + if (!m_nameVector) + return false; + + size_t size = m_nameVector->size(); + for (size_t i = 0; i < size; ++i) { + if (m_nameVector->at(i) == str) + return true; + } + + return false; + } + + void parseClassAttribute(const String&, const bool inCompatMode); + + size_t size() const { return m_nameVector ? m_nameVector->size() : 0; } + void clear() { if (m_nameVector) m_nameVector->clear(); } + const AtomicString& operator[](size_t i) const { ASSERT(m_nameVector); return m_nameVector->at(i); } + + private: + OwnPtr<ClassNameVector> m_nameVector; + }; + + inline static bool isClassWhitespace(UChar c) + { + return c == ' ' || c == '\r' || c == '\n' || c == '\t' || c == '\f'; + } + +} // namespace WebCore + +#endif // ClassNames_h diff --git a/WebCore/dom/ClassNodeList.cpp b/WebCore/dom/ClassNodeList.cpp new file mode 100644 index 0000000..eb1f837 --- /dev/null +++ b/WebCore/dom/ClassNodeList.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 David Smith (catfish.man@gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ClassNodeList.h" + +#include "Document.h" +#include "Element.h" +#include "Node.h" + +namespace WebCore { + +ClassNodeList::ClassNodeList(PassRefPtr<Node> rootNode, const String& classNames, DynamicNodeList::Caches* caches) + : DynamicNodeList(rootNode, caches, true) +{ + m_classNames.parseClassAttribute(classNames, m_rootNode->document()->inCompatMode()); +} + +bool ClassNodeList::nodeMatches(Node* testNode) const +{ + if (!testNode->isElementNode()) + return false; + + if (!testNode->hasClass()) + return false; + + if (!m_classNames.size()) + return false; + + const ClassNames& classes = *static_cast<Element*>(testNode)->getClassNames(); + for (size_t i = 0; i < m_classNames.size(); ++i) { + if (!classes.contains(m_classNames[i])) + return false; + } + + return true; +} + +} // namespace WebCore diff --git a/WebCore/dom/ClassNodeList.h b/WebCore/dom/ClassNodeList.h new file mode 100644 index 0000000..947e346 --- /dev/null +++ b/WebCore/dom/ClassNodeList.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 David Smith (catfish.man@gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ClassNodeList_h +#define ClassNodeList_h + +#include "ClassNames.h" +#include "DynamicNodeList.h" + +namespace WebCore { + + class String; + + class ClassNodeList : public DynamicNodeList { + public: + ClassNodeList(PassRefPtr<Node> rootNode, const String& classNames, DynamicNodeList::Caches*); + + private: + virtual bool nodeMatches(Node*) const; + + ClassNames m_classNames; + }; + +} // namespace WebCore + +#endif // ClassNodeList_h diff --git a/WebCore/dom/Clipboard.cpp b/WebCore/dom/Clipboard.cpp new file mode 100755 index 0000000..4c808d1 --- /dev/null +++ b/WebCore/dom/Clipboard.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Clipboard.h" + +#include "DOMImplementation.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "Image.h" +#include "PluginInfoStore.h" + +namespace WebCore { + +Clipboard::Clipboard(ClipboardAccessPolicy policy, bool isForDragging) + : RefCounted<Clipboard>(0) + , m_policy(policy) + , m_dragStarted(false) + , m_forDragging(isForDragging) + , m_dragImage(0) +{ +} + +void Clipboard::setAccessPolicy(ClipboardAccessPolicy policy) +{ + // once you go numb, can never go back + ASSERT(m_policy != ClipboardNumb || policy == ClipboardNumb); + m_policy = policy; +} + +// These "conversion" methods are called by both WebCore and WebKit, and never make sense to JS, so we don't +// worry about security for these. They don't allow access to the pasteboard anyway. + +static DragOperation dragOpFromIEOp(const String& op) +{ + // yep, it's really just this fixed set + if (op == "none") + return DragOperationNone; + if (op == "copy") + return DragOperationCopy; + if (op == "link") + return DragOperationLink; + if (op == "move") + return DragOperationGeneric; + if (op == "copyLink") + return (DragOperation)(DragOperationCopy | DragOperationLink); + if (op == "copyMove") + return (DragOperation)(DragOperationCopy | DragOperationGeneric | DragOperationMove); + if (op == "linkMove") + return (DragOperation)(DragOperationLink | DragOperationGeneric | DragOperationMove); + if (op == "all") + return DragOperationEvery; + return DragOperationPrivate; // really a marker for "no conversion" +} + +static String IEOpFromDragOp(DragOperation op) +{ + bool moveSet = !!((DragOperationGeneric | DragOperationMove) & op); + + if ((moveSet && (op & DragOperationCopy) && (op & DragOperationLink)) + || (op == DragOperationEvery)) + return "all"; + if (moveSet && (op & DragOperationCopy)) + return "copyMove"; + if (moveSet && (op & DragOperationLink)) + return "linkMove"; + if ((op & DragOperationCopy) && (op & DragOperationLink)) + return "copyLink"; + if (moveSet) + return "move"; + if (op & DragOperationCopy) + return "copy"; + if (op & DragOperationLink) + return "link"; + return "none"; +} + +bool Clipboard::sourceOperation(DragOperation& op) const +{ + if (m_effectAllowed.isNull()) + return false; + op = dragOpFromIEOp(m_effectAllowed); + return true; +} + +bool Clipboard::destinationOperation(DragOperation& op) const +{ + if (m_dropEffect.isNull()) + return false; + op = dragOpFromIEOp(m_dropEffect); + return true; +} + +void Clipboard::setSourceOperation(DragOperation op) +{ + m_effectAllowed = IEOpFromDragOp(op); +} + +void Clipboard::setDestinationOperation(DragOperation op) +{ + m_dropEffect = IEOpFromDragOp(op); +} + +void Clipboard::setDropEffect(const String &effect) +{ + if (m_policy == ClipboardReadable || m_policy == ClipboardTypesReadable) { + m_dropEffect = effect; + } +} + +void Clipboard::setEffectAllowed(const String &effect) +{ + if (m_policy == ClipboardWritable) + m_effectAllowed = effect; +} + +} diff --git a/WebCore/dom/Clipboard.h b/WebCore/dom/Clipboard.h new file mode 100644 index 0000000..5491c25 --- /dev/null +++ b/WebCore/dom/Clipboard.h @@ -0,0 +1,112 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef Clipboard_h +#define Clipboard_h + +#include <wtf/HashSet.h> +#include "AtomicString.h" +#include "ClipboardAccessPolicy.h" +#include "DragActions.h" +#include "DragImage.h" +#include "IntPoint.h" +#include "Node.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + + class CachedImage; + class Element; + class Frame; + class Image; + class KURL; + class Range; + class String; + + // State available during IE's events for drag and drop and copy/paste + class Clipboard : public RefCounted<Clipboard> { + public: + Clipboard(ClipboardAccessPolicy policy, bool isForDragging); + virtual ~Clipboard() { } + + // Is this operation a drag-drop or a copy-paste? + bool isForDragging() const { return m_forDragging; } + + String dropEffect() const { return m_dropEffect; } + void setDropEffect(const String&); + String effectAllowed() const { return m_effectAllowed; } + void setEffectAllowed(const String&); + + virtual void clearData(const String& type) = 0; + virtual void clearAllData() = 0; + virtual String getData(const String& type, bool& success) const = 0; + virtual bool setData(const String& type, const String& data) = 0; + + // extensions beyond IE's API + virtual HashSet<String> types() const = 0; + + IntPoint dragLocation() const { return m_dragLoc; } + CachedImage* dragImage() const { return m_dragImage; } + virtual void setDragImage(CachedImage*, const IntPoint&) = 0; + Node* dragImageElement() { return m_dragImageElement.get(); } + virtual void setDragImageElement(Node*, const IntPoint&) = 0; + + //Provides the DOM specified + virtual DragImageRef createDragImage(IntPoint& dragLoc) const = 0; + virtual void declareAndWriteDragImage(Element*, const KURL&, const String& title, Frame*) = 0; + virtual void writeURL(const KURL&, const String&, Frame*) = 0; + virtual void writeRange(Range*, Frame*) = 0; + + virtual bool hasData() = 0; + + void setAccessPolicy(ClipboardAccessPolicy); + + bool sourceOperation(DragOperation&) const; + bool destinationOperation(DragOperation&) const; + void setSourceOperation(DragOperation); + void setDestinationOperation(DragOperation); + + void setDragHasStarted() { m_dragStarted = true; } + + protected: + ClipboardAccessPolicy policy() const { return m_policy; } + bool dragStarted() const { return m_dragStarted; } + + private: + ClipboardAccessPolicy m_policy; + String m_dropEffect; + String m_effectAllowed; + bool m_dragStarted; + + protected: + bool m_forDragging; + IntPoint m_dragLoc; + CachedImage* m_dragImage; + RefPtr<Node> m_dragImageElement; + }; + +} // namespace WebCore + +#endif // Clipboard_h diff --git a/WebCore/dom/ClipboardAccessPolicy.h b/WebCore/dom/ClipboardAccessPolicy.h new file mode 100644 index 0000000..7a54009 --- /dev/null +++ b/WebCore/dom/ClipboardAccessPolicy.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ClipboardAccessPolicy_h +#define ClipboardAccessPolicy_h + +namespace WebCore { + +enum ClipboardAccessPolicy { + ClipboardNumb, ClipboardImageWritable, ClipboardWritable, ClipboardTypesReadable, ClipboardReadable +}; + +} // namespace + +#endif diff --git a/WebCore/dom/ClipboardEvent.cpp b/WebCore/dom/ClipboardEvent.cpp new file mode 100644 index 0000000..f6c4333 --- /dev/null +++ b/WebCore/dom/ClipboardEvent.cpp @@ -0,0 +1,44 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "ClipboardEvent.h" + +namespace WebCore { + +ClipboardEvent::ClipboardEvent() +{ +} + +ClipboardEvent::ClipboardEvent(const AtomicString& eventType, bool canBubble, bool cancelable, Clipboard* clipboard) + : Event(eventType, canBubble, cancelable), m_clipboard(clipboard) +{ +} + +bool ClipboardEvent::isClipboardEvent() const +{ + return true; +} + +} // namespace WebCore diff --git a/WebCore/dom/ClipboardEvent.h b/WebCore/dom/ClipboardEvent.h new file mode 100644 index 0000000..69fc65b --- /dev/null +++ b/WebCore/dom/ClipboardEvent.h @@ -0,0 +1,49 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef ClipboardEvent_h +#define ClipboardEvent_h + +#include "Clipboard.h" +#include "Event.h" + +namespace WebCore { + + class ClipboardEvent : public Event { + public: + ClipboardEvent(); + ClipboardEvent(const AtomicString& type, bool canBubbleArg, bool cancelableArg, Clipboard* clipboardArg); + + Clipboard* clipboard() const { return m_clipboard.get(); } + + virtual bool isClipboardEvent() const; + + private: + RefPtr<Clipboard> m_clipboard; + }; + +} // namespace WebCore + +#endif // ClipboardEvent_h diff --git a/WebCore/dom/Comment.cpp b/WebCore/dom/Comment.cpp new file mode 100644 index 0000000..18bd89f --- /dev/null +++ b/WebCore/dom/Comment.cpp @@ -0,0 +1,72 @@ +/** + * 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) + * Copyright (C) 2003 Apple Computer, Inc. + * + * 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 "config.h" +#include "Comment.h" + +#include "Document.h" + +namespace WebCore { + +Comment::Comment(Document* doc, const String& text) + : CharacterData(doc, text) +{ +} + +Comment::Comment(Document* doc) + : CharacterData(doc) +{ +} + +Comment::~Comment() +{ +} + +String Comment::nodeName() const +{ + return commentAtom.string(); +} + +Node::NodeType Comment::nodeType() const +{ + return COMMENT_NODE; +} + +PassRefPtr<Node> Comment::cloneNode(bool /*deep*/) +{ + return document()->createComment(m_data); +} + +// DOM Section 1.1.1 +bool Comment::childTypeAllowed(NodeType) +{ + return false; +} + +String Comment::toString() const +{ + // FIXME: We need to substitute entity references here. + return "<!--" + nodeValue() + "-->"; +} + +} // namespace WebCore diff --git a/WebCore/dom/Comment.h b/WebCore/dom/Comment.h new file mode 100644 index 0000000..9e480e4 --- /dev/null +++ b/WebCore/dom/Comment.h @@ -0,0 +1,53 @@ +/* + * 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) + * Copyright (C) 2003 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef Comment_h +#define Comment_h + +#include "CharacterData.h" + +namespace WebCore { + +class Comment : public CharacterData +{ +public: + Comment(Document*, const String &_text); + Comment(Document*); + virtual ~Comment(); + + // DOM methods overridden from parent classes + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + + // Other methods (not part of DOM) + virtual bool isCommentNode() const { return true; } + virtual bool childTypeAllowed(NodeType); + + virtual String toString() const; +}; + +} // namespace WebCore + +#endif // Comment_h diff --git a/WebCore/dom/Comment.idl b/WebCore/dom/Comment.idl new file mode 100644 index 0000000..a89f0e7 --- /dev/null +++ b/WebCore/dom/Comment.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=5D16069F-7E6B-4b28-8647-C36B2ED81ED1, + ImplementationUUID=CB55DB55-411F-451f-97C6-284B99E77F8E + ] Comment : CharacterData { + }; + +} diff --git a/WebCore/dom/ContainerNode.cpp b/WebCore/dom/ContainerNode.cpp new file mode 100644 index 0000000..7103d72 --- /dev/null +++ b/WebCore/dom/ContainerNode.cpp @@ -0,0 +1,1001 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "ContainerNode.h" + +#include "DeleteButtonController.h" +#include "Document.h" +#include "Editor.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "FrameView.h" +#include "InlineTextBox.h" +#include "MutationEvent.h" +#include "RenderTheme.h" +#include "RootInlineBox.h" +#include "SystemTime.h" +#include <wtf/Vector.h> + +namespace WebCore { + +using namespace EventNames; + +static void dispatchChildInsertionEvents(Node*, ExceptionCode&); +static void dispatchChildRemovalEvents(Node*, ExceptionCode&); + +typedef Vector<std::pair<NodeCallback, RefPtr<Node> > > NodeCallbackQueue; +static NodeCallbackQueue* s_postAttachCallbackQueue = 0; + +static size_t s_attachDepth = 0; + +ContainerNode::ContainerNode(Document* doc) + : EventTargetNode(doc), m_firstChild(0), m_lastChild(0) +{ +} + +void ContainerNode::addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode* container) +{ + // We have to tell all children that their parent has died. + Node* n; + Node* next; + for (n = container->firstChild(); n != 0; n = next) { + ASSERT(!n->m_deletionHasBegun); + + next = n->nextSibling(); + n->setPreviousSibling(0); + n->setNextSibling(0); + n->setParent(0); + + if (!n->refCount()) { +#ifndef NDEBUG + n->m_deletionHasBegun = true; +#endif + // Add the node to the list of nodes to be deleted. + // Reuse the nextSibling pointer for this purpose. + if (tail) + tail->setNextSibling(n); + else + head = n; + tail = n; + } else if (n->inDocument()) + n->removedFromDocument(); + } + container->setFirstChild(0); + container->setLastChild(0); +} + +void ContainerNode::removeAllChildren() +{ + // List of nodes to be deleted. + Node* head = 0; + Node* tail = 0; + + addChildNodesToDeletionQueue(head, tail, this); + + Node* n; + Node* next; + while ((n = head) != 0) { + ASSERT(n->m_deletionHasBegun); + + next = n->nextSibling(); + n->setNextSibling(0); + + head = next; + if (next == 0) + tail = 0; + + if (n->hasChildNodes()) + addChildNodesToDeletionQueue(head, tail, static_cast<ContainerNode*>(n)); + + delete n; + } +} + +ContainerNode::~ContainerNode() +{ + removeAllChildren(); +} + + +Node* ContainerNode::virtualFirstChild() const +{ + return m_firstChild; +} + +Node* ContainerNode::virtualLastChild() const +{ + return m_lastChild; +} + +bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec) +{ + // Check that this node is not "floating". + // If it is, it can be deleted as a side effect of sending mutation events. + ASSERT(refCount() || parent()); + + ec = 0; + + // insertBefore(node, 0) is equivalent to appendChild(node) + if (!refChild) + return appendChild(newChild, ec); + + // Make sure adding the new child is OK. + checkAddChild(newChild.get(), ec); + if (ec) + return false; + + // NOT_FOUND_ERR: Raised if refChild is not a child of this node + if (refChild->parentNode() != this) { + ec = NOT_FOUND_ERR; + return false; + } + + bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE; + + // If newChild is a DocumentFragment with no children; there's nothing to do. + // Just return true + if (isFragment && !newChild->firstChild()) + return true; + + // Now actually add the child(ren) + if (refChild->previousSibling() == newChild || refChild == newChild) // nothing to do + return true; + + RefPtr<Node> next = refChild; + RefPtr<Node> prev = refChild->previousSibling(); + + int childCountDelta = 0; + RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild; + while (child) { + RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0; + + // If child is already present in the tree, first remove it from the old location. + if (Node* oldParent = child->parentNode()) + oldParent->removeChild(child.get(), ec); + if (ec) + return 0; + + // FIXME: After sending the mutation events, "this" could be destroyed. + // We can prevent that by doing a "ref", but first we have to make sure + // that no callers call with ref count == 0 and parent = 0 (as of this + // writing, there are definitely callers who call that way). + + // Due to arbitrary code running in response to a DOM mutation event it's + // possible that "next" is no longer a child of "this". + // It's also possible that "child" has been inserted elsewhere. + // In either of those cases, we'll just stop. + if (next->parentNode() != this) + break; + if (child->parentNode()) + break; + + ASSERT(!child->nextSibling()); + ASSERT(!child->previousSibling()); + + childCountDelta++; + + // Add child before "next". + forbidEventDispatch(); + Node* prev = next->previousSibling(); + ASSERT(m_lastChild != prev); + next->setPreviousSibling(child.get()); + if (prev) { + ASSERT(m_firstChild != next); + ASSERT(prev->nextSibling() == next); + prev->setNextSibling(child.get()); + } else { + ASSERT(m_firstChild == next); + m_firstChild = child.get(); + } + child->setParent(this); + child->setPreviousSibling(prev); + child->setNextSibling(next.get()); + allowEventDispatch(); + + // Dispatch the mutation events. + dispatchChildInsertionEvents(child.get(), ec); + + // Add child to the rendering tree. + if (attached() && !child->attached() && child->parent() == this) + child->attach(); + + child = nextChild.release(); + } + + document()->setDocumentChanged(true); + if (childCountDelta) + childrenChanged(false, prev.get(), next.get(), childCountDelta); + dispatchSubtreeModifiedEvent(); + return true; +} + +bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec) +{ + // Check that this node is not "floating". + // If it is, it can be deleted as a side effect of sending mutation events. + ASSERT(refCount() || parent()); + + ec = 0; + + if (oldChild == newChild) // nothing to do + return true; + + // Make sure replacing the old child with the new is ok + checkReplaceChild(newChild.get(), oldChild, ec); + if (ec) + return false; + + // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. + if (!oldChild || oldChild->parentNode() != this) { + ec = NOT_FOUND_ERR; + return false; + } + + RefPtr<Node> prev = oldChild->previousSibling(); + RefPtr<Node> next = oldChild->nextSibling(); + + // Remove the node we're replacing + RefPtr<Node> removedChild = oldChild; + removeChild(oldChild, ec); + if (ec) + return false; + + // FIXME: After sending the mutation events, "this" could be destroyed. + // We can prevent that by doing a "ref", but first we have to make sure + // that no callers call with ref count == 0 and parent = 0 (as of this + // writing, there are definitely callers who call that way). + + bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE; + + // Add the new child(ren) + int childCountDelta = 0; + RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild; + while (child) { + // If the new child is already in the right place, we're done. + if (prev && (prev == child || prev == child->previousSibling())) + break; + + // For a fragment we have more children to do. + RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0; + + // Remove child from its old position. + if (Node* oldParent = child->parentNode()) + oldParent->removeChild(child.get(), ec); + if (ec) + return 0; + + // Due to arbitrary code running in response to a DOM mutation event it's + // possible that "prev" is no longer a child of "this". + // It's also possible that "child" has been inserted elsewhere. + // In either of those cases, we'll just stop. + if (prev && prev->parentNode() != this) + break; + if (child->parentNode()) + break; + + childCountDelta++; + + ASSERT(!child->nextSibling()); + ASSERT(!child->previousSibling()); + + // Add child after "prev". + forbidEventDispatch(); + Node* next; + if (prev) { + next = prev->nextSibling(); + ASSERT(m_firstChild != next); + prev->setNextSibling(child.get()); + } else { + next = m_firstChild; + m_firstChild = child.get(); + } + if (next) { + ASSERT(m_lastChild != prev); + ASSERT(next->previousSibling() == prev); + next->setPreviousSibling(child.get()); + } else { + ASSERT(m_lastChild == prev); + m_lastChild = child.get(); + } + child->setParent(this); + child->setPreviousSibling(prev.get()); + child->setNextSibling(next); + allowEventDispatch(); + + // Dispatch the mutation events + dispatchChildInsertionEvents(child.get(), ec); + + // Add child to the rendering tree + if (attached() && !child->attached() && child->parent() == this) + child->attach(); + + prev = child; + child = nextChild.release(); + } + + document()->setDocumentChanged(true); + if (childCountDelta) + childrenChanged(false, prev.get(), next.get(), childCountDelta); + dispatchSubtreeModifiedEvent(); + return true; +} + +void ContainerNode::willRemove() +{ + for (Node *n = m_firstChild; n != 0; n = n->nextSibling()) + n->willRemove(); + EventTargetNode::willRemove(); +} + +static ExceptionCode willRemoveChild(Node *child) +{ + ExceptionCode ec = 0; + + // fire removed from document mutation events. + dispatchChildRemovalEvents(child, ec); + if (ec) + return ec; + + if (child->attached()) + child->willRemove(); + + return 0; +} + +bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec) +{ + // Check that this node is not "floating". + // If it is, it can be deleted as a side effect of sending mutation events. + ASSERT(refCount() || parent()); + + ec = 0; + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return false; + } + + // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. + if (!oldChild || oldChild->parentNode() != this) { + ec = NOT_FOUND_ERR; + return false; + } + + RefPtr<Node> child = oldChild; + + // dispatch pre-removal mutation events + if (document()->hasListenerType(Document::DOMNODEREMOVED_LISTENER)) { + EventTargetNodeCast(child.get())->dispatchEvent(new MutationEvent(DOMNodeRemovedEvent, true, false, + this, String(), String(), String(), 0), ec, true); + if (ec) + return false; + } + + ec = willRemoveChild(child.get()); + if (ec) + return false; + + // Mutation events might have moved this child into a different parent. + if (child->parentNode() != this) { + ec = NOT_FOUND_ERR; + return false; + } + + document()->removeFocusedNodeOfSubtree(child.get()); + + // FIXME: After sending the mutation events, "this" could be destroyed. + // We can prevent that by doing a "ref", but first we have to make sure + // that no callers call with ref count == 0 and parent = 0 (as of this + // writing, there are definitely callers who call that way). + + forbidEventDispatch(); + + // Remove from rendering tree + if (child->attached()) + child->detach(); + + // Remove the child + Node *prev, *next; + prev = child->previousSibling(); + next = child->nextSibling(); + + if (next) + next->setPreviousSibling(prev); + if (prev) + prev->setNextSibling(next); + if (m_firstChild == child) + m_firstChild = next; + if (m_lastChild == child) + m_lastChild = prev; + + child->setPreviousSibling(0); + child->setNextSibling(0); + child->setParent(0); + + allowEventDispatch(); + + document()->setDocumentChanged(true); + + // Dispatch post-removal mutation events + childrenChanged(false, prev, next, -1); + dispatchSubtreeModifiedEvent(); + + if (child->inDocument()) + child->removedFromDocument(); + else + child->removedFromTree(true); + + return child; +} + +// this differs from other remove functions because it forcibly removes all the children, +// regardless of read-only status or event exceptions, e.g. +bool ContainerNode::removeChildren() +{ + if (!m_firstChild) + return false; + + Node* n; + + // do any prep work needed before actually starting to detach + // and remove... e.g. stop loading frames, fire unload events + for (n = m_firstChild; n; n = n->nextSibling()) + willRemoveChild(n); + + // exclude this node when looking for removed focusedNode since only children will be removed + document()->removeFocusedNodeOfSubtree(this, true); + + forbidEventDispatch(); + int childCountDelta = 0; + while ((n = m_firstChild) != 0) { + childCountDelta--; + + Node *next = n->nextSibling(); + + n->ref(); + + // Remove the node from the tree before calling detach or removedFromDocument (4427024, 4129744) + n->setPreviousSibling(0); + n->setNextSibling(0); + n->setParent(0); + + m_firstChild = next; + if (n == m_lastChild) + m_lastChild = 0; + + if (n->attached()) + n->detach(); + + if (n->inDocument()) + n->removedFromDocument(); + + n->deref(); + } + allowEventDispatch(); + + // Dispatch a single post-removal mutation event denoting a modified subtree. + childrenChanged(false, 0, 0, childCountDelta); + dispatchSubtreeModifiedEvent(); + + return true; +} + +bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec) +{ + // Check that this node is not "floating". + // If it is, it can be deleted as a side effect of sending mutation events. + ASSERT(refCount() || parent()); + + ec = 0; + + // Make sure adding the new child is ok + checkAddChild(newChild.get(), ec); + if (ec) + return 0; + + if (newChild == m_lastChild) // nothing to do + return newChild; + + bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE; + + // If newChild is a DocumentFragment with no children.... there's nothing to do. + // Just return the document fragment + if (isFragment && !newChild->firstChild()) + return true; + + // Now actually add the child(ren) + int childCountDelta = 0; + RefPtr<Node> prev = lastChild(); + RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild; + while (child) { + // For a fragment we have more children to do. + RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0; + + // If child is already present in the tree, first remove it + if (Node* oldParent = child->parentNode()) { + oldParent->removeChild(child.get(), ec); + if (ec) + return 0; + + // If the child has a parent again, just stop what we're doing, because + // that means someone is doing something with DOM mutation -- can't re-parent + // a child that already has a parent. + if (child->parentNode()) + break; + } + + // Append child to the end of the list + childCountDelta++; + forbidEventDispatch(); + child->setParent(this); + if (m_lastChild) { + child->setPreviousSibling(m_lastChild); + m_lastChild->setNextSibling(child.get()); + } else + m_firstChild = child.get(); + m_lastChild = child.get(); + allowEventDispatch(); + + // Dispatch the mutation events + dispatchChildInsertionEvents(child.get(), ec); + + // Add child to the rendering tree + if (attached() && !child->attached() && child->parent() == this) + child->attach(); + + child = nextChild.release(); + } + + document()->setDocumentChanged(true); + childrenChanged(false, prev.get(), 0, childCountDelta); + dispatchSubtreeModifiedEvent(); + return true; +} + +bool ContainerNode::hasChildNodes() const +{ + return m_firstChild; +} + +ContainerNode* ContainerNode::addChild(PassRefPtr<Node> newChild) +{ + // This function is only used during parsing. + // It does not send any DOM mutation events. + + // Check for consistency with DTD, but only when parsing HTML. + if (document()->isHTMLDocument() && !childAllowed(newChild.get())) + return 0; + + forbidEventDispatch(); + newChild->setParent(this); + Node* last = m_lastChild; + if (m_lastChild) { + newChild->setPreviousSibling(m_lastChild); + m_lastChild->setNextSibling(newChild.get()); + } else + m_firstChild = newChild.get(); + m_lastChild = newChild.get(); + allowEventDispatch(); + + document()->incDOMTreeVersion(); + if (inDocument()) + newChild->insertedIntoDocument(); + childrenChanged(true, last, 0, 1); + + if (newChild->isElementNode()) + return static_cast<ContainerNode*>(newChild.get()); + return this; +} + +void ContainerNode::suspendPostAttachCallbacks() +{ + ++s_attachDepth; +} + +void ContainerNode::resumePostAttachCallbacks() +{ + if (s_attachDepth == 1 && s_postAttachCallbackQueue) + dispatchPostAttachCallbacks(); + --s_attachDepth; +} + +void ContainerNode::queuePostAttachCallback(NodeCallback callback, Node* node) +{ + if (!s_postAttachCallbackQueue) + s_postAttachCallbackQueue = new NodeCallbackQueue; + + s_postAttachCallbackQueue->append(std::pair<NodeCallback, RefPtr<Node> >(callback, node)); +} + +void ContainerNode::dispatchPostAttachCallbacks() +{ + // We recalculate size() each time through the loop because a callback + // can add more callbacks to the end of the queue. + for (size_t i = 0; i < s_postAttachCallbackQueue->size(); ++i) { + std::pair<NodeCallback, RefPtr<Node> >& pair = (*s_postAttachCallbackQueue)[i]; + NodeCallback callback = pair.first; + Node* node = pair.second.get(); + + callback(node); + } + s_postAttachCallbackQueue->clear(); +} + +void ContainerNode::attach() +{ + ++s_attachDepth; + + for (Node* child = m_firstChild; child; child = child->nextSibling()) + child->attach(); + EventTargetNode::attach(); + + if (s_attachDepth == 1 && s_postAttachCallbackQueue) + dispatchPostAttachCallbacks(); + --s_attachDepth; +} + +void ContainerNode::detach() +{ + for (Node* child = m_firstChild; child; child = child->nextSibling()) + child->detach(); + EventTargetNode::detach(); +} + +void ContainerNode::insertedIntoDocument() +{ + EventTargetNode::insertedIntoDocument(); + for (Node *child = m_firstChild; child; child = child->nextSibling()) + child->insertedIntoDocument(); +} + +void ContainerNode::removedFromDocument() +{ + EventTargetNode::removedFromDocument(); + for (Node *child = m_firstChild; child; child = child->nextSibling()) + child->removedFromDocument(); +} + +void ContainerNode::insertedIntoTree(bool deep) +{ + EventTargetNode::insertedIntoTree(deep); + if (deep) { + for (Node *child = m_firstChild; child; child = child->nextSibling()) + child->insertedIntoTree(deep); + } +} + +void ContainerNode::removedFromTree(bool deep) +{ + EventTargetNode::removedFromTree(deep); + if (deep) { + for (Node *child = m_firstChild; child; child = child->nextSibling()) + child->removedFromTree(deep); + } +} + +void ContainerNode::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + Node::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + if (document()->hasNodeLists()) + notifyNodeListsChildrenChanged(); +} + +void ContainerNode::cloneChildNodes(ContainerNode *clone) +{ + // disable the delete button so it's elements are not serialized into the markup + if (document()->frame()) + document()->frame()->editor()->deleteButtonController()->disable(); + ExceptionCode ec = 0; + for (Node* n = firstChild(); n && !ec; n = n->nextSibling()) + clone->appendChild(n->cloneNode(true), ec); + if (document()->frame()) + document()->frame()->editor()->deleteButtonController()->enable(); +} + +bool ContainerNode::getUpperLeftCorner(int &xPos, int &yPos) const +{ + if (!renderer()) + return false; + RenderObject *o = renderer(); + RenderObject *p = o; + + xPos = yPos = 0; + if (!o->isInline() || o->isReplaced()) { + o->absolutePosition(xPos, yPos); + return true; + } + + // find the next text/image child, to get a position + while(o) { + p = o; + if (o->firstChild()) + o = o->firstChild(); + else if(o->nextSibling()) + o = o->nextSibling(); + else { + RenderObject *next = 0; + while (!next && o->parent()) { + o = o->parent(); + next = o->nextSibling(); + } + o = next; + + if (!o) + break; + } + + if (!o->isInline() || o->isReplaced()) { + o->absolutePosition(xPos, yPos); + return true; + } + + if (p->element() && p->element() == this && o->isText() && !o->isBR() && !static_cast<RenderText*>(o)->firstTextBox()) { + // do nothing - skip unrendered whitespace that is a child or next sibling of the anchor + } else if ((o->isText() && !o->isBR()) || o->isReplaced()) { + o->container()->absolutePosition(xPos, yPos); + if (o->isText() && static_cast<RenderText *>(o)->firstTextBox()) { + xPos += static_cast<RenderText *>(o)->minXPos(); + yPos += static_cast<RenderText *>(o)->firstTextBox()->root()->topOverflow(); + } else { + xPos += o->xPos(); + yPos += o->yPos(); + } + return true; + } + } + + // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be + // at the end of the document. Scroll to the bottom. + if (!o && document()->view()) { + yPos += document()->view()->contentsHeight(); + return true; + } + return false; +} + +bool ContainerNode::getLowerRightCorner(int &xPos, int &yPos) const +{ + if (!renderer()) + return false; + + RenderObject *o = renderer(); + xPos = yPos = 0; + if (!o->isInline() || o->isReplaced()) + { + o->absolutePosition(xPos, yPos); + xPos += o->width(); + yPos += o->height() + o->borderTopExtra() + o->borderBottomExtra(); + return true; + } + // find the last text/image child, to get a position + while(o) { + if(o->lastChild()) + o = o->lastChild(); + else if(o->previousSibling()) + o = o->previousSibling(); + else { + RenderObject *prev = 0; + while(!prev) { + o = o->parent(); + if(!o) return false; + prev = o->previousSibling(); + } + o = prev; + } + if (o->isText() || o->isReplaced()) { + o->container()->absolutePosition(xPos, yPos); + if (o->isText()) + xPos += static_cast<RenderText *>(o)->minXPos() + o->width(); + else + xPos += o->xPos()+o->width(); + yPos += o->yPos()+o->height(); + return true; + } + } + return true; +} + +IntRect ContainerNode::getRect() const +{ + int xPos = 0, yPos = 0, xEnd = 0, yEnd = 0; + bool foundUpperLeft = getUpperLeftCorner(xPos,yPos); + bool foundLowerRight = getLowerRightCorner(xEnd,yEnd); + + // If we've found one corner, but not the other, + // then we should just return a point at the corner that we did find. + if (foundUpperLeft != foundLowerRight) + { + if (foundUpperLeft) { + xEnd = xPos; + yEnd = yPos; + } else { + xPos = xEnd; + yPos = yEnd; + } + } + + if (xEnd < xPos) + xEnd = xPos; + if (yEnd < yPos) + yEnd = yPos; + + return IntRect(xPos, yPos, xEnd - xPos, yEnd - yPos); +} + +void ContainerNode::setFocus(bool received) +{ + if (m_focused == received) return; + + EventTargetNode::setFocus(received); + + // note that we need to recalc the style + setChanged(); +} + +void ContainerNode::setActive(bool down, bool pause) +{ + if (down == active()) return; + + EventTargetNode::setActive(down); + + // note that we need to recalc the style + // FIXME: Move to Element + if (renderer()) { + bool reactsToPress = renderer()->style()->affectedByActiveRules(); + if (reactsToPress) + setChanged(); + if (renderer() && renderer()->style()->hasAppearance()) { + if (theme()->stateChanged(renderer(), PressedState)) + reactsToPress = true; + } + if (reactsToPress && pause) { + // The delay here is subtle. It relies on an assumption, namely that the amount of time it takes + // to repaint the "down" state of the control is about the same time as it would take to repaint the + // "up" state. Once you assume this, you can just delay for 100ms - that time (assuming that after you + // leave this method, it will be about that long before the flush of the up state happens again). +#ifdef HAVE_FUNC_USLEEP + double startTime = currentTime(); +#endif + + // Ensure there are no pending changes + Document::updateDocumentsRendering(); + // Do an immediate repaint. + if (renderer()) + renderer()->repaint(true); + + // FIXME: Find a substitute for usleep for Win32. + // Better yet, come up with a way of doing this that doesn't use this sort of thing at all. +#ifdef HAVE_FUNC_USLEEP + // Now pause for a small amount of time (1/10th of a second from before we repainted in the pressed state) + double remainingTime = 0.1 - (currentTime() - startTime); + if (remainingTime > 0) + usleep(static_cast<useconds_t>(remainingTime * 1000000.0)); +#endif + } + } +} + +void ContainerNode::setHovered(bool over) +{ + if (over == hovered()) return; + + EventTargetNode::setHovered(over); + + // note that we need to recalc the style + // FIXME: Move to Element + if (renderer()) { + if (renderer()->style()->affectedByHoverRules()) + setChanged(); + if (renderer() && renderer()->style()->hasAppearance()) + theme()->stateChanged(renderer(), HoverState); + } +} + +unsigned ContainerNode::childNodeCount() const +{ + unsigned count = 0; + Node *n; + for (n = firstChild(); n; n = n->nextSibling()) + count++; + return count; +} + +Node *ContainerNode::childNode(unsigned index) const +{ + unsigned i; + Node *n = firstChild(); + for (i = 0; n != 0 && i < index; i++) + n = n->nextSibling(); + return n; +} + +static void dispatchChildInsertionEvents(Node* child, ExceptionCode& ec) +{ + ASSERT(!eventDispatchForbidden()); + + RefPtr<Node> c = child; + DocPtr<Document> doc = child->document(); + + if (c->parentNode() && c->parentNode()->inDocument()) + c->insertedIntoDocument(); + else + c->insertedIntoTree(true); + + if (c->parentNode() && + doc->hasListenerType(Document::DOMNODEINSERTED_LISTENER) && + c->isEventTargetNode()) { + ec = 0; + EventTargetNodeCast(c.get())->dispatchEvent(new MutationEvent(DOMNodeInsertedEvent, true, false, + c->parentNode(), String(), String(), String(), 0), ec, true); + if (ec) + return; + } + + // dispatch the DOMNodeInsertedIntoDocument event to all descendants + if (c->inDocument() && doc->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) + for (; c; c = c->traverseNextNode(child)) { + if (!c->isEventTargetNode()) + continue; + + ec = 0; + EventTargetNodeCast(c.get())->dispatchEvent(new MutationEvent(DOMNodeInsertedIntoDocumentEvent, false, false, + 0, String(), String(), String(), 0), ec, true); + if (ec) + return; + } +} + +static void dispatchChildRemovalEvents(Node* child, ExceptionCode& ec) +{ + RefPtr<Node> c = child; + DocPtr<Document> doc = child->document(); + + // update auxiliary doc info (e.g. iterators) to note that node is being removed + doc->notifyBeforeNodeRemoval(child); // ### use events instead + + // dispatch pre-removal mutation events + if (c->parentNode() && + doc->hasListenerType(Document::DOMNODEREMOVED_LISTENER) && + c->isEventTargetNode()) { + ec = 0; + EventTargetNodeCast(c.get())->dispatchEvent(new MutationEvent(DOMNodeRemovedEvent, true, false, + c->parentNode(), String(), String(), String(), 0), ec, true); + if (ec) + return; + } + + // dispatch the DOMNodeRemovedFromDocument event to all descendants + if (c->inDocument() && doc->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) + for (; c; c = c->traverseNextNode(child)) { + if (!c->isEventTargetNode()) + continue; + ec = 0; + EventTargetNodeCast(c.get())->dispatchEvent(new MutationEvent(DOMNodeRemovedFromDocumentEvent, false, false, + 0, String(), String(), String(), 0), ec, true); + if (ec) + return; + } +} + +} diff --git a/WebCore/dom/ContainerNode.h b/WebCore/dom/ContainerNode.h new file mode 100644 index 0000000..9bc4634 --- /dev/null +++ b/WebCore/dom/ContainerNode.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef ContainerNode_h +#define ContainerNode_h + +#include "EventTargetNode.h" + +namespace WebCore { + +typedef void (*NodeCallback)(Node*); + +class ContainerNode : public EventTargetNode { +public: + ContainerNode(Document*); + virtual ~ContainerNode(); + + Node* firstChild() const { return m_firstChild; } + Node* lastChild() const { return m_lastChild; } + + virtual bool insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode&); + virtual bool replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode&); + virtual bool removeChild(Node* child, ExceptionCode&); + virtual bool appendChild(PassRefPtr<Node> newChild, ExceptionCode&); + + virtual ContainerNode* addChild(PassRefPtr<Node>); + virtual bool hasChildNodes() const; + virtual void attach(); + virtual void detach(); + virtual void willRemove(); + virtual IntRect getRect() const; + virtual void setFocus(bool = true); + virtual void setActive(bool active = true, bool pause = false); + virtual void setHovered(bool = true); + virtual unsigned childNodeCount() const; + virtual Node* childNode(unsigned index) const; + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void insertedIntoTree(bool deep); + virtual void removedFromTree(bool deep); + virtual void childrenChanged(bool createdByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + virtual bool removeChildren(); + + void removeAllChildren(); + + void cloneChildNodes(ContainerNode* clone); + +protected: + static void queuePostAttachCallback(NodeCallback, Node*); + static void suspendPostAttachCallbacks(); + static void resumePostAttachCallbacks(); + + void setFirstChild(Node* child) { m_firstChild = child; } + void setLastChild(Node* child) { m_lastChild = child; } + +private: + static void dispatchPostAttachCallbacks(); + + virtual Node* virtualFirstChild() const; + virtual Node* virtualLastChild() const; + + static void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode*); + + bool getUpperLeftCorner(int& x, int& y) const; + bool getLowerRightCorner(int& x, int& y) const; + + Node* m_firstChild; + Node* m_lastChild; +}; + +} // namespace WebCore + +#endif // ContainerNode_h diff --git a/WebCore/dom/DOMCoreException.h b/WebCore/dom/DOMCoreException.h new file mode 100644 index 0000000..48bf52c --- /dev/null +++ b/WebCore/dom/DOMCoreException.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DOMCoreException_h +#define DOMCodeException_h + +#include "ExceptionBase.h" + +namespace WebCore { + + class DOMCoreException : public ExceptionBase { + public: + DOMCoreException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + }; + +} // namespace WebCore + +#endif // DOMCoreException_h diff --git a/WebCore/dom/DOMCoreException.idl b/WebCore/dom/DOMCoreException.idl new file mode 100644 index 0000000..c9ac356 --- /dev/null +++ b/WebCore/dom/DOMCoreException.idl @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module core { + + interface [ + GenerateConstructor + ] DOMCoreException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(LANGUAGE_JAVASCRIPT) + // Override in a Mozilla compatible format + [DontEnum] DOMString toString(); +#endif + + // ExceptionCode + const unsigned short INDEX_SIZE_ERR = 1; + const unsigned short DOMSTRING_SIZE_ERR = 2; + const unsigned short HIERARCHY_REQUEST_ERR = 3; + const unsigned short WRONG_DOCUMENT_ERR = 4; + const unsigned short INVALID_CHARACTER_ERR = 5; + const unsigned short NO_DATA_ALLOWED_ERR = 6; + const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7; + const unsigned short NOT_FOUND_ERR = 8; + const unsigned short NOT_SUPPORTED_ERR = 9; + const unsigned short INUSE_ATTRIBUTE_ERR = 10; + // Introduced in DOM Level 2: + const unsigned short INVALID_STATE_ERR = 11; + // Introduced in DOM Level 2: + const unsigned short SYNTAX_ERR = 12; + // Introduced in DOM Level 2: + const unsigned short INVALID_MODIFICATION_ERR = 13; + // Introduced in DOM Level 2: + const unsigned short NAMESPACE_ERR = 14; + // Introduced in DOM Level 2: + const unsigned short INVALID_ACCESS_ERR = 15; + // Introduced in DOM Level 3: + const unsigned short VALIDATION_ERR = 16; + // Introduced in DOM Level 3: + const unsigned short TYPE_MISMATCH_ERR = 17; + }; + +} diff --git a/WebCore/dom/DOMImplementation.cpp b/WebCore/dom/DOMImplementation.cpp new file mode 100644 index 0000000..db277d5 --- /dev/null +++ b/WebCore/dom/DOMImplementation.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig (sam@webkit.org) + * + * 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 "config.h" +#include "DOMImplementation.h" + +#include "CSSStyleSheet.h" +#include "DocumentType.h" +#include "Element.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "FTPDirectoryDocument.h" +#include "HTMLDocument.h" +#include "HTMLNames.h" +#include "HTMLViewSourceDocument.h" +#include "Image.h" +#include "ImageDocument.h" +#include "MediaList.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "PluginDocument.h" +#include "PluginInfoStore.h" +#include "RegularExpression.h" +#include "Settings.h" +#include "TextDocument.h" +#include "XMLNames.h" + +#if ENABLE(SVG) +#include "SVGNames.h" +#include "SVGDocument.h" +#endif + +namespace WebCore { + +// FIXME: An implementation of this is still waiting for me to understand the distinction between +// a "malformed" qualified name and one with bad characters in it. For example, is a second colon +// an illegal character or a malformed qualified name? This will determine both what parameters +// this function needs to take and exactly what it will do. Should also be exported so that +// Element can use it too. +static bool qualifiedNameIsMalformed(const String&) +{ + return false; +} + +#if ENABLE(SVG) + +static void addString(HashSet<String, CaseFoldingHash>& set, const char* string) +{ + set.add(string); +} + +static bool isSVG10Feature(const String &feature) +{ + static bool initialized = false; + static HashSet<String, CaseFoldingHash> svgFeatures; + if (!initialized) { +#if ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT) && ENABLE(SVG_FILTER) && ENABLE(SVG_FONTS) + addString(svgFeatures, "svg"); + addString(svgFeatures, "svg.static"); +#endif +// addString(svgFeatures, "svg.animation"); +// addString(svgFeatures, "svg.dynamic"); +// addString(svgFeatures, "svg.dom.animation"); +// addString(svgFeatures, "svg.dom.dynamic"); +#if ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT) && ENABLE(SVG_FILTER) && ENABLE(SVG_FONTS) + addString(svgFeatures, "dom"); + addString(svgFeatures, "dom.svg"); + addString(svgFeatures, "dom.svg.static"); +#endif +// addString(svgFeatures, "svg.all"); +// addString(svgFeatures, "dom.svg.all"); + initialized = true; + } + return svgFeatures.contains(feature); +} + +static bool isSVG11Feature(const String &feature) +{ + static bool initialized = false; + static HashSet<String, CaseFoldingHash> svgFeatures; + if (!initialized) { + // Sadly, we cannot claim to implement any of the SVG 1.1 generic feature sets + // lack of Font and Filter support. + // http://bugs.webkit.org/show_bug.cgi?id=15480 +#if ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT) && ENABLE(SVG_FILTER) && ENABLE(SVG_FONTS) + addString(svgFeatures, "SVG"); + addString(svgFeatures, "SVGDOM"); + addString(svgFeatures, "SVG-static"); + addString(svgFeatures, "SVGDOM-static"); +#endif +// addString(svgFeatures, "SVG-animation); +// addString(svgFeatures, "SVGDOM-animation); +// addString(svgFeatures, "SVG-dynamic); +// addString(svgFeatures, "SVGDOM-dynamic); + addString(svgFeatures, "CoreAttribute"); +#if ENABLE(SVG_USE) + addString(svgFeatures, "Structure"); + addString(svgFeatures, "BasicStructure"); +#endif + addString(svgFeatures, "ContainerAttribute"); + addString(svgFeatures, "ConditionalProcessing"); + addString(svgFeatures, "Image"); + addString(svgFeatures, "Style"); + addString(svgFeatures, "ViewportAttribute"); + addString(svgFeatures, "Shape"); +// addString(svgFeatures, "Text"); // requires altGlyph, bug 6426 + addString(svgFeatures, "BasicText"); + addString(svgFeatures, "PaintAttribute"); + addString(svgFeatures, "BasicPaintAttribute"); + addString(svgFeatures, "OpacityAttribute"); + addString(svgFeatures, "GraphicsAttribute"); + addString(svgFeatures, "BaseGraphicsAttribute"); + addString(svgFeatures, "Marker"); +// addString(svgFeatures, "ColorProfile"); // requires color-profile, bug 6037 + addString(svgFeatures, "Gradient"); + addString(svgFeatures, "Pattern"); + addString(svgFeatures, "Clip"); + addString(svgFeatures, "BasicClip"); + addString(svgFeatures, "Mask"); +#if ENABLE(SVG_FILTER) +// addString(svgFeatures, "Filter"); + addString(svgFeatures, "BasicFilter"); +#endif + addString(svgFeatures, "DocumentEventsAttribute"); + addString(svgFeatures, "GraphicalEventsAttribute"); +// addString(svgFeatures, "AnimationEventsAttribute"); + addString(svgFeatures, "Cursor"); + addString(svgFeatures, "Hyperlinking"); + addString(svgFeatures, "XlinkAttribute"); + addString(svgFeatures, "ExternalResourcesRequired"); +// addString(svgFeatures, "View"); // buggy <view> support, bug 16962 + addString(svgFeatures, "Script"); +// addString(svgFeatures, "Animation"); // <animate> support missing +#if ENABLE(SVG_FONTS) + addString(svgFeatures, "Font"); + addString(svgFeatures, "BasicFont"); +#endif +#if ENABLE(SVG_FOREIGN_OBJECT) + addString(svgFeatures, "Extensibility"); +#endif + initialized = true; + } + return svgFeatures.contains(feature); +} +#endif + +DOMImplementation::~DOMImplementation() +{ +} + +bool DOMImplementation::hasFeature (const String& feature, const String& version) const +{ + String lower = feature.lower(); + if (lower == "core" || lower == "html" || lower == "xml" || lower == "xhtml") + return version.isEmpty() || version == "1.0" || version == "2.0"; + if (lower == "css" + || lower == "css2" + || lower == "events" + || lower == "htmlevents" + || lower == "mouseevents" + || lower == "mutationevents" + || lower == "range" + || lower == "stylesheets" + || lower == "traversal" + || lower == "uievents" + || lower == "views") + return version.isEmpty() || version == "2.0"; + if (lower == "xpath" || lower == "textevents") + return version.isEmpty() || version == "3.0"; + +#if ENABLE(SVG) + if ((version.isEmpty() || version == "1.1") && feature.startsWith("http://www.w3.org/tr/svg11/feature#", false)) { + if (isSVG11Feature(feature.right(feature.length() - 35))) + return true; + } + + if ((version.isEmpty() || version == "1.0") && feature.startsWith("org.w3c.", false)) { + if (isSVG10Feature(feature.right(feature.length() - 8))) + return true; + } +#endif + + return false; +} + +PassRefPtr<DocumentType> DOMImplementation::createDocumentType(const String& qualifiedName, + const String& publicId, const String& systemId, ExceptionCode& ec) +{ + // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied + if (qualifiedName.isNull()) { + ec = NAMESPACE_ERR; + return 0; + } + + // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character. + String prefix, localName; + if (!Document::parseQualifiedName(qualifiedName, prefix, localName)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + + // NAMESPACE_ERR: Raised if the qualifiedName is malformed. + if (qualifiedNameIsMalformed(qualifiedName)) { + ec = NAMESPACE_ERR; + return 0; + } + + ec = 0; + return new DocumentType(this, 0, qualifiedName, publicId, systemId); +} + +DOMImplementation* DOMImplementation::getInterface(const String& /*feature*/) const +{ + // ### + return 0; +} + +PassRefPtr<Document> DOMImplementation::createDocument(const String& namespaceURI, + const String& qualifiedName, DocumentType* doctype, ExceptionCode& ec) +{ + if (!qualifiedName.isEmpty()) { + // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character. + String prefix, localName; + if (!Document::parseQualifiedName(qualifiedName, prefix, localName)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + + // NAMESPACE_ERR: + // - Raised if the qualifiedName is malformed, + // - if the qualifiedName has a prefix and the namespaceURI is null, or + // - if the qualifiedName has a prefix that is "xml" and the namespaceURI is different + // from "http://www.w3.org/XML/1998/namespace" [Namespaces]. + int colonpos = qualifiedName.find(':'); + if (qualifiedNameIsMalformed(qualifiedName) || + (colonpos >= 0 && namespaceURI.isNull()) || + (colonpos == 3 && qualifiedName[0] == 'x' && qualifiedName[1] == 'm' && qualifiedName[2] == 'l' && +#if ENABLE(SVG) + namespaceURI != SVGNames::svgNamespaceURI && +#endif + namespaceURI != XMLNames::xmlNamespaceURI)) { + + ec = NAMESPACE_ERR; + return 0; + } + } + + // WRONG_DOCUMENT_ERR: Raised if doctype has already been used with a different document or was + // created from a different implementation. + if (doctype && (doctype->document() || doctype->implementation() != this)) { + ec = WRONG_DOCUMENT_ERR; + return 0; + } + + RefPtr<Document> doc; +#if ENABLE(SVG) + if (namespaceURI == SVGNames::svgNamespaceURI) + doc = new SVGDocument(this, 0); + else +#endif + if (namespaceURI == HTMLNames::xhtmlNamespaceURI) + doc = new Document(this, 0, true); + else + doc = new Document(this, 0); + + // now get the interesting parts of the doctype + if (doctype) + doc->addChild(doctype); + + if (!qualifiedName.isEmpty()) + doc->addChild(doc->createElementNS(namespaceURI, qualifiedName, ec)); + + ec = 0; + return doc.release(); +} + +PassRefPtr<CSSStyleSheet> DOMImplementation::createCSSStyleSheet(const String&, const String& media, ExceptionCode& ec) +{ + // ### TODO : title should be set, and media could have wrong syntax, in which case we should generate an exception. + ec = 0; + CSSStyleSheet* const nullSheet = 0; + RefPtr<CSSStyleSheet> sheet = new CSSStyleSheet(nullSheet); + sheet->setMedia(new MediaList(sheet.get(), media, true)); + return sheet.release(); +} + +PassRefPtr<Document> DOMImplementation::createDocument(Frame* frame) +{ + return new Document(this, frame); +} + +PassRefPtr<HTMLDocument> DOMImplementation::createHTMLDocument(Frame* frame) +{ + return new HTMLDocument(this, frame); +} + +DOMImplementation* DOMImplementation::instance() +{ + static RefPtr<DOMImplementation> i = new DOMImplementation; + return i.get(); +} + +bool DOMImplementation::isXMLMIMEType(const String& mimeType) +{ + if (mimeType == "text/xml" || mimeType == "application/xml" || mimeType == "text/xsl") + return true; + static const char* validChars = "[0-9a-zA-Z_\\-+~!$\\^{}|.%'`#&*]"; // per RFCs: 3023, 2045 + static RegularExpression xmlTypeRegExp(String("^") + validChars + "+/" + validChars + "+\\+xml$"); + return xmlTypeRegExp.match(mimeType) > -1; +} + +bool DOMImplementation::isTextMIMEType(const String& mimeType) +{ + if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) || + (mimeType.startsWith("text/") && mimeType != "text/html" && + mimeType != "text/xml" && mimeType != "text/xsl")) + return true; + + return false; +} + +PassRefPtr<HTMLDocument> DOMImplementation::createHTMLDocument(const String& title) +{ + RefPtr<HTMLDocument> d = new HTMLDocument(this, 0); + d->open(); + d->write("<!doctype html><html><head><title>" + title + "</title></head><body></body></html>"); + return d.release(); +} + +PassRefPtr<Document> DOMImplementation::createDocument(const String& type, Frame* frame, bool inViewSourceMode) +{ + if (inViewSourceMode) { + if (type == "text/html" || type == "application/xhtml+xml" || type == "image/svg+xml" || isTextMIMEType(type) || isXMLMIMEType(type)) + return new HTMLViewSourceDocument(this, frame, type); + } + + // Plugins cannot take HTML and XHTML from us, and we don't even need to initialize the plugin database for those. + if (type == "text/html") + return new HTMLDocument(this, frame); + if (type == "application/xhtml+xml") + return new Document(this, frame, true); + +#if ENABLE(FTPDIR) + // Plugins cannot take FTP from us either + if (type == "application/x-ftp-directory") + return new FTPDirectoryDocument(this, frame); +#endif + + // PDF is one image type for which a plugin can override built-in support. + // We do not want QuickTime to take over all image types, obviously. + if ((type == "application/pdf" || type == "text/pdf") && PluginInfoStore::supportsMIMEType(type)) + return new PluginDocument(this, frame); + if (Image::supportsType(type)) + return new ImageDocument(this, frame); + // Everything else except text/plain can be overridden by plugins. In particular, Adobe SVG Viewer should be used for SVG, if installed. + // Disallowing plug-ins to use text/plain prevents plug-ins from hijacking a fundamental type that the browser is expected to handle, + // and also serves as an optimization to prevent loading the plug-in database in the common case. + if (type != "text/plain" && PluginInfoStore::supportsMIMEType(type)) + return new PluginDocument(this, frame); + if (isTextMIMEType(type)) + return new TextDocument(this, frame); + +#if ENABLE(SVG) + if (type == "image/svg+xml") { + Settings* settings = frame ? frame->settings() : 0; + if (!settings || !settings->usesDashboardBackwardCompatibilityMode()) + return new SVGDocument(this, frame); + } +#endif + if (isXMLMIMEType(type)) + return new Document(this, frame); + + return new HTMLDocument(this, frame); +} + +} diff --git a/WebCore/dom/DOMImplementation.h b/WebCore/dom/DOMImplementation.h new file mode 100644 index 0000000..80b921c --- /dev/null +++ b/WebCore/dom/DOMImplementation.h @@ -0,0 +1,76 @@ +/* + * 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) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef DOMImplementation_h +#define DOMImplementation_h + +#include <wtf/RefCounted.h> +#include <wtf/Forward.h> + +namespace WebCore { + +class CSSStyleSheet; +class Document; +class DocumentType; +class Frame; +class HTMLDocument; +class String; + +typedef int ExceptionCode; + +class DOMImplementation : public RefCounted<DOMImplementation> { +public: + DOMImplementation() : RefCounted<DOMImplementation>(0) { } + virtual ~DOMImplementation(); + + // DOM methods & attributes for DOMImplementation + bool hasFeature(const String& feature, const String& version) const; + PassRefPtr<DocumentType> createDocumentType(const String& qualifiedName, const String& publicId, const String &systemId, ExceptionCode&); + PassRefPtr<Document> createDocument(const String& namespaceURI, const String& qualifiedName, DocumentType*, ExceptionCode&); + + DOMImplementation* getInterface(const String& feature) const; + + // From the DOMImplementationCSS interface + PassRefPtr<CSSStyleSheet> createCSSStyleSheet(const String& title, const String& media, ExceptionCode&); + + // From the HTMLDOMImplementation interface + PassRefPtr<HTMLDocument> createHTMLDocument(const String& title); + + // Other methods (not part of DOM) + PassRefPtr<Document> createDocument(const String& MIMEType, Frame*, bool inViewSourceMode); + PassRefPtr<Document> createDocument(Frame*); + PassRefPtr<HTMLDocument> createHTMLDocument(Frame*); + + // Returns the static instance of this class - only one instance of this class should + // ever be present, and is used as a factory method for creating Document objects + static DOMImplementation* instance(); + + static bool isXMLMIMEType(const String& MIMEType); + static bool isTextMIMEType(const String& MIMEType); +}; + +} //namespace + +#endif diff --git a/WebCore/dom/DOMImplementation.idl b/WebCore/dom/DOMImplementation.idl new file mode 100644 index 0000000..43d8c33 --- /dev/null +++ b/WebCore/dom/DOMImplementation.idl @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=B0CC344F-963C-4acc-9FC6-EB22649345E5, + ImplementationUUID=9E835092-2CA3-426b-826B-8272A8105E49 + ] DOMImplementation { + + // DOM Level 1 + + [OldStyleObjC] boolean hasFeature(in DOMString feature, + in [ConvertNullToNullString] DOMString version); + + // DOM Level 2 + + [OldStyleObjC] DocumentType createDocumentType(in DOMString qualifiedName, + in DOMString publicId, + in DOMString systemId) + raises(DOMException); + [OldStyleObjC] Document createDocument(in [ConvertNullToNullString] DOMString namespaceURI, + in [ConvertNullToNullString] DOMString qualifiedName, + in [ConvertNullToNullString] DocumentType doctype) + raises(DOMException); + + // DOMImplementationCSS interface from DOM Level 2 CSS + +#if !defined(LANGUAGE_COM) + [OldStyleObjC] CSSStyleSheet createCSSStyleSheet(in DOMString title, + in DOMString media) + raises(DOMException); + + // HTMLDOMImplementation interface from DOM Level 2 HTML + + HTMLDocument createHTMLDocument(in DOMString title); +#endif + }; + +} diff --git a/WebCore/dom/DocPtr.h b/WebCore/dom/DocPtr.h new file mode 100644 index 0000000..9882017 --- /dev/null +++ b/WebCore/dom/DocPtr.h @@ -0,0 +1,115 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * This file is part of the DOM implementation for KDE. + * Copyright (C) 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef DocPtr_h +#define DocPtr_h + +namespace WebCore { + +template <class T> class DocPtr { +public: + DocPtr() : m_ptr(0) {} + DocPtr(T *ptr) : m_ptr(ptr) { if (ptr) ptr->selfOnlyRef(); } + DocPtr(const DocPtr &o) : m_ptr(o.m_ptr) { if (T *ptr = m_ptr) ptr->selfOnlyRef(); } + ~DocPtr() { if (T *ptr = m_ptr) ptr->selfOnlyDeref(); } + + template <class U> DocPtr(const DocPtr<U> &o) : m_ptr(o.get()) { if (T *ptr = m_ptr) ptr->selfOnlyRef(); } + + void resetSkippingRef(T *o) { m_ptr = o; } + + T *get() const { return m_ptr; } + + T &operator*() const { return *m_ptr; } + T *operator->() const { return m_ptr; } + + bool operator!() const { return !m_ptr; } + + // this type conversion operator allows implicit conversion to + // bool but not to other integer types + + typedef T * (DocPtr::*UnspecifiedBoolType)() const; + operator UnspecifiedBoolType() const + { + return m_ptr ? &DocPtr::get : 0; + } + + DocPtr &operator=(const DocPtr &); + DocPtr &operator=(T *); + + private: + T *m_ptr; +}; + +template <class T> DocPtr<T> &DocPtr<T>::operator=(const DocPtr<T> &o) +{ + T *optr = o.m_ptr; + if (optr) + optr->selfOnlyRef(); + if (T *ptr = m_ptr) + ptr->selfOnlyDeref(); + m_ptr = optr; + return *this; +} + +template <class T> inline DocPtr<T> &DocPtr<T>::operator=(T *optr) +{ + if (optr) + optr->selfOnlyRef(); + if (T *ptr = m_ptr) + ptr->selfOnlyDeref(); + m_ptr = optr; + return *this; +} + +template <class T> inline bool operator==(const DocPtr<T> &a, const DocPtr<T> &b) +{ + return a.get() == b.get(); +} + +template <class T> inline bool operator==(const DocPtr<T> &a, const T *b) +{ + return a.get() == b; +} + +template <class T> inline bool operator==(const T *a, const DocPtr<T> &b) +{ + return a == b.get(); +} + +template <class T> inline bool operator!=(const DocPtr<T> &a, const DocPtr<T> &b) +{ + return a.get() != b.get(); +} + +template <class T> inline bool operator!=(const DocPtr<T> &a, const T *b) +{ + return a.get() != b; +} + +template <class T> inline bool operator!=(const T *a, const DocPtr<T> &b) +{ + return a != b.get(); +} + +} // namespace WebCore + +#endif // DocPtr_h diff --git a/WebCore/dom/Document.cpp b/WebCore/dom/Document.cpp new file mode 100644 index 0000000..f6de5de --- /dev/null +++ b/WebCore/dom/Document.cpp @@ -0,0 +1,3830 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "Document.h" + +#include "AXObjectCache.h" +#include "CDATASection.h" +#include "CachedCSSStyleSheet.h" +#include "CSSHelper.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "CSSValueKeywords.h" +#include "CString.h" +#include "ClassNodeList.h" +#include "Comment.h" +#include "CookieJar.h" +#include "DOMImplementation.h" +#include "DocLoader.h" +#include "DocumentFragment.h" +#include "DocumentLoader.h" +#include "DocumentType.h" +#include "DOMWindow.h" +#include "EditingText.h" +#include "Editor.h" +#include "EditorClient.h" +#include "EntityReference.h" +#include "Event.h" +#include "EventHandler.h" +#include "EventListener.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "HTMLBodyElement.h" +#include "HTMLDocument.h" +#include "HTMLElementFactory.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLHeadElement.h" +#include "HTMLImageLoader.h" +#include "HTMLInputElement.h" +#include "HTMLLinkElement.h" +#include "HTMLMapElement.h" +#include "HTMLNameCollection.h" +#include "HTMLNames.h" +#include "HTMLStyleElement.h" +#include "HTMLTitleElement.h" +#include "HTTPParsers.h" +#include "HistoryItem.h" +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "KeyboardEvent.h" +#include "Logging.h" +#include "MouseEvent.h" +#include "MouseEventWithHitTestResults.h" +#include "MutationEvent.h" +#include "NameNodeList.h" +#include "NodeFilter.h" +#include "NodeIterator.h" +#include "OverflowEvent.h" +#include "Page.h" +#include "PlatformKeyboardEvent.h" +#include "ProcessingInstruction.h" +#include "ProgressEvent.h" +#include "RegisteredEventListener.h" +#include "RegularExpression.h" +#include "RenderArena.h" +#include "RenderView.h" +#include "RenderWidget.h" +#include "SecurityOrigin.h" +#include "SegmentedString.h" +#include "SelectionController.h" +#include "Settings.h" +#include "StringHash.h" +#include "StyleSheetList.h" +#include "SystemTime.h" +#include "TextEvent.h" +#include "TextIterator.h" +#include "TextResourceDecoder.h" +#include "TreeWalker.h" +#include "UIEvent.h" +#include "WheelEvent.h" +#include "XMLHttpRequest.h" +#include "XMLTokenizer.h" +#include "kjs_binding.h" +#include "kjs_proxy.h" + +#if ENABLE(DATABASE) +#include "Database.h" +#include "DatabaseThread.h" +#endif + +#if ENABLE(CROSS_DOCUMENT_MESSAGING) +#include "MessageEvent.h" +#endif + +#if ENABLE(XPATH) +#include "XPathEvaluator.h" +#include "XPathExpression.h" +#include "XPathNSResolver.h" +#include "XPathResult.h" +#endif + +#if ENABLE(XSLT) +#include "XSLTProcessor.h" +#endif + +#if ENABLE(XBL) +#include "XBLBindingManager.h" +#endif + +#if ENABLE(SVG) +#include "SVGDocumentExtensions.h" +#include "SVGElementFactory.h" +#include "SVGZoomEvent.h" +#include "SVGStyleElement.h" +#include "TimeScheduler.h" +#endif + +using namespace std; +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +using namespace EventNames; +using namespace HTMLNames; + +// #define INSTRUMENT_LAYOUT_SCHEDULING 1 + +// This amount of time must have elapsed before we will even consider scheduling a layout without a delay. +// FIXME: For faster machines this value can really be lowered to 200. 250 is adequate, but a little high +// for dual G5s. :) +static const int cLayoutScheduleThreshold = 250; + +// Use 1 to represent the document's default form. +static HTMLFormElement* const defaultForm = reinterpret_cast<HTMLFormElement*>(1); + +// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's +static const unsigned PHI = 0x9e3779b9U; + +// DOM Level 2 says (letters added): +// +// a) Name start characters must have one of the categories Ll, Lu, Lo, Lt, Nl. +// b) Name characters other than Name-start characters must have one of the categories Mc, Me, Mn, Lm, or Nd. +// c) Characters in the compatibility area (i.e. with character code greater than #xF900 and less than #xFFFE) are not allowed in XML names. +// d) Characters which have a font or compatibility decomposition (i.e. those with a "compatibility formatting tag" in field 5 of the database -- marked by field 5 beginning with a "<") are not allowed. +// e) The following characters are treated as name-start characters rather than name characters, because the property file classifies them as Alphabetic: [#x02BB-#x02C1], #x0559, #x06E5, #x06E6. +// f) Characters #x20DD-#x20E0 are excluded (in accordance with Unicode, section 5.14). +// g) Character #x00B7 is classified as an extender, because the property list so identifies it. +// h) Character #x0387 is added as a name character, because #x00B7 is its canonical equivalent. +// i) Characters ':' and '_' are allowed as name-start characters. +// j) Characters '-' and '.' are allowed as name characters. +// +// It also contains complete tables. If we decide it's better, we could include those instead of the following code. + +static inline bool isValidNameStart(UChar32 c) +{ + // rule (e) above + if ((c >= 0x02BB && c <= 0x02C1) || c == 0x559 || c == 0x6E5 || c == 0x6E6) + return true; + + // rule (i) above + if (c == ':' || c == '_') + return true; + + // rules (a) and (f) above + const uint32_t nameStartMask = Letter_Lowercase | Letter_Uppercase | Letter_Other | Letter_Titlecase | Number_Letter; + if (!(Unicode::category(c) & nameStartMask)) + return false; + + // rule (c) above + if (c >= 0xF900 && c < 0xFFFE) + return false; + + // rule (d) above + DecompositionType decompType = decompositionType(c); + if (decompType == DecompositionFont || decompType == DecompositionCompat) + return false; + + return true; +} + +static inline bool isValidNamePart(UChar32 c) +{ + // rules (a), (e), and (i) above + if (isValidNameStart(c)) + return true; + + // rules (g) and (h) above + if (c == 0x00B7 || c == 0x0387) + return true; + + // rule (j) above + if (c == '-' || c == '.') + return true; + + // rules (b) and (f) above + const uint32_t otherNamePartMask = Mark_NonSpacing | Mark_Enclosing | Mark_SpacingCombining | Letter_Modifier | Number_DecimalDigit; + if (!(Unicode::category(c) & otherNamePartMask)) + return false; + + // rule (c) above + if (c >= 0xF900 && c < 0xFFFE) + return false; + + // rule (d) above + DecompositionType decompType = decompositionType(c); + if (decompType == DecompositionFont || decompType == DecompositionCompat) + return false; + + return true; +} + +static Widget* widgetForNode(Node* focusedNode) + { + if (!focusedNode) + return 0; + RenderObject* renderer = focusedNode->renderer(); + if (!renderer || !renderer->isWidget()) + return 0; + return static_cast<RenderWidget*>(renderer)->widget(); +} + +static bool acceptsEditingFocus(Node *node) +{ + ASSERT(node); + ASSERT(node->isContentEditable()); + + Node *root = node->rootEditableElement(); + Frame* frame = node->document()->frame(); + if (!frame || !root) + return false; + + return frame->editor()->shouldBeginEditing(rangeOfContents(root).get()); +} + +DeprecatedPtrList<Document>* Document::changedDocuments = 0; + +// FrameView might be 0 +Document::Document(DOMImplementation* impl, Frame* frame, bool isXHTML) + : ContainerNode(0) + , m_implementation(impl) + , m_domtree_version(0) + , m_styleSheets(new StyleSheetList(this)) + , m_title("") + , m_titleSetExplicitly(false) + , m_imageLoadEventTimer(this, &Document::imageLoadEventTimerFired) + , m_updateFocusAppearanceTimer(this, &Document::updateFocusAppearanceTimerFired) +#if ENABLE(XSLT) + , m_transformSource(0) +#endif + , m_xmlVersion("1.0") + , m_xmlStandalone(false) +#if ENABLE(XBL) + , m_bindingManager(new XBLBindingManager(this)) +#endif + , m_savedRenderer(0) + , m_secureForms(0) + , m_designMode(inherit) + , m_selfOnlyRefCount(0) +#if ENABLE(SVG) + , m_svgExtensions(0) +#endif + , m_hasDashboardRegions(false) + , m_dashboardRegionsDirty(false) + , m_accessKeyMapValid(false) + , m_createRenderers(true) + , m_inPageCache(false) + , m_isAllowedToLoadLocalResources(false) + , m_useSecureKeyboardEntryWhenActive(false) + , m_isXHTML(isXHTML) + , m_numNodeLists(0) +#if ENABLE(DATABASE) + , m_hasOpenDatabases(false) +#endif +#if USE(LOW_BANDWIDTH_DISPLAY) + , m_inLowBandwidthDisplay(false) +#endif +{ + m_document.resetSkippingRef(this); + + m_printing = false; + + m_frame = frame; + m_renderArena = 0; + + m_axObjectCache = 0; + + // FIXME: DocLoader probably no longer needs the frame argument + m_docLoader = new DocLoader(frame, this); + + visuallyOrdered = false; + m_bParsing = false; + m_docChanged = false; + m_tokenizer = 0; + m_wellFormed = false; + + setParseMode(Strict); + + m_textColor = Color::black; + m_listenerTypes = 0; + m_inDocument = true; + m_inStyleRecalc = false; + m_closeAfterStyleRecalc = false; + m_usesDescendantRules = false; + m_usesSiblingRules = false; + m_usesFirstLineRules = false; + m_usesFirstLetterRules = false; + m_gotoAnchorNeededAfterStylesheetsLoad = false; + + m_styleSelector = 0; + m_didCalculateStyleSelector = false; + m_pendingStylesheets = 0; + m_ignorePendingStylesheets = false; + m_hasNodesWithPlaceholderStyle = false; + m_pendingSheetLayout = NoLayoutWithPendingSheets; + + m_cssTarget = 0; + + resetLinkColor(); + resetVisitedLinkColor(); + resetActiveLinkColor(); + + m_processingLoadEvent = false; + m_startTime = currentTime(); + m_overMinimumLayoutThreshold = false; + + initSecurityOrigin(); + + static int docID = 0; + m_docID = docID++; +} + +void Document::removedLastRef() +{ + ASSERT(!m_deletionHasBegun); + if (m_selfOnlyRefCount) { + // If removing a child removes the last self-only ref, we don't + // want the document to be destructed until after + // removeAllChildren returns, so we guard ourselves with an + // extra self-only ref. + + DocPtr<Document> 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. + m_docType = 0; + m_focusedNode = 0; + m_hoverNode = 0; + m_activeNode = 0; + m_titleElement = 0; + m_documentElement = 0; + + removeAllChildren(); + + deleteAllValues(m_markers); + m_markers.clear(); + + delete m_tokenizer; + m_tokenizer = 0; + +#ifndef NDEBUG + m_inRemovedLastRefFunction = false; +#endif + } else { +#ifndef NDEBUG + m_deletionHasBegun = true; +#endif + delete this; + } +} + +Document::~Document() +{ + ASSERT(!renderer()); + ASSERT(!m_inPageCache); + ASSERT(!m_savedRenderer); + + removeAllEventListeners(); + +#if ENABLE(SVG) + delete m_svgExtensions; +#endif + + XMLHttpRequest::detachRequests(this); + { + KJS::JSLock lock; + ScriptInterpreter::forgetAllDOMNodesForDocument(this); + } + + if (m_docChanged && changedDocuments) + changedDocuments->remove(this); + delete m_tokenizer; + m_document.resetSkippingRef(0); + delete m_styleSelector; + delete m_docLoader; + + if (m_renderArena) { + delete m_renderArena; + m_renderArena = 0; + } + +#if ENABLE(XSLT) + xmlFreeDoc((xmlDocPtr)m_transformSource); +#endif + +#if ENABLE(XBL) + delete m_bindingManager; +#endif + + deleteAllValues(m_markers); + + if (m_axObjectCache) { + delete m_axObjectCache; + m_axObjectCache = 0; + } + m_decoder = 0; + + unsigned count = sizeof(m_nameCollectionInfo) / sizeof(m_nameCollectionInfo[0]); + for (unsigned i = 0; i < count; i++) + deleteAllValues(m_nameCollectionInfo[i]); + +#if ENABLE(DATABASE) + if (m_databaseThread) { + ASSERT(m_databaseThread->terminationRequested()); + m_databaseThread = 0; + } +#endif + + if (m_styleSheets) + m_styleSheets->documentDestroyed(); +} + +void Document::resetLinkColor() +{ + m_linkColor = Color(0, 0, 238); +} + +void Document::resetVisitedLinkColor() +{ + m_visitedLinkColor = Color(85, 26, 139); +} + +void Document::resetActiveLinkColor() +{ + m_activeLinkColor.setNamedColor("red"); +} + +void Document::setDocType(PassRefPtr<DocumentType> docType) +{ + // This should never be called more than once. + // Note: This is not a public DOM method and can only be called by the parser. + ASSERT(!m_docType || !docType); + if (m_docType && docType) + return; + m_docType = docType; + if (m_docType) + m_docType->setDocument(this); + determineParseMode(); +} + +DOMImplementation* Document::implementation() const +{ + return m_implementation.get(); +} + +void Document::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + + // Invalidate the document element we have cached in case it was replaced. + m_documentElement = 0; +} + +Element* Document::documentElement() const +{ + if (!m_documentElement) { + Node* n = firstChild(); + while (n && !n->isElementNode()) + n = n->nextSibling(); + m_documentElement = static_cast<Element*>(n); + } + + return m_documentElement.get(); +} + +PassRefPtr<Element> Document::createElement(const String &name, ExceptionCode& ec) +{ + if (m_isXHTML) { + if (!isValidName(name)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + + return HTMLElementFactory::createHTMLElement(AtomicString(name), this, 0, false); + } else + return createElementNS(nullAtom, name, ec); +} + +PassRefPtr<DocumentFragment> Document::createDocumentFragment() +{ + return new DocumentFragment(document()); +} + +PassRefPtr<Text> Document::createTextNode(const String &data) +{ + return new Text(this, data); +} + +PassRefPtr<Comment> Document::createComment (const String &data) +{ + return new Comment(this, data); +} + +PassRefPtr<CDATASection> Document::createCDATASection(const String &data, ExceptionCode& ec) +{ + if (isHTMLDocument()) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + return new CDATASection(this, data); +} + +PassRefPtr<ProcessingInstruction> Document::createProcessingInstruction(const String &target, const String &data, ExceptionCode& ec) +{ + if (!isValidName(target)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + if (isHTMLDocument()) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + return new ProcessingInstruction(this, target, data); +} + +PassRefPtr<EntityReference> Document::createEntityReference(const String &name, ExceptionCode& ec) +{ + if (!isValidName(name)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + if (isHTMLDocument()) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + return new EntityReference(this, name); +} + +PassRefPtr<EditingText> Document::createEditingTextNode(const String &text) +{ + return new EditingText(this, text); +} + +PassRefPtr<CSSStyleDeclaration> Document::createCSSStyleDeclaration() +{ + return new CSSMutableStyleDeclaration; +} + +PassRefPtr<Node> Document::importNode(Node* importedNode, bool deep, ExceptionCode& ec) +{ + ec = 0; + + if (!importedNode +#if ENABLE(SVG) + || (importedNode->isSVGElement() && page() && page()->settings()->usesDashboardBackwardCompatibilityMode()) +#endif + ) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + switch (importedNode->nodeType()) { + case TEXT_NODE: + return createTextNode(importedNode->nodeValue()); + case CDATA_SECTION_NODE: + return createCDATASection(importedNode->nodeValue(), ec); + case ENTITY_REFERENCE_NODE: + return createEntityReference(importedNode->nodeName(), ec); + case PROCESSING_INSTRUCTION_NODE: + return createProcessingInstruction(importedNode->nodeName(), importedNode->nodeValue(), ec); + case COMMENT_NODE: + return createComment(importedNode->nodeValue()); + case ELEMENT_NODE: { + Element* oldElement = static_cast<Element*>(importedNode); + RefPtr<Element> newElement = createElementNS(oldElement->namespaceURI(), oldElement->tagQName().toString(), ec); + + if (ec) + return 0; + + NamedAttrMap* attrs = oldElement->attributes(true); + if (attrs) { + unsigned length = attrs->length(); + for (unsigned i = 0; i < length; i++) { + Attribute* attr = attrs->attributeItem(i); + newElement->setAttribute(attr->name(), attr->value().impl(), ec); + if (ec) + return 0; + } + } + + newElement->copyNonAttributeProperties(oldElement); + + if (deep) { + for (Node* oldChild = oldElement->firstChild(); oldChild; oldChild = oldChild->nextSibling()) { + RefPtr<Node> newChild = importNode(oldChild, true, ec); + if (ec) + return 0; + newElement->appendChild(newChild.release(), ec); + if (ec) + return 0; + } + } + + return newElement.release(); + } + case ATTRIBUTE_NODE: { + RefPtr<Attr> newAttr = new Attr(0, this, static_cast<Attr*>(importedNode)->attr()->clone()); + newAttr->createTextChild(); + return newAttr.release(); + } + case DOCUMENT_FRAGMENT_NODE: { + DocumentFragment* oldFragment = static_cast<DocumentFragment*>(importedNode); + RefPtr<DocumentFragment> newFragment = createDocumentFragment(); + if (deep) { + for (Node* oldChild = oldFragment->firstChild(); oldChild; oldChild = oldChild->nextSibling()) { + RefPtr<Node> newChild = importNode(oldChild, true, ec); + if (ec) + return 0; + newFragment->appendChild(newChild.release(), ec); + if (ec) + return 0; + } + } + + return newFragment.release(); + } + case ENTITY_NODE: + case NOTATION_NODE: + // FIXME: It should be possible to import these node types, however in DOM3 the DocumentType is readonly, so there isn't much sense in doing that. + // Ability to add these imported nodes to a DocumentType will be considered for addition to a future release of the DOM. + case DOCUMENT_NODE: + case DOCUMENT_TYPE_NODE: + case XPATH_NAMESPACE_NODE: + break; + } + + ec = NOT_SUPPORTED_ERR; + return 0; +} + + +PassRefPtr<Node> Document::adoptNode(PassRefPtr<Node> source, ExceptionCode& ec) +{ + if (!source) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + if (source->isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return 0; + } + + switch (source->nodeType()) { + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_NODE: + case DOCUMENT_TYPE_NODE: + case XPATH_NAMESPACE_NODE: + ec = NOT_SUPPORTED_ERR; + return 0; + case ATTRIBUTE_NODE: { + Attr* attr = static_cast<Attr*>(source.get()); + if (attr->ownerElement()) + attr->ownerElement()->removeAttributeNode(attr, ec); + attr->m_attrWasSpecifiedOrElementHasRareData = true; + break; + } + default: + if (source->parentNode()) + source->parentNode()->removeChild(source.get(), ec); + } + + for (Node* node = source.get(); node; node = node->traverseNextNode(source.get())) + node->setDocument(this); + + return source; +} + +// FIXME: This should really be in a possible ElementFactory class +PassRefPtr<Element> Document::createElement(const QualifiedName& qName, bool createdByParser, ExceptionCode& ec) +{ + RefPtr<Element> e; + + // FIXME: Use registered namespaces and look up in a hash to find the right factory. + if (qName.namespaceURI() == xhtmlNamespaceURI) + e = HTMLElementFactory::createHTMLElement(qName.localName(), this, 0, createdByParser); +#if ENABLE(SVG) + else if (qName.namespaceURI() == SVGNames::svgNamespaceURI) + e = SVGElementFactory::createSVGElement(qName, this, createdByParser); +#endif + + if (!e) + e = new Element(qName, document()); + + if (e && !qName.prefix().isNull()) { + ec = 0; + e->setPrefix(qName.prefix(), ec); + if (ec) + return 0; + } + + return e.release(); +} + +PassRefPtr<Element> Document::createElementNS(const String &_namespaceURI, const String &qualifiedName, ExceptionCode& ec) +{ + String prefix, localName; + if (!parseQualifiedName(qualifiedName, prefix, localName)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + + RefPtr<Element> e; + QualifiedName qName = QualifiedName(AtomicString(prefix), AtomicString(localName), AtomicString(_namespaceURI)); + + return createElement(qName, false, ec); +} + +Element *Document::getElementById(const AtomicString& elementId) const +{ + if (elementId.length() == 0) + return 0; + + Element *element = m_elementsById.get(elementId.impl()); + if (element) + return element; + + if (m_duplicateIds.contains(elementId.impl())) { + // We know there's at least one node with this id, but we don't know what the first one is. + for (Node *n = traverseNextNode(); n != 0; n = n->traverseNextNode()) { + if (n->isElementNode()) { + element = static_cast<Element*>(n); + if (element->hasID() && element->getAttribute(idAttr) == elementId) { + m_duplicateIds.remove(elementId.impl()); + m_elementsById.set(elementId.impl(), element); + return element; + } + } + } + ASSERT_NOT_REACHED(); + } + return 0; +} + +String Document::readyState() const +{ + if (Frame* f = frame()) { + if (f->loader()->isComplete()) + return "complete"; + if (parsing()) + return "loading"; + return "loaded"; + // FIXME: What does "interactive" mean? + // FIXME: Missing support for "uninitialized". + } + return String(); +} + +String Document::inputEncoding() const +{ + if (TextResourceDecoder* d = decoder()) + return d->encoding().name(); + return String(); +} + +String Document::defaultCharset() const +{ + if (Settings* settings = this->settings()) + return settings->defaultTextEncodingName(); + return String(); +} + +void Document::setCharset(const String& charset) +{ + if (!decoder()) + return; + decoder()->setEncoding(charset, TextResourceDecoder::UserChosenEncoding); +} + +void Document::setXMLVersion(const String& version, ExceptionCode& ec) +{ + // FIXME: also raise NOT_SUPPORTED_ERR if the version is set to a value that is not supported by this Document. + if (!implementation()->hasFeature("XML", String())) { + ec = NOT_SUPPORTED_ERR; + return; + } + + m_xmlVersion = version; +} + +void Document::setXMLStandalone(bool standalone, ExceptionCode& ec) +{ + if (!implementation()->hasFeature("XML", String())) { + ec = NOT_SUPPORTED_ERR; + return; + } + + m_xmlStandalone = standalone; +} + +KURL Document::documentURI() const +{ + return m_baseURL; +} + +void Document::setDocumentURI(const String& uri) +{ + m_baseURL = KURL(uri); +} + +KURL Document::baseURI() const +{ + return m_baseURL; +} + +Element* Document::elementFromPoint(int x, int y) const +{ + if (!renderer()) + return 0; + + HitTestRequest request(true, true); + HitTestResult result(IntPoint(x, y)); + renderer()->layer()->hitTest(request, result); + + Node* n = result.innerNode(); + while (n && !n->isElementNode()) + n = n->parentNode(); + if (n) + n = n->shadowAncestorNode(); + return static_cast<Element*>(n); +} + +void Document::addElementById(const AtomicString& elementId, Element* element) +{ + typedef HashMap<AtomicStringImpl*, Element*>::iterator iterator; + if (!m_duplicateIds.contains(elementId.impl())) { + // Fast path. The ID is not already in m_duplicateIds, so we assume that it's + // also not already in m_elementsById and do an add. If that add succeeds, we're done. + pair<iterator, bool> addResult = m_elementsById.add(elementId.impl(), element); + if (addResult.second) + return; + // The add failed, so this ID was already cached in m_elementsById. + // There are multiple elements with this ID. Remove the m_elementsById + // cache for this ID so getElementById searches for it next time it is called. + m_elementsById.remove(addResult.first); + m_duplicateIds.add(elementId.impl()); + } else { + // There are multiple elements with this ID. If it exists, remove the m_elementsById + // cache for this ID so getElementById searches for it next time it is called. + iterator cachedItem = m_elementsById.find(elementId.impl()); + if (cachedItem != m_elementsById.end()) { + m_elementsById.remove(cachedItem); + m_duplicateIds.add(elementId.impl()); + } + } + m_duplicateIds.add(elementId.impl()); +} + +void Document::removeElementById(const AtomicString& elementId, Element* element) +{ + if (m_elementsById.get(elementId.impl()) == element) + m_elementsById.remove(elementId.impl()); + else + m_duplicateIds.remove(elementId.impl()); +} + +Element* Document::getElementByAccessKey(const String& key) const +{ + if (key.isEmpty()) + return 0; + if (!m_accessKeyMapValid) { + for (Node* n = firstChild(); n; n = n->traverseNextNode()) { + if (!n->isElementNode()) + continue; + Element* element = static_cast<Element*>(n); + const AtomicString& accessKey = element->getAttribute(accesskeyAttr); + if (!accessKey.isEmpty()) + m_elementsByAccessKey.set(accessKey.impl(), element); + } + m_accessKeyMapValid = true; + } + return m_elementsByAccessKey.get(key.impl()); +} + +void Document::updateTitle() +{ + if (Frame* f = frame()) + f->loader()->setTitle(m_title); +} + +void Document::setTitle(const String& title, Element* titleElement) +{ + if (!titleElement) { + // Title set by JavaScript -- overrides any title elements. + m_titleSetExplicitly = true; + if (!isHTMLDocument()) + m_titleElement = 0; + else if (!m_titleElement) { + if (HTMLElement* headElement = head()) { + ExceptionCode ec = 0; + m_titleElement = createElement("title", ec); + ASSERT(!ec); + headElement->appendChild(m_titleElement, ec); + ASSERT(!ec); + } + } + } else if (titleElement != m_titleElement) { + if (m_titleElement || m_titleSetExplicitly) + // Only allow the first title element to change the title -- others have no effect. + return; + m_titleElement = titleElement; + } + + if (m_title == title) + return; + + m_title = title; + updateTitle(); + + if (m_titleSetExplicitly && m_titleElement && m_titleElement->hasTagName(titleTag)) + static_cast<HTMLTitleElement*>(m_titleElement.get())->setText(m_title); +} + +void Document::removeTitle(Element* titleElement) +{ + if (m_titleElement != titleElement) + return; + + m_titleElement = 0; + m_titleSetExplicitly = false; + + // Update title based on first title element in the head, if one exists. + if (HTMLElement* headElement = head()) { + for (Node* e = headElement->firstChild(); e; e = e->nextSibling()) + if (e->hasTagName(titleTag)) { + HTMLTitleElement* titleElement = static_cast<HTMLTitleElement*>(e); + setTitle(titleElement->text(), titleElement); + break; + } + } + + if (!m_titleElement && !m_title.isEmpty()) { + m_title = ""; + updateTitle(); + } +} + +String Document::nodeName() const +{ + return "#document"; +} + +Node::NodeType Document::nodeType() const +{ + return DOCUMENT_NODE; +} + +FrameView* Document::view() const +{ + return m_frame ? m_frame->view() : 0; +} + +Page* Document::page() const +{ + return m_frame ? m_frame->page() : 0; +} + +Settings* Document::settings() const +{ + return m_frame ? m_frame->settings() : 0; +} + +PassRefPtr<Range> Document::createRange() +{ + return new Range(this); +} + +PassRefPtr<NodeIterator> Document::createNodeIterator(Node* root, unsigned whatToShow, + PassRefPtr<NodeFilter> filter, bool expandEntityReferences, ExceptionCode& ec) +{ + if (!root) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + return new NodeIterator(root, whatToShow, filter, expandEntityReferences); +} + +PassRefPtr<TreeWalker> Document::createTreeWalker(Node *root, unsigned whatToShow, + PassRefPtr<NodeFilter> filter, bool expandEntityReferences, ExceptionCode& ec) +{ + if (!root) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + return new TreeWalker(root, whatToShow, filter, expandEntityReferences); +} + +void Document::setDocumentChanged(bool b) +{ + if (b) { + if (!m_docChanged) { + if (!changedDocuments) + changedDocuments = new DeprecatedPtrList<Document>; + changedDocuments->append(this); + } + if (m_accessKeyMapValid) { + m_accessKeyMapValid = false; + m_elementsByAccessKey.clear(); + } + } else { + if (m_docChanged && changedDocuments) + changedDocuments->remove(this); + } + + m_docChanged = b; +} + +void Document::recalcStyle(StyleChange change) +{ + // we should not enter style recalc while painting + if (frame() && frame()->isPainting()) { + ASSERT(!frame()->isPainting()); + return; + } + + if (m_inStyleRecalc) + return; // Guard against re-entrancy. -dwh + + m_inStyleRecalc = true; + suspendPostAttachCallbacks(); + + ASSERT(!renderer() || renderArena()); + if (!renderer() || !renderArena()) + goto bail_out; + + if (change == Force) { + // style selector may set this again during recalc + m_hasNodesWithPlaceholderStyle = false; + + RenderStyle* oldStyle = renderer()->style(); + if (oldStyle) + oldStyle->ref(); + RenderStyle* _style = new (m_renderArena) RenderStyle(); + _style->ref(); + _style->setDisplay(BLOCK); + _style->setVisuallyOrdered(visuallyOrdered); + // ### make the font stuff _really_ work!!!! + + FontDescription fontDescription; + fontDescription.setUsePrinterFont(printing()); + if (Settings* settings = this->settings()) { + fontDescription.setRenderingMode(settings->fontRenderingMode()); + if (printing() && !settings->shouldPrintBackgrounds()) + _style->setForceBackgroundsToWhite(true); + const AtomicString& stdfont = settings->standardFontFamily(); + if (!stdfont.isEmpty()) { + fontDescription.firstFamily().setFamily(stdfont); + fontDescription.firstFamily().appendFamily(0); + } + fontDescription.setKeywordSize(CSS_VAL_MEDIUM - CSS_VAL_XX_SMALL + 1); + m_styleSelector->setFontSize(fontDescription, m_styleSelector->fontSizeForKeyword(CSS_VAL_MEDIUM, inCompatMode(), false)); + } + + _style->setFontDescription(fontDescription); + _style->font().update(m_styleSelector->fontSelector()); + if (inCompatMode()) + _style->setHtmlHacks(true); // enable html specific rendering tricks + + StyleChange ch = diff(_style, oldStyle); + if (renderer() && ch != NoChange) + renderer()->setStyle(_style); + if (change != Force) + change = ch; + + _style->deref(m_renderArena); + if (oldStyle) + oldStyle->deref(m_renderArena); + } + + for (Node* n = firstChild(); n; n = n->nextSibling()) + if (change >= Inherit || n->hasChangedChild() || n->changed()) + n->recalcStyle(change); + + if (changed() && view()) + view()->layout(); + +bail_out: + setChanged(NoStyleChange); + setHasChangedChild(false); + setDocumentChanged(false); + + resumePostAttachCallbacks(); + m_inStyleRecalc = false; + + // If we wanted to call implicitClose() during recalcStyle, do so now that we're finished. + if (m_closeAfterStyleRecalc) { + m_closeAfterStyleRecalc = false; + implicitClose(); + } +} + +void Document::updateRendering() +{ + if (hasChangedChild()) + recalcStyle(NoChange); +} + +void Document::updateDocumentsRendering() +{ + if (!changedDocuments) + return; + + while (Document* doc = changedDocuments->take()) { + doc->m_docChanged = false; + doc->updateRendering(); + } +} + +void Document::updateLayout() +{ + if (Element* oe = ownerElement()) + oe->document()->updateLayout(); + + // FIXME: Dave Hyatt's pretty sure we can remove this because layout calls recalcStyle as needed. + updateRendering(); + + // Only do a layout if changes have occurred that make it necessary. + FrameView* v = view(); + if (v && renderer() && (v->layoutPending() || renderer()->needsLayout())) + v->layout(); +} + +// FIXME: This is a bad idea and needs to be removed eventually. +// Other browsers load stylesheets before they continue parsing the web page. +// Since we don't, we can run JavaScript code that needs answers before the +// stylesheets are loaded. Doing a layout ignoring the pending stylesheets +// lets us get reasonable answers. The long term solution to this problem is +// to instead suspend JavaScript execution. +void Document::updateLayoutIgnorePendingStylesheets() +{ + bool oldIgnore = m_ignorePendingStylesheets; + + if (!haveStylesheetsLoaded()) { + m_ignorePendingStylesheets = true; + // FIXME: We are willing to attempt to suppress painting with outdated style info only once. Our assumption is that it would be + // dangerous to try to stop it a second time, after page content has already been loaded and displayed + // with accurate style information. (Our suppression involves blanking the whole page at the + // moment. If it were more refined, we might be able to do something better.) + // It's worth noting though that this entire method is a hack, since what we really want to do is + // suspend JS instead of doing a layout with inaccurate information. + if (body() && !body()->renderer() && m_pendingSheetLayout == NoLayoutWithPendingSheets) { + m_pendingSheetLayout = DidLayoutWithPendingSheets; + updateStyleSelector(); + } else if (m_hasNodesWithPlaceholderStyle) + // If new nodes have been added or style recalc has been done with style sheets still pending, some nodes + // may not have had their real style calculated yet. Normally this gets cleaned when style sheets arrive + // but here we need up-to-date style immediatly. + recalcStyle(Force); + } + + updateLayout(); + + m_ignorePendingStylesheets = oldIgnore; +} + +void Document::attach() +{ + ASSERT(!attached()); + ASSERT(!m_inPageCache); + + if (!m_renderArena) + m_renderArena = new RenderArena(); + + // Create the rendering tree + setRenderer(new (m_renderArena) RenderView(this, view())); + + if (!m_styleSelector) { + bool matchAuthorAndUserStyles = true; + if (Settings* docSettings = settings()) + matchAuthorAndUserStyles = docSettings->authorAndUserStylesEnabled(); + m_styleSelector = new CSSStyleSelector(this, userStyleSheet(), m_styleSheets.get(), m_mappedElementSheet.get(), !inCompatMode(), matchAuthorAndUserStyles); + m_styleSelector->setEncodedURL(m_url); + } + + recalcStyle(Force); + + RenderObject* render = renderer(); + setRenderer(0); + + ContainerNode::attach(); + + setRenderer(render); +} + +void Document::detach() +{ + ASSERT(attached()); + ASSERT(!m_inPageCache); + + RenderObject* render = renderer(); + + // indicate destruction mode, i.e. attached() but renderer == 0 + setRenderer(0); + + // Empty out these lists as a performance optimization, since detaching + // all the individual render objects will cause all the RenderImage + // objects to remove themselves from the lists. + m_imageLoadEventDispatchSoonList.clear(); + m_imageLoadEventDispatchingList.clear(); + + m_hoverNode = 0; + m_focusedNode = 0; + m_activeNode = 0; + + ContainerNode::detach(); + + if (render) + render->destroy(); + + // FIXME: is this needed or desirable? + m_frame = 0; + + if (m_renderArena) { + delete m_renderArena; + m_renderArena = 0; + } +} + +void Document::removeAllEventListenersFromAllNodes() +{ + m_windowEventListeners.clear(); + removeAllDisconnectedNodeEventListeners(); + for (Node *n = this; n; n = n->traverseNextNode()) { + if (!n->isEventTargetNode()) + continue; + EventTargetNodeCast(n)->removeAllEventListeners(); + } +} + +void Document::registerDisconnectedNodeWithEventListeners(Node* node) +{ + m_disconnectedNodesWithEventListeners.add(node); +} + +void Document::unregisterDisconnectedNodeWithEventListeners(Node* node) +{ + m_disconnectedNodesWithEventListeners.remove(node); +} + +void Document::removeAllDisconnectedNodeEventListeners() +{ + HashSet<Node*>::iterator end = m_disconnectedNodesWithEventListeners.end(); + for (HashSet<Node*>::iterator i = m_disconnectedNodesWithEventListeners.begin(); i != end; ++i) + EventTargetNodeCast(*i)->removeAllEventListeners(); + m_disconnectedNodesWithEventListeners.clear(); +} + +AXObjectCache* Document::axObjectCache() const +{ + // The only document that actually has a AXObjectCache is the top-level + // document. This is because we need to be able to get from any WebCoreAXObject + // to any other WebCoreAXObject on the same page. Using a single cache allows + // lookups across nested webareas (i.e. multiple documents). + + if (m_axObjectCache) { + // return already known top-level cache + if (!ownerElement()) + return m_axObjectCache; + + // In some pages with frames, the cache is created before the sub-webarea is + // inserted into the tree. Here, we catch that case and just toss the old + // cache and start over. + delete m_axObjectCache; + m_axObjectCache = 0; + } + + // ask the top-level document for its cache + Document* doc = topDocument(); + if (doc != this) + return doc->axObjectCache(); + + // this is the top-level document, so install a new cache + m_axObjectCache = new AXObjectCache; + return m_axObjectCache; +} + +void Document::setVisuallyOrdered() +{ + visuallyOrdered = true; + if (renderer()) + renderer()->style()->setVisuallyOrdered(true); +} + +Tokenizer* Document::createTokenizer() +{ + // FIXME: this should probably pass the frame instead + return new XMLTokenizer(this, view()); +} + +void Document::open() +{ + // This is work that we should probably do in clear(), but we can't have it + // happen when implicitOpen() is called unless we reorganize Frame code. + if (Document* parent = parentDocument()) { + if (m_url.isEmpty() || m_url == blankURL()) + setURL(parent->baseURL()); + if (m_baseURL.isEmpty() || m_baseURL == blankURL()) + setBaseURL(parent->baseURL()); + } + + if (m_frame) { + if (m_frame->loader()->isLoadingMainResource() || (tokenizer() && tokenizer()->executingScript())) + return; + + if (m_frame->loader()->state() == FrameStateProvisional) + m_frame->loader()->stopAllLoaders(); + } + + implicitOpen(); + + if (m_frame) + m_frame->loader()->didExplicitOpen(); +} + +void Document::cancelParsing() +{ + if (m_tokenizer) { + // We have to clear the tokenizer to avoid possibly triggering + // the onload handler when closing as a side effect of a cancel-style + // change, such as opening a new document or closing the window while + // still parsing + delete m_tokenizer; + m_tokenizer = 0; + close(); + } +} + +void Document::implicitOpen() +{ + cancelParsing(); + + clear(); + m_tokenizer = createTokenizer(); + setParsing(true); +} + +HTMLElement* Document::body() +{ + Node* de = documentElement(); + if (!de) + return 0; + + // try to prefer a FRAMESET element over BODY + Node* body = 0; + for (Node* i = de->firstChild(); i; i = i->nextSibling()) { + if (i->hasTagName(framesetTag)) + return static_cast<HTMLElement*>(i); + + if (i->hasTagName(bodyTag)) + body = i; + } + return static_cast<HTMLElement*>(body); +} + +void Document::setBody(PassRefPtr<HTMLElement> newBody, ExceptionCode& ec) +{ + if (!newBody) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + HTMLElement* b = body(); + if (!b) + documentElement()->appendChild(newBody, ec); + else + documentElement()->replaceChild(newBody, b, ec); +} + +HTMLHeadElement* Document::head() +{ + Node* de = documentElement(); + if (!de) + return 0; + + for (Node* e = de->firstChild(); e; e = e->nextSibling()) + if (e->hasTagName(headTag)) + return static_cast<HTMLHeadElement*>(e); + + return 0; +} + +void Document::close() +{ + Frame* frame = this->frame(); + if (frame) { + // This code calls implicitClose() if all loading has completed. + FrameLoader* frameLoader = frame->loader(); + frameLoader->endIfNotLoadingMainResource(); + frameLoader->checkCompleted(); + } else { + // Because we have no frame, we don't know if all loading has completed, + // so we just call implicitClose() immediately. FIXME: This might fire + // the load event prematurely <http://bugs.webkit.org/show_bug.cgi?id=14568>. + implicitClose(); + } +} + +void Document::implicitClose() +{ + // If we're in the middle of recalcStyle, we need to defer the close until the style information is accurate and all elements are re-attached. + if (m_inStyleRecalc) { + m_closeAfterStyleRecalc = true; + return; + } + + bool wasLocationChangePending = frame() && frame()->loader()->isScheduledLocationChangePending(); + bool doload = !parsing() && m_tokenizer && !m_processingLoadEvent && !wasLocationChangePending; + + if (!doload) + return; + + m_processingLoadEvent = true; + + m_wellFormed = m_tokenizer && m_tokenizer->wellFormed(); + + // We have to clear the tokenizer, in case someone document.write()s from the + // onLoad event handler, as in Radar 3206524. + delete m_tokenizer; + m_tokenizer = 0; + + // Create a body element if we don't already have one. See Radar 3758785. + if (!this->body() && isHTMLDocument()) { + if (Node* documentElement = this->documentElement()) { + ExceptionCode ec = 0; + documentElement->appendChild(new HTMLBodyElement(this), ec); + ASSERT(!ec); + } + } + + dispatchImageLoadEventsNow(); + this->dispatchWindowEvent(loadEvent, false, false); + if (Frame* f = frame()) + f->loader()->handledOnloadEvents(); +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("onload fired at %d\n", elapsedTime()); +#endif + + m_processingLoadEvent = false; + + // An event handler may have removed the frame + if (!frame()) + return; + + // Make sure both the initial layout and reflow happen after the onload + // fires. This will improve onload scores, and other browsers do it. + // If they wanna cheat, we can too. -dwh + + if (frame()->loader()->isScheduledLocationChangePending() && elapsedTime() < cLayoutScheduleThreshold) { + // Just bail out. Before or during the onload we were shifted to another page. + // The old i-Bench suite does this. When this happens don't bother painting or laying out. + view()->unscheduleRelayout(); + return; + } + + frame()->loader()->checkCallImplicitClose(); + + // Now do our painting/layout, but only if we aren't in a subframe or if we're in a subframe + // that has been sized already. Otherwise, our view size would be incorrect, so doing any + // layout/painting now would be pointless. + if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) { + updateRendering(); + + // Always do a layout after loading if needed. + if (view() && renderer() && (!renderer()->firstChild() || renderer()->needsLayout())) + view()->layout(); + + // Paint immediately after the document is ready. We do this to ensure that any timers set by the + // onload don't have a chance to fire before we would have painted. To avoid over-flushing we only + // worry about this for the top-level document. +#if !PLATFORM(MAC) + // FIXME: This causes a timing issue with the dispatchDidFinishLoad delegate callback. + // See <rdar://problem/5092361> + if (view() && !ownerElement()) + view()->update(); +#endif + } + +#if PLATFORM(MAC) + if (renderer() && AXObjectCache::accessibilityEnabled()) + axObjectCache()->postNotificationToElement(renderer(), "AXLoadComplete"); +#endif + +#if ENABLE(SVG) + // FIXME: Officially, time 0 is when the outermost <svg> receives its + // SVGLoad event, but we don't implement those yet. This is close enough + // for now. In some cases we should have fired earlier. + if (svgExtensions()) + accessSVGExtensions()->startAnimations(); +#endif +} + +void Document::setParsing(bool b) +{ + m_bParsing = b; + if (!m_bParsing && view()) + view()->scheduleRelayout(); + +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement() && !m_bParsing) + printf("Parsing finished at %d\n", elapsedTime()); +#endif +} + +bool Document::shouldScheduleLayout() +{ + // We can update layout if: + // (a) we actually need a layout + // (b) our stylesheets are all loaded + // (c) we have a <body> + return (renderer() && renderer()->needsLayout() && haveStylesheetsLoaded() && + documentElement() && documentElement()->renderer() && + (!documentElement()->hasTagName(htmlTag) || body())); +} + +int Document::minimumLayoutDelay() +{ + if (m_overMinimumLayoutThreshold) + return 0; + + int elapsed = elapsedTime(); + m_overMinimumLayoutThreshold = elapsed > cLayoutScheduleThreshold; + + // We'll want to schedule the timer to fire at the minimum layout threshold. + return max(0, cLayoutScheduleThreshold - elapsed); +} + +int Document::elapsedTime() const +{ + return static_cast<int>((currentTime() - m_startTime) * 1000); +} + +void Document::write(const String& text) +{ +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Beginning a document.write at %d\n", elapsedTime()); +#endif + + if (!m_tokenizer) { + open(); + ASSERT(m_tokenizer); + if (!m_tokenizer) + return; + write("<html>"); + } + m_tokenizer->write(text, false); + +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Ending a document.write at %d\n", elapsedTime()); +#endif +} + +void Document::writeln(const String& text) +{ + write(text); + write("\n"); +} + +void Document::finishParsing() +{ +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Received all data at %d\n", elapsedTime()); +#endif + + // Let the tokenizer go through as much data as it can. There will be three possible outcomes after + // finish() is called: + // (1) All remaining data is parsed, document isn't loaded yet + // (2) All remaining data is parsed, document is loaded, tokenizer gets deleted + // (3) Data is still remaining to be parsed. + if (m_tokenizer) + m_tokenizer->finish(); +} + +void Document::clear() +{ + delete m_tokenizer; + m_tokenizer = 0; + + removeChildren(); + + m_windowEventListeners.clear(); +} + +void Document::setURL(const KURL& url) +{ + if (url == m_url) + return; + + m_url = url; + if (m_styleSelector) + m_styleSelector->setEncodedURL(url); + + m_isAllowedToLoadLocalResources = shouldBeAllowedToLoadLocalResources(); + } + +bool Document::shouldBeAllowedToLoadLocalResources() const +{ + if (FrameLoader::shouldTreatURLAsLocal(m_url.string())) + return true; + + Frame* frame = this->frame(); + if (!frame) + return false; + + DocumentLoader* documentLoader = frame->loader()->documentLoader(); + if (!documentLoader) + return false; + + if (m_url == blankURL() && frame->loader()->opener() && frame->loader()->opener()->document()->isAllowedToLoadLocalResources()) + return true; + + return documentLoader->substituteData().isValid(); +} + +void Document::setBaseURL(const KURL& baseURL) +{ + m_baseURL = baseURL; + if (m_elemSheet) + m_elemSheet->setHref(baseURL.string()); +} + +void Document::setCSSStyleSheet(const String &url, const String& charset, const CachedCSSStyleSheet* sheet) +{ + m_sheet = new CSSStyleSheet(this, url, charset); + m_sheet->parseString(sheet->sheetText()); + + updateStyleSelector(); +} + +#if FRAME_LOADS_USER_STYLESHEET +void Document::setUserStyleSheet(const String& sheet) +{ + if (m_usersheet != sheet) { + m_usersheet = sheet; + updateStyleSelector(); + } +} +#endif + +String Document::userStyleSheet() const +{ +#if FRAME_LOADS_USER_STYLESHEET + return m_usersheet; +#else + Page* page = this->page(); + if (!page) + return String(); + return page->userStyleSheet(); +#endif +} + +CSSStyleSheet* Document::elementSheet() +{ + if (!m_elemSheet) + m_elemSheet = new CSSStyleSheet(this, baseURL().string()); + return m_elemSheet.get(); +} + +CSSStyleSheet* Document::mappedElementSheet() +{ + if (!m_mappedElementSheet) + m_mappedElementSheet = new CSSStyleSheet(this, baseURL().string()); + return m_mappedElementSheet.get(); +} + +static Node* nextNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event) +{ + // Search is inclusive of start + for (Node* n = start; n; n = n->traverseNextNode()) + if (n->isKeyboardFocusable(event) && n->tabIndex() == tabIndex) + return n; + + return 0; +} + +static Node* previousNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event) +{ + // Search is inclusive of start + for (Node* n = start; n; n = n->traversePreviousNode()) + if (n->isKeyboardFocusable(event) && n->tabIndex() == tabIndex) + return n; + + return 0; +} + +static Node* nextNodeWithGreaterTabIndex(Node* start, int tabIndex, KeyboardEvent* event) +{ + // Search is inclusive of start + int winningTabIndex = SHRT_MAX + 1; + Node* winner = 0; + for (Node* n = start; n; n = n->traverseNextNode()) + if (n->isKeyboardFocusable(event) && n->tabIndex() > tabIndex && n->tabIndex() < winningTabIndex) { + winner = n; + winningTabIndex = n->tabIndex(); + } + + return winner; +} + +static Node* previousNodeWithLowerTabIndex(Node* start, int tabIndex, KeyboardEvent* event) +{ + // Search is inclusive of start + int winningTabIndex = 0; + Node* winner = 0; + for (Node* n = start; n; n = n->traversePreviousNode()) + if (n->isKeyboardFocusable(event) && n->tabIndex() < tabIndex && n->tabIndex() > winningTabIndex) { + winner = n; + winningTabIndex = n->tabIndex(); + } + + return winner; +} + +Node* Document::nextFocusableNode(Node* start, KeyboardEvent* event) +{ + if (start) { + // First try to find a node with the same tabindex as start that comes after start in the document. + if (Node* winner = nextNodeWithExactTabIndex(start->traverseNextNode(), start->tabIndex(), event)) + return winner; + + if (start->tabIndex() == 0) + // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order. + return 0; + } + + // Look for the first node in the document that: + // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if start is null), and + // 2) comes first in the document, if there's a tie. + if (Node* winner = nextNodeWithGreaterTabIndex(this, start ? start->tabIndex() : 0, event)) + return winner; + + // There are no nodes with a tabindex greater than start's tabindex, + // so find the first node with a tabindex of 0. + return nextNodeWithExactTabIndex(this, 0, event); +} + +Node* Document::previousFocusableNode(Node* start, KeyboardEvent* event) +{ + Node* last; + for (last = this; last->lastChild(); last = last->lastChild()) + ; // Empty loop. + + // First try to find the last node in the document that comes before start and has the same tabindex as start. + // If start is null, find the last node in the document with a tabindex of 0. + Node* startingNode; + int startingTabIndex; + if (start) { + startingNode = start->traversePreviousNode(); + startingTabIndex = start->tabIndex(); + } else { + startingNode = last; + startingTabIndex = 0; + } + + if (Node* winner = previousNodeWithExactTabIndex(startingNode, startingTabIndex, event)) + return winner; + + // There are no nodes before start with the same tabindex as start, so look for a node that: + // 1) has the highest non-zero tabindex (that is less than start's tabindex), and + // 2) comes last in the document, if there's a tie. + startingTabIndex = (start && start->tabIndex()) ? start->tabIndex() : SHRT_MAX; + return previousNodeWithLowerTabIndex(last, startingTabIndex, event); +} + +int Document::nodeAbsIndex(Node *node) +{ + ASSERT(node->document() == this); + + int absIndex = 0; + for (Node *n = node; n && n != this; n = n->traversePreviousNode()) + absIndex++; + return absIndex; +} + +Node *Document::nodeWithAbsIndex(int absIndex) +{ + Node *n = this; + for (int i = 0; n && (i < absIndex); i++) { + n = n->traverseNextNode(); + } + return n; +} + +void Document::processHttpEquiv(const String &equiv, const String &content) +{ + ASSERT(!equiv.isNull() && !content.isNull()); + + Frame *frame = this->frame(); + + if (equalIgnoringCase(equiv, "default-style")) { + // The preferred style set has been overridden as per section + // 14.3.2 of the HTML4.0 specification. We need to update the + // sheet used variable and then update our style selector. + // For more info, see the test at: + // http://www.hixie.ch/tests/evil/css/import/main/preferred.html + // -dwh + m_selectedStylesheetSet = content; + m_preferredStylesheetSet = content; + updateStyleSelector(); + } else if (equalIgnoringCase(equiv, "refresh")) { + double delay; + String url; + if (frame && parseHTTPRefresh(content, true, delay, url)) { + if (url.isEmpty()) + url = frame->loader()->url().string(); + else + url = completeURL(url).string(); + frame->loader()->scheduleHTTPRedirection(delay, url); + } + } else if (equalIgnoringCase(equiv, "set-cookie")) { + // FIXME: make setCookie work on XML documents too; e.g. in case of <html:meta .....> + if (isHTMLDocument()) + static_cast<HTMLDocument*>(this)->setCookie(content); + } else if (equalIgnoringCase(equiv, "content-language")) + setContentLanguage(content); +} + +MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& request, const IntPoint& documentPoint, const PlatformMouseEvent& event) +{ + ASSERT(!renderer() || renderer()->isRenderView()); + + if (!renderer()) + return MouseEventWithHitTestResults(event, HitTestResult(IntPoint())); + + HitTestResult result(documentPoint); + renderer()->layer()->hitTest(request, result); + + if (!request.readonly) + updateRendering(); + + return MouseEventWithHitTestResults(event, result); +} + +// DOM Section 1.1.1 +bool Document::childTypeAllowed(NodeType type) +{ + switch (type) { + case ATTRIBUTE_NODE: + case CDATA_SECTION_NODE: + case DOCUMENT_FRAGMENT_NODE: + case DOCUMENT_NODE: + case ENTITY_NODE: + case ENTITY_REFERENCE_NODE: + case NOTATION_NODE: + case TEXT_NODE: + case XPATH_NAMESPACE_NODE: + return false; + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + return true; + case DOCUMENT_TYPE_NODE: + case ELEMENT_NODE: + // Documents may contain no more than one of each of these. + // (One Element and one DocumentType.) + for (Node* c = firstChild(); c; c = c->nextSibling()) + if (c->nodeType() == type) + return false; + return true; + } + return false; +} + +bool Document::canReplaceChild(Node* newChild, Node* oldChild) +{ + if (!oldChild) + // ContainerNode::replaceChild will raise a NOT_FOUND_ERR. + return true; + + if (oldChild->nodeType() == newChild->nodeType()) + return true; + + int numDoctypes = 0; + int numElements = 0; + + // First, check how many doctypes and elements we have, not counting + // the child we're about to remove. + for (Node* c = firstChild(); c; c = c->nextSibling()) { + if (c == oldChild) + continue; + + switch (c->nodeType()) { + case DOCUMENT_TYPE_NODE: + numDoctypes++; + break; + case ELEMENT_NODE: + numElements++; + break; + default: + break; + } + } + + // Then, see how many doctypes and elements might be added by the new child. + if (newChild->nodeType() == DOCUMENT_FRAGMENT_NODE) { + for (Node* c = firstChild(); c; c = c->nextSibling()) { + switch (c->nodeType()) { + case ATTRIBUTE_NODE: + case CDATA_SECTION_NODE: + case DOCUMENT_FRAGMENT_NODE: + case DOCUMENT_NODE: + case ENTITY_NODE: + case ENTITY_REFERENCE_NODE: + case NOTATION_NODE: + case TEXT_NODE: + case XPATH_NAMESPACE_NODE: + return false; + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + break; + case DOCUMENT_TYPE_NODE: + numDoctypes++; + break; + case ELEMENT_NODE: + numElements++; + break; + } + } + } else { + switch (newChild->nodeType()) { + case ATTRIBUTE_NODE: + case CDATA_SECTION_NODE: + case DOCUMENT_FRAGMENT_NODE: + case DOCUMENT_NODE: + case ENTITY_NODE: + case ENTITY_REFERENCE_NODE: + case NOTATION_NODE: + case TEXT_NODE: + case XPATH_NAMESPACE_NODE: + return false; + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + return true; + case DOCUMENT_TYPE_NODE: + numDoctypes++; + break; + case ELEMENT_NODE: + numElements++; + break; + } + } + + if (numElements > 1 || numDoctypes > 1) + return false; + + return true; +} + +PassRefPtr<Node> Document::cloneNode(bool /*deep*/) +{ + // Spec says cloning Document nodes is "implementation dependent" + // so we do not support it... + return 0; +} + +StyleSheetList* Document::styleSheets() +{ + return m_styleSheets.get(); +} + +String Document::preferredStylesheetSet() const +{ + return m_preferredStylesheetSet; +} + +String Document::selectedStylesheetSet() const +{ + return m_selectedStylesheetSet; +} + +void Document::setSelectedStylesheetSet(const String& aString) +{ + m_selectedStylesheetSet = aString; + updateStyleSelector(); + if (renderer()) + renderer()->repaint(); +} + +// This method is called whenever a top-level stylesheet has finished loading. +void Document::removePendingSheet() +{ + // Make sure we knew this sheet was pending, and that our count isn't out of sync. + ASSERT(m_pendingStylesheets > 0); + + m_pendingStylesheets--; + +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets); +#endif + + updateStyleSelector(); + + if (!m_pendingStylesheets && m_tokenizer) + m_tokenizer->executeScriptsWaitingForStylesheets(); + + if (!m_pendingStylesheets && m_gotoAnchorNeededAfterStylesheetsLoad && m_frame) + m_frame->loader()->gotoAnchor(); +} + +void Document::updateStyleSelector() +{ + // Don't bother updating, since we haven't loaded all our style info yet + // and haven't calculated the style selector for the first time. + if (!m_didCalculateStyleSelector && !haveStylesheetsLoaded()) + return; + + if (didLayoutWithPendingStylesheets() && m_pendingStylesheets <= 0) { + m_pendingSheetLayout = IgnoreLayoutWithPendingSheets; + if (renderer()) + renderer()->repaint(); + } + +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Beginning update of style selector at time %d.\n", elapsedTime()); +#endif + + recalcStyleSelector(); + recalcStyle(Force); + +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Finished update of style selector at time %d\n", elapsedTime()); +#endif + + if (renderer()) { + renderer()->setNeedsLayoutAndPrefWidthsRecalc(); + if (view()) + view()->scheduleRelayout(); + } +} + +void Document::recalcStyleSelector() +{ + if (!renderer() || !attached()) + return; + + DeprecatedPtrList<StyleSheet> oldStyleSheets = m_styleSheets->styleSheets; + m_styleSheets->styleSheets.clear(); + + bool matchAuthorAndUserStyles = true; + if (Settings* settings = this->settings()) + matchAuthorAndUserStyles = settings->authorAndUserStylesEnabled(); + + Node* n = matchAuthorAndUserStyles ? this : 0; + for ( ; n; n = n->traverseNextNode()) { + StyleSheet* sheet = 0; + + if (n->nodeType() == PROCESSING_INSTRUCTION_NODE) { + // Processing instruction (XML documents only) + ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(n); + sheet = pi->sheet(); +#if ENABLE(XSLT) + // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806> + if (pi->isXSL() && !transformSourceDocument()) { + // Don't apply XSL transforms until loading is finished. + if (!parsing()) + applyXSLTransform(pi); + return; + } +#endif + if (!sheet && !pi->localHref().isEmpty()) { + // Processing instruction with reference to an element in this document - e.g. + // <?xml-stylesheet href="#mystyle">, with the element + // <foo id="mystyle">heading { color: red; }</foo> at some location in + // the document + Element* elem = getElementById(pi->localHref().impl()); + if (elem) { + String sheetText(""); + for (Node* c = elem->firstChild(); c; c = c->nextSibling()) { + if (c->nodeType() == TEXT_NODE || c->nodeType() == CDATA_SECTION_NODE) + sheetText += c->nodeValue(); + } + + CSSStyleSheet* cssSheet = new CSSStyleSheet(this); + cssSheet->parseString(sheetText); + pi->setCSSStyleSheet(cssSheet); + sheet = cssSheet; + } + } + } else if (n->isHTMLElement() && (n->hasTagName(linkTag) || n->hasTagName(styleTag)) +#if ENABLE(SVG) + || (n->isSVGElement() && n->hasTagName(SVGNames::styleTag)) +#endif + ) { + Element* e = static_cast<Element*>(n); + AtomicString title = e->getAttribute(titleAttr); + bool enabledViaScript = false; + if (e->hasLocalName(linkTag)) { + // <LINK> element + HTMLLinkElement* l = static_cast<HTMLLinkElement*>(n); + if (l->isDisabled()) + continue; + enabledViaScript = l->isEnabledViaScript(); + if (l->isLoading()) { + // it is loading but we should still decide which style sheet set to use + if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSet.isEmpty()) { + const AtomicString& rel = e->getAttribute(relAttr); + if (!rel.contains("alternate")) { + m_preferredStylesheetSet = title; + m_selectedStylesheetSet = title; + } + } + continue; + } + if (!l->sheet()) + title = nullAtom; + } + + // Get the current preferred styleset. This is the + // set of sheets that will be enabled. +#if ENABLE(SVG) + if (n->isSVGElement() && n->hasTagName(SVGNames::styleTag)) + sheet = static_cast<SVGStyleElement*>(n)->sheet(); + else +#endif + if (e->hasLocalName(linkTag)) + sheet = static_cast<HTMLLinkElement*>(n)->sheet(); + else + // <STYLE> element + sheet = static_cast<HTMLStyleElement*>(n)->sheet(); + + // Check to see if this sheet belongs to a styleset + // (thus making it PREFERRED or ALTERNATE rather than + // PERSISTENT). + if (!enabledViaScript && !title.isEmpty()) { + // Yes, we have a title. + if (m_preferredStylesheetSet.isEmpty()) { + // No preferred set has been established. If + // we are NOT an alternate sheet, then establish + // us as the preferred set. Otherwise, just ignore + // this sheet. + AtomicString rel = e->getAttribute(relAttr); + if (e->hasLocalName(styleTag) || !rel.contains("alternate")) + m_preferredStylesheetSet = m_selectedStylesheetSet = title; + } + + if (title != m_preferredStylesheetSet) + sheet = 0; + } + } + + if (sheet) { + sheet->ref(); + m_styleSheets->styleSheets.append(sheet); + } + + // For HTML documents, stylesheets are not allowed within/after the <BODY> tag. So we + // can stop searching here. + if (isHTMLDocument() && n->hasTagName(bodyTag)) + break; + } + + // De-reference all the stylesheets in the old list + DeprecatedPtrListIterator<StyleSheet> it(oldStyleSheets); + for (; it.current(); ++it) + it.current()->deref(); + + // Create a new style selector + delete m_styleSelector; + m_styleSelector = new CSSStyleSelector(this, userStyleSheet(), m_styleSheets.get(), m_mappedElementSheet.get(), !inCompatMode(), matchAuthorAndUserStyles); + m_styleSelector->setEncodedURL(m_url); + m_didCalculateStyleSelector = true; +} + +void Document::setHoverNode(PassRefPtr<Node> newHoverNode) +{ + m_hoverNode = newHoverNode; +} + +void Document::setActiveNode(PassRefPtr<Node> newActiveNode) +{ + m_activeNode = newActiveNode; +} + +void Document::focusedNodeRemoved() +{ + setFocusedNode(0); +} + +void Document::removeFocusedNodeOfSubtree(Node* node, bool amongChildrenOnly) +{ + if (!m_focusedNode || this->inPageCache()) // If the document is in the page cache, then we don't need to clear out the focused node. + return; + + bool nodeInSubtree = false; + if (amongChildrenOnly) + nodeInSubtree = m_focusedNode->isDescendantOf(node); + else + nodeInSubtree = (m_focusedNode == node) || m_focusedNode->isDescendantOf(node); + + if (nodeInSubtree) + document()->focusedNodeRemoved(); +} + +void Document::hoveredNodeDetached(Node* node) +{ + if (!m_hoverNode || (node != m_hoverNode && (!m_hoverNode->isTextNode() || node != m_hoverNode->parent()))) + return; + + m_hoverNode = node->parent(); + while (m_hoverNode && !m_hoverNode->renderer()) + m_hoverNode = m_hoverNode->parent(); + if (frame()) + frame()->eventHandler()->scheduleHoverStateUpdate(); +} + +void Document::activeChainNodeDetached(Node* node) +{ + if (!m_activeNode || (node != m_activeNode && (!m_activeNode->isTextNode() || node != m_activeNode->parent()))) + return; + + m_activeNode = node->parent(); + while (m_activeNode && !m_activeNode->renderer()) + m_activeNode = m_activeNode->parent(); +} + +const Vector<DashboardRegionValue>& Document::dashboardRegions() const +{ + return m_dashboardRegions; +} + +void Document::setDashboardRegions(const Vector<DashboardRegionValue>& regions) +{ + m_dashboardRegions = regions; + setDashboardRegionsDirty(false); +} + +bool Document::setFocusedNode(PassRefPtr<Node> newFocusedNode) +{ + // Make sure newFocusedNode is actually in this document + if (newFocusedNode && (newFocusedNode->document() != this)) + return true; + + if (m_focusedNode == newFocusedNode) + return true; + + if (m_inPageCache) + return false; + + bool focusChangeBlocked = false; + RefPtr<Node> oldFocusedNode = m_focusedNode; + m_focusedNode = 0; + + // Remove focus from the existing focus node (if any) + if (oldFocusedNode && !oldFocusedNode->m_inDetach) { + if (oldFocusedNode->active()) + oldFocusedNode->setActive(false); + + oldFocusedNode->setFocus(false); + + // Dispatch a change event for text fields or textareas that have been edited + RenderObject *r = static_cast<RenderObject*>(oldFocusedNode.get()->renderer()); + if (r && (r->isTextArea() || r->isTextField()) && r->isEdited()) { + EventTargetNodeCast(oldFocusedNode.get())->dispatchHTMLEvent(changeEvent, true, false); + if ((r = static_cast<RenderObject*>(oldFocusedNode.get()->renderer()))) + r->setEdited(false); + } + + // Dispatch the blur event and let the node do any other blur related activities (important for text fields) + EventTargetNodeCast(oldFocusedNode.get())->dispatchBlurEvent(); + + if (m_focusedNode) { + // handler shifted focus + focusChangeBlocked = true; + newFocusedNode = 0; + } + EventTargetNodeCast(oldFocusedNode.get())->dispatchUIEvent(DOMFocusOutEvent); + if (m_focusedNode) { + // handler shifted focus + focusChangeBlocked = true; + newFocusedNode = 0; + } + if ((oldFocusedNode.get() == this) && oldFocusedNode->hasOneRef()) + return true; + + if (oldFocusedNode.get() == oldFocusedNode->rootEditableElement()) + frame()->editor()->didEndEditing(); + } + + if (newFocusedNode) { + if (newFocusedNode == newFocusedNode->rootEditableElement() && !acceptsEditingFocus(newFocusedNode.get())) { + // delegate blocks focus change + focusChangeBlocked = true; + goto SetFocusedNodeDone; + } + // Set focus on the new node + m_focusedNode = newFocusedNode.get(); + + // Dispatch the focus event and let the node do any other focus related activities (important for text fields) + EventTargetNodeCast(m_focusedNode.get())->dispatchFocusEvent(); + + if (m_focusedNode != newFocusedNode) { + // handler shifted focus + focusChangeBlocked = true; + goto SetFocusedNodeDone; + } + EventTargetNodeCast(m_focusedNode.get())->dispatchUIEvent(DOMFocusInEvent); + if (m_focusedNode != newFocusedNode) { + // handler shifted focus + focusChangeBlocked = true; + goto SetFocusedNodeDone; + } + m_focusedNode->setFocus(); + + if (m_focusedNode.get() == m_focusedNode->rootEditableElement()) + frame()->editor()->didBeginEditing(); + + // eww, I suck. set the qt focus correctly + // ### find a better place in the code for this + if (view()) { + Widget *focusWidget = widgetForNode(m_focusedNode.get()); + if (focusWidget) { + // Make sure a widget has the right size before giving it focus. + // Otherwise, we are testing edge cases of the Widget code. + // Specifically, in WebCore this does not work well for text fields. + updateLayout(); + // Re-get the widget in case updating the layout changed things. + focusWidget = widgetForNode(m_focusedNode.get()); + } + if (focusWidget) + focusWidget->setFocus(); + else + view()->setFocus(); + } + } + +#if PLATFORM(MAC) + if (!focusChangeBlocked && m_focusedNode && AXObjectCache::accessibilityEnabled()) + axObjectCache()->handleFocusedUIElementChanged(); +#endif + +SetFocusedNodeDone: + updateRendering(); + return !focusChangeBlocked; + } + +void Document::setCSSTarget(Node* n) +{ + if (m_cssTarget) + m_cssTarget->setChanged(); + m_cssTarget = n; + if (n) + n->setChanged(); +} + +Node* Document::getCSSTarget() const +{ + return m_cssTarget; +} + +void Document::attachNodeIterator(NodeIterator *ni) +{ + m_nodeIterators.add(ni); +} + +void Document::detachNodeIterator(NodeIterator *ni) +{ + m_nodeIterators.remove(ni); +} + +void Document::notifyBeforeNodeRemoval(Node *n) +{ + if (Frame* f = frame()) { + f->selectionController()->nodeWillBeRemoved(n); + f->dragCaretController()->nodeWillBeRemoved(n); + } + + HashSet<NodeIterator*>::const_iterator end = m_nodeIterators.end(); + for (HashSet<NodeIterator*>::const_iterator it = m_nodeIterators.begin(); it != end; ++it) + (*it)->notifyBeforeNodeRemoval(n); +} + +DOMWindow* Document::defaultView() const +{ + if (!frame()) + return 0; + + return frame()->domWindow(); +} + +PassRefPtr<Event> Document::createEvent(const String& eventType, ExceptionCode& ec) +{ + if (eventType == "UIEvents" || eventType == "UIEvent") + return new UIEvent; + if (eventType == "MouseEvents" || eventType == "MouseEvent") + return new MouseEvent; + if (eventType == "MutationEvents" || eventType == "MutationEvent") + return new MutationEvent; + if (eventType == "KeyboardEvents" || eventType == "KeyboardEvent") + return new KeyboardEvent; + if (eventType == "HTMLEvents" || eventType == "Event" || eventType == "Events") + return new Event; + if (eventType == "ProgressEvent") + return new ProgressEvent; + if (eventType == "TextEvent") + return new TextEvent; + if (eventType == "OverflowEvent") + return new OverflowEvent; + if (eventType == "WheelEvent") + return new WheelEvent; +#if ENABLE(SVG) + if (eventType == "SVGEvents") + return new Event; + if (eventType == "SVGZoomEvents") + return new SVGZoomEvent; +#endif +#if ENABLE(CROSS_DOCUMENT_MESSAGING) + if (eventType == "MessageEvent") + return new MessageEvent; +#endif + ec = NOT_SUPPORTED_ERR; + return 0; +} + +CSSStyleDeclaration *Document::getOverrideStyle(Element */*elt*/, const String &/*pseudoElt*/) +{ + return 0; // ### +} + +void Document::handleWindowEvent(Event *evt, bool useCapture) +{ + if (m_windowEventListeners.isEmpty()) + return; + + // if any html event listeners are registered on the window, then dispatch them here + RegisteredEventListenerList listenersCopy = m_windowEventListeners; + RegisteredEventListenerList::iterator it = listenersCopy.begin(); + + for (; it != listenersCopy.end(); ++it) + if ((*it)->eventType() == evt->type() && (*it)->useCapture() == useCapture && !(*it)->removed()) + (*it)->listener()->handleEvent(evt, true); +} + +void Document::setHTMLWindowEventListener(const AtomicString &eventType, PassRefPtr<EventListener> listener) +{ + // If we already have it we don't want removeWindowEventListener to delete it + removeHTMLWindowEventListener(eventType); + if (listener) + addWindowEventListener(eventType, listener, false); +} + +EventListener *Document::getHTMLWindowEventListener(const AtomicString &eventType) +{ + RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); + for (; it != m_windowEventListeners.end(); ++it) + if ( (*it)->eventType() == eventType && (*it)->listener()->isHTMLEventListener()) + return (*it)->listener(); + return 0; +} + +void Document::removeHTMLWindowEventListener(const AtomicString &eventType) +{ + RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); + for (; it != m_windowEventListeners.end(); ++it) + if ( (*it)->eventType() == eventType && (*it)->listener()->isHTMLEventListener()) { + m_windowEventListeners.remove(it); + return; + } +} + +void Document::addWindowEventListener(const AtomicString &eventType, PassRefPtr<EventListener> listener, bool useCapture) +{ + // Remove existing identical listener set with identical arguments. + // The DOM 2 spec says that "duplicate instances are discarded" in this case. + removeWindowEventListener(eventType, listener.get(), useCapture); + m_windowEventListeners.append(new RegisteredEventListener(eventType, listener, useCapture)); +} + +void Document::removeWindowEventListener(const AtomicString &eventType, EventListener *listener, bool useCapture) +{ + RegisteredEventListener rl(eventType, listener, useCapture); + RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); + for (; it != m_windowEventListeners.end(); ++it) + if (*(*it) == rl) { + m_windowEventListeners.remove(it); + return; + } +} + +bool Document::hasWindowEventListener(const AtomicString &eventType) +{ + RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); + for (; it != m_windowEventListeners.end(); ++it) + if ((*it)->eventType() == eventType) { + return true; + } + return false; +} + +PassRefPtr<EventListener> Document::createHTMLEventListener(const String& functionName, const String& code, Node *node) +{ + if (Frame* frm = frame()) + if (frm->scriptProxy()->isEnabled()) + return frm->scriptProxy()->createHTMLEventHandler(functionName, code, node); + return 0; +} + +void Document::setHTMLWindowEventListener(const AtomicString& eventType, Attribute* attr) +{ + setHTMLWindowEventListener(eventType, + createHTMLEventListener(attr->localName().string(), attr->value(), 0)); +} + +void Document::dispatchImageLoadEventSoon(HTMLImageLoader *image) +{ + m_imageLoadEventDispatchSoonList.append(image); + if (!m_imageLoadEventTimer.isActive()) + m_imageLoadEventTimer.startOneShot(0); +} + +void Document::removeImage(HTMLImageLoader* image) +{ + // Remove instances of this image from both lists. + // Use loops because we allow multiple instances to get into the lists. + while (m_imageLoadEventDispatchSoonList.removeRef(image)) { } + while (m_imageLoadEventDispatchingList.removeRef(image)) { } + if (m_imageLoadEventDispatchSoonList.isEmpty()) + m_imageLoadEventTimer.stop(); +} + +void Document::dispatchImageLoadEventsNow() +{ + // need to avoid re-entering this function; if new dispatches are + // scheduled before the parent finishes processing the list, they + // will set a timer and eventually be processed + if (!m_imageLoadEventDispatchingList.isEmpty()) { + return; + } + + m_imageLoadEventTimer.stop(); + + m_imageLoadEventDispatchingList = m_imageLoadEventDispatchSoonList; + m_imageLoadEventDispatchSoonList.clear(); + for (DeprecatedPtrListIterator<HTMLImageLoader> it(m_imageLoadEventDispatchingList); it.current();) { + HTMLImageLoader* image = it.current(); + // Must advance iterator *before* dispatching call. + // Otherwise, it might be advanced automatically if dispatching the call had a side effect + // of destroying the current HTMLImageLoader, and then we would advance past the *next* item, + // missing one altogether. + ++it; + image->dispatchLoadEvent(); + } + m_imageLoadEventDispatchingList.clear(); +} + +void Document::imageLoadEventTimerFired(Timer<Document>*) +{ + dispatchImageLoadEventsNow(); +} + +Element* Document::ownerElement() const +{ + if (!frame()) + return 0; + return frame()->ownerElement(); +} + +String Document::cookie() const +{ + return cookies(this, url()); +} + +void Document::setCookie(const String& value) +{ + setCookies(this, url(), policyBaseURL(), value); +} + +String Document::referrer() const +{ + if (frame()) + return frame()->loader()->referrer(); + return String(); +} + +String Document::domain() const +{ + return m_securityOrigin->host(); +} + +void Document::setDomain(const String& newDomain) +{ + // Both NS and IE specify that changing the domain is only allowed when + // the new domain is a suffix of the old domain. + + // FIXME: We should add logging indicating why a domain was not allowed. + + // If the new domain is the same as the old domain, still call + // m_securityOrigin.setDomainForDOM. This will change the + // security check behavior. For example, if a page loaded on port 8000 + // assigns its current domain using document.domain, the page will + // allow other pages loaded on different ports in the same domain that + // have also assigned to access this page. + if (equalIgnoringCase(domain(), newDomain)) { + m_securityOrigin->setDomainFromDOM(newDomain); + return; + } + + int oldLength = domain().length(); + int newLength = newDomain.length(); + // e.g. newDomain = webkit.org (10) and domain() = www.webkit.org (14) + if (newLength >= oldLength) + return; + + String test = domain(); + // Check that it's a subdomain, not e.g. "ebkit.org" + if (test[oldLength - newLength - 1] != '.') + return; + + // Now test is "webkit.org" from domain() + // and we check that it's the same thing as newDomain + test.remove(0, oldLength - newLength); + if (test != newDomain) + return; + + m_securityOrigin->setDomainFromDOM(newDomain); +} + +String Document::lastModified() const +{ + Frame* f = frame(); + if (!f) + return String(); + DocumentLoader* loader = f->loader()->documentLoader(); + if (!loader) + return String(); + return loader->response().httpHeaderField("Last-Modified"); +} + +bool Document::isValidName(const String &name) +{ + const UChar* s = reinterpret_cast<const UChar*>(name.characters()); + unsigned length = name.length(); + + if (length == 0) + return false; + + unsigned i = 0; + + UChar32 c; + U16_NEXT(s, i, length, c) + if (!isValidNameStart(c)) + return false; + + while (i < length) { + U16_NEXT(s, i, length, c) + if (!isValidNamePart(c)) + return false; + } + + return true; +} + +bool Document::parseQualifiedName(const String &qualifiedName, String &prefix, String &localName) +{ + unsigned length = qualifiedName.length(); + + if (length == 0) + return false; + + bool nameStart = true; + bool sawColon = false; + int colonPos = 0; + + const UChar* s = reinterpret_cast<const UChar*>(qualifiedName.characters()); + for (unsigned i = 0; i < length;) { + UChar32 c; + U16_NEXT(s, i, length, c) + if (c == ':') { + if (sawColon) + return false; // multiple colons: not allowed + nameStart = true; + sawColon = true; + colonPos = i - 1; + } else if (nameStart) { + if (!isValidNameStart(c)) + return false; + nameStart = false; + } else { + if (!isValidNamePart(c)) + return false; + } + } + + if (!sawColon) { + prefix = String(); + localName = qualifiedName; + } else { + prefix = qualifiedName.substring(0, colonPos); + localName = qualifiedName.substring(colonPos + 1); + } + + return true; +} + +void Document::addImageMap(HTMLMapElement* imageMap) +{ + const AtomicString& name = imageMap->getName(); + if (!name.impl()) + return; + + // Add the image map, unless there's already another with that name. + // "First map wins" is the rule other browsers seem to implement. + m_imageMapsByName.add(name.impl(), imageMap); +} + +void Document::removeImageMap(HTMLMapElement* imageMap) +{ + // Remove the image map by name. + // But don't remove some other image map that just happens to have the same name. + // FIXME: Use a HashCountedSet as we do for IDs to find the first remaining map + // once a map has been removed. + const AtomicString& name = imageMap->getName(); + if (!name.impl()) + return; + + ImageMapsByName::iterator it = m_imageMapsByName.find(name.impl()); + if (it != m_imageMapsByName.end() && it->second == imageMap) + m_imageMapsByName.remove(it); +} + +HTMLMapElement *Document::getImageMap(const String& url) const +{ + if (url.isNull()) + return 0; + int hashPos = url.find('#'); + String name = (hashPos < 0 ? url : url.substring(hashPos + 1)).impl(); + AtomicString mapName = isHTMLDocument() ? name.lower() : name; + return m_imageMapsByName.get(mapName.impl()); +} + +void Document::setDecoder(TextResourceDecoder *decoder) +{ + m_decoder = decoder; +} + +UChar Document::backslashAsCurrencySymbol() const +{ + if (!m_decoder) + return '\\'; + return m_decoder->encoding().backslashAsCurrencySymbol(); +} + +KURL Document::completeURL(const String& url) +{ + // Always return a null URL when passed a null string. + // FIXME: Should we change the KURL constructor to have this behavior? + if (url.isNull()) + return KURL(); + KURL base = m_baseURL; + if (base.isEmpty()) + base = m_url; + if (!m_decoder) + return KURL(base, url); + return KURL(base, url, m_decoder->encoding()); +} + +bool Document::inPageCache() +{ + return m_inPageCache; +} + +void Document::setInPageCache(bool flag) +{ + if (m_inPageCache == flag) + return; + + m_inPageCache = flag; + if (flag) { + ASSERT(m_savedRenderer == 0); + m_savedRenderer = renderer(); + if (FrameView* v = view()) + v->resetScrollbars(); + } else { + ASSERT(renderer() == 0 || renderer() == m_savedRenderer); + ASSERT(m_renderArena); + setRenderer(m_savedRenderer); + m_savedRenderer = 0; + } +} + +void Document::willSaveToCache() +{ + HashSet<Element*>::iterator end = m_pageCacheCallbackElements.end(); + for (HashSet<Element*>::iterator i = m_pageCacheCallbackElements.begin(); i != end; ++i) + (*i)->willSaveToCache(); +} + +void Document::didRestoreFromCache() +{ + HashSet<Element*>::iterator end = m_pageCacheCallbackElements.end(); + for (HashSet<Element*>::iterator i = m_pageCacheCallbackElements.begin(); i != end; ++i) + (*i)->didRestoreFromCache(); +} + +void Document::registerForCacheCallbacks(Element* e) +{ + m_pageCacheCallbackElements.add(e); +} + +void Document::unregisterForCacheCallbacks(Element* e) +{ + m_pageCacheCallbackElements.remove(e); +} + +void Document::setShouldCreateRenderers(bool f) +{ + m_createRenderers = f; +} + +bool Document::shouldCreateRenderers() +{ + return m_createRenderers; +} + +String Document::toString() const +{ + String result; + + for (Node *child = firstChild(); child != NULL; child = child->nextSibling()) { + result += child->toString(); + } + + return result; +} + +// Support for Javascript execCommand, and related methods + +static Editor::Command command(Document* document, const String& commandName, bool userInterface = false) +{ + Frame* frame = document->frame(); + if (!frame || frame->document() != document) + return Editor::Command(); + return frame->editor()->command(commandName, + userInterface ? CommandFromDOMWithUserInterface : CommandFromDOM); +} + +bool Document::execCommand(const String& commandName, bool userInterface, const String& value) +{ + return command(this, commandName, userInterface).execute(value); +} + +bool Document::queryCommandEnabled(const String& commandName) +{ + return command(this, commandName).isEnabled(); +} + +bool Document::queryCommandIndeterm(const String& commandName) +{ + return command(this, commandName).state() == MixedTriState; +} + +bool Document::queryCommandState(const String& commandName) +{ + return command(this, commandName).state() != FalseTriState; +} + +bool Document::queryCommandSupported(const String& commandName) +{ + return command(this, commandName).isSupported(); +} + +String Document::queryCommandValue(const String& commandName) +{ + return command(this, commandName).value(); +} + +static IntRect placeholderRectForMarker() +{ + return IntRect(-1, -1, -1, -1); +} + +void Document::addMarker(Range *range, DocumentMarker::MarkerType type, String description) +{ + // Use a TextIterator to visit the potentially multiple nodes the range covers. + for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { + RefPtr<Range> textPiece = markedText.range(); + int exception = 0; + DocumentMarker marker = {type, textPiece->startOffset(exception), textPiece->endOffset(exception), description}; + addMarker(textPiece->startContainer(exception), marker); + } +} + +void Document::removeMarkers(Range* range, DocumentMarker::MarkerType markerType) +{ + if (m_markers.isEmpty()) + return; + + ExceptionCode ec = 0; + Node* startContainer = range->startContainer(ec); + Node* endContainer = range->endContainer(ec); + + Node* pastEndNode = range->pastEndNode(); + for (Node* node = range->startNode(); node != pastEndNode; node = node->traverseNextNode()) { + int startOffset = node == startContainer ? range->startOffset(ec) : 0; + int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; + int length = endOffset - startOffset; + removeMarkers(node, startOffset, length, markerType); + } +} + +// Markers are stored in order sorted by their location. +// They do not overlap each other, as currently required by the drawing code in RenderText.cpp. + +void Document::addMarker(Node *node, DocumentMarker newMarker) +{ + ASSERT(newMarker.endOffset >= newMarker.startOffset); + if (newMarker.endOffset == newMarker.startOffset) + return; + + MarkerMapVectorPair* vectorPair = m_markers.get(node); + + if (!vectorPair) { + vectorPair = new MarkerMapVectorPair; + vectorPair->first.append(newMarker); + vectorPair->second.append(placeholderRectForMarker()); + m_markers.set(node, vectorPair); + } else { + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + size_t i; + for (i = 0; i != markers.size();) { + DocumentMarker marker = markers[i]; + + if (newMarker.endOffset < marker.startOffset+1) { + // This is the first marker that is completely after newMarker, and disjoint from it. + // We found our insertion point. + break; + } else if (newMarker.startOffset > marker.endOffset) { + // maker is before newMarker, and disjoint from it. Keep scanning. + i++; + } else if (newMarker == marker) { + // already have this one, NOP + return; + } else { + // marker and newMarker intersect or touch - merge them into newMarker + newMarker.startOffset = min(newMarker.startOffset, marker.startOffset); + newMarker.endOffset = max(newMarker.endOffset, marker.endOffset); + // remove old one, we'll add newMarker later + markers.remove(i); + rects.remove(i); + // it points to the next marker to consider + } + } + // at this point i points to the node before which we want to insert + markers.insert(i, newMarker); + rects.insert(i, placeholderRectForMarker()); + } + + // repaint the affected node + if (node->renderer()) + node->renderer()->repaint(); +} + +// copies markers from srcNode to dstNode, applying the specified shift delta to the copies. The shift is +// useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode. +void Document::copyMarkers(Node *srcNode, unsigned startOffset, int length, Node *dstNode, int delta, DocumentMarker::MarkerType markerType) +{ + if (length <= 0) + return; + + MarkerMapVectorPair* vectorPair = m_markers.get(srcNode); + if (!vectorPair) + return; + + ASSERT(vectorPair->first.size() == vectorPair->second.size()); + + bool docDirty = false; + unsigned endOffset = startOffset + length - 1; + Vector<DocumentMarker>& markers = vectorPair->first; + for (size_t i = 0; i != markers.size(); ++i) { + DocumentMarker marker = markers[i]; + + // stop if we are now past the specified range + if (marker.startOffset > endOffset) + break; + + // skip marker that is before the specified range or is the wrong type + if (marker.endOffset < startOffset || (marker.type != markerType && markerType != DocumentMarker::AllMarkers)) + continue; + + // pin the marker to the specified range and apply the shift delta + docDirty = true; + if (marker.startOffset < startOffset) + marker.startOffset = startOffset; + if (marker.endOffset > endOffset) + marker.endOffset = endOffset; + marker.startOffset += delta; + marker.endOffset += delta; + + addMarker(dstNode, marker); + } + + // repaint the affected node + if (docDirty && dstNode->renderer()) + dstNode->renderer()->repaint(); +} + +void Document::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerType markerType) +{ + if (length <= 0) + return; + + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) + return; + + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + bool docDirty = false; + unsigned endOffset = startOffset + length; + for (size_t i = 0; i < markers.size();) { + DocumentMarker marker = markers[i]; + + // markers are returned in order, so stop if we are now past the specified range + if (marker.startOffset >= endOffset) + break; + + // skip marker that is wrong type or before target + if (marker.endOffset < startOffset || (marker.type != markerType && markerType != DocumentMarker::AllMarkers)) { + i++; + continue; + } + + // at this point we know that marker and target intersect in some way + docDirty = true; + + // pitch the old marker and any associated rect + markers.remove(i); + rects.remove(i); + + // add either of the resulting slices that are left after removing target + if (startOffset > marker.startOffset) { + DocumentMarker newLeft = marker; + newLeft.endOffset = startOffset; + markers.insert(i, newLeft); + rects.insert(i, placeholderRectForMarker()); + // i now points to the newly-inserted node, but we want to skip that one + i++; + } + if (marker.endOffset > endOffset) { + DocumentMarker newRight = marker; + newRight.startOffset = endOffset; + markers.insert(i, newRight); + rects.insert(i, placeholderRectForMarker()); + // i now points to the newly-inserted node, but we want to skip that one + i++; + } + } + + if (markers.isEmpty()) { + ASSERT(rects.isEmpty()); + m_markers.remove(node); + delete vectorPair; + } + + // repaint the affected node + if (docDirty && node->renderer()) + node->renderer()->repaint(); +} + +DocumentMarker* Document::markerContainingPoint(const IntPoint& point, DocumentMarker::MarkerType markerType) +{ + // outer loop: process each node that contains any markers + MarkerMap::iterator end = m_markers.end(); + for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { + // inner loop; process each marker in this node + MarkerMapVectorPair* vectorPair = nodeIterator->second; + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + unsigned markerCount = markers.size(); + for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { + DocumentMarker& marker = markers[markerIndex]; + + // skip marker that is wrong type + if (marker.type != markerType && markerType != DocumentMarker::AllMarkers) + continue; + + IntRect& r = rects[markerIndex]; + + // skip placeholder rects + if (r == placeholderRectForMarker()) + continue; + + if (r.contains(point)) + return ▮ + } + } + + return 0; +} + +Vector<DocumentMarker> Document::markersForNode(Node* node) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (vectorPair) + return vectorPair->first; + return Vector<DocumentMarker>(); +} + +Vector<IntRect> Document::renderedRectsForMarkers(DocumentMarker::MarkerType markerType) +{ + Vector<IntRect> result; + + // outer loop: process each node + MarkerMap::iterator end = m_markers.end(); + for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { + // inner loop; process each marker in this node + MarkerMapVectorPair* vectorPair = nodeIterator->second; + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + unsigned markerCount = markers.size(); + for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { + DocumentMarker marker = markers[markerIndex]; + + // skip marker that is wrong type + if (marker.type != markerType && markerType != DocumentMarker::AllMarkers) + continue; + + IntRect r = rects[markerIndex]; + // skip placeholder rects + if (r == placeholderRectForMarker()) + continue; + + result.append(r); + } + } + + return result; +} + +void Document::removeMarkers(Node* node) +{ + MarkerMap::iterator i = m_markers.find(node); + if (i != m_markers.end()) { + delete i->second; + m_markers.remove(i); + if (RenderObject* renderer = node->renderer()) + renderer->repaint(); + } +} + +void Document::removeMarkers(DocumentMarker::MarkerType markerType) +{ + // outer loop: process each markered node in the document + MarkerMap markerMapCopy = m_markers; + MarkerMap::iterator end = markerMapCopy.end(); + for (MarkerMap::iterator i = markerMapCopy.begin(); i != end; ++i) { + Node* node = i->first.get(); + bool nodeNeedsRepaint = false; + + // inner loop: process each marker in the current node + MarkerMapVectorPair* vectorPair = i->second; + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + for (size_t i = 0; i != markers.size();) { + DocumentMarker marker = markers[i]; + + // skip nodes that are not of the specified type + if (marker.type != markerType && markerType != DocumentMarker::AllMarkers) { + ++i; + continue; + } + + // pitch the old marker + markers.remove(i); + rects.remove(i); + nodeNeedsRepaint = true; + // markerIterator now points to the next node + } + + // Redraw the node if it changed. Do this before the node is removed from m_markers, since + // m_markers might contain the last reference to the node. + if (nodeNeedsRepaint) { + RenderObject* renderer = node->renderer(); + if (renderer) + renderer->repaint(); + } + + // delete the node's list if it is now empty + if (markers.isEmpty()) { + ASSERT(rects.isEmpty()); + m_markers.remove(node); + delete vectorPair; + } + } +} + +void Document::repaintMarkers(DocumentMarker::MarkerType markerType) +{ + // outer loop: process each markered node in the document + MarkerMap::iterator end = m_markers.end(); + for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { + Node* node = i->first.get(); + + // inner loop: process each marker in the current node + MarkerMapVectorPair* vectorPair = i->second; + Vector<DocumentMarker>& markers = vectorPair->first; + bool nodeNeedsRepaint = false; + for (size_t i = 0; i != markers.size(); ++i) { + DocumentMarker marker = markers[i]; + + // skip nodes that are not of the specified type + if (marker.type == markerType || markerType == DocumentMarker::AllMarkers) { + nodeNeedsRepaint = true; + break; + } + } + + if (!nodeNeedsRepaint) + continue; + + // cause the node to be redrawn + if (RenderObject* renderer = node->renderer()) + renderer->repaint(); + } +} + +void Document::setRenderedRectForMarker(Node* node, DocumentMarker marker, const IntRect& r) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) { + ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about + return; + } + + Vector<DocumentMarker>& markers = vectorPair->first; + ASSERT(markers.size() == vectorPair->second.size()); + unsigned markerCount = markers.size(); + for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { + DocumentMarker m = markers[markerIndex]; + if (m == marker) { + vectorPair->second[markerIndex] = r; + return; + } + } + + ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about +} + +void Document::invalidateRenderedRectsForMarkersInRect(const IntRect& r) +{ + // outer loop: process each markered node in the document + MarkerMap::iterator end = m_markers.end(); + for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { + + // inner loop: process each rect in the current node + MarkerMapVectorPair* vectorPair = i->second; + Vector<IntRect>& rects = vectorPair->second; + + unsigned rectCount = rects.size(); + for (unsigned rectIndex = 0; rectIndex < rectCount; ++rectIndex) + if (rects[rectIndex].intersects(r)) + rects[rectIndex] = placeholderRectForMarker(); + } +} + +void Document::shiftMarkers(Node *node, unsigned startOffset, int delta, DocumentMarker::MarkerType markerType) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) + return; + + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + + bool docDirty = false; + for (size_t i = 0; i != markers.size(); ++i) { + DocumentMarker &marker = markers[i]; + if (marker.startOffset >= startOffset && (markerType == DocumentMarker::AllMarkers || marker.type == markerType)) { + ASSERT((int)marker.startOffset + delta >= 0); + marker.startOffset += delta; + marker.endOffset += delta; + docDirty = true; + + // Marker moved, so previously-computed rendered rectangle is now invalid + rects[i] = placeholderRectForMarker(); + } + } + + // repaint the affected node + if (docDirty && node->renderer()) + node->renderer()->repaint(); +} + +#if ENABLE(XSLT) + +void Document::applyXSLTransform(ProcessingInstruction* pi) +{ + RefPtr<XSLTProcessor> processor = XSLTProcessor::create(); + processor->setXSLStylesheet(static_cast<XSLStyleSheet*>(pi->sheet())); + + String resultMIMEType; + String newSource; + String resultEncoding; + if (!processor->transformToString(this, resultMIMEType, newSource, resultEncoding)) + return; + // FIXME: If the transform failed we should probably report an error (like Mozilla does). + processor->createDocumentFromSource(newSource, resultEncoding, resultMIMEType, this, frame()); +} + +void Document::setTransformSource(void* doc) +{ + if (doc == m_transformSource) + return; + + xmlFreeDoc((xmlDocPtr)m_transformSource); + m_transformSource = doc; +} + +#endif + +void Document::setDesignMode(InheritedBool value) +{ + m_designMode = value; +} + +Document::InheritedBool Document::getDesignMode() const +{ + return m_designMode; +} + +bool Document::inDesignMode() const +{ + for (const Document* d = this; d; d = d->parentDocument()) { + if (d->m_designMode != inherit) + return d->m_designMode; + } + return false; +} + +Document *Document::parentDocument() const +{ + Frame *childPart = frame(); + if (!childPart) + return 0; + Frame *parent = childPart->tree()->parent(); + if (!parent) + return 0; + return parent->document(); +} + +Document *Document::topDocument() const +{ + Document *doc = const_cast<Document *>(this); + Element *element; + while ((element = doc->ownerElement())) + doc = element->document(); + + return doc; +} + +PassRefPtr<Attr> Document::createAttributeNS(const String &namespaceURI, const String &qualifiedName, ExceptionCode& ec) +{ + if (qualifiedName.isNull()) { + ec = NAMESPACE_ERR; + return 0; + } + + String localName = qualifiedName; + String prefix; + int colonpos; + if ((colonpos = qualifiedName.find(':')) >= 0) { + prefix = qualifiedName.substring(0, colonpos); + localName = qualifiedName.substring(colonpos + 1); + } + + if (!isValidName(localName)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + + // FIXME: Assume this is a mapped attribute, since createAttribute isn't namespace-aware. There's no harm to XML + // documents if we're wrong. + return new Attr(0, this, new MappedAttribute(QualifiedName(prefix, localName, namespaceURI), StringImpl::empty())); +} + +#if ENABLE(SVG) +const SVGDocumentExtensions* Document::svgExtensions() +{ + return m_svgExtensions; +} + +SVGDocumentExtensions* Document::accessSVGExtensions() +{ + if (!m_svgExtensions) + m_svgExtensions = new SVGDocumentExtensions(this); + return m_svgExtensions; +} +#endif + +PassRefPtr<HTMLCollection> Document::images() +{ + return new HTMLCollection(this, HTMLCollection::DocImages); +} + +PassRefPtr<HTMLCollection> Document::applets() +{ + return new HTMLCollection(this, HTMLCollection::DocApplets); +} + +PassRefPtr<HTMLCollection> Document::embeds() +{ + return new HTMLCollection(this, HTMLCollection::DocEmbeds); +} + +PassRefPtr<HTMLCollection> Document::plugins() +{ + // This is an alias for embeds() required for the JS DOM bindings. + return new HTMLCollection(this, HTMLCollection::DocEmbeds); +} + +PassRefPtr<HTMLCollection> Document::objects() +{ + return new HTMLCollection(this, HTMLCollection::DocObjects); +} + +PassRefPtr<HTMLCollection> Document::scripts() +{ + return new HTMLCollection(this, HTMLCollection::DocScripts); +} + +PassRefPtr<HTMLCollection> Document::links() +{ + return new HTMLCollection(this, HTMLCollection::DocLinks); +} + +PassRefPtr<HTMLCollection> Document::forms() +{ + return new HTMLCollection(this, HTMLCollection::DocForms); +} + +PassRefPtr<HTMLCollection> Document::anchors() +{ + return new HTMLCollection(this, HTMLCollection::DocAnchors); +} + +PassRefPtr<HTMLCollection> Document::all() +{ + return new HTMLCollection(this, HTMLCollection::DocAll); +} + +PassRefPtr<HTMLCollection> Document::windowNamedItems(const String &name) +{ + return new HTMLNameCollection(this, HTMLCollection::WindowNamedItems, name); +} + +PassRefPtr<HTMLCollection> Document::documentNamedItems(const String &name) +{ + return new HTMLNameCollection(this, HTMLCollection::DocumentNamedItems, name); +} + +HTMLCollection::CollectionInfo* Document::nameCollectionInfo(HTMLCollection::Type type, const AtomicString& name) +{ + ASSERT(type >= HTMLCollection::FirstNamedDocumentCachedType); + unsigned index = type - HTMLCollection::FirstNamedDocumentCachedType; + ASSERT(index < HTMLCollection::NumNamedDocumentCachedTypes); + + NamedCollectionMap& map = m_nameCollectionInfo[index]; + NamedCollectionMap::iterator iter = map.find(name.impl()); + if (iter == map.end()) + iter = map.add(name.impl(), new HTMLCollection::CollectionInfo).first; + return iter->second; +} + +void Document::finishedParsing() +{ + setParsing(false); + + ExceptionCode ec = 0; + dispatchEvent(new Event(DOMContentLoadedEvent, true, false), ec); + + if (Frame* f = frame()) + f->loader()->finishedParsing(); +} + +Vector<String> Document::formElementsState() const +{ + Vector<String> stateVector; + stateVector.reserveCapacity(m_formElementsWithState.size() * 3); + typedef ListHashSet<HTMLFormControlElementWithState*>::const_iterator Iterator; + Iterator end = m_formElementsWithState.end(); + for (Iterator it = m_formElementsWithState.begin(); it != end; ++it) { + HTMLFormControlElementWithState* e = *it; + String value; + if (e->saveState(value)) { + stateVector.append(e->name().string()); + stateVector.append(e->type().string()); + stateVector.append(value); + } + } + return stateVector; +} + +#if ENABLE(XPATH) + +PassRefPtr<XPathExpression> Document::createExpression(const String& expression, + XPathNSResolver* resolver, + ExceptionCode& ec) +{ + if (!m_xpathEvaluator) + m_xpathEvaluator = XPathEvaluator::create(); + return m_xpathEvaluator->createExpression(expression, resolver, ec); +} + +PassRefPtr<XPathNSResolver> Document::createNSResolver(Node* nodeResolver) +{ + if (!m_xpathEvaluator) + m_xpathEvaluator = XPathEvaluator::create(); + return m_xpathEvaluator->createNSResolver(nodeResolver); +} + +PassRefPtr<XPathResult> Document::evaluate(const String& expression, + Node* contextNode, + XPathNSResolver* resolver, + unsigned short type, + XPathResult* result, + ExceptionCode& ec) +{ + if (!m_xpathEvaluator) + m_xpathEvaluator = XPathEvaluator::create(); + return m_xpathEvaluator->evaluate(expression, contextNode, resolver, type, result, ec); +} + +#endif // ENABLE(XPATH) + +void Document::setStateForNewFormElements(const Vector<String>& stateVector) +{ + // Walk the state vector backwards so that the value to use for each + // name/type pair first is the one at the end of each individual vector + // in the FormElementStateMap. We're using them like stacks. + typedef FormElementStateMap::iterator Iterator; + m_formElementsWithState.clear(); + for (size_t i = stateVector.size() / 3 * 3; i; i -= 3) { + AtomicString a = stateVector[i - 3]; + AtomicString b = stateVector[i - 2]; + const String& c = stateVector[i - 1]; + FormElementKey key(a.impl(), b.impl()); + Iterator it = m_stateForNewFormElements.find(key); + if (it != m_stateForNewFormElements.end()) + it->second.append(c); + else { + Vector<String> v(1); + v[0] = c; + m_stateForNewFormElements.set(key, v); + } + } +} + +bool Document::hasStateForNewFormElements() const +{ + return !m_stateForNewFormElements.isEmpty(); +} + +bool Document::takeStateForFormElement(AtomicStringImpl* name, AtomicStringImpl* type, String& state) +{ + typedef FormElementStateMap::iterator Iterator; + Iterator it = m_stateForNewFormElements.find(FormElementKey(name, type)); + if (it == m_stateForNewFormElements.end()) + return false; + ASSERT(it->second.size()); + state = it->second.last(); + if (it->second.size() > 1) + it->second.removeLast(); + else + m_stateForNewFormElements.remove(it); + return true; +} + +FormElementKey::FormElementKey(AtomicStringImpl* name, AtomicStringImpl* type) + : m_name(name), m_type(type) +{ + ref(); +} + +FormElementKey::~FormElementKey() +{ + deref(); +} + +FormElementKey::FormElementKey(const FormElementKey& other) + : m_name(other.name()), m_type(other.type()) +{ + ref(); +} + +FormElementKey& FormElementKey::operator=(const FormElementKey& other) +{ + other.ref(); + deref(); + m_name = other.name(); + m_type = other.type(); + return *this; +} + +void FormElementKey::ref() const +{ + if (name() && name() != HashTraits<AtomicStringImpl*>::deletedValue()) + name()->ref(); + if (type()) + type()->ref(); +} + +void FormElementKey::deref() const +{ + if (name() && name() != HashTraits<AtomicStringImpl*>::deletedValue()) + name()->deref(); + if (type()) + type()->deref(); +} + +unsigned FormElementKeyHash::hash(const FormElementKey& k) +{ + ASSERT(sizeof(k) % (sizeof(uint16_t) * 2) == 0); + + unsigned l = sizeof(k) / (sizeof(uint16_t) * 2); + const uint16_t* s = reinterpret_cast<const uint16_t*>(&k); + uint32_t hash = PHI; + + // Main loop + for (; l > 0; l--) { + hash += s[0]; + uint32_t tmp = (s[1] << 11) ^ hash; + hash = (hash << 16) ^ tmp; + s += 2; + hash += hash >> 11; + } + + // Force "avalanching" of final 127 bits + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 2; + hash += hash >> 15; + hash ^= hash << 10; + + // this avoids ever returning a hash code of 0, since that is used to + // signal "hash not computed yet", using a value that is likely to be + // effectively the same as 0 when the low bits are masked + if (hash == 0) + hash = 0x80000000; + + return hash; +} + +FormElementKey FormElementKeyHashTraits::deletedValue() +{ + return HashTraits<AtomicStringImpl*>::deletedValue(); +} + + +String Document::iconURL() +{ + return m_iconURL; +} + +void Document::setIconURL(const String& iconURL, const String& type) +{ + // FIXME - <rdar://problem/4727645> - At some point in the future, we might actually honor the "type" + if (m_iconURL.isEmpty()) + m_iconURL = iconURL; + else if (!type.isEmpty()) + m_iconURL = iconURL; +} + +void Document::setUseSecureKeyboardEntryWhenActive(bool usesSecureKeyboard) +{ + if (m_useSecureKeyboardEntryWhenActive == usesSecureKeyboard) + return; + + m_useSecureKeyboardEntryWhenActive = usesSecureKeyboard; + m_frame->updateSecureKeyboardEntryIfActive(); +} + +bool Document::useSecureKeyboardEntryWhenActive() const +{ + return m_useSecureKeyboardEntryWhenActive; +} + +void Document::initSecurityOrigin() +{ + if (m_securityOrigin && !m_securityOrigin->isEmpty()) + return; // m_securityOrigin has already been initialized. + + m_securityOrigin = SecurityOrigin::createForFrame(m_frame); +} + +void Document::setSecurityOrigin(SecurityOrigin* securityOrigin) +{ + m_securityOrigin = securityOrigin; +} + +void Document::updateFocusAppearanceSoon() +{ + if (!m_updateFocusAppearanceTimer.isActive()) + m_updateFocusAppearanceTimer.startOneShot(0); +} + +void Document::cancelFocusAppearanceUpdate() +{ + m_updateFocusAppearanceTimer.stop(); +} + +void Document::updateFocusAppearanceTimerFired(Timer<Document>*) +{ + Node* node = focusedNode(); + if (!node) + return; + if (!node->isElementNode()) + return; + + updateLayout(); + + Element* element = static_cast<Element*>(node); + if (element->isFocusable()) + element->updateFocusAppearance(false); +} + +// FF method for accessing the selection added for compatability. +DOMSelection* Document::getSelection() const +{ + return frame() ? frame()->domWindow()->getSelection() : 0; +} + +#if ENABLE(DATABASE) + +void Document::addOpenDatabase(Database* database) +{ + if (!m_openDatabaseSet) + m_openDatabaseSet.set(new DatabaseSet); + + ASSERT(!m_openDatabaseSet->contains(database)); + m_openDatabaseSet->add(database); +} + +void Document::removeOpenDatabase(Database* database) +{ + ASSERT(m_openDatabaseSet && m_openDatabaseSet->contains(database)); + if (!m_openDatabaseSet) + return; + + m_openDatabaseSet->remove(database); +} + +DatabaseThread* Document::databaseThread() +{ + if (!m_databaseThread && !m_hasOpenDatabases) { + // Create the database thread on first request - but not if at least one database was already opened, + // because in that case we already had a database thread and terminated it and should not create another. + m_databaseThread = new DatabaseThread(this); + if (!m_databaseThread->start()) + m_databaseThread = 0; + } + + return m_databaseThread.get(); +} + +void Document::stopDatabases() +{ + if (m_openDatabaseSet) { + DatabaseSet::iterator i = m_openDatabaseSet->begin(); + DatabaseSet::iterator end = m_openDatabaseSet->end(); + for (; i != end; ++i) { + (*i)->stop(); + if (m_databaseThread) + m_databaseThread->unscheduleDatabaseTasks(*i); + } + } + + if (m_databaseThread) + m_databaseThread->requestTermination(); +} + +#endif + +} // namespace WebCore diff --git a/WebCore/dom/Document.h b/WebCore/dom/Document.h new file mode 100644 index 0000000..14277ea --- /dev/null +++ b/WebCore/dom/Document.h @@ -0,0 +1,960 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef Document_h +#define Document_h + +#include "Attr.h" +#include "Color.h" +#include "DeprecatedPtrList.h" +#include "DeprecatedValueList.h" +#include "DocumentMarker.h" +#include "HTMLCollection.h" +#include "HTMLFormElement.h" +#include "KURL.h" +#include "StringHash.h" +#include "Timer.h" +#include <wtf/HashCountedSet.h> +#include <wtf/ListHashSet.h> + +// FIXME: We should move Mac off of the old Frame-based user stylesheet loading +// code and onto the new code in Page. We can't do that until the code in Page +// supports non-file: URLs, however. +#if PLATFORM(MAC) +#define FRAME_LOADS_USER_STYLESHEET 1 +#else +#define FRAME_LOADS_USER_STYLESHEET 0 +#endif + +namespace WebCore { + + class AXObjectCache; + class Attr; + class Attribute; + class CDATASection; + class CachedCSSStyleSheet; + class CSSStyleDeclaration; + class CSSStyleSelector; + class CSSStyleSheet; + class Comment; + class Database; + class DOMImplementation; + class DOMSelection; + class DOMWindow; + class DatabaseThread; + class DocLoader; + class DocumentFragment; + class DocumentType; + class EditingText; + class Element; + class EntityReference; + class Event; + class EventListener; + class Frame; + class FrameView; + class HTMLDocument; + class HTMLElement; + class HTMLFormControlElementWithState; + class HTMLFormElement; + class HTMLHeadElement; + class HTMLImageLoader; + class HTMLInputElement; + class HTMLMapElement; + class IntPoint; + class MouseEventWithHitTestResults; + class NameNodeList; + class NodeFilter; + class NodeIterator; + class NodeList; + class Page; + class PlatformMouseEvent; + class ProcessingInstruction; + class Range; + class RegisteredEventListener; + class RenderArena; + class SecurityOrigin; + class Settings; + class StyleSheet; + class StyleSheetList; + class Text; + class TextResourceDecoder; + class Tokenizer; + class TreeWalker; +#if ENABLE(XBL) + class XBLBindingManager; +#endif +#if ENABLE(XPATH) + class XPathEvaluator; + class XPathExpression; + class XPathNSResolver; + class XPathResult; +#endif + + struct DashboardRegionValue; + struct HitTestRequest; + +#if ENABLE(SVG) + class SVGDocumentExtensions; +#endif + + typedef int ExceptionCode; + +class FormElementKey { +public: + FormElementKey(AtomicStringImpl* = 0, AtomicStringImpl* = 0); + ~FormElementKey(); + FormElementKey(const FormElementKey&); + FormElementKey& operator=(const FormElementKey&); + AtomicStringImpl* name() const { return m_name; } + AtomicStringImpl* type() const { return m_type; } +private: + void ref() const; + void deref() const; + AtomicStringImpl* m_name; + AtomicStringImpl* m_type; +}; + +inline bool operator==(const FormElementKey& a, const FormElementKey& b) + { return a.name() == b.name() && a.type() == b.type(); } + +struct FormElementKeyHash { + static unsigned hash(const FormElementKey&); + static bool equal(const FormElementKey& a, const FormElementKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +struct FormElementKeyHashTraits : WTF::GenericHashTraits<FormElementKey> { + static FormElementKey deletedValue(); +}; + +class Document : public ContainerNode { +public: + Document(DOMImplementation*, Frame*, bool isXHTML = false); + virtual ~Document(); + + virtual void removedLastRef(); + + // Nodes belonging to this document hold "self-only" references - + // these are enough to keep the document from being destroyed, but + // not enough to keep it from removing its children. This allows a + // node that outlives its document to still have a valid document + // pointer without introducing reference cycles + + void selfOnlyRef() + { + ASSERT(!m_deletionHasBegun); + ++m_selfOnlyRefCount; + } + void selfOnlyDeref() + { + ASSERT(!m_deletionHasBegun); + --m_selfOnlyRefCount; + if (!m_selfOnlyRefCount && !refCount()) { +#ifndef NDEBUG + m_deletionHasBegun = true; +#endif + delete this; + } + } + + // DOM methods & attributes for Document + + DocumentType* doctype() const { return m_docType.get(); } + + DOMImplementation* implementation() const; + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + Element* documentElement() const; + virtual PassRefPtr<Element> createElement(const String& tagName, ExceptionCode&); + PassRefPtr<DocumentFragment> createDocumentFragment (); + PassRefPtr<Text> createTextNode(const String& data); + PassRefPtr<Comment> createComment(const String& data); + PassRefPtr<CDATASection> createCDATASection(const String& data, ExceptionCode&); + PassRefPtr<ProcessingInstruction> createProcessingInstruction(const String& target, const String& data, ExceptionCode&); + PassRefPtr<Attr> createAttribute(const String& name, ExceptionCode& ec) { return createAttributeNS(String(), name, ec); } + PassRefPtr<Attr> createAttributeNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode&); + PassRefPtr<EntityReference> createEntityReference(const String& name, ExceptionCode&); + PassRefPtr<Node> importNode(Node* importedNode, bool deep, ExceptionCode&); + virtual PassRefPtr<Element> createElementNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode&); + PassRefPtr<Element> createElement(const QualifiedName&, bool createdByParser, ExceptionCode& ec); + Element* getElementById(const AtomicString&) const; + + Element* elementFromPoint(int x, int y) const; + String readyState() const; + String inputEncoding() const; + String defaultCharset() const; + + String charset() const { return inputEncoding(); } + String characterSet() const { return inputEncoding(); } + + void setCharset(const String&); + + String contentLanguage() const { return m_contentLanguage; } + void setContentLanguage(const String& lang) { m_contentLanguage = lang; } + + String xmlEncoding() const { return m_xmlEncoding; } + String xmlVersion() const { return m_xmlVersion; } + bool xmlStandalone() const { return m_xmlStandalone; } + + void setXMLEncoding(const String& encoding) { m_xmlEncoding = encoding; } // read-only property, only to be set from XMLTokenizer + void setXMLVersion(const String&, ExceptionCode&); + void setXMLStandalone(bool, ExceptionCode&); + + KURL documentURI() const; + void setDocumentURI(const String&); + + virtual KURL baseURI() const; + + PassRefPtr<Node> adoptNode(PassRefPtr<Node> source, ExceptionCode&); + + PassRefPtr<HTMLCollection> images(); + PassRefPtr<HTMLCollection> embeds(); + PassRefPtr<HTMLCollection> plugins(); // an alias for embeds() required for the JS DOM bindings. + PassRefPtr<HTMLCollection> applets(); + PassRefPtr<HTMLCollection> links(); + PassRefPtr<HTMLCollection> forms(); + PassRefPtr<HTMLCollection> anchors(); + PassRefPtr<HTMLCollection> all(); + PassRefPtr<HTMLCollection> objects(); + PassRefPtr<HTMLCollection> scripts(); + PassRefPtr<HTMLCollection> windowNamedItems(const String& name); + PassRefPtr<HTMLCollection> documentNamedItems(const String& name); + + HTMLCollection::CollectionInfo* collectionInfo(HTMLCollection::Type type) + { + ASSERT(type >= HTMLCollection::FirstUnnamedDocumentCachedType); + unsigned index = type - HTMLCollection::FirstUnnamedDocumentCachedType; + ASSERT(index < HTMLCollection::NumUnnamedDocumentCachedTypes); + return &m_collectionInfo[index]; + } + + HTMLCollection::CollectionInfo* nameCollectionInfo(HTMLCollection::Type, const AtomicString& name); + + // DOM methods overridden from parent classes + + virtual String nodeName() const; + virtual NodeType nodeType() const; + + // Other methods (not part of DOM) + virtual bool isDocumentNode() const { return true; } + virtual bool isHTMLDocument() const { return false; } + virtual bool isImageDocument() const { return false; } +#if ENABLE(SVG) + virtual bool isSVGDocument() const { return false; } +#endif + virtual bool isPluginDocument() const { return false; } + + CSSStyleSelector* styleSelector() const { return m_styleSelector; } + + Element* getElementByAccessKey(const String& key) const; + + /** + * Updates the pending sheet count and then calls updateStyleSelector. + */ + void removePendingSheet(); + + /** + * This method returns true if all top-level stylesheets have loaded (including + * any @imports that they may be loading). + */ + bool haveStylesheetsLoaded() const + { + return m_pendingStylesheets <= 0 || m_ignorePendingStylesheets +#if USE(LOW_BANDWIDTH_DISPLAY) + || m_inLowBandwidthDisplay +#endif + ; + } + + /** + * Increments the number of pending sheets. The <link> elements + * invoke this to add themselves to the loading list. + */ + void addPendingSheet() { m_pendingStylesheets++; } + + bool gotoAnchorNeededAfterStylesheetsLoad() { return m_gotoAnchorNeededAfterStylesheetsLoad; } + void setGotoAnchorNeededAfterStylesheetsLoad(bool b) { m_gotoAnchorNeededAfterStylesheetsLoad = b; } + + /** + * Called when one or more stylesheets in the document may have been added, removed or changed. + * + * Creates a new style selector and assign it to this document. This is done by iterating through all nodes in + * document (or those before <BODY> in a HTML document), searching for stylesheets. Stylesheets can be contained in + * <LINK>, <STYLE> or <BODY> elements, as well as processing instructions (XML documents only). A list is + * constructed from these which is used to create the a new style selector which collates all of the stylesheets + * found and is used to calculate the derived styles for all rendering objects. + */ + void updateStyleSelector(); + + void recalcStyleSelector(); + + bool usesDescendantRules() const { return m_usesDescendantRules; } + void setUsesDescendantRules(bool b) { m_usesDescendantRules = b; } + bool usesSiblingRules() const { return m_usesSiblingRules; } + void setUsesSiblingRules(bool b) { m_usesSiblingRules = b; } + bool usesFirstLineRules() const { return m_usesFirstLineRules; } + void setUsesFirstLineRules(bool b) { m_usesFirstLineRules = b; } + bool usesFirstLetterRules() const { return m_usesFirstLetterRules; } + void setUsesFirstLetterRules(bool b) { m_usesFirstLetterRules = b; } + + // Machinery for saving and restoring state when you leave and then go back to a page. + void registerFormElementWithState(HTMLFormControlElementWithState* e) { m_formElementsWithState.add(e); } + void unregisterFormElementWithState(HTMLFormControlElementWithState* e) { m_formElementsWithState.remove(e); } + Vector<String> formElementsState() const; + void setStateForNewFormElements(const Vector<String>&); + bool hasStateForNewFormElements() const; + bool takeStateForFormElement(AtomicStringImpl* name, AtomicStringImpl* type, String& state); + + FrameView* view() const; // can be NULL + Frame* frame() const { return m_frame; } // can be NULL + Page* page() const; // can be NULL + Settings* settings() const; // can be NULL + + PassRefPtr<Range> createRange(); + + PassRefPtr<NodeIterator> createNodeIterator(Node* root, unsigned whatToShow, + PassRefPtr<NodeFilter>, bool expandEntityReferences, ExceptionCode&); + + PassRefPtr<TreeWalker> createTreeWalker(Node* root, unsigned whatToShow, + PassRefPtr<NodeFilter>, bool expandEntityReferences, ExceptionCode&); + + // Special support for editing + PassRefPtr<CSSStyleDeclaration> createCSSStyleDeclaration(); + PassRefPtr<EditingText> createEditingTextNode(const String&); + + virtual void recalcStyle( StyleChange = NoChange ); + static DeprecatedPtrList<Document>* changedDocuments; + virtual void updateRendering(); + void updateLayout(); + void updateLayoutIgnorePendingStylesheets(); + static void updateDocumentsRendering(); + DocLoader* docLoader() { return m_docLoader; } + + virtual void attach(); + virtual void detach(); + + RenderArena* renderArena() { return m_renderArena; } + + AXObjectCache* axObjectCache() const; + + // to get visually ordered hebrew and arabic pages right + void setVisuallyOrdered(); + + void open(); + void implicitOpen(); + void close(); + void implicitClose(); + void cancelParsing(); + + void write(const String& text); + void writeln(const String& text); + void finishParsing(); + void clear(); + + bool wellFormed() const { return m_wellFormed; } + + const KURL& url() const { return m_url.isEmpty() ? blankURL() : m_url; } + void setURL(const KURL&); + + const KURL& baseURL() const { return m_baseURL.isEmpty() ? url() : m_baseURL; } + void setBaseURL(const KURL&); + + const String& baseTarget() const { return m_baseTarget; } + void setBaseTarget(const String& baseTarget) { m_baseTarget = baseTarget; } + + KURL completeURL(const String&); + + // from cachedObjectClient + virtual void setCSSStyleSheet(const String& url, const String& charset, const CachedCSSStyleSheet*); + +#if FRAME_LOADS_USER_STYLESHEET + void setUserStyleSheet(const String& sheet); +#endif + + String userStyleSheet() const; + + CSSStyleSheet* elementSheet(); + CSSStyleSheet* mappedElementSheet(); + virtual Tokenizer* createTokenizer(); + Tokenizer* tokenizer() { return m_tokenizer; } + + bool printing() const { return m_printing; } + void setPrinting(bool p) { m_printing = p; } + + enum ParseMode { Compat, AlmostStrict, Strict }; + +private: + virtual void determineParseMode() {} + +public: + void setParseMode(ParseMode m) { m_parseMode = m; } + ParseMode parseMode() const { return m_parseMode; } + + bool inCompatMode() const { return m_parseMode == Compat; } + bool inAlmostStrictMode() const { return m_parseMode == AlmostStrict; } + bool inStrictMode() const { return m_parseMode == Strict; } + + void setParsing(bool); + bool parsing() const { return m_bParsing; } + int minimumLayoutDelay(); + bool shouldScheduleLayout(); + int elapsedTime() const; + + void setTextColor(const Color& color) { m_textColor = color; } + Color textColor() const { return m_textColor; } + + const Color& linkColor() const { return m_linkColor; } + const Color& visitedLinkColor() const { return m_visitedLinkColor; } + const Color& activeLinkColor() const { return m_activeLinkColor; } + void setLinkColor(const Color& c) { m_linkColor = c; } + void setVisitedLinkColor(const Color& c) { m_visitedLinkColor = c; } + void setActiveLinkColor(const Color& c) { m_activeLinkColor = c; } + void resetLinkColor(); + void resetVisitedLinkColor(); + void resetActiveLinkColor(); + + MouseEventWithHitTestResults prepareMouseEvent(const HitTestRequest&, const IntPoint&, const PlatformMouseEvent&); + + virtual bool childTypeAllowed(NodeType); + virtual PassRefPtr<Node> cloneNode(bool deep); + + virtual bool canReplaceChild(Node* newChild, Node* oldChild); + + StyleSheetList* styleSheets(); + + /* Newly proposed CSS3 mechanism for selecting alternate + stylesheets using the DOM. May be subject to change as + spec matures. - dwh + */ + String preferredStylesheetSet() const; + String selectedStylesheetSet() const; + void setSelectedStylesheetSet(const String&); + + bool setFocusedNode(PassRefPtr<Node>); + Node* focusedNode() const { return m_focusedNode.get(); } + + void setHoverNode(PassRefPtr<Node>); + Node* hoverNode() const { return m_hoverNode.get(); } + + void setActiveNode(PassRefPtr<Node>); + Node* activeNode() const { return m_activeNode.get(); } + + void focusedNodeRemoved(); + void removeFocusedNodeOfSubtree(Node*, bool amongChildrenOnly = false); + void hoveredNodeDetached(Node*); + void activeChainNodeDetached(Node*); + + // Updates for :target (CSS3 selector). + void setCSSTarget(Node*); + Node* getCSSTarget() const; + + void setDocumentChanged(bool); + + void attachNodeIterator(NodeIterator*); + void detachNodeIterator(NodeIterator*); + void notifyBeforeNodeRemoval(Node*); + DOMWindow* defaultView() const; + PassRefPtr<Event> createEvent(const String& eventType, ExceptionCode&); + + // keep track of what types of event listeners are registered, so we don't + // dispatch events unnecessarily + enum ListenerType { + DOMSUBTREEMODIFIED_LISTENER = 0x01, + DOMNODEINSERTED_LISTENER = 0x02, + DOMNODEREMOVED_LISTENER = 0x04, + DOMNODEREMOVEDFROMDOCUMENT_LISTENER = 0x08, + DOMNODEINSERTEDINTODOCUMENT_LISTENER = 0x10, + DOMATTRMODIFIED_LISTENER = 0x20, + DOMCHARACTERDATAMODIFIED_LISTENER = 0x40, + OVERFLOWCHANGED_LISTENER = 0x80 + }; + + bool hasListenerType(ListenerType listenerType) const { return (m_listenerTypes & listenerType); } + void addListenerType(ListenerType listenerType) { m_listenerTypes = m_listenerTypes | listenerType; } + + CSSStyleDeclaration* getOverrideStyle(Element*, const String& pseudoElt); + + void handleWindowEvent(Event*, bool useCapture); + void setHTMLWindowEventListener(const AtomicString &eventType, PassRefPtr<EventListener>); + EventListener* getHTMLWindowEventListener(const AtomicString &eventType); + void removeHTMLWindowEventListener(const AtomicString &eventType); + + void setHTMLWindowEventListener(const AtomicString& eventType, Attribute*); + + void addWindowEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + void removeWindowEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + bool hasWindowEventListener(const AtomicString& eventType); + + PassRefPtr<EventListener> createHTMLEventListener(const String& functionName, const String& code, Node*); + + /** + * Searches through the document, starting from fromNode, for the next selectable element that comes after fromNode. + * The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab indexes + * first (from lowest to highest), and then elements without tab indexes (in document order). + * + * @param fromNode The node from which to start searching. The node after this will be focused. May be null. + * + * @return The focus node that comes after fromNode + * + * See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1 + */ + Node* nextFocusableNode(Node* start, KeyboardEvent*); + + /** + * Searches through the document, starting from fromNode, for the previous selectable element (that comes _before_) + * fromNode. The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab + * indexes first (from lowest to highest), and then elements without tab indexes (in document order). + * + * @param fromNode The node from which to start searching. The node before this will be focused. May be null. + * + * @return The focus node that comes before fromNode + * + * See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1 + */ + Node* previousFocusableNode(Node* start, KeyboardEvent*); + + int nodeAbsIndex(Node*); + Node* nodeWithAbsIndex(int absIndex); + + /** + * Handles a HTTP header equivalent set by a meta tag using <meta http-equiv="..." content="...">. This is called + * when a meta tag is encountered during document parsing, and also when a script dynamically changes or adds a meta + * tag. This enables scripts to use meta tags to perform refreshes and set expiry dates in addition to them being + * specified in a HTML file. + * + * @param equiv The http header name (value of the meta tag's "equiv" attribute) + * @param content The header value (value of the meta tag's "content" attribute) + */ + void processHttpEquiv(const String& equiv, const String& content); + + void dispatchImageLoadEventSoon(HTMLImageLoader*); + void dispatchImageLoadEventsNow(); + void removeImage(HTMLImageLoader*); + + // Returns the owning element in the parent document. + // Returns 0 if this is the top level document. + Element* ownerElement() const; + + String title() const { return m_title; } + void setTitle(const String&, Element* titleElement = 0); + void removeTitle(Element* titleElement); + + String cookie() const; + void setCookie(const String&); + + String referrer() const; + + String domain() const; + void setDomain(const String& newDomain); + + String lastModified() const; + + const KURL& policyBaseURL() const { return m_policyBaseURL; } + void setPolicyBaseURL(const KURL& url) { m_policyBaseURL = url; } + + // The following implements the rule from HTML 4 for what valid names are. + // To get this right for all the XML cases, we probably have to improve this or move it + // and make it sensitive to the type of document. + static bool isValidName(const String&); + + // The following breaks a qualified name into a prefix and a local name. + // It also does a validity check, and returns false if the qualified name is invalid + // (empty string or invalid characters). + static bool parseQualifiedName(const String& qualifiedName, String& prefix, String& localName); + + void addElementById(const AtomicString& elementId, Element *element); + void removeElementById(const AtomicString& elementId, Element *element); + + void addImageMap(HTMLMapElement*); + void removeImageMap(HTMLMapElement*); + HTMLMapElement* getImageMap(const String& url) const; + + HTMLElement* body(); + void setBody(PassRefPtr<HTMLElement>, ExceptionCode&); + + HTMLHeadElement* head(); + + String toString() const; + + bool execCommand(const String& command, bool userInterface = false, const String& value = String()); + bool queryCommandEnabled(const String& command); + bool queryCommandIndeterm(const String& command); + bool queryCommandState(const String& command); + bool queryCommandSupported(const String& command); + String queryCommandValue(const String& command); + + void addMarker(Range*, DocumentMarker::MarkerType, String description = String()); + void addMarker(Node*, DocumentMarker); + void copyMarkers(Node *srcNode, unsigned startOffset, int length, Node *dstNode, int delta, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void removeMarkers(Range*, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void removeMarkers(Node*, unsigned startOffset, int length, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void removeMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void removeMarkers(Node*); + void repaintMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void setRenderedRectForMarker(Node*, DocumentMarker, const IntRect&); + void invalidateRenderedRectsForMarkersInRect(const IntRect&); + void shiftMarkers(Node*, unsigned startOffset, int delta, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + + DocumentMarker* markerContainingPoint(const IntPoint&, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + Vector<DocumentMarker> markersForNode(Node*); + Vector<IntRect> renderedRectsForMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + + // designMode support + enum InheritedBool { off = false, on = true, inherit }; + void setDesignMode(InheritedBool value); + InheritedBool getDesignMode() const; + bool inDesignMode() const; + + Document* parentDocument() const; + Document* topDocument() const; + + int docID() const { return m_docID; } + +#if ENABLE(XSLT) + void applyXSLTransform(ProcessingInstruction* pi); + void setTransformSource(void* doc); + const void* transformSource() { return m_transformSource; } + PassRefPtr<Document> transformSourceDocument() { return m_transformSourceDocument; } + void setTransformSourceDocument(Document* doc) { m_transformSourceDocument = doc; } +#endif + +#if ENABLE(XBL) + // XBL methods + XBLBindingManager* bindingManager() const { return m_bindingManager; } +#endif + + void incDOMTreeVersion() { ++m_domtree_version; } + unsigned domTreeVersion() const { return m_domtree_version; } + + void setDocType(PassRefPtr<DocumentType>); + + void finishedParsing(); + +#if ENABLE(XPATH) + // XPathEvaluator methods + PassRefPtr<XPathExpression> createExpression(const String& expression, + XPathNSResolver* resolver, + ExceptionCode& ec); + PassRefPtr<XPathNSResolver> createNSResolver(Node *nodeResolver); + PassRefPtr<XPathResult> evaluate(const String& expression, + Node* contextNode, + XPathNSResolver* resolver, + unsigned short type, + XPathResult* result, + ExceptionCode& ec); +#endif // ENABLE(XPATH) + + enum PendingSheetLayout { NoLayoutWithPendingSheets, DidLayoutWithPendingSheets, IgnoreLayoutWithPendingSheets }; + + bool didLayoutWithPendingStylesheets() const { return m_pendingSheetLayout == DidLayoutWithPendingSheets; } + + void setHasNodesWithPlaceholderStyle() { m_hasNodesWithPlaceholderStyle = true; } + + String iconURL(); + void setIconURL(const String& iconURL, const String& type); + + bool isAllowedToLoadLocalResources() const { return m_isAllowedToLoadLocalResources; } + + void setUseSecureKeyboardEntryWhenActive(bool); + bool useSecureKeyboardEntryWhenActive() const; + +#if USE(LOW_BANDWIDTH_DISPLAY) + void setDocLoader(DocLoader* loader) { m_docLoader = loader; } + bool inLowBandwidthDisplay() const { return m_inLowBandwidthDisplay; } + void setLowBandwidthDisplay(bool lowBandWidth) { m_inLowBandwidthDisplay = lowBandWidth; } +#endif + + void addNodeList() { m_numNodeLists++; } + void removeNodeList() { m_numNodeLists--; } + bool hasNodeLists() const { return m_numNodeLists != 0; } + + void updateFocusAppearanceSoon(); + void cancelFocusAppearanceUpdate(); + + // FF method for accessing the selection added for compatability. + DOMSelection* getSelection() const; + +private: + CSSStyleSelector* m_styleSelector; + bool m_didCalculateStyleSelector; + + Frame* m_frame; + DocLoader* m_docLoader; + Tokenizer* m_tokenizer; + bool m_wellFormed; + KURL m_url; + KURL m_baseURL; + String m_baseTarget; + + RefPtr<DocumentType> m_docType; + RefPtr<DOMImplementation> m_implementation; + + RefPtr<StyleSheet> m_sheet; +#if FRAME_LOADS_USER_STYLESHEET + String m_usersheet; +#endif + + // Track the number of currently loading top-level stylesheets. Sheets + // loaded using the @import directive are not included in this count. + // We use this count of pending sheets to detect when we can begin attaching + // elements. + int m_pendingStylesheets; + + // But sometimes you need to ignore pending stylesheet count to + // force an immediate layout when requested by JS. + bool m_ignorePendingStylesheets; + + // If we do ignore the pending stylesheet count, then we need to add a boolean + // to track that this happened so that we can do a full repaint when the stylesheets + // do eventually load. + PendingSheetLayout m_pendingSheetLayout; + + bool m_hasNodesWithPlaceholderStyle; + + RefPtr<CSSStyleSheet> m_elemSheet; + RefPtr<CSSStyleSheet> m_mappedElementSheet; + + bool m_printing; + + ParseMode m_parseMode; + + Color m_textColor; + + RefPtr<Node> m_focusedNode; + RefPtr<Node> m_hoverNode; + RefPtr<Node> m_activeNode; + mutable RefPtr<Element> m_documentElement; + + unsigned m_domtree_version; + + HashSet<NodeIterator*> m_nodeIterators; + + unsigned short m_listenerTypes; + RefPtr<StyleSheetList> m_styleSheets; + + RegisteredEventListenerList m_windowEventListeners; + + typedef HashMap<FormElementKey, Vector<String>, FormElementKeyHash, FormElementKeyHashTraits> FormElementStateMap; + ListHashSet<HTMLFormControlElementWithState*> m_formElementsWithState; + FormElementStateMap m_stateForNewFormElements; + + Color m_linkColor; + Color m_visitedLinkColor; + Color m_activeLinkColor; + + String m_preferredStylesheetSet; + String m_selectedStylesheetSet; + + bool m_loadingSheet; + bool visuallyOrdered; + bool m_bParsing; + bool m_docChanged; + bool m_inStyleRecalc; + bool m_closeAfterStyleRecalc; + bool m_usesDescendantRules; + bool m_usesSiblingRules; + bool m_usesFirstLineRules; + bool m_usesFirstLetterRules; + bool m_gotoAnchorNeededAfterStylesheetsLoad; + + String m_title; + bool m_titleSetExplicitly; + RefPtr<Element> m_titleElement; + + RenderArena* m_renderArena; + + typedef std::pair<Vector<DocumentMarker>, Vector<IntRect> > MarkerMapVectorPair; + typedef HashMap<RefPtr<Node>, MarkerMapVectorPair*> MarkerMap; + MarkerMap m_markers; + + mutable AXObjectCache* m_axObjectCache; + + DeprecatedPtrList<HTMLImageLoader> m_imageLoadEventDispatchSoonList; + DeprecatedPtrList<HTMLImageLoader> m_imageLoadEventDispatchingList; + Timer<Document> m_imageLoadEventTimer; + + Timer<Document> m_updateFocusAppearanceTimer; + + Node* m_cssTarget; + + bool m_processingLoadEvent; + double m_startTime; + bool m_overMinimumLayoutThreshold; + +#if ENABLE(XSLT) + void* m_transformSource; + RefPtr<Document> m_transformSourceDocument; +#endif + +#if ENABLE(XBL) + XBLBindingManager* m_bindingManager; // The access point through which documents and elements communicate with XBL. +#endif + + typedef HashMap<AtomicStringImpl*, HTMLMapElement*> ImageMapsByName; + ImageMapsByName m_imageMapsByName; + + KURL m_policyBaseURL; + + HashSet<Node*> m_disconnectedNodesWithEventListeners; + + int m_docID; // A unique document identifier used for things like document-specific mapped attributes. + + String m_xmlEncoding; + String m_xmlVersion; + bool m_xmlStandalone; + + String m_contentLanguage; + +public: + bool inPageCache(); + void setInPageCache(bool flag); + + // Elements can register themselves for the "willSaveToCache()" and + // "didRestoreFromCache()" callbacks + void registerForCacheCallbacks(Element*); + void unregisterForCacheCallbacks(Element*); + void willSaveToCache(); + void didRestoreFromCache(); + + void setShouldCreateRenderers(bool); + bool shouldCreateRenderers(); + + void setDecoder(TextResourceDecoder*); + TextResourceDecoder* decoder() const { return m_decoder.get(); } + + UChar backslashAsCurrencySymbol() const; + + void setDashboardRegionsDirty(bool f) { m_dashboardRegionsDirty = f; } + bool dashboardRegionsDirty() const { return m_dashboardRegionsDirty; } + bool hasDashboardRegions () const { return m_hasDashboardRegions; } + void setHasDashboardRegions (bool f) { m_hasDashboardRegions = f; } + const Vector<DashboardRegionValue>& dashboardRegions() const; + void setDashboardRegions(const Vector<DashboardRegionValue>&); + + void removeAllEventListenersFromAllNodes(); + + void registerDisconnectedNodeWithEventListeners(Node*); + void unregisterDisconnectedNodeWithEventListeners(Node*); + + HTMLFormElement::CheckedRadioButtons& checkedRadioButtons() { return m_checkedRadioButtons; } + +#if ENABLE(SVG) + const SVGDocumentExtensions* svgExtensions(); + SVGDocumentExtensions* accessSVGExtensions(); +#endif + + void initSecurityOrigin(); + SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); } + + // Explicitly override the security origin for this document. + // Note: It is dangerous to change the security origin of a document + // that already contains content. + void setSecurityOrigin(SecurityOrigin*); + + bool processingLoadEvent() const { return m_processingLoadEvent; } + +#if ENABLE(DATABASE) + void addOpenDatabase(Database*); + void removeOpenDatabase(Database*); + DatabaseThread* databaseThread(); // Creates the thread as needed, but not if it has been already terminated. + void setHasOpenDatabases() { m_hasOpenDatabases = true; } + bool hasOpenDatabases() { return m_hasOpenDatabases; } + void stopDatabases(); +#endif +protected: + void clearXMLVersion() { m_xmlVersion = String(); } + +private: + bool shouldBeAllowedToLoadLocalResources() const; + + void updateTitle(); + void removeAllDisconnectedNodeEventListeners(); + void imageLoadEventTimerFired(Timer<Document>*); + void updateFocusAppearanceTimerFired(Timer<Document>*); + + RefPtr<SecurityOrigin> m_securityOrigin; + + RenderObject* m_savedRenderer; + int m_secureForms; + + RefPtr<TextResourceDecoder> m_decoder; + + // We maintain the invariant that m_duplicateIds is the count of all elements with a given ID + // excluding the one referenced in m_elementsById, if any. This means it one less than the total count + // when the first node with a given ID is cached, otherwise the same as the total count. + mutable HashMap<AtomicStringImpl*, Element*> m_elementsById; + mutable HashCountedSet<AtomicStringImpl*> m_duplicateIds; + + mutable HashMap<StringImpl*, Element*, CaseFoldingHash> m_elementsByAccessKey; + + InheritedBool m_designMode; + + int m_selfOnlyRefCount; + + HTMLFormElement::CheckedRadioButtons m_checkedRadioButtons; + + typedef HashMap<AtomicStringImpl*, HTMLCollection::CollectionInfo*> NamedCollectionMap; + HTMLCollection::CollectionInfo m_collectionInfo[HTMLCollection::NumUnnamedDocumentCachedTypes]; + NamedCollectionMap m_nameCollectionInfo[HTMLCollection::NumNamedDocumentCachedTypes]; + +#if ENABLE(XPATH) + RefPtr<XPathEvaluator> m_xpathEvaluator; +#endif + +#if ENABLE(SVG) + SVGDocumentExtensions* m_svgExtensions; +#endif + + Vector<DashboardRegionValue> m_dashboardRegions; + bool m_hasDashboardRegions; + bool m_dashboardRegionsDirty; + + mutable bool m_accessKeyMapValid; + bool m_createRenderers; + bool m_inPageCache; + String m_iconURL; + + HashSet<Element*> m_pageCacheCallbackElements; + + bool m_isAllowedToLoadLocalResources; + + bool m_useSecureKeyboardEntryWhenActive; + + bool m_isXHTML; + + unsigned m_numNodeLists; + +#if ENABLE(DATABASE) + RefPtr<DatabaseThread> m_databaseThread; + bool m_hasOpenDatabases; // This never changes back to false, even as the database thread is closed. + typedef HashSet<Database*> DatabaseSet; + OwnPtr<DatabaseSet> m_openDatabaseSet; +#endif +#if USE(LOW_BANDWIDTH_DISPLAY) + bool m_inLowBandwidthDisplay; +#endif +}; + +} // namespace WebCore + +#endif // Document_h diff --git a/WebCore/dom/Document.idl b/WebCore/dom/Document.idl new file mode 100644 index 0000000..ddc25f3 --- /dev/null +++ b/WebCore/dom/Document.idl @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007 Samuel Weinig <sam@webkit.org> + * + * 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. + */ + +module core { + + interface [ + GenerateConstructor, + GenerateToJS, + CustomMarkFunction, + InterfaceUUID=48BB95FC-2D08-4c54-BE65-7558736A4CAE, + ImplementationUUID=FF5CBE81-F817-429c-A6C2-0CCCD2328062 + ] Document : EventTargetNode { + + // DOM Level 1 Core + readonly attribute DocumentType doctype; + readonly attribute DOMImplementation implementation; + readonly attribute Element documentElement; + + Element createElement(in DOMString tagName) + raises (DOMException); + DocumentFragment createDocumentFragment(); + Text createTextNode(in DOMString data); + Comment createComment(in DOMString data); + CDATASection createCDATASection(in DOMString data) + raises(DOMException); + [OldStyleObjC] ProcessingInstruction createProcessingInstruction(in DOMString target, + in DOMString data) + raises (DOMException); + Attr createAttribute(in DOMString name) + raises (DOMException); + EntityReference createEntityReference(in DOMString name) + raises(DOMException); + NodeList getElementsByTagName(in DOMString tagname); + + // Introduced in DOM Level 2: + + [OldStyleObjC] Node importNode(in Node importedNode, + in boolean deep) + raises (DOMException); + [OldStyleObjC] Element createElementNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString qualifiedName) + raises (DOMException); + [OldStyleObjC] Attr createAttributeNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString qualifiedName) + raises (DOMException); + [OldStyleObjC] NodeList getElementsByTagNameNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString localName); + Element getElementById(in DOMString elementId); + + // DOM Level 3 Core + + readonly attribute [ConvertNullStringTo=Null] DOMString inputEncoding; + + readonly attribute [ConvertNullStringTo=Null] DOMString xmlEncoding; + attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString xmlVersion + setter raises (DOMException); + attribute boolean xmlStandalone + setter raises (DOMException); + + Node adoptNode(in Node source) + raises (DOMException); + + attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString documentURI; + +#if !defined(LANGUAGE_COM) + // DOM Level 2 Events (DocumentEvents interface) + + Event createEvent(in DOMString eventType) + raises(DOMException); + + // DOM Level 2 Tranversal and Range (DocumentRange interface) + + Range createRange(); + + // DOM Level 2 Tranversal and Range (DocumentTraversal interface) + +#if !defined(LANGUAGE_OBJECTIVE_C) + NodeIterator createNodeIterator(in Node root, + in unsigned long whatToShow, + in NodeFilter filter, + in boolean entityReferenceExpansion) + raises(DOMException); + TreeWalker createTreeWalker(in Node root, + in unsigned long whatToShow, + in NodeFilter filter, + in boolean entityReferenceExpansion) + raises(DOMException); +#endif // !defined(LANGUAGE_OBJECTIVE_C) + + // DOM Level 2 Abstract Views (DocumentView interface) + + readonly attribute DOMWindow defaultView; + + // DOM Level 2 Style (DocumentStyle interface) + + readonly attribute StyleSheetList styleSheets; + + // DOM Level 2 Style (DocumentCSS interface) + + [OldStyleObjC] CSSStyleDeclaration getOverrideStyle(in Element element, + in DOMString pseudoElement); +#ifdef ENABLE_XPATH + // DOM Level 3 XPath (XPathEvaluator interface) + [OldStyleObjC] XPathExpression createExpression(in DOMString expression, + in XPathNSResolver resolver) + raises(DOMException); + XPathNSResolver createNSResolver(in Node nodeResolver); + [OldStyleObjC] XPathResult evaluate(in DOMString expression, + in Node contextNode, + in XPathNSResolver resolver, + in unsigned short type, + in XPathResult inResult) + raises(DOMException); +#endif // ENABLE_XPATH +#endif // !defined(LANGUAGE_COM) + + // Common extensions + + boolean execCommand(in DOMString command, + in boolean userInterface, + in [ConvertUndefinedOrNullToNullString] DOMString value); + +#if defined(LANGUAGE_OBJECTIVE_C) + // FIXME: remove the these two versions once [Optional] is implemented for Objective-C. + boolean execCommand(in DOMString command, + in boolean userInterface); + boolean execCommand(in DOMString command); +#endif + + boolean queryCommandEnabled(in DOMString command); + boolean queryCommandIndeterm(in DOMString command); + boolean queryCommandState(in DOMString command); + boolean queryCommandSupported(in DOMString command); + [ConvertNullStringTo=False] DOMString queryCommandValue(in DOMString command); + + // Moved down from HTMLDocument + + attribute [ConvertNullToNullString] DOMString title; + readonly attribute DOMString referrer; +#if defined(LANGUAGE_JAVASCRIPT) + attribute [ConvertNullToNullString] DOMString domain; +#else + readonly attribute DOMString domain; +#endif + readonly attribute DOMString URL; + + // FIXME: the DOM spec states that this attribute can + // raise an exception on setting. + attribute [ConvertNullToNullString] DOMString cookie + /*setter raises (DOMException)*/; + + // FIXME: the DOM spec does NOT have this attribute + // raising an exception. + attribute HTMLElement body + setter raises (DOMException); + + readonly attribute HTMLCollection images; + readonly attribute HTMLCollection applets; + readonly attribute HTMLCollection links; + readonly attribute HTMLCollection forms; + readonly attribute HTMLCollection anchors; + readonly attribute DOMString lastModified; + + NodeList getElementsByName(in DOMString elementName); + +#if defined(LANGUAGE_JAVASCRIPT) + attribute [Custom] Location location; +#endif + + // IE extensions + + attribute [ConvertNullStringTo=Undefined, ConvertNullToNullString] DOMString charset; + readonly attribute [ConvertNullStringTo=Undefined] DOMString defaultCharset; + readonly attribute [ConvertNullStringTo=Undefined] DOMString readyState; + + Element elementFromPoint(in long x, in long y); + + // Mozilla extensions +#if defined(LANGUAGE_JAVASCRIPT) + DOMSelection getSelection(); +#endif + readonly attribute [ConvertNullStringTo=Null] DOMString characterSet; + + // WebKit extensions + + readonly attribute [ConvertNullStringTo=Null] DOMString preferredStylesheetSet; + attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString selectedStylesheetSet; + +#if !defined(LANGUAGE_COM) +#if !defined(LANGUAGE_JAVASCRIPT) + CSSStyleDeclaration createCSSStyleDeclaration(); +#endif +#endif + +#if defined(LANGUAGE_OBJECTIVE_C) + // DOM Level 2 Style Interface + [OldStyleObjC, UsesView] CSSStyleDeclaration getComputedStyle(in Element element, + in DOMString pseudoElement); + + // WebKit extension + // FIXME: remove the first version once [Optional] is implemented for Objective-C. + [UsesView] CSSRuleList getMatchedCSSRules(in Element element, + in DOMString pseudoElement); + [UsesView] CSSRuleList getMatchedCSSRules(in Element element, + in DOMString pseudoElement, + in [Optional] boolean authorOnly); +#endif + + // HTML 5 + NodeList getElementsByClassName(in DOMString tagname); + + // DocumentSelector - Selector API + Element querySelector(in [ConvertUndefinedOrNullToNullString] DOMString selectors) + raises(DOMException); + NodeList querySelectorAll(in [ConvertUndefinedOrNullToNullString] DOMString selectors) + raises(DOMException); + }; + +} diff --git a/WebCore/dom/DocumentFragment.cpp b/WebCore/dom/DocumentFragment.cpp new file mode 100644 index 0000000..298f1b9 --- /dev/null +++ b/WebCore/dom/DocumentFragment.cpp @@ -0,0 +1,77 @@ +/** + * 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) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" + +#include "DocumentFragment.h" + +namespace WebCore { + +DocumentFragment::DocumentFragment(Document *doc) : ContainerNode(doc) +{ +} + +String DocumentFragment::nodeName() const +{ + return "#document-fragment"; +} + +Node::NodeType DocumentFragment::nodeType() const +{ + return DOCUMENT_FRAGMENT_NODE; +} + +// DOM Section 1.1.1 +bool DocumentFragment::childTypeAllowed(NodeType type) +{ + switch (type) { + case ELEMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + case COMMENT_NODE: + case TEXT_NODE: + case CDATA_SECTION_NODE: + case ENTITY_REFERENCE_NODE: + return true; + default: + return false; + } +} + +String DocumentFragment::toString() const +{ + String result; + for (Node *child = firstChild(); child != NULL; child = child->nextSibling()) + result += child->toString(); + return result; +} + +PassRefPtr<Node> DocumentFragment::cloneNode(bool deep) +{ + RefPtr<DocumentFragment> clone = new DocumentFragment(document()); + if (deep) + cloneChildNodes(clone.get()); + return clone.release(); +} + +} diff --git a/WebCore/dom/DocumentFragment.h b/WebCore/dom/DocumentFragment.h new file mode 100644 index 0000000..db5e808 --- /dev/null +++ b/WebCore/dom/DocumentFragment.h @@ -0,0 +1,47 @@ +/* + * 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) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef DocumentFragment_h +#define DocumentFragment_h + +#include "ContainerNode.h" + +namespace WebCore { + +class DocumentFragment : public ContainerNode +{ +public: + DocumentFragment(Document*); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); + virtual String toString() const; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/DocumentFragment.idl b/WebCore/dom/DocumentFragment.idl new file mode 100644 index 0000000..24af302 --- /dev/null +++ b/WebCore/dom/DocumentFragment.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=F5C8DAF0-D728-4b2b-9D9C-630621B07D35, + ImplementationUUID=E57BF71F-3FAA-495c-A307-E288F8E5B2EC + ] DocumentFragment : EventTargetNode { + }; + +} diff --git a/WebCore/dom/DocumentMarker.h b/WebCore/dom/DocumentMarker.h new file mode 100644 index 0000000..2ba9b47 --- /dev/null +++ b/WebCore/dom/DocumentMarker.h @@ -0,0 +1,60 @@ +/* + * This file is part of the DOM implementation for WebCore. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef DocumentMarker_h +#define DocumentMarker_h + +#include "PlatformString.h" + +namespace WebCore { + class String; + +// A range of a node within a document that is "marked", such as the range of a misspelled word. +// It optionally includes a description that could be displayed in the user interface. +struct DocumentMarker { + + enum MarkerType { + AllMarkers = -1, + Spelling, + Grammar, + TextMatch + }; + + MarkerType type; + unsigned startOffset; + unsigned endOffset; + String description; + + bool operator==(const DocumentMarker& o) const + { + return type == o.type && startOffset == o.startOffset && endOffset == o.endOffset; + } + + bool operator!=(const DocumentMarker& o) const + { + return !(*this == o); + } +}; + +} // namespace WebCore + +#endif // DocumentMarker_h diff --git a/WebCore/dom/DocumentType.cpp b/WebCore/dom/DocumentType.cpp new file mode 100644 index 0000000..f051363 --- /dev/null +++ b/WebCore/dom/DocumentType.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "DocumentType.h" + +#include "DOMImplementation.h" +#include "Document.h" +#include "NamedNodeMap.h" + +namespace WebCore { + +DocumentType::DocumentType(DOMImplementation* i, Document* document, const String& n, const String& p, const String& s) + : Node(document) + , m_implementation(i) + , m_name(n) + , m_publicId(p) + , m_systemId(s) +{ +} + +DocumentType::DocumentType(Document* document, const String& n, const String& p, const String& s) + : Node(document) + , m_name(n) + , m_publicId(p) + , m_systemId(s) +{ +} + +DocumentType::DocumentType(Document* document, const DocumentType &t) + : Node(document) + , m_implementation(t.m_implementation) + , m_name(t.m_name) + , m_publicId(t.m_publicId) + , m_systemId(t.m_systemId) + , m_subset(t.m_subset) +{ +} + +String DocumentType::toString() const +{ + if (m_name.isEmpty()) + return ""; + + String result = "<!DOCTYPE "; + result += m_name; + if (!m_publicId.isEmpty()) { + result += " PUBLIC \""; + result += m_publicId; + result += "\""; + if (!m_systemId.isEmpty()) { + result += " \""; + result += m_systemId; + result += "\""; + } + } else if (!m_systemId.isEmpty()) { + result += " SYSTEM \""; + result += m_systemId; + result += "\""; + } + if (!m_subset.isEmpty()) { + result += " ["; + result += m_subset; + result += "]"; + } + result += ">"; + return result; +} + +KURL DocumentType::baseURI() const +{ + return KURL(); +} + +String DocumentType::nodeName() const +{ + return name(); +} + +Node::NodeType DocumentType::nodeType() const +{ + return DOCUMENT_TYPE_NODE; +} + +PassRefPtr<Node> DocumentType::cloneNode(bool /*deep*/) +{ + return new DocumentType(document(), m_name, m_publicId, m_systemId); +} + +void DocumentType::insertedIntoDocument() +{ + // Our document node can be null if we were created by a DOMImplementation. We use the parent() instead. + ASSERT(parent() && parent()->isDocumentNode()); + if (parent() && parent()->isDocumentNode()) { + Document* doc = static_cast<Document*>(parent()); + if (!doc->doctype()) + doc->setDocType(this); + } + Node::insertedIntoDocument(); +} + +void DocumentType::removedFromDocument() +{ + if (document() && document()->doctype() == this) + document()->setDocType(0); + Node::removedFromDocument(); +} + +} diff --git a/WebCore/dom/DocumentType.h b/WebCore/dom/DocumentType.h new file mode 100644 index 0000000..c348c4c --- /dev/null +++ b/WebCore/dom/DocumentType.h @@ -0,0 +1,77 @@ +/* + * 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) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ +#ifndef DocumentType_h +#define DocumentType_h + +#include "Node.h" + +namespace WebCore { + +class NamedNodeMap; +class DOMImplementation; + +class DocumentType : public Node +{ +public: + DocumentType(DOMImplementation *, Document *, const String &name, const String &publicId, const String &systemId); + DocumentType(Document *, const String &name, const String &publicId, const String &systemId); + DocumentType(Document *, const DocumentType &); + + // DOM methods & attributes for DocumentType + NamedNodeMap *entities() const { return m_entities.get(); } + NamedNodeMap *notations() const { return m_notations.get(); } + + String name() const { return m_name; } + String publicId() const { return m_publicId; } + String systemId() const { return m_systemId; } + String internalSubset() const { return m_subset; } + + virtual KURL baseURI() const; + + // Other methods (not part of DOM) + DOMImplementation *implementation() const { return m_implementation.get(); } + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual String toString() const; + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + +private: + RefPtr<DOMImplementation> m_implementation; + RefPtr<NamedNodeMap> m_entities; + RefPtr<NamedNodeMap> m_notations; + + String m_name; + String m_publicId; + String m_systemId; + String m_subset; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/DocumentType.idl b/WebCore/dom/DocumentType.idl new file mode 100644 index 0000000..ef7b5b6 --- /dev/null +++ b/WebCore/dom/DocumentType.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + */ + +module core { + + interface [ + GenerateConstructor, + GenerateNativeConverter, + InterfaceUUID=20F04535-A423-4273-8CFE-3AD996100D29, + ImplementationUUID=736D952F-DBAF-458b-834B-F1638700BD88 + ] DocumentType : Node { + + // DOM Level 1 + + readonly attribute DOMString name; + readonly attribute NamedNodeMap entities; + readonly attribute NamedNodeMap notations; + + // DOM Level 2 + + readonly attribute [ConvertNullStringTo=Null] DOMString publicId; + readonly attribute [ConvertNullStringTo=Null] DOMString systemId; + readonly attribute [ConvertNullStringTo=Null] DOMString internalSubset; + + }; + +} diff --git a/WebCore/dom/DynamicNodeList.cpp b/WebCore/dom/DynamicNodeList.cpp new file mode 100644 index 0000000..5a515d2 --- /dev/null +++ b/WebCore/dom/DynamicNodeList.cpp @@ -0,0 +1,183 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "DynamicNodeList.h" + +#include "Document.h" +#include "Element.h" + +namespace WebCore { + +DynamicNodeList::DynamicNodeList(PassRefPtr<Node> rootNode, bool needsNotifications) + : m_rootNode(rootNode) + , m_caches(new Caches) + , m_ownsCaches(true) + , m_needsNotifications(needsNotifications) +{ + m_rootNode->registerDynamicNodeList(this); +} + +DynamicNodeList::DynamicNodeList(PassRefPtr<Node> rootNode, DynamicNodeList::Caches* info, bool needsNotifications) + : m_rootNode(rootNode) + , m_caches(info) + , m_ownsCaches(false) + , m_needsNotifications(needsNotifications) +{ + m_rootNode->registerDynamicNodeList(this); +} + +DynamicNodeList::~DynamicNodeList() +{ + m_rootNode->unregisterDynamicNodeList(this); + if (m_ownsCaches) + delete m_caches; +} + +unsigned DynamicNodeList::length() const +{ + if (m_caches->isLengthCacheValid) + return m_caches->cachedLength; + + unsigned len = 0; + + for (Node* n = m_rootNode->firstChild(); n; n = n->traverseNextNode(m_rootNode.get())) { + if (n->isElementNode()) { + if (nodeMatches(n)) + len++; + } + } + + m_caches->cachedLength = len; + m_caches->isLengthCacheValid = true; + + return len; +} + +Node* DynamicNodeList::itemForwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const +{ + ASSERT(remainingOffset >= 0); + for (Node* n = start; n; n = n->traverseNextNode(m_rootNode.get())) { + if (n->isElementNode()) { + if (nodeMatches(n)) { + if (!remainingOffset) { + m_caches->lastItem = n; + m_caches->lastItemOffset = offset; + m_caches->isItemCacheValid = true; + return n; + } + remainingOffset--; + } + } + } + + return 0; // no matching node in this subtree +} + +Node* DynamicNodeList::itemBackwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const +{ + ASSERT(remainingOffset < 0); + for (Node* n = start; n; n = n->traversePreviousNode(m_rootNode.get())) { + if (n->isElementNode()) { + if (nodeMatches(n)) { + if (!remainingOffset) { + m_caches->lastItem = n; + m_caches->lastItemOffset = offset; + m_caches->isItemCacheValid = true; + return n; + } + remainingOffset++; + } + } + } + + return 0; // no matching node in this subtree +} + +Node* DynamicNodeList::item(unsigned offset) const +{ + int remainingOffset = offset; + Node* start = m_rootNode->firstChild(); + if (m_caches->isItemCacheValid) { + if (offset == m_caches->lastItemOffset) + return m_caches->lastItem; + else if (offset > m_caches->lastItemOffset || m_caches->lastItemOffset - offset < offset) { + start = m_caches->lastItem; + remainingOffset -= m_caches->lastItemOffset; + } + } + + if (remainingOffset < 0) + return itemBackwardsFromCurrent(start, offset, remainingOffset); + return itemForwardsFromCurrent(start, offset, remainingOffset); +} + +Node* DynamicNodeList::itemWithName(const AtomicString& elementId) const +{ + if (m_rootNode->isDocumentNode() || m_rootNode->inDocument()) { + Node* node = m_rootNode->document()->getElementById(elementId); + + if (!node || !nodeMatches(node)) + return 0; + + for (Node* p = node->parentNode(); p; p = p->parentNode()) { + if (p == m_rootNode) + return node; + } + + return 0; + } + + unsigned l = length(); + for (unsigned i = 0; i < l; i++) { + Node* node = item(i); + if (node->isElementNode() && static_cast<Element*>(node)->getIDAttribute() == elementId) + return node; + } + + return 0; +} + +void DynamicNodeList::rootNodeChildrenChanged() +{ + m_caches->reset(); +} + +void DynamicNodeList::rootNodeAttributeChanged() +{ +} + +DynamicNodeList::Caches::Caches() + : lastItem(0) + , isLengthCacheValid(false) + , isItemCacheValid(false) +{ +} + +void DynamicNodeList::Caches::reset() +{ + lastItem = 0; + isLengthCacheValid = false; + isItemCacheValid = false; +} + +} // namespace WebCore diff --git a/WebCore/dom/DynamicNodeList.h b/WebCore/dom/DynamicNodeList.h new file mode 100644 index 0000000..58e50fd --- /dev/null +++ b/WebCore/dom/DynamicNodeList.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef DynamicNodeList_h +#define DynamicNodeList_h + +#include "NodeList.h" +#include <wtf/RefCounted.h> +#include <wtf/Forward.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class AtomicString; + class Node; + + class DynamicNodeList : public NodeList { + public: + struct Caches { + Caches(); + void reset(); + + unsigned cachedLength; + Node* lastItem; + unsigned lastItemOffset; + bool isLengthCacheValid : 1; + bool isItemCacheValid : 1; + }; + + DynamicNodeList(PassRefPtr<Node> rootNode, bool needsNotifications); + DynamicNodeList(PassRefPtr<Node> rootNode, Caches*, bool needsNotifications); + virtual ~DynamicNodeList(); + + bool needsNotifications() const { return m_needsNotifications; } + + // DOM methods & attributes for NodeList + virtual unsigned length() const; + virtual Node* item(unsigned index) const; + virtual Node* itemWithName(const AtomicString&) const; + + // Other methods (not part of DOM) + virtual void rootNodeChildrenChanged(); + virtual void rootNodeAttributeChanged(); + + protected: + virtual bool nodeMatches(Node*) const = 0; + + RefPtr<Node> m_rootNode; + mutable Caches* m_caches; + bool m_ownsCaches; + bool m_needsNotifications; + + private: + Node* itemForwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const; + Node* itemBackwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const; + }; + +} // namespace WebCore + +#endif // DynamicNodeList_h diff --git a/WebCore/dom/EditingText.cpp b/WebCore/dom/EditingText.cpp new file mode 100644 index 0000000..59661e9 --- /dev/null +++ b/WebCore/dom/EditingText.cpp @@ -0,0 +1,52 @@ +/** + * 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) + * Copyright (C) 2003 Apple Computer, Inc. + * + * 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 "config.h" +#include "EditingText.h" + +// FIXME: does this really require a class? Perhaps any Text node +// inside an editable element should have the "always create a renderer" +// behavior. + +namespace WebCore { + +EditingText::EditingText(Document *impl, const String &text) + : Text(impl, text) +{ +} + +EditingText::EditingText(Document *impl) + : Text(impl) +{ +} + +EditingText::~EditingText() +{ +} + +bool EditingText::rendererIsNeeded(RenderStyle *style) +{ + return true; +} + +} // namespace WebCore diff --git a/WebCore/dom/EditingText.h b/WebCore/dom/EditingText.h new file mode 100644 index 0000000..3dcd8c1 --- /dev/null +++ b/WebCore/dom/EditingText.h @@ -0,0 +1,44 @@ +/* + * 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) + * Copyright (C) 2003 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef EditingText_h +#define EditingText_h + +#include "Text.h" + +namespace WebCore { + +class EditingText : public Text +{ +public: + EditingText(Document *impl, const String &text); + EditingText(Document *impl); + virtual ~EditingText(); + + virtual bool rendererIsNeeded(RenderStyle *); +}; + +} // namespace WebCore + +#endif // EditingText_h diff --git a/WebCore/dom/Element.cpp b/WebCore/dom/Element.cpp new file mode 100644 index 0000000..8af84e2 --- /dev/null +++ b/WebCore/dom/Element.cpp @@ -0,0 +1,1230 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2007 David Smith (catfish.man@gmail.com) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * (C) 2007 Eric Seidel (eric@webkit.org) + * + * 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 "config.h" +#include "Element.h" + +#include "CSSStyleSelector.h" +#include "CString.h" +#include "ClassNames.h" +#include "ClassNodeList.h" +#include "Document.h" +#include "Editor.h" +#include "ExceptionCode.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "NamedAttrMap.h" +#include "NodeList.h" +#include "Page.h" +#include "PlatformString.h" +#include "RenderBlock.h" +#include "SelectionController.h" +#include "TextIterator.h" +#include "XMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; +using namespace XMLNames; + +class ElementRareData { +public: + ElementRareData(Element*); + void resetComputedStyle(Element*); + + IntSize m_minimumSizeForResizing; + RenderStyle* m_computedStyle; + bool m_needsFocusAppearanceUpdateSoonAfterAttach; +}; + +typedef HashMap<const Element*, ElementRareData*> ElementRareDataMap; + +static ElementRareDataMap& rareDataMap() +{ + static ElementRareDataMap* dataMap = new ElementRareDataMap; + return *dataMap; +} + +static ElementRareData* rareDataFromMap(const Element* element) +{ + return rareDataMap().get(element); +} + +static inline IntSize defaultMinimumSizeForResizing() +{ + return IntSize(INT_MAX, INT_MAX); +} + +inline ElementRareData::ElementRareData(Element* element) + : m_minimumSizeForResizing(defaultMinimumSizeForResizing()) + , m_computedStyle(0) + , m_needsFocusAppearanceUpdateSoonAfterAttach(false) +{ +} + +void ElementRareData::resetComputedStyle(Element* element) +{ + if (!m_computedStyle) + return; + m_computedStyle->deref(element->document()->renderArena()); + m_computedStyle = 0; +} + +Element::Element(const QualifiedName& qName, Document *doc) + : ContainerNode(doc) + , m_tagName(qName) + , m_isStyleAttributeValid(true) + , m_synchronizingStyleAttribute(false) + , m_parsingChildrenFinished(true) +{ +} + +Element::~Element() +{ + if (namedAttrMap) + namedAttrMap->detachFromElement(); + + if (!m_attrWasSpecifiedOrElementHasRareData) + ASSERT(!rareDataMap().contains(this)); + else { + ElementRareDataMap& dataMap = rareDataMap(); + ElementRareDataMap::iterator it = dataMap.find(this); + ASSERT(it != dataMap.end()); + delete it->second; + dataMap.remove(it); + } +} + +inline ElementRareData* Element::rareData() +{ + return m_attrWasSpecifiedOrElementHasRareData ? rareDataFromMap(this) : 0; +} + +inline const ElementRareData* Element::rareData() const +{ + return m_attrWasSpecifiedOrElementHasRareData ? rareDataFromMap(this) : 0; +} + +ElementRareData* Element::createRareData() +{ + if (m_attrWasSpecifiedOrElementHasRareData) + return rareDataMap().get(this); + ASSERT(!rareDataMap().contains(this)); + ElementRareData* data = new ElementRareData(this); + rareDataMap().set(this, data); + m_attrWasSpecifiedOrElementHasRareData = true; + return data; +} + +PassRefPtr<Node> Element::cloneNode(bool deep) +{ + ExceptionCode ec = 0; + RefPtr<Element> clone = document()->createElementNS(namespaceURI(), nodeName(), ec); + ASSERT(!ec); + + // clone attributes + if (namedAttrMap) + *clone->attributes() = *namedAttrMap; + + clone->copyNonAttributeProperties(this); + + if (deep) + cloneChildNodes(clone.get()); + + return clone.release(); +} + +void Element::removeAttribute(const QualifiedName& name, ExceptionCode& ec) +{ + if (namedAttrMap) { + namedAttrMap->removeNamedItem(name, ec); + if (ec == NOT_FOUND_ERR) + ec = 0; + } +} + +void Element::setAttribute(const QualifiedName& name, const String &value) +{ + ExceptionCode ec = 0; + setAttribute(name, value.impl(), ec); +} + +void Element::setBooleanAttribute(const QualifiedName& name, bool b) +{ + if (b) + setAttribute(name, name.localName()); + else { + ExceptionCode ex; + removeAttribute(name, ex); + } +} + +// Virtual function, defined in base class. +NamedAttrMap *Element::attributes() const +{ + return attributes(false); +} + +NamedAttrMap* Element::attributes(bool readonly) const +{ + updateStyleAttributeIfNeeded(); + if (!readonly && !namedAttrMap) + createAttributeMap(); + return namedAttrMap.get(); +} + +Node::NodeType Element::nodeType() const +{ + return ELEMENT_NODE; +} + +const ClassNames* Element::getClassNames() const +{ + return 0; +} + +const AtomicString& Element::getIDAttribute() const +{ + return namedAttrMap ? namedAttrMap->id() : nullAtom; +} + +bool Element::hasAttribute(const QualifiedName& name) const +{ + return hasAttributeNS(name.namespaceURI(), name.localName()); +} + +const AtomicString& Element::getAttribute(const QualifiedName& name) const +{ + if (name == styleAttr) + updateStyleAttributeIfNeeded(); + + if (namedAttrMap) + if (Attribute* a = namedAttrMap->getAttributeItem(name)) + return a->value(); + + return nullAtom; +} + +void Element::scrollIntoView(bool alignToTop) +{ + document()->updateLayoutIgnorePendingStylesheets(); + IntRect bounds = getRect(); + if (renderer()) { + // Align to the top / bottom and to the closest edge. + if (alignToTop) + renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways); + else + renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignBottomAlways); + } +} + +void Element::scrollIntoViewIfNeeded(bool centerIfNeeded) +{ + document()->updateLayoutIgnorePendingStylesheets(); + IntRect bounds = getRect(); + if (renderer()) { + if (centerIfNeeded) + renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignCenterIfNeeded, RenderLayer::gAlignCenterIfNeeded); + else + renderer()->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignToEdgeIfNeeded); + } +} + +void Element::scrollByUnits(int units, ScrollGranularity granularity) +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject *rend = renderer()) { + if (rend->hasOverflowClip()) { + ScrollDirection direction = ScrollDown; + if (units < 0) { + direction = ScrollUp; + units = -units; + } + rend->layer()->scroll(direction, granularity, units); + } + } +} + +void Element::scrollByLines(int lines) +{ + scrollByUnits(lines, ScrollByLine); +} + +void Element::scrollByPages(int pages) +{ + scrollByUnits(pages, ScrollByPage); +} + +int Element::offsetLeft() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->offsetLeft(); + return 0; +} + +int Element::offsetTop() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->offsetTop(); + return 0; +} + +int Element::offsetWidth() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->offsetWidth(); + return 0; +} + +int Element::offsetHeight() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->offsetHeight(); + return 0; +} + +Element* Element::offsetParent() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + if (RenderObject* offsetParent = rend->offsetParent()) + return static_cast<Element*>(offsetParent->element()); + return 0; +} + +int Element::clientLeft() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + if (RenderObject* rend = renderer()) + return rend->clientLeft(); + return 0; +} + +int Element::clientTop() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + if (RenderObject* rend = renderer()) + return rend->clientTop(); + return 0; +} + +int Element::clientWidth() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + // When in strict mode, clientWidth for the document element should return the width of the containing frame. + // When in quirks mode, clientWidth for the body element should return the width of the containing frame. + bool inCompatMode = document()->inCompatMode(); + if ((!inCompatMode && document()->documentElement() == this) || + (inCompatMode && isHTMLElement() && document()->body() == this)) { + if (FrameView* view = document()->view()) + return view->visibleWidth(); + } + + + if (RenderObject* rend = renderer()) + return rend->clientWidth(); + return 0; +} + +int Element::clientHeight() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + // When in strict mode, clientHeight for the document element should return the height of the containing frame. + // When in quirks mode, clientHeight for the body element should return the height of the containing frame. + bool inCompatMode = document()->inCompatMode(); + + if ((!inCompatMode && document()->documentElement() == this) || + (inCompatMode && isHTMLElement() && document()->body() == this)) { + if (FrameView* view = document()->view()) + return view->visibleHeight(); + } + + if (RenderObject* rend = renderer()) + return rend->clientHeight(); + return 0; +} + +int Element::scrollLeft() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->scrollLeft(); + return 0; +} + +int Element::scrollTop() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->scrollTop(); + return 0; +} + +void Element::setScrollLeft(int newLeft) +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject *rend = renderer()) + rend->setScrollLeft(newLeft); +} + +void Element::setScrollTop(int newTop) +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject *rend = renderer()) + rend->setScrollTop(newTop); +} + +int Element::scrollWidth() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->scrollWidth(); + return 0; +} + +int Element::scrollHeight() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + return rend->scrollHeight(); + return 0; +} + +static inline bool shouldIgnoreAttributeCase(const Element* e) +{ + return e && e->document()->isHTMLDocument() && e->isHTMLElement(); +} + +const AtomicString& Element::getAttribute(const String& name) const +{ + String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; + if (localName == styleAttr.localName()) + updateStyleAttributeIfNeeded(); + + if (namedAttrMap) + if (Attribute* a = namedAttrMap->getAttributeItem(localName)) + return a->value(); + + return nullAtom; +} + +const AtomicString& Element::getAttributeNS(const String& namespaceURI, const String& localName) const +{ + return getAttribute(QualifiedName(nullAtom, localName, namespaceURI)); +} + +void Element::setAttribute(const String& name, const String& value, ExceptionCode& ec) +{ + if (!Document::isValidName(name)) { + ec = INVALID_CHARACTER_ERR; + return; + } + + String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; + + // allocate attributemap if necessary + Attribute* old = attributes(false)->getAttributeItem(localName); + + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly + if (namedAttrMap->isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + document()->incDOMTreeVersion(); + + if (localName == idAttr.localName()) + updateId(old ? old->value() : nullAtom, value); + + if (old && value.isNull()) + namedAttrMap->removeAttribute(old->name()); + else if (!old && !value.isNull()) + namedAttrMap->addAttribute(createAttribute(QualifiedName(nullAtom, localName, nullAtom), value.impl())); + else if (old && !value.isNull()) { + old->setValue(value); + attributeChanged(old); + } +} + +void Element::setAttribute(const QualifiedName& name, StringImpl* value, ExceptionCode& ec) +{ + document()->incDOMTreeVersion(); + + // allocate attributemap if necessary + Attribute* old = attributes(false)->getAttributeItem(name); + + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly + if (namedAttrMap->isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + if (name == idAttr) + updateId(old ? old->value() : nullAtom, value); + + if (old && !value) + namedAttrMap->removeAttribute(name); + else if (!old && value) + namedAttrMap->addAttribute(createAttribute(name, value)); + else if (old && value) { + old->setValue(value); + attributeChanged(old); + } +} + +Attribute* Element::createAttribute(const QualifiedName& name, StringImpl* value) +{ + return new Attribute(name, value); +} + +void Element::setAttributeMap(NamedAttrMap* list) +{ + document()->incDOMTreeVersion(); + + // If setting the whole map changes the id attribute, we need to call updateId. + + Attribute *oldId = namedAttrMap ? namedAttrMap->getAttributeItem(idAttr) : 0; + Attribute *newId = list ? list->getAttributeItem(idAttr) : 0; + + if (oldId || newId) + updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom); + + if (namedAttrMap) + namedAttrMap->element = 0; + + namedAttrMap = list; + + if (namedAttrMap) { + namedAttrMap->element = this; + unsigned len = namedAttrMap->length(); + for (unsigned i = 0; i < len; i++) + attributeChanged(namedAttrMap->attrs[i]); + // FIXME: What about attributes that were in the old map that are not in the new map? + } +} + +bool Element::hasAttributes() const +{ + updateStyleAttributeIfNeeded(); + return namedAttrMap && namedAttrMap->length() > 0; +} + +String Element::nodeName() const +{ + return m_tagName.toString(); +} + +String Element::nodeNamePreservingCase() const +{ + return m_tagName.toString(); +} + +void Element::setPrefix(const AtomicString &_prefix, ExceptionCode& ec) +{ + ec = 0; + checkSetPrefix(_prefix, ec); + if (ec) + return; + + m_tagName.setPrefix(_prefix); +} + +KURL Element::baseURI() const +{ + KURL base(getAttribute(baseAttr)); + if (!base.protocol().isEmpty()) + return base; + + Node* parent = parentNode(); + if (!parent) + return base; + + KURL parentBase = parent->baseURI(); + if (parentBase.isNull()) + return base; + + return KURL(parentBase, base.string()); +} + +Node* Element::insertAdjacentElement(const String& where, Node* newChild, int& exception) +{ + if (!newChild) { + // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative + exception = TYPE_MISMATCH_ERR; + return 0; + } + + // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd", + // a document fragment is created and the elements appended in the correct order. This document + // fragment isn't returned anywhere. + // + // This is impossible for us to implement as the DOM tree does not allow for such structures, + // Opera also appears to disallow such usage. + + if (equalIgnoringCase(where, "beforeBegin")) { + if (Node* p = parent()) + return p->insertBefore(newChild, this, exception) ? newChild : 0; + } else if (equalIgnoringCase(where, "afterBegin")) { + return insertBefore(newChild, firstChild(), exception) ? newChild : 0; + } else if (equalIgnoringCase(where, "beforeEnd")) { + return appendChild(newChild, exception) ? newChild : 0; + } else if (equalIgnoringCase(where, "afterEnd")) { + if (Node* p = parent()) + return p->insertBefore(newChild, nextSibling(), exception) ? newChild : 0; + } else { + // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative + exception = NOT_SUPPORTED_ERR; + } + return 0; +} + +bool Element::contains(const Node* node) const +{ + if (!node) + return false; + return this == node || node->isDescendantOf(this); +} + +void Element::createAttributeMap() const +{ + namedAttrMap = new NamedAttrMap(const_cast<Element*>(this)); +} + +bool Element::isURLAttribute(Attribute *attr) const +{ + return false; +} + +const QualifiedName& Element::imageSourceAttributeName() const +{ + return srcAttr; +} + +RenderStyle* Element::styleForRenderer(RenderObject* parentRenderer) +{ + return document()->styleSelector()->styleForElement(this); +} + +RenderObject* Element::createRenderer(RenderArena* arena, RenderStyle* style) +{ + if (document()->documentElement() == this && style->display() == NONE) { + // Ignore display: none on root elements. Force a display of block in that case. + RenderBlock* result = new (arena) RenderBlock(this); + if (result) + result->setAnimatableStyle(style); + return result; + } + return RenderObject::createObject(this, style); +} + + +void Element::insertedIntoDocument() +{ + // need to do superclass processing first so inDocument() is true + // by the time we reach updateId + ContainerNode::insertedIntoDocument(); + + if (hasID()) { + NamedAttrMap* attrs = attributes(true); + if (attrs) { + Attribute* idItem = attrs->getAttributeItem(idAttr); + if (idItem && !idItem->isNull()) + updateId(nullAtom, idItem->value()); + } + } +} + +void Element::removedFromDocument() +{ + if (hasID()) { + NamedAttrMap* attrs = attributes(true); + if (attrs) { + Attribute* idItem = attrs->getAttributeItem(idAttr); + if (idItem && !idItem->isNull()) + updateId(idItem->value(), nullAtom); + } + } + + ContainerNode::removedFromDocument(); +} + +void Element::attach() +{ + createRendererIfNeeded(); + ContainerNode::attach(); + if (ElementRareData* rd = rareData()) { + if (rd->m_needsFocusAppearanceUpdateSoonAfterAttach) { + if (isFocusable() && document()->focusedNode() == this) + document()->updateFocusAppearanceSoon(); + rd->m_needsFocusAppearanceUpdateSoonAfterAttach = false; + } + } +} + +void Element::detach() +{ + cancelFocusAppearanceUpdate(); + if (ElementRareData* rd = rareData()) + rd->resetComputedStyle(this); + ContainerNode::detach(); +} + +void Element::recalcStyle(StyleChange change) +{ + RenderStyle* currentStyle = renderStyle(); + bool hasParentStyle = parentNode() ? parentNode()->renderStyle() : false; + bool hasPositionalRules = changed() && currentStyle && currentStyle->childrenAffectedByPositionalRules(); + +#if ENABLE(SVG) + if (!hasParentStyle && isShadowNode() && isSVGElement()) + hasParentStyle = true; +#endif + + if ((change > NoChange || changed())) { + if (ElementRareData* rd = rareData()) + rd->resetComputedStyle(this); + } + if (hasParentStyle && (change >= Inherit || changed())) { + RenderStyle *newStyle = document()->styleSelector()->styleForElement(this); + StyleChange ch = diff(currentStyle, newStyle); + if (ch == Detach) { + if (attached()) + detach(); + // ### Suboptimal. Style gets calculated again. + attach(); + // attach recalulates the style for all children. No need to do it twice. + setChanged(NoStyleChange); + setHasChangedChild(false); + newStyle->deref(document()->renderArena()); + return; + } + + if (currentStyle) { + // Preserve "affected by" bits that were propagated to us from descendants in the case where we didn't do a full + // style change (e.g., only inline style changed). + if (currentStyle->affectedByHoverRules()) + newStyle->setAffectedByHoverRules(true); + if (currentStyle->affectedByActiveRules()) + newStyle->setAffectedByActiveRules(true); + if (currentStyle->affectedByDragRules()) + newStyle->setAffectedByDragRules(true); + if (currentStyle->childrenAffectedByForwardPositionalRules()) + newStyle->setChildrenAffectedByForwardPositionalRules(); + if (currentStyle->childrenAffectedByBackwardPositionalRules()) + newStyle->setChildrenAffectedByBackwardPositionalRules(); + if (currentStyle->childrenAffectedByFirstChildRules()) + newStyle->setChildrenAffectedByFirstChildRules(); + if (currentStyle->childrenAffectedByLastChildRules()) + newStyle->setChildrenAffectedByLastChildRules(); + if (currentStyle->childrenAffectedByDirectAdjacentRules()) + newStyle->setChildrenAffectedByDirectAdjacentRules(); + } + + if (ch != NoChange) + setRenderStyle(newStyle); + else if (changed() && (document()->usesSiblingRules() || document()->usesDescendantRules())) { + // Although no change occurred, we use the new style so that the cousin style sharing code won't get + // fooled into believing this style is the same. This is only necessary if the document actually uses + // sibling/descendant rules, since otherwise it isn't possible for ancestor styles to affect sharing of + // descendants. + if (renderer()) + renderer()->setStyleInternal(newStyle); + else + setRenderStyle(newStyle); + } + + newStyle->deref(document()->renderArena()); + + if (change != Force) { + if ((document()->usesDescendantRules() || hasPositionalRules) && styleChangeType() == FullStyleChange) + change = Force; + else + change = ch; + } + } + + for (Node *n = firstChild(); n; n = n->nextSibling()) { + if (change >= Inherit || n->isTextNode() || n->hasChangedChild() || n->changed()) + n->recalcStyle(change); + } + + setChanged(NoStyleChange); + setHasChangedChild(false); +} + +bool Element::childTypeAllowed(NodeType type) +{ + switch (type) { + case ELEMENT_NODE: + case TEXT_NODE: + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + case CDATA_SECTION_NODE: + case ENTITY_REFERENCE_NODE: + return true; + break; + default: + return false; + } +} + +static void checkForSiblingStyleChanges(Element* e, RenderStyle* style, bool finishedParsingCallback, + Node* beforeChange, Node* afterChange, int childCountDelta) +{ + if (!style || (e->changed() && style->childrenAffectedByPositionalRules())) + return; + + // :first-child. In the parser callback case, we don't have to check anything, since we were right the first time. + // In the DOM case, we only need to do something if |afterChange| is not 0. + // |afterChange| is 0 in the parser case, so it works out that we'll skip this block. + if (style->childrenAffectedByFirstChildRules() && afterChange) { + // Find our new first child. + Node* newFirstChild = 0; + for (newFirstChild = e->firstChild(); newFirstChild && !newFirstChild->isElementNode(); newFirstChild = newFirstChild->nextSibling()) {}; + + // Find the first element node following |afterChange| + Node* firstElementAfterInsertion = 0; + for (firstElementAfterInsertion = afterChange; + firstElementAfterInsertion && !firstElementAfterInsertion->isElementNode(); + firstElementAfterInsertion = firstElementAfterInsertion->nextSibling()) {}; + + // This is the insert/append case. + if (newFirstChild != firstElementAfterInsertion && firstElementAfterInsertion && firstElementAfterInsertion->attached() && + firstElementAfterInsertion->renderStyle() && firstElementAfterInsertion->renderStyle()->firstChildState()) + firstElementAfterInsertion->setChanged(); + + // We also have to handle node removal. + if (childCountDelta < 0 && newFirstChild == firstElementAfterInsertion && newFirstChild && newFirstChild->renderStyle() && !newFirstChild->renderStyle()->firstChildState()) + newFirstChild->setChanged(); + } + + // :last-child. In the parser callback case, we don't have to check anything, since we were right the first time. + // In the DOM case, we only need to do something if |afterChange| is not 0. + if (style->childrenAffectedByLastChildRules() && beforeChange) { + // Find our new last child. + Node* newLastChild = 0; + for (newLastChild = e->lastChild(); newLastChild && !newLastChild->isElementNode(); newLastChild = newLastChild->previousSibling()) {}; + + // Find the last element node going backwards from |beforeChange| + Node* lastElementBeforeInsertion = 0; + for (lastElementBeforeInsertion = beforeChange; + lastElementBeforeInsertion && !lastElementBeforeInsertion->isElementNode(); + lastElementBeforeInsertion = lastElementBeforeInsertion->previousSibling()) {}; + + if (newLastChild != lastElementBeforeInsertion && lastElementBeforeInsertion && lastElementBeforeInsertion->attached() && + lastElementBeforeInsertion->renderStyle() && lastElementBeforeInsertion->renderStyle()->lastChildState()) + lastElementBeforeInsertion->setChanged(); + + // We also have to handle node removal. The parser callback case is similar to node removal as well in that we need to change the last child + // to match now. + if ((childCountDelta < 0 || finishedParsingCallback) && newLastChild == lastElementBeforeInsertion && newLastChild && newLastChild->renderStyle() && !newLastChild->renderStyle()->lastChildState()) + newLastChild->setChanged(); + } + + // The + selector. We need to invalidate the first element following the insertion point. It is the only possible element + // that could be affected by this DOM change. + if (style->childrenAffectedByDirectAdjacentRules() && afterChange) { + Node* firstElementAfterInsertion = 0; + for (firstElementAfterInsertion = afterChange; + firstElementAfterInsertion && !firstElementAfterInsertion->isElementNode(); + firstElementAfterInsertion = firstElementAfterInsertion->nextSibling()) {}; + if (firstElementAfterInsertion && firstElementAfterInsertion->attached()) + firstElementAfterInsertion->setChanged(); + } + + // Forward positional selectors include the ~ selector, nth-child, nth-of-type, first-of-type and only-of-type. + // Backward positional selectors include nth-last-child, nth-last-of-type, last-of-type and only-of-type. + // We have to invalidate everything following the insertion point in the forward case, and everything before the insertion point in the + // backward case. + // |afterChange| is 0 in the parser callback case, so we won't do any work for the forward case if we don't have to. + // For performance reasons we just mark the parent node as changed, since we don't want to make childrenChanged O(n^2) by crawling all our kids + // here. recalcStyle will then force a walk of the children when it sees that this has happened. + if ((style->childrenAffectedByForwardPositionalRules() && afterChange) || + (style->childrenAffectedByBackwardPositionalRules() && beforeChange)) + e->setChanged(); + + // :empty selector. + if (style->affectedByEmpty() && (!style->emptyState() || e->hasChildNodes())) + e->setChanged(); +} + +void Element::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + if (!changedByParser) + checkForSiblingStyleChanges(this, renderStyle(), false, beforeChange, afterChange, childCountDelta); +} + +void Element::finishParsingChildren() +{ + ContainerNode::finishParsingChildren(); + m_parsingChildrenFinished = true; + checkForSiblingStyleChanges(this, renderStyle(), true, lastChild(), 0, 0); +} + +void Element::dispatchAttrRemovalEvent(Attribute*) +{ + ASSERT(!eventDispatchForbidden()); +#if 0 + if (!document()->hasListenerType(Document::DOMATTRMODIFIED_LISTENER)) + return; + ExceptionCode ec = 0; + dispatchEvent(new MutationEvent(DOMAttrModifiedEvent, true, false, attr, attr->value(), + attr->value(), document()->attrName(attr->id()), MutationEvent::REMOVAL), ec); +#endif +} + +void Element::dispatchAttrAdditionEvent(Attribute *attr) +{ + ASSERT(!eventDispatchForbidden()); +#if 0 + if (!document()->hasListenerType(Document::DOMATTRMODIFIED_LISTENER)) + return; + ExceptionCode ec = 0; + dispatchEvent(new MutationEvent(DOMAttrModifiedEvent, true, false, attr, attr->value(), + attr->value(),document()->attrName(attr->id()), MutationEvent::ADDITION), ec); +#endif +} + +String Element::openTagStartToString() const +{ + String result = "<" + nodeName(); + + NamedAttrMap *attrMap = attributes(true); + + if (attrMap) { + unsigned numAttrs = attrMap->length(); + for (unsigned i = 0; i < numAttrs; i++) { + result += " "; + + Attribute *attribute = attrMap->attributeItem(i); + result += attribute->name().toString(); + if (!attribute->value().isNull()) { + result += "=\""; + // FIXME: substitute entities for any instances of " or ' + result += attribute->value(); + result += "\""; + } + } + } + + return result; +} + +String Element::toString() const +{ + String result = openTagStartToString(); + + if (hasChildNodes()) { + result += ">"; + + for (Node *child = firstChild(); child != NULL; child = child->nextSibling()) { + result += child->toString(); + } + + result += "</"; + result += nodeName(); + result += ">"; + } else { + result += " />"; + } + + return result; +} + +void Element::updateId(const AtomicString& oldId, const AtomicString& newId) +{ + if (!inDocument()) + return; + + if (oldId == newId) + return; + + Document* doc = document(); + if (!oldId.isEmpty()) + doc->removeElementById(oldId, this); + if (!newId.isEmpty()) + doc->addElementById(newId, this); +} + +#ifndef NDEBUG +void Element::formatForDebugger(char* buffer, unsigned length) const +{ + String result; + String s; + + s = nodeName(); + if (s.length() > 0) { + result += s; + } + + s = getAttribute(idAttr); + if (s.length() > 0) { + if (result.length() > 0) + result += "; "; + result += "id="; + result += s; + } + + s = getAttribute(classAttr); + if (s.length() > 0) { + if (result.length() > 0) + result += "; "; + result += "class="; + result += s; + } + + strncpy(buffer, result.utf8().data(), length - 1); +} +#endif + +PassRefPtr<Attr> Element::setAttributeNode(Attr *attr, ExceptionCode& ec) +{ + ASSERT(attr); + return static_pointer_cast<Attr>(attributes(false)->setNamedItem(attr, ec)); +} + +PassRefPtr<Attr> Element::setAttributeNodeNS(Attr* attr, ExceptionCode& ec) +{ + ASSERT(attr); + return static_pointer_cast<Attr>(attributes(false)->setNamedItem(attr, ec)); +} + +PassRefPtr<Attr> Element::removeAttributeNode(Attr *attr, ExceptionCode& ec) +{ + if (!attr || attr->ownerElement() != this) { + ec = NOT_FOUND_ERR; + return 0; + } + if (document() != attr->document()) { + ec = WRONG_DOCUMENT_ERR; + return 0; + } + + NamedAttrMap *attrs = attributes(true); + if (!attrs) + return 0; + + return static_pointer_cast<Attr>(attrs->removeNamedItem(attr->qualifiedName(), ec)); +} + +void Element::setAttributeNS(const String& namespaceURI, const String& qualifiedName, const String& value, ExceptionCode& ec) +{ + String prefix, localName; + if (!Document::parseQualifiedName(qualifiedName, prefix, localName)) { + ec = INVALID_CHARACTER_ERR; + return; + } + setAttribute(QualifiedName(prefix, localName, namespaceURI), value.impl(), ec); +} + +void Element::removeAttribute(const String& name, ExceptionCode& ec) +{ + String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; + + if (namedAttrMap) { + namedAttrMap->removeNamedItem(localName, ec); + if (ec == NOT_FOUND_ERR) + ec = 0; + } +} + +void Element::removeAttributeNS(const String& namespaceURI, const String& localName, ExceptionCode& ec) +{ + removeAttribute(QualifiedName(nullAtom, localName, namespaceURI), ec); +} + +PassRefPtr<Attr> Element::getAttributeNode(const String& name) +{ + NamedAttrMap* attrs = attributes(true); + if (!attrs) + return 0; + String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; + return static_pointer_cast<Attr>(attrs->getNamedItem(localName)); +} + +PassRefPtr<Attr> Element::getAttributeNodeNS(const String& namespaceURI, const String& localName) +{ + NamedAttrMap* attrs = attributes(true); + if (!attrs) + return 0; + return static_pointer_cast<Attr>(attrs->getNamedItem(QualifiedName(nullAtom, localName, namespaceURI))); +} + +bool Element::hasAttribute(const String& name) const +{ + NamedAttrMap* attrs = attributes(true); + if (!attrs) + return false; + String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; + return attrs->getAttributeItem(localName); +} + +bool Element::hasAttributeNS(const String& namespaceURI, const String& localName) const +{ + NamedAttrMap* attrs = attributes(true); + if (!attrs) + return false; + return attrs->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI)); +} + +CSSStyleDeclaration *Element::style() +{ + return 0; +} + +void Element::focus(bool restorePreviousSelection) +{ + Document* doc = document(); + if (doc->focusedNode() == this) + return; + + doc->updateLayoutIgnorePendingStylesheets(); + + if (!supportsFocus()) + return; + + if (Page* page = doc->page()) + page->focusController()->setFocusedNode(this, doc->frame()); + + if (!isFocusable()) { + createRareData()->m_needsFocusAppearanceUpdateSoonAfterAttach = true; + return; + } + + cancelFocusAppearanceUpdate(); + updateFocusAppearance(restorePreviousSelection); +} + +void Element::updateFocusAppearance(bool restorePreviousSelection) +{ + if (this == rootEditableElement()) { + Frame* frame = document()->frame(); + if (!frame) + return; + + // FIXME: We should restore the previous selection if there is one. + Selection newSelection = hasTagName(htmlTag) || hasTagName(bodyTag) ? Selection(Position(this, 0), DOWNSTREAM) : Selection::selectionFromContentsOfNode(this); + + if (frame->shouldChangeSelection(newSelection)) { + frame->selectionController()->setSelection(newSelection); + frame->revealSelection(); + } + } else if (renderer() && !renderer()->isWidget()) + renderer()->enclosingLayer()->scrollRectToVisible(getRect()); +} + +void Element::blur() +{ + cancelFocusAppearanceUpdate(); + Document* doc = document(); + if (doc->focusedNode() == this) { + if (doc->frame()) + doc->frame()->page()->focusController()->setFocusedNode(0, doc->frame()); + else + doc->setFocusedNode(0); + } +} + +String Element::innerText() const +{ + // We need to update layout, since plainText uses line boxes in the render tree. + document()->updateLayoutIgnorePendingStylesheets(); + + if (!renderer()) + return textContent(true); + + return plainText(rangeOfContents(const_cast<Element*>(this)).get()); +} + +String Element::outerText() const +{ + // Getting outerText is the same as getting innerText, only + // setting is different. You would think this should get the plain + // text for the outer range, but this is wrong, <br> for instance + // would return different values for inner and outer text by such + // a rule, but it doesn't in WinIE, and we want to match that. + return innerText(); +} + +String Element::title() const +{ + return String(); +} + +IntSize Element::minimumSizeForResizing() const +{ + const ElementRareData* rd = rareData(); + return rd ? rd->m_minimumSizeForResizing : defaultMinimumSizeForResizing(); +} + +void Element::setMinimumSizeForResizing(const IntSize& size) +{ + if (size == defaultMinimumSizeForResizing() && !rareData()) + return; + createRareData()->m_minimumSizeForResizing = size; +} + +RenderStyle* Element::computedStyle() +{ + if (RenderStyle* usedStyle = renderStyle()) + return usedStyle; + + if (!attached()) + // FIXME: Try to do better than this. Ensure that styleForElement() works for elements that are not in the + // document tree and figure out when to destroy the computed style for such elements. + return 0; + + ElementRareData* rd = createRareData(); + if (!rd->m_computedStyle) + rd->m_computedStyle = document()->styleSelector()->styleForElement(this, parent() ? parent()->computedStyle() : 0); + return rd->m_computedStyle; +} + +void Element::cancelFocusAppearanceUpdate() +{ + if (ElementRareData* rd = rareData()) + rd->m_needsFocusAppearanceUpdateSoonAfterAttach = false; + if (document()->focusedNode() == this) + document()->cancelFocusAppearanceUpdate(); +} + +bool Element::virtualHasTagName(const QualifiedName& name) const +{ + return hasTagName(name); +} + +} diff --git a/WebCore/dom/Element.h b/WebCore/dom/Element.h new file mode 100644 index 0000000..33727cd --- /dev/null +++ b/WebCore/dom/Element.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef Element_h +#define Element_h + +#include "ContainerNode.h" +#include "QualifiedName.h" +#include "ScrollTypes.h" + +namespace WebCore { + +class Attr; +class Attribute; +class CSSStyleDeclaration; +class ClassNames; +class ElementRareData; +class IntSize; + +class Element : public ContainerNode { +public: + Element(const QualifiedName&, Document*); + ~Element(); + + // Used to quickly determine whether or not an element has a given CSS class. + virtual const ClassNames* getClassNames() const; + const AtomicString& getIDAttribute() const; + bool hasAttribute(const QualifiedName&) const; + const AtomicString& getAttribute(const QualifiedName&) const; + void setAttribute(const QualifiedName&, StringImpl* value, ExceptionCode&); + void removeAttribute(const QualifiedName&, ExceptionCode&); + + bool hasAttributes() const; + + bool hasAttribute(const String& name) const; + bool hasAttributeNS(const String& namespaceURI, const String& localName) const; + + const AtomicString& getAttribute(const String& name) const; + const AtomicString& getAttributeNS(const String& namespaceURI, const String& localName) const; + + void setAttribute(const String& name, const String& value, ExceptionCode&); + void setAttributeNS(const String& namespaceURI, const String& qualifiedName, const String& value, ExceptionCode&); + + void scrollIntoView (bool alignToTop = true); + void scrollIntoViewIfNeeded(bool centerIfNeeded = true); + + void scrollByUnits(int units, ScrollGranularity); + void scrollByLines(int lines); + void scrollByPages(int pages); + + int offsetLeft(); + int offsetTop(); + int offsetWidth(); + int offsetHeight(); + Element* offsetParent(); + int clientLeft(); + int clientTop(); + int clientWidth(); + int clientHeight(); + int scrollLeft(); + int scrollTop(); + void setScrollLeft(int); + void setScrollTop(int); + int scrollWidth(); + int scrollHeight(); + + void removeAttribute(const String& name, ExceptionCode&); + void removeAttributeNS(const String& namespaceURI, const String& localName, ExceptionCode&); + + PassRefPtr<Attr> getAttributeNode(const String& name); + PassRefPtr<Attr> getAttributeNodeNS(const String& namespaceURI, const String& localName); + PassRefPtr<Attr> setAttributeNode(Attr*, ExceptionCode&); + PassRefPtr<Attr> setAttributeNodeNS(Attr*, ExceptionCode&); + PassRefPtr<Attr> removeAttributeNode(Attr*, ExceptionCode&); + + virtual CSSStyleDeclaration* style(); + + const QualifiedName& tagQName() const { return m_tagName; } + String tagName() const { return nodeName(); } + bool hasTagName(const QualifiedName& tagName) const { return m_tagName.matches(tagName); } + + // A fast function for checking the local name against another atomic string. + bool hasLocalName(const AtomicString& other) const { return m_tagName.localName() == other; } + bool hasLocalName(const QualifiedName& other) const { return m_tagName.localName() == other.localName(); } + + virtual const AtomicString& localName() const { return m_tagName.localName(); } + virtual const AtomicString& prefix() const { return m_tagName.prefix(); } + virtual void setPrefix(const AtomicString &_prefix, ExceptionCode&); + virtual const AtomicString& namespaceURI() const { return m_tagName.namespaceURI(); } + + virtual KURL baseURI() const; + + // DOM methods overridden from parent classes + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual String nodeName() const; + virtual bool isElementNode() const { return true; } + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + virtual bool isInputTypeHidden() const { return false; } + + String nodeNamePreservingCase() const; + + // convenience methods which ignore exceptions + void setAttribute(const QualifiedName&, const String& value); + void setBooleanAttribute(const QualifiedName& name, bool); + + virtual NamedAttrMap* attributes() const; + NamedAttrMap* attributes(bool readonly) const; + + // This method is called whenever an attribute is added, changed or removed. + virtual void attributeChanged(Attribute*, bool preserveDecls = false) {} + + // not part of the DOM + void setAttributeMap(NamedAttrMap*); + + virtual void copyNonAttributeProperties(const Element* source) {} + + virtual void attach(); + virtual void detach(); + virtual RenderStyle* styleForRenderer(RenderObject* parent); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void recalcStyle(StyleChange = NoChange); + + virtual RenderStyle* computedStyle(); + + virtual bool childTypeAllowed(NodeType); + + virtual Attribute* createAttribute(const QualifiedName& name, StringImpl* value); + + void dispatchAttrRemovalEvent(Attribute*); + void dispatchAttrAdditionEvent(Attribute*); + + virtual void accessKeyAction(bool sendToAnyEvent) { } + + virtual String toString() const; + + virtual bool isURLAttribute(Attribute*) const; + virtual const QualifiedName& imageSourceAttributeName() const; + virtual String target() const { return String(); } + + virtual void focus(bool restorePreviousSelection = true); + virtual void updateFocusAppearance(bool restorePreviousSelection); + void blur(); + +#ifndef NDEBUG + virtual void formatForDebugger(char* buffer, unsigned length) const; +#endif + + Node* insertAdjacentElement(const String& where, Node* newChild, ExceptionCode&); + bool contains(const Node*) const; + + String innerText() const; + String outerText() const; + + virtual String title() const; + + String openTagStartToString() const; + + void updateId(const AtomicString& oldId, const AtomicString& newId); + + IntSize minimumSizeForResizing() const; + void setMinimumSizeForResizing(const IntSize&); + + // Use Document::registerForPageCacheCallbacks() to subscribe these + virtual void willSaveToCache() { } + virtual void didRestoreFromCache() { } + + bool isFinishedParsingChildren() const { return m_parsingChildrenFinished; } + virtual void finishParsingChildren(); + virtual void beginParsingChildren() { m_parsingChildrenFinished = false; } + +private: + ElementRareData* rareData(); + const ElementRareData* rareData() const; + ElementRareData* createRareData(); + + virtual void createAttributeMap() const; + + virtual void updateStyleAttributeIfNeeded() const {} + + void updateFocusAppearanceSoonAfterAttach(); + void cancelFocusAppearanceUpdate(); + + virtual bool virtualHasTagName(const QualifiedName&) const; + +private: + QualifiedName m_tagName; + +protected: + mutable RefPtr<NamedAttrMap> namedAttrMap; + + // These two bits are really used by the StyledElement subclass, but they are pulled up here in order to be shared with other + // Element bits. + mutable bool m_isStyleAttributeValid : 1; + mutable bool m_synchronizingStyleAttribute : 1; + +private: + bool m_parsingChildrenFinished : 1; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/Element.idl b/WebCore/dom/Element.idl new file mode 100644 index 0000000..31ff128 --- /dev/null +++ b/WebCore/dom/Element.idl @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module core { + + interface [ + GenerateConstructor, + GenerateNativeConverter, + InterfaceUUID=FEFE9C21-E58C-4b5b-821A-61A514613763, + ImplementationUUID=12E5B08E-A680-4baf-9D1E-108AEF7ABBFB + ] Element : EventTargetNode { + + // DOM Level 1 Core + + readonly attribute [ConvertNullStringTo=Null] DOMString tagName; + + [ConvertNullStringTo=Null] DOMString getAttribute(in DOMString name); + [OldStyleObjC, Custom] void setAttribute(in DOMString name, + in DOMString value) + raises(DOMException); + void removeAttribute(in DOMString name) + raises(DOMException); + Attr getAttributeNode(in DOMString name); + [Custom] Attr setAttributeNode(in Attr newAttr) + raises(DOMException); + Attr removeAttributeNode(in Attr oldAttr) + raises(DOMException); + NodeList getElementsByTagName(in DOMString name); + + // DOM Level 2 Core + + [OldStyleObjC] DOMString getAttributeNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString localName); + [OldStyleObjC, Custom] void setAttributeNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString qualifiedName, + in DOMString value) + raises(DOMException); + [OldStyleObjC] void removeAttributeNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString localName) + raises(DOMException); + [OldStyleObjC] NodeList getElementsByTagNameNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString localName); + [OldStyleObjC] Attr getAttributeNodeNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString localName); + [Custom] Attr setAttributeNodeNS(in Attr newAttr) + raises(DOMException); + boolean hasAttribute(in DOMString name); + [OldStyleObjC] boolean hasAttributeNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString localName); + +#if !defined(LANGUAGE_COM) + readonly attribute CSSStyleDeclaration style; +#endif + + // Common extensions + + readonly attribute long offsetLeft; + readonly attribute long offsetTop; + readonly attribute long offsetWidth; + readonly attribute long offsetHeight; + readonly attribute Element offsetParent; + readonly attribute long clientLeft; + readonly attribute long clientTop; + readonly attribute long clientWidth; + readonly attribute long clientHeight; + attribute long scrollLeft; + attribute long scrollTop; + readonly attribute long scrollWidth; + readonly attribute long scrollHeight; + + void focus(); + void blur(); + void scrollIntoView(in [Optional] boolean alignWithTop); + + // IE extensions + + Node insertAdjacentElement(in DOMString position, + in Node element) + raises(DOMException); + boolean contains(in Element element); + + // WebKit extensions + + void scrollIntoViewIfNeeded(in [Optional] boolean centerIfNeeded); + void scrollByLines(in long lines); + void scrollByPages(in long pages); + + // HTML 5 + NodeList getElementsByClassName(in DOMString name); + + // ElementSelector - Selector API + Element querySelector(in [ConvertUndefinedOrNullToNullString] DOMString selectors) + raises(DOMException); + NodeList querySelectorAll(in [ConvertUndefinedOrNullToNullString] DOMString selectors) + raises(DOMException); + +#if defined(LANGUAGE_OBJECTIVE_C) + // Objective-C extensions + readonly attribute DOMString innerText; +#endif + + }; + +} diff --git a/WebCore/dom/Entity.cpp b/WebCore/dom/Entity.cpp new file mode 100644 index 0000000..8e0db94 --- /dev/null +++ b/WebCore/dom/Entity.cpp @@ -0,0 +1,110 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "Entity.h" + +namespace WebCore { + +Entity::Entity(Document* doc) + : ContainerNode(doc) +{ +} + +Entity::Entity(Document* doc, const String& name) + : ContainerNode(doc) + , m_name(name) +{ +} + +Entity::Entity(Document* doc, const String& publicId, const String& systemId, const String& notationName) + : ContainerNode(doc) + , m_publicId(publicId) + , m_systemId(systemId) + , m_notationName(notationName) +{ +} + +String Entity::nodeName() const +{ + return m_name; +} + +Node::NodeType Entity::nodeType() const +{ + return ENTITY_NODE; +} + +PassRefPtr<Node> Entity::cloneNode(bool /*deep*/) +{ + // Spec says cloning Entity nodes is "implementation dependent". We do not support it. + return 0; +} + +// DOM Section 1.1.1 +bool Entity::childTypeAllowed(NodeType type) +{ + switch (type) { + case ELEMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + case COMMENT_NODE: + case TEXT_NODE: + case CDATA_SECTION_NODE: + case ENTITY_REFERENCE_NODE: + return true; + break; + default: + return false; + } +} + +String Entity::toString() const +{ + String result = "<!ENTITY' "; + + if (!m_name.isEmpty()) { + result += " "; + result += m_name; + } + + if (!m_publicId.isEmpty()) { + result += " PUBLIC \""; + result += m_publicId; + result += "\" \""; + result += m_systemId; + result += "\""; + } else if (!m_systemId.isEmpty()) { + result += " SYSTEM \""; + result += m_systemId; + result += "\""; + } + + if (!m_notationName.isEmpty()) { + result += " NDATA "; + result += m_notationName; + } + + result += ">"; + + return result; +} + +} // namespace diff --git a/WebCore/dom/Entity.h b/WebCore/dom/Entity.h new file mode 100644 index 0000000..5c4297b --- /dev/null +++ b/WebCore/dom/Entity.h @@ -0,0 +1,58 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef Entity_h +#define Entity_h + +#include "ContainerNode.h" + +namespace WebCore { + +class Entity : public ContainerNode +{ +public: + Entity(Document*); + Entity(Document*, const String& name); + Entity(Document*, const String& publicId, const String& systemId, const String& notationName); + + // DOM methods & attributes for Entity + String publicId() const { return m_publicId; } + String systemId() const { return m_systemId; } + String notationName() const { return m_notationName; } + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); + virtual String toString() const; + +private: + String m_publicId; + String m_systemId; + String m_notationName; + String m_name; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/Entity.idl b/WebCore/dom/Entity.idl new file mode 100644 index 0000000..b154797 --- /dev/null +++ b/WebCore/dom/Entity.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=5CDB5ACA-F3A7-47ea-B89C-F335E4342C55, + ImplementationUUID=DDD2A621-59FD-4bb2-9F95-7061C3FB9F06 + ] Entity : Node { + readonly attribute [ConvertNullStringTo=Null] DOMString publicId; + readonly attribute [ConvertNullStringTo=Null] DOMString systemId; + readonly attribute [ConvertNullStringTo=Null] DOMString notationName; + }; + +} diff --git a/WebCore/dom/EntityReference.cpp b/WebCore/dom/EntityReference.cpp new file mode 100644 index 0000000..8f57519 --- /dev/null +++ b/WebCore/dom/EntityReference.cpp @@ -0,0 +1,84 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "EntityReference.h" + +namespace WebCore { + +EntityReference::EntityReference(Document* doc) + : ContainerNode(doc) +{ +} + +EntityReference::EntityReference(Document* doc, const String& entityName) + : ContainerNode(doc) + , m_entityName(entityName) +{ +} + +String EntityReference::nodeName() const +{ + return m_entityName; +} + +Node::NodeType EntityReference::nodeType() const +{ + return ENTITY_REFERENCE_NODE; +} + +PassRefPtr<Node> EntityReference::cloneNode(bool deep) +{ + RefPtr<EntityReference> clone = new EntityReference(document(), m_entityName); + // ### make sure children are readonly + // ### since we are a reference, should we clone children anyway (even if not deep?) + if (deep) + cloneChildNodes(clone.get()); + return clone.release(); +} + +// DOM Section 1.1.1 +bool EntityReference::childTypeAllowed(NodeType type) +{ + switch (type) { + case ELEMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + case COMMENT_NODE: + case TEXT_NODE: + case CDATA_SECTION_NODE: + case ENTITY_REFERENCE_NODE: + return true; + break; + default: + return false; + } +} + +String EntityReference::toString() const +{ + String result = "&"; + result += m_entityName; + result += ";"; + + return result; +} + +} // namespace diff --git a/WebCore/dom/EntityReference.h b/WebCore/dom/EntityReference.h new file mode 100644 index 0000000..0536e6e --- /dev/null +++ b/WebCore/dom/EntityReference.h @@ -0,0 +1,49 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef EntityReference_h +#define EntityReference_h + +#include "ContainerNode.h" + +namespace WebCore { + +class EntityReference : public ContainerNode +{ +public: + EntityReference(Document*); + EntityReference(Document*, const String& entityName); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); + virtual String toString() const; + +private: + String m_entityName; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/EntityReference.idl b/WebCore/dom/EntityReference.idl new file mode 100644 index 0000000..8a206e9 --- /dev/null +++ b/WebCore/dom/EntityReference.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=61BF4A03-19FB-4ac4-A624-5BF0893FDA65, + ImplementationUUID=486E1182-CF4F-450b-B411-A584CA42BBD0 + ] EntityReference : Node { + }; + +} diff --git a/WebCore/dom/Event.cpp b/WebCore/dom/Event.cpp new file mode 100644 index 0000000..543dfff --- /dev/null +++ b/WebCore/dom/Event.cpp @@ -0,0 +1,174 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "Event.h" + +#include "AtomicString.h" +#include "SystemTime.h" + +namespace WebCore { + +Event::Event() + : RefCounted<Event>(0) + , m_canBubble(false) + , m_cancelable(false) + , m_propagationStopped(false) + , m_defaultPrevented(false) + , m_defaultHandled(false) + , m_cancelBubble(false) + , m_currentTarget(0) + , m_eventPhase(0) + , m_createTime(static_cast<DOMTimeStamp>(currentTime() * 1000.0)) +{ +} + +Event::Event(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg) + : RefCounted<Event>(0) + , m_type(eventType) + , m_canBubble(canBubbleArg) + , m_cancelable(cancelableArg) + , m_propagationStopped(false) + , m_defaultPrevented(false) + , m_defaultHandled(false) + , m_cancelBubble(false) + , m_currentTarget(0) + , m_eventPhase(0) + , m_createTime(static_cast<DOMTimeStamp>(currentTime() * 1000.0)) +{ +} + +Event::~Event() +{ +} + +void Event::initEvent(const AtomicString& eventTypeArg, bool canBubbleArg, bool cancelableArg) +{ + if (dispatched()) + return; + + m_type = eventTypeArg; + m_canBubble = canBubbleArg; + m_cancelable = cancelableArg; +} + +bool Event::isUIEvent() const +{ + return false; +} + +bool Event::isMouseEvent() const +{ + return false; +} + +bool Event::isMutationEvent() const +{ + return false; +} + +bool Event::isKeyboardEvent() const +{ + return false; +} + +bool Event::isTextEvent() const +{ + return false; +} + +bool Event::isDragEvent() const +{ + return false; +} + +bool Event::isClipboardEvent() const +{ + return false; +} + +bool Event::isWheelEvent() const +{ + return false; +} + +#if ENABLE(CROSS_DOCUMENT_MESSAGING) +bool Event::isMessageEvent() const +{ + return false; +} +#endif + +bool Event::isBeforeTextInsertedEvent() const +{ + return false; +} + +bool Event::isOverflowEvent() const +{ + return false; +} + +bool Event::isProgressEvent() const +{ + return false; +} + +#if ENABLE(SVG) +bool Event::isSVGZoomEvent() const +{ + return false; +} +#endif + + +bool Event::storesResultAsString() const +{ + return false; +} + +void Event::storeResult(const String&) +{ +} + +void Event::setTarget(PassRefPtr<EventTarget> target) +{ + m_target = target; + if (m_target) + receivedTarget(); +} + +void Event::receivedTarget() +{ +} + +void Event::setUnderlyingEvent(PassRefPtr<Event> ue) +{ + // Prohibit creation of a cycle -- just do nothing in that case. + for (Event* e = ue.get(); e; e = e->underlyingEvent()) + if (e == this) + return; + m_underlyingEvent = ue; +} + +} // namespace WebCore diff --git a/WebCore/dom/Event.h b/WebCore/dom/Event.h new file mode 100644 index 0000000..b74bc16 --- /dev/null +++ b/WebCore/dom/Event.h @@ -0,0 +1,159 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef Event_h +#define Event_h + +#include "AtomicString.h" +#include "EventTarget.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + + class Clipboard; + + // FIXME: this should probably defined elsewhere. + typedef unsigned long long DOMTimeStamp; + + class Event : public RefCounted<Event> { + public: + enum PhaseType { + CAPTURING_PHASE = 1, + AT_TARGET = 2, + BUBBLING_PHASE = 3 + }; + + enum EventType { + MOUSEDOWN = 1, + MOUSEUP = 2, + MOUSEOVER = 4, + MOUSEOUT = 8, + MOUSEMOVE = 16, + MOUSEDRAG = 32, + CLICK = 64, + DBLCLICK = 128, + KEYDOWN = 256, + KEYUP = 512, + KEYPRESS = 1024, + DRAGDROP = 2048, + FOCUS = 4096, + BLUR = 8192, + SELECT = 16384, + CHANGE = 32768 + }; + + Event(); + Event(const AtomicString& type, bool canBubble, bool cancelable); + virtual ~Event(); + + void initEvent(const AtomicString& type, bool canBubble, bool cancelable); + + const AtomicString& type() const { return m_type; } + + EventTarget* target() const { return m_target.get(); } + void setTarget(PassRefPtr<EventTarget>); + + EventTarget* currentTarget() const { return m_currentTarget; } + void setCurrentTarget(EventTarget* currentTarget) { m_currentTarget = currentTarget; } + + unsigned short eventPhase() const { return m_eventPhase; } + void setEventPhase(unsigned short eventPhase) { m_eventPhase = eventPhase; } + + bool bubbles() const { return m_canBubble; } + bool cancelable() const { return m_cancelable; } + DOMTimeStamp timeStamp() { return m_createTime; } + void stopPropagation() { m_propagationStopped = true; } + + // IE Extensions + EventTarget* srcElement() const { return target(); } // MSIE extension - "the object that fired the event" + + bool returnValue() const { return !defaultPrevented(); } + void setReturnValue(bool returnValue) { setDefaultPrevented(!returnValue); } + + Clipboard* clipboardData() const { return isClipboardEvent() ? clipboard() : 0; } + + virtual bool isUIEvent() const; + virtual bool isMouseEvent() const; + virtual bool isMutationEvent() const; + virtual bool isKeyboardEvent() const; + virtual bool isTextEvent() const; + virtual bool isDragEvent() const; // a subset of mouse events + virtual bool isClipboardEvent() const; +#if ENABLE(CROSS_DOCUMENT_MESSAGING) + virtual bool isMessageEvent() const; +#endif + virtual bool isWheelEvent() const; + virtual bool isBeforeTextInsertedEvent() const; + virtual bool isOverflowEvent() const; + virtual bool isProgressEvent() const; +#if ENABLE(SVG) + virtual bool isSVGZoomEvent() const; +#endif + + bool propagationStopped() const { return m_propagationStopped; } + + bool defaultPrevented() const { return m_defaultPrevented; } + void preventDefault() { if (m_cancelable) m_defaultPrevented = true; } + void setDefaultPrevented(bool defaultPrevented) { m_defaultPrevented = defaultPrevented; } + + bool defaultHandled() const { return m_defaultHandled; } + void setDefaultHandled() { m_defaultHandled = true; } + + bool cancelBubble() const { return m_cancelBubble; } + void setCancelBubble(bool cancel) { m_cancelBubble = cancel; } + + Event* underlyingEvent() const { return m_underlyingEvent.get(); } + void setUnderlyingEvent(PassRefPtr<Event>); + + virtual bool storesResultAsString() const; + virtual void storeResult(const String&); + + virtual Clipboard* clipboard() const { return 0; } + + protected: + virtual void receivedTarget(); + bool dispatched() const { return m_target; } + + private: + AtomicString m_type; + bool m_canBubble; + bool m_cancelable; + + bool m_propagationStopped; + bool m_defaultPrevented; + bool m_defaultHandled; + bool m_cancelBubble; + + EventTarget* m_currentTarget; + unsigned short m_eventPhase; + RefPtr<EventTarget> m_target; + DOMTimeStamp m_createTime; + + RefPtr<Event> m_underlyingEvent; + }; + +} // namespace WebCore + +#endif // Event_h diff --git a/WebCore/dom/Event.idl b/WebCore/dom/Event.idl new file mode 100644 index 0000000..ae394b9 --- /dev/null +++ b/WebCore/dom/Event.idl @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module events { + + // Introduced in DOM Level 2: + interface [ + GenerateConstructor, + ObjCCustomInternalImpl, + InterfaceUUID=D17495FA-ACAD-4d27-9362-E19E057B189D, + ImplementationUUID=CFDCDDB2-5B3F-412d-BDA4-80B23C721549 + ] Event { + + // DOM PhaseType + const unsigned short CAPTURING_PHASE = 1; + const unsigned short AT_TARGET = 2; + const unsigned short BUBBLING_PHASE = 3; + +#if !defined(LANGUAGE_OBJECTIVE_C) + // Reverse-engineered from Netscape + const unsigned short MOUSEDOWN = 1; + const unsigned short MOUSEUP = 2; + const unsigned short MOUSEOVER = 4; + const unsigned short MOUSEOUT = 8; + const unsigned short MOUSEMOVE = 16; + const unsigned short MOUSEDRAG = 32; + const unsigned short CLICK = 64; + const unsigned short DBLCLICK = 128; + const unsigned short KEYDOWN = 256; + const unsigned short KEYUP = 512; + const unsigned short KEYPRESS = 1024; + const unsigned short DRAGDROP = 2048; + const unsigned short FOCUS = 4096; + const unsigned short BLUR = 8192; + const unsigned short SELECT = 16384; + const unsigned short CHANGE = 32768; +#endif + + readonly attribute DOMString type; + readonly attribute EventTarget target; + readonly attribute EventTarget currentTarget; + readonly attribute unsigned short eventPhase; + readonly attribute boolean bubbles; + readonly attribute boolean cancelable; +#if !defined(LANGUAGE_COM) + readonly attribute DOMTimeStamp timeStamp; +#endif + void stopPropagation(); + void preventDefault(); + [OldStyleObjC] void initEvent(in DOMString eventTypeArg, + in boolean canBubbleArg, + in boolean cancelableArg); + + // IE Extensions + readonly attribute EventTarget srcElement; + attribute boolean returnValue; + attribute boolean cancelBubble; + +#if defined(LANGUAGE_JAVASCRIPT) + readonly attribute [Custom] Clipboard clipboardData; +#endif + + }; + +} diff --git a/WebCore/dom/EventException.h b/WebCore/dom/EventException.h new file mode 100644 index 0000000..15a0dc8 --- /dev/null +++ b/WebCore/dom/EventException.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EventException_h +#define EventException_h + +#include "ExceptionBase.h" + +namespace WebCore { + + class EventException : public ExceptionBase { + public: + EventException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + + static const int EventExceptionOffset = 100; + static const int EventExceptionMax = 199; + + enum EventExceptionCode { + UNSPECIFIED_EVENT_TYPE_ERR = EventExceptionOffset + }; + }; + +} // namespace WebCore + +#endif // EventException_h diff --git a/WebCore/dom/EventException.idl b/WebCore/dom/EventException.idl new file mode 100644 index 0000000..61cfd65 --- /dev/null +++ b/WebCore/dom/EventException.idl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module events { + + // Introduced in DOM Level 2: + interface [ + GenerateConstructor + ] EventException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(LANGUAGE_JAVASCRIPT) + // Override in a Mozilla compatible format + [DontEnum] DOMString toString(); +#endif + + // EventExceptionCode + const unsigned short UNSPECIFIED_EVENT_TYPE_ERR = 0; + + }; + +} diff --git a/WebCore/dom/EventListener.h b/WebCore/dom/EventListener.h new file mode 100644 index 0000000..0938041 --- /dev/null +++ b/WebCore/dom/EventListener.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef EventListener_h +#define EventListener_h + +#include <wtf/RefCounted.h> + +namespace WebCore { + + class Event; + + class EventListener : public RefCounted<EventListener> { + public: + EventListener() : RefCounted<EventListener>(0) { } + virtual ~EventListener() { } + virtual void handleEvent(Event*, bool isWindowEvent = false) = 0; + virtual bool isHTMLEventListener() const { return false; } + }; + +} + +#endif diff --git a/WebCore/dom/EventListener.idl b/WebCore/dom/EventListener.idl new file mode 100644 index 0000000..9d28703 --- /dev/null +++ b/WebCore/dom/EventListener.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module events { + + // Introduced in DOM Level 2: + interface [ + ObjCProtocol, + InterfaceUUID=B04F2AE3-71E2-4ebe-ABFE-EF4938354082, + ImplementationUUID=DDFDD342-A78B-4f19-8F32-A5DF51B56E08 + ] EventListener { + void handleEvent(in Event evt); + }; + +} diff --git a/WebCore/dom/EventNames.cpp b/WebCore/dom/EventNames.cpp new file mode 100644 index 0000000..c5aaa37 --- /dev/null +++ b/WebCore/dom/EventNames.cpp @@ -0,0 +1,51 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2005 Apple Computer, Inc. + * + * 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 "config.h" + +#ifdef AVOID_STATIC_CONSTRUCTORS +#define DOM_EVENT_NAMES_HIDE_GLOBALS 1 +#endif + +#include "EventNames.h" +#include "StaticConstructors.h" + +namespace WebCore { namespace EventNames { + +#define DEFINE_EVENT_GLOBAL(name) \ + DEFINE_GLOBAL(AtomicString, name##Event, #name) +DOM_EVENT_NAMES_FOR_EACH(DEFINE_EVENT_GLOBAL) + +void init() +{ + static bool initialized; + if (!initialized) { + // Use placement new to initialize the globals. + + AtomicString::init(); + #define INITIALIZE_GLOBAL(name) new ((void*)&name##Event) AtomicString(#name); + DOM_EVENT_NAMES_FOR_EACH(INITIALIZE_GLOBAL) + initialized = true; + } +} + +} } diff --git a/WebCore/dom/EventNames.h b/WebCore/dom/EventNames.h new file mode 100644 index 0000000..843fb16 --- /dev/null +++ b/WebCore/dom/EventNames.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2005, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Jon Shier (jshier@iastate.edu) + * + * 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. + * + */ + +#ifndef EventNames_h +#define EventNames_h + +#include "AtomicString.h" + +namespace WebCore { namespace EventNames { + +#define DOM_EVENT_NAMES_FOR_EACH(macro) \ + \ + macro(abort) \ + macro(beforecopy) \ + macro(beforecut) \ + macro(beforepaste) \ + macro(beforeunload) \ + macro(blur) \ + macro(change) \ + macro(click) \ + macro(contextmenu) \ + macro(copy) \ + macro(cut) \ + macro(dblclick) \ + macro(drag) \ + macro(dragend) \ + macro(dragenter) \ + macro(dragleave) \ + macro(dragover) \ + macro(dragstart) \ + macro(drop) \ + macro(error) \ + macro(focus) \ + macro(input) \ + macro(keydown) \ + macro(keypress) \ + macro(keyup) \ + macro(load) \ + macro(message) \ + macro(mousedown) \ + macro(mousemove) \ + macro(mouseout) \ + macro(mouseover) \ + macro(mouseup) \ + macro(mousewheel) \ + macro(overflowchanged) \ + macro(paste) \ + macro(readystatechange) \ + macro(reset) \ + macro(resize) \ + macro(scroll) \ + macro(search) \ + macro(select) \ + macro(selectstart) \ + macro(submit) \ + macro(textInput) \ + macro(unload) \ + macro(zoom) \ + \ + macro(DOMActivate) \ + macro(DOMAttrModified) \ + macro(DOMCharacterDataModified) \ + macro(DOMFocusIn) \ + macro(DOMFocusOut) \ + macro(DOMNodeInserted) \ + macro(DOMNodeInsertedIntoDocument) \ + macro(DOMNodeRemoved) \ + macro(DOMNodeRemovedFromDocument) \ + macro(DOMSubtreeModified) \ + macro(DOMContentLoaded) \ + \ + macro(webkitBeforeTextInserted) \ + macro(webkitEditableContentChanged) \ + \ + macro(canshowcurrentframe) \ + macro(canplay) \ + macro(canplaythrough) \ + macro(dataunavailable) \ + macro(durationchange) \ + macro(emptied) \ + macro(ended) \ + macro(loadedfirstframe) \ + macro(loadedmetadata) \ + macro(pause) \ + macro(play) \ + macro(ratechange) \ + macro(timeupdate) \ + macro(volumechange) \ + macro(waiting) \ + \ + macro(begin) \ + macro(progress) \ + macro(stalled) \ + \ +// end of DOM_EVENT_NAMES_FOR_EACH + +#ifndef DOM_EVENT_NAMES_HIDE_GLOBALS + #define DOM_EVENT_NAMES_DECLARE(name) extern const AtomicString name##Event; + DOM_EVENT_NAMES_FOR_EACH(DOM_EVENT_NAMES_DECLARE) + #undef DOM_EVENT_NAMES_DECLARE +#endif + + void init(); + +} } + +#endif diff --git a/WebCore/dom/EventTarget.cpp b/WebCore/dom/EventTarget.cpp new file mode 100644 index 0000000..8eb8b9a --- /dev/null +++ b/WebCore/dom/EventTarget.cpp @@ -0,0 +1,335 @@ +/* + * 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) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "EventTarget.h" + +#include "Node.h" +#include "NodeList.h" +#include "Document.h" +#include "Event.h" +#include "EventListener.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "kjs_proxy.h" +#include "RegisteredEventListener.h" + +namespace WebCore { + +using namespace EventNames; + +#ifndef NDEBUG +static int gEventDispatchForbidden = 0; +#endif + +EventTarget::~EventTarget() +{ +} + +EventTargetNode* EventTarget::toNode() +{ + return 0; +} + +XMLHttpRequest* EventTarget::toXMLHttpRequest() +{ + return 0; +} + +#if ENABLE(SVG) +SVGElementInstance* EventTarget::toSVGElementInstance() +{ + return 0; +} +#endif + +static inline void addListenerTypeToDocumentIfNeeded(const AtomicString& eventType, Document* document) +{ + Document::ListenerType type = static_cast<Document::ListenerType>(0); + + if (eventType == DOMSubtreeModifiedEvent) + type = Document::DOMSUBTREEMODIFIED_LISTENER; + else if (eventType == DOMNodeInsertedEvent) + type = Document::DOMNODEINSERTED_LISTENER; + else if (eventType == DOMNodeRemovedEvent) + type = Document::DOMNODEREMOVED_LISTENER; + else if (eventType == DOMNodeRemovedFromDocumentEvent) + type = Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER; + else if (eventType == DOMNodeInsertedIntoDocumentEvent) + type = Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER; + else if (eventType == DOMAttrModifiedEvent) + type = Document::DOMATTRMODIFIED_LISTENER; + else if (eventType == DOMCharacterDataModifiedEvent) + type = Document::DOMCHARACTERDATAMODIFIED_LISTENER; + else if (eventType == overflowchangedEvent) + type = Document::OVERFLOWCHANGED_LISTENER; + + if (type) + document->addListenerType(type); +} + +void EventTarget::addEventListener(EventTargetNode* referenceNode, const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) +{ + ASSERT(referenceNode); + if (!referenceNode->document()->attached()) + return; + + addListenerTypeToDocumentIfNeeded(eventType, referenceNode->document()); + + if (!referenceNode->m_regdListeners) + referenceNode->m_regdListeners = new RegisteredEventListenerList; + + // Remove existing identical listener set with identical arguments. + // The DOM2 spec says that "duplicate instances are discarded" in this case. + removeEventListener(referenceNode, eventType, listener.get(), useCapture); + + // adding the first one + if (referenceNode->m_regdListeners->isEmpty() && !referenceNode->inDocument()) + referenceNode->document()->registerDisconnectedNodeWithEventListeners(referenceNode); + + referenceNode->m_regdListeners->append(new RegisteredEventListener(eventType, listener.get(), useCapture)); +} + +void EventTarget::removeEventListener(EventTargetNode* referenceNode, const AtomicString& eventType, EventListener* listener, bool useCapture) +{ + ASSERT(referenceNode); + if (!referenceNode->m_regdListeners) + return; + + RegisteredEventListener rl(eventType, listener, useCapture); + + RegisteredEventListenerList::Iterator end = referenceNode->m_regdListeners->end(); + for (RegisteredEventListenerList::Iterator it = referenceNode->m_regdListeners->begin(); it != end; ++it) { + if (*(*it).get() == rl) { + (*it)->setRemoved(true); + it = referenceNode->m_regdListeners->remove(it); + + // removed last + if (referenceNode->m_regdListeners->isEmpty() && !referenceNode->inDocument()) + referenceNode->document()->unregisterDisconnectedNodeWithEventListeners(referenceNode); + + return; + } + } +} + +bool EventTarget::dispatchGenericEvent(EventTargetNode* referenceNode, PassRefPtr<Event> e, ExceptionCode&, bool tempEvent) +{ + RefPtr<Event> evt(e); + + ASSERT(!eventDispatchForbidden()); + ASSERT(evt->target()); + ASSERT(!evt->type().isNull()); // JavaScript code could create an event with an empty name + + // work out what nodes to send event to + DeprecatedPtrList<Node> nodeChain; + + if (referenceNode->inDocument()) { + for (Node* n = referenceNode; n; n = n->eventParentNode()) { + n->ref(); + nodeChain.prepend(n); + } + } else { + // if node is not in the document just send event to itself + referenceNode->ref(); + nodeChain.prepend(referenceNode); + } + + DeprecatedPtrListIterator<Node> it(nodeChain); + + // Before we begin dispatching events, give the target node a chance to do some work prior + // to the DOM event handlers getting a crack. + void* data = preDispatchEventHandler(evt.get()); + + // trigger any capturing event handlers on our way down + evt->setEventPhase(Event::CAPTURING_PHASE); + it.toFirst(); + + // Handle window events for capture phase, except load events, this quirk is needed + // because Mozilla used to never propagate load events to the window object + if (evt->type() != loadEvent && it.current()->isDocumentNode() && !evt->propagationStopped()) + static_cast<Document*>(it.current())->handleWindowEvent(evt.get(), true); + + EventTargetNode* eventTargetNode = 0; + for (; it.current() && it.current() != referenceNode && !evt->propagationStopped(); ++it) { + eventTargetNode = EventTargetNodeCast(it.current()); + evt->setCurrentTarget(eventTargetRespectingSVGTargetRules(eventTargetNode)); + + eventTargetNode->handleLocalEvents(evt.get(), true); + } + + // dispatch to the actual target node + it.toLast(); + + if (!evt->propagationStopped()) { + evt->setEventPhase(Event::AT_TARGET); + + eventTargetNode = EventTargetNodeCast(it.current()); + evt->setCurrentTarget(eventTargetRespectingSVGTargetRules(eventTargetNode)); + + // We do want capturing event listeners to be invoked here, even though + // that violates the specification since Mozilla does it. + eventTargetNode->handleLocalEvents(evt.get(), true); + + eventTargetNode->handleLocalEvents(evt.get(), false); + } + + --it; + + // ok, now bubble up again (only non-capturing event handlers will be called) + // ### recalculate the node chain here? (e.g. if target node moved in document by previous event handlers) + // no. the DOM specs says: + // The chain of EventTargets from the event target to the top of the tree + // is determined before the initial dispatch of the event. + // If modifications occur to the tree during event processing, + // event flow will proceed based on the initial state of the tree. + // + // since the initial dispatch is before the capturing phase, + // there's no need to recalculate the node chain. + // (tobias) + + if (evt->bubbles()) { + evt->setEventPhase(Event::BUBBLING_PHASE); + + for (; it.current() && !evt->propagationStopped() && !evt->cancelBubble(); --it) { + eventTargetNode = EventTargetNodeCast(it.current()); + evt->setCurrentTarget(eventTargetRespectingSVGTargetRules(eventTargetNode)); + + eventTargetNode->handleLocalEvents(evt.get(), false); + } + + it.toFirst(); + + // Handle window events for bubbling phase, except load events, this quirk is needed + // because Mozilla used to never propagate load events at all + if (evt->type() != loadEvent && it.current()->isDocumentNode() && !evt->propagationStopped() && !evt->cancelBubble()) { + evt->setCurrentTarget(EventTargetNodeCast(it.current())); + static_cast<Document*>(it.current())->handleWindowEvent(evt.get(), false); + } + } + + evt->setCurrentTarget(0); + evt->setEventPhase(0); // I guess this is correct, the spec does not seem to say + // anything about the default event handler phase. + + + // Now call the post dispatch. + postDispatchEventHandler(evt.get(), data); + + // now we call all default event handlers (this is not part of DOM - it is internal to WebCore) + it.toLast(); + + if (evt->bubbles()) + for (; it.current() && !evt->defaultPrevented() && !evt->defaultHandled(); --it) + EventTargetNodeCast(it.current())->defaultEventHandler(evt.get()); + else if (!evt->defaultPrevented() && !evt->defaultHandled()) + EventTargetNodeCast(it.current())->defaultEventHandler(evt.get()); + + // deref all nodes in chain + it.toFirst(); + for (; it.current(); ++it) + it.current()->deref(); // this may delete us + + Document::updateDocumentsRendering(); + + // If tempEvent is true, this means that the DOM implementation + // will not be storing a reference to the event, i.e. there is no + // way to retrieve it from javascript if a script does not already + // have a reference to it in a variable. So there is no need for + // the interpreter to keep the event in it's cache + Frame* frame = referenceNode->document()->frame(); + if (tempEvent && frame && frame->scriptProxy()->isEnabled()) + frame->scriptProxy()->finishedWithEvent(evt.get()); + + return !evt->defaultPrevented(); // ### what if defaultPrevented was called before dispatchEvent? +} + +void EventTarget::removeAllEventListeners(EventTargetNode* referenceNode) +{ + delete referenceNode->m_regdListeners; + referenceNode->m_regdListeners = 0; +} + +void EventTarget::insertedIntoDocument(EventTargetNode* referenceNode) +{ + if (referenceNode && referenceNode->m_regdListeners && !referenceNode->m_regdListeners->isEmpty()) + referenceNode->document()->unregisterDisconnectedNodeWithEventListeners(referenceNode); +} + +void EventTarget::removedFromDocument(EventTargetNode* referenceNode) +{ + if (referenceNode && referenceNode->m_regdListeners && !referenceNode->m_regdListeners->isEmpty()) + referenceNode->document()->registerDisconnectedNodeWithEventListeners(referenceNode); +} + +void EventTarget::handleLocalEvents(EventTargetNode* referenceNode, Event* evt, bool useCapture) +{ + ASSERT(referenceNode); + if (!referenceNode->m_regdListeners || referenceNode->m_regdListeners->isEmpty()) + return; + + RegisteredEventListenerList listenersCopy = *referenceNode->m_regdListeners; + RegisteredEventListenerList::Iterator end = listenersCopy.end(); + + for (RegisteredEventListenerList::Iterator it = listenersCopy.begin(); it != end; ++it) { + if ((*it)->eventType() == evt->type() && (*it)->useCapture() == useCapture && !(*it)->removed()) + (*it)->listener()->handleEvent(evt, false); + } +} + +EventTarget* EventTarget::eventTargetRespectingSVGTargetRules(EventTargetNode*& referenceNode) +{ + // TODO: SVG will add logic here soon. + return referenceNode; +} + +#ifndef NDEBUG +void forbidEventDispatch() +{ + ++gEventDispatchForbidden; +} + +void allowEventDispatch() +{ + if (gEventDispatchForbidden > 0) + --gEventDispatchForbidden; +} + +bool eventDispatchForbidden() +{ + return gEventDispatchForbidden > 0; +} +#endif // NDEBUG + +} // end namespace diff --git a/WebCore/dom/EventTarget.h b/WebCore/dom/EventTarget.h new file mode 100644 index 0000000..87e6810 --- /dev/null +++ b/WebCore/dom/EventTarget.h @@ -0,0 +1,116 @@ +/* + * 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) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef EventTarget_h +#define EventTarget_h + +#include <wtf/Forward.h> + +namespace WebCore { + + class AtomicString; + class Event; + class EventListener; + class EventTargetNode; + class RegisteredEventListener; + class SVGElementInstance; + class XMLHttpRequest; + + typedef int ExceptionCode; + + template<typename T> class DeprecatedValueList; + typedef DeprecatedValueList<RefPtr<RegisteredEventListener> > RegisteredEventListenerList; + + class EventTarget { + public: + virtual EventTargetNode* toNode(); + virtual XMLHttpRequest* toXMLHttpRequest(); + +#if ENABLE(SVG) + virtual SVGElementInstance* toSVGElementInstance(); +#endif + + virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture) = 0; + virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture) = 0; + virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&, bool tempEvent = false) = 0; + + void ref() { refEventTarget(); } + void deref() { derefEventTarget(); } + + // Handlers to do/undo actions on the target node before an event is dispatched to it and after the event + // has been dispatched. The data pointer is handed back by the preDispatch and passed to postDispatch. + virtual void* preDispatchEventHandler(Event*) { return 0; } + virtual void postDispatchEventHandler(Event*, void* dataFromPreDispatch) { } + + protected: + virtual ~EventTarget(); + + void addEventListener(EventTargetNode* referenceNode, const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + void removeEventListener(EventTargetNode* referenceNode, const AtomicString& eventType, EventListener*, bool useCapture); + + bool dispatchGenericEvent(EventTargetNode* referenceNode, PassRefPtr<Event>, ExceptionCode&, bool tempEvent); + void removeAllEventListeners(EventTargetNode* referenceNode); + + void insertedIntoDocument(EventTargetNode* referenceNode); + void removedFromDocument(EventTargetNode* referenceNode); + + void handleLocalEvents(EventTargetNode* referenceNode, Event*, bool useCapture); + + // For non SVG elements it will return 'referenceNode' and not modify it. + // For SVG elements it eventually returns an event target not equal to 'referenceNode'. + // + // If 'referenceNode' is a child of a SVG <use> element it will return the corresponding SVGElementInstance + // as new event target - and 'referenceNode' will be set to the shadow tree element associated with + // the SVGElementInstance. Be sure to always dispatch/handle your events on this new event target. + EventTarget* eventTargetRespectingSVGTargetRules(EventTargetNode*& referenceNode); + + private: + virtual void refEventTarget() = 0; + virtual void derefEventTarget() = 0; + }; + +#ifndef NDEBUG + +void forbidEventDispatch(); +void allowEventDispatch(); +bool eventDispatchForbidden(); + +#else + +inline void forbidEventDispatch() { } +inline void allowEventDispatch() { } + +#endif // NDEBUG + +} +#endif diff --git a/WebCore/dom/EventTarget.idl b/WebCore/dom/EventTarget.idl new file mode 100644 index 0000000..d3f46f7 --- /dev/null +++ b/WebCore/dom/EventTarget.idl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module events { + + // Introduced in DOM Level 2: + interface [ + ObjCProtocol, + PureInterface, + InterfaceUUID=1D71C7EC-0BA0-4044-BDFD-56B3E8F5F9D4 + ] EventTarget { + [OldStyleObjC, EventTargetNodeCast] void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [OldStyleObjC, EventTargetNodeCast] void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [EventTargetNodeCast] boolean dispatchEvent(in Event event) + raises(EventException); + }; + +} diff --git a/WebCore/dom/EventTargetNode.cpp b/WebCore/dom/EventTargetNode.cpp new file mode 100644 index 0000000..146ee8b --- /dev/null +++ b/WebCore/dom/EventTargetNode.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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 "config.h" +#include "EventTargetNode.h" + +#include "Document.h" +#include "Event.h" +#include "EventException.h" +#include "EventHandler.h" +#include "EventListener.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "MutationEvent.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PlatformWheelEvent.h" +#include "ProgressEvent.h" +#include "RegisteredEventListener.h" +#include "TextEvent.h" +#include "WheelEvent.h" + +namespace WebCore { + +using namespace EventNames; + +EventTargetNode::EventTargetNode(Document *doc) + : Node(doc) + , m_regdListeners(0) +{ +} + +EventTargetNode::~EventTargetNode() +{ + if (m_regdListeners && !m_regdListeners->isEmpty() && !inDocument()) + document()->unregisterDisconnectedNodeWithEventListeners(this); + + delete m_regdListeners; + m_regdListeners = 0; +} + +void EventTargetNode::insertedIntoDocument() +{ + EventTarget::insertedIntoDocument(this); + Node::insertedIntoDocument(); +} + +void EventTargetNode::removedFromDocument() +{ + EventTarget::removedFromDocument(this); + Node::removedFromDocument(); +} + +void EventTargetNode::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) +{ + EventTarget::addEventListener(this, eventType, listener, useCapture); +} + +void EventTargetNode::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) +{ + EventTarget::removeEventListener(this, eventType, listener, useCapture); +} + +void EventTargetNode::removeAllEventListeners() +{ + EventTarget::removeAllEventListeners(this); +} + +void EventTargetNode::handleLocalEvents(Event *evt, bool useCapture) +{ + if (disabled() && evt->isMouseEvent()) + return; + + EventTarget::handleLocalEvents(this, evt, useCapture); +} + +bool EventTargetNode::dispatchEvent(PassRefPtr<Event> e, ExceptionCode& ec, bool tempEvent) +{ + RefPtr<Event> evt(e); + ASSERT(!eventDispatchForbidden()); + if (!evt || evt->type().isEmpty()) { + ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + return false; + } + + EventTargetNode* eventTarget = this; + evt->setTarget(eventTargetRespectingSVGTargetRules(eventTarget)); + + RefPtr<FrameView> view = document()->view(); + return dispatchGenericEvent(eventTarget, evt.release(), ec, tempEvent); +} + +bool EventTargetNode::dispatchSubtreeModifiedEvent() +{ + ASSERT(!eventDispatchForbidden()); + + document()->incDOMTreeVersion(); + + notifyNodeListsAttributeChanged(); // FIXME: Can do better some day. Really only care about the name attribute changing. + + if (!document()->hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER)) + return false; + ExceptionCode ec = 0; + return dispatchEvent(new MutationEvent(DOMSubtreeModifiedEvent, + true,false,0,String(),String(),String(),0),ec,true); +} + +void EventTargetNode::dispatchWindowEvent(const AtomicString &eventType, bool canBubbleArg, bool cancelableArg) +{ + ASSERT(!eventDispatchForbidden()); + ExceptionCode ec = 0; + RefPtr<Event> evt = new Event(eventType, canBubbleArg, cancelableArg); + RefPtr<Document> doc = document(); + evt->setTarget(doc); + doc->handleWindowEvent(evt.get(), true); + doc->handleWindowEvent(evt.get(), false); + + if (eventType == loadEvent) { + // For onload events, send a separate load event to the enclosing frame only. + // This is a DOM extension and is independent of bubbling/capturing rules of + // the DOM. + Element* ownerElement = doc->ownerElement(); + if (ownerElement) { + RefPtr<Event> ownerEvent = new Event(eventType, false, cancelableArg); + ownerEvent->setTarget(ownerElement); + ownerElement->dispatchGenericEvent(ownerElement, ownerEvent.release(), ec, true); + } + } +} + +bool EventTargetNode::dispatchUIEvent(const AtomicString& eventType, int detail, PassRefPtr<Event> underlyingEvent) +{ + ASSERT(!eventDispatchForbidden()); + ASSERT(eventType == DOMFocusInEvent || eventType == DOMFocusOutEvent || eventType == DOMActivateEvent); + + bool cancelable = eventType == DOMActivateEvent; + + ExceptionCode ec = 0; + RefPtr<UIEvent> evt = new UIEvent(eventType, true, cancelable, document()->defaultView(), detail); + evt->setUnderlyingEvent(underlyingEvent); + return dispatchEvent(evt.release(), ec, true); +} + +bool EventTargetNode::dispatchKeyEvent(const PlatformKeyboardEvent& key) +{ + ASSERT(!eventDispatchForbidden()); + ExceptionCode ec = 0; + RefPtr<KeyboardEvent> keyboardEvent = new KeyboardEvent(key, document()->defaultView()); + bool r = dispatchEvent(keyboardEvent,ec,true); + + // we want to return false if default is prevented (already taken care of) + // or if the element is default-handled by the DOM. Otherwise we let it just + // let it get handled by AppKit + if (keyboardEvent->defaultHandled()) + r = false; + + return r; +} + +bool EventTargetNode::dispatchMouseEvent(const PlatformMouseEvent& event, const AtomicString& eventType, + int detail, Node* relatedTarget) +{ + ASSERT(!eventDispatchForbidden()); + + IntPoint contentsPos; + if (FrameView* view = document()->view()) + contentsPos = view->windowToContents(event.pos()); + + short button = event.button(); + + ASSERT(event.eventType() == MouseEventMoved || button != NoButton); + + return dispatchMouseEvent(eventType, button, detail, + contentsPos.x(), contentsPos.y(), event.globalX(), event.globalY(), + event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), + false, relatedTarget); +} + +void EventTargetNode::dispatchSimulatedMouseEvent(const AtomicString& eventType, + PassRefPtr<Event> underlyingEvent) +{ + ASSERT(!eventDispatchForbidden()); + + if (m_dispatchingSimulatedEvent) + return; + + bool ctrlKey = false; + bool altKey = false; + bool shiftKey = false; + bool metaKey = false; + if (UIEventWithKeyState* keyStateEvent = findEventWithKeyState(underlyingEvent.get())) { + ctrlKey = keyStateEvent->ctrlKey(); + altKey = keyStateEvent->altKey(); + shiftKey = keyStateEvent->shiftKey(); + metaKey = keyStateEvent->metaKey(); + } + + m_dispatchingSimulatedEvent = true; + + // Like Gecko, we just pass 0 for everything when we make a fake mouse event. + // Internet Explorer instead gives the current mouse position and state. + dispatchMouseEvent(eventType, 0, 0, 0, 0, 0, 0, + ctrlKey, altKey, shiftKey, metaKey, true, 0, underlyingEvent); + + m_dispatchingSimulatedEvent = false; +} + +void EventTargetNode::dispatchSimulatedClick(PassRefPtr<Event> event, bool sendMouseEvents, bool showPressedLook) +{ + if (m_dispatchingSimulatedEvent) + return; + + // send mousedown and mouseup before the click, if requested + if (sendMouseEvents) + dispatchSimulatedMouseEvent(mousedownEvent, event.get()); + setActive(true, showPressedLook); + if (sendMouseEvents) + dispatchSimulatedMouseEvent(mouseupEvent, event.get()); + setActive(false); + + // always send click + dispatchSimulatedMouseEvent(clickEvent, event); +} + +bool EventTargetNode::dispatchMouseEvent(const AtomicString& eventType, int button, int detail, + int pageX, int pageY, int screenX, int screenY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + bool isSimulated, Node* relatedTargetArg, PassRefPtr<Event> underlyingEvent) +{ + ASSERT(!eventDispatchForbidden()); + if (disabled()) // Don't even send DOM events for disabled controls.. + return true; + + if (eventType.isEmpty()) + return false; // Shouldn't happen. + + // Dispatching the first event can easily result in this node being destroyed. + // Since we dispatch up to three events here, we need to make sure we're referenced + // so the pointer will be good for the two subsequent ones. + RefPtr<Node> protect(this); + + bool cancelable = eventType != mousemoveEvent; + + ExceptionCode ec = 0; + + bool swallowEvent = false; + + // Attempting to dispatch with a non-EventTarget relatedTarget causes the relatedTarget to be silently ignored. + RefPtr<EventTargetNode> relatedTarget = (relatedTargetArg && relatedTargetArg->isEventTargetNode()) + ? static_cast<EventTargetNode*>(relatedTargetArg) : 0; + + RefPtr<Event> mouseEvent = new MouseEvent(eventType, + true, cancelable, document()->defaultView(), + detail, screenX, screenY, pageX, pageY, + ctrlKey, altKey, shiftKey, metaKey, button, + relatedTarget.get(), 0, isSimulated); + mouseEvent->setUnderlyingEvent(underlyingEvent.get()); + + dispatchEvent(mouseEvent, ec, true); + bool defaultHandled = mouseEvent->defaultHandled(); + bool defaultPrevented = mouseEvent->defaultPrevented(); + if (defaultHandled || defaultPrevented) + swallowEvent = true; + + // Special case: If it's a double click event, we also send the dblclick event. This is not part + // of the DOM specs, but is used for compatibility with the ondblclick="" attribute. This is treated + // as a separate event in other DOM-compliant browsers like Firefox, and so we do the same. + if (eventType == clickEvent && detail == 2) { + RefPtr<Event> doubleClickEvent = new MouseEvent(dblclickEvent, + true, cancelable, document()->defaultView(), + detail, screenX, screenY, pageX, pageY, + ctrlKey, altKey, shiftKey, metaKey, button, + relatedTarget.get(), 0, isSimulated); + doubleClickEvent->setUnderlyingEvent(underlyingEvent.get()); + if (defaultHandled) + doubleClickEvent->setDefaultHandled(); + dispatchEvent(doubleClickEvent, ec, true); + if (doubleClickEvent->defaultHandled() || doubleClickEvent->defaultPrevented()) + swallowEvent = true; + } + + return swallowEvent; +} + +void EventTargetNode::dispatchWheelEvent(PlatformWheelEvent& e) +{ + ASSERT(!eventDispatchForbidden()); + if (e.deltaX() == 0 && e.deltaY() == 0) + return; + + FrameView* view = document()->view(); + if (!view) + return; + + IntPoint pos = view->windowToContents(e.pos()); + + RefPtr<WheelEvent> we = new WheelEvent(e.deltaX(), e.deltaY(), + document()->defaultView(), e.globalX(), e.globalY(), pos.x(), pos.y(), + e.ctrlKey(), e.altKey(), e.shiftKey(), e.metaKey()); + ExceptionCode ec = 0; + if (!dispatchEvent(we, ec, true)) + e.accept(); +} + + +void EventTargetNode::dispatchFocusEvent() +{ + dispatchHTMLEvent(focusEvent, false, false); +} + +void EventTargetNode::dispatchBlurEvent() +{ + dispatchHTMLEvent(blurEvent, false, false); +} + +bool EventTargetNode::dispatchHTMLEvent(const AtomicString &eventType, bool canBubbleArg, bool cancelableArg) +{ + ASSERT(!eventDispatchForbidden()); + ExceptionCode ec = 0; + return dispatchEvent(new Event(eventType, canBubbleArg, cancelableArg), ec, true); +} + +bool EventTargetNode::dispatchProgressEvent(const AtomicString &eventType, bool lengthComputableArg, unsigned loadedArg, unsigned totalArg) +{ + ASSERT(!eventDispatchForbidden()); + ExceptionCode ec = 0; + return dispatchEvent(new ProgressEvent(eventType, lengthComputableArg, loadedArg, totalArg), ec, true); +} + +void EventTargetNode::removeHTMLEventListener(const AtomicString &eventType) +{ + if (!m_regdListeners) // nothing to remove + return; + + RegisteredEventListenerList::Iterator end = m_regdListeners->end(); + for (RegisteredEventListenerList::Iterator it = m_regdListeners->begin(); it != end; ++it) + if ((*it)->eventType() == eventType && (*it)->listener()->isHTMLEventListener()) { + it = m_regdListeners->remove(it); + // removed last + if (m_regdListeners->isEmpty() && !inDocument()) + document()->unregisterDisconnectedNodeWithEventListeners(this); + return; + } +} + +void EventTargetNode::setHTMLEventListener(const AtomicString &eventType, PassRefPtr<EventListener> listener) +{ + // In case we are the only one holding a reference to it, we don't want removeHTMLEventListener to destroy it. + removeHTMLEventListener(eventType); + if (listener) + addEventListener(eventType, listener.get(), false); +} + +EventListener *EventTargetNode::getHTMLEventListener(const AtomicString &eventType) +{ + if (!m_regdListeners) + return 0; + + RegisteredEventListenerList::Iterator end = m_regdListeners->end(); + for (RegisteredEventListenerList::Iterator it = m_regdListeners->begin(); it != end; ++it) + if ((*it)->eventType() == eventType && (*it)->listener()->isHTMLEventListener()) + return (*it)->listener(); + return 0; +} + +bool EventTargetNode::disabled() const +{ + return false; +} + +void EventTargetNode::defaultEventHandler(Event* event) +{ + if (event->target() != this) + return; + const AtomicString& eventType = event->type(); + if (eventType == keydownEvent || eventType == keypressEvent) { + if (event->isKeyboardEvent()) + if (Frame* frame = document()->frame()) + frame->eventHandler()->defaultKeyboardEventHandler(static_cast<KeyboardEvent*>(event)); + } else if (eventType == clickEvent) { + int detail = event->isUIEvent() ? static_cast<UIEvent*>(event)->detail() : 0; + dispatchUIEvent(DOMActivateEvent, detail, event); + } else if (eventType == contextmenuEvent) { + if (Frame* frame = document()->frame()) + if (Page* page = frame->page()) + page->contextMenuController()->handleContextMenuEvent(event); + } else if (eventType == textInputEvent) { + if (event->isTextEvent()) + if (Frame* frame = document()->frame()) + frame->eventHandler()->defaultTextInputEventHandler(static_cast<TextEvent*>(event)); + } +} + +} // namespace WebCore diff --git a/WebCore/dom/EventTargetNode.h b/WebCore/dom/EventTargetNode.h new file mode 100644 index 0000000..9a0647b --- /dev/null +++ b/WebCore/dom/EventTargetNode.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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. + * + */ + +#ifndef EventTargetNode_h +#define EventTargetNode_h + +#include "EventTarget.h" +#include "Node.h" + +namespace WebCore { + +class EventTargetNode : public Node, + public EventTarget { +public: + EventTargetNode(Document*); + virtual ~EventTargetNode(); + + virtual bool isEventTargetNode() const { return true; } + virtual EventTargetNode* toNode() { return this; } + + virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&, bool tempEvent = false); + void removeAllEventListeners(); + + void setHTMLEventListener(const AtomicString& eventType, PassRefPtr<EventListener>); + void removeHTMLEventListener(const AtomicString& eventType); + bool dispatchHTMLEvent(const AtomicString& eventType, bool canBubble, bool cancelable); + EventListener* getHTMLEventListener(const AtomicString& eventType); + + bool dispatchSubtreeModifiedEvent(); + void dispatchWindowEvent(const AtomicString& eventType, bool canBubble, bool cancelable); + bool dispatchUIEvent(const AtomicString& eventType, int detail = 0, PassRefPtr<Event> underlyingEvent = 0); + bool dispatchKeyEvent(const PlatformKeyboardEvent&); + void dispatchWheelEvent(PlatformWheelEvent&); + bool dispatchMouseEvent(const PlatformMouseEvent&, const AtomicString& eventType, + int clickCount = 0, Node* relatedTarget = 0); + bool dispatchMouseEvent(const AtomicString& eventType, int button, int clickCount, + int pageX, int pageY, int screenX, int screenY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + bool isSimulated = false, Node* relatedTarget = 0, PassRefPtr<Event> underlyingEvent = 0); + void dispatchSimulatedMouseEvent(const AtomicString& eventType, PassRefPtr<Event> underlyingEvent = 0); + void dispatchSimulatedClick(PassRefPtr<Event> underlyingEvent, bool sendMouseEvents = false, bool showPressedLook = true); + bool dispatchProgressEvent(const AtomicString &eventType, bool lengthComputableArg, unsigned loadedArg, unsigned totalArg); + + virtual void handleLocalEvents(Event*, bool useCapture); + + virtual void dispatchFocusEvent(); + virtual void dispatchBlurEvent(); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + /** + * Perform the default action for an event e.g. submitting a form + */ + virtual void defaultEventHandler(Event*); + + /** + * Used for disabled form elements; if true, prevents mouse events from being dispatched + * to event listeners, and prevents DOMActivate events from being sent at all. + */ + virtual bool disabled() const; + + RegisteredEventListenerList* localEventListeners() const { return m_regdListeners; } + + using Node::ref; + using Node::deref; + +protected: + friend class EventTarget; + RegisteredEventListenerList* m_regdListeners; + +private: + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } +}; + +inline EventTargetNode* EventTargetNodeCast(Node* n) +{ + ASSERT(n->isEventTargetNode()); + return static_cast<EventTargetNode*>(n); +} + +inline const EventTargetNode* EventTargetNodeCast(const Node* n) +{ + ASSERT(n->isEventTargetNode()); + return static_cast<const EventTargetNode*>(n); +} + +} // namespace WebCore + +#endif // EventTargetNode_h diff --git a/WebCore/dom/ExceptionBase.cpp b/WebCore/dom/ExceptionBase.cpp new file mode 100644 index 0000000..d2526bd --- /dev/null +++ b/WebCore/dom/ExceptionBase.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ExceptionBase.h" + +namespace WebCore { + +ExceptionBase::ExceptionBase(const ExceptionCodeDescription& description) + : RefCounted<ExceptionBase>(0) +{ + m_code = description.code; + if (description.name) { + m_name = description.name; + m_message = String::format("%s: %s Exception %d", description.name, description.typeName, description.code); + } else + m_message = String::format("%s Exception %d", description.typeName, description.code); +} + +String ExceptionBase::toString() const +{ + return "Error: " + m_message; +} + +} // namespace WebCore diff --git a/WebCore/dom/ExceptionBase.h b/WebCore/dom/ExceptionBase.h new file mode 100644 index 0000000..3fe14ae --- /dev/null +++ b/WebCore/dom/ExceptionBase.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ExceptionBase_h +#define ExceptionBase_h + +#include "ExceptionCode.h" +#include "PlatformString.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + + class ExceptionBase : public RefCounted<ExceptionBase> { + public: + ExceptionBase(const ExceptionCodeDescription&); + + unsigned short code() const { return m_code; } + String name() const { return m_name; } + String message() const { return m_message; } + + String toString() const; + + private: + unsigned short m_code; + String m_name; + String m_message; + }; + +} // namespace WebCore + +#endif // ExceptionBase_h diff --git a/WebCore/dom/ExceptionCode.cpp b/WebCore/dom/ExceptionCode.cpp new file mode 100644 index 0000000..e7ee20d --- /dev/null +++ b/WebCore/dom/ExceptionCode.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ExceptionCode.h" + +#include "EventException.h" +#include "RangeException.h" +#include "XMLHttpRequestException.h" + +#if ENABLE(SVG) +#include "SVGException.h" +#endif + +#if ENABLE(XPATH) +#include "XPathException.h" +#endif + +namespace WebCore { + +static const char* const exceptionNames[] = { + "INDEX_SIZE_ERR", + "DOMSTRING_SIZE_ERR", + "HIERARCHY_REQUEST_ERR", + "WRONG_DOCUMENT_ERR", + "INVALID_CHARACTER_ERR", + "NO_DATA_ALLOWED_ERR", + "NO_MODIFICATION_ALLOWED_ERR", + "NOT_FOUND_ERR", + "NOT_SUPPORTED_ERR", + "INUSE_ATTRIBUTE_ERR", + "INVALID_STATE_ERR", + "SYNTAX_ERR", + "INVALID_MODIFICATION_ERR", + "NAMESPACE_ERR", + "INVALID_ACCESS_ERR", + "VALIDATION_ERR", + "TYPE_MISMATCH_ERR" +}; + +static const char* const rangeExceptionNames[] = { + "BAD_BOUNDARYPOINTS_ERR", + "INVALID_NODE_TYPE_ERR" +}; + +static const char* const eventExceptionNames[] = { + "UNSPECIFIED_EVENT_TYPE_ERR" +}; + +static const char* const xmlHttpRequestExceptionNames[] = { + "NETWORK_ERR" +}; + +#if ENABLE(XPATH) +static const char* const xpathExceptionNames[] = { + "INVALID_EXPRESSION_ERR", + "TYPE_ERR" +}; +#endif + +#if ENABLE(SVG) +static const char* const svgExceptionNames[] = { + "SVG_WRONG_TYPE_ERR", + "SVG_INVALID_VALUE_ERR", + "SVG_MATRIX_NOT_INVERTABLE" +}; +#endif + +void getExceptionCodeDescription(ExceptionCode ec, ExceptionCodeDescription& description) +{ + ASSERT(ec); + + const char* typeName; + int code = ec; + const char* const* nameTable; + int nameTableSize; + int nameTableOffset; + ExceptionType type; + + if (code >= RangeException::RangeExceptionOffset && code <= RangeException::RangeExceptionMax) { + type = RangeExceptionType; + typeName = "DOM Range"; + code -= RangeException::RangeExceptionOffset; + nameTable = rangeExceptionNames; + nameTableSize = sizeof(rangeExceptionNames) / sizeof(rangeExceptionNames[0]); + nameTableOffset = RangeException::BAD_BOUNDARYPOINTS_ERR; + } else if (code >= EventException::EventExceptionOffset && code <= EventException::EventExceptionMax) { + type = EventExceptionType; + typeName = "DOM Events"; + code -= EventException::EventExceptionOffset; + nameTable = eventExceptionNames; + nameTableSize = sizeof(eventExceptionNames) / sizeof(eventExceptionNames[0]); + nameTableOffset = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + } else if (code >= XMLHttpRequestException::XMLHttpRequestExceptionOffset && code <= XMLHttpRequestException::XMLHttpRequestExceptionMax) { + type = XMLHttpRequestExceptionType; + typeName = "XMLHttpRequest"; + code -= XMLHttpRequestException::XMLHttpRequestExceptionOffset; + nameTable = xmlHttpRequestExceptionNames; + nameTableSize = sizeof(xmlHttpRequestExceptionNames) / sizeof(xmlHttpRequestExceptionNames[0]); + // XMLHttpRequest exception codes start with 101 and we don't want 100 empty elements in the name array + nameTableOffset = XMLHttpRequestException::NETWORK_ERR; +#if ENABLE(XPATH) + } else if (code >= XPathException::XPathExceptionOffset && code <= XPathException::XPathExceptionMax) { + type = XPathExceptionType; + typeName = "DOM XPath"; + code -= XPathException::XPathExceptionOffset; + nameTable = xpathExceptionNames; + nameTableSize = sizeof(xpathExceptionNames) / sizeof(xpathExceptionNames[0]); + // XPath exception codes start with 51 and we don't want 51 empty elements in the name array + nameTableOffset = XPathException::INVALID_EXPRESSION_ERR; +#endif +#if ENABLE(SVG) + } else if (code >= SVGException::SVGExceptionOffset && code <= SVGException::SVGExceptionMax) { + type = SVGExceptionType; + typeName = "DOM SVG"; + code -= SVGException::SVGExceptionOffset; + nameTable = svgExceptionNames; + nameTableSize = sizeof(svgExceptionNames) / sizeof(svgExceptionNames[0]); + nameTableOffset = SVGException::SVG_WRONG_TYPE_ERR; +#endif + } else { + type = DOMExceptionType; + typeName = "DOM"; + nameTable = exceptionNames; + nameTableSize = sizeof(exceptionNames) / sizeof(exceptionNames[0]); + nameTableOffset = INDEX_SIZE_ERR; + } + + description.typeName = typeName; + description.name = (ec >= nameTableOffset && ec - nameTableOffset < nameTableSize) ? nameTable[ec - nameTableOffset] : 0; + description.code = code; + description.type = type; + + // All exceptions used in the DOM code should have names. + ASSERT(description.name); +} + +} // namespace WebCore diff --git a/WebCore/dom/ExceptionCode.h b/WebCore/dom/ExceptionCode.h new file mode 100644 index 0000000..7e75587 --- /dev/null +++ b/WebCore/dom/ExceptionCode.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ExceptionCode_h +#define ExceptionCode_h + +namespace WebCore { + + // The DOM standards use unsigned short for exception codes. + // In our DOM implementation we use int instead, and use different + // numerical ranges for different types of DOM exception, so that + // an exception of any type can be expressed with a single integer. + typedef int ExceptionCode; + + enum { + INDEX_SIZE_ERR = 1, + DOMSTRING_SIZE_ERR = 2, + HIERARCHY_REQUEST_ERR = 3, + WRONG_DOCUMENT_ERR = 4, + INVALID_CHARACTER_ERR = 5, + NO_DATA_ALLOWED_ERR = 6, + NO_MODIFICATION_ALLOWED_ERR = 7, + NOT_FOUND_ERR = 8, + NOT_SUPPORTED_ERR = 9, + INUSE_ATTRIBUTE_ERR = 10, + + // Introduced in DOM Level 2: + INVALID_STATE_ERR = 11, + SYNTAX_ERR = 12, + INVALID_MODIFICATION_ERR = 13, + NAMESPACE_ERR = 14, + INVALID_ACCESS_ERR = 15, + + // Introduced in DOM Level 3: + VALIDATION_ERR = 16, + TYPE_MISMATCH_ERR = 17 + }; + + enum ExceptionType { + DOMExceptionType, + RangeExceptionType, + EventExceptionType, + XMLHttpRequestExceptionType +#if ENABLE(XPATH) + , XPathExceptionType +#endif +#if ENABLE(SVG) + , SVGExceptionType +#endif + }; + + + struct ExceptionCodeDescription { + const char* typeName; // has spaces and is suitable for use in exception description strings; maximum length is 10 characters + const char* name; // exception name, also intended for use in exception description strings; 0 if name not known; maximum length is 27 characters + int code; // numeric value of the exception within a particular type + ExceptionType type; + }; + void getExceptionCodeDescription(ExceptionCode, ExceptionCodeDescription&); + +} // namespace WebCore + +#endif // ExceptionCode_h diff --git a/WebCore/dom/KeyboardEvent.cpp b/WebCore/dom/KeyboardEvent.cpp new file mode 100644 index 0000000..0ee8cb5 --- /dev/null +++ b/WebCore/dom/KeyboardEvent.cpp @@ -0,0 +1,166 @@ +/** + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "KeyboardEvent.h" + +#include "Document.h" +#include "DOMWindow.h" +#include "EventNames.h" +#include "EventHandler.h" +#include "Frame.h" +#include "PlatformKeyboardEvent.h" +#include "Settings.h" + +namespace WebCore { + +using namespace EventNames; + +static inline const AtomicString& eventTypeForKeyboardEventType(PlatformKeyboardEvent::Type type) +{ + switch (type) { + case PlatformKeyboardEvent::KeyUp: + return keyupEvent; + case PlatformKeyboardEvent::RawKeyDown: + return keydownEvent; + case PlatformKeyboardEvent::Char: + return keypressEvent; + case PlatformKeyboardEvent::KeyDown: + // The caller should disambiguate the combined event into RawKeyDown or Char events. + break; + } + ASSERT_NOT_REACHED(); + return keydownEvent; +} + +KeyboardEvent::KeyboardEvent() + : m_keyEvent(0) + , m_keyLocation(DOM_KEY_LOCATION_STANDARD) + , m_altGraphKey(false) +{ +} + +KeyboardEvent::KeyboardEvent(const PlatformKeyboardEvent& key, AbstractView* view) + : UIEventWithKeyState(eventTypeForKeyboardEventType(key.type()), + true, true, view, 0, key.ctrlKey(), key.altKey(), key.shiftKey(), key.metaKey()) + , m_keyEvent(new PlatformKeyboardEvent(key)) + , m_keyIdentifier(key.keyIdentifier()) + , m_keyLocation(key.isKeypad() ? DOM_KEY_LOCATION_NUMPAD : DOM_KEY_LOCATION_STANDARD) // FIXME: differentiate right/left, too + , m_altGraphKey(false) +{ +} + +KeyboardEvent::KeyboardEvent(const AtomicString& eventType, bool canBubble, bool cancelable, AbstractView *view, + const String &keyIdentifier, unsigned keyLocation, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool altGraphKey) + : UIEventWithKeyState(eventType, canBubble, cancelable, view, 0, ctrlKey, altKey, shiftKey, metaKey) + , m_keyEvent(0) + , m_keyIdentifier(keyIdentifier) + , m_keyLocation(keyLocation) + , m_altGraphKey(altGraphKey) +{ +} + +KeyboardEvent::~KeyboardEvent() +{ + delete m_keyEvent; +} + +void KeyboardEvent::initKeyboardEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, + const String &keyIdentifier, unsigned keyLocation, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool altGraphKey) +{ + if (dispatched()) + return; + + initUIEvent(type, canBubble, cancelable, view, 0); + + m_keyIdentifier = keyIdentifier; + m_keyLocation = keyLocation; + m_ctrlKey = ctrlKey; + m_shiftKey = shiftKey; + m_altKey = altKey; + m_metaKey = metaKey; + m_altGraphKey = altGraphKey; +} + +bool KeyboardEvent::getModifierState(const String& keyIdentifier) const +{ + if (keyIdentifier == "Control") + return ctrlKey(); + if (keyIdentifier == "Shift") + return shiftKey(); + if (keyIdentifier == "Alt") + return altKey(); + if (keyIdentifier == "Meta") + return metaKey(); + return false; +} + +int KeyboardEvent::keyCode() const +{ + // IE: virtual key code for keyup/keydown, character code for keypress + // Firefox: virtual key code for keyup/keydown, zero for keypress + // We match IE. + if (!m_keyEvent) + return 0; + if (type() == keydownEvent || type() == keyupEvent) + return m_keyEvent->windowsVirtualKeyCode(); + return charCode(); +} + +int KeyboardEvent::charCode() const +{ + // IE: not supported + // Firefox: 0 for keydown/keyup events, character code for keypress + // We match Firefox, unless in backward compatibility mode, where we always return the character code. + bool backwardCompatibilityMode = false; + if (view()) + backwardCompatibilityMode = view()->frame()->eventHandler()->needsKeyboardEventDisambiguationQuirks(); + + if (!m_keyEvent || (type() != keypressEvent && !backwardCompatibilityMode)) + return 0; + String text = m_keyEvent->text(); + return static_cast<int>(text.characterStartingAt(0)); +} + +bool KeyboardEvent::isKeyboardEvent() const +{ + return true; +} + +int KeyboardEvent::which() const +{ + // Netscape's "which" returns a virtual key code for keydown and keyup, and a character code for keypress. + // That's exactly what IE's "keyCode" returns. So they are the same for keyboard events. + return keyCode(); +} + +KeyboardEvent* findKeyboardEvent(Event* event) +{ + for (Event* e = event; e; e = e->underlyingEvent()) + if (e->isKeyboardEvent()) + return static_cast<KeyboardEvent*>(e); + return 0; +} + +} // namespace WebCore diff --git a/WebCore/dom/KeyboardEvent.h b/WebCore/dom/KeyboardEvent.h new file mode 100644 index 0000000..c77d3d0 --- /dev/null +++ b/WebCore/dom/KeyboardEvent.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef KeyboardEvent_h +#define KeyboardEvent_h + +#include "UIEventWithKeyState.h" +#include <wtf/Vector.h> + +namespace WebCore { + + class PlatformKeyboardEvent; + +#if PLATFORM(MAC) + struct KeypressCommand { + KeypressCommand(const String& commandName) : commandName(commandName) {} + KeypressCommand(const String& commandName, const String& text) : commandName(commandName), text(text) { ASSERT(commandName == "insertText:"); } + + String commandName; + String text; + }; +#endif + + // Introduced in DOM Level 3 + class KeyboardEvent : public UIEventWithKeyState { + public: + enum KeyLocationCode { + DOM_KEY_LOCATION_STANDARD = 0x00, + DOM_KEY_LOCATION_LEFT = 0x01, + DOM_KEY_LOCATION_RIGHT = 0x02, + DOM_KEY_LOCATION_NUMPAD = 0x03 + }; + + KeyboardEvent(); + KeyboardEvent(const PlatformKeyboardEvent&, AbstractView*); + KeyboardEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView*, + const String& keyIdentifier, unsigned keyLocation, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool altGraphKey); + virtual ~KeyboardEvent(); + + void initKeyboardEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView*, + const String& keyIdentifier, unsigned keyLocation, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool altGraphKey = false); + + String keyIdentifier() const { return m_keyIdentifier; } + unsigned keyLocation() const { return m_keyLocation; } + + bool getModifierState(const String& keyIdentifier) const; + + bool altGraphKey() const { return m_altGraphKey; } + + const PlatformKeyboardEvent* keyEvent() const { return m_keyEvent; } + + int keyCode() const; // key code for keydown and keyup, character for keypress + int charCode() const; // character code for keypress, 0 for keydown and keyup + + virtual bool isKeyboardEvent() const; + virtual int which() const; + +#if PLATFORM(MAC) + // We only have this need to store keypress command info on the Mac. + Vector<KeypressCommand>& keypressCommands() { return m_keypressCommands; } +#endif + + private: + PlatformKeyboardEvent* m_keyEvent; + String m_keyIdentifier; + unsigned m_keyLocation; + bool m_altGraphKey : 1; + +#if PLATFORM(MAC) + Vector<KeypressCommand> m_keypressCommands; +#endif + }; + + KeyboardEvent* findKeyboardEvent(Event*); + +} // namespace WebCore + +#endif // KeyboardEvent_h diff --git a/WebCore/dom/KeyboardEvent.idl b/WebCore/dom/KeyboardEvent.idl new file mode 100644 index 0000000..7e1f1ee --- /dev/null +++ b/WebCore/dom/KeyboardEvent.idl @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module events { + + // Introduced in DOM Level 3: + interface [ + GenerateConstructor + ] KeyboardEvent : UIEvent { + +#if !defined(LANGUAGE_JAVASCRIPT) + // KeyLocationCode + const unsigned long KEY_LOCATION_STANDARD = 0x00; + const unsigned long KEY_LOCATION_LEFT = 0x01; + const unsigned long KEY_LOCATION_RIGHT = 0x02; + const unsigned long KEY_LOCATION_NUMPAD = 0x03; +#endif + + readonly attribute DOMString keyIdentifier; + readonly attribute unsigned long keyLocation; + readonly attribute boolean ctrlKey; + readonly attribute boolean shiftKey; + readonly attribute boolean altKey; + readonly attribute boolean metaKey; + readonly attribute boolean altGraphKey; + +#if !defined(LANGUAGE_JAVASCRIPT) + boolean getModifierState(in DOMString keyIdentifierArg); +#endif + + // FIXME: this does not match the version in the DOM spec. + void initKeyboardEvent(in AtomicString type, + in boolean canBubble, + in boolean cancelable, + in DOMWindow view, + in DOMString keyIdentifier, + in unsigned long keyLocation, + in boolean ctrlKey, + in boolean altKey, + in boolean shiftKey, + in boolean metaKey, + in boolean altGraphKey); + + // WebKit Extensions +#if !defined(LANGUAGE_JAVASCRIPT) + readonly attribute long keyCode; + readonly attribute long charCode; + + void initKeyboardEvent(in AtomicString type, + in boolean canBubble, + in boolean cancelable, + in DOMWindow view, + in DOMString keyIdentifier, + in unsigned long keyLocation, + in boolean ctrlKey, + in boolean altKey, + in boolean shiftKey, + in boolean metaKey); +#endif + + }; + +} diff --git a/WebCore/dom/MappedAttribute.cpp b/WebCore/dom/MappedAttribute.cpp new file mode 100644 index 0000000..0bfeb5c --- /dev/null +++ b/WebCore/dom/MappedAttribute.cpp @@ -0,0 +1,35 @@ +/** + * 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 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "MappedAttribute.h" + +namespace WebCore { + +Attribute* MappedAttribute::clone(bool preserveDecl) const +{ + return new MappedAttribute(name(), value(), preserveDecl ? m_styleDecl.get() : 0); +} + +} diff --git a/WebCore/dom/MappedAttribute.h b/WebCore/dom/MappedAttribute.h new file mode 100644 index 0000000..0ecc3cb --- /dev/null +++ b/WebCore/dom/MappedAttribute.h @@ -0,0 +1,67 @@ +/* + * 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 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef MappedAttribute_h +#define MappedAttribute_h + +#include "Attribute.h" +#include "CSSMappedAttributeDeclaration.h" + +namespace WebCore { + +class Attr; +class CSSStyleDeclaration; +class CSSStyleSelector; +class Element; +class NamedAttrMap; + +class MappedAttribute : public Attribute +{ +public: + MappedAttribute(const QualifiedName& name, const AtomicString& value, CSSMappedAttributeDeclaration* decl = 0) + : Attribute(name, value), m_styleDecl(decl) + { + } + + MappedAttribute(const AtomicString& name, const AtomicString& value, CSSMappedAttributeDeclaration* decl = 0) + : Attribute(name, value), m_styleDecl(decl) + { + } + + virtual Attribute* clone(bool preserveDecl=true) const; + + virtual CSSStyleDeclaration* style() const { return m_styleDecl.get(); } + + CSSMappedAttributeDeclaration* decl() const { return m_styleDecl.get(); } + void setDecl(CSSMappedAttributeDeclaration* decl) { m_styleDecl = decl; } + +private: + RefPtr<CSSMappedAttributeDeclaration> m_styleDecl; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/MappedAttributeEntry.h b/WebCore/dom/MappedAttributeEntry.h new file mode 100644 index 0000000..745ad23 --- /dev/null +++ b/WebCore/dom/MappedAttributeEntry.h @@ -0,0 +1,55 @@ +/* + * 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 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef MappedAttributeEntry_h +#define MappedAttributeEntry_h + +namespace WebCore { + +enum MappedAttributeEntry { + eNone + , eUniversal + , ePersistent + , eReplaced + , eBlock + , eHR + , eUnorderedList + , eListItem + , eTable + , eCell + , eCaption + , eBDO + , ePre +#if ENABLE(SVG) + , eSVG +#endif +// When adding new entries, make sure to keep eLastEntry at the end of the list. + , eLastEntry +}; + +} + +#endif diff --git a/WebCore/dom/MessageEvent.cpp b/WebCore/dom/MessageEvent.cpp new file mode 100644 index 0000000..06f253d --- /dev/null +++ b/WebCore/dom/MessageEvent.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2007 Henry Mason (hmason@mac.com) + * Copyright (C) 2003, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" + +#if ENABLE(CROSS_DOCUMENT_MESSAGING) + +#include "DOMWindow.h" +#include "EventNames.h" +#include "MessageEvent.h" + +namespace WebCore { + +using namespace EventNames; + +MessageEvent::MessageEvent() +{ +} + +MessageEvent::MessageEvent(const String& data, const String& domain, const String& uri, DOMWindow* source) + : Event(messageEvent, true, true) + , m_data(data) + , m_domain(domain) + , m_uri(uri) + , m_source(source) +{ +} + +MessageEvent::~MessageEvent() +{ +} + +void MessageEvent::initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& data, const String& domain, const String& uri, DOMWindow* source) +{ + if (dispatched()) + return; + + initEvent(type, canBubble, cancelable); + + m_data = data; + m_domain = domain; + m_uri = uri; + m_source = source; +} + +bool MessageEvent::isMessageEvent() const +{ + return true; +} + +} // namespace WebCore + +#endif // ENABLE(CROSS_DOCUMENT_MESSAGING) diff --git a/WebCore/dom/MessageEvent.h b/WebCore/dom/MessageEvent.h new file mode 100644 index 0000000..f8b5f78 --- /dev/null +++ b/WebCore/dom/MessageEvent.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007 Henry Mason (hmason@mac.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef MessageEvent_h +#define MessageEvent_h + +#if ENABLE(CROSS_DOCUMENT_MESSAGING) + +#include "Event.h" + +namespace WebCore { + + class DOMWindow; + + class MessageEvent : public Event { + public: + MessageEvent(); + MessageEvent(const String& data, const String& domain, const String& uri, DOMWindow* source); + virtual ~MessageEvent(); + + void initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& data, const String& domain, const String& uri, DOMWindow* source); + + const String& data() const { return m_data; } + const String& domain() const { return m_domain; } + const String& uri() const { return m_uri; } + DOMWindow* source() const { return m_source.get(); } + + virtual bool isMessageEvent() const; + + private: + String m_data; + String m_domain; + String m_uri; + RefPtr<DOMWindow> m_source; + }; + +} // namespace WebCore + +#endif // ENABLE(CROSS_DOCUMENT_MESSAGING) + +#endif // MessageEvent_h diff --git a/WebCore/dom/MessageEvent.idl b/WebCore/dom/MessageEvent.idl new file mode 100644 index 0000000..22f5262 --- /dev/null +++ b/WebCore/dom/MessageEvent.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2007 Henry Mason <hmason@mac.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +module events { + + interface [Conditional=CROSS_DOCUMENT_MESSAGING,GenerateConstructor] MessageEvent : Event { + + readonly attribute DOMString data; + readonly attribute DOMString domain; + readonly attribute DOMString uri; + readonly attribute DOMWindow source; + + void initMessageEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString dataArg, in DOMString domainArg, in DOMString uriArg, in DOMWindow sourceArg); + + }; + +} diff --git a/WebCore/dom/MouseEvent.cpp b/WebCore/dom/MouseEvent.cpp new file mode 100644 index 0000000..245d620 --- /dev/null +++ b/WebCore/dom/MouseEvent.cpp @@ -0,0 +1,122 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "MouseEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +using namespace EventNames; + +MouseEvent::MouseEvent() + : m_button(0) + , m_buttonDown(false) +{ +} + +MouseEvent::MouseEvent(const AtomicString& eventType, bool canBubble, bool cancelable, AbstractView* view, + int detail, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + unsigned short button, EventTargetNode* relatedTarget, + Clipboard* clipboard, bool isSimulated) + : MouseRelatedEvent(eventType, canBubble, cancelable, view, detail, screenX, screenY, + pageX, pageY, ctrlKey, altKey, shiftKey, metaKey, isSimulated) + , m_button(button == (unsigned short)-1 ? 0 : button) + , m_buttonDown(button != (unsigned short)-1) + , m_relatedTarget(relatedTarget) + , m_clipboard(clipboard) +{ +} + +MouseEvent::~MouseEvent() +{ +} + +void MouseEvent::initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, + int detail, int screenX, int screenY, int clientX, int clientY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + unsigned short button, EventTargetNode* relatedTarget) +{ + if (dispatched()) + return; + + initUIEvent(type, canBubble, cancelable, view, detail); + + m_screenX = screenX; + m_screenY = screenY; + m_ctrlKey = ctrlKey; + m_altKey = altKey; + m_shiftKey = shiftKey; + m_metaKey = metaKey; + m_button = button == (unsigned short)-1 ? 0 : button; + m_buttonDown = button != (unsigned short)-1; + m_relatedTarget = relatedTarget; + + initCoordinates(clientX, clientY); + + // FIXME: m_isSimulated is not set to false here. + // FIXME: m_clipboard is not set to 0 here. +} + +bool MouseEvent::isMouseEvent() const +{ + return true; +} + +bool MouseEvent::isDragEvent() const +{ + const AtomicString& t = type(); + return t == dragenterEvent || t == dragoverEvent || t == dragleaveEvent || t == dropEvent + || t == dragstartEvent|| t == dragEvent || t == dragendEvent; +} + +int MouseEvent::which() const +{ + // For the DOM, the return values for left, middle and right mouse buttons are 0, 1, 2, respectively. + // For the Netscape "which" property, the return values for left, middle and right mouse buttons are 1, 2, 3, respectively. + // So we must add 1. + return m_button + 1; +} + +Node* MouseEvent::toElement() const +{ + // MSIE extension - "the object toward which the user is moving the mouse pointer" + if (type() == mouseoutEvent) + return relatedTarget(); + + return target() ? target()->toNode() : 0; +} + +Node* MouseEvent::fromElement() const +{ + // MSIE extension - "object from which activation or the mouse pointer is exiting during the event" (huh?) + if (type() != mouseoutEvent) + return relatedTarget(); + + return target() ? target()->toNode() : 0; +} + +} // namespace WebCore diff --git a/WebCore/dom/MouseEvent.h b/WebCore/dom/MouseEvent.h new file mode 100644 index 0000000..4a2d505 --- /dev/null +++ b/WebCore/dom/MouseEvent.h @@ -0,0 +1,76 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef MouseEvent_h +#define MouseEvent_h + +#include "Clipboard.h" +#include "EventTargetNode.h" +#include "MouseRelatedEvent.h" + +namespace WebCore { + + // Introduced in DOM Level 2 + class MouseEvent : public MouseRelatedEvent { + public: + MouseEvent(); + MouseEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, + int detail, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, + EventTargetNode* relatedTarget, Clipboard* clipboard = 0, bool isSimulated = false); + virtual ~MouseEvent(); + + void initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, + int detail, int screenX, int screenY, int clientX, int clientY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + unsigned short button, EventTargetNode* relatedTarget); + + // WinIE uses 1,4,2 for left/middle/right but not for click (just for mousedown/up, maybe others), + // but we will match the standard DOM. + unsigned short button() const { return m_button; } + bool buttonDown() const { return m_buttonDown; } + EventTargetNode* relatedTarget() const { return m_relatedTarget.get(); } + + Clipboard* clipboard() const { return m_clipboard.get(); } + + Node* toElement() const; + Node* fromElement() const; + + Clipboard* dataTransfer() const { return isDragEvent() ? m_clipboard.get() : 0; } + + virtual bool isMouseEvent() const; + virtual bool isDragEvent() const; + virtual int which() const; + + private: + unsigned short m_button; + bool m_buttonDown; + RefPtr<EventTargetNode> m_relatedTarget; + RefPtr<Clipboard> m_clipboard; + }; + +} // namespace WebCore + +#endif // MouseEvent_h diff --git a/WebCore/dom/MouseEvent.idl b/WebCore/dom/MouseEvent.idl new file mode 100644 index 0000000..01f5215 --- /dev/null +++ b/WebCore/dom/MouseEvent.idl @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + */ + +module events { + + // Introduced in DOM Level 2: + interface [ + GenerateConstructor + ] MouseEvent : UIEvent { + readonly attribute long screenX; + readonly attribute long screenY; + readonly attribute long clientX; + readonly attribute long clientY; + readonly attribute boolean ctrlKey; + readonly attribute boolean shiftKey; + readonly attribute boolean altKey; + readonly attribute boolean metaKey; + readonly attribute unsigned short button; + readonly attribute EventTarget relatedTarget; + + [OldStyleObjC] void initMouseEvent(in AtomicString type, + in boolean canBubble, + in boolean cancelable, + in DOMWindow view, + in long detail, + in long screenX, + in long screenY, + in long clientX, + in long clientY, + in boolean ctrlKey, + in boolean altKey, + in boolean shiftKey, + in boolean metaKey, + in unsigned short button, + in EventTarget relatedTarget); + + // extensions + readonly attribute long offsetX; + readonly attribute long offsetY; + readonly attribute long x; + readonly attribute long y; + readonly attribute Node fromElement; + readonly attribute Node toElement; + +#if defined(LANGUAGE_JAVASCRIPT) + readonly attribute Clipboard dataTransfer; +#endif + }; + +} diff --git a/WebCore/dom/MouseRelatedEvent.cpp b/WebCore/dom/MouseRelatedEvent.cpp new file mode 100644 index 0000000..994cab5 --- /dev/null +++ b/WebCore/dom/MouseRelatedEvent.cpp @@ -0,0 +1,189 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "MouseRelatedEvent.h" + +#include "DOMWindow.h" +#include "Document.h" +#include "Frame.h" +#include "FrameView.h" +#include "Node.h" +#include "RenderLayer.h" +#include "RenderObject.h" + +namespace WebCore { + +MouseRelatedEvent::MouseRelatedEvent() + : m_screenX(0) + , m_screenY(0) + , m_clientX(0) + , m_clientY(0) + , m_pageX(0) + , m_pageY(0) + , m_layerX(0) + , m_layerY(0) + , m_offsetX(0) + , m_offsetY(0) + , m_isSimulated(false) +{ +} + +static int contentsX(AbstractView* abstractView) +{ + if (!abstractView) + return 0; + Frame* frame = abstractView->frame(); + if (!frame) + return 0; + FrameView* frameView = frame->view(); + if (!frameView) + return 0; + return frameView->contentsX(); +} + +static int contentsY(AbstractView* abstractView) +{ + if (!abstractView) + return 0; + Frame* frame = abstractView->frame(); + if (!frame) + return 0; + FrameView* frameView = frame->view(); + if (!frameView) + return 0; + return frameView->contentsY(); +} + +MouseRelatedEvent::MouseRelatedEvent(const AtomicString& eventType, bool canBubble, bool cancelable, AbstractView* view, + int detail, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool isSimulated) + : UIEventWithKeyState(eventType, canBubble, cancelable, view, detail, ctrlKey, altKey, shiftKey, metaKey) + , m_screenX(screenX) + , m_screenY(screenY) + , m_clientX(pageX - contentsX(view)) + , m_clientY(pageY - contentsY(view)) + , m_pageX(pageX) + , m_pageY(pageY) + , m_isSimulated(isSimulated) +{ + initCoordinates(); +} + +void MouseRelatedEvent::initCoordinates() +{ + // Set up initial values for coordinates. + // Correct values can't be computed until we have at target, so receivedTarget + // does the "real" computation. + m_layerX = m_pageX; + m_layerY = m_pageY; + m_offsetX = m_pageX; + m_offsetY = m_pageY; +} + +void MouseRelatedEvent::initCoordinates(int clientX, int clientY) +{ + // Set up initial values for coordinates. + // Correct values can't be computed until we have at target, so receivedTarget + // does the "real" computation. + m_clientX = clientX; + m_clientY = clientY; + m_pageX = clientX + contentsX(view()); + m_pageY = clientY + contentsY(view()); + m_layerX = m_pageX; + m_layerY = m_pageY; + m_offsetX = m_pageX; + m_offsetY = m_pageY; +} + +void MouseRelatedEvent::receivedTarget() +{ + ASSERT(target()); + Node* targ = target()->toNode(); + if (!targ) + return; + + // Compute coordinates that are based on the target. + m_layerX = m_pageX; + m_layerY = m_pageY; + m_offsetX = m_pageX; + m_offsetY = m_pageY; + + // Must have an updated render tree for this math to work correctly. + targ->document()->updateRendering(); + + // Adjust offsetX/Y to be relative to the target's position. + if (!isSimulated()) { + if (RenderObject* r = targ->renderer()) { + int rx, ry; + if (r->absolutePosition(rx, ry)) { + m_offsetX -= rx; + m_offsetY -= ry; + } + } + } + + // Adjust layerX/Y to be relative to the layer. + // FIXME: We're pretty sure this is the wrong defintion of "layer." + // Our RenderLayer is a more modern concept, and layerX/Y is some + // other notion about groups of elements (left over from the Netscape 4 days?); + // we should test and fix this. + Node* n = targ; + while (n && !n->renderer()) + n = n->parent(); + if (n) { + RenderLayer* layer = n->renderer()->enclosingLayer(); + layer->updateLayerPosition(); + for (; layer; layer = layer->parent()) { + m_layerX -= layer->xPos(); + m_layerY -= layer->yPos(); + } + } +} + +int MouseRelatedEvent::pageX() const +{ + return m_pageX; +} + +int MouseRelatedEvent::pageY() const +{ + return m_pageY; +} + +int MouseRelatedEvent::x() const +{ + // FIXME: This is not correct. + // See Microsoft documentation and <http://www.quirksmode.org/dom/w3c_events.html>. + return m_clientX; +} + +int MouseRelatedEvent::y() const +{ + // FIXME: This is not correct. + // See Microsoft documentation and <http://www.quirksmode.org/dom/w3c_events.html>. + return m_clientY; +} + +} // namespace WebCore diff --git a/WebCore/dom/MouseRelatedEvent.h b/WebCore/dom/MouseRelatedEvent.h new file mode 100644 index 0000000..9d88d58 --- /dev/null +++ b/WebCore/dom/MouseRelatedEvent.h @@ -0,0 +1,78 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef MouseRelatedEvent_h +#define MouseRelatedEvent_h + +#include "UIEventWithKeyState.h" + +namespace WebCore { + + // Internal only: Helper class for what's common between mouse and wheel events. + class MouseRelatedEvent : public UIEventWithKeyState { + public: + MouseRelatedEvent(); + MouseRelatedEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, + int detail, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool isSimulated = false); + + int screenX() const { return m_screenX; } + int screenY() const { return m_screenY; } + int clientX() const { return m_clientX; } + int clientY() const { return m_clientY; } + int layerX() const { return m_layerX; } + int layerY() const { return m_layerY; } + int offsetX() const { return m_offsetX; } + int offsetY() const { return m_offsetY; } + bool isSimulated() const { return m_isSimulated; } + virtual int pageX() const; + virtual int pageY() const; + int x() const; + int y() const; + + protected: + void initCoordinates(); + void initCoordinates(int clientX, int clientY); + virtual void receivedTarget(); + + // Expose these so MouseEvent::initMouseEvent can set them. + int m_screenX; + int m_screenY; + int m_clientX; + int m_clientY; + + private: + int m_pageX; + int m_pageY; + int m_layerX; + int m_layerY; + int m_offsetX; + int m_offsetY; + bool m_isSimulated; + }; + +} // namespace WebCore + +#endif // MouseRelatedEvent_h diff --git a/WebCore/dom/MutationEvent.cpp b/WebCore/dom/MutationEvent.cpp new file mode 100644 index 0000000..6ab577e --- /dev/null +++ b/WebCore/dom/MutationEvent.cpp @@ -0,0 +1,68 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "MutationEvent.h" + +namespace WebCore { + +MutationEvent::MutationEvent() + : m_attrChange(0) +{ +} + +MutationEvent::MutationEvent(const AtomicString& type, bool canBubble, bool cancelable, Node* relatedNode, + const String& prevValue, const String& newValue, + const String& attrName, unsigned short attrChange) + : Event(type, canBubble, cancelable) + , m_relatedNode(relatedNode) + , m_prevValue(prevValue) + , m_newValue(newValue) + , m_attrName(attrName) + , m_attrChange(attrChange) +{ +} + +void MutationEvent::initMutationEvent(const AtomicString& type, bool canBubble, bool cancelable, Node* relatedNode, + const String& prevValue, const String& newValue, + const String& attrName, unsigned short attrChange) +{ + if (dispatched()) + return; + + initEvent(type, canBubble, cancelable); + + m_relatedNode = relatedNode; + m_prevValue = prevValue; + m_newValue = newValue; + m_attrName = attrName; + m_attrChange = attrChange; +} + +bool MutationEvent::isMutationEvent() const +{ + return true; +} + +} // namespace WebCore diff --git a/WebCore/dom/MutationEvent.h b/WebCore/dom/MutationEvent.h new file mode 100644 index 0000000..a820ed3 --- /dev/null +++ b/WebCore/dom/MutationEvent.h @@ -0,0 +1,69 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef MutationEvent_h +#define MutationEvent_h + +#include "Event.h" +#include "Node.h" + +namespace WebCore { + + class MutationEvent : public Event { + public: + enum attrChangeType { + MODIFICATION = 1, + ADDITION = 2, + REMOVAL = 3 + }; + + MutationEvent(); + MutationEvent(const AtomicString& type, bool canBubble, bool cancelable, Node* relatedNode, + const String& prevValue, const String& newValue, + const String& attrName, unsigned short attrChange); + + void initMutationEvent(const AtomicString& type, bool canBubble, bool cancelable, Node* relatedNode, + const String& prevValue, const String& newValue, + const String& attrName, unsigned short attrChange); + + Node* relatedNode() const { return m_relatedNode.get(); } + String prevValue() const { return m_prevValue; } + String newValue() const { return m_newValue; } + String attrName() const { return m_attrName; } + unsigned short attrChange() const { return m_attrChange; } + + virtual bool isMutationEvent() const; + + private: + RefPtr<Node> m_relatedNode; + String m_prevValue; + String m_newValue; + String m_attrName; + unsigned short m_attrChange; + }; + +} // namespace WebCore + +#endif // MutationEvent_h diff --git a/WebCore/dom/MutationEvent.idl b/WebCore/dom/MutationEvent.idl new file mode 100644 index 0000000..a383091 --- /dev/null +++ b/WebCore/dom/MutationEvent.idl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module events { + + // Introduced in DOM Level 2: + interface [ + GenerateConstructor + ] MutationEvent : Event { + + // attrChangeType + const unsigned short MODIFICATION = 1; + const unsigned short ADDITION = 2; + const unsigned short REMOVAL = 3; + + readonly attribute Node relatedNode; + readonly attribute DOMString prevValue; + readonly attribute DOMString newValue; + readonly attribute DOMString attrName; + readonly attribute unsigned short attrChange; + + [OldStyleObjC] void initMutationEvent(in AtomicString type, + in boolean canBubble, + in boolean cancelable, + in Node relatedNode, + in DOMString prevValue, + in DOMString newValue, + in DOMString attrName, + in unsigned short attrChange); + + }; + +} diff --git a/WebCore/dom/NameNodeList.cpp b/WebCore/dom/NameNodeList.cpp new file mode 100644 index 0000000..be6e72b --- /dev/null +++ b/WebCore/dom/NameNodeList.cpp @@ -0,0 +1,51 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2007 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "NameNodeList.h" + +#include "Element.h" +#include "HTMLNames.h" +#include <wtf/Assertions.h> + +namespace WebCore { + +using namespace HTMLNames; + +NameNodeList::NameNodeList(PassRefPtr<Node> rootNode, const String& name, DynamicNodeList::Caches* caches) + : DynamicNodeList(rootNode, caches, true) + , m_nodeName(name) +{ +} + +void NameNodeList::rootNodeAttributeChanged() +{ + DynamicNodeList::rootNodeChildrenChanged(); +} + +bool NameNodeList::nodeMatches(Node* testNode) const +{ + ASSERT(testNode->isElementNode()); + return static_cast<Element*>(testNode)->getAttribute(nameAttr) == m_nodeName; +} + +} // namespace WebCore diff --git a/WebCore/dom/NameNodeList.h b/WebCore/dom/NameNodeList.h new file mode 100644 index 0000000..bb8c7f4 --- /dev/null +++ b/WebCore/dom/NameNodeList.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef NameNodeList_h +#define NameNodeList_h + +#include "DynamicNodeList.h" +#include "AtomicString.h" + +namespace WebCore { + + class String; + + // NodeList which lists all Nodes in a Element with a given "name" attribute + class NameNodeList : public DynamicNodeList { + public: + NameNodeList(PassRefPtr<Node> rootNode, const String& name, DynamicNodeList::Caches*); + + virtual void rootNodeAttributeChanged(); + + private: + virtual bool nodeMatches(Node*) const; + + AtomicString m_nodeName; + }; + +} // namespace WebCore + +#endif // NameNodeList_h diff --git a/WebCore/dom/NamedAttrMap.cpp b/WebCore/dom/NamedAttrMap.cpp new file mode 100644 index 0000000..ee89bcc --- /dev/null +++ b/WebCore/dom/NamedAttrMap.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * (C) 2007 Eric Seidel (eric@webkit.org) + * + * 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 "config.h" +#include "NamedAttrMap.h" + +#include "Document.h" +#include "Element.h" +#include "ExceptionCode.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +static inline bool shouldIgnoreAttributeCase(const Element* e) +{ + return e && e->document()->isHTMLDocument() && e->isHTMLElement(); +} + +NamedAttrMap::NamedAttrMap(Element *e) + : element(e) + , attrs(0) + , len(0) +{ +} + +NamedAttrMap::~NamedAttrMap() +{ + NamedAttrMap::clearAttributes(); // virtual method, so qualify just to be explicit +} + +bool NamedAttrMap::isMappedAttributeMap() const +{ + return false; +} + +PassRefPtr<Node> NamedAttrMap::getNamedItem(const String& name) const +{ + String localName = shouldIgnoreAttributeCase(element) ? name.lower() : name; + Attribute* a = getAttributeItem(localName); + if (!a) + return 0; + + return a->createAttrIfNeeded(element); +} + +PassRefPtr<Node> NamedAttrMap::getNamedItemNS(const String& namespaceURI, const String& localName) const +{ + return getNamedItem(QualifiedName(nullAtom, localName, namespaceURI)); +} + +PassRefPtr<Node> NamedAttrMap::removeNamedItem(const String& name, ExceptionCode& ec) +{ + String localName = shouldIgnoreAttributeCase(element) ? name.lower() : name; + Attribute* a = getAttributeItem(localName); + if (!a) { + ec = NOT_FOUND_ERR; + return 0; + } + + return removeNamedItem(a->name(), ec); +} + +PassRefPtr<Node> NamedAttrMap::removeNamedItemNS(const String& namespaceURI, const String& localName, ExceptionCode& ec) +{ + return removeNamedItem(QualifiedName(nullAtom, localName, namespaceURI), ec); +} + +PassRefPtr<Node> NamedAttrMap::getNamedItem(const QualifiedName& name) const +{ + Attribute* a = getAttributeItem(name); + if (!a) + return 0; + + return a->createAttrIfNeeded(element); +} + +PassRefPtr<Node> NamedAttrMap::setNamedItem(Node* arg, ExceptionCode& ec) +{ + if (!element) { + ec = NOT_FOUND_ERR; + return 0; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly. + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return 0; + } + + // WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map. + if (arg->document() != element->document()) { + ec = WRONG_DOCUMENT_ERR; + return 0; + } + + // Not mentioned in spec: throw a HIERARCHY_REQUEST_ERROR if the user passes in a non-attribute node + if (!arg->isAttributeNode()) { + ec = HIERARCHY_REQUEST_ERR; + return 0; + } + Attr *attr = static_cast<Attr*>(arg); + + Attribute* a = attr->attr(); + Attribute* old = getAttributeItem(a->name()); + if (old == a) + return RefPtr<Node>(arg); // we know about it already + + // INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object. + // The DOM user must explicitly clone Attr nodes to re-use them in other elements. + if (attr->ownerElement()) { + ec = INUSE_ATTRIBUTE_ERR; + return 0; + } + + if (a->name() == idAttr) + element->updateId(old ? old->value() : nullAtom, a->value()); + + // ### slightly inefficient - resizes attribute array twice. + RefPtr<Node> r; + if (old) { + r = old->createAttrIfNeeded(element); + removeAttribute(a->name()); + } + + addAttribute(a); + return r.release(); +} + +// The DOM2 spec doesn't say that removeAttribute[NS] throws NOT_FOUND_ERR +// if the attribute is not found, but at this level we have to throw NOT_FOUND_ERR +// because of removeNamedItem, removeNamedItemNS, and removeAttributeNode. +PassRefPtr<Node> NamedAttrMap::removeNamedItem(const QualifiedName& name, ExceptionCode& ec) +{ + // ### should this really be raised when the attribute to remove isn't there at all? + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return 0; + } + + Attribute* a = getAttributeItem(name); + if (!a) { + ec = NOT_FOUND_ERR; + return 0; + } + + RefPtr<Node> r = a->createAttrIfNeeded(element); + + if (name == idAttr) + element->updateId(a->value(), nullAtom); + + removeAttribute(name); + return r.release(); +} + +PassRefPtr<Node> NamedAttrMap::item ( unsigned index ) const +{ + if (index >= len) + return 0; + + return attrs[index]->createAttrIfNeeded(element); +} + +Attribute* NamedAttrMap::getAttributeItem(const String& name) const +{ + for (unsigned i = 0; i < len; ++i) { + if (!attrs[i]->name().hasPrefix() && + attrs[i]->name().localName() == name) + return attrs[i]; + + if (attrs[i]->name().toString() == name) + return attrs[i]; + } + return 0; +} + +Attribute* NamedAttrMap::getAttributeItem(const QualifiedName& name) const +{ + for (unsigned i = 0; i < len; ++i) { + if (attrs[i]->name().matches(name)) + return attrs[i]; + } + return 0; +} + +void NamedAttrMap::clearAttributes() +{ + if (attrs) { + for (unsigned i = 0; i < len; i++) { + if (attrs[i]->attr()) + attrs[i]->attr()->m_element = 0; + attrs[i]->deref(); + } + fastFree(attrs); + attrs = 0; + } + len = 0; +} + +void NamedAttrMap::detachFromElement() +{ + // we allow a NamedAttrMap w/o an element in case someone still has a reference + // to if after the element gets deleted - but the map is now invalid + element = 0; + clearAttributes(); +} + +NamedAttrMap& NamedAttrMap::operator=(const NamedAttrMap& other) +{ + // clone all attributes in the other map, but attach to our element + if (!element) + return *this; + + // If assigning the map changes the id attribute, we need to call + // updateId. + + Attribute *oldId = getAttributeItem(idAttr); + Attribute *newId = other.getAttributeItem(idAttr); + + if (oldId || newId) + element->updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom); + + clearAttributes(); + len = other.len; + attrs = static_cast<Attribute **>(fastMalloc(len * sizeof(Attribute *))); + + // first initialize attrs vector, then call attributeChanged on it + // this allows attributeChanged to use getAttribute + for (unsigned i = 0; i < len; i++) { + attrs[i] = other.attrs[i]->clone(); + attrs[i]->ref(); + } + + // FIXME: This is wasteful. The class list could be preserved on a copy, and we + // wouldn't have to waste time reparsing the attribute. + // The derived class, HTMLNamedAttrMap, which manages a parsed class list for the CLASS attribute, + // will update its member variable when parse attribute is called. + for(unsigned i = 0; i < len; i++) + element->attributeChanged(attrs[i], true); + + return *this; +} + +void NamedAttrMap::addAttribute(PassRefPtr<Attribute> prpAttribute) +{ + Attribute* attribute = prpAttribute.releaseRef(); // The attrs array will own this pointer. + + // Add the attribute to the list + attrs = static_cast<Attribute**>(fastRealloc(attrs, (len + 1) * sizeof(Attribute*))); + attrs[len++] = attribute; + + if (Attr* attr = attribute->attr()) + attr->m_element = element; + + // Notify the element that the attribute has been added, and dispatch appropriate mutation events + // Note that element may be null here if we are called from insertAttr() during parsing + if (element) { + element->attributeChanged(attribute); + // Because of our updateStyleAttributeIfNeeded() style modification events are never sent at the right time, so don't bother sending them. + if (attribute->name() != styleAttr) { + element->dispatchAttrAdditionEvent(attribute); + element->dispatchSubtreeModifiedEvent(); + } + } +} + +void NamedAttrMap::removeAttribute(const QualifiedName& name) +{ + unsigned index = len+1; + for (unsigned i = 0; i < len; ++i) + if (attrs[i]->name().matches(name)) { + index = i; + break; + } + + if (index >= len) return; + + // Remove the attribute from the list + Attribute* attr = attrs[index]; + if (attrs[index]->attr()) + attrs[index]->attr()->m_element = 0; + if (len == 1) { + fastFree(attrs); + attrs = 0; + len = 0; + } else { + Attribute **newAttrs = static_cast<Attribute **>(fastMalloc((len - 1) * sizeof(Attribute *))); + unsigned i; + for (i = 0; i < unsigned(index); i++) + newAttrs[i] = attrs[i]; + len--; + for (; i < len; i++) + newAttrs[i] = attrs[i+1]; + fastFree(attrs); + attrs = newAttrs; + } + + // Notify the element that the attribute has been removed + // dispatch appropriate mutation events + if (element && !attr->m_value.isNull()) { + AtomicString value = attr->m_value; + attr->m_value = nullAtom; + element->attributeChanged(attr); + attr->m_value = value; + } + if (element) { + element->dispatchAttrRemovalEvent(attr); + element->dispatchSubtreeModifiedEvent(); + } + attr->deref(); +} + +bool NamedAttrMap::mapsEquivalent(const NamedAttrMap* otherMap) const +{ + if (!otherMap) + return false; + + if (length() != otherMap->length()) + return false; + + for (unsigned i = 0; i < length(); i++) { + Attribute *attr = attributeItem(i); + Attribute *otherAttr = otherMap->getAttributeItem(attr->name()); + + if (!otherAttr || attr->value() != otherAttr->value()) + return false; + } + + return true; +} + +bool NamedAttrMap::isReadOnlyNode() +{ + return element && element->isReadOnlyNode(); +} + +} diff --git a/WebCore/dom/NamedAttrMap.h b/WebCore/dom/NamedAttrMap.h new file mode 100644 index 0000000..dd57284 --- /dev/null +++ b/WebCore/dom/NamedAttrMap.h @@ -0,0 +1,103 @@ +/* + * 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 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef NamedAttrMap_h +#define NamedAttrMap_h + +#include "Attribute.h" +#include "NamedNodeMap.h" + +#ifdef __OBJC__ +#define id id_AVOID_KEYWORD +#endif + +namespace WebCore { + +// the map of attributes of an element +class NamedAttrMap : public NamedNodeMap { + friend class Element; +public: + NamedAttrMap(Element*); + virtual ~NamedAttrMap(); + NamedAttrMap(const NamedAttrMap&); + NamedAttrMap &operator =(const NamedAttrMap &other); + + // DOM methods & attributes for NamedNodeMap + + virtual PassRefPtr<Node> getNamedItem(const String& name) const; + virtual PassRefPtr<Node> removeNamedItem(const String& name, ExceptionCode&); + + virtual PassRefPtr<Node> getNamedItemNS(const String& namespaceURI, const String& localName) const; + virtual PassRefPtr<Node> removeNamedItemNS(const String& namespaceURI, const String& localName, ExceptionCode&); + + virtual PassRefPtr<Node> getNamedItem(const QualifiedName& name) const; + virtual PassRefPtr<Node> removeNamedItem(const QualifiedName& name, ExceptionCode&); + virtual PassRefPtr<Node> setNamedItem(Node* arg, ExceptionCode&); + + virtual PassRefPtr<Node> item(unsigned index) const; + unsigned length() const { return len; } + + // Other methods (not part of DOM) + Attribute* attributeItem(unsigned index) const { return attrs[index]; } + Attribute* getAttributeItem(const QualifiedName& name) const; + Attribute* getAttributeItem(const String& name) const; + virtual bool isReadOnlyNode(); + + // used during parsing: only inserts if not already there + // no error checking! + void insertAttribute(PassRefPtr<Attribute> newAttribute, bool allowDuplicates) + { + ASSERT(!element); + if (allowDuplicates || !getAttributeItem(newAttribute->name())) + addAttribute(newAttribute); + } + + virtual bool isMappedAttributeMap() const; + + const AtomicString& id() const { return m_id; } + void setID(const AtomicString& _id) { m_id = _id; } + + bool mapsEquivalent(const NamedAttrMap* otherMap) const; + +protected: + // this method is internal, does no error checking at all + void addAttribute(PassRefPtr<Attribute>); + // this method is internal, does no error checking at all + void removeAttribute(const QualifiedName& name); + virtual void clearAttributes(); + void detachFromElement(); + + Element *element; + Attribute **attrs; + unsigned len; + AtomicString m_id; +}; + +} //namespace + +#undef id + +#endif diff --git a/WebCore/dom/NamedMappedAttrMap.cpp b/WebCore/dom/NamedMappedAttrMap.cpp new file mode 100644 index 0000000..afa4d76 --- /dev/null +++ b/WebCore/dom/NamedMappedAttrMap.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2007 David Smith (catfish.man@gmail.com) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "NamedMappedAttrMap.h" + +#include "Document.h" +#include "Element.h" + +namespace WebCore { + +NamedMappedAttrMap::NamedMappedAttrMap(Element *e) + : NamedAttrMap(e) + , m_mappedAttributeCount(0) +{ +} + +void NamedMappedAttrMap::clearAttributes() +{ + m_classNames.clear(); + m_mappedAttributeCount = 0; + NamedAttrMap::clearAttributes(); +} + +bool NamedMappedAttrMap::isMappedAttributeMap() const +{ + return true; +} + +int NamedMappedAttrMap::declCount() const +{ + int result = 0; + for (unsigned i = 0; i < length(); i++) { + MappedAttribute* attr = attributeItem(i); + if (attr->decl()) + result++; + } + return result; +} + +bool NamedMappedAttrMap::mapsEquivalent(const NamedMappedAttrMap* otherMap) const +{ + // The # of decls must match. + if (declCount() != otherMap->declCount()) + return false; + + // The values for each decl must match. + for (unsigned i = 0; i < length(); i++) { + MappedAttribute* attr = attributeItem(i); + if (attr->decl()) { + Attribute* otherAttr = otherMap->getAttributeItem(attr->name()); + if (!otherAttr || (attr->value() != otherAttr->value())) + return false; + } + } + return true; +} + +void NamedMappedAttrMap::parseClassAttribute(const String& classStr) +{ + if (!element->hasClass()) { + m_classNames.clear(); + return; + } + + m_classNames.parseClassAttribute(classStr, element->document()->inCompatMode()); +} + + +} diff --git a/WebCore/dom/NamedMappedAttrMap.h b/WebCore/dom/NamedMappedAttrMap.h new file mode 100644 index 0000000..2889a34 --- /dev/null +++ b/WebCore/dom/NamedMappedAttrMap.h @@ -0,0 +1,71 @@ +/* + * 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 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2007 David Smith (catfish.man@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef NamedMappedAttrMap_h +#define NamedMappedAttrMap_h + +#include "ClassNames.h" +#include "MappedAttribute.h" +#include "NamedAttrMap.h" + +namespace WebCore { + +class NamedMappedAttrMap : public NamedAttrMap +{ +public: + NamedMappedAttrMap(Element *e); + + virtual void clearAttributes(); + + virtual bool isMappedAttributeMap() const; + + void parseClassAttribute(const String&); + + const ClassNames* getClassNames() const { return &m_classNames; } + + virtual bool hasMappedAttributes() const { return m_mappedAttributeCount > 0; } + void declRemoved() { m_mappedAttributeCount--; } + void declAdded() { m_mappedAttributeCount++; } + + bool mapsEquivalent(const NamedMappedAttrMap* otherMap) const; + int declCount() const; + + MappedAttribute* attributeItem(unsigned index) const + { return static_cast<MappedAttribute*>(NamedAttrMap::attributeItem(index)); } + MappedAttribute* getAttributeItem(const QualifiedName& name) const + { return static_cast<MappedAttribute*>(NamedAttrMap::getAttributeItem(name)); } + MappedAttribute* getAttributeItem(const String& name) const + { return static_cast<MappedAttribute*>(NamedAttrMap::getAttributeItem(name)); } + +private: + ClassNames m_classNames; + int m_mappedAttributeCount; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/NamedNodeMap.h b/WebCore/dom/NamedNodeMap.h new file mode 100644 index 0000000..cc70bea --- /dev/null +++ b/WebCore/dom/NamedNodeMap.h @@ -0,0 +1,68 @@ +/* + * 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) + * Copyright (C) 2004, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef NamedNodeMap_h +#define NamedNodeMap_h + +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class Node; +class QualifiedName; +class String; + +typedef int ExceptionCode; + +// Generic NamedNodeMap interface +// Other classes implement this for more specific situations e.g. attributes of an element. +class NamedNodeMap : public RefCounted<NamedNodeMap> { +public: + NamedNodeMap() : RefCounted<NamedNodeMap>(0) { } + virtual ~NamedNodeMap() { } + + virtual PassRefPtr<Node> getNamedItem(const String& name) const = 0; + virtual PassRefPtr<Node> removeNamedItem(const String& name, ExceptionCode&) = 0; + + virtual PassRefPtr<Node> getNamedItemNS(const String& namespaceURI, const String& localName) const = 0; + PassRefPtr<Node> setNamedItemNS(Node* arg, ExceptionCode& ec) { return setNamedItem(arg, ec); } + virtual PassRefPtr<Node> removeNamedItemNS(const String& namespaceURI, const String& localName, ExceptionCode&) = 0; + + // DOM methods & attributes for NamedNodeMap + virtual PassRefPtr<Node> getNamedItem(const QualifiedName& attrName) const = 0; + virtual PassRefPtr<Node> removeNamedItem(const QualifiedName& attrName, ExceptionCode&) = 0; + virtual PassRefPtr<Node> setNamedItem(Node*, ExceptionCode&) = 0; + + virtual PassRefPtr<Node> item(unsigned index) const = 0; + virtual unsigned length() const = 0; + + // Other methods (not part of DOM) + virtual bool isReadOnlyNode() { return false; } +}; + +} //namespace + +#endif diff --git a/WebCore/dom/NamedNodeMap.idl b/WebCore/dom/NamedNodeMap.idl new file mode 100644 index 0000000..3310ded --- /dev/null +++ b/WebCore/dom/NamedNodeMap.idl @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * 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. + */ + +module core { + + interface [ + GenerateConstructor, + HasIndexGetter, + HasNameGetter, + InterfaceUUID=08DAF7A4-4C32-4709-B72F-622721FF0FB8, + ImplementationUUID=A1CC9F5B-092D-4a04-96D9-D7718A8D6242 + ] NamedNodeMap { + + Node getNamedItem(in DOMString name); + + Node setNamedItem(in Node node) + raises(DOMException); + + Node removeNamedItem(in DOMString name) + raises(DOMException); + + Node item(in unsigned long index); + + readonly attribute unsigned long length; + + + // Introduced in DOM Level 2: + + [OldStyleObjC] Node getNamedItemNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString localName) + // FIXME: the implementation does take an exceptioncode parameter. + /*raises(DOMException)*/; + + Node setNamedItemNS(in Node node) + raises(DOMException); + + [OldStyleObjC] Node removeNamedItemNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString localName) + raises(DOMException); + + }; + +} diff --git a/WebCore/dom/Node.cpp b/WebCore/dom/Node.cpp new file mode 100644 index 0000000..8a52e0e --- /dev/null +++ b/WebCore/dom/Node.cpp @@ -0,0 +1,1656 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Trolltech ASA + * + * 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 "config.h" +#include "Node.h" + +#include "CSSParser.h" +#include "CSSRule.h" +#include "CSSRuleList.h" +#include "CSSSelector.h" +#include "CSSStyleRule.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "CString.h" +#include "ChildNodeList.h" +#include "ClassNodeList.h" +#include "DOMImplementation.h" +#include "Document.h" +#include "DynamicNodeList.h" +#include "Element.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "HTMLNames.h" +#include "HTMLNames.h" +#include "Logging.h" +#include "NameNodeList.h" +#include "NamedAttrMap.h" +#include "RenderObject.h" +#include "SelectorNodeList.h" +#include "TagNodeList.h" +#include "Text.h" +#include "XMLNames.h" +#include "htmlediting.h" +#include "kjs_binding.h" + +namespace WebCore { + +using namespace HTMLNames; + +typedef HashSet<DynamicNodeList*> NodeListSet; +struct NodeListsNodeData { + NodeListSet m_listsToNotify; + DynamicNodeList::Caches m_childNodeListCaches; + + typedef HashMap<String, DynamicNodeList::Caches*> CacheMap; + CacheMap m_classNodeListCaches; + CacheMap m_nameNodeListCaches; + + ~NodeListsNodeData() + { + deleteAllValues(m_classNodeListCaches); + deleteAllValues(m_nameNodeListCaches); + } +}; + +bool Node::isSupported(const String& feature, const String& version) +{ + return DOMImplementation::instance()->hasFeature(feature, version); +} + +#ifndef NDEBUG +WTFLogChannel LogWebCoreNodeLeaks = { 0x00000000, "", WTFLogChannelOn }; + +struct NodeCounter { + static unsigned count; + + ~NodeCounter() + { + if (count) + LOG(WebCoreNodeLeaks, "LEAK: %u Node\n", count); + } +}; +unsigned NodeCounter::count = 0; +static NodeCounter nodeCounter; + +static bool shouldIgnoreLeaks = false; +static HashSet<Node*> ignoreSet; +#endif + +void Node::startIgnoringLeaks() +{ +#ifndef NDEBUG + shouldIgnoreLeaks = true; +#endif +} + +void Node::stopIgnoringLeaks() +{ +#ifndef NDEBUG + shouldIgnoreLeaks = false; +#endif +} + +Node::Node(Document *doc) + : m_document(doc), + m_previous(0), + m_next(0), + m_renderer(0), + m_tabIndex(0), + m_hasId(false), + m_hasClass(false), + m_attached(false), + m_styleChange(NoStyleChange), + m_hasChangedChild(false), + m_inDocument(false), + m_isLink(false), + m_attrWasSpecifiedOrElementHasRareData(false), + m_focused(false), + m_active(false), + m_hovered(false), + m_inActiveChain(false), + m_inDetach(false), + m_dispatchingSimulatedEvent(false), + m_inSubtreeMark(false) +{ +#ifndef NDEBUG + if (shouldIgnoreLeaks) + ignoreSet.add(this); + else + ++NodeCounter::count; +#endif +} + +void Node::setDocument(Document* doc) +{ + if (inDocument() || m_document == doc) + return; + + willMoveToNewOwnerDocument(); + + { + KJS::JSLock lock; + ScriptInterpreter::updateDOMNodeDocument(this, m_document.get(), doc); + } + m_document = doc; + + didMoveToNewOwnerDocument(); +} + +Node::~Node() +{ +#ifndef NDEBUG + HashSet<Node*>::iterator it = ignoreSet.find(this); + if (it != ignoreSet.end()) + ignoreSet.remove(it); + else + --NodeCounter::count; +#endif + if (renderer()) + detach(); + + if (m_previous) + m_previous->setNextSibling(0); + if (m_next) + m_next->setPreviousSibling(0); +} + +String Node::nodeValue() const +{ + return String(); +} + +void Node::setNodeValue(const String& /*nodeValue*/, ExceptionCode& ec) +{ + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // by default nodeValue is null, so setting it has no effect +} + +PassRefPtr<NodeList> Node::childNodes() +{ + if (!m_nodeLists) + m_nodeLists.set(new NodeListsNodeData); + + return new ChildNodeList(this, &m_nodeLists->m_childNodeListCaches); +} + +Node* Node::virtualFirstChild() const +{ + return 0; +} + +Node* Node::virtualLastChild() const +{ + return 0; +} + +bool Node::virtualHasTagName(const QualifiedName&) const +{ + return false; +} + +Node *Node::lastDescendant() const +{ + Node *n = const_cast<Node *>(this); + while (n && n->lastChild()) + n = n->lastChild(); + return n; +} + +Node* Node::firstDescendant() const +{ + Node *n = const_cast<Node *>(this); + while (n && n->firstChild()) + n = n->firstChild(); + return n; +} + +bool Node::insertBefore(PassRefPtr<Node>, Node*, ExceptionCode& ec) +{ + ec = HIERARCHY_REQUEST_ERR; + return false; +} + +bool Node::replaceChild(PassRefPtr<Node>, Node*, ExceptionCode& ec) +{ + ec = HIERARCHY_REQUEST_ERR; + return false; +} + +bool Node::removeChild(Node*, ExceptionCode& ec) +{ + ec = NOT_FOUND_ERR; + return false; +} + +bool Node::appendChild(PassRefPtr<Node>, ExceptionCode& ec) +{ + ec = HIERARCHY_REQUEST_ERR; + return false; +} + +void Node::remove(ExceptionCode& ec) +{ + ref(); + if (Node *p = parentNode()) + p->removeChild(this, ec); + else + ec = HIERARCHY_REQUEST_ERR; + deref(); +} + +bool Node::hasChildNodes( ) const +{ + return false; +} + +void Node::normalize () +{ + ExceptionCode ec = 0; + Node *child = firstChild(); + + if (isElementNode()) { + // Normalize any attribute children we might have + Element *element = static_cast<Element *>(this); + NamedAttrMap *attrMap = element->attributes(); + + if (attrMap) { + unsigned numAttrs = attrMap->length(); + + for (unsigned i = 0; i < numAttrs; i++) { + Attribute *attribute = attrMap->attributeItem(i); + Attr *attr = attribute->attr(); + + if (attr) + attr->normalize(); + } + } + } + + // Recursively go through the subtree beneath us, normalizing all nodes. In the case + // where there are two adjacent text nodes, they are merged together + while (child) { + Node *nextChild = child->nextSibling(); + + if (nextChild && child->nodeType() == TEXT_NODE && nextChild->nodeType() == TEXT_NODE) { + // Current child and the next one are both text nodes... merge them + Text *currentText = static_cast<Text*>(child); + Text *nextText = static_cast<Text*>(nextChild); + + currentText->appendData(nextText->data(),ec); + if (ec) + return; + + nextChild->remove(ec); + if (ec) + return; + } + else { + child->normalize(); + child = nextChild; + } + } + + // Check if we have a single empty text node left and remove it if so + child = firstChild(); + if (child && !child->nextSibling() && child->isTextNode()) { + Text *text = static_cast<Text*>(child); + if (text->data().isEmpty()) + child->remove(ec); + } +} + +const AtomicString& Node::prefix() const +{ + // For nodes other than elements and attributes, the prefix is always null + return nullAtom; +} + +void Node::setPrefix(const AtomicString& /*prefix*/, ExceptionCode& ec) +{ + // The spec says that for nodes other than elements and attributes, prefix is always null. + // It does not say what to do when the user tries to set the prefix on another type of + // node, however Mozilla throws a NAMESPACE_ERR exception. + ec = NAMESPACE_ERR; +} + +const AtomicString& Node::localName() const +{ + return nullAtom; +} + +const AtomicString& Node::namespaceURI() const +{ + return nullAtom; +} + +ContainerNode* Node::addChild(PassRefPtr<Node>) +{ + return 0; +} + +bool Node::isContentEditable() const +{ + return parent() && parent()->isContentEditable(); +} + +bool Node::isContentRichlyEditable() const +{ + return parent() && parent()->isContentRichlyEditable(); +} + +bool Node::shouldUseInputMethod() const +{ + return isContentEditable(); +} + +IntRect Node::getRect() const +{ + int _x, _y; + if (renderer() && renderer()->absolutePosition(_x, _y)) + return IntRect( _x, _y, renderer()->width(), renderer()->height() + renderer()->borderTopExtra() + renderer()->borderBottomExtra()); + + return IntRect(); +} + +void Node::setChanged(StyleChangeType changeType) +{ + if ((changeType != NoStyleChange) && !attached()) // changed compared to what? + return; + + if (!(changeType == InlineStyleChange && m_styleChange == FullStyleChange)) + m_styleChange = changeType; + + if (m_styleChange != NoStyleChange) { + for (Node* p = parentNode(); p; p = p->parentNode()) + p->setHasChangedChild(true); + document()->setDocumentChanged(true); + } +} + +bool Node::isFocusable() const +{ + return false; +} + +bool Node::isKeyboardFocusable(KeyboardEvent*) const +{ + return isFocusable(); +} + +bool Node::isMouseFocusable() const +{ + return isFocusable(); +} + +unsigned Node::nodeIndex() const +{ + Node *_tempNode = previousSibling(); + unsigned count=0; + for( count=0; _tempNode; count++ ) + _tempNode = _tempNode->previousSibling(); + return count; +} + +void Node::registerDynamicNodeList(DynamicNodeList* list) +{ + if (!m_nodeLists) + m_nodeLists.set(new NodeListsNodeData); + else if (!m_document->hasNodeLists()) + // We haven't been receiving notifications while there were no registered lists, so the cache is invalid now. + m_nodeLists->m_childNodeListCaches.reset(); + + if (list->needsNotifications()) + m_nodeLists->m_listsToNotify.add(list); + m_document->addNodeList(); +} + +void Node::unregisterDynamicNodeList(DynamicNodeList* list) +{ + ASSERT(m_nodeLists); + m_document->removeNodeList(); + if (list->needsNotifications()) + m_nodeLists->m_listsToNotify.remove(list); +} + +void Node::notifyLocalNodeListsAttributeChanged() +{ + if (!m_nodeLists) + return; + + NodeListSet::iterator end = m_nodeLists->m_listsToNotify.end(); + for (NodeListSet::iterator i = m_nodeLists->m_listsToNotify.begin(); i != end; ++i) + (*i)->rootNodeAttributeChanged(); +} + +void Node::notifyNodeListsAttributeChanged() +{ + for (Node *n = this; n; n = n->parentNode()) + n->notifyLocalNodeListsAttributeChanged(); +} + +void Node::notifyLocalNodeListsChildrenChanged() +{ + if (!m_nodeLists) + return; + + m_nodeLists->m_childNodeListCaches.reset(); + + NodeListSet::iterator end = m_nodeLists->m_listsToNotify.end(); + for (NodeListSet::iterator i = m_nodeLists->m_listsToNotify.begin(); i != end; ++i) + (*i)->rootNodeChildrenChanged(); +} + +void Node::notifyNodeListsChildrenChanged() +{ + for (Node *n = this; n; n = n->parentNode()) + n->notifyLocalNodeListsChildrenChanged(); +} + +unsigned Node::childNodeCount() const +{ + return 0; +} + +Node *Node::childNode(unsigned /*index*/) const +{ + return 0; +} + +Node *Node::traverseNextNode(const Node *stayWithin) const +{ + if (firstChild()) + return firstChild(); + if (this == stayWithin) + return 0; + if (nextSibling()) + return nextSibling(); + const Node *n = this; + while (n && !n->nextSibling() && (!stayWithin || n->parentNode() != stayWithin)) + n = n->parentNode(); + if (n) + return n->nextSibling(); + return 0; +} + +Node *Node::traverseNextSibling(const Node *stayWithin) const +{ + if (this == stayWithin) + return 0; + if (nextSibling()) + return nextSibling(); + const Node *n = this; + while (n && !n->nextSibling() && (!stayWithin || n->parentNode() != stayWithin)) + n = n->parentNode(); + if (n) + return n->nextSibling(); + return 0; +} + +Node *Node::traversePreviousNode(const Node *stayWithin) const +{ + if (this == stayWithin) + return 0; + if (previousSibling()) { + Node *n = previousSibling(); + while (n->lastChild()) + n = n->lastChild(); + return n; + } + return parentNode(); +} + +Node *Node::traversePreviousNodePostOrder(const Node *stayWithin) const +{ + if (lastChild()) + return lastChild(); + if (this == stayWithin) + return 0; + if (previousSibling()) + return previousSibling(); + const Node *n = this; + while (n && !n->previousSibling() && (!stayWithin || n->parentNode() != stayWithin)) + n = n->parentNode(); + if (n) + return n->previousSibling(); + return 0; +} + +Node* Node::traversePreviousSiblingPostOrder(const Node* stayWithin) const +{ + if (this == stayWithin) + return 0; + if (previousSibling()) + return previousSibling(); + const Node *n = this; + while (n && !n->previousSibling() && (!stayWithin || n->parentNode() != stayWithin)) + n = n->parentNode(); + if (n) + return n->previousSibling(); + return 0; +} + +void Node::checkSetPrefix(const AtomicString &_prefix, ExceptionCode& ec) +{ + // Perform error checking as required by spec for setting Node.prefix. Used by + // Element::setPrefix() and Attr::setPrefix() + + // FIXME: Implement support for INVALID_CHARACTER_ERR: Raised if the specified prefix contains an illegal character. + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // FIXME: Implement NAMESPACE_ERR: - Raised if the specified prefix is malformed + // We have to comment this out, since it's used for attributes and tag names, and we've only + // switched one over. + /* + // - if the namespaceURI of this node is null, + // - if the specified prefix is "xml" and the namespaceURI of this node is different from + // "http://www.w3.org/XML/1998/namespace", + // - if this node is an attribute and the specified prefix is "xmlns" and + // the namespaceURI of this node is different from "http://www.w3.org/2000/xmlns/", + // - or if this node is an attribute and the qualifiedName of this node is "xmlns" [Namespaces]. + if ((namespacePart(id()) == noNamespace && id() > ID_LAST_TAG) || + (_prefix == "xml" && String(document()->namespaceURI(id())) != "http://www.w3.org/XML/1998/namespace")) { + ec = NAMESPACE_ERR; + return; + }*/ +} + +bool Node::canReplaceChild(Node* newChild, Node* oldChild) +{ + if (newChild->nodeType() != DOCUMENT_FRAGMENT_NODE) { + if (!childTypeAllowed(newChild->nodeType())) + return false; + } + else { + for (Node *n = newChild->firstChild(); n; n = n->nextSibling()) { + if (!childTypeAllowed(n->nodeType())) + return false; + } + } + + return true; +} + +void Node::checkReplaceChild(Node* newChild, Node* oldChild, ExceptionCode& ec) +{ + // Perform error checking as required by spec for adding a new child. Used by + // appendChild(), replaceChild() and insertBefore() + + // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null + if (!newChild) { + ec = NOT_FOUND_ERR; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + bool shouldAdoptChild = false; + + // WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that + // created this node. + // We assume that if newChild is a DocumentFragment, all children are created from the same document + // as the fragment itself (otherwise they could not have been added as children) + if (newChild->document() != document()) { + // but if the child is not in a document yet then loosen the + // restriction, so that e.g. creating an element with the Option() + // constructor and then adding it to a different document works, + // as it does in Mozilla and Mac IE. + if (!newChild->inDocument()) { + shouldAdoptChild = true; + } else { + ec = WRONG_DOCUMENT_ERR; + return; + } + } + + // HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the + // newChild node, or if the node to append is one of this node's ancestors. + + // check for ancestor/same node + if (newChild == this || isDescendantOf(newChild)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + if (!canReplaceChild(newChild, oldChild)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + // change the document pointer of newChild and all of its children to be the new document + if (shouldAdoptChild) + for (Node* node = newChild; node; node = node->traverseNextNode(newChild)) + node->setDocument(document()); +} + +void Node::checkAddChild(Node *newChild, ExceptionCode& ec) +{ + // Perform error checking as required by spec for adding a new child. Used by + // appendChild(), replaceChild() and insertBefore() + + // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null + if (!newChild) { + ec = NOT_FOUND_ERR; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + bool shouldAdoptChild = false; + + // WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that + // created this node. + // We assume that if newChild is a DocumentFragment, all children are created from the same document + // as the fragment itself (otherwise they could not have been added as children) + if (newChild->document() != document()) { + // but if the child is not in a document yet then loosen the + // restriction, so that e.g. creating an element with the Option() + // constructor and then adding it to a different document works, + // as it does in Mozilla and Mac IE. + if (!newChild->inDocument()) { + shouldAdoptChild = true; + } else { + ec = WRONG_DOCUMENT_ERR; + return; + } + } + + // HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the + // newChild node, or if the node to append is one of this node's ancestors. + + // check for ancestor/same node + if (newChild == this || isDescendantOf(newChild)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + if (newChild->nodeType() != DOCUMENT_FRAGMENT_NODE) { + if (!childTypeAllowed(newChild->nodeType())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + else { + for (Node *n = newChild->firstChild(); n; n = n->nextSibling()) { + if (!childTypeAllowed(n->nodeType())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + } + + // change the document pointer of newChild and all of its children to be the new document + if (shouldAdoptChild) + for (Node* node = newChild; node; node = node->traverseNextNode(newChild)) + node->setDocument(document()); +} + +bool Node::isDescendantOf(const Node *other) const +{ + // Return true if other is an ancestor of this, otherwise false + if (!other) + return false; + for (const Node *n = parentNode(); n; n = n->parentNode()) { + if (n == other) + return true; + } + return false; +} + +bool Node::childAllowed(Node* newChild) +{ + return childTypeAllowed(newChild->nodeType()); +} + +Node::StyleChange Node::diff( RenderStyle *s1, RenderStyle *s2 ) const +{ + // FIXME: The behavior of this function is just totally wrong. It doesn't handle + // explicit inheritance of non-inherited properties and so you end up not re-resolving + // style in cases where you need to. + StyleChange ch = NoInherit; + EDisplay display1 = s1 ? s1->display() : NONE; + bool fl1 = s1 && s1->hasPseudoStyle(RenderStyle::FIRST_LETTER); + EDisplay display2 = s2 ? s2->display() : NONE; + bool fl2 = s2 && s2->hasPseudoStyle(RenderStyle::FIRST_LETTER); + + if (display1 != display2 || fl1 != fl2 || (s1 && s2 && !s1->contentDataEquivalent(s2))) + ch = Detach; + else if (!s1 || !s2) + ch = Inherit; + else if (*s1 == *s2) + ch = NoChange; + else if (s1->inheritedNotEqual(s2)) + ch = Inherit; + + // If the pseudoStyles have changed, we want any StyleChange that is not NoChange + // because setStyle will do the right thing with anything else. + if (ch == NoChange && s1->hasPseudoStyle(RenderStyle::BEFORE)) { + RenderStyle* ps2 = s2->getPseudoStyle(RenderStyle::BEFORE); + if (!ps2) + ch = NoInherit; + else { + RenderStyle* ps1 = s1->getPseudoStyle(RenderStyle::BEFORE); + ch = ps1 && *ps1 == *ps2 ? NoChange : NoInherit; + } + } + if (ch == NoChange && s1->hasPseudoStyle(RenderStyle::AFTER)) { + RenderStyle* ps2 = s2->getPseudoStyle(RenderStyle::AFTER); + if (!ps2) + ch = NoInherit; + else { + RenderStyle* ps1 = s1->getPseudoStyle(RenderStyle::AFTER); + ch = ps2 && *ps1 == *ps2 ? NoChange : NoInherit; + } + } + + return ch; +} + +void Node::attach() +{ + ASSERT(!attached()); + ASSERT(!renderer() || (renderer()->style() && renderer()->parent())); + + // If this node got a renderer it may be the previousRenderer() of sibling text nodes and thus affect the + // result of Text::rendererIsNeeded() for those nodes. + if (renderer()) { + for (Node* next = nextSibling(); next; next = next->nextSibling()) { + if (next->renderer()) + break; + if (!next->attached()) + break; // Assume this means none of the following siblings are attached. + if (next->isTextNode()) + next->createRendererIfNeeded(); + } + } + + m_attached = true; +} + +void Node::willRemove() +{ +} + +void Node::detach() +{ + m_inDetach = true; + + if (renderer()) + renderer()->destroy(); + setRenderer(0); + + Document* doc = document(); + if (m_hovered) + doc->hoveredNodeDetached(this); + if (m_inActiveChain) + doc->activeChainNodeDetached(this); + + m_active = false; + m_hovered = false; + m_inActiveChain = false; + m_attached = false; + m_inDetach = false; +} + +void Node::insertedIntoDocument() +{ + setInDocument(true); + insertedIntoTree(false); +} + +void Node::removedFromDocument() +{ + if (m_document && m_document->getCSSTarget() == this) + m_document->setCSSTarget(0); + + setInDocument(false); + removedFromTree(false); +} + +bool Node::isReadOnlyNode() +{ + // Entity & Entity Reference nodes and their descendants are read-only + Node *n = this; + while (n) { + if (n->nodeType() == ENTITY_NODE || n->nodeType() == ENTITY_REFERENCE_NODE) + return true; + n = n->parentNode(); + } + return false; +} + +Node *Node::previousEditable() const +{ + Node *node = previousLeafNode(); + while (node) { + if (node->isContentEditable()) + return node; + node = node->previousLeafNode(); + } + return 0; +} + +// Offset specifies the child node to start at. If it is past +// the last child, it specifies to start at next sibling. +Node *Node::nextEditable(int offset) const +{ + ASSERT(offset>=0); + Node *node; + if (hasChildNodes()) + node = (offset >= (int)childNodeCount()) ? nextSibling() : childNode(offset)->nextLeafNode(); + else + node = nextLeafNode(); + while (node) { + if (node->isContentEditable()) + return node; + node = node->nextLeafNode(); + } + return 0; +} + +Node *Node::nextEditable() const +{ + Node *node = nextLeafNode(); + while (node) { + if (node->isContentEditable()) + return node; + node = node->nextLeafNode(); + } + return 0; +} + +RenderObject * Node::previousRenderer() +{ + for (Node *n = previousSibling(); n; n = n->previousSibling()) { + if (n->renderer()) + return n->renderer(); + } + return 0; +} + +RenderObject * Node::nextRenderer() +{ + // Avoid an O(n^2) problem with this function by not checking for nextRenderer() when the parent element hasn't even + // been attached yet. + if (parent() && !parent()->attached()) + return 0; + + for (Node *n = nextSibling(); n; n = n->nextSibling()) { + if (n->renderer()) + return n->renderer(); + } + return 0; +} + +// FIXME: This code is used by editing. Seems like it could move over there and not pollute Node. +Node *Node::previousNodeConsideringAtomicNodes() const +{ + if (previousSibling()) { + Node *n = previousSibling(); + while (!isAtomicNode(n) && n->lastChild()) + n = n->lastChild(); + return n; + } + else if (parentNode()) { + return parentNode(); + } + else { + return 0; + } +} + +Node *Node::nextNodeConsideringAtomicNodes() const +{ + if (!isAtomicNode(this) && firstChild()) + return firstChild(); + if (nextSibling()) + return nextSibling(); + const Node *n = this; + while (n && !n->nextSibling()) + n = n->parentNode(); + if (n) + return n->nextSibling(); + return 0; +} + +Node *Node::previousLeafNode() const +{ + Node *node = previousNodeConsideringAtomicNodes(); + while (node) { + if (isAtomicNode(node)) + return node; + node = node->previousNodeConsideringAtomicNodes(); + } + return 0; +} + +Node *Node::nextLeafNode() const +{ + Node *node = nextNodeConsideringAtomicNodes(); + while (node) { + if (isAtomicNode(node)) + return node; + node = node->nextNodeConsideringAtomicNodes(); + } + return 0; +} + +void Node::createRendererIfNeeded() +{ + if (!document()->shouldCreateRenderers()) + return; + + ASSERT(!renderer()); + + Node *parent = parentNode(); + ASSERT(parent); + + RenderObject *parentRenderer = parent->renderer(); + if (parentRenderer && parentRenderer->canHaveChildren() +#if ENABLE(SVG) + && parent->childShouldCreateRenderer(this) +#endif + ) { + RenderStyle* style = styleForRenderer(parentRenderer); + if (rendererIsNeeded(style)) { + if (RenderObject* r = createRenderer(document()->renderArena(), style)) { + if (!parentRenderer->isChildAllowed(r, style)) + r->destroy(); + else { + setRenderer(r); + renderer()->setAnimatableStyle(style); + parentRenderer->addChild(renderer(), nextRenderer()); + } + } + } + style->deref(document()->renderArena()); + } +} + +RenderStyle *Node::styleForRenderer(RenderObject *parent) +{ + RenderStyle *style = parent->style(); + style->ref(); + return style; +} + +bool Node::rendererIsNeeded(RenderStyle *style) +{ + return (document()->documentElement() == this) || (style->display() != NONE); +} + +RenderObject *Node::createRenderer(RenderArena *arena, RenderStyle *style) +{ + ASSERT(false); + return 0; +} + +RenderStyle* Node::renderStyle() const +{ + return m_renderer ? m_renderer->style() : 0; +} + +void Node::setRenderStyle(RenderStyle* s) +{ + if (m_renderer) + m_renderer->setAnimatableStyle(s); +} + +RenderStyle* Node::computedStyle() +{ + return parent() ? parent()->computedStyle() : 0; +} + +int Node::maxCharacterOffset() const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +// FIXME: Shouldn't these functions be in the editing code? Code that asks questions about HTML in the core DOM class +// is obviously misplaced. +bool Node::canStartSelection() const +{ + if (isContentEditable()) + return true; + return parent() ? parent()->canStartSelection() : true; +} + +Node* Node::shadowAncestorNode() +{ +#if ENABLE(SVG) + // SVG elements living in a shadow tree only occour when <use> created them. + // For these cases we do NOT want to return the shadowParentNode() here + // but the actual shadow tree element - as main difference to the HTML forms + // shadow tree concept. (This function _could_ be made virtual - opinions?) + if (isSVGElement()) + return this; +#endif + + Node *n = this; + while (n) { + if (n->isShadowNode()) + return n->shadowParentNode(); + n = n->parentNode(); + } + return this; +} + +bool Node::isBlockFlow() const +{ + return renderer() && renderer()->isBlockFlow(); +} + +bool Node::isBlockFlowOrBlockTable() const +{ + return renderer() && (renderer()->isBlockFlow() || renderer()->isTable() && !renderer()->isInline()); +} + +bool Node::isEditableBlock() const +{ + return isContentEditable() && isBlockFlow(); +} + +Element *Node::enclosingBlockFlowOrTableElement() const +{ + Node *n = const_cast<Node *>(this); + if (isBlockFlowOrBlockTable()) + return static_cast<Element *>(n); + + while (1) { + n = n->parentNode(); + if (!n) + break; + if (n->isBlockFlowOrBlockTable() || n->hasTagName(bodyTag)) + return static_cast<Element *>(n); + } + return 0; +} + +Element *Node::enclosingBlockFlowElement() const +{ + Node *n = const_cast<Node *>(this); + if (isBlockFlow()) + return static_cast<Element *>(n); + + while (1) { + n = n->parentNode(); + if (!n) + break; + if (n->isBlockFlow() || n->hasTagName(bodyTag)) + return static_cast<Element *>(n); + } + return 0; +} + +Element *Node::enclosingInlineElement() const +{ + Node *n = const_cast<Node *>(this); + Node *p; + + while (1) { + p = n->parentNode(); + if (!p || p->isBlockFlow() || p->hasTagName(bodyTag)) + return static_cast<Element *>(n); + // Also stop if any previous sibling is a block + for (Node *sibling = n->previousSibling(); sibling; sibling = sibling->previousSibling()) { + if (sibling->isBlockFlow()) + return static_cast<Element *>(n); + } + n = p; + } + ASSERT_NOT_REACHED(); + return 0; +} + +Element* Node::rootEditableElement() const +{ + Element* result = 0; + for (Node* n = const_cast<Node*>(this); n && n->isContentEditable(); n = n->parentNode()) { + if (n->isElementNode()) + result = static_cast<Element*>(n); + if (n->hasTagName(bodyTag)) + break; + } + return result; +} + +bool Node::inSameContainingBlockFlowElement(Node *n) +{ + return n ? enclosingBlockFlowElement() == n->enclosingBlockFlowElement() : false; +} + +// FIXME: End of obviously misplaced HTML editing functions. Try to move these out of Node. + +PassRefPtr<NodeList> Node::getElementsByTagName(const String& name) +{ + return getElementsByTagNameNS("*", name); +} + +PassRefPtr<NodeList> Node::getElementsByTagNameNS(const String& namespaceURI, const String& localName) +{ + if (localName.isNull()) + return 0; + + String name = localName; + if (document()->isHTMLDocument()) + name = localName.lower(); + return new TagNodeList(this, namespaceURI.isEmpty() ? nullAtom : AtomicString(namespaceURI), name); +} + +PassRefPtr<NodeList> Node::getElementsByName(const String& elementName) +{ + if (!m_nodeLists) + m_nodeLists.set(new NodeListsNodeData); + + pair<NodeListsNodeData::CacheMap::iterator, bool> result = m_nodeLists->m_nameNodeListCaches.add(elementName, 0); + if (result.second) + result.first->second = new DynamicNodeList::Caches; + + return new NameNodeList(this, elementName, result.first->second); +} + +PassRefPtr<NodeList> Node::getElementsByClassName(const String& classNames) +{ + if (!m_nodeLists) + m_nodeLists.set(new NodeListsNodeData); + + pair<NodeListsNodeData::CacheMap::iterator, bool> result = m_nodeLists->m_classNodeListCaches.add(classNames, 0); + if (result.second) + result.first->second = new DynamicNodeList::Caches; + + return new ClassNodeList(this, classNames, result.first->second); +} + +PassRefPtr<Element> Node::querySelector(const String& selectors, ExceptionCode& ec) +{ + if (selectors.isNull() || selectors.isEmpty()) { + ec = SYNTAX_ERR; + return 0; + } + CSSStyleSheet tempStyleSheet(document()); + CSSParser p(true); + RefPtr<CSSRule> rule = p.parseRule(&tempStyleSheet, selectors + "{}"); + if (!rule || !rule->isStyleRule()) { + ec = SYNTAX_ERR; + return 0; + } + + CSSStyleSelector* styleSelector = document()->styleSelector(); + CSSSelector* querySelector = static_cast<CSSStyleRule*>(rule.get())->selector(); + + // FIXME: We can speed this up by implementing caching similar to the one use by getElementById + for (Node* n = firstChild(); n; n = n->traverseNextNode(this)) { + if (n->isElementNode()) { + Element* element = static_cast<Element*>(n); + styleSelector->initElementAndPseudoState(element); + for (CSSSelector* selector = querySelector; selector; selector = selector->next()) { + if (styleSelector->checkSelector(selector)) + return element; + } + } + } + + return 0; +} + +PassRefPtr<NodeList> Node::querySelectorAll(const String& selectors, ExceptionCode& ec) +{ + if (selectors.isNull() || selectors.isEmpty()) { + ec = SYNTAX_ERR; + return 0; + } + CSSStyleSheet tempStyleSheet(document()); + CSSParser p(true); + RefPtr<CSSRule> rule = p.parseRule(&tempStyleSheet, selectors + "{}"); + if (!rule || !rule->isStyleRule()) { + ec = SYNTAX_ERR; + return 0; + } + + SelectorNodeList* resultList = new SelectorNodeList(this, static_cast<CSSStyleRule*>(rule.get())->selector()); + + return resultList; +} + +Document *Node::ownerDocument() const +{ + Document *doc = document(); + return doc == this ? 0 : doc; +} + +bool Node::hasAttributes() const +{ + return false; +} + +NamedAttrMap* Node::attributes() const +{ + return 0; +} + +KURL Node::baseURI() const +{ + return parentNode() ? parentNode()->baseURI() : KURL(); +} + +bool Node::isEqualNode(Node *other) const +{ + if (!other) + return false; + + if (nodeType() != other->nodeType()) + return false; + + if (nodeName() != other->nodeName()) + return false; + + if (localName() != other->localName()) + return false; + + if (namespaceURI() != other->namespaceURI()) + return false; + + if (prefix() != other->prefix()) + return false; + + if (nodeValue() != other->nodeValue()) + return false; + + NamedAttrMap *attrs = attributes(); + NamedAttrMap *otherAttrs = other->attributes(); + + if (!attrs && otherAttrs) + return false; + + if (attrs && !attrs->mapsEquivalent(otherAttrs)) + return false; + + Node *child = firstChild(); + Node *otherChild = other->firstChild(); + + while (child) { + if (!child->isEqualNode(otherChild)) + return false; + + child = child->nextSibling(); + otherChild = otherChild->nextSibling(); + } + + if (otherChild) + return false; + + // FIXME: For DocumentType nodes we should check equality on + // the entities and notations NamedNodeMaps as well. + + return true; +} + +bool Node::isDefaultNamespace(const String &namespaceURI) const +{ + // Implemented according to + // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#isDefaultNamespaceAlgo + + switch (nodeType()) { + case ELEMENT_NODE: { + const Element *elem = static_cast<const Element *>(this); + + if (elem->prefix().isNull()) + return elem->namespaceURI() == namespaceURI; + + if (elem->hasAttributes()) { + NamedAttrMap *attrs = elem->attributes(); + + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute *attr = attrs->attributeItem(i); + + if (attr->localName() == "xmlns") + return attr->value() == namespaceURI; + } + } + + if (Element* ancestor = ancestorElement()) + return ancestor->isDefaultNamespace(namespaceURI); + + return false; + } + case DOCUMENT_NODE: + return static_cast <const Document *>(this)->documentElement()->isDefaultNamespace(namespaceURI); + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_TYPE_NODE: + case DOCUMENT_FRAGMENT_NODE: + return false; + case ATTRIBUTE_NODE: { + const Attr *attr = static_cast<const Attr *>(this); + if (attr->ownerElement()) + return attr->ownerElement()->isDefaultNamespace(namespaceURI); + return false; + } + default: + if (Element* ancestor = ancestorElement()) + return ancestor->isDefaultNamespace(namespaceURI); + return false; + } +} + +String Node::lookupPrefix(const String &namespaceURI) const +{ + // Implemented according to + // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespacePrefixAlgo + + if (namespaceURI.isEmpty()) + return String(); + + switch (nodeType()) { + case ELEMENT_NODE: + return lookupNamespacePrefix(namespaceURI, static_cast<const Element *>(this)); + case DOCUMENT_NODE: + return static_cast<const Document *>(this)->documentElement()->lookupPrefix(namespaceURI); + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_FRAGMENT_NODE: + case DOCUMENT_TYPE_NODE: + return String(); + case ATTRIBUTE_NODE: { + const Attr *attr = static_cast<const Attr *>(this); + if (attr->ownerElement()) + return attr->ownerElement()->lookupPrefix(namespaceURI); + return String(); + } + default: + if (Element* ancestor = ancestorElement()) + return ancestor->lookupPrefix(namespaceURI); + return String(); + } +} + +String Node::lookupNamespaceURI(const String &prefix) const +{ + // Implemented according to + // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespaceURIAlgo + + if (!prefix.isNull() && prefix.isEmpty()) + return String(); + + switch (nodeType()) { + case ELEMENT_NODE: { + const Element *elem = static_cast<const Element *>(this); + + if (!elem->namespaceURI().isNull() && elem->prefix() == prefix) + return elem->namespaceURI(); + + if (elem->hasAttributes()) { + NamedAttrMap *attrs = elem->attributes(); + + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute *attr = attrs->attributeItem(i); + + if (attr->prefix() == "xmlns" && attr->localName() == prefix) { + if (!attr->value().isEmpty()) + return attr->value(); + + return String(); + } else if (attr->localName() == "xmlns" && prefix.isNull()) { + if (!attr->value().isEmpty()) + return attr->value(); + + return String(); + } + } + } + if (Element* ancestor = ancestorElement()) + return ancestor->lookupNamespaceURI(prefix); + return String(); + } + case DOCUMENT_NODE: + return static_cast<const Document *>(this)->documentElement()->lookupNamespaceURI(prefix); + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_TYPE_NODE: + case DOCUMENT_FRAGMENT_NODE: + return String(); + case ATTRIBUTE_NODE: { + const Attr *attr = static_cast<const Attr *>(this); + + if (attr->ownerElement()) + return attr->ownerElement()->lookupNamespaceURI(prefix); + else + return String(); + } + default: + if (Element* ancestor = ancestorElement()) + return ancestor->lookupNamespaceURI(prefix); + return String(); + } +} + +String Node::lookupNamespacePrefix(const String &_namespaceURI, const Element *originalElement) const +{ + if (_namespaceURI.isNull()) + return String(); + + if (originalElement->lookupNamespaceURI(prefix()) == _namespaceURI) + return prefix(); + + if (hasAttributes()) { + NamedAttrMap *attrs = attributes(); + + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute *attr = attrs->attributeItem(i); + + if (attr->prefix() == "xmlns" && + attr->value() == _namespaceURI && + originalElement->lookupNamespaceURI(attr->localName()) == _namespaceURI) + return attr->localName(); + } + } + + if (Element* ancestor = ancestorElement()) + return ancestor->lookupNamespacePrefix(_namespaceURI, originalElement); + return String(); +} + +String Node::textContent(bool convertBRsToNewlines) const +{ + switch (nodeType()) { + case TEXT_NODE: + case CDATA_SECTION_NODE: + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + return nodeValue(); + + case ELEMENT_NODE: + if (hasTagName(brTag) && + convertBRsToNewlines) + return "\n"; + case ATTRIBUTE_NODE: + case ENTITY_NODE: + case ENTITY_REFERENCE_NODE: + case DOCUMENT_FRAGMENT_NODE: { + String s = ""; + + for (Node *child = firstChild(); child; child = child->nextSibling()) { + if (child->nodeType() == COMMENT_NODE || child->nodeType() == PROCESSING_INSTRUCTION_NODE) + continue; + + s += child->textContent(convertBRsToNewlines); + } + + return s; + } + + case DOCUMENT_NODE: + case DOCUMENT_TYPE_NODE: + case NOTATION_NODE: + default: + return String(); + } +} + +void Node::setTextContent(const String &text, ExceptionCode& ec) +{ + switch (nodeType()) { + case TEXT_NODE: + case CDATA_SECTION_NODE: + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + setNodeValue(text, ec); + break; + case ELEMENT_NODE: + case ATTRIBUTE_NODE: + case ENTITY_NODE: + case ENTITY_REFERENCE_NODE: + case DOCUMENT_FRAGMENT_NODE: { + ContainerNode *container = static_cast<ContainerNode *>(this); + + container->removeChildren(); + + if (!text.isEmpty()) + appendChild(document()->createTextNode(text), ec); + break; + } + case DOCUMENT_NODE: + case DOCUMENT_TYPE_NODE: + case NOTATION_NODE: + default: + // Do nothing + break; + } +} + +Element* Node::ancestorElement() const +{ + // In theory, there can be EntityReference nodes between elements, but this is currently not supported. + for (Node* n = parentNode(); n; n = n->parentNode()) { + if (n->isElementNode()) + return static_cast<Element*>(n); + } + return 0; +} + +bool Node::offsetInCharacters() const +{ + return false; +} + +#ifndef NDEBUG + +static void appendAttributeDesc(const Node* node, String& string, const QualifiedName& name, const char* attrDesc) +{ + if (node->isElementNode()) { + String attr = static_cast<const Element*>(node)->getAttribute(name); + if (!attr.isEmpty()) { + string += attrDesc; + string += attr; + } + } +} + +void Node::showNode(const char* prefix) const +{ + if (!prefix) + prefix = ""; + if (isTextNode()) { + String value = nodeValue(); + value.replace('\\', "\\\\"); + value.replace('\n', "\\n"); + fprintf(stderr, "%s%s\t%p \"%s\"\n", prefix, nodeName().utf8().data(), this, value.utf8().data()); + } else { + String attrs = ""; + appendAttributeDesc(this, attrs, classAttr, " CLASS="); + appendAttributeDesc(this, attrs, styleAttr, " STYLE="); + fprintf(stderr, "%s%s\t%p%s\n", prefix, nodeName().utf8().data(), this, attrs.utf8().data()); + } +} + +void Node::showTreeForThis() const +{ + showTreeAndMark(this, "*"); +} + +void Node::showTreeAndMark(const Node* markedNode1, const char* markedLabel1, const Node* markedNode2, const char * markedLabel2) const +{ + const Node* rootNode; + const Node* node = this; + while (node->parentNode() && !node->hasTagName(bodyTag)) + node = node->parentNode(); + rootNode = node; + + for (node = rootNode; node; node = node->traverseNextNode()) { + if (node == markedNode1) + fprintf(stderr, "%s", markedLabel1); + if (node == markedNode2) + fprintf(stderr, "%s", markedLabel2); + + for (const Node* tmpNode = node; tmpNode && tmpNode != rootNode; tmpNode = tmpNode->parentNode()) + fprintf(stderr, "\t"); + node->showNode(); + } +} + +void Node::formatForDebugger(char* buffer, unsigned length) const +{ + String result; + String s; + + s = nodeName(); + if (s.length() == 0) + result += "<none>"; + else + result += s; + + strncpy(buffer, result.utf8().data(), length - 1); +} + +#endif + +} + +#ifndef NDEBUG + +void showTree(const WebCore::Node* node) +{ + if (node) + node->showTreeForThis(); +} + +#endif diff --git a/WebCore/dom/Node.h b/WebCore/dom/Node.h new file mode 100644 index 0000000..3abdfc0 --- /dev/null +++ b/WebCore/dom/Node.h @@ -0,0 +1,514 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef Node_h +#define Node_h + +#include "DocPtr.h" +#include "PlatformString.h" +#include "TreeShared.h" +#include <wtf/Assertions.h> +#include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class AtomicString; +class ContainerNode; +class Document; +class DynamicNodeList; +class Element; +class Event; +class EventListener; +class IntRect; +class KeyboardEvent; +class KURL; +class NamedAttrMap; +class NodeList; +class PlatformKeyboardEvent; +class PlatformMouseEvent; +class PlatformWheelEvent; +class QualifiedName; +class RegisteredEventListener; +class RenderArena; +class RenderObject; +class RenderStyle; + +struct NodeListsNodeData; + +typedef int ExceptionCode; + +enum StyleChangeType { NoStyleChange, InlineStyleChange, FullStyleChange }; + +// this class implements nodes, which can have a parent but no children: +class Node : public TreeShared<Node> { + friend class Document; +public: + enum NodeType { + ELEMENT_NODE = 1, + ATTRIBUTE_NODE = 2, + TEXT_NODE = 3, + CDATA_SECTION_NODE = 4, + ENTITY_REFERENCE_NODE = 5, + ENTITY_NODE = 6, + PROCESSING_INSTRUCTION_NODE = 7, + COMMENT_NODE = 8, + DOCUMENT_NODE = 9, + DOCUMENT_TYPE_NODE = 10, + DOCUMENT_FRAGMENT_NODE = 11, + NOTATION_NODE = 12, + XPATH_NAMESPACE_NODE = 13 + }; + + static bool isSupported(const String& feature, const String& version); + + static void startIgnoringLeaks(); + static void stopIgnoringLeaks(); + + Node(Document*); + virtual ~Node(); + + // DOM methods & attributes for Node + + bool hasTagName(const QualifiedName& name) const { return virtualHasTagName(name); } + virtual String nodeName() const = 0; + virtual String nodeValue() const; + virtual void setNodeValue(const String&, ExceptionCode&); + virtual NodeType nodeType() const = 0; + Node* parentNode() const { return parent(); } + Node* parentElement() const { return parent(); } // IE extension + Node* previousSibling() const { return m_previous; } + Node* nextSibling() const { return m_next; } + virtual PassRefPtr<NodeList> childNodes(); + Node* firstChild() const { return virtualFirstChild(); } + Node* lastChild() const { return virtualLastChild(); } + virtual bool hasAttributes() const; + virtual NamedAttrMap* attributes() const; + + virtual KURL baseURI() const; + + // These should all actually return a node, but this is only important for language bindings, + // which will already know and hold a ref on the right node to return. Returning bool allows + // these methods to be more efficient since they don't need to return a ref + virtual bool insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode&); + virtual bool replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode&); + virtual bool removeChild(Node* child, ExceptionCode&); + virtual bool appendChild(PassRefPtr<Node> newChild, ExceptionCode&); + + virtual void remove(ExceptionCode&); + virtual bool hasChildNodes() const; + virtual PassRefPtr<Node> cloneNode(bool deep) = 0; + virtual const AtomicString& localName() const; + virtual const AtomicString& namespaceURI() const; + virtual const AtomicString& prefix() const; + virtual void setPrefix(const AtomicString&, ExceptionCode&); + void normalize(); + + bool isSameNode(Node* other) const { return this == other; } + bool isEqualNode(Node*) const; + bool isDefaultNamespace(const String& namespaceURI) const; + String lookupPrefix(const String& namespaceURI) const; + String lookupNamespaceURI(const String& prefix) const; + String lookupNamespacePrefix(const String& namespaceURI, const Element* originalElement) const; + + String textContent(bool convertBRsToNewlines = false) const; + void setTextContent(const String&, ExceptionCode&); + + Node* lastDescendant() const; + Node* firstDescendant() const; + + // Other methods (not part of DOM) + + virtual bool isElementNode() const { return false; } + virtual bool isHTMLElement() const { return false; } + +#if ENABLE(SVG) + virtual +#endif + bool isSVGElement() const { return false; } + + virtual bool isStyledElement() const { return false; } + virtual bool isFrameOwnerElement() const { return false; } + virtual bool isAttributeNode() const { return false; } + virtual bool isTextNode() const { return false; } + virtual bool isCommentNode() const { return false; } + virtual bool isCharacterDataNode() const { return false; } + virtual bool isDocumentNode() const { return false; } + virtual bool isEventTargetNode() const { return false; } + virtual bool isShadowNode() const { return false; } + virtual Node* shadowParentNode() { return 0; } + Node* shadowAncestorNode(); + + // The node's parent for the purpose of event capture and bubbling. + virtual Node* eventParentNode() { return parentNode(); } + + bool isBlockFlow() const; + bool isBlockFlowOrBlockTable() const; + + // Used by <form> elements to indicate a malformed state of some kind, typically + // used to keep from applying the bottom margin of the form. + virtual bool isMalformed() { return false; } + virtual void setMalformed(bool malformed) { } + + // These low-level calls give the caller responsibility for maintaining the integrity of the tree. + void setPreviousSibling(Node* previous) { m_previous = previous; } + void setNextSibling(Node* next) { m_next = next; } + + // FIXME: These two functions belong in editing -- "atomic node" is an editing concept. + Node* previousNodeConsideringAtomicNodes() const; + Node* nextNodeConsideringAtomicNodes() const; + + /** (Not part of the official DOM) + * Returns the next leaf node. + * + * Using this function delivers leaf nodes as if the whole DOM tree were a linear chain of its leaf nodes. + * @return next leaf node or 0 if there are no more. + */ + Node* nextLeafNode() const; + + /** (Not part of the official DOM) + * Returns the previous leaf node. + * + * Using this function delivers leaf nodes as if the whole DOM tree were a linear chain of its leaf nodes. + * @return previous leaf node or 0 if there are no more. + */ + Node* previousLeafNode() const; + + bool isEditableBlock() const; + Element* enclosingBlockFlowElement() const; + Element* enclosingBlockFlowOrTableElement() const; + Element* enclosingInlineElement() const; + Element* rootEditableElement() const; + + bool inSameContainingBlockFlowElement(Node*); + + // Used by the parser. Checks against the DTD, unlike DOM operations like appendChild(). + // Also does not dispatch DOM mutation events. + // Returns the appropriate container node for future insertions as you parse, or 0 for failure. + virtual ContainerNode* addChild(PassRefPtr<Node>); + + // Called by the parser when this element's close tag is reached, + // signalling that all child tags have been parsed and added. + // This is needed for <applet> and <object> elements, which can't lay themselves out + // until they know all of their nested <param>s. [Radar 3603191, 4040848]. + // Also used for script elements and some SVG elements for similar purposes, + // but making parsing a special case in this respect should be avoided if possible. + virtual void finishParsingChildren() { } + virtual void beginParsingChildren() { } + + // Called by the frame right before dispatching an unloadEvent. [Radar 4532113] + // This is needed for HTMLInputElements to tell the frame that it is done editing + // (sends textFieldDidEndEditing notification) + virtual void aboutToUnload() { } + + // For <link> and <style> elements. + virtual bool sheetLoaded() { return true; } + + bool hasID() const { return m_hasId; } + bool hasClass() const { return m_hasClass; } + bool active() const { return m_active; } + bool inActiveChain() const { return m_inActiveChain; } + bool inDetach() const { return m_inDetach; } + bool hovered() const { return m_hovered; } + bool focused() const { return m_focused; } + bool attached() const { return m_attached; } + void setAttached(bool b = true) { m_attached = b; } + bool changed() const { return m_styleChange != NoStyleChange; } + StyleChangeType styleChangeType() const { return static_cast<StyleChangeType>(m_styleChange); } + bool hasChangedChild() const { return m_hasChangedChild; } + bool isLink() const { return m_isLink; } + void setHasID(bool b = true) { m_hasId = b; } + void setHasClass(bool b = true) { m_hasClass = b; } + void setHasChangedChild( bool b = true ) { m_hasChangedChild = b; } + void setInDocument(bool b = true) { m_inDocument = b; } + void setInActiveChain(bool b = true) { m_inActiveChain = b; } + void setChanged(StyleChangeType changeType = FullStyleChange); + + virtual void setFocus(bool b = true) { m_focused = b; } + virtual void setActive(bool b = true, bool pause=false) { m_active = b; } + virtual void setHovered(bool b = true) { m_hovered = b; } + + short tabIndex() const { return m_tabIndex; } + void setTabIndex(short i) { m_tabIndex = i; } + + /** + * Whether this node can receive the keyboard focus. + */ + virtual bool supportsFocus() const { return isFocusable(); } + virtual bool isFocusable() const; + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual bool isMouseFocusable() const; + + virtual bool isControl() const { return false; } // Eventually the notion of what is a control will be extensible. + virtual bool isEnabled() const { return true; } + virtual bool isChecked() const { return false; } + virtual bool isIndeterminate() const { return false; } + virtual bool isReadOnlyControl() const { return false; } + + virtual bool isContentEditable() const; + virtual bool isContentRichlyEditable() const; + virtual bool shouldUseInputMethod() const; + virtual IntRect getRect() const; + + enum StyleChange { NoChange, NoInherit, Inherit, Detach, Force }; + virtual void recalcStyle(StyleChange = NoChange) { } + StyleChange diff(RenderStyle*, RenderStyle*) const; + + unsigned nodeIndex() const; + + // Returns the DOM ownerDocument attribute. This method never returns NULL, except in the case + // of (1) a Document node or (2) a DocumentType node that is not used with any Document yet. + virtual Document* ownerDocument() const; + + // Returns the document associated with this node. This method never returns NULL, except in the case + // of a DocumentType node that is not used with any Document yet. A Document node returns itself. + Document* document() const + { + ASSERT(this); + ASSERT(m_document || nodeType() == DOCUMENT_TYPE_NODE && !inDocument()); + return m_document.get(); + } + void setDocument(Document*); + + // Returns true if this node is associated with a document and is in its associated document's + // node tree, false otherwise. + bool inDocument() const + { + ASSERT(m_document || !m_inDocument); + return m_inDocument; + } + + virtual bool isReadOnlyNode(); + virtual bool childTypeAllowed(NodeType) { return false; } + virtual unsigned childNodeCount() const; + virtual Node* childNode(unsigned index) const; + + /** + * Does a pre-order traversal of the tree to find the node next node after this one. This uses the same order that + * the tags appear in the source file. + * + * @param stayWithin If not null, the traversal will stop once the specified node is reached. This can be used to + * restrict traversal to a particular sub-tree. + * + * @return The next node, in document order + * + * see @ref traversePreviousNode() + */ + Node* traverseNextNode(const Node* stayWithin = 0) const; + + /* Like traverseNextNode, but skips children and starts with the next sibling. */ + Node* traverseNextSibling(const Node* stayWithin = 0) const; + + /** + * Does a reverse pre-order traversal to find the node that comes before the current one in document order + * + * see @ref traverseNextNode() + */ + Node* traversePreviousNode(const Node * stayWithin = 0) const; + + /* Like traversePreviousNode, but visits nodes before their children. */ + Node* traversePreviousNodePostOrder(const Node *stayWithin = 0) const; + Node* traversePreviousSiblingPostOrder(const Node *stayWithin = 0) const; + + /** + * Finds previous or next editable leaf node. + */ + Node* previousEditable() const; + Node* nextEditable() const; + Node* nextEditable(int offset) const; + + RenderObject* renderer() const { return m_renderer; } + RenderObject* nextRenderer(); + RenderObject* previousRenderer(); + void setRenderer(RenderObject* renderer) { m_renderer = renderer; } + + void checkSetPrefix(const AtomicString& prefix, ExceptionCode&); + bool isDescendantOf(const Node*) const; + + // These two methods are mutually exclusive. The former is used to do strict error-checking + // when adding children via the public DOM API (e.g., appendChild()). The latter is called only when parsing, + // to sanity-check against the DTD for error recovery. + void checkAddChild(Node* newChild, ExceptionCode&); // Error-checking when adding via the DOM API + virtual bool childAllowed(Node* newChild); // Error-checking during parsing that checks the DTD + + void checkReplaceChild(Node* newChild, Node* oldChild, ExceptionCode&); + virtual bool canReplaceChild(Node* newChild, Node* oldChild); + + // Used to determine whether range offsets use characters or node indices. + virtual bool offsetInCharacters() const; + // Number of DOM 16-bit units contained in node. Note that rendered text length can be different - e.g. because of + // css-transform:capitalize breaking up precomposed characters and ligatures. + virtual int maxCharacterOffset() const; + + // FIXME: We should try to find a better location for these methods. + virtual bool canSelectAll() const { return false; } + virtual void selectAll() { } + + // Whether or not a selection can be started in this object + virtual bool canStartSelection() const; + + // ----------------------------------------------------------------------------- + // Integration with rendering tree + + /** + * Attaches this node to the rendering tree. This calculates the style to be applied to the node and creates an + * appropriate RenderObject which will be inserted into the tree (except when the style has display: none). This + * makes the node visible in the FrameView. + */ + virtual void attach(); + + /** + * Detaches the node from the rendering tree, making it invisible in the rendered view. This method will remove + * the node's rendering object from the rendering tree and delete it. + */ + virtual void detach(); + + virtual void willRemove(); + void createRendererIfNeeded(); + virtual RenderStyle* styleForRenderer(RenderObject* parent); + virtual bool rendererIsNeeded(RenderStyle*); +#if ENABLE(SVG) + virtual bool childShouldCreateRenderer(Node*) const { return true; } +#endif + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + // Wrapper for nodes that don't have a renderer, but still cache the style (like HTMLOptionElement). + virtual RenderStyle* renderStyle() const; + virtual void setRenderStyle(RenderStyle*); + + virtual RenderStyle* computedStyle(); + + // ----------------------------------------------------------------------------- + // Notification of document structure changes + + /** + * Notifies the node that it has been inserted into the document. This is called during document parsing, and also + * when a node is added through the DOM methods insertBefore(), appendChild() or replaceChild(). Note that this only + * happens when the node becomes part of the document tree, i.e. only when the document is actually an ancestor of + * the node. The call happens _after_ the node has been added to the tree. + * + * This is similar to the DOMNodeInsertedIntoDocument DOM event, but does not require the overhead of event + * dispatching. + */ + virtual void insertedIntoDocument(); + + /** + * Notifies the node that it is no longer part of the document tree, i.e. when the document is no longer an ancestor + * node. + * + * This is similar to the DOMNodeRemovedFromDocument DOM event, but does not require the overhead of event + * dispatching, and is called _after_ the node is removed from the tree. + */ + virtual void removedFromDocument(); + + // These functions are called whenever you are connected or disconnected from a tree. That tree may be the main + // document tree, or it could be another disconnected tree. Override these functions to do any work that depends + // on connectedness to some ancestor (e.g., an ancestor <form> for example). + virtual void insertedIntoTree(bool deep) { } + virtual void removedFromTree(bool deep) { } + + /** + * Notifies the node that it's list of children have changed (either by adding or removing child nodes), or a child + * node that is of the type CDATA_SECTION_NODE, TEXT_NODE or COMMENT_NODE has changed its value. + */ + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0) {}; + + virtual String toString() const = 0; + +#ifndef NDEBUG + virtual void formatForDebugger(char* buffer, unsigned length) const; + + void showNode(const char* prefix = "") const; + void showTreeForThis() const; + void showTreeAndMark(const Node* markedNode1, const char* markedLabel1, const Node* markedNode2 = 0, const char* markedLabel2 = 0) const; +#endif + + void registerDynamicNodeList(DynamicNodeList*); + void unregisterDynamicNodeList(DynamicNodeList*); + void notifyNodeListsChildrenChanged(); + void notifyLocalNodeListsChildrenChanged(); + void notifyNodeListsAttributeChanged(); + void notifyLocalNodeListsAttributeChanged(); + + PassRefPtr<NodeList> getElementsByTagName(const String&); + PassRefPtr<NodeList> getElementsByTagNameNS(const String& namespaceURI, const String& localName); + PassRefPtr<NodeList> getElementsByName(const String& elementName); + PassRefPtr<NodeList> getElementsByClassName(const String& classNames); + + PassRefPtr<Element> querySelector(const String& selectors, ExceptionCode&); + PassRefPtr<NodeList> querySelectorAll(const String& selectors, ExceptionCode&); + +private: // members + DocPtr<Document> m_document; + Node* m_previous; + Node* m_next; + RenderObject* m_renderer; + +protected: + virtual void willMoveToNewOwnerDocument() { } + virtual void didMoveToNewOwnerDocument() { } + + OwnPtr<NodeListsNodeData> m_nodeLists; + + short m_tabIndex; + + // make sure we don't use more than 16 bits here -- adding more would increase the size of all Nodes + + bool m_hasId : 1; + bool m_hasClass : 1; + bool m_attached : 1; + unsigned m_styleChange : 2; + bool m_hasChangedChild : 1; + bool m_inDocument : 1; + + bool m_isLink : 1; + bool m_attrWasSpecifiedOrElementHasRareData : 1; // used in Attr for one thing and Element for another + bool m_focused : 1; + bool m_active : 1; + bool m_hovered : 1; + bool m_inActiveChain : 1; + + bool m_inDetach : 1; + bool m_dispatchingSimulatedEvent : 1; + +public: + bool m_inSubtreeMark : 1; + // 0 bits left + +private: + Element* ancestorElement() const; + + virtual Node* virtualFirstChild() const; + virtual Node* virtualLastChild() const; + virtual bool virtualHasTagName(const QualifiedName&) const; +}; + +} //namespace + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::Node*); +#endif + +#endif diff --git a/WebCore/dom/Node.idl b/WebCore/dom/Node.idl new file mode 100644 index 0000000..c6af454 --- /dev/null +++ b/WebCore/dom/Node.idl @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module core { + + interface [ + CustomMarkFunction, + GenerateConstructor, + GenerateNativeConverter, + GenerateToJS, + ObjCCustomInternalImpl, + InterfaceUUID=84BA0D7A-7E3E-4a7b-B6FB-7653E8FB54ED, + ImplementationUUID=81B47FDB-94B0-40fd-8E0C-FB2A6E53CC04 + ] Node { + // NodeType + const unsigned short ELEMENT_NODE = 1; + const unsigned short ATTRIBUTE_NODE = 2; + const unsigned short TEXT_NODE = 3; + const unsigned short CDATA_SECTION_NODE = 4; + const unsigned short ENTITY_REFERENCE_NODE = 5; + const unsigned short ENTITY_NODE = 6; + const unsigned short PROCESSING_INSTRUCTION_NODE = 7; + const unsigned short COMMENT_NODE = 8; + const unsigned short DOCUMENT_NODE = 9; + const unsigned short DOCUMENT_TYPE_NODE = 10; + const unsigned short DOCUMENT_FRAGMENT_NODE = 11; + const unsigned short NOTATION_NODE = 12; + + readonly attribute [ConvertNullStringTo=Null] DOMString nodeName; + + // FIXME: the spec says this can also raise on retrieval. + attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString nodeValue + setter raises(DOMException); + + readonly attribute unsigned short nodeType; + readonly attribute Node parentNode; + readonly attribute NodeList childNodes; + readonly attribute Node firstChild; + readonly attribute Node lastChild; + readonly attribute Node previousSibling; + readonly attribute Node nextSibling; + readonly attribute NamedNodeMap attributes; + readonly attribute Document ownerDocument; + + [OldStyleObjC, Custom] Node insertBefore(in [Return] Node newChild, + in Node refChild) + raises(DOMException); + [OldStyleObjC, Custom] Node replaceChild(in Node newChild, + in [Return] Node oldChild) + raises(DOMException); + [Custom] Node removeChild(in [Return] Node oldChild) + raises(DOMException); + [Custom] Node appendChild(in [Return] Node newChild) + raises(DOMException); + + boolean hasChildNodes(); + Node cloneNode(in boolean deep); + void normalize(); + + // Introduced in DOM Level 2: + + [OldStyleObjC] boolean isSupported(in DOMString feature, + in [ConvertNullToNullString] DOMString version); + + readonly attribute [ConvertNullStringTo=Null] DOMString namespaceURI; + attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString prefix + setter raises(DOMException); + readonly attribute [ConvertNullStringTo=Null] DOMString localName; + + boolean hasAttributes(); + + // Introduced in DOM Level 3: + + readonly attribute [ConvertNullStringTo=Null] DOMString baseURI; + + // FIXME: the spec says this can also raise on retrieval. + attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString textContent + setter raises(DOMException); + + boolean isSameNode(in Node other); + boolean isEqualNode(in Node other); + [ConvertNullStringTo=Null] DOMString lookupPrefix(in [ConvertNullToNullString] DOMString namespaceURI); + boolean isDefaultNamespace(in [ConvertNullToNullString] DOMString namespaceURI); + [ConvertNullStringTo=Null] DOMString lookupNamespaceURI(in [ConvertNullToNullString] DOMString prefix); + +#if 0 + // DocumentPosition + const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01; + const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02; + const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04; + const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08; + const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10; + const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; + + unsigned short compareDocumentPosition(in Node other) + raises(DOMException); + + DOMObject getFeature(in DOMString feature, + in DOMString version); + DOMUserData setUserData(in DOMString key, + in DOMUserData data, + in UserDataHandler handler); + DOMUserData getUserData(in DOMString key); +#endif /* 0 */ + + // IE extentions + readonly attribute Node parentElement; + +#if defined(LANGUAGE_OBJECTIVE_C) + // Objective-C extensions + readonly attribute boolean isContentEditable; +#endif /* defined(LANGUAGE_OBJECTIVE_C) */ + }; + +} diff --git a/WebCore/dom/NodeFilter.cpp b/WebCore/dom/NodeFilter.cpp new file mode 100644 index 0000000..1844a2d --- /dev/null +++ b/WebCore/dom/NodeFilter.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "NodeFilter.h" + +using namespace KJS; + +namespace WebCore { + +short NodeFilter::acceptNode(Node* node, JSValue*& exception) const +{ + // cast to short silences "enumeral and non-enumeral types in return" warning + return m_condition ? m_condition->acceptNode(node, exception) : static_cast<short>(FILTER_ACCEPT); +} + +} // namespace WebCore diff --git a/WebCore/dom/NodeFilter.h b/WebCore/dom/NodeFilter.h new file mode 100644 index 0000000..2f7c822 --- /dev/null +++ b/WebCore/dom/NodeFilter.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef NodeFilter_h +#define NodeFilter_h + +#include "NodeFilterCondition.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class NodeFilter : public RefCounted<NodeFilter> { + public: + /** + * The following constants are returned by the acceptNode() + * method: + */ + enum { + FILTER_ACCEPT = 1, + FILTER_REJECT = 2, + FILTER_SKIP = 3 + }; + + /** + * These are the available values for the whatToShow parameter. + * They are the same as the set of possible types for Node, and + * their values are derived by using a bit position corresponding + * to the value of NodeType for the equivalent node type. + */ + enum { + SHOW_ALL = 0xFFFFFFFF, + SHOW_ELEMENT = 0x00000001, + SHOW_ATTRIBUTE = 0x00000002, + SHOW_TEXT = 0x00000004, + SHOW_CDATA_SECTION = 0x00000008, + SHOW_ENTITY_REFERENCE = 0x00000010, + SHOW_ENTITY = 0x00000020, + SHOW_PROCESSING_INSTRUCTION = 0x00000040, + SHOW_COMMENT = 0x00000080, + SHOW_DOCUMENT = 0x00000100, + SHOW_DOCUMENT_TYPE = 0x00000200, + SHOW_DOCUMENT_FRAGMENT = 0x00000400, + SHOW_NOTATION = 0x00000800 + }; + + NodeFilter(PassRefPtr<NodeFilterCondition> condition) : RefCounted<NodeFilter>(0), m_condition(condition) { } + short acceptNode(Node*, KJS::JSValue*& exception) const; + void mark() { m_condition->mark(); }; + + // For non-JS bindings. Silently ignores the JavaScript exception if any. + short acceptNode(Node* node) const { KJS::JSValue* exception; return acceptNode(node, exception); } + + private: + RefPtr<NodeFilterCondition> m_condition; + }; + +} // namespace WebCore + +#endif // NodeFilter_h diff --git a/WebCore/dom/NodeFilter.idl b/WebCore/dom/NodeFilter.idl new file mode 100644 index 0000000..3cc5e86 --- /dev/null +++ b/WebCore/dom/NodeFilter.idl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module traversal { + + // Introduced in DOM Level 2: + interface [GenerateConstructor, CustomMarkFunction, CustomNativeConverter, ObjCProtocol] NodeFilter { + + // Constants returned by acceptNode + const short FILTER_ACCEPT = 1; + const short FILTER_REJECT = 2; + const short FILTER_SKIP = 3; + + // Constants for whatToShow + const unsigned long SHOW_ALL = 0xFFFFFFFF; + const unsigned long SHOW_ELEMENT = 0x00000001; + const unsigned long SHOW_ATTRIBUTE = 0x00000002; + const unsigned long SHOW_TEXT = 0x00000004; + const unsigned long SHOW_CDATA_SECTION = 0x00000008; + const unsigned long SHOW_ENTITY_REFERENCE = 0x00000010; + const unsigned long SHOW_ENTITY = 0x00000020; + const unsigned long SHOW_PROCESSING_INSTRUCTION = 0x00000040; + const unsigned long SHOW_COMMENT = 0x00000080; + const unsigned long SHOW_DOCUMENT = 0x00000100; + const unsigned long SHOW_DOCUMENT_TYPE = 0x00000200; + const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x00000400; + const unsigned long SHOW_NOTATION = 0x00000800; + + [Custom] short acceptNode(in Node n); + + }; + +} diff --git a/WebCore/dom/NodeFilterCondition.cpp b/WebCore/dom/NodeFilterCondition.cpp new file mode 100644 index 0000000..48bdcb4 --- /dev/null +++ b/WebCore/dom/NodeFilterCondition.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "NodeFilterCondition.h" + +#include "NodeFilter.h" + +using namespace KJS; + +namespace WebCore { + +short NodeFilterCondition::acceptNode(Node*, JSValue*&) const +{ + return NodeFilter::FILTER_ACCEPT; +} + +} // namespace WebCore diff --git a/WebCore/dom/NodeFilterCondition.h b/WebCore/dom/NodeFilterCondition.h new file mode 100644 index 0000000..7596684 --- /dev/null +++ b/WebCore/dom/NodeFilterCondition.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef NodeFilterCondition_h +#define NodeFilterCondition_h + +#include <wtf/RefCounted.h> + +namespace KJS { + class JSValue; +} + +namespace WebCore { + + class Node; + + class NodeFilterCondition : public RefCounted<NodeFilterCondition> { + public: + NodeFilterCondition() : RefCounted<NodeFilterCondition>(0) { } + virtual ~NodeFilterCondition() { } + virtual short acceptNode(Node*, KJS::JSValue*& exception) const; + virtual void mark() { } + }; + +} // namespace WebCore + +#endif // NodeFilterCondition_h diff --git a/WebCore/dom/NodeIterator.cpp b/WebCore/dom/NodeIterator.cpp new file mode 100644 index 0000000..502d393 --- /dev/null +++ b/WebCore/dom/NodeIterator.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "NodeIterator.h" + +#include "Document.h" +#include "ExceptionCode.h" +#include "NodeFilter.h" + +using namespace KJS; + +namespace WebCore { + +NodeIterator::NodePointer::NodePointer() +{ +} + +NodeIterator::NodePointer::NodePointer(PassRefPtr<Node> n, bool b) + : node(n) + , isPointerBeforeNode(b) +{ +} + +void NodeIterator::NodePointer::clear() +{ + node.clear(); +} + +bool NodeIterator::NodePointer::moveToNext(Node* root) +{ + if (!node) + return false; + if (isPointerBeforeNode) { + isPointerBeforeNode = false; + return true; + } + node = node->traverseNextNode(root); + return node; +} + +bool NodeIterator::NodePointer::moveToPrevious(Node* root) +{ + if (!node) + return false; + if (!isPointerBeforeNode) { + isPointerBeforeNode = true; + return true; + } + node = node->traversePreviousNode(root); + return node; +} + +NodeIterator::NodeIterator(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) + : Traversal(rootNode, whatToShow, filter, expandEntityReferences) + , m_referenceNode(root(), true) + , m_detached(false) +{ + root()->document()->attachNodeIterator(this); +} + +NodeIterator::~NodeIterator() +{ + root()->document()->detachNodeIterator(this); +} + +Node* NodeIterator::nextNode(ExceptionCode& ec, JSValue*& exception) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + Node* result = 0; + + m_candidateNode = m_referenceNode; + while (m_candidateNode.moveToNext(root())) { + // NodeIterators treat the DOM tree as a flat list of nodes. + // In other words, FILTER_REJECT does not pass over descendants + // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP. + exception = 0; + bool nodeWasAccepted = acceptNode(m_candidateNode.node.get(), exception) == NodeFilter::FILTER_ACCEPT; + if (exception) + break; + if (nodeWasAccepted) { + m_referenceNode = m_candidateNode; + result = m_referenceNode.node.get(); + break; + } + } + + m_candidateNode.clear(); + return result; +} + +Node* NodeIterator::previousNode(ExceptionCode& ec, JSValue*& exception) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + Node* result = 0; + + m_candidateNode = m_referenceNode; + while (m_candidateNode.moveToPrevious(root())) { + // NodeIterators treat the DOM tree as a flat list of nodes. + // In other words, FILTER_REJECT does not pass over descendants + // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP. + exception = 0; + bool nodeWasAccepted = acceptNode(m_candidateNode.node.get(), exception) == NodeFilter::FILTER_ACCEPT; + if (exception) + break; + if (nodeWasAccepted) { + m_referenceNode = m_candidateNode; + result = m_referenceNode.node.get(); + break; + } + } + + m_candidateNode.clear(); + return result; +} + +void NodeIterator::detach() +{ + root()->document()->detachNodeIterator(this); + m_detached = true; + m_referenceNode.node.clear(); +} + +void NodeIterator::notifyBeforeNodeRemoval(Node* removedNode) +{ + updateForNodeRemoval(removedNode, m_referenceNode); +} + +void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenceNode) const +{ + ASSERT(!m_detached); + ASSERT(removedNode); + ASSERT(root()->document() == removedNode->document()); + + // Iterator is not affected if the removed node is the reference node and is the root. + // or if removed node is not the reference node, or the ancestor of the reference node. + if (!removedNode->isDescendantOf(root())) + return; + bool willRemoveReferenceNode = removedNode == referenceNode.node; + bool willRemoveReferenceNodeAncestor = referenceNode.node && referenceNode.node->isDescendantOf(removedNode); + if (!willRemoveReferenceNode && !willRemoveReferenceNodeAncestor) + return; + + if (referenceNode.isPointerBeforeNode) { + Node* node = removedNode->traverseNextNode(root()); + if (node) { + // Move out from under the node being removed if the reference node is + // a descendant of the node being removed. + if (willRemoveReferenceNodeAncestor) { + while (node && node->isDescendantOf(removedNode)) + node = node->traverseNextNode(root()); + } + if (node) + referenceNode.node = node; + } else { + node = removedNode->traversePreviousNode(root()); + if (node) { + // Move out from under the node being removed if the reference node is + // a descendant of the node being removed. + if (willRemoveReferenceNodeAncestor) { + while (node && node->isDescendantOf(removedNode)) + node = node->traversePreviousNode(root()); + } + if (node) { + // Removing last node. + // Need to move the pointer after the node preceding the + // new reference node. + referenceNode.node = node; + referenceNode.isPointerBeforeNode = false; + } + } + } + } else { + Node* node = removedNode->traversePreviousNode(root()); + if (node) { + // Move out from under the node being removed if the reference node is + // a descendant of the node being removed. + if (willRemoveReferenceNodeAncestor) { + while (node && node->isDescendantOf(removedNode)) + node = node->traversePreviousNode(root()); + } + if (node) + referenceNode.node = node; + } else { + node = removedNode->traverseNextNode(root()); + // Move out from under the node being removed if the reference node is + // a descendant of the node being removed. + if (willRemoveReferenceNodeAncestor) { + while (node && node->isDescendantOf(removedNode)) + node = node->traversePreviousNode(root()); + } + if (node) + referenceNode.node = node; + } + } +} + +} // namespace WebCore diff --git a/WebCore/dom/NodeIterator.h b/WebCore/dom/NodeIterator.h new file mode 100644 index 0000000..b2a7c70 --- /dev/null +++ b/WebCore/dom/NodeIterator.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef NodeIterator_h +#define NodeIterator_h + +#include "Traversal.h" +#include <wtf/PassRefPtr.h> + +namespace WebCore { + + typedef int ExceptionCode; + + class NodeIterator : public Traversal { + public: + NodeIterator(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); + virtual ~NodeIterator(); + + Node* nextNode(ExceptionCode&, KJS::JSValue*& exception); + Node* previousNode(ExceptionCode&, KJS::JSValue*& exception); + void detach(); + + Node* referenceNode() const { return m_referenceNode.node.get(); } + bool pointerBeforeReferenceNode() const { return m_referenceNode.isPointerBeforeNode; } + + // This function is called before any node is removed from the document tree. + void notifyBeforeNodeRemoval(Node* nodeToBeRemoved); + + // For non-JS bindings. Silently ignores the JavaScript exception if any. + Node* nextNode(ExceptionCode& ec) { KJS::JSValue* exception; return nextNode(ec, exception); } + Node* previousNode(ExceptionCode& ec) { KJS::JSValue* exception; return previousNode(ec, exception); } + + private: + struct NodePointer { + RefPtr<Node> node; + bool isPointerBeforeNode; + NodePointer(); + NodePointer(PassRefPtr<Node>, bool); + void clear(); + bool moveToNext(Node* root); + bool moveToPrevious(Node* root); + }; + + void updateForNodeRemoval(Node* nodeToBeRemoved, NodePointer&) const; + + NodePointer m_referenceNode; + NodePointer m_candidateNode; + bool m_detached; + }; + +} // namespace WebCore + +#endif // NodeIterator_h diff --git a/WebCore/dom/NodeIterator.idl b/WebCore/dom/NodeIterator.idl new file mode 100644 index 0000000..e129de3 --- /dev/null +++ b/WebCore/dom/NodeIterator.idl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module traversal { + + // Introduced in DOM Level 2: + interface [CustomMarkFunction] NodeIterator { + readonly attribute Node root; + readonly attribute unsigned long whatToShow; + readonly attribute [ObjCIvar] NodeFilter filter; + readonly attribute boolean expandEntityReferences; + readonly attribute Node referenceNode; + readonly attribute boolean pointerBeforeReferenceNode; + + [Custom] Node nextNode() + raises (DOMException); + [Custom] Node previousNode() + raises (DOMException); + void detach(); + }; + +} diff --git a/WebCore/dom/NodeList.h b/WebCore/dom/NodeList.h new file mode 100644 index 0000000..2bc1443 --- /dev/null +++ b/WebCore/dom/NodeList.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef NodeList_h +#define NodeList_h + +#include <wtf/RefCounted.h> + +namespace WebCore { + + class AtomicString; + class Node; + + class NodeList : public RefCounted<NodeList> { + public: + NodeList() : RefCounted<NodeList>(0) { } + virtual ~NodeList() { } + + // DOM methods & attributes for NodeList + virtual unsigned length() const = 0; + virtual Node* item(unsigned index) const = 0; + virtual Node* itemWithName(const AtomicString&) const = 0; + }; + +} // namespace WebCore + +#endif // NodeList_h diff --git a/WebCore/dom/NodeList.idl b/WebCore/dom/NodeList.idl new file mode 100644 index 0000000..cf21cc7 --- /dev/null +++ b/WebCore/dom/NodeList.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * 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. + */ + +module core { + + interface [ + GenerateConstructor, + HasIndexGetter, + HasNameGetter, + CustomCall, + InterfaceUUID=F9A9F6A9-385C-414e-A6F6-E2E0CF574130, + ImplementationUUID=BBB49E8B-DB1D-4c4a-B970-D300FB4609FA + ] NodeList { + + Node item(in [IsIndex] unsigned long index); + + readonly attribute unsigned long length; + + }; + +} diff --git a/WebCore/dom/Notation.cpp b/WebCore/dom/Notation.cpp new file mode 100644 index 0000000..7081d98 --- /dev/null +++ b/WebCore/dom/Notation.cpp @@ -0,0 +1,61 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "Notation.h" + +namespace WebCore { + +Notation::Notation(Document* doc) : ContainerNode(doc) +{ +} + +Notation::Notation(Document* doc, const String& name, const String& publicId, const String& systemId) + : ContainerNode(doc) + , m_name(name) + , m_publicId(publicId) + , m_systemId(systemId) +{ +} + +String Notation::nodeName() const +{ + return m_name; +} + +Node::NodeType Notation::nodeType() const +{ + return NOTATION_NODE; +} + +PassRefPtr<Node> Notation::cloneNode(bool /*deep*/) +{ + // Spec says cloning Notation nodes is "implementation dependent". We do not support it. + return 0; +} + +// DOM Section 1.1.1 +bool Notation::childTypeAllowed(NodeType) +{ + return false; +} + +} // namespace diff --git a/WebCore/dom/Notation.h b/WebCore/dom/Notation.h new file mode 100644 index 0000000..6f5bf3f --- /dev/null +++ b/WebCore/dom/Notation.h @@ -0,0 +1,55 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef Notation_h +#define Notation_h + +#include "CachedResourceClient.h" +#include "ContainerNode.h" + +namespace WebCore { + +class Notation : public ContainerNode +{ +public: + Notation(Document*); + Notation(Document*, const String& name, const String& publicId, const String& systemId); + + // DOM methods & attributes for Notation + String publicId() const { return m_publicId; } + String systemId() const { return m_systemId; } + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); + +private: + String m_name; + String m_publicId; + String m_systemId; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/Notation.idl b/WebCore/dom/Notation.idl new file mode 100644 index 0000000..a16fde6 --- /dev/null +++ b/WebCore/dom/Notation.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=6580C703-F5FF-40a7-ACF2-AB80EBC83CA1, + ImplementationUUID=A52869F7-A3CE-4f4c-8C27-E369C4ED9FF9 + ] Notation : Node { + readonly attribute [ConvertNullStringTo=Null] DOMString publicId; + readonly attribute [ConvertNullStringTo=Null] DOMString systemId; + }; + +} diff --git a/WebCore/dom/OverflowEvent.cpp b/WebCore/dom/OverflowEvent.cpp new file mode 100644 index 0000000..b32213d --- /dev/null +++ b/WebCore/dom/OverflowEvent.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "OverflowEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +using namespace EventNames; + +OverflowEvent::OverflowEvent() + : Event(overflowchangedEvent, false, false) + , m_orient(VERTICAL) + , m_horizontalOverflow(false) + , m_verticalOverflow(false) +{ +} + +OverflowEvent::OverflowEvent(bool horizontalOverflowChanged, bool horizontalOverflow, bool verticalOverflowChanged, bool verticalOverflow) + : Event(overflowchangedEvent, false, false) + , m_horizontalOverflow(horizontalOverflow) + , m_verticalOverflow(verticalOverflow) +{ + ASSERT(horizontalOverflowChanged || verticalOverflowChanged); + + if (horizontalOverflowChanged && verticalOverflowChanged) + m_orient = BOTH; + else if (horizontalOverflowChanged) + m_orient = HORIZONTAL; + else + m_orient = VERTICAL; +} + +bool OverflowEvent::isOverflowEvent() const +{ + return true; +} + +void OverflowEvent::initOverflowEvent(unsigned short orient, bool horizontalOverflow, bool verticalOverflow) +{ + if (dispatched()) + return; + + m_orient = orient; + m_horizontalOverflow = horizontalOverflow; + m_verticalOverflow = verticalOverflow; +} + +} diff --git a/WebCore/dom/OverflowEvent.h b/WebCore/dom/OverflowEvent.h new file mode 100644 index 0000000..fb3e289 --- /dev/null +++ b/WebCore/dom/OverflowEvent.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OverflowEvent_h +#define OverflowEvent_h + +#include "Event.h" + +namespace WebCore { + + class OverflowEvent : public Event { + public: + enum orientType { + VERTICAL = 0, + HORIZONTAL = 1, + BOTH = 2 + }; + + OverflowEvent(); + OverflowEvent(bool horizontalOverflowChanged, bool horizontalOverflow, bool verticalOverflowChanged, bool verticalOverflow); + + void initOverflowEvent(unsigned short orient, bool horizontalOverflow, bool verticalOverflow); + + unsigned short orient() const { return m_orient; } + bool horizontalOverflow() const { return m_horizontalOverflow; } + bool verticalOverflow() const { return m_verticalOverflow; } + + virtual bool isOverflowEvent() const; + + private: + unsigned short m_orient; + bool m_horizontalOverflow; + bool m_verticalOverflow; + }; +} + +#endif // OverflowEvent_h + diff --git a/WebCore/dom/OverflowEvent.idl b/WebCore/dom/OverflowEvent.idl new file mode 100644 index 0000000..4a1bed5 --- /dev/null +++ b/WebCore/dom/OverflowEvent.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module events { + + interface [ + GenerateConstructor + ] OverflowEvent : Event { + const unsigned short HORIZONTAL = 0; + const unsigned short VERTICAL = 1; + const unsigned short BOTH = 2; + + readonly attribute unsigned short orient; + readonly attribute boolean horizontalOverflow; + readonly attribute boolean verticalOverflow; + + void initOverflowEvent(in unsigned short orient, + in boolean horizontalOverflow, + in boolean verticalOverflow); + }; + +} diff --git a/WebCore/dom/Position.cpp b/WebCore/dom/Position.cpp new file mode 100644 index 0000000..84ff856 --- /dev/null +++ b/WebCore/dom/Position.cpp @@ -0,0 +1,763 @@ +/* + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Position.h" + +#include "CSSComputedStyleDeclaration.h" +#include "CString.h" +#include "CharacterNames.h" +#include "Document.h" +#include "Element.h" +#include "HTMLNames.h" +#include "Logging.h" +#include "PositionIterator.h" +#include "RenderBlock.h" +#include "Text.h" +#include "TextIterator.h" +#include "htmlediting.h" +#include "visible_units.h" +#include <stdio.h> + +namespace WebCore { + +using namespace HTMLNames; + +static Node *nextRenderedEditable(Node *node) +{ + while (1) { + node = node->nextEditable(); + if (!node) + return 0; + if (!node->renderer()) + continue; + if (node->renderer()->inlineBox(0)) + return node; + } + return 0; +} + +static Node *previousRenderedEditable(Node *node) +{ + while (1) { + node = node->previousEditable(); + if (!node) + return 0; + if (!node->renderer()) + continue; + if (node->renderer()->inlineBox(0)) + return node; + } + return 0; +} + +Position::Position(Node* node, int offset) + : m_node(node) + , m_offset(offset) +{ +} + +Position::Position(const PositionIterator& it) + : m_node(it.m_parent) + , m_offset(it.m_child ? it.m_child->nodeIndex() : (it.m_parent->hasChildNodes() ? maxDeepOffset(it.m_parent) : it.m_offset)) +{ +} + +void Position::clear() +{ + m_node = 0; + m_offset = 0; +} + +Element* Position::documentElement() const +{ + if (Node* n = node()) + if (Element* e = n->document()->documentElement()) + return e; + return 0; +} + +Element *Position::element() const +{ + Node *n; + for (n = node(); n && !n->isElementNode(); n = n->parentNode()) + ; // empty loop body + return static_cast<Element *>(n); +} + +PassRefPtr<CSSComputedStyleDeclaration> Position::computedStyle() const +{ + Element *elem = element(); + if (!elem) + return 0; + return new CSSComputedStyleDeclaration(elem); +} + +Position Position::previous(EUsingComposedCharacters usingComposedCharacters) const +{ + Node *n = node(); + if (!n) + return *this; + + int o = offset(); + // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier. + ASSERT(o >= 0); + + if (o > 0) { + Node *child = n->childNode(o - 1); + if (child) { + return Position(child, maxDeepOffset(child)); + } + // There are two reasons child might be 0: + // 1) The node is node like a text node that is not an element, and therefore has no children. + // Going backward one character at a time is correct. + // 2) The old offset was a bogus offset like (<br>, 1), and there is no child. + // Going from 1 to 0 is correct. + return Position(n, usingComposedCharacters ? uncheckedPreviousOffset(n, o) : o - 1); + } + + Node *parent = n->parentNode(); + if (!parent) + return *this; + + return Position(parent, n->nodeIndex()); +} + +Position Position::next(EUsingComposedCharacters usingComposedCharacters) const +{ + Node *n = node(); + if (!n) + return *this; + + int o = offset(); + // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier. + ASSERT(o >= 0); + + Node* child = n->childNode(o); + if (child || !n->hasChildNodes() && o < maxDeepOffset(n)) { + if (child) + return Position(child, 0); + + // There are two reasons child might be 0: + // 1) The node is node like a text node that is not an element, and therefore has no children. + // Going forward one character at a time is correct. + // 2) The new offset is a bogus offset like (<br>, 1), and there is no child. + // Going from 0 to 1 is correct. + return Position(n, usingComposedCharacters ? uncheckedNextOffset(n, o) : o + 1); + } + + Node *parent = n->parentNode(); + if (!parent) + return *this; + + return Position(parent, n->nodeIndex() + 1); +} + +int Position::uncheckedPreviousOffset(const Node* n, int current) +{ + return n->renderer() ? n->renderer()->previousOffset(current) : current - 1; +} + +int Position::uncheckedNextOffset(const Node* n, int current) +{ + return n->renderer() ? n->renderer()->nextOffset(current) : current + 1; +} + +bool Position::atStart() const +{ + Node *n = node(); + if (!n) + return true; + + return offset() <= 0 && n->parent() == 0; +} + +bool Position::atEnd() const +{ + Node *n = node(); + if (!n) + return true; + + return n->parent() == 0 && offset() >= maxDeepOffset(n); +} + +int Position::renderedOffset() const +{ + if (!node()->isTextNode()) + return offset(); + + if (!node()->renderer()) + return offset(); + + int result = 0; + RenderText *textRenderer = static_cast<RenderText *>(node()->renderer()); + for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + int start = box->m_start; + int end = box->m_start + box->m_len; + if (offset() < start) + return result; + if (offset() <= end) { + result += offset() - start; + return result; + } + result += box->m_len; + } + return result; +} + +// return first preceding DOM position rendered at a different location, or "this" +Position Position::previousCharacterPosition(EAffinity affinity) const +{ + if (isNull()) + return Position(); + + Node *fromRootEditableElement = node()->rootEditableElement(); + + bool atStartOfLine = isStartOfLine(VisiblePosition(*this, affinity)); + bool rendered = isCandidate(); + + Position currentPos = *this; + while (!currentPos.atStart()) { + currentPos = currentPos.previous(); + + if (currentPos.node()->rootEditableElement() != fromRootEditableElement) + return *this; + + if (atStartOfLine || !rendered) { + if (currentPos.isCandidate()) + return currentPos; + } else if (rendersInDifferentPosition(currentPos)) + return currentPos; + } + + return *this; +} + +// return first following position rendered at a different location, or "this" +Position Position::nextCharacterPosition(EAffinity affinity) const +{ + if (isNull()) + return Position(); + + Node *fromRootEditableElement = node()->rootEditableElement(); + + bool atEndOfLine = isEndOfLine(VisiblePosition(*this, affinity)); + bool rendered = isCandidate(); + + Position currentPos = *this; + while (!currentPos.atEnd()) { + currentPos = currentPos.next(); + + if (currentPos.node()->rootEditableElement() != fromRootEditableElement) + return *this; + + if (atEndOfLine || !rendered) { + if (currentPos.isCandidate()) + return currentPos; + } else if (rendersInDifferentPosition(currentPos)) + return currentPos; + } + + return *this; +} + +// upstream() and downstream() want to return positions that are either in a +// text node or at just before a non-text node. This method checks for that. +static bool isStreamer(const PositionIterator& pos) +{ + if (!pos.node()) + return true; + + if (isAtomicNode(pos.node())) + return true; + + return pos.atStartOfNode(); +} + +// enclosingBlock does some expensive editability checks, upstream and downstream +// can avoid those because they do their own editability checking. +static Node* enclosingBlockIgnoringEditability(Node* node) +{ + while (node && !isBlock(node)) + node = node->parentNode(); + + return node; +} + +// p.upstream() returns the start of the range of positions that map to the same VisiblePosition as P. +Position Position::upstream() const +{ + Node* startNode = node(); + if (!startNode) + return Position(); + + // iterate backward from there, looking for a qualified position + Node* originalBlock = enclosingBlockIgnoringEditability(startNode); + PositionIterator lastVisible = *this; + PositionIterator currentPos = lastVisible; + bool startEditable = startNode->isContentEditable(); + Node* lastNode = startNode; + for (; !currentPos.atStart(); currentPos.decrement()) { + Node* currentNode = currentPos.node(); + + // Don't check for an editability change if we haven't moved to a different node, + // to avoid the expense of computing isContentEditable(). + if (currentNode != lastNode) { + // Don't change editability. + bool currentEditable = currentNode->isContentEditable(); + if (startEditable != currentEditable) + break; + lastNode = currentNode; + } + + // Don't enter a new enclosing block flow or table element. There is code below that + // terminates early if we're about to leave a block. + if (isBlock(currentNode) && currentNode != originalBlock) + return lastVisible; + + // skip position in unrendered or invisible node + RenderObject* renderer = currentNode->renderer(); + if (!renderer || renderer->style()->visibility() != VISIBLE) + continue; + + // track last visible streamer position + if (isStreamer(currentPos)) + lastVisible = currentPos; + + // Don't leave a block flow or table element. We could rely on code above to terminate and + // return lastVisible on the next iteration, but we terminate early to avoid doing a nodeIndex() call. + if (isBlock(currentNode) && currentPos.atStartOfNode()) + return lastVisible; + + // Return position after tables and nodes which have content that can be ignored. + if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { + if (currentPos.atEndOfNode()) + return Position(currentNode, maxDeepOffset(currentNode)); + continue; + } + + // return current position if it is in rendered text + if (renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox()) { + if (currentNode != startNode) { + // This assertion fires in layout tests in the case-transform.html test because + // of a mix-up between offsets in the text in the DOM tree with text in the + // render tree which can have a different length due to case transformation. + // Until we resolve that, disable this so we can run the layout tests! + //ASSERT(currentOffset >= renderer->caretMaxOffset()); + return Position(currentNode, renderer->caretMaxOffset()); + } + + unsigned textOffset = currentPos.offsetInLeafNode(); + RenderText* textRenderer = static_cast<RenderText*>(renderer); + for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (textOffset > box->start() && textOffset <= box->start() + box->len()) + return currentPos; + + if (box != textRenderer->lastTextBox() && + !box->nextOnLine() && + textOffset == box->start() + box->len() + 1) + return currentPos; + } + } + } + + return lastVisible; +} + +// P.downstream() returns the end of the range of positions that map to the same VisiblePosition as P. +Position Position::downstream() const +{ + Node* startNode = node(); + if (!startNode) + return Position(); + + // iterate forward from there, looking for a qualified position + Node* originalBlock = enclosingBlockIgnoringEditability(startNode); + PositionIterator lastVisible = *this; + PositionIterator currentPos = lastVisible; + bool startEditable = startNode->isContentEditable(); + Node* lastNode = startNode; + for (; !currentPos.atEnd(); currentPos.increment()) { + Node* currentNode = currentPos.node(); + + // Don't check for an editability change if we haven't moved to a different node, + // to avoid the expense of computing isContentEditable(). + if (currentNode != lastNode) { + // Don't change editability. + bool currentEditable = currentNode->isContentEditable(); + if (startEditable != currentEditable) + break; + lastNode = currentNode; + } + + // stop before going above the body, up into the head + // return the last visible streamer position + if (currentNode->hasTagName(bodyTag) && currentPos.atEndOfNode()) + break; + + // Do not enter a new enclosing block. + if (isBlock(currentNode) && currentNode != originalBlock) + return lastVisible; + // Do not leave the original enclosing block. + // Note: The first position after the last one in the original block + // will be [originalBlock->parentNode(), originalBlock->nodeIndex() + 1]. + if (originalBlock && originalBlock->parentNode() == currentNode) + return lastVisible; + + // skip position in unrendered or invisible node + RenderObject* renderer = currentNode->renderer(); + if (!renderer || renderer->style()->visibility() != VISIBLE) + continue; + + // track last visible streamer position + if (isStreamer(currentPos)) + lastVisible = currentPos; + + // Return position before tables and nodes which have content that can be ignored. + if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { + if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset()) + return Position(currentNode, renderer->caretMinOffset()); + continue; + } + + // return current position if it is in rendered text + if (renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox()) { + if (currentNode != startNode) { + ASSERT(currentPos.atStartOfNode()); + return Position(currentNode, renderer->caretMinOffset()); + } + + unsigned textOffset = currentPos.offsetInLeafNode(); + + RenderText* textRenderer = static_cast<RenderText*>(renderer); + for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (textOffset >= box->start() && textOffset <= box->end()) + return currentPos; + + if (box != textRenderer->lastTextBox() && + !box->nextOnLine() && + textOffset == box->start() + box->len()) { + return currentPos; + } + } + } + } + + return lastVisible; +} + +bool Position::hasRenderedNonAnonymousDescendantsWithHeight(RenderObject* renderer) +{ + RenderObject* stop = renderer->nextInPreOrderAfterChildren(); + for (RenderObject *o = renderer->firstChild(); o && o != stop; o = o->nextInPreOrder()) + if (o->element() && o->height()) + return true; + + return false; +} + +bool Position::nodeIsUserSelectNone(Node* node) +{ + return node && node->renderer() && node->renderer()->style()->userSelect() == SELECT_NONE; +} + +bool Position::isCandidate() const +{ + if (isNull()) + return false; + + RenderObject *renderer = node()->renderer(); + if (!renderer) + return false; + + if (renderer->style()->visibility() != VISIBLE) + return false; + + if (renderer->isBR()) + return offset() == 0 && !nodeIsUserSelectNone(node()->parent()); + + if (renderer->isText()) + return inRenderedText() && !nodeIsUserSelectNone(node()); + + if (isTableElement(node()) || editingIgnoresContent(node())) + return (offset() == 0 || offset() == maxDeepOffset(node())) && !nodeIsUserSelectNone(node()->parent()); + + if (!node()->hasTagName(htmlTag) && renderer->isBlockFlow() && !hasRenderedNonAnonymousDescendantsWithHeight(renderer) && + (renderer->height() || node()->hasTagName(bodyTag))) + return offset() == 0 && !nodeIsUserSelectNone(node()); + + return false; +} + +bool Position::inRenderedText() const +{ + if (isNull() || !node()->isTextNode()) + return false; + + RenderObject *renderer = node()->renderer(); + if (!renderer) + return false; + + RenderText *textRenderer = static_cast<RenderText *>(renderer); + for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (offset() < box->m_start && !textRenderer->containsReversedText()) { + // The offset we're looking for is before this node + // this means the offset must be in content that is + // not rendered. Return false. + return false; + } + if (box->containsCaretOffset(offset())) + // Return false for offsets inside composed characters. + return offset() == 0 || offset() == textRenderer->nextOffset(textRenderer->previousOffset(offset())); + } + + return false; +} + +static unsigned caretMaxRenderedOffset(const Node* n) +{ + RenderObject* r = n->renderer(); + if (r) + return r->caretMaxRenderedOffset(); + + if (n->isCharacterDataNode()) + return static_cast<const CharacterData*>(n)->length(); + return 1; +} + +bool Position::isRenderedCharacter() const +{ + if (isNull() || !node()->isTextNode()) + return false; + + RenderObject *renderer = node()->renderer(); + if (!renderer) + return false; + + RenderText *textRenderer = static_cast<RenderText *>(renderer); + for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (offset() < box->m_start && !textRenderer->containsReversedText()) { + // The offset we're looking for is before this node + // this means the offset must be in content that is + // not rendered. Return false. + return false; + } + if (offset() >= box->m_start && offset() < box->m_start + box->m_len) + return true; + } + + return false; +} + +bool Position::rendersInDifferentPosition(const Position &pos) const +{ + if (isNull() || pos.isNull()) + return false; + + RenderObject *renderer = node()->renderer(); + if (!renderer) + return false; + + RenderObject *posRenderer = pos.node()->renderer(); + if (!posRenderer) + return false; + + if (renderer->style()->visibility() != VISIBLE || + posRenderer->style()->visibility() != VISIBLE) + return false; + + if (node() == pos.node()) { + if (node()->hasTagName(brTag)) + return false; + + if (offset() == pos.offset()) + return false; + + if (!node()->isTextNode() && !pos.node()->isTextNode()) { + if (offset() != pos.offset()) + return true; + } + } + + if (node()->hasTagName(brTag) && pos.isCandidate()) + return true; + + if (pos.node()->hasTagName(brTag) && isCandidate()) + return true; + + if (node()->enclosingBlockFlowElement() != pos.node()->enclosingBlockFlowElement()) + return true; + + if (node()->isTextNode() && !inRenderedText()) + return false; + + if (pos.node()->isTextNode() && !pos.inRenderedText()) + return false; + + int thisRenderedOffset = renderedOffset(); + int posRenderedOffset = pos.renderedOffset(); + + if (renderer == posRenderer && thisRenderedOffset == posRenderedOffset) + return false; + + LOG(Editing, "renderer: %p [%p]\n", renderer, renderer ? renderer->inlineBox(offset()) : 0); + LOG(Editing, "thisRenderedOffset: %d\n", thisRenderedOffset); + LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, posRenderer ? posRenderer->inlineBox(offset()) : 0); + LOG(Editing, "posRenderedOffset: %d\n", posRenderedOffset); + LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(node()), caretMaxRenderedOffset(node())); + LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(pos.node()), caretMaxRenderedOffset(pos.node())); + LOG(Editing, "----------------------------------------------------------------------\n"); + + InlineBox *b1 = renderer ? renderer->inlineBox(offset()) : 0; + InlineBox *b2 = posRenderer ? posRenderer->inlineBox(pos.offset()) : 0; + + if (!b1 || !b2) { + return false; + } + + if (b1->root() != b2->root()) { + return true; + } + + if (nextRenderedEditable(node()) == pos.node() && + thisRenderedOffset == (int)caretMaxRenderedOffset(node()) && posRenderedOffset == 0) { + return false; + } + + if (previousRenderedEditable(node()) == pos.node() && + thisRenderedOffset == 0 && posRenderedOffset == (int)caretMaxRenderedOffset(pos.node())) { + return false; + } + + return true; +} + +// This assumes that it starts in editable content. +Position Position::leadingWhitespacePosition(EAffinity affinity, bool considerNonCollapsibleWhitespace) const +{ + ASSERT(isEditablePosition(*this)); + if (isNull()) + return Position(); + + if (upstream().node()->hasTagName(brTag)) + return Position(); + + Position prev = previousCharacterPosition(affinity); + if (prev != *this && prev.node()->inSameContainingBlockFlowElement(node()) && prev.node()->isTextNode()) { + String string = static_cast<Text *>(prev.node())->data(); + UChar c = string[prev.offset()]; + if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c)) + if (isEditablePosition(prev)) + return prev; + } + + return Position(); +} + +// This assumes that it starts in editable content. +Position Position::trailingWhitespacePosition(EAffinity affinity, bool considerNonCollapsibleWhitespace) const +{ + ASSERT(isEditablePosition(*this)); + if (isNull()) + return Position(); + + VisiblePosition v(*this); + UChar c = v.characterAfter(); + // The space must not be in another paragraph and it must be editable. + if (!isEndOfParagraph(v) && v.next(true).isNotNull()) + if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c)) + return *this; + + return Position(); +} + +void Position::debugPosition(const char* msg) const +{ + if (isNull()) + fprintf(stderr, "Position [%s]: null\n", msg); + else + fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, node()->nodeName().utf8().data(), node(), offset()); +} + +#ifndef NDEBUG + +void Position::formatForDebugger(char* buffer, unsigned length) const +{ + String result; + + if (isNull()) + result = "<null>"; + else { + char s[1024]; + result += "offset "; + result += String::number(m_offset); + result += " of "; + m_node->formatForDebugger(s, sizeof(s)); + result += s; + } + + strncpy(buffer, result.utf8().data(), length - 1); +} + +void Position::showTreeForThis() const +{ + if (m_node) + m_node->showTreeForThis(); +} + +#endif + +Position startPosition(const Range *r) +{ + if (!r || r->isDetached()) + return Position(); + ExceptionCode ec; + return Position(r->startContainer(ec), r->startOffset(ec)); +} + +Position endPosition(const Range *r) +{ + if (!r || r->isDetached()) + return Position(); + ExceptionCode ec; + return Position(r->endContainer(ec), r->endOffset(ec)); +} + +} // namespace WebCore + +#ifndef NDEBUG + +void showTree(const WebCore::Position& pos) +{ + pos.showTreeForThis(); +} + +void showTree(const WebCore::Position* pos) +{ + if (pos) + pos->showTreeForThis(); +} + +#endif diff --git a/WebCore/dom/Position.h b/WebCore/dom/Position.h new file mode 100644 index 0000000..8b99bc2 --- /dev/null +++ b/WebCore/dom/Position.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Position_h +#define Position_h + +#include "Node.h" +#include "TextAffinity.h" + +namespace WebCore { + +class CSSComputedStyleDeclaration; +class Element; +class PositionIterator; +class Range; + +enum EUsingComposedCharacters { NotUsingComposedCharacters = false, UsingComposedCharacters = true }; + +class Position +{ +public: + Position() : m_node(0), m_offset(0) { } + Position(Node*, int offset); + Position(const PositionIterator&); + + void clear(); + + Node *node() const { return m_node.get(); } + Element* documentElement() const; + int offset() const { return m_offset; } + + bool isNull() const { return m_node == 0; } + bool isNotNull() const { return m_node != 0; } + + Element* element() const; + PassRefPtr<CSSComputedStyleDeclaration> computedStyle() const; + + // Move up or down the DOM by one position. + // Offsets are computed using render text for nodes that have renderers - but note that even when + // using composed characters, the result may be inside a single user-visible character if a ligature is formed. + Position previous(EUsingComposedCharacters usingComposedCharacters=NotUsingComposedCharacters) const; + Position next(EUsingComposedCharacters usingComposedCharacters=NotUsingComposedCharacters) const; + static int uncheckedPreviousOffset(const Node*, int current); + static int uncheckedNextOffset(const Node*, int current); + + bool atStart() const; + bool atEnd() const; + + // FIXME: Make these non-member functions and put them somewhere in the editing directory. + // These aren't really basic "position" operations. More high level editing helper functions. + Position leadingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace = false) const; + Position trailingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace = false) const; + + // p.upstream() through p.downstream() is the range of positions that map to the same VisiblePosition as p. + Position upstream() const; + Position downstream() const; + + bool isCandidate() const; + bool inRenderedText() const; + bool isRenderedCharacter() const; + bool rendersInDifferentPosition(const Position &pos) const; + + static bool hasRenderedNonAnonymousDescendantsWithHeight(RenderObject*); + static bool nodeIsUserSelectNone(Node*); + + void debugPosition(const char* msg = "") const; + +#ifndef NDEBUG + void formatForDebugger(char* buffer, unsigned length) const; + void showTreeForThis() const; +#endif + +private: + int renderedOffset() const; + + Position previousCharacterPosition(EAffinity) const; + Position nextCharacterPosition(EAffinity) const; + RefPtr<Node> m_node; + int m_offset; +}; + +inline bool operator==(const Position &a, const Position &b) +{ + return a.node() == b.node() && a.offset() == b.offset(); +} + +inline bool operator!=(const Position &a, const Position &b) +{ + return !(a == b); +} + +Position startPosition(const Range*); +Position endPosition(const Range*); + +} // namespace WebCore + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::Position&); +void showTree(const WebCore::Position*); +#endif + +#endif // Position_h diff --git a/WebCore/dom/PositionIterator.cpp b/WebCore/dom/PositionIterator.cpp new file mode 100644 index 0000000..110b14e --- /dev/null +++ b/WebCore/dom/PositionIterator.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PositionIterator.h" + +#include "Node.h" +#include "RenderObject.h" +#include "htmlediting.h" + +namespace WebCore { + +using namespace HTMLNames; + +void PositionIterator::increment() +{ + if (!m_parent) + return; + + if (m_child) { + m_parent = m_child; + m_child = m_parent->firstChild(); + m_offset = 0; + return; + } + + if (!m_parent->hasChildNodes() && m_offset < maxDeepOffset(m_parent)) + m_offset = Position::uncheckedNextOffset(m_parent, m_offset); + else { + m_child = m_parent; + m_parent = m_child->parentNode(); + m_child = m_child->nextSibling(); + m_offset = 0; + } +} + +void PositionIterator::decrement() +{ + if (!m_parent) + return; + + if (m_child) { + m_parent = m_child->previousSibling(); + if (m_parent) { + m_child = 0; + m_offset = m_parent->hasChildNodes() ? 0 : maxDeepOffset(m_parent); + } else { + m_child = m_child->parentNode(); + m_parent = m_child->parentNode(); + m_offset = 0; + } + return; + } + + if (m_offset) { + m_offset = Position::uncheckedPreviousOffset(m_parent, m_offset); + } else { + if (m_parent->hasChildNodes()) { + m_parent = m_parent->lastChild(); + if (!m_parent->hasChildNodes()) + m_offset = maxDeepOffset(m_parent); + } else { + m_child = m_parent; + m_parent = m_parent->parentNode(); + } + } +} + +bool PositionIterator::atStart() const +{ + if (!m_parent) + return true; + if (m_parent->parentNode()) + return false; + return !m_parent->hasChildNodes() && !m_offset || m_child && !m_child->previousSibling(); +} + +bool PositionIterator::atEnd() const +{ + if (!m_parent) + return true; + if (m_child) + return false; + return !m_parent->parentNode() && (m_parent->hasChildNodes() || m_offset >= maxDeepOffset(m_parent)); +} + +bool PositionIterator::atStartOfNode() const +{ + if (!m_parent) + return true; + if (!m_child) + return !m_parent->hasChildNodes() && !m_offset; + return !m_child->previousSibling(); +} + +bool PositionIterator::atEndOfNode() const +{ + if (!m_parent) + return true; + if (m_child) + return false; + return m_parent->hasChildNodes() || m_offset >= maxDeepOffset(m_parent); +} + +bool PositionIterator::isCandidate() const +{ + if (!m_parent) + return false; + + RenderObject* renderer = m_parent->renderer(); + if (!renderer) + return false; + + if (renderer->style()->visibility() != VISIBLE) + return false; + + if (renderer->isBR()) + return !m_offset && !Position::nodeIsUserSelectNone(m_parent->parent()); + + if (renderer->isText()) + return Position(*this).inRenderedText() && !Position::nodeIsUserSelectNone(m_parent); + + if (isTableElement(m_parent) || editingIgnoresContent(m_parent)) + return (atStartOfNode() || atEndOfNode()) && !Position::nodeIsUserSelectNone(m_parent->parent()); + + if (!m_parent->hasTagName(htmlTag) && renderer->isBlockFlow() && !Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer) && + (renderer->height() || m_parent->hasTagName(bodyTag))) + return atStartOfNode() && !Position::nodeIsUserSelectNone(m_parent); + + return false; +} + +} // namespace WebCore diff --git a/WebCore/dom/PositionIterator.h b/WebCore/dom/PositionIterator.h new file mode 100644 index 0000000..9c0038f --- /dev/null +++ b/WebCore/dom/PositionIterator.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PositionIterator_h +#define PositionIterator_h + +#include "Position.h" + +namespace WebCore { + +// A Position iterator with constant-time +// increment, decrement, and several predicates on the Position it is at. +// Conversion to/from Position is O(n) in the offset. +class PositionIterator { +public: + PositionIterator() + : m_parent(0) + , m_child(0) + , m_offset(0) + { + } + + PositionIterator(const Position& pos) + : m_parent(pos.node()) + , m_child(m_parent->childNode(pos.offset())) + , m_offset(m_child ? 0 : pos.offset()) + { + } + + void increment(); + void decrement(); + + Node* node() const { return m_parent; } + int offsetInLeafNode() const { return m_offset; } + + bool atStart() const; + bool atEnd() const; + bool atStartOfNode() const; + bool atEndOfNode() const; + bool isCandidate() const; + +private: + friend class Position; + Node* m_parent; + Node* m_child; + int m_offset; +}; + +} // namespace WebCore + +#endif // PositionIterator_h diff --git a/WebCore/dom/ProcessingInstruction.cpp b/WebCore/dom/ProcessingInstruction.cpp new file mode 100644 index 0000000..34b00d4 --- /dev/null +++ b/WebCore/dom/ProcessingInstruction.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "ProcessingInstruction.h" + +#include "CSSStyleSheet.h" +#include "CachedCSSStyleSheet.h" +#include "CachedXSLStyleSheet.h" +#include "Document.h" +#include "DocLoader.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "XSLStyleSheet.h" +#include "XMLTokenizer.h" // for parseAttributes() + +namespace WebCore { + +ProcessingInstruction::ProcessingInstruction(Document* doc) + : ContainerNode(doc) + , m_cachedSheet(0) + , m_loading(false) +#if ENABLE(XSLT) + , m_isXSL(false) +#endif +{ +} + +ProcessingInstruction::ProcessingInstruction(Document* doc, const String& target, const String& data) + : ContainerNode(doc) + , m_target(target) + , m_data(data) + , m_cachedSheet(0) + , m_loading(false) +#if ENABLE(XSLT) + , m_isXSL(false) +#endif +{ +} + +ProcessingInstruction::~ProcessingInstruction() +{ + if (m_cachedSheet) + m_cachedSheet->deref(this); +} + +void ProcessingInstruction::setData(const String& data, ExceptionCode& ec) +{ + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly. + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + m_data = data; +} + +String ProcessingInstruction::nodeName() const +{ + return m_target; +} + +Node::NodeType ProcessingInstruction::nodeType() const +{ + return PROCESSING_INSTRUCTION_NODE; +} + +String ProcessingInstruction::nodeValue() const +{ + return m_data; +} + +void ProcessingInstruction::setNodeValue(const String& nodeValue, ExceptionCode& ec) +{ + // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData() + setData(nodeValue, ec); +} + +PassRefPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/) +{ + // ### copy m_localHref + return new ProcessingInstruction(document(), m_target, m_data); +} + +// DOM Section 1.1.1 +bool ProcessingInstruction::childTypeAllowed(NodeType) +{ + return false; +} + +bool ProcessingInstruction::checkStyleSheet() +{ + if (m_target == "xml-stylesheet") { + // see http://www.w3.org/TR/xml-stylesheet/ + // ### support stylesheet included in a fragment of this (or another) document + // ### make sure this gets called when adding from javascript + bool attrsOk; + const HashMap<String, String> attrs = parseAttributes(m_data, attrsOk); + if (!attrsOk) + return true; + HashMap<String, String>::const_iterator i = attrs.find("type"); + String type; + if (i != attrs.end()) + type = i->second; + + bool isCSS = type.isEmpty() || type == "text/css"; +#if ENABLE(XSLT) + m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" || + type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom=xml"); + if (!isCSS && !m_isXSL) +#else + if (!isCSS) +#endif + return true; + + String href = attrs.get("href"); + + if (href.length() > 1) { + if (href[0] == '#') { + m_localHref = href.substring(1); +#if ENABLE(XSLT) + // We need to make a synthetic XSLStyleSheet that is embedded. It needs to be able + // to kick off import/include loads that can hang off some parent sheet. + if (m_isXSL) { + m_sheet = new XSLStyleSheet(this, m_localHref, true); + m_loading = false; + } + return !m_isXSL; +#endif + } + else + { + // FIXME: some validation on the URL? + if (document()->frame()) { + m_loading = true; + document()->addPendingSheet(); + if (m_cachedSheet) + m_cachedSheet->deref(this); +#if ENABLE(XSLT) + if (m_isXSL) + m_cachedSheet = document()->docLoader()->requestXSLStyleSheet(document()->completeURL(href).string()); + else +#endif + { + String charset = attrs.get("charset"); + if (charset.isEmpty()) + charset = document()->frame()->loader()->encoding(); + + m_cachedSheet = document()->docLoader()->requestCSSStyleSheet(document()->completeURL(href).string(), charset); + } + if (m_cachedSheet) + m_cachedSheet->ref(this); +#if ENABLE(XSLT) + return !m_isXSL; +#endif + } + } + } + } + + return true; +} + +bool ProcessingInstruction::isLoading() const +{ + if (m_loading) + return true; + if (!m_sheet) + return false; + return m_sheet->isLoading(); +} + +bool ProcessingInstruction::sheetLoaded() +{ + if (!isLoading()) { + document()->removePendingSheet(); + return true; + } + return false; +} + +void ProcessingInstruction::setCSSStyleSheet(const String& url, const String& charset, const CachedCSSStyleSheet* sheet) +{ +#if ENABLE(XSLT) + ASSERT(!m_isXSL); +#endif + m_sheet = new CSSStyleSheet(this, url, charset); + parseStyleSheet(sheet->sheetText()); +} + +#if ENABLE(XSLT) +void ProcessingInstruction::setXSLStyleSheet(const String& url, const String& sheet) +{ + ASSERT(m_isXSL); + m_sheet = new XSLStyleSheet(this, url); + parseStyleSheet(sheet); +} +#endif + +void ProcessingInstruction::parseStyleSheet(const String& sheet) +{ + m_sheet->parseString(sheet, true); + if (m_cachedSheet) + m_cachedSheet->deref(this); + m_cachedSheet = 0; + + m_loading = false; + m_sheet->checkLoaded(); +} + +String ProcessingInstruction::toString() const +{ + String result = "<?"; + result += m_target; + result += " "; + result += m_data; + result += "?>"; + return result; +} + +void ProcessingInstruction::setCSSStyleSheet(CSSStyleSheet* sheet) +{ + ASSERT(!m_cachedSheet); + ASSERT(!m_loading); + m_sheet = sheet; +} + +bool ProcessingInstruction::offsetInCharacters() const +{ + return true; +} + +int ProcessingInstruction::maxCharacterOffset() const +{ + return static_cast<int>(m_data.length()); +} + +} // namespace diff --git a/WebCore/dom/ProcessingInstruction.h b/WebCore/dom/ProcessingInstruction.h new file mode 100644 index 0000000..be4a63e --- /dev/null +++ b/WebCore/dom/ProcessingInstruction.h @@ -0,0 +1,89 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef ProcessingInstruction_h +#define ProcessingInstruction_h + +#include "CachedResourceClient.h" +#include "ContainerNode.h" + +namespace WebCore { + +class StyleSheet; +class CSSStyleSheet; + +class ProcessingInstruction : public ContainerNode, private CachedResourceClient +{ +public: + ProcessingInstruction(Document*); + ProcessingInstruction(Document*, const String& target, const String& data); + virtual ~ProcessingInstruction(); + + // DOM methods & attributes for Notation + String target() const { return m_target; } + String data() const { return m_data; } + void setData(const String&, ExceptionCode&); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual String nodeValue() const; + virtual void setNodeValue(const String&, ExceptionCode&); + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); + virtual bool offsetInCharacters() const; + virtual int maxCharacterOffset() const; + + // Other methods (not part of DOM) + String localHref() const { return m_localHref; } + StyleSheet* sheet() const { return m_sheet.get(); } + bool checkStyleSheet(); + virtual void setCSSStyleSheet(const String& url, const String& charset, const CachedCSSStyleSheet*); +#if ENABLE(XSLT) + virtual void setXSLStyleSheet(const String& url, const String& sheet); +#endif + void setCSSStyleSheet(CSSStyleSheet*); + bool isLoading() const; + virtual bool sheetLoaded(); + virtual String toString() const; + +#if ENABLE(XSLT) + bool isXSL() const { return m_isXSL; } +#endif + +private: + void parseStyleSheet(const String& sheet); + + String m_target; + String m_data; + String m_localHref; + CachedResource* m_cachedSheet; + RefPtr<StyleSheet> m_sheet; + bool m_loading; +#if ENABLE(XSLT) + bool m_isXSL; +#endif +}; + +} //namespace + +#endif diff --git a/WebCore/dom/ProcessingInstruction.idl b/WebCore/dom/ProcessingInstruction.idl new file mode 100644 index 0000000..d0923f1 --- /dev/null +++ b/WebCore/dom/ProcessingInstruction.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=5947E8F8-B5CB-4a51-B883-B91F344F1E13, + ImplementationUUID=7EEC0376-3D76-4643-A964-97B8AC1FB6D3 + ] ProcessingInstruction : Node { + + // DOM Level 1 + + readonly attribute [ConvertNullStringTo=Null] DOMString target; + attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString data + setter raises(DOMException); + +#if !defined(LANGUAGE_COM) + // interface LinkStyle from DOM Level 2 Style Sheets + readonly attribute StyleSheet sheet; +#endif + + }; + +} diff --git a/WebCore/dom/ProgressEvent.cpp b/WebCore/dom/ProgressEvent.cpp new file mode 100644 index 0000000..99de500 --- /dev/null +++ b/WebCore/dom/ProgressEvent.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ProgressEvent.h" + +namespace WebCore { + +ProgressEvent::ProgressEvent() + : m_lengthComputable(false) + , m_loaded(0) + , m_total(0) +{ +} + +ProgressEvent::ProgressEvent(const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total) + : Event(type, false, true) + , m_lengthComputable(lengthComputable) + , m_loaded(loaded) + , m_total(total) +{ +} + +void ProgressEvent::initProgressEvent(const AtomicString& typeArg, + bool canBubbleArg, + bool cancelableArg, + bool lengthComputableArg, + unsigned loadedArg, + unsigned totalArg) +{ + if (dispatched()) + return; + + initEvent(typeArg, canBubbleArg, cancelableArg); + + m_lengthComputable = lengthComputableArg; + m_loaded = loadedArg; + m_total = totalArg; +} + +} diff --git a/WebCore/dom/ProgressEvent.h b/WebCore/dom/ProgressEvent.h new file mode 100644 index 0000000..e215091 --- /dev/null +++ b/WebCore/dom/ProgressEvent.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ProgressEvent_h +#define ProgressEvent_h + +#include "Event.h" + +namespace WebCore { + + class ProgressEvent : public Event { + public: + ProgressEvent(); + ProgressEvent(const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total); + + void initProgressEvent(const AtomicString& typeArg, + bool canBubbleArg, + bool cancelableArg, + bool lengthComputableArg, + unsigned loadedArg, + unsigned totalArg); + + bool lengthComputable() const { return m_lengthComputable; } + unsigned loaded() const { return m_loaded; } + unsigned total() const { return m_total; } + + virtual bool isProgressEvent() const { return true; } + + private: + bool m_lengthComputable; + unsigned m_loaded; + unsigned m_total; + }; +} + +#endif // ProgressEvent_h + diff --git a/WebCore/dom/ProgressEvent.idl b/WebCore/dom/ProgressEvent.idl new file mode 100644 index 0000000..2db72af --- /dev/null +++ b/WebCore/dom/ProgressEvent.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module events { + + interface [ + GenerateConstructor + ] ProgressEvent : Event { + readonly attribute boolean lengthComputable; + readonly attribute unsigned long loaded; + readonly attribute unsigned long total; + void initProgressEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in boolean lengthComputableArg, + in unsigned long loadedArg, + in unsigned long totalArg); + }; + +} diff --git a/WebCore/dom/QualifiedName.cpp b/WebCore/dom/QualifiedName.cpp new file mode 100644 index 0000000..c9d7e28 --- /dev/null +++ b/WebCore/dom/QualifiedName.cpp @@ -0,0 +1,181 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" + +#ifdef AVOID_STATIC_CONSTRUCTORS +#define WEBCORE_QUALIFIEDNAME_HIDE_GLOBALS 1 +#else +#define QNAME_DEFAULT_CONSTRUCTOR +#endif + +#include "QualifiedName.h" +#include "StaticConstructors.h" +#include <wtf/Assertions.h> +#include <wtf/HashSet.h> + +namespace WebCore { + +struct QualifiedNameComponents { + StringImpl* m_prefix; + StringImpl* m_localName; + StringImpl* m_namespace; +}; + +// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's +static const unsigned PHI = 0x9e3779b9U; + +static inline unsigned hashComponents(const QualifiedNameComponents& buf) +{ + ASSERT(sizeof(QualifiedNameComponents) % (sizeof(uint16_t) * 2) == 0); + + unsigned l = sizeof(QualifiedNameComponents) / (sizeof(uint16_t) * 2); + const uint16_t* s = reinterpret_cast<const uint16_t*>(&buf); + uint32_t hash = PHI; + + // Main loop + for (; l > 0; l--) { + hash += s[0]; + uint32_t tmp = (s[1] << 11) ^ hash; + hash = (hash << 16) ^ tmp; + s += 2; + hash += hash >> 11; + } + + // Force "avalanching" of final 127 bits + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 2; + hash += hash >> 15; + hash ^= hash << 10; + + // this avoids ever returning a hash code of 0, since that is used to + // signal "hash not computed yet", using a value that is likely to be + // effectively the same as 0 when the low bits are masked + if (hash == 0) + hash = 0x80000000; + + return hash; +} + +struct QNameHash { + static unsigned hash(const QualifiedName::QualifiedNameImpl* name) { + QualifiedNameComponents c = { name->m_prefix.impl(), name->m_localName.impl(), name->m_namespace.impl() }; + return hashComponents(c); + } + + static bool equal(const QualifiedName::QualifiedNameImpl* a, const QualifiedName::QualifiedNameImpl* b) { return a == b; } + + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +typedef HashSet<QualifiedName::QualifiedNameImpl*, QNameHash> QNameSet; + +struct QNameComponentsTranslator { + static unsigned hash(const QualifiedNameComponents& components) { + return hashComponents(components); + } + static bool equal(QualifiedName::QualifiedNameImpl* name, const QualifiedNameComponents& c) { + return c.m_prefix == name->m_prefix.impl() && c.m_localName == name->m_localName.impl() && c.m_namespace == name->m_namespace.impl(); + } + static void translate(QualifiedName::QualifiedNameImpl*& location, const QualifiedNameComponents& components, unsigned hash) { + location = QualifiedName::QualifiedNameImpl::create(components.m_prefix, components.m_localName, components.m_namespace).releaseRef(); + } +}; + +static QNameSet* gNameCache; + +QualifiedName::QualifiedName(const AtomicString& p, const AtomicString& l, const AtomicString& n) + : m_impl(0) +{ + if (!gNameCache) + gNameCache = new QNameSet; + QualifiedNameComponents components = { p.impl(), l.impl(), n.impl() }; + pair<QNameSet::iterator, bool> addResult = gNameCache->add<QualifiedNameComponents, QNameComponentsTranslator>(components); + m_impl = *addResult.first; + if (!addResult.second) + m_impl->ref(); +} + +QualifiedName::~QualifiedName() +{ + deref(); +} + +QualifiedName::QualifiedName(const QualifiedName& other) +{ + m_impl = other.m_impl; + ref(); +} + +const QualifiedName& QualifiedName::operator=(const QualifiedName& other) +{ + if (m_impl != other.m_impl) { + deref(); + m_impl = other.m_impl; + ref(); + } + + return *this; +} + +void QualifiedName::deref() +{ +#ifdef QNAME_DEFAULT_CONSTRUCTOR + if (!m_impl) + return; +#endif + + if (m_impl->hasOneRef()) + gNameCache->remove(m_impl); + m_impl->deref(); +} + +void QualifiedName::setPrefix(const AtomicString& prefix) +{ + QualifiedName other(prefix, localName(), namespaceURI()); + *this = other; +} + +String QualifiedName::toString() const +{ + String local = localName(); + if (hasPrefix()) + return prefix() + ":" + local; + return local; +} + +// Global init routines +DEFINE_GLOBAL(QualifiedName, anyName, nullAtom, starAtom, starAtom) + +void QualifiedName::init() +{ + static bool initialized; + if (!initialized) { + // Use placement new to initialize the globals. + + AtomicString::init(); + new ((void*)&anyName) QualifiedName(nullAtom, starAtom, starAtom); + initialized = true; + } +} + +} diff --git a/WebCore/dom/QualifiedName.h b/WebCore/dom/QualifiedName.h new file mode 100644 index 0000000..5aa8abf --- /dev/null +++ b/WebCore/dom/QualifiedName.h @@ -0,0 +1,97 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2005 Apple Computer, Inc. + * + * 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. + * + */ +#ifndef QualifiedName_h +#define QualifiedName_h + +#include "AtomicString.h" + +namespace WebCore { + +class QualifiedName { +public: + class QualifiedNameImpl : public RefCounted<QualifiedNameImpl> { + public: + static PassRefPtr<QualifiedNameImpl> create(const AtomicString& p, const AtomicString& l, const AtomicString& n) + { + return adoptRef(new QualifiedNameImpl(p, l, n)); + } + + AtomicString m_prefix; + AtomicString m_localName; + AtomicString m_namespace; + + private: + QualifiedNameImpl(const AtomicString& p, const AtomicString& l, const AtomicString& n) + : m_prefix(p) + , m_localName(l) + , m_namespace(n) + { + } + }; + + QualifiedName(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI); + ~QualifiedName(); +#ifdef QNAME_DEFAULT_CONSTRUCTOR + QualifiedName() : m_impl(0) { } +#endif + + QualifiedName(const QualifiedName&); + const QualifiedName& operator=(const QualifiedName&); + + bool operator==(const QualifiedName& other) const { return m_impl == other.m_impl; } + bool operator!=(const QualifiedName& other) const { return !(*this == other); } + + bool matches(const QualifiedName& other) const { return m_impl == other.m_impl || (localName() == other.localName() && namespaceURI() == other.namespaceURI()); } + + bool hasPrefix() const { return m_impl->m_prefix != nullAtom; } + void setPrefix(const AtomicString& prefix); + + const AtomicString& prefix() const { return m_impl->m_prefix; } + const AtomicString& localName() const { return m_impl->m_localName; } + const AtomicString& namespaceURI() const { return m_impl->m_namespace; } + + String toString() const; + + QualifiedNameImpl* impl() const { return m_impl; } + + // Init routine for globals + static void init(); + +private: + void ref() { m_impl->ref(); } + void deref(); + + QualifiedNameImpl* m_impl; +}; + +#ifndef WEBCORE_QUALIFIEDNAME_HIDE_GLOBALS +extern const QualifiedName anyName; +inline const QualifiedName& anyQName() { return anyName; } +#endif + +inline bool operator==(const AtomicString& a, const QualifiedName& q) { return a == q.localName(); } +inline bool operator!=(const AtomicString& a, const QualifiedName& q) { return a != q.localName(); } +inline bool operator==(const QualifiedName& q, const AtomicString& a) { return a == q.localName(); } +inline bool operator!=(const QualifiedName& q, const AtomicString& a) { return a != q.localName(); } + +} +#endif diff --git a/WebCore/dom/Range.cpp b/WebCore/dom/Range.cpp new file mode 100644 index 0000000..88292c3 --- /dev/null +++ b/WebCore/dom/Range.cpp @@ -0,0 +1,1718 @@ +/** + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Gunnstein Lye (gunnstein@netcom.no) + * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "Range.h" + +#include "CString.h" +#include "Document.h" +#include "DocumentFragment.h" +#include "ExceptionCode.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "ProcessingInstruction.h" +#include "RangeException.h" +#include "RenderBlock.h" +#include "Text.h" +#include "TextIterator.h" +#include "markup.h" +#include "visible_units.h" +#include <stdio.h> + +namespace WebCore { + +using namespace std; +using namespace HTMLNames; + +#ifndef NDEBUG +class RangeCounter { +public: + static unsigned count; + ~RangeCounter() + { + if (count) + fprintf(stderr, "LEAK: %u Range\n", count); + } +}; +unsigned RangeCounter::count = 0; +static RangeCounter rangeCounter; +#endif + +Range::Range(Document* ownerDocument) + : RefCounted<Range>(0) + , m_ownerDocument(ownerDocument) + , m_startContainer(ownerDocument) + , m_startOffset(0) + , m_endContainer(ownerDocument) + , m_endOffset(0) + , m_detached(false) +{ +#ifndef NDEBUG + ++RangeCounter::count; +#endif +} + +Range::Range(Document* ownerDocument, + Node* startContainer, int startOffset, + Node* endContainer, int endOffset) + : RefCounted<Range>(0) + , m_ownerDocument(ownerDocument) + , m_startContainer(ownerDocument) + , m_startOffset(0) + , m_endContainer(ownerDocument) + , m_endOffset(0) + , m_detached(false) +{ +#ifndef NDEBUG + ++RangeCounter::count; +#endif + // Simply setting the containers and offsets directly would not do any of the checking + // that setStart and setEnd do, so we must call those functions. + ExceptionCode ec = 0; + setStart(startContainer, startOffset, ec); + ASSERT(ec == 0); + setEnd(endContainer, endOffset, ec); + ASSERT(ec == 0); +} + +Range::Range(Document* ownerDocument, const Position& start, const Position& end) + : RefCounted<Range>(0) + , m_ownerDocument(ownerDocument) + , m_startContainer(ownerDocument) + , m_startOffset(0) + , m_endContainer(ownerDocument) + , m_endOffset(0) + , m_detached(false) +{ +#ifndef NDEBUG + ++RangeCounter::count; +#endif + // Simply setting the containers and offsets directly would not do any of the checking + // that setStart and setEnd do, so we must call those functions. + ExceptionCode ec = 0; + setStart(start.node(), start.offset(), ec); + ASSERT(ec == 0); + setEnd(end.node(), end.offset(), ec); + ASSERT(ec == 0); +} + +Range::~Range() +{ +#ifndef NDEBUG + --RangeCounter::count; +#endif +} + +Node *Range::startContainer(ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + return m_startContainer.get(); +} + +int Range::startOffset(ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + return m_startOffset; +} + +Node *Range::endContainer(ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + return m_endContainer.get(); +} + +int Range::endOffset(ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + return m_endOffset; +} + +Node *Range::commonAncestorContainer(ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + Node *com = commonAncestorContainer(m_startContainer.get(), m_endContainer.get()); + if (!com) // should never happen + ec = WRONG_DOCUMENT_ERR; + return com; +} + +Node *Range::commonAncestorContainer(Node *containerA, Node *containerB) +{ + Node *parentStart; + + for (parentStart = containerA; parentStart; parentStart = parentStart->parentNode()) { + Node *parentEnd = containerB; + while (parentEnd && (parentStart != parentEnd)) + parentEnd = parentEnd->parentNode(); + if (parentStart == parentEnd) + break; + } + + return parentStart; +} + +bool Range::collapsed(ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + return (m_startContainer == m_endContainer && m_startOffset == m_endOffset); +} + +void Range::setStart( Node *refNode, int offset, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + ec = 0; + checkNodeWOffset(refNode, offset, ec); + if (ec) + return; + + m_startContainer = refNode; + m_startOffset = offset; + + // check if different root container + Node* endRootContainer = m_endContainer.get(); + while (endRootContainer->parentNode()) + endRootContainer = endRootContainer->parentNode(); + Node* startRootContainer = m_startContainer.get(); + while (startRootContainer->parentNode()) + startRootContainer = startRootContainer->parentNode(); + if (startRootContainer != endRootContainer) + collapse(true, ec); + // check if new start after end + else if (compareBoundaryPoints(m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset) > 0) + collapse(true, ec); +} + +void Range::setEnd( Node *refNode, int offset, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + ec = 0; + checkNodeWOffset(refNode, offset, ec); + if (ec) + return; + + m_endContainer = refNode; + m_endOffset = offset; + + // check if different root container + Node* endRootContainer = m_endContainer.get(); + while (endRootContainer->parentNode()) + endRootContainer = endRootContainer->parentNode(); + Node* startRootContainer = m_startContainer.get(); + while (startRootContainer->parentNode()) + startRootContainer = startRootContainer->parentNode(); + if (startRootContainer != endRootContainer) + collapse(false, ec); + // check if new end before start + if (compareBoundaryPoints(m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset) > 0) + collapse(false, ec); +} + +void Range::collapse( bool toStart, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (toStart) { // collapse to start + m_endContainer = m_startContainer; + m_endOffset = m_startOffset; + } else { // collapse to end + m_startContainer = m_endContainer; + m_startOffset = m_endOffset; + } +} + +bool Range::isPointInRange(Node* refNode, int offset, ExceptionCode& ec) +{ + if (!refNode) { + ec = NOT_FOUND_ERR; + return false; + } + + if (m_detached && refNode->attached()) { + ec = INVALID_STATE_ERR; + return false; + } + + if (!m_detached && !refNode->attached()) { + // firefox doesn't throw an exception for this case; it returns false + return false; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return false; + } + + ec = 0; + checkNodeWOffset(refNode, offset, ec); + if (ec) + return false; + + // point is not before the start and not after the end + if ((compareBoundaryPoints(refNode, offset, m_startContainer.get(), m_startOffset) != -1) && + (compareBoundaryPoints(refNode, offset, m_endContainer.get(), m_endOffset) != 1)) + return true; + else + return false; +} + +short Range::comparePoint(Node* refNode, int offset, ExceptionCode& ec) +{ + // http://developer.mozilla.org/en/docs/DOM:range.comparePoint + // This method returns -1, 0 or 1 depending on if the point described by the + // refNode node and an offset within the node is before, same as, or after the range respectively. + + if (!refNode) { + ec = NOT_FOUND_ERR; + return 0; + } + + if (m_detached && refNode->attached()) { + ec = INVALID_STATE_ERR; + return 0; + } + + if (!m_detached && !refNode->attached()) { + // firefox doesn't throw an exception for this case; it returns -1 + return -1; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return 0; + } + + ec = 0; + checkNodeWOffset(refNode, offset, ec); + if (ec) + return 0; + + // compare to start, and point comes before + if (compareBoundaryPoints(refNode, offset, m_startContainer.get(), m_startOffset) == -1) + return -1; + + // compare to end, and point comes after + if (compareBoundaryPoints(refNode, offset, m_endContainer.get(), m_endOffset) == 1) + return 1; + + // point is in the middle of this range, or on the boundary points + return 0; +} + +Range::CompareResults Range::compareNode(Node* refNode, ExceptionCode& ec) +{ + // http://developer.mozilla.org/en/docs/DOM:range.compareNode + // This method returns 0, 1, 2, or 3 based on if the node is before, after, + // before and after(surrounds), or inside the range, respectively + + if (!refNode) { + ec = NOT_FOUND_ERR; + return NODE_BEFORE; + } + + if (m_detached && refNode->attached()) { + ec = INVALID_STATE_ERR; + return NODE_BEFORE; + } + + if (!m_detached && !refNode->attached()) { + // firefox doesn't throw an exception for this case; it returns 0 + return NODE_BEFORE; + } + + if (refNode->document() != m_ownerDocument) { + // firefox doesn't throw an exception for this case; it returns 0 + return NODE_BEFORE; + } + + Node* parentNode = refNode->parentNode(); + unsigned nodeIndex = refNode->nodeIndex(); + + if (!parentNode) { + // if the node is the top document we should return NODE_BEFORE_AND_AFTER + // but we throw to match firefox behavior + ec = NOT_FOUND_ERR; + return NODE_BEFORE; + } + + if (comparePoint(parentNode, nodeIndex, ec) == -1) { // starts before + if (comparePoint(parentNode, nodeIndex + 1, ec) == 1) // ends after the range + return NODE_BEFORE_AND_AFTER; + return NODE_BEFORE; // ends before or in the range + } else { // starts at or after the range start + if (comparePoint(parentNode, nodeIndex + 1, ec) == 1) // ends after the range + return NODE_AFTER; + return NODE_INSIDE; // ends inside the range + } +} + + +short Range::compareBoundaryPoints(CompareHow how, const Range *sourceRange, ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + if (!sourceRange) { + ec = NOT_FOUND_ERR; + return 0; + } + + ec = 0; + Node *thisCont = commonAncestorContainer(ec); + if (ec) + return 0; + Node *sourceCont = sourceRange->commonAncestorContainer(ec); + if (ec) + return 0; + + if (thisCont->document() != sourceCont->document()) { + ec = WRONG_DOCUMENT_ERR; + return 0; + } + + Node *thisTop = thisCont; + Node *sourceTop = sourceCont; + while (thisTop->parentNode()) + thisTop = thisTop->parentNode(); + while (sourceTop->parentNode()) + sourceTop = sourceTop->parentNode(); + if (thisTop != sourceTop) { // in different DocumentFragments + ec = WRONG_DOCUMENT_ERR; + return 0; + } + + switch(how) + { + case START_TO_START: + return compareBoundaryPoints( m_startContainer.get(), m_startOffset, + sourceRange->startContainer(ec), sourceRange->startOffset(ec) ); + break; + case START_TO_END: + return compareBoundaryPoints( m_startContainer.get(), m_startOffset, + sourceRange->endContainer(ec), sourceRange->endOffset(ec) ); + break; + case END_TO_END: + return compareBoundaryPoints( m_endContainer.get(), m_endOffset, + sourceRange->endContainer(ec), sourceRange->endOffset(ec) ); + break; + case END_TO_START: + return compareBoundaryPoints( m_endContainer.get(), m_endOffset, + sourceRange->startContainer(ec), sourceRange->startOffset(ec) ); + break; + default: + ec = SYNTAX_ERR; + return 0; + } +} + +short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *containerB, int offsetB ) +{ + ASSERT(containerA && containerB); + if (!containerA) + return -1; + if (!containerB) + return 1; + // see DOM2 traversal & range section 2.5 + + // case 1: both points have the same container + if(containerA == containerB) { + if (offsetA == offsetB) + return 0; // A is equal to B + if (offsetA < offsetB) + return -1; // A is before B + else + return 1; // A is after B + } + + // case 2: node C (container B or an ancestor) is a child node of A + Node *c = containerB; + while (c && c->parentNode() != containerA) + c = c->parentNode(); + if (c) { + int offsetC = 0; + Node *n = containerA->firstChild(); + while (n != c && offsetC < offsetA) { + offsetC++; + n = n->nextSibling(); + } + + if (offsetA <= offsetC) + return -1; // A is before B + else + return 1; // A is after B + } + + // case 3: node C (container A or an ancestor) is a child node of B + c = containerA; + while (c && c->parentNode() != containerB) + c = c->parentNode(); + if (c) { + int offsetC = 0; + Node *n = containerB->firstChild(); + while (n != c && offsetC < offsetB) { + offsetC++; + n = n->nextSibling(); + } + + if(offsetC < offsetB) + return -1; // A is before B + else + return 1; // A is after B + } + + // case 4: containers A & B are siblings, or children of siblings + // ### we need to do a traversal here instead + Node *cmnRoot = commonAncestorContainer(containerA,containerB); + if (!cmnRoot) + return 0; + Node *childA = containerA; + while (childA && childA->parentNode() != cmnRoot) + childA = childA->parentNode(); + if (!childA) + childA = cmnRoot; + Node *childB = containerB; + while (childB && childB->parentNode() != cmnRoot) + childB = childB->parentNode(); + if (!childB) + childB = cmnRoot; + + if (childA == childB) + return 0; // A is equal to B + + Node *n = cmnRoot->firstChild(); + while (n) { + if (n == childA) + return -1; // A is before B + if (n == childB) + return 1; // A is after B + n = n->nextSibling(); + } + + // Should never reach this point. + ASSERT(0); + return 0; +} + +short Range::compareBoundaryPoints( const Position &a, const Position &b ) +{ + return compareBoundaryPoints(a.node(), a.offset(), b.node(), b.offset()); +} + +bool Range::boundaryPointsValid() const +{ + return m_startContainer && m_endContainer && compareBoundaryPoints(m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset) <= 0; +} + +void Range::deleteContents(ExceptionCode& ec) { + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + ec = 0; + checkDeleteExtract(ec); + if (ec) + return; + + processContents(DELETE_CONTENTS,ec); +} + +bool Range::intersectsNode(Node* refNode, ExceptionCode& ec) +{ + // http://developer.mozilla.org/en/docs/DOM:range.intersectsNode + // Returns a bool if the node intersects the range. + + if (!refNode) { + ec = NOT_FOUND_ERR; + return false; + } + + if (m_detached && refNode->attached() || + !m_detached && !refNode->attached() || + refNode->document() != m_ownerDocument) + // firefox doesn't throw an exception for these case; it returns false + return false; + + Node* parentNode = refNode->parentNode(); + unsigned nodeIndex = refNode->nodeIndex(); + + if (!parentNode) { + // if the node is the top document we should return NODE_BEFORE_AND_AFTER + // but we throw to match firefox behavior + ec = NOT_FOUND_ERR; + return false; + } + + if (comparePoint(parentNode, nodeIndex, ec) == -1 && // starts before start + comparePoint(parentNode, nodeIndex + 1, ec) == -1) { // ends before start + return false; + } else if(comparePoint(parentNode, nodeIndex, ec) == 1 && // starts after end + comparePoint(parentNode, nodeIndex + 1, ec) == 1) { // ends after end + return false; + } + + return true; //all other cases +} + +PassRefPtr<DocumentFragment> Range::processContents ( ActionType action, ExceptionCode& ec) +{ + // ### when mutation events are implemented, we will have to take into account + // situations where the tree is being transformed while we delete - ugh! + + // ### perhaps disable node deletion notification for this range while we do this? + + RefPtr<DocumentFragment> fragment; + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + fragment = new DocumentFragment(m_ownerDocument.get()); + + ec = 0; + if (collapsed(ec)) + return fragment.release(); + if (ec) + return 0; + + Node *cmnRoot = commonAncestorContainer(ec); + if (ec) + return 0; + + // what is the highest node that partially selects the start of the range? + Node *partialStart = 0; + if (m_startContainer != cmnRoot) { + partialStart = m_startContainer.get(); + while (partialStart->parentNode() != cmnRoot) + partialStart = partialStart->parentNode(); + } + + // what is the highest node that partially selects the end of the range? + Node *partialEnd = 0; + if (m_endContainer != cmnRoot) { + partialEnd = m_endContainer.get(); + while (partialEnd->parentNode() != cmnRoot) + partialEnd = partialEnd->parentNode(); + } + + // Simple case: the start and end containers are the same. We just grab + // everything >= start offset and < end offset + if (m_startContainer == m_endContainer) { + if(m_startContainer->nodeType() == Node::TEXT_NODE || + m_startContainer->nodeType() == Node::CDATA_SECTION_NODE || + m_startContainer->nodeType() == Node::COMMENT_NODE) { + + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_startContainer->cloneNode(true)); + c->deleteData(m_endOffset, c->length() - m_endOffset, ec); + c->deleteData(0, m_startOffset, ec); + fragment->appendChild(c.release(), ec); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + static_cast<CharacterData*>(m_startContainer.get())->deleteData(m_startOffset,m_endOffset-m_startOffset,ec); + m_startContainer->document()->updateLayout(); + } + } + else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_startContainer->cloneNode(true)); + c->setData(c->data().substring(m_startOffset, m_endOffset - m_startOffset), ec); + fragment->appendChild(c.release(), ec); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + ProcessingInstruction* pi= static_cast<ProcessingInstruction*>(m_startContainer.get()); + String data(pi->data()); + data.remove(m_startOffset, m_endOffset - m_startOffset); + pi->setData(data, ec); + } + } + else { + Node *n = m_startContainer->firstChild(); + unsigned i; + for (i = 0; n && i < m_startOffset; i++) // skip until m_startOffset + n = n->nextSibling(); + while (n && i < m_endOffset) { // delete until m_endOffset + Node *next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + fragment->appendChild(n,ec); // will remove n from it's parent + else if (action == CLONE_CONTENTS) + fragment->appendChild(n->cloneNode(true),ec); + else + m_startContainer->removeChild(n,ec); + n = next; + i++; + } + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + collapse(true,ec); + return fragment.release(); + } + + // Complex case: Start and end containers are different. + // There are three possiblities here: + // 1. Start container == cmnRoot (End container must be a descendant) + // 2. End container == cmnRoot (Start container must be a descendant) + // 3. Neither is cmnRoot, they are both descendants + // + // In case 3, we grab everything after the start (up until a direct child + // of cmnRoot) into leftContents, and everything before the end (up until + // a direct child of cmnRoot) into rightContents. Then we process all + // cmnRoot children between leftContents and rightContents + // + // In case 1 or 2, we skip either processing of leftContents or rightContents, + // in which case the last lot of nodes either goes from the first or last + // child of cmnRoot. + // + // These are deleted, cloned, or extracted (i.e. both) depending on action. + + RefPtr<Node> leftContents; + if (m_startContainer != cmnRoot) { + // process the left-hand side of the range, up until the last ancestor of + // m_startContainer before cmnRoot + if(m_startContainer->nodeType() == Node::TEXT_NODE || + m_startContainer->nodeType() == Node::CDATA_SECTION_NODE || + m_startContainer->nodeType() == Node::COMMENT_NODE) { + + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_startContainer->cloneNode(true)); + c->deleteData(0, m_startOffset, ec); + leftContents = c.release(); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + static_cast<CharacterData*>(m_startContainer.get())->deleteData( + m_startOffset, static_cast<CharacterData*>(m_startContainer.get())->length() - m_startOffset, ec); + m_startContainer->document()->updateLayout(); + } + } + else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_startContainer->cloneNode(true)); + c->setData(c->data().substring(m_startOffset), ec); + leftContents = c.release(); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + ProcessingInstruction* pi= static_cast<ProcessingInstruction*>(m_startContainer.get()); + String data(pi->data()); + pi->setData(data.left(m_startOffset), ec); + } + } + else { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + leftContents = m_startContainer->cloneNode(false); + Node *n = m_startContainer->firstChild(); + for (unsigned i = 0; n && i < m_startOffset; i++) // skip until m_startOffset + n = n->nextSibling(); + while (n) { // process until end + Node *next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + leftContents->appendChild(n,ec); // will remove n from m_startContainer + else if (action == CLONE_CONTENTS) + leftContents->appendChild(n->cloneNode(true),ec); + else + m_startContainer->removeChild(n,ec); + n = next; + } + } + + Node *leftParent = m_startContainer->parentNode(); + Node *n = m_startContainer->nextSibling(); + for (; leftParent != cmnRoot; leftParent = leftParent->parentNode()) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<Node> leftContentsParent = leftParent->cloneNode(false); + leftContentsParent->appendChild(leftContents,ec); + leftContents = leftContentsParent; + } + + Node *next; + for (; n; n = next) { + next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + leftContents->appendChild(n,ec); // will remove n from leftParent + else if (action == CLONE_CONTENTS) + leftContents->appendChild(n->cloneNode(true),ec); + else + leftParent->removeChild(n,ec); + } + n = leftParent->nextSibling(); + } + } + + RefPtr<Node> rightContents = 0; + if (m_endContainer != cmnRoot) { + // delete the right-hand side of the range, up until the last ancestor of + // m_endContainer before cmnRoot + if(m_endContainer->nodeType() == Node::TEXT_NODE || + m_endContainer->nodeType() == Node::CDATA_SECTION_NODE || + m_endContainer->nodeType() == Node::COMMENT_NODE) { + + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_endContainer->cloneNode(true)); + c->deleteData(m_endOffset, static_cast<CharacterData*>(m_endContainer.get())->length() - m_endOffset, ec); + rightContents = c; + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + static_cast<CharacterData*>(m_endContainer.get())->deleteData(0, m_endOffset, ec); + m_startContainer->document()->updateLayout(); + } + } + else if (m_endContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_endContainer->cloneNode(true)); + c->setData(c->data().left(m_endOffset), ec); + rightContents = c.release(); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + ProcessingInstruction* pi= static_cast<ProcessingInstruction*>(m_endContainer.get()); + pi->setData(pi->data().substring(m_endOffset), ec); + } + } + else { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + rightContents = m_endContainer->cloneNode(false); + Node *n = m_endContainer->firstChild(); + if (n && m_endOffset) { + for (unsigned i = 0; i+1 < m_endOffset; i++) { // skip to m_endOffset + Node *next = n->nextSibling(); + if (!next) + break; + n = next; + } + Node *prev; + for (; n; n = prev) { + prev = n->previousSibling(); + if (action == EXTRACT_CONTENTS) + rightContents->insertBefore(n,rightContents->firstChild(),ec); // will remove n from it's parent + else if (action == CLONE_CONTENTS) + rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),ec); + else + m_endContainer->removeChild(n,ec); + } + } + } + + Node *rightParent = m_endContainer->parentNode(); + Node *n = m_endContainer->previousSibling(); + for (; rightParent != cmnRoot; rightParent = rightParent->parentNode()) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<Node> rightContentsParent = rightParent->cloneNode(false); + rightContentsParent->appendChild(rightContents,ec); + rightContents = rightContentsParent; + } + + Node *prev; + for (; n; n = prev) { + prev = n->previousSibling(); + if (action == EXTRACT_CONTENTS) + rightContents->insertBefore(n,rightContents->firstChild(),ec); // will remove n from it's parent + else if (action == CLONE_CONTENTS) + rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),ec); + else + rightParent->removeChild(n,ec); + } + n = rightParent->previousSibling(); + } + } + + // delete all children of cmnRoot between the start and end container + + Node *processStart; // child of cmnRooot + if (m_startContainer == cmnRoot) { + unsigned i; + processStart = m_startContainer->firstChild(); + for (i = 0; i < m_startOffset; i++) + processStart = processStart->nextSibling(); + } + else { + processStart = m_startContainer.get(); + while (processStart->parentNode() != cmnRoot) + processStart = processStart->parentNode(); + processStart = processStart->nextSibling(); + } + Node *processEnd; // child of cmnRooot + if (m_endContainer == cmnRoot) { + unsigned i; + processEnd = m_endContainer->firstChild(); + for (i = 0; i < m_endOffset; i++) + processEnd = processEnd->nextSibling(); + } + else { + processEnd = m_endContainer.get(); + while (processEnd->parentNode() != cmnRoot) + processEnd = processEnd->parentNode(); + } + + // Now add leftContents, stuff in between, and rightContents to the fragment + // (or just delete the stuff in between) + + if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && leftContents) + fragment->appendChild(leftContents,ec); + + Node *next; + Node *n; + if (processStart) { + for (n = processStart; n && n != processEnd; n = next) { + next = n->nextSibling(); + + if (action == EXTRACT_CONTENTS) + fragment->appendChild(n,ec); // will remove from cmnRoot + else if (action == CLONE_CONTENTS) + fragment->appendChild(n->cloneNode(true),ec); + else + cmnRoot->removeChild(n,ec); + } + } + + if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents) + fragment->appendChild(rightContents,ec); + + // collapse to the proper position - see spec section 2.6 + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + if (!partialStart && !partialEnd) + collapse(true,ec); + else if (partialStart) { + m_startContainer = partialStart->parentNode(); + m_endContainer = partialStart->parentNode(); + m_startOffset = m_endOffset = partialStart->nodeIndex()+1; + } + else if (partialEnd) { + m_startContainer = partialEnd->parentNode(); + m_endContainer = partialEnd->parentNode(); + m_startOffset = m_endOffset = partialEnd->nodeIndex(); + } + } + return fragment.release(); +} + + +PassRefPtr<DocumentFragment> Range::extractContents(ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + ec = 0; + checkDeleteExtract(ec); + if (ec) + return 0; + + return processContents(EXTRACT_CONTENTS,ec); +} + +PassRefPtr<DocumentFragment> Range::cloneContents( int &ec ) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + return processContents(CLONE_CONTENTS,ec); +} + +void Range::insertNode(PassRefPtr<Node> newNode, ExceptionCode& ec) +{ + ec = 0; + + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!newNode) { + ec = NOT_FOUND_ERR; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of + // the Range is read-only. + if (containedByReadOnly()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were + // not created from the same document. + if (newNode->document() != m_startContainer->document()) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + + // HIERARCHY_REQUEST_ERR: Raised if the container of the start of the Range is of a type that + // does not allow children of the type of newNode or if newNode is an ancestor of the container. + + // an extra one here - if a text node is going to split, it must have a parent to insert into + if (m_startContainer->nodeType() == Node::TEXT_NODE && !m_startContainer->parentNode()) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + // In the case where the container is a text node, we check against the container's parent, because + // text nodes get split up upon insertion. + Node *checkAgainst; + if (m_startContainer->nodeType() == Node::TEXT_NODE) + checkAgainst = m_startContainer->parentNode(); + else + checkAgainst = m_startContainer.get(); + + if (newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { + // check each child node, not the DocumentFragment itself + Node *c; + for (c = newNode->firstChild(); c; c = c->nextSibling()) { + if (!checkAgainst->childTypeAllowed(c->nodeType())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + } else { + if (!checkAgainst->childTypeAllowed(newNode->nodeType())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + + for (Node *n = m_startContainer.get(); n; n = n->parentNode()) { + if (n == newNode) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + + // INVALID_NODE_TYPE_ERR: Raised if newNode is an Attr, Entity, Notation, or Document node. + if (newNode->nodeType() == Node::ATTRIBUTE_NODE || + newNode->nodeType() == Node::ENTITY_NODE || + newNode->nodeType() == Node::NOTATION_NODE || + newNode->nodeType() == Node::DOCUMENT_NODE) { + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } + + unsigned endOffsetDelta = 0; + if (m_startContainer->nodeType() == Node::TEXT_NODE || + m_startContainer->nodeType() == Node::CDATA_SECTION_NODE) { + RefPtr<Text> newText = static_cast<Text*>(m_startContainer.get())->splitText(m_startOffset, ec); + if (ec) + return; + + if (m_startContainer == m_endContainer) + endOffsetDelta = -m_startOffset; + + m_startContainer->parentNode()->insertBefore(newNode, newText.get(), ec); + if (ec) + return; + m_endContainer = newText; + } else { + if (m_startContainer == m_endContainer) { + bool isFragment = newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE; + endOffsetDelta = isFragment ? newNode->childNodeCount() : 1; + } + + m_startContainer->insertBefore(newNode, m_startContainer->childNode(m_startOffset), ec); + if (ec) + return; + } + m_endOffset += endOffsetDelta; +} + +String Range::toString(ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return String(); + } + + Vector<UChar> result; + + Node* pastEnd = pastEndNode(); + for (Node* n = startNode(); n != pastEnd; n = n->traverseNextNode()) { + if (n->nodeType() == Node::TEXT_NODE || n->nodeType() == Node::CDATA_SECTION_NODE) { + String data = static_cast<CharacterData*>(n)->data(); + unsigned length = data.length(); + unsigned start = (n == m_startContainer) ? min(m_startOffset, length) : 0; + unsigned end = (n == m_endContainer) ? min(max(start, m_endOffset), length) : length; + result.append(data.characters() + start, end - start); + } + } + + return String::adopt(result); +} + +String Range::toHTML() const +{ + return createMarkup(this); +} + +String Range::text() const +{ + if (m_detached) + return String(); + + // We need to update layout, since plainText uses line boxes in the render tree. + // FIXME: As with innerText, we'd like this to work even if there are no render objects. + m_startContainer->document()->updateLayout(); + + // FIXME: Maybe DOMRange constructor should take const DOMRange*; if it did we would not need this const_cast. + return plainText(const_cast<Range *>(this)); +} + +PassRefPtr<DocumentFragment> Range::createContextualFragment(const String &html, ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + Node* htmlElement = m_startContainer->isHTMLElement() ? m_startContainer.get() : m_startContainer->parentNode(); + if (!htmlElement || !htmlElement->isHTMLElement()) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + RefPtr<DocumentFragment> fragment = static_cast<HTMLElement*>(htmlElement)->createContextualFragment(html); + if (!fragment) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + return fragment.release(); +} + + +void Range::detach(ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + m_startContainer = 0; + m_endContainer = 0; + m_detached = true; +} + +bool Range::isDetached() const +{ + return m_detached; +} + +void Range::checkNodeWOffset(Node* n, int offset, ExceptionCode& ec) const +{ + switch (n->nodeType()) { + case Node::DOCUMENT_TYPE_NODE: + case Node::ENTITY_NODE: + case Node::NOTATION_NODE: + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + case Node::TEXT_NODE: + if (static_cast<unsigned>(offset) > static_cast<CharacterData*>(n)->length()) + ec = INDEX_SIZE_ERR; + return; + case Node::PROCESSING_INSTRUCTION_NODE: + if (static_cast<unsigned>(offset) > static_cast<ProcessingInstruction*>(n)->data().length()) + ec = INDEX_SIZE_ERR; + return; + case Node::ATTRIBUTE_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_NODE: + case Node::ELEMENT_NODE: + case Node::ENTITY_REFERENCE_NODE: + case Node::XPATH_NAMESPACE_NODE: + if (static_cast<unsigned>(offset) > n->childNodeCount()) + ec = INDEX_SIZE_ERR; + return; + } + ASSERT_NOT_REACHED(); +} + +void Range::checkNodeBA(Node* n, ExceptionCode& ec) const +{ + // INVALID_NODE_TYPE_ERR: Raised if the root container of refNode is not an + // Attr, Document or DocumentFragment node or part of a shadow DOM tree + // or if refNode is a Document, DocumentFragment, Attr, Entity, or Notation node. + Node *root = n; + while (root->parentNode()) + root = root->parentNode(); + + if (!(root->nodeType() == Node::ATTRIBUTE_NODE || + root->nodeType() == Node::DOCUMENT_NODE || + root->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || + root->isShadowNode())) { + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } + + if( n->nodeType() == Node::DOCUMENT_NODE || + n->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || + n->nodeType() == Node::ATTRIBUTE_NODE || + n->nodeType() == Node::ENTITY_NODE || + n->nodeType() == Node::NOTATION_NODE ) + ec = RangeException::INVALID_NODE_TYPE_ERR; + +} + +PassRefPtr<Range> Range::cloneRange(ExceptionCode& ec) const +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + return new Range(m_ownerDocument.get(), m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset); +} + +void Range::setStartAfter( Node *refNode, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + ec = 0; + checkNodeBA( refNode, ec ); + if (ec) + return; + + setStart(refNode->parentNode(), refNode->nodeIndex() + 1, ec); +} + +void Range::setEndBefore( Node *refNode, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + ec = 0; + checkNodeBA(refNode, ec); + if (ec) + return; + + setEnd(refNode->parentNode(), refNode->nodeIndex(), ec); +} + +void Range::setEndAfter( Node *refNode, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + ec = 0; + checkNodeBA(refNode, ec); + if (ec) + return; + + setEnd(refNode->parentNode(), refNode->nodeIndex() + 1, ec); + +} + +void Range::selectNode( Node *refNode, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + // INVALID_NODE_TYPE_ERR: Raised if an ancestor of refNode is an Entity, Notation or + // DocumentType node or if refNode is a Document, DocumentFragment, Attr, Entity, or Notation + // node. + Node *anc; + for (anc = refNode->parentNode(); anc; anc = anc->parentNode()) { + if (anc->nodeType() == Node::ENTITY_NODE || + anc->nodeType() == Node::NOTATION_NODE || + anc->nodeType() == Node::DOCUMENT_TYPE_NODE) { + + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } + } + + if (refNode->nodeType() == Node::DOCUMENT_NODE || + refNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || + refNode->nodeType() == Node::ATTRIBUTE_NODE || + refNode->nodeType() == Node::ENTITY_NODE || + refNode->nodeType() == Node::NOTATION_NODE) { + + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } + + ec = 0; + setStartBefore( refNode, ec ); + if (ec) + return; + setEndAfter( refNode, ec ); +} + +void Range::selectNodeContents( Node *refNode, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + // INVALID_NODE_TYPE_ERR: Raised if refNode or an ancestor of refNode is an Entity, Notation + // or DocumentType node. + Node *n; + for (n = refNode; n; n = n->parentNode()) { + if (n->nodeType() == Node::ENTITY_NODE || + n->nodeType() == Node::NOTATION_NODE || + n->nodeType() == Node::DOCUMENT_TYPE_NODE) { + + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } + } + + m_startContainer = refNode; + m_startOffset = 0; + m_endContainer = refNode; + m_endOffset = maxEndOffset(); +} + +void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) +{ + RefPtr<Node> newParent = passNewParent; + + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!newParent) { + ec = NOT_FOUND_ERR; + return; + } + + // INVALID_NODE_TYPE_ERR: Raised if node is an Attr, Entity, DocumentType, Notation, + // Document, or DocumentFragment node. + switch (newParent->nodeType()) { + case Node::ATTRIBUTE_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_NODE: + case Node::DOCUMENT_TYPE_NODE: + case Node::ENTITY_NODE: + case Node::NOTATION_NODE: + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + case Node::ELEMENT_NODE: + case Node::ENTITY_REFERENCE_NODE: + case Node::PROCESSING_INSTRUCTION_NODE: + case Node::TEXT_NODE: + case Node::XPATH_NAMESPACE_NODE: + break; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of + // the Range is read-only. + if (containedByReadOnly()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were + // not created from the same document. + if (newParent->document() != m_startContainer->document()) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-Text node. + if (m_startContainer->nodeType() != Node::TEXT_NODE) { + if (m_startOffset > 0 && m_startOffset < maxStartOffset()) { + ec = RangeException::BAD_BOUNDARYPOINTS_ERR; + return; + } + } + if (m_endContainer->nodeType() != Node::TEXT_NODE) { + if (m_endOffset > 0 && m_endOffset < maxEndOffset()) { + ec = RangeException::BAD_BOUNDARYPOINTS_ERR; + return; + } + } + + // Raise a HIERARCHY_REQUEST_ERR if m_startContainer doesn't accept children like newParent. + Node* parentOfNewParent = m_startContainer.get(); + // If m_startContainer is a textNode, it will be split and it will be its parent that will + // need to accept newParent. + if (parentOfNewParent->isTextNode()) + parentOfNewParent = parentOfNewParent->parentNode(); + if (!parentOfNewParent->childTypeAllowed(newParent->nodeType())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + if (m_startContainer == newParent || m_startContainer->isDescendantOf(newParent.get())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + // FIXME: Do we need a check if the node would end up with a child node of a type not + // allowed by the type of node? + + ec = 0; + while (Node* n = newParent->firstChild()) { + newParent->removeChild(n, ec); + if (ec) + return; + } + RefPtr<DocumentFragment> fragment = extractContents(ec); + if (ec) + return; + insertNode(newParent, ec); + if (ec) + return; + newParent->appendChild(fragment.release(), ec); + if (ec) + return; + selectNode(newParent.get(), ec); +} + +void Range::setStartBefore( Node *refNode, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + ec = 0; + checkNodeBA(refNode, ec); + if (ec) + return; + + setStart(refNode->parentNode(), refNode->nodeIndex(), ec); +} + +void Range::checkDeleteExtract(ExceptionCode& ec) +{ + if (!commonAncestorContainer(ec) || ec) + return; + + Node *pastEnd = pastEndNode(); + for (Node *n = startNode(); n != pastEnd; n = n->traverseNextNode()) { + if (n->isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { // ### is this for only directly under the DF, or anywhere? + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + + if (containedByReadOnly()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } +} + +bool Range::containedByReadOnly() const +{ + Node *n; + for (n = m_startContainer.get(); n; n = n->parentNode()) { + if (n->isReadOnlyNode()) + return true; + } + for (n = m_endContainer.get(); n; n = n->parentNode()) { + if (n->isReadOnlyNode()) + return true; + } + return false; +} + +Position Range::startPosition() const +{ + return Position(m_startContainer.get(), m_startOffset); +} + +Position Range::endPosition() const +{ + return Position(m_endContainer.get(), m_endOffset); +} + +Node *Range::startNode() const +{ + if (!m_startContainer) + return 0; + if (m_startContainer->offsetInCharacters()) + return m_startContainer.get(); + Node *child = m_startContainer->childNode(m_startOffset); + if (child) + return child; + if (m_startOffset == 0) + return m_startContainer.get(); + return m_startContainer->traverseNextSibling(); +} + +Position Range::editingStartPosition() const +{ + // This function is used by range style computations to avoid bugs like: + // <rdar://problem/4017641> REGRESSION (Mail): you can only bold/unbold a selection starting from end of line once + // It is important to skip certain irrelevant content at the start of the selection, so we do not wind up + // with a spurious "mixed" style. + + VisiblePosition visiblePosition(m_startContainer.get(), m_startOffset, VP_DEFAULT_AFFINITY); + if (visiblePosition.isNull()) + return Position(); + + ExceptionCode ec = 0; + // if the selection is a caret, just return the position, since the style + // behind us is relevant + if (collapsed(ec)) + return visiblePosition.deepEquivalent(); + + // if the selection starts just before a paragraph break, skip over it + if (isEndOfParagraph(visiblePosition)) + return visiblePosition.next().deepEquivalent().downstream(); + + // otherwise, make sure to be at the start of the first selected node, + // instead of possibly at the end of the last node before the selection + return visiblePosition.deepEquivalent().downstream(); +} + +Node *Range::pastEndNode() const +{ + if (!m_startContainer || !m_endContainer) + return 0; + if (m_endContainer->offsetInCharacters()) + return m_endContainer->traverseNextSibling(); + Node *child = m_endContainer->childNode(m_endOffset); + if (child) + return child; + return m_endContainer->traverseNextSibling(); +} + +IntRect Range::boundingBox() +{ + IntRect result; + Vector<IntRect> rects; + addLineBoxRects(rects); + const size_t n = rects.size(); + for (size_t i = 0; i < n; ++i) + result.unite(rects[i]); + return result; +} + +void Range::addLineBoxRects(Vector<IntRect>& rects, bool useSelectionHeight) +{ + if (!m_startContainer || !m_endContainer) + return; + + RenderObject* start = m_startContainer->renderer(); + RenderObject* end = m_endContainer->renderer(); + if (!start || !end) + return; + + RenderObject* stop = end->nextInPreOrderAfterChildren(); + for (RenderObject* r = start; r && r != stop; r = r->nextInPreOrder()) { + // only ask leaf render objects for their line box rects + if (!r->firstChild()) { + int startOffset = r == start ? m_startOffset : 0; + int endOffset = r == end ? m_endOffset : UINT_MAX; + r->addLineBoxRects(rects, startOffset, endOffset, useSelectionHeight); + } + } +} + +#ifndef NDEBUG +#define FormatBufferSize 1024 +void Range::formatForDebugger(char *buffer, unsigned length) const +{ + String result; + String s; + + if (!m_startContainer || !m_endContainer) + result = "<empty>"; + else { + char s[FormatBufferSize]; + result += "from offset "; + result += String::number(m_startOffset); + result += " of "; + m_startContainer->formatForDebugger(s, FormatBufferSize); + result += s; + result += " to offset "; + result += String::number(m_endOffset); + result += " of "; + m_endContainer->formatForDebugger(s, FormatBufferSize); + result += s; + } + + strncpy(buffer, result.utf8().data(), length - 1); +} +#undef FormatBufferSize +#endif + +bool operator==(const Range &a, const Range &b) +{ + if (&a == &b) + return true; + // Not strictly legal C++, but in practice this can happen, and works fine with GCC. + if (!&a || !&b) + return false; + bool ad = a.isDetached(); + bool bd = b.isDetached(); + if (ad && bd) + return true; + if (ad || bd) + return false; + int exception = 0; + return a.startContainer(exception) == b.startContainer(exception) + && a.endContainer(exception) == b.endContainer(exception) + && a.startOffset(exception) == b.startOffset(exception) + && a.endOffset(exception) == b.endOffset(exception); +} + +PassRefPtr<Range> rangeOfContents(Node* node) +{ + ASSERT(node); + RefPtr<Range> range = new Range(node->document()); + int exception = 0; + range->selectNodeContents(node, exception); + return range.release(); +} + +unsigned Range::maxStartOffset() const +{ + if (!m_startContainer) + return 0; + if (!m_startContainer->offsetInCharacters()) + return m_startContainer->childNodeCount(); + return m_startContainer->maxCharacterOffset(); +} + +unsigned Range::maxEndOffset() const +{ + if (!m_endContainer) + return 0; + if (!m_endContainer->offsetInCharacters()) + return m_endContainer->childNodeCount(); + return m_endContainer->maxCharacterOffset(); +} + +} diff --git a/WebCore/dom/Range.h b/WebCore/dom/Range.h new file mode 100644 index 0000000..7568748 --- /dev/null +++ b/WebCore/dom/Range.h @@ -0,0 +1,145 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Gunnstein Lye (gunnstein@netcom.no) + * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef Range_h +#define Range_h + +#include <wtf/RefCounted.h> +#include <wtf/Forward.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +typedef int ExceptionCode; + +class DocumentFragment; +class Document; +class IntRect; +class Node; +class Position; +class String; + +class Range : public RefCounted<Range> +{ +public: + Range(Document*); + Range(Document*, Node* startContainer, int startOffset, Node* endContainer, int endOffset); + Range(Document*, const Position&, const Position&); + ~Range(); + + Document* ownerDocument() const { return m_ownerDocument.get(); } + + Node* startContainer(ExceptionCode&) const; + int startOffset(ExceptionCode&) const; + Node* endContainer(ExceptionCode&) const; + int endOffset(ExceptionCode&) const; + bool collapsed(ExceptionCode&) const; + + Node* commonAncestorContainer(ExceptionCode&) const; + static Node* commonAncestorContainer(Node* containerA, Node* containerB); + void setStart(Node* container, int offset, ExceptionCode&); + void setEnd(Node* container, int offset, ExceptionCode&); + void collapse(bool toStart, ExceptionCode&); + bool isPointInRange(Node* refNode, int offset, ExceptionCode& ec); + short comparePoint(Node* refNode, int offset, ExceptionCode& ec); + enum CompareResults { NODE_BEFORE, NODE_AFTER, NODE_BEFORE_AND_AFTER, NODE_INSIDE }; + CompareResults compareNode(Node* refNode, ExceptionCode&); + enum CompareHow { START_TO_START, START_TO_END, END_TO_END, END_TO_START }; + short compareBoundaryPoints(CompareHow, const Range* sourceRange, ExceptionCode&) const; + static short compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB); + static short compareBoundaryPoints(const Position&, const Position&); + bool boundaryPointsValid() const; + bool intersectsNode(Node* refNode, ExceptionCode&); + void deleteContents(ExceptionCode&); + PassRefPtr<DocumentFragment> extractContents(ExceptionCode&); + PassRefPtr<DocumentFragment> cloneContents(ExceptionCode&); + void insertNode(PassRefPtr<Node>, ExceptionCode&); + String toString(ExceptionCode&) const; + + String toHTML() const; + String text() const; + + PassRefPtr<DocumentFragment> createContextualFragment(const String& html, ExceptionCode&) const; + + void detach(ExceptionCode&); + bool isDetached() const; + PassRefPtr<Range> cloneRange(ExceptionCode&) const; + + void setStartAfter(Node*, ExceptionCode&); + void setEndBefore(Node*, ExceptionCode&); + void setEndAfter(Node*, ExceptionCode&); + void selectNode(Node*, ExceptionCode&); + void selectNodeContents(Node*, ExceptionCode&); + void surroundContents(PassRefPtr<Node>, ExceptionCode&); + void setStartBefore(Node*, ExceptionCode&); + + enum ActionType { + DELETE_CONTENTS, + EXTRACT_CONTENTS, + CLONE_CONTENTS + }; + PassRefPtr<DocumentFragment> processContents(ActionType, ExceptionCode&); + + Position startPosition() const; + Position endPosition() const; + + Node* startNode() const; + Node* pastEndNode() const; + + Position editingStartPosition() const; + + IntRect boundingBox(); + void addLineBoxRects(Vector<IntRect>&, bool useSelectionHeight = false); + +#ifndef NDEBUG + void formatForDebugger(char* buffer, unsigned length) const; +#endif + +private: + RefPtr<Document> m_ownerDocument; + RefPtr<Node> m_startContainer; + unsigned m_startOffset; + RefPtr<Node> m_endContainer; + unsigned m_endOffset; + bool m_detached; + + void checkNodeWOffset(Node*, int offset, ExceptionCode&) const; + void checkNodeBA(Node*, ExceptionCode&) const; + void checkDeleteExtract(ExceptionCode&); + bool containedByReadOnly() const; + unsigned maxStartOffset() const; + unsigned maxEndOffset() const; +}; + +PassRefPtr<Range> rangeOfContents(Node*); + +bool operator==(const Range&, const Range&); +inline bool operator!=(const Range& a, const Range& b) { return !(a == b); } + +} // namespace + +#endif diff --git a/WebCore/dom/Range.idl b/WebCore/dom/Range.idl new file mode 100644 index 0000000..4344474 --- /dev/null +++ b/WebCore/dom/Range.idl @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module ranges { + + // Introduced in DOM Level 2: + interface [GenerateConstructor] Range { + + readonly attribute Node startContainer + getter raises(DOMException); + readonly attribute long startOffset + getter raises(DOMException); + readonly attribute Node endContainer + getter raises(DOMException); + readonly attribute long endOffset + getter raises(DOMException); + readonly attribute boolean collapsed + getter raises(DOMException); + readonly attribute Node commonAncestorContainer + getter raises(DOMException); + + [OldStyleObjC] void setStart(in Node refNode, + in long offset) + raises(RangeException, DOMException); + [OldStyleObjC] void setEnd(in Node refNode, + in long offset) + raises(RangeException, DOMException); + void setStartBefore(in Node refNode) + raises(RangeException, DOMException); + void setStartAfter(in Node refNode) + raises(RangeException, DOMException); + void setEndBefore(in Node refNode) + raises(RangeException, DOMException); + void setEndAfter(in Node refNode) + raises(RangeException, DOMException); + void collapse(in boolean toStart) + raises(DOMException); + void selectNode(in Node refNode) + raises(RangeException, DOMException); + void selectNodeContents(in Node refNode) + raises(RangeException, DOMException); + + // CompareHow + const unsigned short START_TO_START = 0; + const unsigned short START_TO_END = 1; + const unsigned short END_TO_END = 2; + const unsigned short END_TO_START = 3; + + [OldStyleObjC] short compareBoundaryPoints(in CompareHow how, + in Range sourceRange) + raises(DOMException); + + void deleteContents() + raises(DOMException); + DocumentFragment extractContents() + raises(DOMException); + DocumentFragment cloneContents() + raises(DOMException); + void insertNode(in Node newNode) + raises(DOMException, RangeException); + void surroundContents(in Node newParent) + raises(DOMException, RangeException); + Range cloneRange() + raises(DOMException); + DOMString toString() + raises(DOMException); + + void detach() + raises(DOMException); + + // extensions + + DocumentFragment createContextualFragment(in DOMString html) + raises(DOMException); + + // WebKit extensions + + boolean intersectsNode(in Node refNode) + raises(RangeException, DOMException); + + short compareNode(in Node refNode) + raises(RangeException, DOMException); + + // CompareResults + const unsigned short NODE_BEFORE = 0; + const unsigned short NODE_AFTER = 1; + const unsigned short NODE_BEFORE_AND_AFTER = 2; + const unsigned short NODE_INSIDE = 3; + + short comparePoint(in Node refNode, + in long offset) + raises(RangeException, DOMException); + + boolean isPointInRange(in Node refNode, + in long offset) + raises(RangeException, DOMException); + +#if !defined(LANGUAGE_JAVASCRIPT) + readonly attribute DOMString text; +#endif + }; + +} diff --git a/WebCore/dom/RangeException.h b/WebCore/dom/RangeException.h new file mode 100644 index 0000000..11a35a5 --- /dev/null +++ b/WebCore/dom/RangeException.h @@ -0,0 +1,52 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Gunnstein Lye (gunnstein@netcom.no) + * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RangeException_h +#define RangeException_h + +#include "ExceptionBase.h" + +namespace WebCore { + + class RangeException : public ExceptionBase { + public: + RangeException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + + static const int RangeExceptionOffset = 200; + static const int RangeExceptionMax = 299; + + enum RangeExceptionCode { + BAD_BOUNDARYPOINTS_ERR = RangeExceptionOffset + 1, + INVALID_NODE_TYPE_ERR + }; + }; + +} // namespace WebCore + +#endif // RangeException_h diff --git a/WebCore/dom/RangeException.idl b/WebCore/dom/RangeException.idl new file mode 100644 index 0000000..36cde16 --- /dev/null +++ b/WebCore/dom/RangeException.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + */ + +module ranges { + + interface [ + GenerateConstructor + ] RangeException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(LANGUAGE_JAVASCRIPT) + [DontEnum] DOMString toString(); +#endif + + // DOM Level 2 + + const unsigned short BAD_BOUNDARYPOINTS_ERR = 1; + const unsigned short INVALID_NODE_TYPE_ERR = 2; + }; + +} diff --git a/WebCore/dom/RegisteredEventListener.cpp b/WebCore/dom/RegisteredEventListener.cpp new file mode 100644 index 0000000..e995bd0 --- /dev/null +++ b/WebCore/dom/RegisteredEventListener.cpp @@ -0,0 +1,46 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "RegisteredEventListener.h" + +#include "EventListener.h" + +namespace WebCore { + +RegisteredEventListener::RegisteredEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) + : RefCounted<RegisteredEventListener>(0) + , m_eventType(eventType) + , m_listener(listener) + , m_useCapture(useCapture) + , m_removed(false) +{ +} + +bool operator==(const RegisteredEventListener& a, const RegisteredEventListener& b) +{ + return a.eventType() == b.eventType() && a.listener() == b.listener() && a.useCapture() == b.useCapture(); +} + +} // namespace WebCore diff --git a/WebCore/dom/RegisteredEventListener.h b/WebCore/dom/RegisteredEventListener.h new file mode 100644 index 0000000..b1bb0ef --- /dev/null +++ b/WebCore/dom/RegisteredEventListener.h @@ -0,0 +1,60 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RegisteredEventListener_h +#define RegisteredEventListener_h + +#include "AtomicString.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + + class EventListener; + + class RegisteredEventListener : public RefCounted<RegisteredEventListener> { + public: + RegisteredEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + + const AtomicString& eventType() const { return m_eventType; } + EventListener* listener() const { return m_listener.get(); } + bool useCapture() const { return m_useCapture; } + + bool removed() const { return m_removed; } + void setRemoved(bool removed) { m_removed = removed; } + + private: + AtomicString m_eventType; + RefPtr<EventListener> m_listener; + bool m_useCapture; + bool m_removed; + }; + + + bool operator==(const RegisteredEventListener&, const RegisteredEventListener&); + inline bool operator!=(const RegisteredEventListener& a, const RegisteredEventListener& b) { return !(a == b); } + +} // namespace WebCore + +#endif // RegisteredEventListener_h diff --git a/WebCore/dom/SelectorNodeList.cpp b/WebCore/dom/SelectorNodeList.cpp new file mode 100644 index 0000000..2015a0e --- /dev/null +++ b/WebCore/dom/SelectorNodeList.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SelectorNodeList.h" + +#include "CSSSelector.h" +#include "CSSStyleSelector.h" +#include "Document.h" +#include "Element.h" +#include "Node.h" + +namespace WebCore { + +SelectorNodeList::SelectorNodeList(PassRefPtr<Node> rootNode, CSSSelector* querySelector) +{ + Document* document = rootNode->document(); + CSSStyleSelector* styleSelector = document->styleSelector(); + for (Node* n = rootNode->firstChild(); n; n = n->traverseNextNode(rootNode.get())) { + if (n->isElementNode()) { + styleSelector->initElementAndPseudoState(static_cast<Element*>(n)); + for (CSSSelector* selector = querySelector; selector; selector = selector->next()) { + if (styleSelector->checkSelector(selector)) { + m_nodes.append(n); + break; + } + } + } + } +} + +} // namespace WebCore diff --git a/WebCore/dom/SelectorNodeList.h b/WebCore/dom/SelectorNodeList.h new file mode 100644 index 0000000..147e028 --- /dev/null +++ b/WebCore/dom/SelectorNodeList.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SelectorNodeList_h +#define SelectorNodeList_h + +#include "StaticNodeList.h" +#include <wtf/Forward.h> + +namespace WebCore { + + class Node; + class CSSSelector; + + class SelectorNodeList : public StaticNodeList { + public: + SelectorNodeList(PassRefPtr<Node> rootNode, CSSSelector*); + }; + +} // namespace WebCore + +#endif // SelectorNodeList_h diff --git a/WebCore/dom/StaticNodeList.cpp b/WebCore/dom/StaticNodeList.cpp new file mode 100644 index 0000000..3d7c70d --- /dev/null +++ b/WebCore/dom/StaticNodeList.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StaticNodeList.h" + +#include "AtomicString.h" +#include "Node.h" +#include "Element.h" + +namespace WebCore { + +unsigned StaticNodeList::length() const +{ + return m_nodes.size(); +} + +Node* StaticNodeList::item(unsigned index) const +{ + if (index < m_nodes.size()) + return m_nodes[index].get(); + return 0; +} + +Node* StaticNodeList::itemWithName(const AtomicString& elementId) const +{ + size_t length = m_nodes.size(); + for (size_t i = 0; i < length; ++i) { + Node* node = m_nodes[i].get(); + if (node->isElementNode() && static_cast<Element*>(node)->getIDAttribute() == elementId) + return node; + } + + return 0; +} + +} // namespace WebCore diff --git a/WebCore/dom/StaticNodeList.h b/WebCore/dom/StaticNodeList.h new file mode 100644 index 0000000..aacd1ca --- /dev/null +++ b/WebCore/dom/StaticNodeList.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef StaticNodeList_h +#define StaticNodeList_h + +#include "NodeList.h" +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class Node; + + class StaticNodeList : public NodeList { + public: + // Derived classes should build up the Vector in their constructor. + StaticNodeList() { } + virtual ~StaticNodeList() { } + + virtual unsigned length() const; + virtual Node* item(unsigned index) const; + virtual Node* itemWithName(const AtomicString&) const; + + protected: + Vector<RefPtr<Node> > m_nodes; + }; + +} // namespace WebCore + +#endif // StaticNodeList_h diff --git a/WebCore/dom/StyleElement.cpp b/WebCore/dom/StyleElement.cpp new file mode 100644 index 0000000..8280880 --- /dev/null +++ b/WebCore/dom/StyleElement.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2006, 2007 Rob Buis + * Copyright (C) 2008 Apple, Inc. All rights reserved. + * + * 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 "config.h" +#include "StyleElement.h" + +#include "Document.h" +#include "Element.h" +#include "MappedAttribute.h" +#include "MediaList.h" +#include "MediaQueryEvaluator.h" + +namespace WebCore { + +StyleElement::StyleElement() +{ +} + +StyleSheet* StyleElement::sheet(Element* e) +{ + if (!m_sheet) + createSheet(e); + return m_sheet.get(); +} + +void StyleElement::insertedIntoDocument(Document* document, Element* element) +{ + process(element); +} + +void StyleElement::removedFromDocument(Document* document) +{ + if (m_sheet) + document->updateStyleSelector(); +} + +void StyleElement::process(Element* e) +{ + if (!e || !e->inDocument()) + return; + + Vector<UChar> text; + + for (Node* c = e->firstChild(); c; c = c->nextSibling()) + if (c->nodeType() == Node::TEXT_NODE || c->nodeType() == Node::CDATA_SECTION_NODE || c->nodeType() == Node::COMMENT_NODE) + append(text, c->nodeValue()); + + createSheet(e, String::adopt(text)); +} + +void StyleElement::createSheet(Element* e, const String& text) +{ + Document* document = e->document(); + if (m_sheet) { + if (static_cast<CSSStyleSheet*>(m_sheet.get())->isLoading()) + document->removePendingSheet(); + m_sheet = 0; + } + + // If type is empty or CSS, this is a CSS style sheet. + const AtomicString& type = this->type(); + if (type.isEmpty() || (e->isHTMLElement() ? equalIgnoringCase(type, "text/css") : (type == "text/css"))) { + RefPtr<MediaList> mediaList = new MediaList((CSSStyleSheet*)0, media(), e->isHTMLElement()); + MediaQueryEvaluator screenEval("screen", true); + MediaQueryEvaluator printEval("print", true); + if (screenEval.eval(mediaList.get()) || printEval.eval(mediaList.get())) { + document->addPendingSheet(); + setLoading(true); + m_sheet = new CSSStyleSheet(e, String(), document->inputEncoding()); + m_sheet->parseString(text, !document->inCompatMode()); + m_sheet->setMedia(mediaList.get()); + m_sheet->setTitle(e->title()); + setLoading(false); + } + } + + if (m_sheet) + m_sheet->checkLoaded(); +} + +} diff --git a/WebCore/dom/StyleElement.h b/WebCore/dom/StyleElement.h new file mode 100644 index 0000000..7f83909 --- /dev/null +++ b/WebCore/dom/StyleElement.h @@ -0,0 +1,56 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2006, 2007 Rob Buis + * + * 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. + * + */ +#ifndef StyleElement_h +#define StyleElement_h + +#include "CSSStyleSheet.h" + +namespace WebCore { + +class Element; + +class StyleElement { +public: + StyleElement(); + virtual ~StyleElement() {} + +protected: + StyleSheet* sheet(Element*); + + virtual void setLoading(bool) {} + + virtual const AtomicString& type() const = 0; + virtual const AtomicString& media() const = 0; + + void insertedIntoDocument(Document*, Element*); + void removedFromDocument(Document*); + void process(Element*); + + void createSheet(Element* e, const String& text = String()); + +protected: + RefPtr<CSSStyleSheet> m_sheet; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/StyledElement.cpp b/WebCore/dom/StyledElement.cpp new file mode 100644 index 0000000..6d9a7d7 --- /dev/null +++ b/WebCore/dom/StyledElement.cpp @@ -0,0 +1,486 @@ +/** + * 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 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "StyledElement.h" + +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "CSSValueKeywords.h" +#include "ClassNames.h" +#include "Document.h" +#include "HTMLNames.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +struct MappedAttributeKey { + uint16_t type; + StringImpl* name; + StringImpl* value; + MappedAttributeKey(MappedAttributeEntry t = eNone, StringImpl* n = 0, StringImpl* v = 0) + : type(t), name(n), value(v) { } +}; + +static inline bool operator==(const MappedAttributeKey& a, const MappedAttributeKey& b) + { return a.type == b.type && a.name == b.name && a.value == b.value; } + +struct MappedAttributeKeyTraits : WTF::GenericHashTraits<MappedAttributeKey> { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static MappedAttributeKey deletedValue() { return eLastEntry; } +}; + +struct MappedAttributeHash { + static unsigned hash(const MappedAttributeKey&); + static bool equal(const MappedAttributeKey& a, const MappedAttributeKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +typedef HashMap<MappedAttributeKey, CSSMappedAttributeDeclaration*, MappedAttributeHash, MappedAttributeKeyTraits> MappedAttributeDecls; + +static MappedAttributeDecls* mappedAttributeDecls = 0; + +CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr) +{ + if (!mappedAttributeDecls) + return 0; + return mappedAttributeDecls->get(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl())); +} + +void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr, CSSMappedAttributeDeclaration* decl) +{ + if (!mappedAttributeDecls) + mappedAttributeDecls = new MappedAttributeDecls; + mappedAttributeDecls->set(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()), decl); +} + +void StyledElement::removeMappedAttributeDecl(MappedAttributeEntry entryType, + const QualifiedName& attrName, const AtomicString& attrValue) +{ + if (!mappedAttributeDecls) + return; + mappedAttributeDecls->remove(MappedAttributeKey(entryType, attrName.localName().impl(), attrValue.impl())); +} + +void StyledElement::invalidateStyleAttribute() +{ + m_isStyleAttributeValid = false; +} + +void StyledElement::updateStyleAttributeIfNeeded() const +{ + if (!m_isStyleAttributeValid) { + m_isStyleAttributeValid = true; + m_synchronizingStyleAttribute = true; + if (m_inlineStyleDecl) + const_cast<StyledElement*>(this)->setAttribute(styleAttr, m_inlineStyleDecl->cssText()); + m_synchronizingStyleAttribute = false; + } +} + +StyledElement::StyledElement(const QualifiedName& name, Document *doc) + : Element(name, doc) +{ +} + +StyledElement::~StyledElement() +{ + destroyInlineStyleDecl(); +} + +Attribute* StyledElement::createAttribute(const QualifiedName& name, StringImpl* value) +{ + return new MappedAttribute(name, value); +} + +void StyledElement::createInlineStyleDecl() +{ + m_inlineStyleDecl = new CSSMutableStyleDeclaration; + m_inlineStyleDecl->setParent(document()->elementSheet()); + m_inlineStyleDecl->setNode(this); + m_inlineStyleDecl->setStrictParsing(isHTMLElement() && !document()->inCompatMode()); +} + +void StyledElement::destroyInlineStyleDecl() +{ + if (m_inlineStyleDecl) { + m_inlineStyleDecl->setNode(0); + m_inlineStyleDecl->setParent(0); + m_inlineStyleDecl = 0; + } +} + +void StyledElement::attributeChanged(Attribute* attr, bool preserveDecls) +{ + MappedAttribute* mappedAttr = static_cast<MappedAttribute*>(attr); + if (mappedAttr->decl() && !preserveDecls) { + mappedAttr->setDecl(0); + setChanged(); + if (namedAttrMap) + mappedAttributes()->declRemoved(); + } + + bool checkDecl = true; + MappedAttributeEntry entry; + bool needToParse = mapToEntry(attr->name(), entry); + if (preserveDecls) { + if (mappedAttr->decl()) { + setChanged(); + if (namedAttrMap) + mappedAttributes()->declAdded(); + checkDecl = false; + } + } + else if (!attr->isNull() && entry != eNone) { + CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr); + if (decl) { + mappedAttr->setDecl(decl); + setChanged(); + if (namedAttrMap) + mappedAttributes()->declAdded(); + checkDecl = false; + } else + needToParse = true; + } + + if (needToParse) + parseMappedAttribute(mappedAttr); + + if (entry == eNone && ownerDocument()->attached() && ownerDocument()->styleSelector()->hasSelectorForAttribute(attr->name().localName())) + setChanged(); + + if (checkDecl && mappedAttr->decl()) { + // Add the decl to the table in the appropriate spot. + setMappedAttributeDecl(entry, attr, mappedAttr->decl()); + mappedAttr->decl()->setMappedState(entry, attr->name(), attr->value()); + mappedAttr->decl()->setParent(0); + mappedAttr->decl()->setNode(0); + if (namedAttrMap) + mappedAttributes()->declAdded(); + } +} + +bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + result = eNone; + if (attrName == styleAttr) + return !m_synchronizingStyleAttribute; + return true; +} + +void StyledElement::parseMappedAttribute(MappedAttribute *attr) +{ + if (attr->name() == idAttr) { + // unique id + setHasID(!attr->isNull()); + if (namedAttrMap) { + if (attr->isNull()) + namedAttrMap->setID(nullAtom); + else if (document()->inCompatMode() && !attr->value().impl()->isLower()) + namedAttrMap->setID(AtomicString(attr->value().string().lower())); + else + namedAttrMap->setID(attr->value()); + } + setChanged(); + } else if (attr->name() == classAttr) { + // class + bool hasClass = false; + if (!attr->isEmpty()) { + const AtomicString& value = attr->value(); + unsigned len = value.length(); + for (unsigned i = 0; i < len; ++i) { + if (!isClassWhitespace(value[i])) { + hasClass = true; + break; + } + } + } + setHasClass(hasClass); + if (namedAttrMap) + mappedAttributes()->parseClassAttribute(attr->value()); + setChanged(); + } else if (attr->name() == styleAttr) { + if (attr->isNull()) + destroyInlineStyleDecl(); + else + getInlineStyleDecl()->parseDeclaration(attr->value()); + m_isStyleAttributeValid = true; + setChanged(); + } +} + +void StyledElement::createAttributeMap() const +{ + namedAttrMap = new NamedMappedAttrMap(const_cast<StyledElement*>(this)); +} + +CSSMutableStyleDeclaration* StyledElement::getInlineStyleDecl() +{ + if (!m_inlineStyleDecl) + createInlineStyleDecl(); + return m_inlineStyleDecl.get(); +} + +CSSStyleDeclaration* StyledElement::style() +{ + return getInlineStyleDecl(); +} + +const ClassNames* StyledElement::getClassNames() const +{ + return namedAttrMap ? mappedAttributes()->getClassNames() : 0; +} + +static inline int toHex(UChar c) { + return ((c >= '0' && c <= '9') ? (c - '0') + : ((c >= 'a' && c <= 'f') ? (c - 'a' + 10) + : (( c >= 'A' && c <= 'F') ? (c - 'A' + 10) + : -1))); +} + +void StyledElement::addCSSProperty(MappedAttribute* attr, int id, const String &value) +{ + if (!attr->decl()) createMappedDecl(attr); + attr->decl()->setProperty(id, value, false); +} + +void StyledElement::addCSSProperty(MappedAttribute* attr, int id, int value) +{ + if (!attr->decl()) createMappedDecl(attr); + attr->decl()->setProperty(id, value, false); +} + +void StyledElement::addCSSStringProperty(MappedAttribute* attr, int id, const String &value, CSSPrimitiveValue::UnitTypes type) +{ + if (!attr->decl()) createMappedDecl(attr); + attr->decl()->setStringProperty(id, value, type, false); +} + +void StyledElement::addCSSImageProperty(MappedAttribute* attr, int id, const String& url) +{ + if (!attr->decl()) createMappedDecl(attr); + attr->decl()->setImageProperty(id, url, false); +} + +void StyledElement::addCSSLength(MappedAttribute* attr, int id, const String &value) +{ + // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct + // length unit and make the appropriate parsed value. + if (!attr->decl()) + createMappedDecl(attr); + + // strip attribute garbage.. + StringImpl* v = value.impl(); + if (v) { + unsigned int l = 0; + + while (l < v->length() && (*v)[l] <= ' ') + l++; + + for (; l < v->length(); l++) { + UChar cc = (*v)[l]; + if (cc > '9') + break; + if (cc < '0') { + if (cc == '%' || cc == '*') + l++; + if (cc != '.') + break; + } + } + + if (l != v->length()) { + attr->decl()->setLengthProperty(id, v->substring(0, l), false); + return; + } + } + + attr->decl()->setLengthProperty(id, value, false); +} + +/* color parsing that tries to match as close as possible IE 6. */ +void StyledElement::addCSSColor(MappedAttribute* attr, int id, const String& c) +{ + // this is the only case no color gets applied in IE. + if (!c.length()) + return; + + if (!attr->decl()) + createMappedDecl(attr); + + if (attr->decl()->setProperty(id, c, false)) + return; + + String color = c; + // not something that fits the specs. + + // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value + // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have. + + // the length of the color value is rounded up to the next + // multiple of 3. each part of the rgb triple then gets one third + // of the length. + // + // Each triplet is parsed byte by byte, mapping + // each number to a hex value (0-9a-fA-F to their values + // everything else to 0). + // + // The highest non zero digit in all triplets is remembered, and + // used as a normalization point to normalize to values between 0 + // and 255. + + if (color.lower() != "transparent") { + if (color[0] == '#') + color.remove(0, 1); + int basicLength = (color.length() + 2) / 3; + if (basicLength > 1) { + // IE ignores colors with three digits or less + int colors[3] = { 0, 0, 0 }; + int component = 0; + int pos = 0; + int maxDigit = basicLength-1; + while (component < 3) { + // search forward for digits in the string + int numDigits = 0; + while (pos < (int)color.length() && numDigits < basicLength) { + int hex = toHex(color[pos]); + colors[component] = (colors[component] << 4); + if (hex > 0) { + colors[component] += hex; + maxDigit = min(maxDigit, numDigits); + } + numDigits++; + pos++; + } + while (numDigits++ < basicLength) + colors[component] <<= 4; + component++; + } + maxDigit = basicLength - maxDigit; + + // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits + maxDigit -= 2; + colors[0] >>= 4*maxDigit; + colors[1] >>= 4*maxDigit; + colors[2] >>= 4*maxDigit; + // ASSERT(colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100); + + color = String::format("#%02x%02x%02x", colors[0], colors[1], colors[2]); + if (attr->decl()->setProperty(id, color, false)) + return; + } + } + attr->decl()->setProperty(id, CSS_VAL_BLACK, false); +} + +void StyledElement::createMappedDecl(MappedAttribute* attr) +{ + CSSMappedAttributeDeclaration* decl = new CSSMappedAttributeDeclaration(0); + attr->setDecl(decl); + decl->setParent(document()->elementSheet()); + decl->setNode(this); + decl->setStrictParsing(false); // Mapped attributes are just always quirky. +} + +// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's +// or anything like that. +const unsigned PHI = 0x9e3779b9U; + +// Paul Hsieh's SuperFastHash +// http://www.azillionmonkeys.com/qed/hash.html +unsigned MappedAttributeHash::hash(const MappedAttributeKey& key) +{ + uint32_t hash = PHI; + uint32_t tmp; + + const uint16_t* p; + + p = reinterpret_cast<const uint16_t*>(&key.name); + hash += p[0]; + tmp = (p[1] << 11) ^ hash; + hash = (hash << 16) ^ tmp; + hash += hash >> 11; + ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8); + if (sizeof(key.name) == 8) { + p += 2; + hash += p[0]; + tmp = (p[1] << 11) ^ hash; + hash = (hash << 16) ^ tmp; + hash += hash >> 11; + } + + p = reinterpret_cast<const uint16_t*>(&key.value); + hash += p[0]; + tmp = (p[1] << 11) ^ hash; + hash = (hash << 16) ^ tmp; + hash += hash >> 11; + ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8); + if (sizeof(key.value) == 8) { + p += 2; + hash += p[0]; + tmp = (p[1] << 11) ^ hash; + hash = (hash << 16) ^ tmp; + hash += hash >> 11; + } + + // Handle end case + hash += key.type; + hash ^= hash << 11; + hash += hash >> 17; + + // Force "avalanching" of final 127 bits + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 2; + hash += hash >> 15; + hash ^= hash << 10; + + // This avoids ever returning a hash code of 0, since that is used to + // signal "hash not computed yet", using a value that is likely to be + // effectively the same as 0 when the low bits are masked + if (hash == 0) + hash = 0x80000000; + + return hash; +} + +void StyledElement::copyNonAttributeProperties(const Element *sourceElement) +{ + const StyledElement* source = static_cast<const StyledElement*>(sourceElement); + if (!source->m_inlineStyleDecl) + return; + + *getInlineStyleDecl() = *source->m_inlineStyleDecl; + m_isStyleAttributeValid = source->m_isStyleAttributeValid; + m_synchronizingStyleAttribute = source->m_synchronizingStyleAttribute; + + Element::copyNonAttributeProperties(sourceElement); +} + +} diff --git a/WebCore/dom/StyledElement.h b/WebCore/dom/StyledElement.h new file mode 100644 index 0000000..82df0bf --- /dev/null +++ b/WebCore/dom/StyledElement.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef StyledElement_h +#define StyledElement_h + +#include "Element.h" +#include "NamedMappedAttrMap.h" + +namespace WebCore { + +class CSSMappedAttributeDeclaration; +class MappedAttribute; + +class StyledElement : public Element { +public: + StyledElement(const QualifiedName&, Document*); + virtual ~StyledElement(); + + virtual bool isStyledElement() const { return true; } + + NamedMappedAttrMap* mappedAttributes() { return static_cast<NamedMappedAttrMap*>(namedAttrMap.get()); } + const NamedMappedAttrMap* mappedAttributes() const { return static_cast<NamedMappedAttrMap*>(namedAttrMap.get()); } + bool hasMappedAttributes() const { return namedAttrMap && mappedAttributes()->hasMappedAttributes(); } + bool isMappedAttribute(const QualifiedName& name) const { MappedAttributeEntry res = eNone; mapToEntry(name, res); return res != eNone; } + + void addCSSLength(MappedAttribute* attr, int id, const String &value); + void addCSSProperty(MappedAttribute* attr, int id, const String &value); + void addCSSProperty(MappedAttribute* attr, int id, int value); + void addCSSStringProperty(MappedAttribute* attr, int id, const String &value, CSSPrimitiveValue::UnitTypes = CSSPrimitiveValue::CSS_STRING); + void addCSSImageProperty(MappedAttribute*, int propertyID, const String& url); + void addCSSColor(MappedAttribute* attr, int id, const String &c); + void createMappedDecl(MappedAttribute* attr); + + static CSSMappedAttributeDeclaration* getMappedAttributeDecl(MappedAttributeEntry type, Attribute* attr); + static void setMappedAttributeDecl(MappedAttributeEntry type, Attribute* attr, CSSMappedAttributeDeclaration* decl); + static void removeMappedAttributeDecl(MappedAttributeEntry type, const QualifiedName& attrName, const AtomicString& attrValue); + + CSSMutableStyleDeclaration* inlineStyleDecl() const { return m_inlineStyleDecl.get(); } + virtual bool canHaveAdditionalAttributeStyleDecls() const { return false; } + virtual void additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>&) {}; + CSSMutableStyleDeclaration* getInlineStyleDecl(); + CSSStyleDeclaration* style(); + void createInlineStyleDecl(); + void destroyInlineStyleDecl(); + void invalidateStyleAttribute(); + virtual void updateStyleAttributeIfNeeded() const; + + virtual const ClassNames* getClassNames() const; + virtual void attributeChanged(Attribute*, bool preserveDecls = false); + virtual void parseMappedAttribute(MappedAttribute*); + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; + virtual void createAttributeMap() const; + virtual Attribute* createAttribute(const QualifiedName& name, StringImpl* value); + + virtual void copyNonAttributeProperties(const Element*); + +protected: + RefPtr<CSSMutableStyleDeclaration> m_inlineStyleDecl; +}; + +} //namespace + +#endif diff --git a/WebCore/dom/TagNodeList.cpp b/WebCore/dom/TagNodeList.cpp new file mode 100644 index 0000000..81ac8e7 --- /dev/null +++ b/WebCore/dom/TagNodeList.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Trolltech ASA + * + * 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 "config.h" +#include "TagNodeList.h" + +#include "Element.h" +#include <wtf/Assertions.h> + +namespace WebCore { + +TagNodeList::TagNodeList(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName) + : DynamicNodeList(rootNode, true) + , m_namespaceURI(namespaceURI) + , m_localName(localName) +{ + ASSERT(m_namespaceURI.isNull() || !m_namespaceURI.isEmpty()); +} + +bool TagNodeList::nodeMatches(Node* testNode) const +{ + if (!testNode->isElementNode()) + return false; + + if (m_namespaceURI != starAtom && m_namespaceURI != testNode->namespaceURI()) + return false; + + return m_localName == starAtom || m_localName == testNode->localName(); +} + +} // namespace WebCore diff --git a/WebCore/dom/TagNodeList.h b/WebCore/dom/TagNodeList.h new file mode 100644 index 0000000..6593a0a --- /dev/null +++ b/WebCore/dom/TagNodeList.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Trolltech ASA + * + * 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. + */ + +#ifndef TagNodeList_h +#define TagNodeList_h + +#include "AtomicString.h" +#include "DynamicNodeList.h" + +namespace WebCore { + + // NodeList that limits to a particular tag. + class TagNodeList : public DynamicNodeList { + public: + TagNodeList(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName); + + private: + virtual bool nodeMatches(Node*) const; + + AtomicString m_namespaceURI; + AtomicString m_localName; + }; + +} // namespace WebCore + +#endif // TagNodeList_h diff --git a/WebCore/dom/Text.cpp b/WebCore/dom/Text.cpp new file mode 100644 index 0000000..d61214f --- /dev/null +++ b/WebCore/dom/Text.cpp @@ -0,0 +1,330 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "Text.h" + +#include "CString.h" +#include "Document.h" +#include "ExceptionCode.h" +#include "RenderText.h" +#include "TextBreakIterator.h" + +#if ENABLE(SVG) +#include "RenderSVGInlineText.h" +#endif // ENABLE(SVG) + +namespace WebCore { + +// DOM Section 1.1.1 + +// ### allow having children in text nodes for entities, comments etc. + +Text::Text(Document *doc, const String &_text) + : CharacterData(doc, _text) +{ +} + +Text::Text(Document *doc) + : CharacterData(doc) +{ +} + +Text::~Text() +{ +} + +PassRefPtr<Text> Text::splitText(unsigned offset, ExceptionCode& ec) +{ + ec = 0; + + // FIXME: This does not copy markers + + // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than + // the number of 16-bit units in data. + if (offset > m_data->length()) { + ec = INDEX_SIZE_ERR; + return 0; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return 0; + } + + RefPtr<StringImpl> oldStr = m_data; + RefPtr<Text> newText = createNew(oldStr->substring(offset)); + m_data = oldStr->substring(0, offset); + + dispatchModifiedEvent(oldStr.get()); + + if (parentNode()) + parentNode()->insertBefore(newText.get(), nextSibling(), ec); + if (ec) + return 0; + + if (renderer()) + static_cast<RenderText*>(renderer())->setText(m_data); + + return newText.release(); +} + +static const Text* earliestLogicallyAdjacentTextNode(const Text* t) +{ + const Node* n = t; + while ((n = n->previousSibling())) { + Node::NodeType type = n->nodeType(); + if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { + t = static_cast<const Text*>(n); + continue; + } + + // We would need to visit EntityReference child text nodes if they existed + ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes()); + break; + } + return t; +} + +static const Text* latestLogicallyAdjacentTextNode(const Text* t) +{ + const Node* n = t; + while ((n = n->nextSibling())) { + Node::NodeType type = n->nodeType(); + if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { + t = static_cast<const Text*>(n); + continue; + } + + // We would need to visit EntityReference child text nodes if they existed + ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes()); + break; + } + return t; +} + +String Text::wholeText() const +{ + const Text* startText = earliestLogicallyAdjacentTextNode(this); + const Text* endText = latestLogicallyAdjacentTextNode(this); + + Vector<UChar> result; + Node* onePastEndText = endText->nextSibling(); + for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) { + if (!n->isTextNode()) + continue; + const Text* t = static_cast<const Text*>(n); + const String& data = t->data(); + result.append(data.characters(), data.length()); + } + + return String::adopt(result); +} + +PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&) +{ + // We don't support "read-only" text nodes (no Entity node support) + // Thus, we remove all adjacent text nodes, and replace the contents of this one. + ASSERT(!isReadOnlyNode()); + // This method only raises exceptions when dealing with Entity nodes (which we don't support) + + // Protect startText and endText against mutation event handlers removing the last ref + RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this)); + RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this)); + + RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away + Node* parent = parentNode(); // Protect against mutation handlers moving this node during traversal + ExceptionCode ignored = 0; + for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) { + RefPtr<Node> nodeToRemove(n.release()); + n = nodeToRemove->nextSibling(); + parent->removeChild(nodeToRemove.get(), ignored); + } + + if (this != endText) { + Node* onePastEndText = endText->nextSibling(); + for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) { + RefPtr<Node> nodeToRemove(n.release()); + n = nodeToRemove->nextSibling(); + parent->removeChild(nodeToRemove.get(), ignored); + } + } + + if (newText.isEmpty()) { + if (parent && parentNode() == parent) + parent->removeChild(this, ignored); + return 0; + } + + setData(newText, ignored); + return protectedThis.release(); +} + +String Text::nodeName() const +{ + return textAtom.string(); +} + +Node::NodeType Text::nodeType() const +{ + return TEXT_NODE; +} + +PassRefPtr<Node> Text::cloneNode(bool /*deep*/) +{ + return document()->createTextNode(m_data); +} + +bool Text::rendererIsNeeded(RenderStyle *style) +{ + if (!CharacterData::rendererIsNeeded(style)) + return false; + + bool onlyWS = containsOnlyWhitespace(); + if (!onlyWS) + return true; + + RenderObject *par = parentNode()->renderer(); + + if (par->isTable() || par->isTableRow() || par->isTableSection() || par->isTableCol() || par->isFrameSet()) + return false; + + if (style->preserveNewline()) // pre/pre-wrap/pre-line always make renderers. + return true; + + RenderObject *prev = previousRenderer(); + if (prev && prev->isBR()) // <span><br/> <br/></span> + return false; + + if (par->isInlineFlow()) { + // <span><div/> <div/></span> + if (prev && !prev->isInline()) + return false; + } else { + if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline())) + return false; + + RenderObject *first = par->firstChild(); + while (first && first->isFloatingOrPositioned()) + first = first->nextSibling(); + RenderObject *next = nextRenderer(); + if (!first || next == first) + // Whitespace at the start of a block just goes away. Don't even + // make a render object for this text. + return false; + } + + return true; +} + +RenderObject *Text::createRenderer(RenderArena *arena, RenderStyle *style) +{ +#if ENABLE(SVG) + if (parentNode()->isSVGElement()) + return new (arena) RenderSVGInlineText(this, m_data); +#endif // ENABLE(SVG) + + return new (arena) RenderText(this, m_data); +} + +void Text::attach() +{ + createRendererIfNeeded(); + CharacterData::attach(); +} + +void Text::recalcStyle( StyleChange change ) +{ + if (change != NoChange && parentNode()) + if (renderer()) + renderer()->setStyle(parentNode()->renderer()->style()); + if (changed() && renderer() && renderer()->isText()) + static_cast<RenderText*>(renderer())->setText(m_data); + setChanged(NoStyleChange); +} + +// DOM Section 1.1.1 +bool Text::childTypeAllowed(NodeType) +{ + return false; +} + +PassRefPtr<Text> Text::createNew(PassRefPtr<StringImpl> string) +{ + return new Text(document(), string); +} + +String Text::toString() const +{ + // FIXME: substitute entity references as needed! + return nodeValue(); +} + +PassRefPtr<Text> Text::createWithLengthLimit(Document* doc, const String& text, unsigned& charsLeft, unsigned maxChars) +{ + if (charsLeft == text.length() && charsLeft <= maxChars) { + charsLeft = 0; + return new Text(doc, text); + } + + unsigned start = text.length() - charsLeft; + unsigned end = start + std::min(charsLeft, maxChars); + + // check we are not on an unbreakable boundary + TextBreakIterator* it = characterBreakIterator(text.characters(), text.length()); + if (end < text.length() && !isTextBreak(it, end)) + end = textBreakPreceding(it, end); + + // maxChars of unbreakable characters could lead to infinite loop + if (end <= start) + end = text.length(); + + String nodeText = text.substring(start, end - start); + charsLeft = text.length() - end; + + return new Text(doc, nodeText); +} + +#ifndef NDEBUG +void Text::formatForDebugger(char *buffer, unsigned length) const +{ + String result; + String s; + + s = nodeName(); + if (s.length() > 0) { + result += s; + } + + s = nodeValue(); + if (s.length() > 0) { + if (result.length() > 0) + result += "; "; + result += "value="; + result += s; + } + + strncpy(buffer, result.utf8().data(), length - 1); +} +#endif + +} // namespace WebCore diff --git a/WebCore/dom/Text.h b/WebCore/dom/Text.h new file mode 100644 index 0000000..e5282d5 --- /dev/null +++ b/WebCore/dom/Text.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef Text_h +#define Text_h + +#include "CharacterData.h" + +namespace WebCore { + +const unsigned cTextNodeLengthLimit = 1 << 16; + +class Text : public CharacterData { +public: + Text(Document *impl, const String &_text); + Text(Document *impl); + virtual ~Text(); + + // DOM methods & attributes for CharacterData + + PassRefPtr<Text> splitText(unsigned offset, ExceptionCode&); + + // DOM Level 3: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1312295772 + String wholeText() const; + PassRefPtr<Text> replaceWholeText(const String&, ExceptionCode&); + + // DOM methods overridden from parent classes + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + + // Other methods (not part of DOM) + + virtual bool isTextNode() const { return true; } + virtual void attach(); + virtual bool rendererIsNeeded(RenderStyle*); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void recalcStyle(StyleChange = NoChange); + virtual bool childTypeAllowed(NodeType); + + virtual String toString() const; + + static PassRefPtr<Text> createWithLengthLimit(Document*, const String&, unsigned& charsLeft, unsigned maxChars = cTextNodeLengthLimit); + +#ifndef NDEBUG + virtual void formatForDebugger(char* buffer, unsigned length) const; +#endif + +protected: + virtual PassRefPtr<Text> createNew(PassRefPtr<StringImpl>); +}; + +} // namespace WebCore + +#endif // Text_h diff --git a/WebCore/dom/Text.idl b/WebCore/dom/Text.idl new file mode 100644 index 0000000..1b0009d --- /dev/null +++ b/WebCore/dom/Text.idl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module core { + + interface [ + GenerateConstructor, + InterfaceUUID=4EA6B89C-F8E5-462a-A807-519446123184, + ImplementationUUID=CB5D61CB-D30D-486e-9BD7-F42B7611C2E5 + ] Text : CharacterData { + + // DOM Level 1 + + Text splitText(in [IsIndex] unsigned long offset) + raises (DOMException); + + // Introduced in DOM Level 3: + readonly attribute DOMString wholeText; + Text replaceWholeText(in DOMString content) + raises(DOMException); + }; + +} diff --git a/WebCore/dom/TextEvent.cpp b/WebCore/dom/TextEvent.cpp new file mode 100644 index 0000000..3f48a2d --- /dev/null +++ b/WebCore/dom/TextEvent.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "TextEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +using namespace EventNames; + +TextEvent::TextEvent() + : m_isLineBreak(false) + , m_isBackTab(false) +{ +} + +TextEvent::TextEvent(AbstractView* view, const String& data) + : UIEvent(textInputEvent, true, true, view, 0) + , m_data(data) + , m_isLineBreak(false) + , m_isBackTab(false) +{ +} + +TextEvent::~TextEvent() +{ +} + +void TextEvent::initTextEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, const String& data) +{ + if (dispatched()) + return; + + initUIEvent(type, canBubble, cancelable, view, 0); + + m_data = data; +} + +bool TextEvent::isTextEvent() const +{ + return true; +} + +} // namespace WebCore diff --git a/WebCore/dom/TextEvent.h b/WebCore/dom/TextEvent.h new file mode 100644 index 0000000..3297a01 --- /dev/null +++ b/WebCore/dom/TextEvent.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TextEvent_h +#define TextEvent_h + +#include "UIEvent.h" + +namespace WebCore { + + class TextEvent : public UIEvent { + public: + TextEvent(); + TextEvent(AbstractView*, const String& data); + virtual ~TextEvent(); + + void initTextEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView*, const String& data); + + String data() const { return m_data; } + + virtual bool isTextEvent() const; + + // If true, any newline characters in the text are line breaks only, not paragraph separators. + bool isLineBreak() const { return m_isLineBreak; } + void setIsLineBreak(bool isLineBreak) { m_isLineBreak = isLineBreak; } + + // If true, any tab characters in the text are backtabs. + bool isBackTab() const { return m_isBackTab; } + void setIsBackTab(bool isBackTab) { m_isBackTab = isBackTab; } + + private: + String m_data; + bool m_isLineBreak; + bool m_isBackTab; + }; + +} // namespace WebCore + +#endif // TextEvent_h diff --git a/WebCore/dom/TextEvent.idl b/WebCore/dom/TextEvent.idl new file mode 100644 index 0000000..779afd1 --- /dev/null +++ b/WebCore/dom/TextEvent.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module events { + + // Introduced in DOM Level 3: + interface [ + GenerateConstructor + ] TextEvent : UIEvent { + + readonly attribute DOMString data; + + void initTextEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in DOMWindow viewArg, + in DOMString dataArg); + + }; + +} diff --git a/WebCore/dom/Tokenizer.h b/WebCore/dom/Tokenizer.h new file mode 100644 index 0000000..1dfaca2 --- /dev/null +++ b/WebCore/dom/Tokenizer.h @@ -0,0 +1,78 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) + * + * 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. + * + */ + +#ifndef Tokenizer_h +#define Tokenizer_h + +namespace WebCore { + + class SegmentedString; + + class Tokenizer { + public: + Tokenizer(bool viewSourceMode = false) + : m_parserStopped(false) + , m_inViewSourceMode(viewSourceMode) + { + } + + virtual ~Tokenizer() { } + + // Script output must be prepended, while new data + // received during executing a script must be appended, hence the + // extra bool to be able to distinguish between both cases. + // document.write() always uses false, while the loader uses true. + virtual bool write(const SegmentedString&, bool appendData) = 0; + virtual void finish() = 0; + virtual bool isWaitingForScripts() const = 0; + virtual void stopParsing() { m_parserStopped = true; } + virtual bool processingData() const { return false; } + virtual int executingScript() const { return 0; } + + virtual bool wantsRawData() const { return false; } + virtual bool writeRawData(const char* data, int len) { return false; } + + bool inViewSourceMode() const { return m_inViewSourceMode; } + void setInViewSourceMode(bool mode) { m_inViewSourceMode = mode; } + + virtual bool wellFormed() const { return true; } + + virtual int lineNumber() const { return -1; } + virtual int columnNumber() const { return -1; } + + virtual void executeScriptsWaitingForStylesheets() {} + + virtual bool isHTMLTokenizer() const { return false; } + + protected: + // The tokenizer has buffers, so parsing may continue even after + // it stops receiving data. We use m_parserStopped to stop the tokenizer + // even when it has buffered data. + bool m_parserStopped; + bool m_inViewSourceMode; + }; + +} // namespace WebCore + +#endif // Tokenizer_h diff --git a/WebCore/dom/Traversal.cpp b/WebCore/dom/Traversal.cpp new file mode 100644 index 0000000..d9dd0a7 --- /dev/null +++ b/WebCore/dom/Traversal.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "Traversal.h" + +#include "Node.h" +#include "NodeFilter.h" + +using namespace KJS; + +namespace WebCore { + +Traversal::Traversal(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> nodeFilter, bool expandEntityReferences) + : RefCounted<Traversal>(0) + , m_root(rootNode) + , m_whatToShow(whatToShow) + , m_filter(nodeFilter) + , m_expandEntityReferences(expandEntityReferences) +{ +} + +Traversal::~Traversal() +{ +} + +short Traversal::acceptNode(Node* node, JSValue*& exception) const +{ + // FIXME: To handle XML properly we would have to check m_expandEntityReferences. + + // The bid twiddling here is done to map DOM node types, which are given as integers from + // 1 through 12, to whatToShow bit masks. + if (!(((1 << (node->nodeType() - 1)) & m_whatToShow))) + return NodeFilter::FILTER_SKIP; + if (!m_filter) + return NodeFilter::FILTER_ACCEPT; + return m_filter->acceptNode(node, exception); +} + +} // namespace WebCore diff --git a/WebCore/dom/Traversal.h b/WebCore/dom/Traversal.h new file mode 100644 index 0000000..78265e0 --- /dev/null +++ b/WebCore/dom/Traversal.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef Traversal_h +#define Traversal_h + +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace KJS { + class JSValue; +} + +namespace WebCore { + + class Node; + class NodeFilter; + + class Traversal : public RefCounted<Traversal> { + public: + Traversal(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); + virtual ~Traversal(); + + Node* root() const { return m_root.get(); } + unsigned whatToShow() const { return m_whatToShow; } + NodeFilter* filter() const { return m_filter.get(); } + bool expandEntityReferences() const { return m_expandEntityReferences; } + + protected: + short acceptNode(Node*, KJS::JSValue*& jsException) const; + + private: + RefPtr<Node> m_root; + unsigned m_whatToShow; + RefPtr<NodeFilter> m_filter; + bool m_expandEntityReferences; + }; + +} // namespace WebCore + +#endif // Traversal_h diff --git a/WebCore/dom/TreeWalker.cpp b/WebCore/dom/TreeWalker.cpp new file mode 100644 index 0000000..09c1c95 --- /dev/null +++ b/WebCore/dom/TreeWalker.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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 "config.h" +#include "TreeWalker.h" + +#include "ExceptionCode.h" +#include "Node.h" +#include "NodeFilter.h" +#include <wtf/PassRefPtr.h> + +using namespace KJS; + +namespace WebCore { + +TreeWalker::TreeWalker(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) + : Traversal(rootNode, whatToShow, filter, expandEntityReferences) + , m_current(root()) +{ +} + +void TreeWalker::setCurrentNode(PassRefPtr<Node> node, ExceptionCode& ec) +{ + if (!node) { + ec = NOT_SUPPORTED_ERR; + return; + } + m_current = node; +} + +inline Node* TreeWalker::setCurrent(PassRefPtr<Node> node) +{ + m_current = node; + return m_current.get(); +} + +Node* TreeWalker::parentNode(JSValue*& exception) +{ + exception = 0; + RefPtr<Node> node = m_current; + while (node != root()) { + node = node->parentNode(); + if (!node) + return 0; + short acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return setCurrent(node.release()); + } + return 0; +} + +Node* TreeWalker::firstChild(JSValue*& exception) +{ + exception = 0; + for (RefPtr<Node> node = m_current->firstChild(); node; ) { + short acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + switch (acceptNodeResult) { + case NodeFilter::FILTER_ACCEPT: + m_current = node.release(); + return m_current.get(); + case NodeFilter::FILTER_SKIP: + if (node->firstChild()) { + node = node->firstChild(); + continue; + } + break; + case NodeFilter::FILTER_REJECT: + break; + } + do { + if (node->nextSibling()) { + node = node->nextSibling(); + break; + } + Node* parent = node->parentNode(); + if (!parent || parent == root() || parent == m_current) + return 0; + node = parent; + } while (node); + } + return 0; +} + +Node* TreeWalker::lastChild(JSValue*& exception) +{ + exception = 0; + for (RefPtr<Node> node = m_current->lastChild(); node; ) { + short acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + switch (acceptNodeResult) { + case NodeFilter::FILTER_ACCEPT: + m_current = node.release(); + return m_current.get(); + case NodeFilter::FILTER_SKIP: + if (node->lastChild()) { + node = node->lastChild(); + continue; + } + break; + case NodeFilter::FILTER_REJECT: + break; + } + do { + if (node->previousSibling()) { + node = node->previousSibling(); + break; + } + Node* parent = node->parentNode(); + if (!parent || parent == root() || parent == m_current) + return 0; + node = parent; + } while (node); + } + return 0; +} + +Node* TreeWalker::previousSibling(JSValue*& exception) +{ + exception = 0; + RefPtr<Node> node = m_current; + if (node == root()) + return 0; + while (1) { + for (RefPtr<Node> sibling = node->previousSibling(); sibling; ) { + short acceptNodeResult = acceptNode(sibling.get(), exception); + if (exception) + return 0; + switch (acceptNodeResult) { + case NodeFilter::FILTER_ACCEPT: + m_current = sibling.release(); + return m_current.get(); + case NodeFilter::FILTER_SKIP: + if (sibling->firstChild()) { + sibling = sibling->firstChild(); + continue; + } + break; + case NodeFilter::FILTER_REJECT: + break; + } + sibling = sibling->previousSibling(); + } + node = node->parentNode(); + if (!node || node == root()) + return 0; + short acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return 0; + } +} + +Node* TreeWalker::nextSibling(JSValue*& exception) +{ + exception = 0; + RefPtr<Node> node = m_current; + if (node == root()) + return 0; + while (1) { + for (RefPtr<Node> sibling = node->nextSibling(); sibling; ) { + short acceptNodeResult = acceptNode(sibling.get(), exception); + if (exception) + return 0; + switch (acceptNodeResult) { + case NodeFilter::FILTER_ACCEPT: + m_current = sibling.release(); + return m_current.get(); + case NodeFilter::FILTER_SKIP: + if (sibling->firstChild()) { + sibling = sibling->firstChild(); + continue; + } + break; + case NodeFilter::FILTER_REJECT: + break; + } + sibling = sibling->nextSibling(); + } + node = node->parentNode(); + if (!node || node == root()) + return 0; + short acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return 0; + } +} + +Node* TreeWalker::previousNode(JSValue*& exception) +{ + exception = 0; + RefPtr<Node> node = m_current; + while (node != root()) { + while (Node* previousSibling = node->previousSibling()) { + node = previousSibling; + short acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_REJECT) + continue; + while (Node* lastChild = node->lastChild()) { + node = lastChild; + acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + continue; + } + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) { + m_current = node.release(); + return m_current.get(); + } + } + if (node == root()) + return 0; + Node* parent = node->parentNode(); + if (!parent) + return 0; + node = parent; + short acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return setCurrent(node.release()); + } + return 0; +} + +Node* TreeWalker::nextNode(JSValue*& exception) +{ + exception = 0; + RefPtr<Node> node = m_current; +Children: + while (Node* firstChild = node->firstChild()) { + node = firstChild; + short acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return setCurrent(node.release()); + if (acceptNodeResult == NodeFilter::FILTER_REJECT) + break; + } + while (Node* nextSibling = node->traverseNextSibling(root())) { + node = nextSibling; + short acceptNodeResult = acceptNode(node.get(), exception); + if (exception) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return setCurrent(node.release()); + if (acceptNodeResult == NodeFilter::FILTER_SKIP) + goto Children; + } + return 0; +} + +} // namespace WebCore diff --git a/WebCore/dom/TreeWalker.h b/WebCore/dom/TreeWalker.h new file mode 100644 index 0000000..8b0ea98 --- /dev/null +++ b/WebCore/dom/TreeWalker.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef TreeWalker_h +#define TreeWalker_h + +#include "Traversal.h" + +namespace WebCore { + + typedef int ExceptionCode; + + class TreeWalker : public Traversal { + public: + TreeWalker(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); + + Node* currentNode() const { return m_current.get(); } + void setCurrentNode(PassRefPtr<Node>, ExceptionCode&); + + Node* parentNode(KJS::JSValue*& exception); + Node* firstChild(KJS::JSValue*& exception); + Node* lastChild(KJS::JSValue*& exception); + Node* previousSibling(KJS::JSValue*& exception); + Node* nextSibling(KJS::JSValue*& exception); + Node* previousNode(KJS::JSValue*& exception); + Node* nextNode(KJS::JSValue*& exception); + + // For non-JS bindings. Silently ignores the JavaScript exception if any. + Node* parentNode() { KJS::JSValue* exception; return parentNode(exception); } + Node* firstChild() { KJS::JSValue* exception; return firstChild(exception); } + Node* lastChild() { KJS::JSValue* exception; return lastChild(exception); } + Node* previousSibling() { KJS::JSValue* exception; return previousSibling(exception); } + Node* nextSibling() { KJS::JSValue* exception; return nextSibling(exception); } + Node* previousNode() { KJS::JSValue* exception; return previousNode(exception); } + Node* nextNode() { KJS::JSValue* exception; return nextNode(exception); } + + private: + Node* setCurrent(PassRefPtr<Node>); + + RefPtr<Node> m_current; + }; + +} // namespace WebCore + +#endif // TreeWalker_h diff --git a/WebCore/dom/TreeWalker.idl b/WebCore/dom/TreeWalker.idl new file mode 100644 index 0000000..4f98d3a --- /dev/null +++ b/WebCore/dom/TreeWalker.idl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module traversal { + + // Introduced in DOM Level 2: + interface [CustomMarkFunction] TreeWalker { + readonly attribute Node root; + readonly attribute unsigned long whatToShow; + readonly attribute [ObjCIvar] NodeFilter filter; + readonly attribute boolean expandEntityReferences; + attribute Node currentNode + setter raises(DOMException); + + [Custom] Node parentNode(); + [Custom] Node firstChild(); + [Custom] Node lastChild(); + [Custom] Node previousSibling(); + [Custom] Node nextSibling(); + [Custom] Node previousNode(); + [Custom] Node nextNode(); + }; + +} diff --git a/WebCore/dom/UIEvent.cpp b/WebCore/dom/UIEvent.cpp new file mode 100644 index 0000000..f247eed --- /dev/null +++ b/WebCore/dom/UIEvent.cpp @@ -0,0 +1,99 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "UIEvent.h" + +#include "DOMWindow.h" + +namespace WebCore { + +UIEvent::UIEvent() + : m_detail(0) +{ +} + +UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, AbstractView* viewArg, int detailArg) + : Event(eventType, canBubbleArg, cancelableArg) + , m_view(viewArg) + , m_detail(detailArg) +{ +} + +UIEvent::~UIEvent() +{ +} + +void UIEvent::initUIEvent(const AtomicString& typeArg, bool canBubbleArg, bool cancelableArg, AbstractView* viewArg, int detailArg) +{ + if (dispatched()) + return; + + initEvent(typeArg, canBubbleArg, cancelableArg); + + m_view = viewArg; + m_detail = detailArg; +} + +bool UIEvent::isUIEvent() const +{ + return true; +} + +int UIEvent::keyCode() const +{ + return 0; +} + +int UIEvent::charCode() const +{ + return 0; +} + +int UIEvent::layerX() const +{ + return 0; +} + +int UIEvent::layerY() const +{ + return 0; +} + +int UIEvent::pageX() const +{ + return 0; +} + +int UIEvent::pageY() const +{ + return 0; +} + +int UIEvent::which() const +{ + return 0; +} + +} // namespace WebCore diff --git a/WebCore/dom/UIEvent.h b/WebCore/dom/UIEvent.h new file mode 100644 index 0000000..9c5e0cd --- /dev/null +++ b/WebCore/dom/UIEvent.h @@ -0,0 +1,68 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef UIEvent_h +#define UIEvent_h + +#include "Event.h" + +namespace WebCore { + + class DOMWindow; + + typedef DOMWindow AbstractView; + + class UIEvent : public Event { + public: + UIEvent(); + UIEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, int detail); + virtual ~UIEvent(); + + void initUIEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, int detail); + + AbstractView* view() const { return m_view.get(); } + int detail() const { return m_detail; } + + virtual bool isUIEvent() const; + + virtual int keyCode() const; + virtual int charCode() const; + + virtual int layerX() const; + virtual int layerY() const; + + virtual int pageX() const; + virtual int pageY() const; + + virtual int which() const; + + private: + RefPtr<AbstractView> m_view; + int m_detail; + }; + +} // namespace WebCore + +#endif // UIEvent_h diff --git a/WebCore/dom/UIEvent.idl b/WebCore/dom/UIEvent.idl new file mode 100644 index 0000000..f6eeb8d --- /dev/null +++ b/WebCore/dom/UIEvent.idl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module events { + + // Introduced in DOM Level 2: + interface [ + GenerateConstructor + ] UIEvent : Event { + readonly attribute DOMWindow view; + readonly attribute long detail; + + [OldStyleObjC] void initUIEvent(in AtomicString type, + in boolean canBubble, + in boolean cancelable, + in DOMWindow view, + in long detail); + + // extentsions + readonly attribute long keyCode; + readonly attribute long charCode; + readonly attribute long layerX; + readonly attribute long layerY; + readonly attribute long pageX; + readonly attribute long pageY; + readonly attribute long which; + }; + +} diff --git a/WebCore/dom/UIEventWithKeyState.cpp b/WebCore/dom/UIEventWithKeyState.cpp new file mode 100644 index 0000000..d757b60 --- /dev/null +++ b/WebCore/dom/UIEventWithKeyState.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "UIEventWithKeyState.h" + +namespace WebCore { + +UIEventWithKeyState* findEventWithKeyState(Event* event) +{ + for (Event* e = event; e; e = e->underlyingEvent()) + if (e->isKeyboardEvent() || e->isMouseEvent()) + return static_cast<UIEventWithKeyState*>(e); + return 0; +} + +} // namespace WebCore diff --git a/WebCore/dom/UIEventWithKeyState.h b/WebCore/dom/UIEventWithKeyState.h new file mode 100644 index 0000000..9d42129 --- /dev/null +++ b/WebCore/dom/UIEventWithKeyState.h @@ -0,0 +1,70 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef UIEventWithKeyState_h +#define UIEventWithKeyState_h + +#include "UIEvent.h" + +namespace WebCore { + + class UIEventWithKeyState : public UIEvent { + public: + UIEventWithKeyState() + : m_ctrlKey(false) + , m_altKey(false) + , m_shiftKey(false) + , m_metaKey(false) + { + } + + UIEventWithKeyState(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, + int detail, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) + : UIEvent(type, canBubble, cancelable, view, detail) + , m_ctrlKey(ctrlKey) + , m_altKey(altKey) + , m_shiftKey(shiftKey) + , m_metaKey(metaKey) + { + } + + bool ctrlKey() const { return m_ctrlKey; } + bool shiftKey() const { return m_shiftKey; } + bool altKey() const { return m_altKey; } + bool metaKey() const { return m_metaKey; } + + protected: + // Expose these so init functions can set them. + bool m_ctrlKey : 1; + bool m_altKey : 1; + bool m_shiftKey : 1; + bool m_metaKey : 1; + }; + + UIEventWithKeyState* findEventWithKeyState(Event*); + +} // namespace WebCore + +#endif // UIEventWithKeyState_h diff --git a/WebCore/dom/WheelEvent.cpp b/WebCore/dom/WheelEvent.cpp new file mode 100644 index 0000000..02b2448 --- /dev/null +++ b/WebCore/dom/WheelEvent.cpp @@ -0,0 +1,85 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. + * + * 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 "config.h" +#include "WheelEvent.h" + +#include "EventNames.h" + +#include <wtf/MathExtras.h> + +namespace WebCore { + +using namespace EventNames; + +WheelEvent::WheelEvent() + : m_wheelDeltaX(0) + , m_wheelDeltaY(0) +{ +} + +WheelEvent::WheelEvent(float wheelDeltaX, float wheelDeltaY, AbstractView* view, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) + : MouseRelatedEvent(mousewheelEvent, + true, true, view, 0, screenX, screenY, pageX, pageY, + ctrlKey, altKey, shiftKey, metaKey) + , m_wheelDeltaX(lroundf(wheelDeltaX) * 120) + , m_wheelDeltaY(lroundf(wheelDeltaY) * 120) // Normalize to the Windows 120 multiple +{ + // Rounding delta to zero makes no sense and breaks Google Maps, <http://bugs.webkit.org/show_bug.cgi?id=16078>. + if (wheelDeltaX && !m_wheelDeltaX) + m_wheelDeltaX = (wheelDeltaX > 0) ? 120 : -120; + if (wheelDeltaY && !m_wheelDeltaY) + m_wheelDeltaY = (wheelDeltaY > 0) ? 120 : -120; +} + +void WheelEvent::initWheelEvent(int wheelDeltaX, int wheelDeltaY, AbstractView* view, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) +{ + if (dispatched()) + return; + + initUIEvent(mousewheelEvent, true, true, view, 0); + + m_screenX = screenX; + m_screenY = screenY; + m_ctrlKey = ctrlKey; + m_altKey = altKey; + m_shiftKey = shiftKey; + m_metaKey = metaKey; + m_wheelDeltaX = wheelDeltaX; + m_wheelDeltaY = wheelDeltaY; + + initCoordinates(pageX, pageY); +} + + +bool WheelEvent::isWheelEvent() const +{ + return true; +} + +} // namespace WebCore diff --git a/WebCore/dom/WheelEvent.h b/WebCore/dom/WheelEvent.h new file mode 100644 index 0000000..9d21949 --- /dev/null +++ b/WebCore/dom/WheelEvent.h @@ -0,0 +1,61 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef WheelEvent_h +#define WheelEvent_h + +#include "MouseRelatedEvent.h" + +namespace WebCore { + + // extension: mouse wheel event + class WheelEvent : public MouseRelatedEvent { + public: + WheelEvent(); + WheelEvent(float wheelDeltaX, float wheelDeltaY, AbstractView*, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + + void initWheelEvent(int wheelDeltaX, int wheelDeltaY, AbstractView*, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + + int wheelDelta() const { if (m_wheelDeltaY == 0) return m_wheelDeltaX; return m_wheelDeltaY; } + int wheelDeltaX() const { return m_wheelDeltaX; } + int wheelDeltaY() const { return m_wheelDeltaY; } + + // Needed for Objective-C legacy support + bool isHorizontal() const { return m_wheelDeltaX; } + + private: + virtual bool isWheelEvent() const; + + int m_wheelDeltaX; + int m_wheelDeltaY; + }; + +} // namespace WebCore + +#endif // WheelEvent_h diff --git a/WebCore/dom/WheelEvent.idl b/WebCore/dom/WheelEvent.idl new file mode 100644 index 0000000..1445509 --- /dev/null +++ b/WebCore/dom/WheelEvent.idl @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module events { + + // Based off of proposed IDL interface for WheelEvent: + interface [ + GenerateConstructor + ] WheelEvent : UIEvent { + readonly attribute long screenX; + readonly attribute long screenY; + readonly attribute long clientX; + readonly attribute long clientY; + readonly attribute boolean ctrlKey; + readonly attribute boolean shiftKey; + readonly attribute boolean altKey; + readonly attribute boolean metaKey; + readonly attribute long wheelDelta; + readonly attribute long wheelDeltaX; + readonly attribute long wheelDeltaY; + + // WebKit Extensions + readonly attribute long offsetX; + readonly attribute long offsetY; + readonly attribute long x; + readonly attribute long y; + +#if defined(LANGUAGE_OBJECTIVE_C) + readonly attribute boolean isHorizontal; +#endif /* defined(LANGUAGE_OBJECTIVE_C) */ + +#if !defined(LANGUAGE_JAVASCRIPT) + void initWheelEvent(in long wheelDeltaX, + in long wheelDeltaY, + in DOMWindow view, + in long screenX, + in long screenY, + in long clientX, + in long clientY, + in boolean ctrlKey, + in boolean altKey, + in boolean shiftKey, + in boolean metaKey); +#endif /* !defined(LANGUAGE_JAVASCRIPT) */ + }; + +} diff --git a/WebCore/dom/XMLTokenizer.cpp b/WebCore/dom/XMLTokenizer.cpp new file mode 100644 index 0000000..fe7c95a --- /dev/null +++ b/WebCore/dom/XMLTokenizer.cpp @@ -0,0 +1,2105 @@ +/* + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) + * Copyright (C) 2007 Trolltech ASA + * + * 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 "config.h" +#include "XMLTokenizer.h" + +#include "CDATASection.h" +#include "CString.h" +#include "Cache.h" +#include "CachedScript.h" +#include "Comment.h" +#include "DocLoader.h" +#include "Document.h" +#include "DocumentFragment.h" +#include "DocumentType.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HTMLNames.h" +#include "HTMLScriptElement.h" +#include "HTMLStyleElement.h" +#include "HTMLTokenizer.h" +#include "ProcessingInstruction.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#ifndef USE_QXMLSTREAM +#include <libxml/parser.h> +#include <libxml/parserInternals.h> +#else +#include <QDebug> +#endif +#include <wtf/Platform.h> +#include <wtf/StringExtras.h> +#include <wtf/Threading.h> +#include <wtf/Vector.h> + +#if ENABLE(XSLT) +#include <libxslt/xslt.h> +#endif + +#if ENABLE(SVG) +#include "SVGNames.h" +#include "SVGStyleElement.h" +#include "XLinkNames.h" +#endif + +using namespace std; + +namespace WebCore { + +using namespace EventNames; +using namespace HTMLNames; + +const int maxErrors = 25; + +#ifndef USE_QXMLSTREAM +class PendingCallbacks { +public: + PendingCallbacks() + { + m_callbacks.setAutoDelete(true); + } + + void appendStartElementNSCallback(const xmlChar* xmlLocalName, const xmlChar* xmlPrefix, const xmlChar* xmlURI, int nb_namespaces, + const xmlChar** namespaces, int nb_attributes, int nb_defaulted, const xmlChar** attributes) + { + PendingStartElementNSCallback* callback = new PendingStartElementNSCallback; + + callback->xmlLocalName = xmlStrdup(xmlLocalName); + callback->xmlPrefix = xmlStrdup(xmlPrefix); + callback->xmlURI = xmlStrdup(xmlURI); + callback->nb_namespaces = nb_namespaces; + callback->namespaces = reinterpret_cast<xmlChar**>(xmlMalloc(sizeof(xmlChar*) * nb_namespaces * 2)); + for (int i = 0; i < nb_namespaces * 2 ; i++) + callback->namespaces[i] = xmlStrdup(namespaces[i]); + callback->nb_attributes = nb_attributes; + callback->nb_defaulted = nb_defaulted; + callback->attributes = reinterpret_cast<xmlChar**>(xmlMalloc(sizeof(xmlChar*) * nb_attributes * 5)); + for (int i = 0; i < nb_attributes; i++) { + // Each attribute has 5 elements in the array: + // name, prefix, uri, value and an end pointer. + + for (int j = 0; j < 3; j++) + callback->attributes[i * 5 + j] = xmlStrdup(attributes[i * 5 + j]); + + int len = attributes[i * 5 + 4] - attributes[i * 5 + 3]; + + callback->attributes[i * 5 + 3] = xmlStrndup(attributes[i * 5 + 3], len); + callback->attributes[i * 5 + 4] = callback->attributes[i * 5 + 3] + len; + } + + m_callbacks.append(callback); + } + + void appendEndElementNSCallback() + { + PendingEndElementNSCallback* callback = new PendingEndElementNSCallback; + + m_callbacks.append(callback); + } + + void appendCharactersCallback(const xmlChar* s, int len) + { + PendingCharactersCallback* callback = new PendingCharactersCallback; + + callback->s = xmlStrndup(s, len); + callback->len = len; + + m_callbacks.append(callback); + } + + void appendProcessingInstructionCallback(const xmlChar* target, const xmlChar* data) + { + PendingProcessingInstructionCallback* callback = new PendingProcessingInstructionCallback; + + callback->target = xmlStrdup(target); + callback->data = xmlStrdup(data); + + m_callbacks.append(callback); + } + + void appendCDATABlockCallback(const xmlChar* s, int len) + { + PendingCDATABlockCallback* callback = new PendingCDATABlockCallback; + + callback->s = xmlStrndup(s, len); + callback->len = len; + + m_callbacks.append(callback); + } + + void appendCommentCallback(const xmlChar* s) + { + PendingCommentCallback* callback = new PendingCommentCallback; + + callback->s = xmlStrdup(s); + + m_callbacks.append(callback); + } + + void appendInternalSubsetCallback(const xmlChar* name, const xmlChar* externalID, const xmlChar* systemID) + { + PendingInternalSubsetCallback* callback = new PendingInternalSubsetCallback; + + callback->name = xmlStrdup(name); + callback->externalID = xmlStrdup(externalID); + callback->systemID = xmlStrdup(systemID); + + m_callbacks.append(callback); + } + + void appendErrorCallback(XMLTokenizer::ErrorType type, const char* message, int lineNumber, int columnNumber) + { + PendingErrorCallback* callback = new PendingErrorCallback; + + callback->message = strdup(message); + callback->type = type; + callback->lineNumber = lineNumber; + callback->columnNumber = columnNumber; + + m_callbacks.append(callback); + } + + void callAndRemoveFirstCallback(XMLTokenizer* tokenizer) + { + PendingCallback* cb = m_callbacks.getFirst(); + + cb->call(tokenizer); + m_callbacks.removeFirst(); + } + + bool isEmpty() const { return m_callbacks.isEmpty(); } + +private: + struct PendingCallback { + + virtual ~PendingCallback() { } + + virtual void call(XMLTokenizer* tokenizer) = 0; + }; + + struct PendingStartElementNSCallback : public PendingCallback { + virtual ~PendingStartElementNSCallback() { + xmlFree(xmlLocalName); + xmlFree(xmlPrefix); + xmlFree(xmlURI); + for (int i = 0; i < nb_namespaces * 2; i++) + xmlFree(namespaces[i]); + xmlFree(namespaces); + for (int i = 0; i < nb_attributes; i++) + for (int j = 0; j < 4; j++) + xmlFree(attributes[i * 5 + j]); + xmlFree(attributes); + } + + virtual void call(XMLTokenizer* tokenizer) { + tokenizer->startElementNs(xmlLocalName, xmlPrefix, xmlURI, + nb_namespaces, (const xmlChar**)namespaces, + nb_attributes, nb_defaulted, (const xmlChar**)(attributes)); + } + + xmlChar* xmlLocalName; + xmlChar* xmlPrefix; + xmlChar* xmlURI; + int nb_namespaces; + xmlChar** namespaces; + int nb_attributes; + int nb_defaulted; + xmlChar** attributes; + }; + + struct PendingEndElementNSCallback : public PendingCallback { + virtual void call(XMLTokenizer* tokenizer) + { + tokenizer->endElementNs(); + } + }; + + struct PendingCharactersCallback : public PendingCallback { + virtual ~PendingCharactersCallback() + { + xmlFree(s); + } + + virtual void call(XMLTokenizer* tokenizer) + { + tokenizer->characters(s, len); + } + + xmlChar* s; + int len; + }; + + struct PendingProcessingInstructionCallback : public PendingCallback { + virtual ~PendingProcessingInstructionCallback() + { + xmlFree(target); + xmlFree(data); + } + + virtual void call(XMLTokenizer* tokenizer) + { + tokenizer->processingInstruction(target, data); + } + + xmlChar* target; + xmlChar* data; + }; + + struct PendingCDATABlockCallback : public PendingCallback { + virtual ~PendingCDATABlockCallback() + { + xmlFree(s); + } + + virtual void call(XMLTokenizer* tokenizer) + { + tokenizer->cdataBlock(s, len); + } + + xmlChar* s; + int len; + }; + + struct PendingCommentCallback : public PendingCallback { + virtual ~PendingCommentCallback() + { + xmlFree(s); + } + + virtual void call(XMLTokenizer* tokenizer) + { + tokenizer->comment(s); + } + + xmlChar* s; + }; + + struct PendingInternalSubsetCallback : public PendingCallback { + virtual ~PendingInternalSubsetCallback() + { + xmlFree(name); + xmlFree(externalID); + xmlFree(systemID); + } + + virtual void call(XMLTokenizer* tokenizer) + { + tokenizer->internalSubset(name, externalID, systemID); + } + + xmlChar* name; + xmlChar* externalID; + xmlChar* systemID; + }; + + struct PendingErrorCallback: public PendingCallback { + virtual ~PendingErrorCallback() + { + free (message); + } + + virtual void call(XMLTokenizer* tokenizer) + { + tokenizer->handleError(type, message, lineNumber, columnNumber); + } + + XMLTokenizer::ErrorType type; + char* message; + int lineNumber; + int columnNumber; + }; + +public: + DeprecatedPtrList<PendingCallback> m_callbacks; +}; +// -------------------------------- + +static int globalDescriptor = 0; +static DocLoader* globalDocLoader = 0; +static ThreadIdentifier libxmlLoaderThread = 0; + +static int matchFunc(const char* uri) +{ + // Only match loads initiated due to uses of libxml2 from within XMLTokenizer to avoid + // interfering with client applications that also use libxml2. http://bugs.webkit.org/show_bug.cgi?id=17353 + return globalDocLoader && currentThread() == libxmlLoaderThread; +} + +class OffsetBuffer { +public: + OffsetBuffer(const Vector<char>& b) : m_buffer(b), m_currentOffset(0) { } + + int readOutBytes(char* outputBuffer, unsigned askedToRead) { + unsigned bytesLeft = m_buffer.size() - m_currentOffset; + unsigned lenToCopy = min(askedToRead, bytesLeft); + if (lenToCopy) { + memcpy(outputBuffer, m_buffer.data() + m_currentOffset, lenToCopy); + m_currentOffset += lenToCopy; + } + return lenToCopy; + } + +private: + Vector<char> m_buffer; + unsigned m_currentOffset; +}; + +static bool shouldAllowExternalLoad(const char* inURI) +{ + if (strstr(inURI, "/etc/xml/catalog") + || strstr(inURI, "http://www.w3.org/Graphics/SVG") == inURI + || strstr(inURI, "http://www.w3.org/TR/xhtml") == inURI) + return false; + return true; +} +static void* openFunc(const char* uri) +{ + ASSERT(globalDocLoader); + ASSERT(currentThread() == libxmlLoaderThread); + + if (!shouldAllowExternalLoad(uri)) + return &globalDescriptor; + + ResourceError error; + ResourceResponse response; + Vector<char> data; + + DocLoader* docLoader = globalDocLoader; + globalDocLoader = 0; + // FIXME: We should restore the original global error handler as well. + + if (docLoader->frame()) + docLoader->frame()->loader()->loadResourceSynchronously(KURL(uri), error, response, data); + + globalDocLoader = docLoader; + + return new OffsetBuffer(data); +} + +static int readFunc(void* context, char* buffer, int len) +{ + // Do 0-byte reads in case of a null descriptor + if (context == &globalDescriptor) + return 0; + + OffsetBuffer* data = static_cast<OffsetBuffer*>(context); + return data->readOutBytes(buffer, len); +} + +static int writeFunc(void* context, const char* buffer, int len) +{ + // Always just do 0-byte writes + return 0; +} + +static int closeFunc(void* context) +{ + if (context != &globalDescriptor) { + OffsetBuffer* data = static_cast<OffsetBuffer*>(context); + delete data; + } + return 0; +} + +static void errorFunc(void*, const char*, ...) +{ + // FIXME: It would be nice to display error messages somewhere. +} + +void setLoaderForLibXMLCallbacks(DocLoader* docLoader) +{ + globalDocLoader = docLoader; +} + +static xmlParserCtxtPtr createStringParser(xmlSAXHandlerPtr handlers, void* userData) +{ + static bool didInit = false; + if (!didInit) { + xmlInitParser(); + xmlRegisterInputCallbacks(matchFunc, openFunc, readFunc, closeFunc); + xmlRegisterOutputCallbacks(matchFunc, openFunc, writeFunc, closeFunc); + libxmlLoaderThread = currentThread(); + didInit = true; + } + + xmlParserCtxtPtr parser = xmlCreatePushParserCtxt(handlers, 0, 0, 0, 0); + parser->_private = userData; + parser->replaceEntities = true; + const UChar BOM = 0xFEFF; + const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM); + xmlSwitchEncoding(parser, BOMHighByte == 0xFF ? XML_CHAR_ENCODING_UTF16LE : XML_CHAR_ENCODING_UTF16BE); + return parser; +} +#endif + +#if defined(USE_QXMLSTREAM) && QT_VERSION >= 0x040400 +class EntityResolver : public QXmlStreamEntityResolver +{ + virtual QString resolveUndeclaredEntity(const QString &name); +}; + +QString EntityResolver::resolveUndeclaredEntity(const QString &name) +{ + UChar c = decodeNamedEntity(name.toUtf8().constData()); + return QString(c); +} +#endif + +// -------------------------------- + +XMLTokenizer::XMLTokenizer(Document* _doc, FrameView* _view) + : m_doc(_doc) + , m_view(_view) +#ifdef USE_QXMLSTREAM + , m_wroteText(false) +#else + , m_context(0) +#endif + , m_currentNode(_doc) + , m_currentNodeIsReferenced(false) + , m_sawError(false) + , m_sawXSLTransform(false) + , m_sawFirstElement(false) + , m_isXHTMLDocument(false) + , m_parserPaused(false) + , m_requestingScript(false) + , m_finishCalled(false) + , m_errorCount(0) + , m_lastErrorLine(0) + , m_lastErrorColumn(0) + , m_pendingScript(0) + , m_scriptStartLine(0) + , m_parsingFragment(false) +#ifndef USE_QXMLSTREAM + , m_pendingCallbacks(new PendingCallbacks) +#endif +{ +#if defined(USE_QXMLSTREAM) && QT_VERSION >= 0x040400 + m_stream.setEntityResolver(new EntityResolver); +#endif +} + +XMLTokenizer::XMLTokenizer(DocumentFragment* fragment, Element* parentElement) + : m_doc(fragment->document()) + , m_view(0) +#ifdef USE_QXMLSTREAM + , m_wroteText(false) +#else + , m_context(0) +#endif + , m_currentNode(fragment) + , m_currentNodeIsReferenced(fragment) + , m_sawError(false) + , m_sawXSLTransform(false) + , m_sawFirstElement(false) + , m_isXHTMLDocument(false) + , m_parserPaused(false) + , m_requestingScript(false) + , m_finishCalled(false) + , m_errorCount(0) + , m_lastErrorLine(0) + , m_lastErrorColumn(0) + , m_pendingScript(0) + , m_scriptStartLine(0) + , m_parsingFragment(true) +#ifndef USE_QXMLSTREAM + , m_pendingCallbacks(new PendingCallbacks) +#endif +{ + if (fragment) + fragment->ref(); + if (m_doc) + m_doc->ref(); + + // Add namespaces based on the parent node + Vector<Element*> elemStack; + while (parentElement) { + elemStack.append(parentElement); + + Node* n = parentElement->parentNode(); + if (!n || !n->isElementNode()) + break; + parentElement = static_cast<Element*>(n); + } + + if (elemStack.isEmpty()) + return; + +#if !PLATFORM(QT) || QT_VERSION < 0x040400 + for (Element* element = elemStack.last(); !elemStack.isEmpty(); elemStack.removeLast()) { + if (NamedAttrMap* attrs = element->attributes()) { + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute* attr = attrs->attributeItem(i); + if (attr->localName() == "xmlns") + m_defaultNamespaceURI = attr->value(); + else if (attr->prefix() == "xmlns") + m_prefixToNamespaceMap.set(attr->localName(), attr->value()); + } + } + } +#else + QXmlStreamNamespaceDeclarations namespaces; + for (Element* element = elemStack.last(); !elemStack.isEmpty(); elemStack.removeLast()) { + if (NamedAttrMap* attrs = element->attributes()) { + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute* attr = attrs->attributeItem(i); + if (attr->localName() == "xmlns") + m_defaultNamespaceURI = attr->value(); + else if (attr->prefix() == "xmlns") + namespaces.append(QXmlStreamNamespaceDeclaration(attr->localName(), attr->value())); + } + } + } + m_stream.addExtraNamespaceDeclarations(namespaces); + m_stream.setEntityResolver(new EntityResolver); +#endif + + // If the parent element is not in document tree, there may be no xmlns attribute; just default to the parent's namespace. + if (m_defaultNamespaceURI.isNull() && !parentElement->inDocument()) + m_defaultNamespaceURI = parentElement->namespaceURI(); +} + +XMLTokenizer::~XMLTokenizer() +{ + setCurrentNode(0); + if (m_parsingFragment && m_doc) + m_doc->deref(); + if (m_pendingScript) + m_pendingScript->deref(this); +#if defined(USE_QXMLSTREAM) && QT_VERSION >= 0x040400 + delete m_stream.entityResolver(); +#endif +} + +void XMLTokenizer::setCurrentNode(Node* n) +{ + bool nodeNeedsReference = n && n != m_doc; + if (nodeNeedsReference) + n->ref(); + if (m_currentNodeIsReferenced) + m_currentNode->deref(); + m_currentNode = n; + m_currentNodeIsReferenced = nodeNeedsReference; +} + +bool XMLTokenizer::write(const SegmentedString& s, bool /*appendData*/) +{ + String parseString = s.toString(); + + if (m_sawXSLTransform || !m_sawFirstElement) + m_originalSourceForTransform += parseString; + + if (m_parserStopped || m_sawXSLTransform) + return false; + + if (m_parserPaused) { + m_pendingSrc.append(s); + return false; + } + +#ifndef USE_QXMLSTREAM + if (!m_context) + initializeParserContext(); + + // libXML throws an error if you try to switch the encoding for an empty string. + if (parseString.length()) { + // Hack around libxml2's lack of encoding overide support by manually + // resetting the encoding to UTF-16 before every chunk. Otherwise libxml + // will detect <?xml version="1.0" encoding="<encoding name>"?> blocks + // and switch encodings, causing the parse to fail. + const UChar BOM = 0xFEFF; + const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM); + xmlSwitchEncoding(m_context, BOMHighByte == 0xFF ? XML_CHAR_ENCODING_UTF16LE : XML_CHAR_ENCODING_UTF16BE); + + xmlParseChunk(m_context, reinterpret_cast<const char*>(parseString.characters()), sizeof(UChar) * parseString.length(), 0); + } +#else + m_wroteText = true; + QString data(parseString); + if (!data.isEmpty()) { +#if QT_VERSION < 0x040400 + if (!m_sawFirstElement) { + int idx = data.indexOf(QLatin1String("<?xml")); + if (idx != -1) { + int start = idx + 5; + int end = data.indexOf(QLatin1String("?>"), start); + QString content = data.mid(start, end-start); + bool ok = true; + HashMap<String, String> attrs = parseAttributes(content, ok); + String version = attrs.get("version"); + String encoding = attrs.get("encoding"); + ExceptionCode ec = 0; + if (!m_parsingFragment) { + if (!version.isEmpty()) + m_doc->setXMLVersion(version, ec); + if (!encoding.isEmpty()) + m_doc->setXMLEncoding(encoding); + } + } + } +#endif + m_stream.addData(data); + parse(); + } +#endif + + return false; +} +#ifndef USE_QXMLSTREAM +static inline String toString(const xmlChar* str, unsigned len) +{ + return UTF8Encoding().decode(reinterpret_cast<const char*>(str), len); +} + +static inline String toString(const xmlChar* str) +{ + if (!str) + return String(); + + return UTF8Encoding().decode(reinterpret_cast<const char*>(str), strlen(reinterpret_cast<const char*>(str))); +} + +struct _xmlSAX2Namespace { + const xmlChar* prefix; + const xmlChar* uri; +}; +typedef struct _xmlSAX2Namespace xmlSAX2Namespace; + +static inline void handleElementNamespaces(Element* newElement, const xmlChar** libxmlNamespaces, int nb_namespaces, ExceptionCode& ec) +{ + xmlSAX2Namespace* namespaces = reinterpret_cast<xmlSAX2Namespace*>(libxmlNamespaces); + for(int i = 0; i < nb_namespaces; i++) { + String namespaceQName = "xmlns"; + String namespaceURI = toString(namespaces[i].uri); + if (namespaces[i].prefix) + namespaceQName = "xmlns:" + toString(namespaces[i].prefix); + newElement->setAttributeNS("http://www.w3.org/2000/xmlns/", namespaceQName, namespaceURI, ec); + if (ec) // exception setting attributes + return; + } +} + +struct _xmlSAX2Attributes { + const xmlChar* localname; + const xmlChar* prefix; + const xmlChar* uri; + const xmlChar* value; + const xmlChar* end; +}; +typedef struct _xmlSAX2Attributes xmlSAX2Attributes; + +static inline void handleElementAttributes(Element* newElement, const xmlChar** libxmlAttributes, int nb_attributes, ExceptionCode& ec) +{ + xmlSAX2Attributes* attributes = reinterpret_cast<xmlSAX2Attributes*>(libxmlAttributes); + for(int i = 0; i < nb_attributes; i++) { + String attrLocalName = toString(attributes[i].localname); + int valueLength = (int) (attributes[i].end - attributes[i].value); + String attrValue = toString(attributes[i].value, valueLength); + String attrPrefix = toString(attributes[i].prefix); + String attrURI = attrPrefix.isEmpty() ? String() : toString(attributes[i].uri); + String attrQName = attrPrefix.isEmpty() ? attrLocalName : attrPrefix + ":" + attrLocalName; + + newElement->setAttributeNS(attrURI, attrQName, attrValue, ec); + if (ec) // exception setting attributes + return; + } +} + +void XMLTokenizer::startElementNs(const xmlChar* xmlLocalName, const xmlChar* xmlPrefix, const xmlChar* xmlURI, int nb_namespaces, + const xmlChar** libxmlNamespaces, int nb_attributes, int nb_defaulted, const xmlChar** libxmlAttributes) +{ + if (m_parserStopped) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendStartElementNSCallback(xmlLocalName, xmlPrefix, xmlURI, nb_namespaces, libxmlNamespaces, + nb_attributes, nb_defaulted, libxmlAttributes); + return; + } + + m_sawFirstElement = true; + + exitText(); + + String localName = toString(xmlLocalName); + String uri = toString(xmlURI); + String prefix = toString(xmlPrefix); + + if (m_parsingFragment && uri.isNull()) { + if (!prefix.isNull()) + uri = m_prefixToNamespaceMap.get(prefix); + else + uri = m_defaultNamespaceURI; + } + + ExceptionCode ec = 0; + QualifiedName qName(prefix, localName, uri); + RefPtr<Element> newElement = m_doc->createElement(qName, true, ec); + if (!newElement) { + stopParsing(); + return; + } + + handleElementNamespaces(newElement.get(), libxmlNamespaces, nb_namespaces, ec); + if (ec) { + stopParsing(); + return; + } + + handleElementAttributes(newElement.get(), libxmlAttributes, nb_attributes, ec); + if (ec) { + stopParsing(); + return; + } + + newElement->beginParsingChildren(); + + if (newElement->hasTagName(scriptTag)) + static_cast<HTMLScriptElement*>(newElement.get())->setCreatedByParser(true); + else if (newElement->hasTagName(HTMLNames::styleTag)) + static_cast<HTMLStyleElement*>(newElement.get())->setCreatedByParser(true); +#if ENABLE(SVG) + else if (newElement->hasTagName(SVGNames::styleTag)) + static_cast<SVGStyleElement*>(newElement.get())->setCreatedByParser(true); +#endif + + if (newElement->hasTagName(HTMLNames::scriptTag) +#if ENABLE(SVG) + || newElement->hasTagName(SVGNames::scriptTag) +#endif + ) + m_scriptStartLine = lineNumber(); + + if (!m_currentNode->addChild(newElement.get())) { + stopParsing(); + return; + } + + setCurrentNode(newElement.get()); + if (m_view && !newElement->attached()) + newElement->attach(); +} + +void XMLTokenizer::endElementNs() +{ + if (m_parserStopped) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendEndElementNSCallback(); + return; + } + + exitText(); + + Node* n = m_currentNode; + RefPtr<Node> parent = n->parentNode(); + n->finishParsingChildren(); + + // don't load external scripts for standalone documents (for now) + if (n->isElementNode() && m_view && (static_cast<Element*>(n)->hasTagName(scriptTag) +#if ENABLE(SVG) + || static_cast<Element*>(n)->hasTagName(SVGNames::scriptTag) +#endif + )) { + + + ASSERT(!m_pendingScript); + + m_requestingScript = true; + + Element* scriptElement = static_cast<Element*>(n); + String scriptHref; + + if (static_cast<Element*>(n)->hasTagName(scriptTag)) + scriptHref = scriptElement->getAttribute(srcAttr); +#if ENABLE(SVG) + else if (static_cast<Element*>(n)->hasTagName(SVGNames::scriptTag)) + scriptHref = scriptElement->getAttribute(XLinkNames::hrefAttr); +#endif + + if (!scriptHref.isEmpty()) { + // we have a src attribute + const AtomicString& charset = scriptElement->getAttribute(charsetAttr); + if ((m_pendingScript = m_doc->docLoader()->requestScript(scriptHref, charset))) { + m_scriptElement = scriptElement; + m_pendingScript->ref(this); + + // m_pendingScript will be 0 if script was already loaded and ref() executed it + if (m_pendingScript) + pauseParsing(); + } else + m_scriptElement = 0; + + } else { + String scriptCode = ""; + for (Node* child = scriptElement->firstChild(); child; child = child->nextSibling()) { + if (child->isTextNode() || child->nodeType() == Node::CDATA_SECTION_NODE) + scriptCode += static_cast<CharacterData*>(child)->data(); + } + m_view->frame()->loader()->executeScript(m_doc->url().string(), m_scriptStartLine - 1, scriptCode); + } + + m_requestingScript = false; + } + + setCurrentNode(parent.get()); +} + +void XMLTokenizer::characters(const xmlChar* s, int len) +{ + if (m_parserStopped) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendCharactersCallback(s, len); + return; + } + + if (m_currentNode->isTextNode() || enterText()) + m_bufferedText.append(s, len); +} + +void XMLTokenizer::error(ErrorType type, const char* message, va_list args) +{ + if (m_parserStopped) + return; + +#if PLATFORM(WIN_OS) + char m[1024]; + vsnprintf(m, sizeof(m) - 1, message, args); +#else + char* m; + vasprintf(&m, message, args); +#endif + + if (m_parserPaused) + m_pendingCallbacks->appendErrorCallback(type, m, lineNumber(), columnNumber()); + else + handleError(type, m, lineNumber(), columnNumber()); + +#if !PLATFORM(WIN_OS) + free(m); +#endif +} + +void XMLTokenizer::processingInstruction(const xmlChar* target, const xmlChar* data) +{ + if (m_parserStopped) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendProcessingInstructionCallback(target, data); + return; + } + + exitText(); + + // ### handle exceptions + int exception = 0; + RefPtr<ProcessingInstruction> pi = m_doc->createProcessingInstruction( + toString(target), toString(data), exception); + if (exception) + return; + + if (!m_currentNode->addChild(pi.get())) + return; + if (m_view && !pi->attached()) + pi->attach(); + + // don't load stylesheets for standalone documents + if (m_doc->frame()) { + m_sawXSLTransform = !m_sawFirstElement && !pi->checkStyleSheet(); +#if ENABLE(XSLT) + // Pretend we didn't see this PI if we're the result of a transform. + if (m_sawXSLTransform && !m_doc->transformSourceDocument()) +#else + if (m_sawXSLTransform) +#endif + // Stop the SAX parser. + stopParsing(); + } +} + +void XMLTokenizer::cdataBlock(const xmlChar* s, int len) +{ + if (m_parserStopped) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendCDATABlockCallback(s, len); + return; + } + + exitText(); + + RefPtr<Node> newNode = new CDATASection(m_doc, toString(s, len)); + if (!m_currentNode->addChild(newNode.get())) + return; + if (m_view && !newNode->attached()) + newNode->attach(); +} + +void XMLTokenizer::comment(const xmlChar* s) +{ + if (m_parserStopped) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendCommentCallback(s); + return; + } + + exitText(); + + RefPtr<Node> newNode = new Comment(m_doc, toString(s)); + m_currentNode->addChild(newNode.get()); + if (m_view && !newNode->attached()) + newNode->attach(); +} + +void XMLTokenizer::startDocument(const xmlChar* version, const xmlChar* encoding, int standalone) +{ + ExceptionCode ec = 0; + + if (version) + m_doc->setXMLVersion(toString(version), ec); + m_doc->setXMLStandalone(standalone == 1, ec); // possible values are 0, 1, and -1 + if (encoding) + m_doc->setXMLEncoding(toString(encoding)); +} + +void XMLTokenizer::endDocument() +{ + exitText(); +} + +void XMLTokenizer::internalSubset(const xmlChar* name, const xmlChar* externalID, const xmlChar* systemID) +{ + if (m_parserStopped) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendInternalSubsetCallback(name, externalID, systemID); + return; + } + + if (m_doc) + m_doc->addChild(new DocumentType(m_doc, toString(name), toString(externalID), toString(systemID))); +} + +static inline XMLTokenizer* getTokenizer(void* closure) +{ + xmlParserCtxtPtr ctxt = static_cast<xmlParserCtxtPtr>(closure); + return static_cast<XMLTokenizer*>(ctxt->_private); +} + +// This is a hack around http://bugzilla.gnome.org/show_bug.cgi?id=159219 +// Otherwise libxml seems to call all the SAX callbacks twice for any replaced entity. +static inline bool hackAroundLibXMLEntityBug(void* closure) +{ +#if LIBXML_VERSION >= 20627 + // This bug has been fixed in libxml 2.6.27. + return false; +#else + return static_cast<xmlParserCtxtPtr>(closure)->node; +#endif +} + +static void startElementNsHandler(void* closure, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri, int nb_namespaces, const xmlChar** namespaces, int nb_attributes, int nb_defaulted, const xmlChar** libxmlAttributes) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getTokenizer(closure)->startElementNs(localname, prefix, uri, nb_namespaces, namespaces, nb_attributes, nb_defaulted, libxmlAttributes); +} + +static void endElementNsHandler(void* closure, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getTokenizer(closure)->endElementNs(); +} + +static void charactersHandler(void* closure, const xmlChar* s, int len) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getTokenizer(closure)->characters(s, len); +} + +static void processingInstructionHandler(void* closure, const xmlChar* target, const xmlChar* data) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getTokenizer(closure)->processingInstruction(target, data); +} + +static void cdataBlockHandler(void* closure, const xmlChar* s, int len) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getTokenizer(closure)->cdataBlock(s, len); +} + +static void commentHandler(void* closure, const xmlChar* comment) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getTokenizer(closure)->comment(comment); +} + +WTF_ATTRIBUTE_PRINTF(2, 3) +static void warningHandler(void* closure, const char* message, ...) +{ + va_list args; + va_start(args, message); + getTokenizer(closure)->error(XMLTokenizer::warning, message, args); + va_end(args); +} + +WTF_ATTRIBUTE_PRINTF(2, 3) +static void fatalErrorHandler(void* closure, const char* message, ...) +{ + va_list args; + va_start(args, message); + getTokenizer(closure)->error(XMLTokenizer::fatal, message, args); + va_end(args); +} + +WTF_ATTRIBUTE_PRINTF(2, 3) +static void normalErrorHandler(void* closure, const char* message, ...) +{ + va_list args; + va_start(args, message); + getTokenizer(closure)->error(XMLTokenizer::nonFatal, message, args); + va_end(args); +} + +// Using a global variable entity and marking it XML_INTERNAL_PREDEFINED_ENTITY is +// a hack to avoid malloc/free. Using a global variable like this could cause trouble +// if libxml implementation details were to change +static xmlChar sharedXHTMLEntityResult[5] = {0,0,0,0,0}; +static xmlEntity sharedXHTMLEntity = { + 0, XML_ENTITY_DECL, 0, 0, 0, 0, 0, 0, 0, + sharedXHTMLEntityResult, sharedXHTMLEntityResult, 0, + XML_INTERNAL_PREDEFINED_ENTITY, 0, 0, 0, 0, 0 +}; + +static xmlEntityPtr getXHTMLEntity(const xmlChar* name) +{ + UChar c = decodeNamedEntity(reinterpret_cast<const char*>(name)); + if (!c) + return 0; + + CString value = String(&c, 1).utf8(); + ASSERT(value.length() < 5); + sharedXHTMLEntity.length = value.length(); + sharedXHTMLEntity.name = name; + memcpy(sharedXHTMLEntityResult, value.data(), sharedXHTMLEntity.length + 1); + + return &sharedXHTMLEntity; +} + +static xmlEntityPtr getEntityHandler(void* closure, const xmlChar* name) +{ + xmlParserCtxtPtr ctxt = static_cast<xmlParserCtxtPtr>(closure); + xmlEntityPtr ent = xmlGetPredefinedEntity(name); + if (ent) { + ent->etype = XML_INTERNAL_PREDEFINED_ENTITY; + return ent; + } + + ent = xmlGetDocEntity(ctxt->myDoc, name); + if (!ent && getTokenizer(closure)->isXHTMLDocument()) { + ent = getXHTMLEntity(name); + if (ent) + ent->etype = XML_INTERNAL_GENERAL_ENTITY; + } + + return ent; +} + +static void startDocumentHandler(void* closure) +{ + xmlParserCtxt* ctxt = static_cast<xmlParserCtxt*>(closure); + getTokenizer(closure)->startDocument(ctxt->version, ctxt->encoding, ctxt->standalone); + xmlSAX2StartDocument(closure); +} + +static void endDocumentHandler(void* closure) +{ + getTokenizer(closure)->endDocument(); + xmlSAX2EndDocument(closure); +} + +static void internalSubsetHandler(void* closure, const xmlChar* name, const xmlChar* externalID, const xmlChar* systemID) +{ + getTokenizer(closure)->internalSubset(name, externalID, systemID); + xmlSAX2InternalSubset(closure, name, externalID, systemID); +} + +static void externalSubsetHandler(void* closure, const xmlChar* name, const xmlChar* externalId, const xmlChar* systemId) +{ + String extId = toString(externalId); + if ((extId == "-//W3C//DTD XHTML 1.0 Transitional//EN") + || (extId == "-//W3C//DTD XHTML 1.1//EN") + || (extId == "-//W3C//DTD XHTML 1.0 Strict//EN") + || (extId == "-//W3C//DTD XHTML 1.0 Frameset//EN") + || (extId == "-//W3C//DTD XHTML Basic 1.0//EN") + || (extId == "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN") + || (extId == "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN") + || (extId == "-//WAPFORUM//DTD XHTML Mobile 1.0//EN")) + getTokenizer(closure)->setIsXHTMLDocument(true); // controls if we replace entities or not. +} + +static void ignorableWhitespaceHandler(void* ctx, const xmlChar* ch, int len) +{ + // nothing to do, but we need this to work around a crasher + // http://bugzilla.gnome.org/show_bug.cgi?id=172255 + // http://bugs.webkit.org/show_bug.cgi?id=5792 +} +#endif + +void XMLTokenizer::handleError(ErrorType type, const char* m, int lineNumber, int columnNumber) +{ + if (type == fatal || (m_errorCount < maxErrors && m_lastErrorLine != lineNumber && m_lastErrorColumn != columnNumber)) { + switch (type) { + case warning: + m_errorMessages += String::format("warning on line %d at column %d: %s", lineNumber, columnNumber, m); + break; + case fatal: + case nonFatal: + m_errorMessages += String::format("error on line %d at column %d: %s", lineNumber, columnNumber, m); + } + + m_lastErrorLine = lineNumber; + m_lastErrorColumn = columnNumber; + ++m_errorCount; + } + + if (type != warning) + m_sawError = true; + + if (type == fatal) + stopParsing(); +} + +bool XMLTokenizer::enterText() +{ +#ifndef USE_QXMLSTREAM + ASSERT(m_bufferedText.size() == 0); +#endif + RefPtr<Node> newNode = new Text(m_doc, ""); + if (!m_currentNode->addChild(newNode.get())) + return false; + setCurrentNode(newNode.get()); + return true; +} + +void XMLTokenizer::exitText() +{ + if (m_parserStopped) + return; + + if (!m_currentNode || !m_currentNode->isTextNode()) + return; + +#ifndef USE_QXMLSTREAM + ExceptionCode ec = 0; + static_cast<Text*>(m_currentNode)->appendData(toString(m_bufferedText.data(), m_bufferedText.size()), ec); + Vector<xmlChar> empty; + m_bufferedText.swap(empty); +#endif + + if (m_view && m_currentNode && !m_currentNode->attached()) + m_currentNode->attach(); + + // FIXME: What's the right thing to do if the parent is really 0? + // Just leaving the current node set to the text node doesn't make much sense. + if (Node* par = m_currentNode->parentNode()) + setCurrentNode(par); +} + +void XMLTokenizer::initializeParserContext() +{ +#ifndef USE_QXMLSTREAM + xmlSAXHandler sax; + memset(&sax, 0, sizeof(sax)); + sax.error = normalErrorHandler; + sax.fatalError = fatalErrorHandler; + sax.characters = charactersHandler; + sax.processingInstruction = processingInstructionHandler; + sax.cdataBlock = cdataBlockHandler; + sax.comment = commentHandler; + sax.warning = warningHandler; + sax.startElementNs = startElementNsHandler; + sax.endElementNs = endElementNsHandler; + sax.getEntity = getEntityHandler; + sax.startDocument = startDocumentHandler; + sax.endDocument = endDocumentHandler; + sax.internalSubset = internalSubsetHandler; + sax.externalSubset = externalSubsetHandler; + sax.ignorableWhitespace = ignorableWhitespaceHandler; + sax.entityDecl = xmlSAX2EntityDecl; + sax.initialized = XML_SAX2_MAGIC; +#endif + m_parserStopped = false; + m_sawError = false; + m_sawXSLTransform = false; + m_sawFirstElement = false; + +#ifndef USE_QXMLSTREAM + m_context = createStringParser(&sax, this); +#endif +} + +void XMLTokenizer::end() +{ +#if ENABLE(XSLT) + if (m_sawXSLTransform) { + m_doc->setTransformSource(xmlDocPtrForString(m_doc->docLoader(), m_originalSourceForTransform, m_doc->url().string())); + + m_doc->setParsing(false); // Make the doc think it's done, so it will apply xsl sheets. + m_doc->updateStyleSelector(); + m_doc->setParsing(true); + m_parserStopped = true; + } +#endif + +#ifndef USE_QXMLSTREAM + if (m_context) { + // Tell libxml we're done. + xmlParseChunk(m_context, 0, 0, 1); + + if (m_context->myDoc) + xmlFreeDoc(m_context->myDoc); + xmlFreeParserCtxt(m_context); + m_context = 0; + } +#else + if (m_stream.error() == QXmlStreamReader::PrematureEndOfDocumentError || (m_wroteText && !m_sawFirstElement)) { + handleError(fatal, qPrintable(m_stream.errorString()), lineNumber(), + columnNumber()); + } +#endif + + if (m_sawError) + insertErrorMessageBlock(); + else { + exitText(); + m_doc->updateStyleSelector(); + } + + setCurrentNode(0); + if (!m_parsingFragment) + m_doc->finishedParsing(); +} + +void XMLTokenizer::finish() +{ + if (m_parserPaused) + m_finishCalled = true; + else + end(); +} + +static inline RefPtr<Element> createXHTMLParserErrorHeader(Document* doc, const String& errorMessages) +{ + ExceptionCode ec = 0; + RefPtr<Element> reportElement = doc->createElementNS(xhtmlNamespaceURI, "parsererror", ec); + reportElement->setAttribute(styleAttr, "display: block; white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black"); + + RefPtr<Element> h3 = doc->createElementNS(xhtmlNamespaceURI, "h3", ec); + reportElement->appendChild(h3.get(), ec); + h3->appendChild(doc->createTextNode("This page contains the following errors:"), ec); + + RefPtr<Element> fixed = doc->createElementNS(xhtmlNamespaceURI, "div", ec); + reportElement->appendChild(fixed.get(), ec); + fixed->setAttribute(styleAttr, "font-family:monospace;font-size:12px"); + fixed->appendChild(doc->createTextNode(errorMessages), ec); + + h3 = doc->createElementNS(xhtmlNamespaceURI, "h3", ec); + reportElement->appendChild(h3.get(), ec); + h3->appendChild(doc->createTextNode("Below is a rendering of the page up to the first error."), ec); + + return reportElement; +} + +void XMLTokenizer::insertErrorMessageBlock() +{ +#ifdef USE_QXMLSTREAM + if (m_parsingFragment) + return; +#endif + // One or more errors occurred during parsing of the code. Display an error block to the user above + // the normal content (the DOM tree is created manually and includes line/col info regarding + // where the errors are located) + + // Create elements for display + ExceptionCode ec = 0; + Document* doc = m_doc; + Node* documentElement = doc->documentElement(); + if (!documentElement) { + RefPtr<Node> rootElement = doc->createElementNS(xhtmlNamespaceURI, "html", ec); + doc->appendChild(rootElement, ec); + RefPtr<Node> body = doc->createElementNS(xhtmlNamespaceURI, "body", ec); + rootElement->appendChild(body, ec); + documentElement = body.get(); + } +#if ENABLE(SVG) + else if (documentElement->namespaceURI() == SVGNames::svgNamespaceURI) { + // Until our SVG implementation has text support, it is best if we + // wrap the erroneous SVG document in an xhtml document and render + // the combined document with error messages. + RefPtr<Node> rootElement = doc->createElementNS(xhtmlNamespaceURI, "html", ec); + RefPtr<Node> body = doc->createElementNS(xhtmlNamespaceURI, "body", ec); + rootElement->appendChild(body, ec); + body->appendChild(documentElement, ec); + doc->appendChild(rootElement.get(), ec); + documentElement = body.get(); + } +#endif + + RefPtr<Element> reportElement = createXHTMLParserErrorHeader(doc, m_errorMessages); + documentElement->insertBefore(reportElement, documentElement->firstChild(), ec); +#if ENABLE(XSLT) + if (doc->transformSourceDocument()) { + RefPtr<Element> par = doc->createElementNS(xhtmlNamespaceURI, "p", ec); + reportElement->appendChild(par, ec); + par->setAttribute(styleAttr, "white-space: normal"); + par->appendChild(doc->createTextNode("This document was created as the result of an XSL transformation. The line and column numbers given are from the transformed result."), ec); + } +#endif + doc->updateRendering(); +} + +void XMLTokenizer::notifyFinished(CachedResource* finishedObj) +{ + ASSERT(m_pendingScript == finishedObj); + ASSERT(m_pendingScript->accessCount() > 0); + + String cachedScriptUrl = m_pendingScript->url(); + String scriptSource = m_pendingScript->script(); + bool errorOccurred = m_pendingScript->errorOccurred(); + m_pendingScript->deref(this); + m_pendingScript = 0; + + RefPtr<Element> e = m_scriptElement; + m_scriptElement = 0; + + if (errorOccurred) + EventTargetNodeCast(e.get())->dispatchHTMLEvent(errorEvent, true, false); + else { + m_view->frame()->loader()->executeScript(cachedScriptUrl, 0, scriptSource); + EventTargetNodeCast(e.get())->dispatchHTMLEvent(loadEvent, false, false); + } + + m_scriptElement = 0; + + if (!m_requestingScript) + resumeParsing(); +} + +bool XMLTokenizer::isWaitingForScripts() const +{ + return m_pendingScript != 0; +} + +#if ENABLE(XSLT) +void* xmlDocPtrForString(DocLoader* docLoader, const String& source, const String& url) +{ + if (source.isEmpty()) + return 0; + + // Parse in a single chunk into an xmlDocPtr + // FIXME: Hook up error handlers so that a failure to parse the main document results in + // good error messages. + const UChar BOM = 0xFEFF; + const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM); + + xmlGenericErrorFunc oldErrorFunc = xmlGenericError; + void* oldErrorContext = xmlGenericErrorContext; + + setLoaderForLibXMLCallbacks(docLoader); + xmlSetGenericErrorFunc(0, errorFunc); + + xmlDocPtr sourceDoc = xmlReadMemory(reinterpret_cast<const char*>(source.characters()), + source.length() * sizeof(UChar), + url.latin1().data(), + BOMHighByte == 0xFF ? "UTF-16LE" : "UTF-16BE", + XSLT_PARSE_OPTIONS); + + setLoaderForLibXMLCallbacks(0); + xmlSetGenericErrorFunc(oldErrorContext, oldErrorFunc); + + return sourceDoc; +} +#endif + +int XMLTokenizer::lineNumber() const +{ +#ifndef USE_QXMLSTREAM + return m_context ? m_context->input->line : 1; +#else + return m_stream.lineNumber(); +#endif +} + +int XMLTokenizer::columnNumber() const +{ +#ifndef USE_QXMLSTREAM + return m_context ? m_context->input->col : 1; +#else + return m_stream.columnNumber(); +#endif +} + +void XMLTokenizer::stopParsing() +{ + Tokenizer::stopParsing(); +#ifndef USE_QXMLSTREAM + xmlStopParser(m_context); +#endif +} + +void XMLTokenizer::pauseParsing() +{ + if (m_parsingFragment) + return; + + m_parserPaused = true; +} + +void XMLTokenizer::resumeParsing() +{ + ASSERT(m_parserPaused); + + m_parserPaused = false; + + // First, execute any pending callbacks +#ifndef USE_QXMLSTREAM + while (!m_pendingCallbacks->isEmpty()) { + m_pendingCallbacks->callAndRemoveFirstCallback(this); + + // A callback paused the parser + if (m_parserPaused) + return; + } +#else + parse(); + if (m_parserPaused) + return; +#endif + + // Then, write any pending data + SegmentedString rest = m_pendingSrc; + m_pendingSrc.clear(); + write(rest, false); + + // Finally, if finish() has been called and write() didn't result + // in any further callbacks being queued, call end() + if (m_finishCalled +#ifndef USE_QXMLSTREAM + && m_pendingCallbacks->isEmpty()) +#else + ) +#endif + end(); +} + +#ifndef USE_QXMLSTREAM +static void balancedStartElementNsHandler(void* closure, const xmlChar* localname, const xmlChar* prefix, + const xmlChar* uri, int nb_namespaces, const xmlChar** namespaces, + int nb_attributes, int nb_defaulted, const xmlChar** libxmlAttributes) +{ + static_cast<XMLTokenizer*>(closure)->startElementNs(localname, prefix, uri, nb_namespaces, namespaces, nb_attributes, nb_defaulted, libxmlAttributes); +} + +static void balancedEndElementNsHandler(void* closure, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri) +{ + static_cast<XMLTokenizer*>(closure)->endElementNs(); +} + +static void balancedCharactersHandler(void* closure, const xmlChar* s, int len) +{ + static_cast<XMLTokenizer*>(closure)->characters(s, len); +} + +static void balancedProcessingInstructionHandler(void* closure, const xmlChar* target, const xmlChar* data) +{ + static_cast<XMLTokenizer*>(closure)->processingInstruction(target, data); +} + +static void balancedCdataBlockHandler(void* closure, const xmlChar* s, int len) +{ + static_cast<XMLTokenizer*>(closure)->cdataBlock(s, len); +} + +static void balancedCommentHandler(void* closure, const xmlChar* comment) +{ + static_cast<XMLTokenizer*>(closure)->comment(comment); +} + +WTF_ATTRIBUTE_PRINTF(2, 3) +static void balancedWarningHandler(void* closure, const char* message, ...) +{ + va_list args; + va_start(args, message); + static_cast<XMLTokenizer*>(closure)->error(XMLTokenizer::warning, message, args); + va_end(args); +} +#endif +bool parseXMLDocumentFragment(const String& string, DocumentFragment* fragment, Element* parent) +{ + if (!string.length()) + return true; + + XMLTokenizer tokenizer(fragment, parent); + +#ifndef USE_QXMLSTREAM + xmlSAXHandler sax; + memset(&sax, 0, sizeof(sax)); + + sax.characters = balancedCharactersHandler; + sax.processingInstruction = balancedProcessingInstructionHandler; + sax.startElementNs = balancedStartElementNsHandler; + sax.endElementNs = balancedEndElementNsHandler; + sax.cdataBlock = balancedCdataBlockHandler; + sax.ignorableWhitespace = balancedCharactersHandler; + sax.comment = balancedCommentHandler; + sax.warning = balancedWarningHandler; + sax.initialized = XML_SAX2_MAGIC; + + int result = xmlParseBalancedChunkMemory(0, &sax, &tokenizer, 0, (const xmlChar*)string.utf8().data(), 0); + tokenizer.endDocument(); + return result == 0; +#else + tokenizer.write(String("<qxmlstreamdummyelement>"), false); + tokenizer.write(string, false); + tokenizer.write(String("</qxmlstreamdummyelement>"), false); + tokenizer.finish(); + return !tokenizer.hasError(); +#endif +} + +// -------------------------------- + +struct AttributeParseState { + HashMap<String, String> attributes; + bool gotAttributes; +}; + +#ifndef USE_QXMLSTREAM +static void attributesStartElementNsHandler(void* closure, const xmlChar* xmlLocalName, const xmlChar* xmlPrefix, + const xmlChar* xmlURI, int nb_namespaces, const xmlChar** namespaces, + int nb_attributes, int nb_defaulted, const xmlChar** libxmlAttributes) +{ + if (strcmp(reinterpret_cast<const char*>(xmlLocalName), "attrs") != 0) + return; + + xmlParserCtxtPtr ctxt = static_cast<xmlParserCtxtPtr>(closure); + AttributeParseState* state = static_cast<AttributeParseState*>(ctxt->_private); + + state->gotAttributes = true; + + xmlSAX2Attributes* attributes = reinterpret_cast<xmlSAX2Attributes*>(libxmlAttributes); + for(int i = 0; i < nb_attributes; i++) { + String attrLocalName = toString(attributes[i].localname); + int valueLength = (int) (attributes[i].end - attributes[i].value); + String attrValue = toString(attributes[i].value, valueLength); + String attrPrefix = toString(attributes[i].prefix); + String attrQName = attrPrefix.isEmpty() ? attrLocalName : attrPrefix + ":" + attrLocalName; + + state->attributes.set(attrQName, attrValue); + } +} +#else +static void attributesStartElementNsHandler(AttributeParseState* state, const QXmlStreamAttributes& attrs) +{ + if (attrs.count() <= 0) + return; + + state->gotAttributes = true; + + for(int i = 0; i < attrs.count(); i++) { + const QXmlStreamAttribute& attr = attrs[i]; + String attrLocalName = attr.name(); + String attrValue = attr.value(); + String attrURI = attr.namespaceUri(); + String attrQName = attr.qualifiedName(); + state->attributes.set(attrQName, attrValue); + } +} +#endif + +HashMap<String, String> parseAttributes(const String& string, bool& attrsOK) +{ + AttributeParseState state; + state.gotAttributes = false; + +#ifndef USE_QXMLSTREAM + xmlSAXHandler sax; + memset(&sax, 0, sizeof(sax)); + sax.startElementNs = attributesStartElementNsHandler; + sax.initialized = XML_SAX2_MAGIC; + xmlParserCtxtPtr parser = createStringParser(&sax, &state); + String parseString = "<?xml version=\"1.0\"?><attrs " + string + " />"; + xmlParseChunk(parser, reinterpret_cast<const char*>(parseString.characters()), parseString.length() * sizeof(UChar), 1); + if (parser->myDoc) + xmlFreeDoc(parser->myDoc); + xmlFreeParserCtxt(parser); +#else + QXmlStreamReader stream; + QString dummy = QString("<?xml version=\"1.0\"?><attrs %1 />").arg(string); + stream.addData(dummy); + while (!stream.atEnd()) { + stream.readNext(); + if (stream.isStartElement()) { + attributesStartElementNsHandler(&state, stream.attributes()); + } + } +#endif + attrsOK = state.gotAttributes; + return state.attributes; +} + +#ifdef USE_QXMLSTREAM +static inline String prefixFromQName(const QString& qName) +{ + const int offset = qName.indexOf(QLatin1Char(':')); + if (offset <= 0) + return String(); + else + return qName.left(offset); +} + +static inline void handleElementNamespaces(Element* newElement, const QXmlStreamNamespaceDeclarations &ns, + ExceptionCode& ec) +{ + for (int i = 0; i < ns.count(); ++i) { + const QXmlStreamNamespaceDeclaration &decl = ns[i]; + String namespaceURI = decl.namespaceUri(); + String namespaceQName = decl.prefix().isEmpty() ? String("xmlns") : String("xmlns:") + decl.prefix(); + newElement->setAttributeNS("http://www.w3.org/2000/xmlns/", namespaceQName, namespaceURI, ec); + if (ec) // exception setting attributes + return; + } +} + +static inline void handleElementAttributes(Element* newElement, const QXmlStreamAttributes &attrs, ExceptionCode& ec) +{ + for (int i = 0; i < attrs.count(); ++i) { + const QXmlStreamAttribute &attr = attrs[i]; + String attrLocalName = attr.name(); + String attrValue = attr.value(); + String attrURI = attr.namespaceUri().isEmpty() ? String() : String(attr.namespaceUri()); + String attrQName = attr.qualifiedName(); + newElement->setAttributeNS(attrURI, attrQName, attrValue, ec); + if (ec) // exception setting attributes + return; + } +} + +void XMLTokenizer::parse() +{ + while (!m_parserStopped && !m_parserPaused && !m_stream.atEnd()) { + m_stream.readNext(); + switch (m_stream.tokenType()) { + case QXmlStreamReader::StartDocument: { + startDocument(); + } + break; + case QXmlStreamReader::EndDocument: { + endDocument(); + } + break; + case QXmlStreamReader::StartElement: { + parseStartElement(); + } + break; + case QXmlStreamReader::EndElement: { + parseEndElement(); + } + break; + case QXmlStreamReader::Characters: { + if (m_stream.isCDATA()) { + //cdata + parseCdata(); + } else { + //characters + parseCharacters(); + } + } + break; + case QXmlStreamReader::Comment: { + parseComment(); + } + break; + case QXmlStreamReader::DTD: { + //qDebug()<<"------------- DTD"; + parseDtd(); + } + break; + case QXmlStreamReader::EntityReference: { + //qDebug()<<"---------- ENTITY = "<<m_stream.name().toString() + // <<", t = "<<m_stream.text().toString(); + if (isXHTMLDocument()) { + QString entity = m_stream.name().toString(); + UChar c = decodeNamedEntity(entity.toUtf8().constData()); + if (m_currentNode->isTextNode() || enterText()) { + ExceptionCode ec = 0; + String str(&c, 1); + //qDebug()<<" ------- adding entity "<<str; + static_cast<Text*>(m_currentNode)->appendData(str, ec); + } + } + } + break; + case QXmlStreamReader::ProcessingInstruction: { + parseProcessingInstruction(); + } + break; + default: { + if (m_stream.error() != QXmlStreamReader::PrematureEndOfDocumentError) { + ErrorType type = (m_stream.error() == QXmlStreamReader::NotWellFormedError) ? + fatal : warning; + handleError(type, qPrintable(m_stream.errorString()), lineNumber(), + columnNumber()); + } + } + break; + } + } +} + +void XMLTokenizer::startDocument() +{ + initializeParserContext(); + ExceptionCode ec = 0; + + if (!m_parsingFragment) { + m_doc->setXMLStandalone(m_stream.isStandaloneDocument(), ec); + +#if QT_VERSION >= 0x040400 + QStringRef version = m_stream.documentVersion(); + if (!version.isEmpty()) + m_doc->setXMLVersion(version, ec); + QStringRef encoding = m_stream.documentEncoding(); + if (!encoding.isEmpty()) + m_doc->setXMLEncoding(encoding); +#endif + } +} + +void XMLTokenizer::parseStartElement() +{ + if (!m_sawFirstElement && m_parsingFragment) { + // skip dummy element for fragments + m_sawFirstElement = true; + return; + } + m_sawFirstElement = true; + + exitText(); + + String localName = m_stream.name(); + String uri = m_stream.namespaceUri(); + String prefix = prefixFromQName(m_stream.qualifiedName().toString()); + + if (m_parsingFragment && uri.isNull()) { + Q_ASSERT (prefix.isNull()); + uri = m_defaultNamespaceURI; + } + + ExceptionCode ec = 0; + QualifiedName qName(prefix, localName, uri); + RefPtr<Element> newElement = m_doc->createElement(qName, true, ec); + if (!newElement) { + stopParsing(); + return; + } + + handleElementNamespaces(newElement.get(), m_stream.namespaceDeclarations(), ec); + if (ec) { + stopParsing(); + return; + } + + handleElementAttributes(newElement.get(), m_stream.attributes(), ec); + if (ec) { + stopParsing(); + return; + } + + if (newElement->hasTagName(scriptTag)) + static_cast<HTMLScriptElement*>(newElement.get())->setCreatedByParser(true); + + if (newElement->hasTagName(HTMLNames::scriptTag) +#if ENABLE(SVG) + || newElement->hasTagName(SVGNames::scriptTag) +#endif + ) + m_scriptStartLine = lineNumber(); + + if (!m_currentNode->addChild(newElement.get())) { + stopParsing(); + return; + } + + setCurrentNode(newElement.get()); + if (m_view && !newElement->attached()) + newElement->attach(); +} + +void XMLTokenizer::parseEndElement() +{ + exitText(); + + Node* n = m_currentNode; + + // skip end of dummy element +// if (m_parsingFragment & n->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) +// return; + + RefPtr<Node> parent = n->parentNode(); + n->finishParsingChildren(); + + // don't load external scripts for standalone documents (for now) + if (n->isElementNode() && m_view && (static_cast<Element*>(n)->hasTagName(scriptTag) +#if ENABLE(SVG) + || static_cast<Element*>(n)->hasTagName(SVGNames::scriptTag) +#endif + )) { + + + ASSERT(!m_pendingScript); + + m_requestingScript = true; + + Element* scriptElement = static_cast<Element*>(n); + String scriptHref; + + if (static_cast<Element*>(n)->hasTagName(scriptTag)) + scriptHref = scriptElement->getAttribute(srcAttr); +#if ENABLE(SVG) + else if (static_cast<Element*>(n)->hasTagName(SVGNames::scriptTag)) + scriptHref = scriptElement->getAttribute(XLinkNames::hrefAttr); +#endif + if (!scriptHref.isEmpty()) { + // we have a src attribute + const AtomicString& charset = scriptElement->getAttribute(charsetAttr); + if ((m_pendingScript = m_doc->docLoader()->requestScript(scriptHref, charset))) { + m_scriptElement = scriptElement; + m_pendingScript->ref(this); + + // m_pendingScript will be 0 if script was already loaded and ref() executed it + if (m_pendingScript) + pauseParsing(); + } else + m_scriptElement = 0; + + } else { + String scriptCode = ""; + for (Node* child = scriptElement->firstChild(); child; child = child->nextSibling()) { + if (child->isTextNode() || child->nodeType() == Node::CDATA_SECTION_NODE) + scriptCode += static_cast<CharacterData*>(child)->data(); + } + m_view->frame()->loader()->executeScript(m_doc->url().string(), m_scriptStartLine - 1, scriptCode); + } + m_requestingScript = false; + } + + setCurrentNode(parent.get()); +} + +void XMLTokenizer::parseCharacters() +{ + if (m_currentNode->isTextNode() || enterText()) { + ExceptionCode ec = 0; + static_cast<Text*>(m_currentNode)->appendData(m_stream.text(), ec); + } +} + +void XMLTokenizer::parseProcessingInstruction() +{ + exitText(); + + // ### handle exceptions + int exception = 0; + RefPtr<ProcessingInstruction> pi = m_doc->createProcessingInstruction( + m_stream.processingInstructionTarget(), + m_stream.processingInstructionData(), exception); + if (exception) + return; + + if (!m_currentNode->addChild(pi.get())) + return; + if (m_view && !pi->attached()) + pi->attach(); + + // don't load stylesheets for standalone documents + if (m_doc->frame()) { + m_sawXSLTransform = !m_sawFirstElement && !pi->checkStyleSheet(); + if (m_sawXSLTransform) + stopParsing(); + } +} + +void XMLTokenizer::parseCdata() +{ + exitText(); + + RefPtr<Node> newNode = new CDATASection(m_doc, m_stream.text()); + if (!m_currentNode->addChild(newNode.get())) + return; + if (m_view && !newNode->attached()) + newNode->attach(); +} + +void XMLTokenizer::parseComment() +{ + exitText(); + + RefPtr<Node> newNode = new Comment(m_doc, m_stream.text()); + m_currentNode->addChild(newNode.get()); + if (m_view && !newNode->attached()) + newNode->attach(); +} + +void XMLTokenizer::endDocument() +{ +} + +bool XMLTokenizer::hasError() const +{ + return m_stream.hasError(); +} + +#if QT_VERSION < 0x040400 +static QString parseId(const QString &dtd, int *pos, bool *ok) +{ + *ok = true; + int start = *pos + 1; + int end = start; + if (dtd.at(*pos) == QLatin1Char('\'')) + while (start < dtd.length() && dtd.at(end) != QLatin1Char('\'')) + ++end; + else if (dtd.at(*pos) == QLatin1Char('\"')) + while (start < dtd.length() && dtd.at(end) != QLatin1Char('\"')) + ++end; + else { + *ok = false; + return QString(); + } + *pos = end + 1; + return dtd.mid(start, end - start); +} +#endif + +void XMLTokenizer::parseDtd() +{ +#if QT_VERSION >= 0x040400 + QStringRef name = m_stream.dtdName(); + QStringRef publicId = m_stream.dtdPublicId(); + QStringRef systemId = m_stream.dtdSystemId(); +#else + QString dtd = m_stream.text().toString(); + + int start = dtd.indexOf("<!DOCTYPE ") + 10; + while (start < dtd.length() && dtd.at(start).isSpace()) + ++start; + int end = start; + while (start < dtd.length() && !dtd.at(end).isSpace()) + ++end; + QString name = dtd.mid(start, end - start); + + start = end; + while (start < dtd.length() && dtd.at(start).isSpace()) + ++start; + end = start; + while (start < dtd.length() && !dtd.at(end).isSpace()) + ++end; + QString id = dtd.mid(start, end - start); + start = end; + while (start < dtd.length() && dtd.at(start).isSpace()) + ++start; + QString publicId; + QString systemId; + if (id == QLatin1String("PUBLIC")) { + bool ok; + publicId = parseId(dtd, &start, &ok); + if (!ok) { + handleError(fatal, "Invalid DOCTYPE", lineNumber(), columnNumber()); + return; + } + while (start < dtd.length() && dtd.at(start).isSpace()) + ++start; + systemId = parseId(dtd, &start, &ok); + if (!ok) { + handleError(fatal, "Invalid DOCTYPE", lineNumber(), columnNumber()); + return; + } + } else if (id == QLatin1String("SYSTEM")) { + bool ok; + systemId = parseId(dtd, &start, &ok); + if (!ok) { + handleError(fatal, "Invalid DOCTYPE", lineNumber(), columnNumber()); + return; + } + } else if (id == QLatin1String("[") || id == QLatin1String(">")) { + } else { + handleError(fatal, "Invalid DOCTYPE", lineNumber(), columnNumber()); + return; + } +#endif + + //qDebug() << dtd << name << publicId << systemId; + if ((publicId == "-//W3C//DTD XHTML 1.0 Transitional//EN") + || (publicId == "-//W3C//DTD XHTML 1.1//EN") + || (publicId == "-//W3C//DTD XHTML 1.0 Strict//EN") + || (publicId == "-//W3C//DTD XHTML 1.0 Frameset//EN") + || (publicId == "-//W3C//DTD XHTML Basic 1.0//EN") + || (publicId == "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN") + || (publicId == "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN") + || (publicId == "-//WAPFORUM//DTD XHTML Mobile 1.0//EN")) { + setIsXHTMLDocument(true); // controls if we replace entities or not. + } + if (!m_parsingFragment) + m_doc->addChild(new DocumentType(m_doc, name, publicId, systemId)); + +} +#endif +} + + diff --git a/WebCore/dom/XMLTokenizer.h b/WebCore/dom/XMLTokenizer.h new file mode 100644 index 0000000..e754e84 --- /dev/null +++ b/WebCore/dom/XMLTokenizer.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) + * Copyright (C) 2007 Trolltech ASA + * + * 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. + * + */ + +#ifndef XMLTokenizer_h +#define XMLTokenizer_h + +#include "CachedResourceClient.h" +#include "SegmentedString.h" +#include "StringHash.h" +#include "Tokenizer.h" +#include <wtf/HashMap.h> +#include <wtf/OwnPtr.h> + +#if PLATFORM(QT) +#if !ENABLE(XSLT) +#define USE_QXMLSTREAM +#endif +#endif + +#ifndef USE_QXMLSTREAM +#include <libxml/tree.h> +#include <libxml/xmlstring.h> +#else +#include <QtXml/qxmlstream.h> +#endif + +namespace WebCore { + + class Node; + class CachedScript; + class DocLoader; + class DocumentFragment; + class Document; + class Element; + class FrameView; + class PendingCallbacks; + + class XMLTokenizer : public Tokenizer, public CachedResourceClient { + public: + XMLTokenizer(Document*, FrameView* = 0); + XMLTokenizer(DocumentFragment*, Element*); + ~XMLTokenizer(); + + enum ErrorType { warning, nonFatal, fatal }; + + // from Tokenizer + virtual bool write(const SegmentedString&, bool appendData); + virtual void finish(); + virtual bool isWaitingForScripts() const; + virtual void stopParsing(); + + void end(); + + void pauseParsing(); + void resumeParsing(); + + void setIsXHTMLDocument(bool isXHTML) { m_isXHTMLDocument = isXHTML; } + bool isXHTMLDocument() const { return m_isXHTMLDocument; } + + // from CachedResourceClient + virtual void notifyFinished(CachedResource* finishedObj); + +#ifndef USE_QXMLSTREAM + // callbacks from parser SAX + void error(ErrorType, const char* message, va_list args) WTF_ATTRIBUTE_PRINTF(3, 0); + void startElementNs(const xmlChar* xmlLocalName, const xmlChar* xmlPrefix, const xmlChar* xmlURI, int nb_namespaces, + const xmlChar** namespaces, int nb_attributes, int nb_defaulted, const xmlChar** libxmlAttributes); + void endElementNs(); + void characters(const xmlChar* s, int len); + void processingInstruction(const xmlChar* target, const xmlChar* data); + void cdataBlock(const xmlChar* s, int len); + void comment(const xmlChar* s); + void startDocument(const xmlChar* version, const xmlChar* encoding, int standalone); + void internalSubset(const xmlChar* name, const xmlChar* externalID, const xmlChar* systemID); + void endDocument(); +#else + void parse(); + void startDocument(); + void parseStartElement(); + void parseEndElement(); + void parseCharacters(); + void parseProcessingInstruction(); + void parseCdata(); + void parseComment(); + void endDocument(); + void parseDtd(); + bool hasError() const; +#endif + + void handleError(ErrorType type, const char* m, int lineNumber, int columnNumber); + + virtual bool wellFormed() const { return !m_sawError; } + + int lineNumber() const; + int columnNumber() const; + + private: + void initializeParserContext(); + void setCurrentNode(Node*); + + void insertErrorMessageBlock(); + + bool enterText(); + void exitText(); + + Document* m_doc; + FrameView* m_view; + + String m_originalSourceForTransform; + +#ifdef USE_QXMLSTREAM + QXmlStreamReader m_stream; + bool m_wroteText; +#else + xmlParserCtxtPtr m_context; +#endif + Node* m_currentNode; + bool m_currentNodeIsReferenced; + + bool m_sawError; + bool m_sawXSLTransform; + bool m_sawFirstElement; + bool m_isXHTMLDocument; + + bool m_parserPaused; + bool m_requestingScript; + bool m_finishCalled; + + int m_errorCount; + int m_lastErrorLine; + int m_lastErrorColumn; + String m_errorMessages; + + CachedScript* m_pendingScript; + RefPtr<Element> m_scriptElement; + int m_scriptStartLine; + + bool m_parsingFragment; + String m_defaultNamespaceURI; + + typedef HashMap<String, String> PrefixForNamespaceMap; + PrefixForNamespaceMap m_prefixToNamespaceMap; +#ifndef USE_QXMLSTREAM + OwnPtr<PendingCallbacks> m_pendingCallbacks; + Vector<xmlChar> m_bufferedText; +#endif + SegmentedString m_pendingSrc; + }; + +#if ENABLE(XSLT) +void* xmlDocPtrForString(DocLoader*, const String& source, const String& url); +void setLoaderForLibXMLCallbacks(DocLoader*); +#endif + +HashMap<String, String> parseAttributes(const String&, bool& attrsOK); +bool parseXMLDocumentFragment(const String&, DocumentFragment*, Element* parent = 0); + +} // namespace WebCore + +#endif // XMLTokenizer_h diff --git a/WebCore/dom/make_names.pl b/WebCore/dom/make_names.pl new file mode 100755 index 0000000..04d3683 --- /dev/null +++ b/WebCore/dom/make_names.pl @@ -0,0 +1,497 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of +# its contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use strict; +use Getopt::Long; +use File::Path; +use Config; + +my $printFactory = 0; +my $cppNamespace = ""; +my $namespace = ""; +my $namespacePrefix = ""; +my $namespaceURI = ""; +my $tagsFile = ""; +my $attrsFile = ""; +my $outputDir = "."; +my @tags = (); +my @attrs = (); +my $tagsNullNamespace = 0; +my $attrsNullNamespace = 0; +my $extraDefines = 0; +my $preprocessor = "/usr/bin/gcc -E -P -x c++"; + +GetOptions('tags=s' => \$tagsFile, + 'attrs=s' => \$attrsFile, + 'outputDir=s' => \$outputDir, + 'namespace=s' => \$namespace, + 'namespacePrefix=s' => \$namespacePrefix, + 'namespaceURI=s' => \$namespaceURI, + 'cppNamespace=s' => \$cppNamespace, + 'factory' => \$printFactory, + 'tagsNullNamespace' => \$tagsNullNamespace, + 'attrsNullNamespace' => \$attrsNullNamespace, + 'extraDefines=s' => \$extraDefines, + 'preprocessor=s' => \$preprocessor); + +die "You must specify a namespace (e.g. SVG) for <namespace>Names.h" unless $namespace; +die "You must specify a namespaceURI (e.g. http://www.w3.org/2000/svg)" unless $namespaceURI; +die "You must specify a cppNamespace (e.g. DOM) used for <cppNamespace>::<namespace>Names::fooTag" unless $cppNamespace; +die "You must specify at least one of --tags <file> or --attrs <file>" unless (length($tagsFile) || length($attrsFile)); + +$namespacePrefix = $namespace unless $namespacePrefix; + +@tags = readNames($tagsFile) if length($tagsFile); +@attrs = readNames($attrsFile) if length($attrsFile); + +mkpath($outputDir); +my $namesBasePath = "$outputDir/${namespace}Names"; +my $factoryBasePath = "$outputDir/${namespace}ElementFactory"; + +printNamesHeaderFile("$namesBasePath.h"); +printNamesCppFile("$namesBasePath.cpp"); +if ($printFactory) { + printFactoryCppFile("$factoryBasePath.cpp"); + printFactoryHeaderFile("$factoryBasePath.h"); +} + + +## Support routines + +sub readNames +{ + my $namesFile = shift; + + if ($extraDefines eq 0) { + die "Failed to open file: $namesFile" unless open NAMES, $preprocessor . " " . $namesFile . "|" or die; + } else { + die "Failed to open file: $namesFile" unless open NAMES, $preprocessor . " -D" . join(" -D", split(" ", $extraDefines)) . " " . $namesFile . "|" or die; + } + + my @names = (); + while (<NAMES>) { + next if (m/#/); + next if (m/^[ \t]*$/); + s/-/_/g; + chomp $_; + push @names, $_; + } + close(NAMES); + + die "Failed to read names from file: $namesFile" unless (scalar(@names)); + + return @names +} + +sub printMacros +{ + my ($F, $macro, $suffix, @names) = @_; + for my $name (@names) { + print F " $macro $name","$suffix;\n"; + } +} + +sub printConstructors +{ + my ($F, @names) = @_; + print F "#if ENABLE(SVG)\n"; + for my $name (@names) { + my $upperCase = upperCaseName($name); + + print F "${namespace}Element *${name}Constructor(Document *doc, bool createdByParser)\n"; + print F "{\n"; + print F " return new ${namespace}${upperCase}Element(${name}Tag, doc);\n"; + print F "}\n\n"; + } + print F "#endif\n"; +} + +sub printFunctionInits +{ + my ($F, @names) = @_; + for my $name (@names) { + print F " gFunctionMap->set(${name}Tag.localName().impl(), ${name}Constructor);\n"; + } +} + +sub svgCapitalizationHacks +{ + my $name = shift; + + if ($name =~ /^fe(.+)$/) { + $name = "FE" . ucfirst $1; + } + $name =~ s/kern/Kern/; + $name =~ s/mpath/MPath/; + $name =~ s/svg/SVG/; + $name =~ s/tref/TRef/; + $name =~ s/tspan/TSpan/; + + return $name; +} + +sub upperCaseName +{ + my $name = shift; + + $name = svgCapitalizationHacks($name) if ($namespace eq "SVG"); + + while ($name =~ /^(.*?)_(.*)/) { + $name = $1 . ucfirst $2; + } + + return ucfirst $name; +} + +sub printLicenseHeader +{ + my $F = shift; + print F "/* + * THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT. + * + * + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +"; +} + +sub printNamesHeaderFile +{ + my ($headerPath) = shift; + my $F; + open F, ">$headerPath"; + + printLicenseHeader($F); + print F "#ifndef DOM_${namespace}NAMES_H\n"; + print F "#define DOM_${namespace}NAMES_H\n\n"; + print F "#include \"QualifiedName.h\"\n\n"; + + print F "namespace $cppNamespace { namespace ${namespace}Names {\n\n"; + + my $lowerNamespace = lc($namespacePrefix); + print F "#ifndef DOM_${namespace}NAMES_HIDE_GLOBALS\n"; + print F "// Namespace\n"; + print F "extern const WebCore::AtomicString ${lowerNamespace}NamespaceURI;\n\n"; + + if (scalar(@tags)) { + print F "// Tags\n"; + printMacros($F, "extern const WebCore::QualifiedName", "Tag", @tags); + print F "\n\nWebCore::QualifiedName** get${namespace}Tags(size_t* size);\n"; + } + + if (scalar(@attrs)) { + print F "// Attributes\n"; + printMacros($F, "extern const WebCore::QualifiedName", "Attr", @attrs); + print F "\n\nWebCore::QualifiedName** get${namespace}Attr(size_t* size);\n"; + } + print F "#endif\n\n"; + print F "void init();\n\n"; + print F "} }\n\n"; + print F "#endif\n\n"; + + close F; +} + +sub printNamesCppFile +{ + my $cppPath = shift; + my $F; + open F, ">$cppPath"; + + printLicenseHeader($F); + + my $lowerNamespace = lc($namespacePrefix); + +print F "#include \"config.h\"\n"; + +print F "#ifdef AVOID_STATIC_CONSTRUCTORS\n"; +print F "#define DOM_${namespace}NAMES_HIDE_GLOBALS 1\n"; +print F "#else\n"; +print F "#define QNAME_DEFAULT_CONSTRUCTOR 1\n"; +print F "#endif\n\n"; + + +print F "#include \"${namespace}Names.h\"\n\n"; +print F "#include \"StaticConstructors.h\"\n"; + +print F "namespace $cppNamespace { namespace ${namespace}Names { + +using namespace WebCore; + +DEFINE_GLOBAL(AtomicString, ${lowerNamespace}NamespaceURI, \"$namespaceURI\") +"; + + if (scalar(@tags)) { + print F "// Tags\n"; + for my $name (@tags) { + print F "DEFINE_GLOBAL(QualifiedName, ", $name, "Tag, nullAtom, \"$name\", ${lowerNamespace}NamespaceURI);\n"; + } + + print F "\n\nWebCore::QualifiedName** get${namespace}Tags(size_t* size)\n"; + print F "{\n static WebCore::QualifiedName* ${namespace}Tags[] = {\n"; + for my $name (@tags) { + print F " (WebCore::QualifiedName*)&${name}Tag,\n"; + } + print F " };\n"; + print F " *size = ", scalar(@tags), ";\n"; + print F " return ${namespace}Tags;\n"; + print F "}\n"; + + } + + if (scalar(@attrs)) { + print F "\n// Attributes\n"; + for my $name (@attrs) { + print F "DEFINE_GLOBAL(QualifiedName, ", $name, "Attr, nullAtom, \"$name\", ${lowerNamespace}NamespaceURI);\n"; + } + print F "\n\nWebCore::QualifiedName** get${namespace}Attrs(size_t* size)\n"; + print F "{\n static WebCore::QualifiedName* ${namespace}Attr[] = {\n"; + for my $name (@attrs) { + print F " (WebCore::QualifiedName*)&${name}Attr,\n"; + } + print F " };\n"; + print F " *size = ", scalar(@attrs), ";\n"; + print F " return ${namespace}Attr;\n"; + print F "}\n"; + } + +print F "\nvoid init() +{ + static bool initialized = false; + if (initialized) + return; + initialized = true; + + // Use placement new to initialize the globals. + + AtomicString::init(); +"; + + print(F " AtomicString ${lowerNamespace}NS(\"$namespaceURI\");\n\n"); + + print(F " // Namespace\n"); + print(F " new ((void*)&${lowerNamespace}NamespaceURI) AtomicString(${lowerNamespace}NS);\n\n"); + if (scalar(@tags)) { + my $tagsNamespace = $tagsNullNamespace ? "nullAtom" : "${lowerNamespace}NS"; + printDefinitions($F, \@tags, "tags", $tagsNamespace); + } + if (scalar(@attrs)) { + my $attrsNamespace = $attrsNullNamespace ? "nullAtom" : "${lowerNamespace}NS"; + printDefinitions($F, \@attrs, "attributes", $attrsNamespace); + } + + print F "}\n\n} }\n\n"; + close F; +} + +sub printElementIncludes +{ + my ($F, @names) = @_; + for my $name (@names) { + my $upperCase = upperCaseName($name); + print F "#include \"${namespace}${upperCase}Element.h\"\n"; + } +} + +sub printDefinitions +{ + my ($F, $namesRef, $type, $namespaceURI) = @_; + my $singularType = substr($type, 0, -1); + my $shortType = substr($singularType, 0, 4); + my $shortCamelType = ucfirst($shortType); + my $shortUpperType = uc($shortType); + + print F " // " . ucfirst($type) . "\n"; + + for my $name (@$namesRef) { + print F " const char *$name","${shortCamelType}String = \"$name\";\n"; + } + + for my $name (@$namesRef) { + if ($name =~ /_/) { + my $realName = $name; + $realName =~ s/_/-/g; + print F " ${name}${shortCamelType}String = \"$realName\";\n"; + } + } + print F "\n"; + + for my $name (@$namesRef) { + print F " new ((void*)&$name","${shortCamelType}) QualifiedName(nullAtom, $name","${shortCamelType}String, $namespaceURI);\n"; + } + +} + +sub printFactoryCppFile +{ + my $cppPath = shift; + my $F; + open F, ">$cppPath"; + +printLicenseHeader($F); + +print F <<END +#include "config.h" +#include "${namespace}ElementFactory.h" +#include "${namespace}Names.h" +#include "Page.h" +#include "Settings.h" +END +; + +printElementIncludes($F, @tags); + +print F <<END +#include <wtf/HashMap.h> + +using namespace WebCore; +using namespace ${cppNamespace}::${namespace}Names; + +typedef ${namespace}Element *(*ConstructorFunc)(Document *doc, bool createdByParser); +typedef WTF::HashMap<AtomicStringImpl*, ConstructorFunc> FunctionMap; + +static FunctionMap *gFunctionMap = 0; + +namespace ${cppNamespace} { + +END +; + +printConstructors($F, @tags); + +print F <<END +#if ENABLE(SVG) +static inline void createFunctionMapIfNecessary() +{ + if (gFunctionMap) + return; + // Create the table. + gFunctionMap = new FunctionMap; + + // Populate it with constructor functions. +END +; + +printFunctionInits($F, @tags); + +print F <<END +} +#endif + +${namespace}Element *${namespace}ElementFactory::create${namespace}Element(const QualifiedName& qName, Document* doc, bool createdByParser) +{ +#if ENABLE(SVG) + // Don't make elements without a document + if (!doc) + return 0; + + Settings* settings = doc->settings(); + if (settings && settings->usesDashboardBackwardCompatibilityMode()) + return 0; + + createFunctionMapIfNecessary(); + ConstructorFunc func = gFunctionMap->get(qName.localName().impl()); + if (func) + return func(doc, createdByParser); + + return new ${namespace}Element(qName, doc); +#else + return 0; +#endif +} + +} // namespace + +END +; + + close F; +} + +sub printFactoryHeaderFile +{ + my $headerPath = shift; + my $F; + open F, ">$headerPath"; + + printLicenseHeader($F); + +print F "#ifndef ${namespace}ELEMENTFACTORY_H\n"; +print F "#define ${namespace}ELEMENTFACTORY_H\n\n"; + +print F " +namespace WebCore { + class Element; + class Document; + class QualifiedName; + class AtomicString; +} + +namespace ${cppNamespace} +{ + class ${namespace}Element; + + // The idea behind this class is that there will eventually be a mapping from namespace URIs to ElementFactories that can dispense + // elements. In a compound document world, the generic createElement function (will end up being virtual) will be called. + class ${namespace}ElementFactory + { + public: + WebCore::Element *createElement(const WebCore::QualifiedName& qName, WebCore::Document *doc, bool createdByParser = true); + static ${namespace}Element *create${namespace}Element(const WebCore::QualifiedName& qName, WebCore::Document *doc, bool createdByParser = true); + }; +} + +#endif + +"; + + close F; +} |