diff options
Diffstat (limited to 'Source/WebCore/dom')
310 files changed, 49432 insertions, 0 deletions
diff --git a/Source/WebCore/dom/ActiveDOMObject.cpp b/Source/WebCore/dom/ActiveDOMObject.cpp new file mode 100644 index 0000000..0b70b24 --- /dev/null +++ b/Source/WebCore/dom/ActiveDOMObject.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 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 "ActiveDOMObject.h" + +#include "ScriptExecutionContext.h" +#include "WorkerContext.h" +#include "WorkerThread.h" + +namespace WebCore { + +ActiveDOMObject::ActiveDOMObject(ScriptExecutionContext* scriptExecutionContext, void* upcastPointer) + : m_scriptExecutionContext(scriptExecutionContext) + , m_pendingActivityCount(0) +{ + if (m_scriptExecutionContext) { + ASSERT(m_scriptExecutionContext->isContextThread()); + m_scriptExecutionContext->createdActiveDOMObject(this, upcastPointer); + } +} + +ActiveDOMObject::~ActiveDOMObject() +{ + if (m_scriptExecutionContext) { + ASSERT(m_scriptExecutionContext->isContextThread()); + m_scriptExecutionContext->destroyedActiveDOMObject(this); + } +} + +bool ActiveDOMObject::hasPendingActivity() const +{ + return m_pendingActivityCount; +} + +void ActiveDOMObject::contextDestroyed() +{ + m_scriptExecutionContext = 0; +} + +bool ActiveDOMObject::canSuspend() const +{ + return false; +} + +void ActiveDOMObject::suspend(ReasonForSuspension) +{ +} + +void ActiveDOMObject::resume() +{ +} + +void ActiveDOMObject::stop() +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ActiveDOMObject.h b/Source/WebCore/dom/ActiveDOMObject.h new file mode 100644 index 0000000..a6ec7d3 --- /dev/null +++ b/Source/WebCore/dom/ActiveDOMObject.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 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 ActiveDOMObject_h +#define ActiveDOMObject_h + +#include <wtf/Assertions.h> + +namespace WebCore { + + class ScriptExecutionContext; + + class ActiveDOMObject { + public: + ActiveDOMObject(ScriptExecutionContext*, void* upcastPointer); + + ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; } + virtual bool hasPendingActivity() const; + + virtual void contextDestroyed(); + + // canSuspend() is used by the caller if there is a choice between suspending and stopping. + // For example, a page won't be suspended and placed in the back/forward cache if it has + // the objects that can not be suspended. + // However, 'suspend' can be called even if canSuspend() would return 'false'. That + // happens in step-by-step JS debugging for example - in this case it would be incorrect + // to stop the object. Exact semantics of suspend is up to the object then. + enum ReasonForSuspension { + JavaScriptDebuggerPaused, + WillShowDialog, + DocumentWillBecomeInactive + }; + virtual bool canSuspend() const; + virtual void suspend(ReasonForSuspension); + virtual void resume(); + virtual void stop(); + + template<class T> void setPendingActivity(T* thisObject) + { + ASSERT(thisObject == this); + thisObject->ref(); + m_pendingActivityCount++; + } + + template<class T> void unsetPendingActivity(T* thisObject) + { + ASSERT(m_pendingActivityCount > 0); + --m_pendingActivityCount; + thisObject->deref(); + } + + protected: + virtual ~ActiveDOMObject(); + + private: + ScriptExecutionContext* m_scriptExecutionContext; + unsigned m_pendingActivityCount; + }; + +} // namespace WebCore + +#endif // ActiveDOMObject_h diff --git a/Source/WebCore/dom/AsyncScriptRunner.cpp b/Source/WebCore/dom/AsyncScriptRunner.cpp new file mode 100644 index 0000000..28b1b31 --- /dev/null +++ b/Source/WebCore/dom/AsyncScriptRunner.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 Google, 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 "AsyncScriptRunner.h" + +#include "CachedScript.h" +#include "Document.h" +#include "Element.h" +#include "PendingScript.h" +#include "ScriptElement.h" + +namespace WebCore { + +AsyncScriptRunner::AsyncScriptRunner(Document* document) + : m_document(document) + , m_timer(this, &AsyncScriptRunner::timerFired) +{ + ASSERT(document); +} + +AsyncScriptRunner::~AsyncScriptRunner() +{ + for (size_t i = 0; i < m_scriptsToExecuteSoon.size(); ++i) + m_document->decrementLoadEventDelayCount(); +} + +void AsyncScriptRunner::executeScriptSoon(ScriptElement* scriptElement, CachedResourceHandle<CachedScript> cachedScript) +{ + ASSERT_ARG(scriptElement, scriptElement); + + Element* element = scriptElement->element(); + ASSERT(element); + ASSERT(element->inDocument()); + + m_document->incrementLoadEventDelayCount(); + m_scriptsToExecuteSoon.append(PendingScript(element, cachedScript.get())); + if (!m_timer.isActive()) + m_timer.startOneShot(0); +} + +void AsyncScriptRunner::suspend() +{ + m_timer.stop(); +} + +void AsyncScriptRunner::resume() +{ + if (hasPendingScripts()) + m_timer.startOneShot(0); +} + +void AsyncScriptRunner::timerFired(Timer<AsyncScriptRunner>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_timer); + + RefPtr<Document> protect(m_document); + + Vector<PendingScript> scripts; + scripts.swap(m_scriptsToExecuteSoon); + size_t size = scripts.size(); + for (size_t i = 0; i < size; ++i) { + CachedScript* cachedScript = scripts[i].cachedScript(); + RefPtr<Element> element = scripts[i].releaseElementAndClear(); + toScriptElement(element.get())->execute(cachedScript); + m_document->decrementLoadEventDelayCount(); + } +} + +} diff --git a/Source/WebCore/dom/AsyncScriptRunner.h b/Source/WebCore/dom/AsyncScriptRunner.h new file mode 100644 index 0000000..2326f67 --- /dev/null +++ b/Source/WebCore/dom/AsyncScriptRunner.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google, 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 AsyncScriptRunner_h +#define AsyncScriptRunner_h + +#include "CachedResourceHandle.h" +#include "Timer.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class CachedScript; +class Document; +class PendingScript; +class ScriptElement; + +class AsyncScriptRunner : public Noncopyable { +public: + static PassOwnPtr<AsyncScriptRunner> create(Document* document) { return new AsyncScriptRunner(document); } + ~AsyncScriptRunner(); + + void executeScriptSoon(ScriptElement*, CachedResourceHandle<CachedScript>); + bool hasPendingScripts() const { return !m_scriptsToExecuteSoon.isEmpty(); } + void suspend(); + void resume(); + +private: + AsyncScriptRunner(Document*); + + void timerFired(Timer<AsyncScriptRunner>*); + + Document* m_document; + Vector<PendingScript> m_scriptsToExecuteSoon; // http://www.whatwg.org/specs/web-apps/current-work/#set-of-scripts-that-will-execute-as-soon-as-possible + Timer<AsyncScriptRunner> m_timer; +}; + +} + +#endif diff --git a/Source/WebCore/dom/Attr.cpp b/Source/WebCore/dom/Attr.cpp new file mode 100644 index 0000000..f497394 --- /dev/null +++ b/Source/WebCore/dom/Attr.cpp @@ -0,0 +1,192 @@ +/* + * 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, 2009, 2010 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 "Element.h" +#include "ExceptionCode.h" +#include "HTMLNames.h" +#include "Text.h" +#include "XMLNSNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline Attr::Attr(Element* element, Document* document, PassRefPtr<Attribute> attribute) + : ContainerNode(document) + , m_element(element) + , m_attribute(attribute) + , m_ignoreChildrenChanged(0) + , m_specified(true) +{ + ASSERT(!m_attribute->attr()); + m_attribute->bindAttr(this); +} + +PassRefPtr<Attr> Attr::create(Element* element, Document* document, PassRefPtr<Attribute> attribute) +{ + RefPtr<Attr> attr = adoptRef(new Attr(element, document, attribute)); + attr->createTextChild(); + return attr.release(); +} + +Attr::~Attr() +{ + ASSERT(m_attribute->attr() == this); + m_attribute->unbindAttr(this); +} + +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; + + if ((prefix == xmlnsAtom && namespaceURI() != XMLNSNames::xmlnsNamespaceURI) + || static_cast<Attr*>(this)->qualifiedName() == xmlnsAtom) { + ec = NAMESPACE_ERR; + return; + } + + m_attribute->setPrefix(prefix.isEmpty() ? AtomicString() : prefix); +} + +String Attr::nodeValue() const +{ + return value(); +} + +void Attr::setValue(const AtomicString& value) +{ + m_ignoreChildrenChanged++; + removeChildren(); + m_attribute->setValue(value); + createTextChild(); + m_ignoreChildrenChanged--; +} + +void Attr::setValue(const AtomicString& value, ExceptionCode&) +{ + if (m_element && m_element->isIdAttributeName(m_attribute->name())) + m_element->updateId(m_element->getIdAttribute(), value); + + setValue(value); + + if (m_element) + m_element->attributeChanged(m_attribute.get()); +} + +void Attr::setNodeValue(const String& v, ExceptionCode& ec) +{ + setValue(v, ec); +} + +PassRefPtr<Node> Attr::cloneNode(bool /*deep*/) +{ + RefPtr<Attr> clone = adoptRef(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(); + } + + if (m_element && m_element->isIdAttributeName(m_attribute->name())) + m_element->updateId(m_attribute->value(), val); + + m_attribute->setValue(val.impl()); + if (m_element) + m_element->attributeChanged(m_attribute.get()); +} + +bool Attr::isId() const +{ + return qualifiedName().matches(document()->idAttributeName()); +} + +} diff --git a/Source/WebCore/dom/Attr.h b/Source/WebCore/dom/Attr.h new file mode 100644 index 0000000..e76d2fa --- /dev/null +++ b/Source/WebCore/dom/Attr.h @@ -0,0 +1,98 @@ +/* + * 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, 2009 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 { + +// 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 NamedNodeMap; +public: + static PassRefPtr<Attr> create(Element*, Document*, PassRefPtr<Attribute>); + virtual ~Attr(); + + String name() const { return qualifiedName().toString(); } + bool specified() const { return m_specified; } + Element* ownerElement() const { return m_element; } + + const AtomicString& value() const { return m_attribute->value(); } + void setValue(const AtomicString&, ExceptionCode&); + void setValue(const AtomicString&); + + Attribute* attr() const { return m_attribute.get(); } + const QualifiedName& qualifiedName() const { return m_attribute->name(); } + + bool isId() const; + + // An extension to get presentational information for attributes. + CSSStyleDeclaration* style() { return m_attribute->style(); } + + void setSpecified(bool specified) { m_specified = specified; } + +private: + Attr(Element*, Document*, PassRefPtr<Attribute>); + + void createTextChild(); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + + const AtomicString& localName() const; + const AtomicString& namespaceURI() const; + 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); + + 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 const AtomicString& virtualPrefix() const { return prefix(); } + virtual const AtomicString& virtualLocalName() const { return localName(); } + virtual const AtomicString& virtualNamespaceURI() const { return namespaceURI(); } + + Element* m_element; + RefPtr<Attribute> m_attribute; + unsigned m_ignoreChildrenChanged : 31; + bool m_specified : 1; +}; + +} // namespace WebCore + +#endif // Attr_h diff --git a/Source/WebCore/dom/Attr.idl b/Source/WebCore/dom/Attr.idl new file mode 100644 index 0000000..bff9c7b --- /dev/null +++ b/Source/WebCore/dom/Attr.idl @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 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, + GenerateNativeConverter + ] Attr : Node { + + // DOM Level 1 + + readonly attribute [ConvertNullStringTo=Null] DOMString name; + + readonly attribute boolean specified; + + attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString value + setter raises(DOMException); + + // DOM Level 2 + + readonly attribute Element ownerElement; + + // DOM Level 3 + + readonly attribute boolean isId; + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + // This extension is no longer needed, but it has to remain available in Objective C, as it's public API. + readonly attribute CSSStyleDeclaration style; +#endif + }; + +} diff --git a/Source/WebCore/dom/Attribute.cpp b/Source/WebCore/dom/Attribute.cpp new file mode 100644 index 0000000..20c05d8 --- /dev/null +++ b/Source/WebCore/dom/Attribute.cpp @@ -0,0 +1,89 @@ +/* + * 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" +#include <wtf/HashMap.h> +#include <wtf/UnusedParam.h> + +namespace WebCore { + +typedef HashMap<Attribute*, Attr*> AttributeAttrMap; +static AttributeAttrMap& attributeAttrMap() +{ + DEFINE_STATIC_LOCAL(AttributeAttrMap, map, ()); + return map; +} + +PassRefPtr<Attribute> Attribute::clone() const +{ + return adoptRef(new Attribute(m_name, m_value, m_isMappedAttribute, m_styleDecl.get())); +} + +Attr* Attribute::attr() const +{ + if (m_hasAttr) { + ASSERT(attributeAttrMap().contains(const_cast<Attribute*>(this))); + return attributeAttrMap().get(const_cast<Attribute*>(this)); + } + + ASSERT(!attributeAttrMap().contains(const_cast<Attribute*>(this))); + return 0; +} + +PassRefPtr<Attr> Attribute::createAttrIfNeeded(Element* e) +{ + RefPtr<Attr> r; + if (m_hasAttr) { + ASSERT(attributeAttrMap().contains(this)); + r = attributeAttrMap().get(this); + } else { + ASSERT(!attributeAttrMap().contains(this)); + r = Attr::create(e, e->document(), this); // This will end up calling Attribute::bindAttr. + ASSERT(attributeAttrMap().contains(this)); + } + + return r.release(); +} + +void Attribute::bindAttr(Attr* attr) +{ + ASSERT(!m_hasAttr); + ASSERT(!attributeAttrMap().contains(this)); + attributeAttrMap().set(this, attr); + m_hasAttr = true; +} + +void Attribute::unbindAttr(Attr* attr) +{ + ASSERT(m_hasAttr); + ASSERT(attributeAttrMap().contains(this)); + ASSERT_UNUSED(attr, attributeAttrMap().get(this) == attr); + attributeAttrMap().remove(this); + m_hasAttr = false; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/Attribute.h b/Source/WebCore/dom/Attribute.h new file mode 100644 index 0000000..cf84a6f --- /dev/null +++ b/Source/WebCore/dom/Attribute.h @@ -0,0 +1,121 @@ +/* + * 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, 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 Attribute_h +#define Attribute_h + +#include "CSSMappedAttributeDeclaration.h" +#include "QualifiedName.h" + +namespace WebCore { + +class Attr; +class CSSStyleDeclaration; +class Element; +class NamedNodeMap; + +// This has no counterpart in DOM. +// It is an internal representation of the node value of an Attr. +// The actual Attr with its value as a Text child is allocated only if needed. +class Attribute : public RefCounted<Attribute> { + friend class Attr; + friend class NamedNodeMap; +public: + static PassRefPtr<Attribute> create(const QualifiedName& name, const AtomicString& value) + { + return adoptRef(new Attribute(name, value, false, 0)); + } + static PassRefPtr<Attribute> createMapped(const QualifiedName& name, const AtomicString& value) + { + return adoptRef(new Attribute(name, value, true, 0)); + } + static PassRefPtr<Attribute> createMapped(const AtomicString& name, const AtomicString& value) + { + return adoptRef(new Attribute(name, value, true, 0)); + } + + 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; + PassRefPtr<Attr> createAttrIfNeeded(Element*); + + bool isNull() const { return m_value.isNull(); } + bool isEmpty() const { return m_value.isEmpty(); } + + PassRefPtr<Attribute> clone() const; + + // An extension to get the style information for presentational attributes. + CSSStyleDeclaration* style() const { return m_styleDecl.get(); } + CSSMappedAttributeDeclaration* decl() const { return m_styleDecl.get(); } + void setDecl(PassRefPtr<CSSMappedAttributeDeclaration> decl) { m_styleDecl = decl; } + + void setValue(const AtomicString& value) { m_value = value; } + void setPrefix(const AtomicString& prefix) { m_name.setPrefix(prefix); } + + // Note: This API is only for HTMLTreeBuilder. It is not safe to change the + // name of an attribute once parseMappedAttribute has been called as DOM + // elements may have placed the Attribute in a hash by name. + void parserSetName(const QualifiedName& name) { m_name = name; } + + bool isMappedAttribute() { return m_isMappedAttribute; } + +private: + Attribute(const QualifiedName& name, const AtomicString& value, bool isMappedAttribute, CSSMappedAttributeDeclaration* styleDecl) + : m_isMappedAttribute(isMappedAttribute) + , m_hasAttr(false) + , m_name(name) + , m_value(value) + , m_styleDecl(styleDecl) + { + } + + Attribute(const AtomicString& name, const AtomicString& value, bool isMappedAttribute, CSSMappedAttributeDeclaration* styleDecl) + : m_isMappedAttribute(isMappedAttribute) + , m_hasAttr(false) + , m_name(nullAtom, name, nullAtom) + , m_value(value) + , m_styleDecl(styleDecl) + { + } + + void bindAttr(Attr*); + void unbindAttr(Attr*); + + // These booleans will go into the spare 32-bits of padding from RefCounted in 64-bit. + bool m_isMappedAttribute; + bool m_hasAttr; + + QualifiedName m_name; + AtomicString m_value; + RefPtr<CSSMappedAttributeDeclaration> m_styleDecl; +}; + +} // namespace WebCore + +#endif // Attribute_h diff --git a/Source/WebCore/dom/BeforeLoadEvent.h b/Source/WebCore/dom/BeforeLoadEvent.h new file mode 100644 index 0000000..f60e438 --- /dev/null +++ b/Source/WebCore/dom/BeforeLoadEvent.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 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 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 BeforeLoadEvent_h +#define BeforeLoadEvent_h + +#include "Event.h" + +namespace WebCore { + +class BeforeLoadEvent : public Event { +public: + virtual bool isBeforeLoadEvent() const { return true; } + + static PassRefPtr<BeforeLoadEvent> create(const String& url) + { + return adoptRef(new BeforeLoadEvent(url)); + } + + void initBeforeLoadEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& url) + { + if (dispatched()) + return; + + initEvent(type, canBubble, cancelable); + + m_url = url; + } + + const String& url() const { return m_url; } + +private: + BeforeLoadEvent(const String& url) + : Event(eventNames().beforeloadEvent, false, true) + , m_url(url) + {} + + String m_url; +}; + +} // namespace WebCore + +#endif // BeforeLoadEvent_h diff --git a/Source/WebCore/dom/BeforeLoadEvent.idl b/Source/WebCore/dom/BeforeLoadEvent.idl new file mode 100644 index 0000000..9c8b7e5 --- /dev/null +++ b/Source/WebCore/dom/BeforeLoadEvent.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009 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 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 BeforeLoadEvent : Event { + void initBeforeLoadEvent(in DOMString type, + in boolean canBubble, + in boolean cancelable, + in DOMString url); + readonly attribute DOMString url; + }; + +} diff --git a/Source/WebCore/dom/BeforeProcessEvent.cpp b/Source/WebCore/dom/BeforeProcessEvent.cpp new file mode 100644 index 0000000..7314481 --- /dev/null +++ b/Source/WebCore/dom/BeforeProcessEvent.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 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 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 "BeforeProcessEvent.h" + +namespace WebCore { + +String BeforeProcessEvent::text() const +{ + // FIXME - Return innerText for <style> elements and inline <script> elements, or the resource text for remote <script> elements + return String(); +} + +void BeforeProcessEvent::setText(const String&) +{ + // FIXME - Replace innerText for <style> elements and inline <script> elements, and remove the src attribute on + // remote <script> elements, replacing it with an innerText property. +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/BeforeProcessEvent.h b/Source/WebCore/dom/BeforeProcessEvent.h new file mode 100644 index 0000000..035d2da --- /dev/null +++ b/Source/WebCore/dom/BeforeProcessEvent.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 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 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 BeforeProcessEvent_h +#define BeforeProcessEvent_h + +#include "Event.h" +#include "EventNames.h" + +namespace WebCore { + +class BeforeProcessEvent : public Event { +public: + static PassRefPtr<BeforeProcessEvent> create() + { + return adoptRef(new BeforeProcessEvent); + } + + void initBeforeProcessEvent(const AtomicString& type, bool canBubble, bool cancelable) + { + initEvent(type, canBubble, cancelable); + } + + String text() const; + void setText(const String&); + +private: + BeforeProcessEvent() + : Event(eventNames().beforeprocessEvent, false, true) + { + } + +}; + +} // namespace WebCore + +#endif // BeforeProcessEvent_h diff --git a/Source/WebCore/dom/BeforeProcessEvent.idl b/Source/WebCore/dom/BeforeProcessEvent.idl new file mode 100644 index 0000000..28b1a81 --- /dev/null +++ b/Source/WebCore/dom/BeforeProcessEvent.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 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 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 BeforeProcessEvent : Event { + void initBeforeProcessEvent(in DOMString type, in boolean canBubble, in boolean cancelable); + attribute DOMString text; + }; + +} diff --git a/Source/WebCore/dom/BeforeTextInsertedEvent.cpp b/Source/WebCore/dom/BeforeTextInsertedEvent.cpp new file mode 100644 index 0000000..472eefc --- /dev/null +++ b/Source/WebCore/dom/BeforeTextInsertedEvent.cpp @@ -0,0 +1,42 @@ +/* + * 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 { + +BeforeTextInsertedEvent::BeforeTextInsertedEvent(const String& text) + : Event(eventNames().webkitBeforeTextInsertedEvent, false, true), m_text(text) +{ +} + +BeforeTextInsertedEvent::~BeforeTextInsertedEvent() +{ +} + +} diff --git a/Source/WebCore/dom/BeforeTextInsertedEvent.h b/Source/WebCore/dom/BeforeTextInsertedEvent.h new file mode 100644 index 0000000..a6966ff --- /dev/null +++ b/Source/WebCore/dom/BeforeTextInsertedEvent.h @@ -0,0 +1,55 @@ +/* + * 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: + virtual ~BeforeTextInsertedEvent(); + + static PassRefPtr<BeforeTextInsertedEvent> create(const String& text) + { + return adoptRef(new BeforeTextInsertedEvent(text)); + } + + virtual bool isBeforeTextInsertedEvent() const { return true; } + + const String& text() const { return m_text; } + void setText(const String& s) { m_text = s; } + +private: + BeforeTextInsertedEvent(const String&); + + String m_text; +}; + +} // namespace + +#endif diff --git a/Source/WebCore/dom/BeforeUnloadEvent.cpp b/Source/WebCore/dom/BeforeUnloadEvent.cpp new file mode 100644 index 0000000..19adcab --- /dev/null +++ b/Source/WebCore/dom/BeforeUnloadEvent.cpp @@ -0,0 +1,49 @@ +/** + * 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 { + +BeforeUnloadEvent::BeforeUnloadEvent() + : Event(eventNames().beforeunloadEvent, false, true) +{ +} + +BeforeUnloadEvent::~BeforeUnloadEvent() +{ +} + +bool BeforeUnloadEvent::storesResultAsString() const +{ + return true; +} + +void BeforeUnloadEvent::storeResult(const String& s) +{ + m_result = s; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/BeforeUnloadEvent.h b/Source/WebCore/dom/BeforeUnloadEvent.h new file mode 100644 index 0000000..136e85c --- /dev/null +++ b/Source/WebCore/dom/BeforeUnloadEvent.h @@ -0,0 +1,53 @@ +/* + * 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: + virtual ~BeforeUnloadEvent(); + + static PassRefPtr<BeforeUnloadEvent> create() + { + return adoptRef(new BeforeUnloadEvent); + } + + virtual bool storesResultAsString() const; + virtual void storeResult(const String&); + + String result() const { return m_result; } + + private: + BeforeUnloadEvent(); + + String m_result; + }; + +} // namespace WebCore + +#endif // BeforeUnloadEvent_h diff --git a/Source/WebCore/dom/CDATASection.cpp b/Source/WebCore/dom/CDATASection.cpp new file mode 100644 index 0000000..d73054e --- /dev/null +++ b/Source/WebCore/dom/CDATASection.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2008, 2009 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 { + +inline CDATASection::CDATASection(Document* document, const String& data) + : Text(document, data) +{ +} + +PassRefPtr<CDATASection> CDATASection::create(Document* document, const String& data) +{ + return adoptRef(new CDATASection(document, data)); +} + +String CDATASection::nodeName() const +{ + return "#cdata-section"; +} + +Node::NodeType CDATASection::nodeType() const +{ + return CDATA_SECTION_NODE; +} + +PassRefPtr<Node> CDATASection::cloneNode(bool /*deep*/) +{ + return create(document(), data()); +} + +bool CDATASection::childTypeAllowed(NodeType) +{ + return false; +} + +PassRefPtr<Text> CDATASection::virtualCreate(const String& data) +{ + return create(document(), data); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/CDATASection.h b/Source/WebCore/dom/CDATASection.h new file mode 100644 index 0000000..5cf07e1 --- /dev/null +++ b/Source/WebCore/dom/CDATASection.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2009 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 CDATASection_h +#define CDATASection_h + +#include "Text.h" + +namespace WebCore { + +class CDATASection : public Text { +public: + static PassRefPtr<CDATASection> create(Document*, const String&); + +private: + CDATASection(Document*, const String&); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); + virtual PassRefPtr<Text> virtualCreate(const String&); +}; + +} // namespace WebCore + +#endif // CDATASection_h diff --git a/Source/WebCore/dom/CDATASection.idl b/Source/WebCore/dom/CDATASection.idl new file mode 100644 index 0000000..70a4f55 --- /dev/null +++ b/Source/WebCore/dom/CDATASection.idl @@ -0,0 +1,25 @@ +/* + * 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 CDATASection : Text { + }; + +} diff --git a/Source/WebCore/dom/CSSMappedAttributeDeclaration.cpp b/Source/WebCore/dom/CSSMappedAttributeDeclaration.cpp new file mode 100644 index 0000000..9ee6474 --- /dev/null +++ b/Source/WebCore/dom/CSSMappedAttributeDeclaration.cpp @@ -0,0 +1,36 @@ +/** + * 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/Source/WebCore/dom/CSSMappedAttributeDeclaration.h b/Source/WebCore/dom/CSSMappedAttributeDeclaration.h new file mode 100644 index 0000000..19ba5a0 --- /dev/null +++ b/Source/WebCore/dom/CSSMappedAttributeDeclaration.h @@ -0,0 +1,65 @@ +/* + * 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, 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 CSSMappedAttributeDeclaration_h +#define CSSMappedAttributeDeclaration_h + +#include "CSSMutableStyleDeclaration.h" +#include "MappedAttributeEntry.h" +#include "QualifiedName.h" + +namespace WebCore { + +class CSSMappedAttributeDeclaration : public CSSMutableStyleDeclaration { +public: + static PassRefPtr<CSSMappedAttributeDeclaration> create() + { + return adoptRef(new CSSMappedAttributeDeclaration(0)); + } + + virtual ~CSSMappedAttributeDeclaration(); + + void setMappedState(MappedAttributeEntry type, const QualifiedName& name, const AtomicString& val) + { + m_entryType = type; + m_attrName = name; + m_attrValue = val; + } + +private: + CSSMappedAttributeDeclaration(CSSRule* parentRule) + : CSSMutableStyleDeclaration(parentRule) + , m_entryType(eNone) + , m_attrName(anyQName()) + { + } + + MappedAttributeEntry m_entryType; + QualifiedName m_attrName; + AtomicString m_attrValue; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/dom/CharacterData.cpp b/Source/WebCore/dom/CharacterData.cpp new file mode 100644 index 0000000..640e3fa --- /dev/null +++ b/Source/WebCore/dom/CharacterData.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 "EventNames.h" +#include "ExceptionCode.h" +#include "InspectorInstrumentation.h" +#include "MutationEvent.h" +#include "RenderText.h" + +namespace WebCore { + +void CharacterData::setData(const String& data, ExceptionCode&) +{ + StringImpl* dataImpl = data.impl() ? data.impl() : StringImpl::empty(); + if (equal(m_data.get(), dataImpl)) + return; + + unsigned oldLength = length(); + + setDataAndUpdate(dataImpl, 0, oldLength, dataImpl->length()); + document()->textRemoved(this, 0, oldLength); +} + +String CharacterData::substringData(unsigned offset, unsigned count, ExceptionCode& ec) +{ + checkCharDataOperation(offset, ec); + if (ec) + return String(); + + return m_data->substring(offset, count); +} + +void CharacterData::parserAppendData(const String& data) +{ + String newStr = m_data; + newStr.append(data); + + int oldLength = m_data->length(); + m_data = newStr.impl(); + + updateRenderer(oldLength, 0); + // We don't call dispatchModifiedEvent here because we don't want the + // parser to dispatch DOM mutation events. + if (parentNode()) + parentNode()->childrenChanged(); +} + +void CharacterData::appendData(const String& data, ExceptionCode&) +{ + String newStr = m_data; + newStr.append(data); + + setDataAndUpdate(newStr.impl(), m_data->length(), 0, data.length()); + + // FIXME: Should we call textInserted here? +} + +void CharacterData::insertData(unsigned offset, const String& data, ExceptionCode& ec) +{ + checkCharDataOperation(offset, ec); + if (ec) + return; + + String newStr = m_data; + newStr.insert(data, offset); + + setDataAndUpdate(newStr.impl(), offset, 0, data.length()); + + document()->textInserted(this, offset, data.length()); +} + +void CharacterData::deleteData(unsigned offset, unsigned count, ExceptionCode& ec) +{ + checkCharDataOperation(offset, ec); + if (ec) + return; + + unsigned realCount; + if (offset + count > length()) + realCount = length() - offset; + else + realCount = count; + + String newStr = m_data; + newStr.remove(offset, realCount); + + setDataAndUpdate(newStr.impl(), offset, count, 0); + + document()->textRemoved(this, offset, realCount); +} + +void CharacterData::replaceData(unsigned offset, unsigned count, const String& data, ExceptionCode& ec) +{ + checkCharDataOperation(offset, ec); + if (ec) + return; + + unsigned realCount; + if (offset + count > length()) + realCount = length() - offset; + else + realCount = count; + + String newStr = m_data; + newStr.remove(offset, realCount); + newStr.insert(data, offset); + + setDataAndUpdate(newStr.impl(), offset, count, data.length()); + + // update the markers for spell checking and grammar checking + document()->textRemoved(this, offset, realCount); + document()->textInserted(this, offset, data.length()); +} + +String CharacterData::nodeValue() const +{ + return m_data; +} + +bool CharacterData::containsOnlyWhitespace() const +{ + return !m_data || m_data->containsOnlyWhitespace(); +} + +void CharacterData::setNodeValue(const String& nodeValue, ExceptionCode& ec) +{ + setData(nodeValue, ec); +} + +void CharacterData::setDataAndUpdate(PassRefPtr<StringImpl> newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength) +{ + if (document()->frame()) + document()->frame()->selection()->textWillBeReplaced(this, offsetOfReplacedData, oldLength, newLength); + RefPtr<StringImpl> oldData = m_data; + m_data = newData; + updateRenderer(offsetOfReplacedData, oldLength); + dispatchModifiedEvent(oldData.get()); +} + +void CharacterData::updateRenderer(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData) +{ + if ((!renderer() || !rendererIsNeeded(renderer()->style())) && attached()) { + detach(); + attach(); + } else if (renderer()) + toRenderText(renderer())->setTextWithOffset(m_data, offsetOfReplacedData, lengthOfReplacedData); +} + +void CharacterData::dispatchModifiedEvent(StringImpl* oldData) +{ + if (parentNode()) + parentNode()->childrenChanged(); + if (document()->hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) + dispatchEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, 0, oldData, m_data)); + dispatchSubtreeModifiedEvent(); +#if ENABLE(INSPECTOR) + InspectorInstrumentation::characterDataModified(document(), this); +#endif +} + +void CharacterData::checkCharDataOperation(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 > length()) { + ec = INDEX_SIZE_ERR; + return; + } +} + +int CharacterData::maxCharacterOffset() const +{ + return static_cast<int>(length()); +} + +bool CharacterData::rendererIsNeeded(RenderStyle *style) +{ + if (!m_data || !length()) + return false; + return Node::rendererIsNeeded(style); +} + +bool CharacterData::offsetInCharacters() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/CharacterData.h b/Source/WebCore/dom/CharacterData.h new file mode 100644 index 0000000..31da63c --- /dev/null +++ b/Source/WebCore/dom/CharacterData.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 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 "Node.h" + +namespace WebCore { + +class CharacterData : public Node { +public: + 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&, ExceptionCode&); + + bool containsOnlyWhitespace() const; + + StringImpl* dataImpl() { return m_data.get(); } + + // Like appendData, but optimized for the parser (e.g., no mutation events). + void parserAppendData(const String&); + +protected: + CharacterData(Document* document, const String& text, ConstructionType type) + : Node(document, type) + , m_data(text.impl() ? text.impl() : StringImpl::empty()) + { + ASSERT(type == CreateComment || type == CreateText); + } + + virtual bool rendererIsNeeded(RenderStyle*); + + void setDataImpl(PassRefPtr<StringImpl> impl) { m_data = impl; } + void dispatchModifiedEvent(StringImpl* oldValue); + +private: + virtual String nodeValue() const; + virtual void setNodeValue(const String&, ExceptionCode&); + virtual bool isCharacterDataNode() const { return true; } + virtual int maxCharacterOffset() const; + virtual bool offsetInCharacters() const; + void setDataAndUpdate(PassRefPtr<StringImpl>, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength); + void updateRenderer(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData); + void checkCharDataOperation(unsigned offset, ExceptionCode&); + + RefPtr<StringImpl> m_data; +}; + +} // namespace WebCore + +#endif // CharacterData_h + diff --git a/Source/WebCore/dom/CharacterData.idl b/Source/WebCore/dom/CharacterData.idl new file mode 100644 index 0000000..4ddd782 --- /dev/null +++ b/Source/WebCore/dom/CharacterData.idl @@ -0,0 +1,51 @@ +/* + * 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 CharacterData : Node { + + 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/Source/WebCore/dom/CheckedRadioButtons.cpp b/Source/WebCore/dom/CheckedRadioButtons.cpp new file mode 100644 index 0000000..3cf8848 --- /dev/null +++ b/Source/WebCore/dom/CheckedRadioButtons.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2007, 2008, 2009 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 "CheckedRadioButtons.h" + +#include "HTMLInputElement.h" + +namespace WebCore { + +void CheckedRadioButtons::addButton(HTMLFormControlElement* element) +{ + // We only want to add radio buttons. + if (!element->isRadioButton()) + return; + + // Without a name, there is no group. + if (element->name().isEmpty()) + return; + + HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element); + + // We only track checked buttons. + if (!inputElement->checked()) + return; + + if (!m_nameToCheckedRadioButtonMap) + m_nameToCheckedRadioButtonMap.set(new NameToInputMap); + + pair<NameToInputMap::iterator, bool> result = m_nameToCheckedRadioButtonMap->add(element->name().impl(), inputElement); + if (result.second) + return; + + HTMLInputElement* oldCheckedButton = result.first->second; + if (oldCheckedButton == inputElement) + return; + + result.first->second = inputElement; + oldCheckedButton->setChecked(false); +} + +HTMLInputElement* CheckedRadioButtons::checkedButtonForGroup(const AtomicString& name) const +{ + if (!m_nameToCheckedRadioButtonMap) + return 0; + + m_nameToCheckedRadioButtonMap->checkConsistency(); + + return m_nameToCheckedRadioButtonMap->get(name.impl()); +} + +void CheckedRadioButtons::removeButton(HTMLFormControlElement* element) +{ + if (element->name().isEmpty() || !m_nameToCheckedRadioButtonMap) + return; + + m_nameToCheckedRadioButtonMap->checkConsistency(); + + NameToInputMap::iterator it = m_nameToCheckedRadioButtonMap->find(element->name().impl()); + if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element) + return; + + InputElement* inputElement = toInputElement(element); + ASSERT_UNUSED(inputElement, inputElement); + ASSERT(inputElement->isChecked()); + ASSERT(element->isRadioButton()); + + m_nameToCheckedRadioButtonMap->remove(it); + if (m_nameToCheckedRadioButtonMap->isEmpty()) + m_nameToCheckedRadioButtonMap.clear(); +} + +} // namespace diff --git a/Source/WebCore/dom/CheckedRadioButtons.h b/Source/WebCore/dom/CheckedRadioButtons.h new file mode 100644 index 0000000..0fa457a --- /dev/null +++ b/Source/WebCore/dom/CheckedRadioButtons.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007, 2008, 2009 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 CheckedRadioButtons_h +#define CheckedRadioButtons_h + +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class HTMLFormControlElement; +class HTMLInputElement; + +class CheckedRadioButtons { +public: + void addButton(HTMLFormControlElement*); + void removeButton(HTMLFormControlElement*); + HTMLInputElement* checkedButtonForGroup(const AtomicString& groupName) const; + +private: + typedef HashMap<AtomicStringImpl*, HTMLInputElement*> NameToInputMap; + OwnPtr<NameToInputMap> m_nameToCheckedRadioButtonMap; +}; + +} // namespace WebCore + +#endif // CheckedRadioButtons_h diff --git a/Source/WebCore/dom/ChildNodeList.cpp b/Source/WebCore/dom/ChildNodeList.cpp new file mode 100644 index 0000000..3328c7c --- /dev/null +++ b/Source/WebCore/dom/ChildNodeList.cpp @@ -0,0 +1,109 @@ +/** + * 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, 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 "ChildNodeList.h" + +#include "Element.h" + +namespace WebCore { + +ChildNodeList::ChildNodeList(PassRefPtr<Node> rootNode, DynamicNodeList::Caches* info) + : DynamicNodeList(rootNode, info) +{ +} + +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(Element* testNode) const +{ + // Note: Due to the overrides of the length and item functions above, + // this function will be called only by DynamicNodeList::itemWithName, + // for an element that was located with getElementById. + return testNode->parentNode() == m_rootNode; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ChildNodeList.h b/Source/WebCore/dom/ChildNodeList.h new file mode 100644 index 0000000..f38106d --- /dev/null +++ b/Source/WebCore/dom/ChildNodeList.h @@ -0,0 +1,50 @@ +/* + * 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" +#include <wtf/PassRefPtr.h> + +namespace WebCore { + + class ChildNodeList : public DynamicNodeList { + public: + static PassRefPtr<ChildNodeList> create(PassRefPtr<Node> rootNode, Caches* caches) + { + return adoptRef(new ChildNodeList(rootNode, caches)); + } + + virtual unsigned length() const; + virtual Node* item(unsigned index) const; + + protected: + ChildNodeList(PassRefPtr<Node> rootNode, Caches*); + + virtual bool nodeMatches(Element*) const; + }; + +} // namespace WebCore + +#endif // ChildNodeList_h diff --git a/Source/WebCore/dom/ClassNodeList.cpp b/Source/WebCore/dom/ClassNodeList.cpp new file mode 100644 index 0000000..60d668b --- /dev/null +++ b/Source/WebCore/dom/ClassNodeList.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007, 2008 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 "StyledElement.h" + +namespace WebCore { + +ClassNodeList::ClassNodeList(PassRefPtr<Node> rootNode, const String& classNames) + : DynamicNodeList(rootNode) + , m_classNames(classNames, m_rootNode->document()->inQuirksMode()) + , m_originalClassNames(classNames) +{ +} + +ClassNodeList::~ClassNodeList() +{ + m_rootNode->removeCachedClassNodeList(this, m_originalClassNames); +} + +bool ClassNodeList::nodeMatches(Element* testNode) const +{ + if (!testNode->hasClass()) + return false; + if (!m_classNames.size()) + return false; + ASSERT(testNode->isStyledElement()); + return static_cast<StyledElement*>(testNode)->classNames().containsAll(m_classNames); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ClassNodeList.h b/Source/WebCore/dom/ClassNodeList.h new file mode 100644 index 0000000..13d5d24 --- /dev/null +++ b/Source/WebCore/dom/ClassNodeList.h @@ -0,0 +1,59 @@ +/* + * 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 "DynamicNodeList.h" +#include "Node.h" +#include "SpaceSplitString.h" + +namespace WebCore { + + class ClassNodeList : public DynamicNodeList { + public: + static PassRefPtr<ClassNodeList> create(PassRefPtr<Node> rootNode, const String& classNames) + { + return adoptRef(new ClassNodeList(rootNode, classNames)); + } + + virtual ~ClassNodeList(); + + private: + ClassNodeList(PassRefPtr<Node> rootNode, const String& classNames); + + virtual bool nodeMatches(Element*) const; + + SpaceSplitString m_classNames; + String m_originalClassNames; + }; + +} // namespace WebCore + +#endif // ClassNodeList_h diff --git a/Source/WebCore/dom/ClientRect.cpp b/Source/WebCore/dom/ClientRect.cpp new file mode 100644 index 0000000..b132222 --- /dev/null +++ b/Source/WebCore/dom/ClientRect.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 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 "ClientRect.h" + +namespace WebCore { + +ClientRect::ClientRect() +{ +} + +ClientRect::ClientRect(const IntRect& rect) + : m_rect(rect) +{ +} + +ClientRect::ClientRect(const FloatRect& rect) + : m_rect(rect) +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ClientRect.h b/Source/WebCore/dom/ClientRect.h new file mode 100644 index 0000000..f9acef0 --- /dev/null +++ b/Source/WebCore/dom/ClientRect.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009 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 ClientRect_h +#define ClientRect_h + +#include "FloatRect.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class IntRect; + + class ClientRect : public RefCounted<ClientRect> { + public: + static PassRefPtr<ClientRect> create() { return adoptRef(new ClientRect); } + static PassRefPtr<ClientRect> create(const IntRect& rect) { return adoptRef(new ClientRect(rect)); } + static PassRefPtr<ClientRect> create(const FloatRect& rect) { return adoptRef(new ClientRect(rect)); } + + float top() const { return m_rect.y(); } + float right() const { return m_rect.right(); } + float bottom() const { return m_rect.bottom(); } + float left() const { return m_rect.x(); } + float width() const { return m_rect.width(); } + float height() const { return m_rect.height(); } + + private: + ClientRect(); + ClientRect(const IntRect&); + ClientRect(const FloatRect&); + + FloatRect m_rect; + }; + +} // namespace WebCore + +#endif // ClientRect_h diff --git a/Source/WebCore/dom/ClientRect.idl b/Source/WebCore/dom/ClientRect.idl new file mode 100644 index 0000000..7dbdd68 --- /dev/null +++ b/Source/WebCore/dom/ClientRect.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 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 view { + + interface ClientRect { + readonly attribute float top; + readonly attribute float right; + readonly attribute float bottom; + readonly attribute float left; + readonly attribute float width; + readonly attribute float height; + }; + +} diff --git a/Source/WebCore/dom/ClientRectList.cpp b/Source/WebCore/dom/ClientRectList.cpp new file mode 100644 index 0000000..95ec758 --- /dev/null +++ b/Source/WebCore/dom/ClientRectList.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 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 "ClientRectList.h" + +#include "ExceptionCode.h" +#include "ClientRect.h" + +namespace WebCore { + +ClientRectList::ClientRectList() +{ +} + +ClientRectList::ClientRectList(const Vector<FloatQuad>& quads) +{ + m_list.reserveInitialCapacity(quads.size()); + for (size_t i = 0; i < quads.size(); ++i) + m_list.append(ClientRect::create(quads[i].enclosingBoundingBox())); +} + +ClientRectList::~ClientRectList() +{ +} + +unsigned ClientRectList::length() const +{ + return m_list.size(); +} + +ClientRect* ClientRectList::item(unsigned index) +{ + if (index >= m_list.size()) { + // FIXME: this should throw an exception. + // ec = INDEX_SIZE_ERR; + return 0; + } + + return m_list[index].get(); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ClientRectList.h b/Source/WebCore/dom/ClientRectList.h new file mode 100644 index 0000000..03915b1 --- /dev/null +++ b/Source/WebCore/dom/ClientRectList.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 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 ClientRectList_h +#define ClientRectList_h + +#include "FloatQuad.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class ClientRect; + + class ClientRectList : public RefCounted<ClientRectList> { + public: + static PassRefPtr<ClientRectList> create() { return adoptRef(new ClientRectList); } + static PassRefPtr<ClientRectList> create(const Vector<FloatQuad>& quads) { return adoptRef(new ClientRectList(quads)); } + ~ClientRectList(); + + unsigned length() const; + ClientRect* item(unsigned index); + + private: + ClientRectList(); + ClientRectList(const Vector<FloatQuad>&); + + Vector<RefPtr<ClientRect> > m_list; + }; + +} // namespace WebCore + +#endif // ClientRectList_h diff --git a/Source/WebCore/dom/ClientRectList.idl b/Source/WebCore/dom/ClientRectList.idl new file mode 100644 index 0000000..6372418 --- /dev/null +++ b/Source/WebCore/dom/ClientRectList.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009 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 view { + + interface [ + HasIndexGetter + ] ClientRectList { + readonly attribute unsigned long length; + ClientRect item(in [IsIndex] unsigned long index); + // FIXME: Fix list behavior to allow custom exceptions to be thrown. + }; + +} diff --git a/Source/WebCore/dom/Clipboard.cpp b/Source/WebCore/dom/Clipboard.cpp new file mode 100644 index 0000000..6956257 --- /dev/null +++ b/Source/WebCore/dom/Clipboard.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2006, 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 "Clipboard.h" + +#include "CachedImage.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "Image.h" + +namespace WebCore { + +Clipboard::Clipboard(ClipboardAccessPolicy policy, ClipboardType clipboardType) + : m_policy(policy) + , m_dropEffect("uninitialized") + , m_effectAllowed("uninitialized") + , m_dragStarted(false) + , m_clipboardType(clipboardType) + , 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 == "uninitialized") + return DragOperationEvery; + if (op == "none") + return DragOperationNone; + if (op == "copy") + return DragOperationCopy; + if (op == "link") + return DragOperationLink; + if (op == "move") + return (DragOperation)(DragOperationGeneric | DragOperationMove); + 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"; +} + +DragOperation Clipboard::sourceOperation() const +{ + DragOperation op = dragOpFromIEOp(m_effectAllowed); + ASSERT(op != DragOperationPrivate); + return op; +} + +DragOperation Clipboard::destinationOperation() const +{ + DragOperation op = dragOpFromIEOp(m_dropEffect); + ASSERT(op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == (DragOperation)(DragOperationGeneric | DragOperationMove) || op == DragOperationEvery); + return op; +} + +void Clipboard::setSourceOperation(DragOperation op) +{ + ASSERT_ARG(op, op != DragOperationPrivate); + m_effectAllowed = IEOpFromDragOp(op); +} + +void Clipboard::setDestinationOperation(DragOperation op) +{ + ASSERT_ARG(op, op == DragOperationCopy || op == DragOperationNone || op == DragOperationLink || op == DragOperationGeneric || op == DragOperationMove || op == (DragOperation)(DragOperationGeneric | DragOperationMove)); + m_dropEffect = IEOpFromDragOp(op); +} + +void Clipboard::setDropEffect(const String &effect) +{ + if (!isForDragAndDrop()) + return; + + // The attribute must ignore any attempts to set it to a value other than none, copy, link, and move. + if (effect != "none" && effect != "copy" && effect != "link" && effect != "move") + return; + + if (m_policy == ClipboardReadable || m_policy == ClipboardTypesReadable) + m_dropEffect = effect; +} + +void Clipboard::setEffectAllowed(const String &effect) +{ + if (!isForDragAndDrop()) + return; + + if (dragOpFromIEOp(effect) == DragOperationPrivate) { + // This means that there was no conversion, and the effectAllowed that + // we are passed isn't a valid effectAllowed, so we should ignore it, + // and not set m_effectAllowed. + + // The attribute must ignore any attempts to set it to a value other than + // none, copy, copyLink, copyMove, link, linkMove, move, all, and uninitialized. + return; + } + + + if (m_policy == ClipboardWritable) + m_effectAllowed = effect; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/Clipboard.h b/Source/WebCore/dom/Clipboard.h new file mode 100644 index 0000000..b8eadfb --- /dev/null +++ b/Source/WebCore/dom/Clipboard.h @@ -0,0 +1,116 @@ +/* + * 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, 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 Clipboard_h +#define Clipboard_h + +#include "CachedResourceHandle.h" +#include "ClipboardAccessPolicy.h" +#include "DragActions.h" +#include "DragImage.h" +#include "IntPoint.h" +#include "Node.h" + +namespace WebCore { + + class DragData; + class FileList; + class Frame; + + // State available during IE's events for drag and drop and copy/paste + class Clipboard : public RefCounted<Clipboard> { + public: + // Whether this clipboard is serving a drag-drop or copy-paste request. + enum ClipboardType { + CopyAndPaste, + DragAndDrop, + }; + static PassRefPtr<Clipboard> create(ClipboardAccessPolicy, DragData*, Frame*); + + virtual ~Clipboard() { } + + bool isForCopyAndPaste() const { return m_clipboardType == CopyAndPaste; } + bool isForDragAndDrop() const { return m_clipboardType == DragAndDrop; } + + String dropEffect() const { return dropEffectIsUninitialized() ? "none" : m_dropEffect; } + void setDropEffect(const String&); + bool dropEffectIsUninitialized() const { return m_dropEffect == "uninitialized"; } + 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; + virtual PassRefPtr<FileList> files() const = 0; + + IntPoint dragLocation() const { return m_dragLoc; } + CachedImage* dragImage() const { return m_dragImage.get(); } + virtual void setDragImage(CachedImage*, const IntPoint&) = 0; + Node* dragImageElement() const { return m_dragImageElement.get(); } + virtual void setDragImageElement(Node*, const IntPoint&) = 0; + + virtual DragImageRef createDragImage(IntPoint& dragLocation) const = 0; +#if ENABLE(DRAG_SUPPORT) + virtual void declareAndWriteDragImage(Element*, const KURL&, const String& title, Frame*) = 0; +#endif + virtual void writeURL(const KURL&, const String&, Frame*) = 0; + virtual void writeRange(Range*, Frame*) = 0; + virtual void writePlainText(const String&) = 0; + + virtual bool hasData() = 0; + + void setAccessPolicy(ClipboardAccessPolicy); + + DragOperation sourceOperation() const; + DragOperation destinationOperation() const; + void setSourceOperation(DragOperation); + void setDestinationOperation(DragOperation); + + void setDragHasStarted() { m_dragStarted = true; } + + protected: + Clipboard(ClipboardAccessPolicy, ClipboardType); + + 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; + ClipboardType m_clipboardType; + + protected: + IntPoint m_dragLoc; + CachedResourceHandle<CachedImage> m_dragImage; + RefPtr<Node> m_dragImageElement; + }; + +} // namespace WebCore + +#endif // Clipboard_h diff --git a/Source/WebCore/dom/Clipboard.idl b/Source/WebCore/dom/Clipboard.idl new file mode 100644 index 0000000..1024a36 --- /dev/null +++ b/Source/WebCore/dom/Clipboard.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 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. + * 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 Clipboard { + attribute [ConvertNullStringTo=Undefined] DOMString dropEffect; + attribute [ConvertNullStringTo=Undefined] DOMString effectAllowed; + readonly attribute [CustomGetter] Array types; + readonly attribute FileList files; + + [Custom] void clearData(in [Optional] DOMString type) + raises(DOMException); + [Custom] void getData(in DOMString type) + raises(DOMException); + // FIXME: RequiresAllArguments is probably bogus here. + [RequiresAllArguments] boolean setData(in DOMString type, in DOMString data); + [Custom] void setDragImage(in HTMLImageElement image, in long x, in long y) + raises(DOMException); + }; + +} diff --git a/Source/WebCore/dom/ClipboardAccessPolicy.h b/Source/WebCore/dom/ClipboardAccessPolicy.h new file mode 100644 index 0000000..7a54009 --- /dev/null +++ b/Source/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/Source/WebCore/dom/ClipboardEvent.cpp b/Source/WebCore/dom/ClipboardEvent.cpp new file mode 100644 index 0000000..b64703d --- /dev/null +++ b/Source/WebCore/dom/ClipboardEvent.cpp @@ -0,0 +1,46 @@ +/* + * 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, 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 "ClipboardEvent.h" + +namespace WebCore { + +ClipboardEvent::ClipboardEvent() +{ +} + +ClipboardEvent::ClipboardEvent(const AtomicString& eventType, bool canBubble, bool cancelable, PassRefPtr<Clipboard> clipboard) + : Event(eventType, canBubble, cancelable), m_clipboard(clipboard) +{ +} + +ClipboardEvent::~ClipboardEvent() +{ +} + +bool ClipboardEvent::isClipboardEvent() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ClipboardEvent.h b/Source/WebCore/dom/ClipboardEvent.h new file mode 100644 index 0000000..42741e6 --- /dev/null +++ b/Source/WebCore/dom/ClipboardEvent.h @@ -0,0 +1,58 @@ +/* + * 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, 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 ClipboardEvent_h +#define ClipboardEvent_h + +#include "Clipboard.h" +#include "Event.h" + +namespace WebCore { + + class ClipboardEvent : public Event { + public: + virtual ~ClipboardEvent(); + + static PassRefPtr<ClipboardEvent> create() + { + return adoptRef(new ClipboardEvent); + } + static PassRefPtr<ClipboardEvent> create(const AtomicString& type, bool canBubbleArg, bool cancelableArg, PassRefPtr<Clipboard> clipboardArg) + { + return adoptRef(new ClipboardEvent(type, canBubbleArg, cancelableArg, clipboardArg)); + } + + Clipboard* clipboard() const { return m_clipboard.get(); } + + virtual bool isClipboardEvent() const; + + private: + ClipboardEvent(); + ClipboardEvent(const AtomicString& type, bool canBubbleArg, bool cancelableArg, PassRefPtr<Clipboard>); + + RefPtr<Clipboard> m_clipboard; + }; + +} // namespace WebCore + +#endif // ClipboardEvent_h diff --git a/Source/WebCore/dom/Comment.cpp b/Source/WebCore/dom/Comment.cpp new file mode 100644 index 0000000..00f1724 --- /dev/null +++ b/Source/WebCore/dom/Comment.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2009 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 "Comment.h" + +#include "Document.h" + +namespace WebCore { + +inline Comment::Comment(Document* document, const String& text) + : CharacterData(document, text, CreateComment) +{ +} + +PassRefPtr<Comment> Comment::create(Document* document, const String& text) +{ + return adoptRef(new Comment(document, text)); +} + +String Comment::nodeName() const +{ + return commentAtom.string(); +} + +Node::NodeType Comment::nodeType() const +{ + return COMMENT_NODE; +} + +PassRefPtr<Node> Comment::cloneNode(bool /*deep*/) +{ + return create(document(), data()); +} + +bool Comment::childTypeAllowed(NodeType) +{ + return false; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/Comment.h b/Source/WebCore/dom/Comment.h new file mode 100644 index 0000000..a0210c4 --- /dev/null +++ b/Source/WebCore/dom/Comment.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2009 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 Comment_h +#define Comment_h + +#include "CharacterData.h" + +namespace WebCore { + +class Comment : public CharacterData { +public: + static PassRefPtr<Comment> create(Document*, const String&); + +private: + Comment(Document*, const String&); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); +}; + +} // namespace WebCore + +#endif // Comment_h diff --git a/Source/WebCore/dom/Comment.idl b/Source/WebCore/dom/Comment.idl new file mode 100644 index 0000000..b9f4e31 --- /dev/null +++ b/Source/WebCore/dom/Comment.idl @@ -0,0 +1,25 @@ +/* + * 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 Comment : CharacterData { + }; + +} diff --git a/Source/WebCore/dom/CompositionEvent.cpp b/Source/WebCore/dom/CompositionEvent.cpp new file mode 100644 index 0000000..508d5e6 --- /dev/null +++ b/Source/WebCore/dom/CompositionEvent.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2009 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 "CompositionEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +CompositionEvent::CompositionEvent() +{ +} + +CompositionEvent::CompositionEvent(const AtomicString& type, PassRefPtr<AbstractView> view, const String& data) + : UIEvent(type, true, true, view, 0) + , m_data(data) +{ +} + +CompositionEvent::~CompositionEvent() +{ +} + +void CompositionEvent::initCompositionEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, const String& data) +{ + if (dispatched()) + return; + + initUIEvent(type, canBubble, cancelable, view, 0); + + m_data = data; +} + +bool CompositionEvent::isCompositionEvent() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/CompositionEvent.h b/Source/WebCore/dom/CompositionEvent.h new file mode 100644 index 0000000..4ff01ae --- /dev/null +++ b/Source/WebCore/dom/CompositionEvent.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009 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 CompositionEvent_h +#define CompositionEvent_h + +#include "UIEvent.h" + +namespace WebCore { + + class CompositionEvent : public UIEvent { + public: + static PassRefPtr<CompositionEvent> create() + { + return adoptRef(new CompositionEvent); + } + static PassRefPtr<CompositionEvent> create(const AtomicString& type, PassRefPtr<AbstractView> view, const String& data) + { + return adoptRef(new CompositionEvent(type, view, data)); + } + virtual ~CompositionEvent(); + + void initCompositionEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, const String& data); + + String data() const { return m_data; } + + virtual bool isCompositionEvent() const; + + private: + CompositionEvent(); + CompositionEvent(const AtomicString& type, PassRefPtr<AbstractView> view, const String& data); + + String m_data; + }; + +} // namespace WebCore + +#endif // CompositionEvent_h diff --git a/Source/WebCore/dom/CompositionEvent.idl b/Source/WebCore/dom/CompositionEvent.idl new file mode 100644 index 0000000..28d6625 --- /dev/null +++ b/Source/WebCore/dom/CompositionEvent.idl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 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 CompositionEvent : UIEvent { + + readonly attribute DOMString data; + + void initCompositionEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in DOMWindow viewArg, + in DOMString dataArg); + + }; + +} diff --git a/Source/WebCore/dom/ContainerNode.cpp b/Source/WebCore/dom/ContainerNode.cpp new file mode 100644 index 0000000..bc881c6 --- /dev/null +++ b/Source/WebCore/dom/ContainerNode.cpp @@ -0,0 +1,1076 @@ +/* + * 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, 2009 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 "BeforeLoadEvent.h" +#include "MemoryCache.h" +#include "ContainerNodeAlgorithms.h" +#include "DeleteButtonController.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "FloatRect.h" +#include "Frame.h" +#include "FrameView.h" +#include "InlineTextBox.h" +#include "InspectorInstrumentation.h" +#include "MutationEvent.h" +#include "ResourceLoadScheduler.h" +#include "Page.h" +#include "RenderBox.h" +#include "RenderTheme.h" +#include "RootInlineBox.h" +#include <wtf/CurrentTime.h> +#include <wtf/Vector.h> + +namespace WebCore { + +static void notifyChildInserted(Node*); +static void dispatchChildInsertionEvents(Node*); +static void dispatchChildRemovalEvents(Node*); + +typedef Vector<std::pair<NodeCallback, RefPtr<Node> > > NodeCallbackQueue; +typedef Vector<RefPtr<Node>, 1> NodeVector; +static NodeCallbackQueue* s_postAttachCallbackQueue; + +static size_t s_attachDepth; +static bool s_shouldReEnableMemoryCacheCallsAfterAttach; + +static void collectTargetNodes(Node* node, NodeVector& nodes) +{ + if (node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE) { + nodes.append(node); + return; + } + + for (Node* child = node->firstChild(); child; child = child->nextSibling()) + nodes.append(child); +} + +void ContainerNode::removeAllChildren() +{ + removeAllChildrenInContainer<Node, ContainerNode>(this); +} + +void ContainerNode::takeAllChildrenFrom(ContainerNode* oldParent) +{ + NodeVector children; + for (Node* child = oldParent->firstChild(); child; child = child->nextSibling()) + children.append(child); + oldParent->removeAllChildren(); + + for (unsigned i = 0; i < children.size(); ++i) { + ExceptionCode ec = 0; + if (children[i]->attached()) + children[i]->detach(); + // FIXME: We need a no mutation event version of adoptNode. + RefPtr<Node> child = document()->adoptNode(children[i].release(), ec); + ASSERT(!ec); + parserAddChild(child.get()); + if (attached() && !child->attached()) + child->attach(); + } +} + +ContainerNode::~ContainerNode() +{ + removeAllChildren(); +} + +bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec, bool shouldLazyAttach) +{ + // Check that this node is not "floating". + // If it is, it can be deleted as a side effect of sending mutation events. + ASSERT(refCount() || parentNode()); + + ec = 0; + + // insertBefore(node, 0) is equivalent to appendChild(node) + if (!refChild) + return appendChild(newChild, ec, shouldLazyAttach); + + // 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; + } + + NodeVector targets; + collectTargetNodes(newChild.get(), targets); + if (targets.isEmpty()) + 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> refChildPreviousSibling = refChild->previousSibling(); + for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { + Node* child = it->get(); + + // If child is already present in the tree, first remove it from the old location. + if (ContainerNode* oldParent = child->parentNode()) + oldParent->removeChild(child, 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). + + // 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; + +#if ENABLE(INSPECTOR) + InspectorInstrumentation::willInsertDOMNode(document(), child, this); +#endif + + insertBeforeCommon(next.get(), child); + + // Send notification about the children change. + childrenChanged(false, refChildPreviousSibling.get(), next.get(), 1); + notifyChildInserted(child); + + // Add child to the rendering tree. + if (attached() && !child->attached() && child->parentNode() == this) { + if (shouldLazyAttach) + child->lazyAttach(); + else + child->attach(); + } + + // Now that the child is attached to the render tree, dispatch + // the relevant mutation events. + dispatchChildInsertionEvents(child); + } + + dispatchSubtreeModifiedEvent(); + return true; +} + +void ContainerNode::insertBeforeCommon(Node* nextChild, Node* newChild) +{ + ASSERT(newChild); + ASSERT(!newChild->parentNode()); // Use insertBefore if you need to handle reparenting (and want DOM mutation events). + ASSERT(!newChild->nextSibling()); + ASSERT(!newChild->previousSibling()); + + forbidEventDispatch(); + Node* prev = nextChild->previousSibling(); + ASSERT(m_lastChild != prev); + nextChild->setPreviousSibling(newChild); + if (prev) { + ASSERT(m_firstChild != nextChild); + ASSERT(prev->nextSibling() == nextChild); + prev->setNextSibling(newChild); + } else { + ASSERT(m_firstChild == nextChild); + m_firstChild = newChild; + } + newChild->setParent(this); + newChild->setPreviousSibling(prev); + newChild->setNextSibling(nextChild); + allowEventDispatch(); +} + +void ContainerNode::parserInsertBefore(PassRefPtr<Node> newChild, Node* nextChild) +{ + ASSERT(newChild); + ASSERT(nextChild); + ASSERT(nextChild->parentNode() == this); + + NodeVector targets; + collectTargetNodes(newChild.get(), targets); + if (targets.isEmpty()) + return; + + if (nextChild->previousSibling() == newChild || nextChild == newChild) // nothing to do + return; + + RefPtr<Node> next = nextChild; + RefPtr<Node> nextChildPreviousSibling = nextChild->previousSibling(); + for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { + Node* child = it->get(); + +#if ENABLE(INSPECTOR) + InspectorInstrumentation::willInsertDOMNode(document(), child, this); +#endif + + insertBeforeCommon(next.get(), child); + + childrenChanged(true, nextChildPreviousSibling.get(), nextChild, 1); + notifyChildInserted(child); + } +} + +bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec, bool shouldLazyAttach) +{ + // Check that this node is not "floating". + // If it is, it can be deleted as a side effect of sending mutation events. + ASSERT(refCount() || parentNode()); + + 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) + 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 (ContainerNode* oldParent = child->parentNode()) + oldParent->removeChild(child.get(), ec); + if (ec) + return false; + + // 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; + + ASSERT(!child->nextSibling()); + ASSERT(!child->previousSibling()); + +#if ENABLE(INSPECTOR) + InspectorInstrumentation::willInsertDOMNode(document(), child.get(), this); +#endif + + // 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(); + + childrenChanged(false, prev.get(), next, 1); + notifyChildInserted(child.get()); + + // Add child to the rendering tree + if (attached() && !child->attached() && child->parentNode() == this) { + if (shouldLazyAttach) + child->lazyAttach(); + else + child->attach(); + } + + // Now that the child is attached to the render tree, dispatch + // the relevant mutation events. + dispatchChildInsertionEvents(child.get()); + + prev = child; + child = nextChild.release(); + } + + dispatchSubtreeModifiedEvent(); + return true; +} + +void ContainerNode::willRemove() +{ + NodeVector nodes; + for (Node* n = m_lastChild; n; n = n->previousSibling()) + nodes.append(n); + for (; nodes.size(); nodes.removeLast()) + nodes.last().get()->willRemove(); + Node::willRemove(); +} + +static void willRemoveChild(Node* child) +{ + // update auxiliary doc info (e.g. iterators) to note that node is being removed + child->document()->nodeWillBeRemoved(child); + child->document()->incDOMTreeVersion(); + + // fire removed from document mutation events. + dispatchChildRemovalEvents(child); + child->willRemove(); +} + +static void willRemoveChildren(ContainerNode* container) +{ + container->document()->nodeChildrenWillBeRemoved(container); + container->document()->incDOMTreeVersion(); + + NodeVector children; + for (Node* n = container->firstChild(); n; n = n->nextSibling()) + children.append(n); + + for (NodeVector::const_iterator it = children.begin(); it != children.end(); it++) { + Node* child = it->get(); + // fire removed from document mutation events. + dispatchChildRemovalEvents(child); + child->willRemove(); + } +} + +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() || parentNode()); + + 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; + willRemoveChild(child.get()); + + // 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()); + + // Events fired when blurring currently focused node might have moved this + // child into a different parent. + if (child->parentNode() != this) { + ec = NOT_FOUND_ERR; + 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). + + Node* prev = child->previousSibling(); + Node* next = child->nextSibling(); + removeBetween(prev, next, child.get()); + + // Dispatch post-removal mutation events + childrenChanged(false, prev, next, -1); + dispatchSubtreeModifiedEvent(); + + if (child->inDocument()) + child->removedFromDocument(); + else + child->removedFromTree(true); + + return child; +} + +void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node* oldChild) +{ + ASSERT(oldChild); + ASSERT(oldChild->parentNode() == this); + + forbidEventDispatch(); + + // Remove from rendering tree + if (oldChild->attached()) + oldChild->detach(); + + if (nextChild) + nextChild->setPreviousSibling(previousChild); + if (previousChild) + previousChild->setNextSibling(nextChild); + if (m_firstChild == oldChild) + m_firstChild = nextChild; + if (m_lastChild == oldChild) + m_lastChild = previousChild; + + oldChild->setPreviousSibling(0); + oldChild->setNextSibling(0); + oldChild->setParent(0); + + allowEventDispatch(); +} + +void ContainerNode::parserRemoveChild(Node* oldChild) +{ + ASSERT(oldChild); + ASSERT(oldChild->parentNode() == this); + + Node* prev = oldChild->previousSibling(); + Node* next = oldChild->nextSibling(); + + removeBetween(prev, next, oldChild); + + childrenChanged(true, prev, next, -1); + if (oldChild->inDocument()) + oldChild->removedFromDocument(); + else + oldChild->removedFromTree(true); +} + +// this differs from other remove functions because it forcibly removes all the children, +// regardless of read-only status or event exceptions, e.g. +void ContainerNode::removeChildren() +{ + if (!m_firstChild) + return; + + // The container node can be removed from event handlers. + RefPtr<ContainerNode> protect(this); + + // Do any prep work needed before actually starting to detach + // and remove... e.g. stop loading frames, fire unload events. + willRemoveChildren(protect.get()); + + // exclude this node when looking for removed focusedNode since only children will be removed + document()->removeFocusedNodeOfSubtree(this, true); + + forbidEventDispatch(); + Vector<RefPtr<Node> > removedChildren; + while (RefPtr<Node> n = m_firstChild) { + Node* next = n->nextSibling(); + + // Remove the node from the tree before calling detach or removedFromDocument (4427024, 4129744). + // removeChild() does this after calling detach(). There is no explanation for + // this discrepancy between removeChild() and its optimized version removeChildren(). + n->setPreviousSibling(0); + n->setNextSibling(0); + n->setParent(0); + + m_firstChild = next; + if (n == m_lastChild) + m_lastChild = 0; + + if (n->attached()) + n->detach(); + + removedChildren.append(n.release()); + } + allowEventDispatch(); + + size_t removedChildrenCount = removedChildren.size(); + + // Dispatch a single post-removal mutation event denoting a modified subtree. + childrenChanged(false, 0, 0, -static_cast<int>(removedChildrenCount)); + dispatchSubtreeModifiedEvent(); + + for (size_t i = 0; i < removedChildrenCount; ++i) { + Node* removedChild = removedChildren[i].get(); + if (removedChild->inDocument()) + removedChild->removedFromDocument(); + // removeChild() calls removedFromTree(true) if the child was not in the + // document. There is no explanation for this discrepancy between removeChild() + // and its optimized version removeChildren(). + } +} + +bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bool shouldLazyAttach) +{ + // Check that this node is not "floating". + // If it is, it can be deleted as a side effect of sending mutation events. + ASSERT(refCount() || parentNode()); + + ec = 0; + + // Make sure adding the new child is ok + checkAddChild(newChild.get(), ec); + if (ec) + return false; + + if (newChild == m_lastChild) // nothing to do + return newChild; + + NodeVector targets; + collectTargetNodes(newChild.get(), targets); + if (targets.isEmpty()) + return true; + + // Now actually add the child(ren) + RefPtr<Node> prev = lastChild(); + for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { + Node* child = it->get(); + // If child is already present in the tree, first remove it + if (ContainerNode* oldParent = child->parentNode()) { + oldParent->removeChild(child, ec); + if (ec) + return false; + + // 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; + } + +#if ENABLE(INSPECTOR) + InspectorInstrumentation::willInsertDOMNode(document(), child, this); +#endif + + // Append child to the end of the list + forbidEventDispatch(); + child->setParent(this); + if (m_lastChild) { + child->setPreviousSibling(m_lastChild); + m_lastChild->setNextSibling(child); + } else + m_firstChild = child; + m_lastChild = child; + allowEventDispatch(); + + // Send notification about the children change. + childrenChanged(false, prev.get(), 0, 1); + notifyChildInserted(child); + + // Add child to the rendering tree + if (attached() && !child->attached() && child->parentNode() == this) { + if (shouldLazyAttach) + child->lazyAttach(); + else + child->attach(); + } + + // Now that the child is attached to the render tree, dispatch + // the relevant mutation events. + dispatchChildInsertionEvents(child); + prev = child; + } + + dispatchSubtreeModifiedEvent(); + return true; +} + +void ContainerNode::parserAddChild(PassRefPtr<Node> newChild) +{ + ASSERT(newChild); + ASSERT(!newChild->parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events). + +#if ENABLE(INSPECTOR) + InspectorInstrumentation::willInsertDOMNode(document(), newChild.get(), this); +#endif + + forbidEventDispatch(); + Node* last = m_lastChild; + // FIXME: This method should take a PassRefPtr. + appendChildToContainer<Node, ContainerNode>(newChild.get(), this); + allowEventDispatch(); + + // FIXME: Why doesn't this use notifyChildInserted(newChild) instead? + document()->incDOMTreeVersion(); + if (inDocument()) + newChild->insertedIntoDocument(); + childrenChanged(true, last, 0, 1); +} + +void ContainerNode::deprecatedParserAddChild(PassRefPtr<Node> node) +{ + parserAddChild(node); +} + +void ContainerNode::suspendPostAttachCallbacks() +{ + if (!s_attachDepth) { + ASSERT(!s_shouldReEnableMemoryCacheCallsAfterAttach); + if (Page* page = document()->page()) { + if (page->areMemoryCacheClientCallsEnabled()) { + page->setMemoryCacheClientCallsEnabled(false); + s_shouldReEnableMemoryCacheCallsAfterAttach = true; + } + } + resourceLoadScheduler()->suspendPendingRequests(); + } + ++s_attachDepth; +} + +void ContainerNode::resumePostAttachCallbacks() +{ + if (s_attachDepth == 1) { + if (s_postAttachCallbackQueue) + dispatchPostAttachCallbacks(); + if (s_shouldReEnableMemoryCacheCallsAfterAttach) { + s_shouldReEnableMemoryCacheCallsAfterAttach = false; + if (Page* page = document()->page()) + page->setMemoryCacheClientCallsEnabled(true); + } + resourceLoadScheduler()->resumePendingRequests(); + } + --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)); +} + +bool ContainerNode::postAttachCallbacksAreSuspended() +{ + return s_attachDepth; +} + +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() +{ + for (Node* child = m_firstChild; child; child = child->nextSibling()) + child->attach(); + Node::attach(); +} + +void ContainerNode::detach() +{ + for (Node* child = m_firstChild; child; child = child->nextSibling()) + child->detach(); + clearChildNeedsStyleRecalc(); + Node::detach(); +} + +void ContainerNode::insertedIntoDocument() +{ + Node::insertedIntoDocument(); + insertedIntoTree(false); + for (Node* child = m_firstChild; child; child = child->nextSibling()) + child->insertedIntoDocument(); +} + +void ContainerNode::removedFromDocument() +{ + Node::removedFromDocument(); + if (document()->cssTarget() == this) + document()->setCSSTarget(0); + clearInDocument(); + removedFromTree(false); + for (Node* child = m_firstChild; child; child = child->nextSibling()) + child->removedFromDocument(); +} + +void ContainerNode::insertedIntoTree(bool deep) +{ + if (!deep) + return; + for (Node* child = m_firstChild; child; child = child->nextSibling()) + child->insertedIntoTree(true); +} + +void ContainerNode::removedFromTree(bool deep) +{ + if (!deep) + return; + for (Node* child = m_firstChild; child; child = child->nextSibling()) + child->removedFromTree(true); +} + +void ContainerNode::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + Node::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + if (!changedByParser && childCountDelta) + document()->nodeChildrenChanged(this); + if (document()->hasNodeListCaches()) + notifyNodeListsChildrenChanged(); +} + +void ContainerNode::cloneChildNodes(ContainerNode *clone) +{ + // disable the delete button so it's elements are not serialized into the markup + bool isEditorEnabled = document()->frame() && document()->frame()->editor()->canEdit(); + if (isEditorEnabled) + document()->frame()->editor()->deleteButtonController()->disable(); + ExceptionCode ec = 0; + for (Node* n = firstChild(); n && !ec; n = n->nextSibling()) + clone->appendChild(n->cloneNode(true), ec); + if (isEditorEnabled && document()->frame()) + document()->frame()->editor()->deleteButtonController()->enable(); +} + +bool ContainerNode::getUpperLeftCorner(FloatPoint& point) const +{ + if (!renderer()) + return false; + // What is this code really trying to do? + RenderObject *o = renderer(); + RenderObject *p = o; + + if (!o->isInline() || o->isReplaced()) { + point = o->localToAbsolute(FloatPoint(), false, true); + 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; + } + ASSERT(o); + + if (!o->isInline() || o->isReplaced()) { + point = o->localToAbsolute(FloatPoint(), false, true); + return true; + } + + if (p->node() && p->node() == this && o->isText() && !o->isBR() && !toRenderText(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()) { + point = FloatPoint(); + if (o->isText() && toRenderText(o)->firstTextBox()) { + point.move(toRenderText(o)->linesBoundingBox().x(), + toRenderText(o)->firstTextBox()->root()->lineTop()); + } else if (o->isBox()) { + RenderBox* box = toRenderBox(o); + point.move(box->x(), box->y()); + } + point = o->container()->localToAbsolute(point, false, true); + 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. FIXME: who said anything about scrolling? + if (!o && document()->view()) { + point = FloatPoint(0, document()->view()->contentsHeight()); + return true; + } + return false; +} + +// FIXME: This doesn't work correctly with transforms. +bool ContainerNode::getLowerRightCorner(FloatPoint& point) const +{ + if (!renderer()) + return false; + + RenderObject* o = renderer(); + if (!o->isInline() || o->isReplaced()) { + RenderBox* box = toRenderBox(o); + point = o->localToAbsolute(FloatPoint(), false, true); + point.move(box->width(), box->height()); + 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; + } + ASSERT(o); + if (o->isText() || o->isReplaced()) { + point = FloatPoint(); + if (o->isText()) { + RenderText* text = toRenderText(o); + IntRect linesBox = text->linesBoundingBox(); + if (!linesBox.x() && !linesBox.width() && !linesBox.y() && !linesBox.height()) + continue; + point.move(linesBox.x() + linesBox.width(), linesBox.y() + linesBox.height()); + } else { + RenderBox* box = toRenderBox(o); + point.move(box->x() + box->width(), box->y() + box->height()); + } + point = o->container()->localToAbsolute(point, false, true); + return true; + } + } + return true; +} + +IntRect ContainerNode::getRect() const +{ + FloatPoint upperLeft, lowerRight; + bool foundUpperLeft = getUpperLeftCorner(upperLeft); + bool foundLowerRight = getLowerRightCorner(lowerRight); + + // 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) + lowerRight = upperLeft; + else + upperLeft = lowerRight; + } + + lowerRight.setX(max(upperLeft.x(), lowerRight.x())); + lowerRight.setY(max(upperLeft.y(), lowerRight.y())); + + return enclosingIntRect(FloatRect(upperLeft, lowerRight - upperLeft)); +} + +void ContainerNode::setFocus(bool received) +{ + if (focused() == received) + return; + + Node::setFocus(received); + + // note that we need to recalc the style + setNeedsStyleRecalc(); +} + +void ContainerNode::setActive(bool down, bool pause) +{ + if (down == active()) return; + + Node::setActive(down); + + // note that we need to recalc the style + // FIXME: Move to Element + if (renderer()) { + bool reactsToPress = renderer()->style()->affectedByActiveRules(); + if (reactsToPress) + setNeedsStyleRecalc(); + if (renderer() && renderer()->style()->hasAppearance()) { + if (renderer()->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::updateStyleForAllDocuments(); + // 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; + + Node::setHovered(over); + + // note that we need to recalc the style + // FIXME: Move to Element + if (renderer()) { + if (renderer()->style()->affectedByHoverRules()) + setNeedsStyleRecalc(); + if (renderer() && renderer()->style()->hasAppearance()) + renderer()->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 notifyChildInserted(Node* child) +{ + ASSERT(!eventDispatchForbidden()); + +#if ENABLE(INSPECTOR) + InspectorInstrumentation::didInsertDOMNode(child->document(), child); +#endif + + RefPtr<Node> c = child; + RefPtr<Document> document = child->document(); + + Node* parentOrHostNode = c->parentOrHostNode(); + if (parentOrHostNode && parentOrHostNode->inDocument()) + c->insertedIntoDocument(); + else + c->insertedIntoTree(true); + + document->incDOMTreeVersion(); +} + +static void dispatchChildInsertionEvents(Node* child) +{ + ASSERT(!eventDispatchForbidden()); + + RefPtr<Node> c = child; + RefPtr<Document> document = child->document(); + + if (c->parentNode() && document->hasListenerType(Document::DOMNODEINSERTED_LISTENER)) + c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, c->parentNode())); + + // dispatch the DOMNodeInsertedIntoDocument event to all descendants + if (c->inDocument() && document->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) { + for (; c; c = c->traverseNextNode(child)) + c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeInsertedIntoDocumentEvent, false)); + } +} + +static void dispatchChildRemovalEvents(Node* child) +{ + ASSERT(!eventDispatchForbidden()); + +#if ENABLE(INSPECTOR) + InspectorInstrumentation::willRemoveDOMNode(child->document(), child); +#endif + + RefPtr<Node> c = child; + RefPtr<Document> document = child->document(); + + // dispatch pre-removal mutation events + if (c->parentNode() && document->hasListenerType(Document::DOMNODEREMOVED_LISTENER)) + c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, true, c->parentNode())); + + // dispatch the DOMNodeRemovedFromDocument event to all descendants + if (c->inDocument() && document->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) { + for (; c; c = c->traverseNextNode(child)) + c->dispatchScopedEvent(MutationEvent::create(eventNames().DOMNodeRemovedFromDocumentEvent, false)); + } +} + +bool ContainerNode::dispatchBeforeLoadEvent(const String& sourceURL) +{ + if (!document()->hasListenerType(Document::BEFORELOAD_LISTENER)) + return true; + + RefPtr<ContainerNode> protector(this); + RefPtr<BeforeLoadEvent> beforeLoadEvent = BeforeLoadEvent::create(sourceURL); + dispatchEvent(beforeLoadEvent.get()); + return !beforeLoadEvent->defaultPrevented(); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ContainerNode.h b/Source/WebCore/dom/ContainerNode.h new file mode 100644 index 0000000..76eb1bd --- /dev/null +++ b/Source/WebCore/dom/ContainerNode.h @@ -0,0 +1,175 @@ +/* + * 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, 2009, 2010 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 "Node.h" + +namespace WebCore { + +class FloatPoint; + +typedef void (*NodeCallback)(Node*); + +namespace Private { + template<class GenericNode, class GenericNodeContainer> + void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer* container); +}; + +class ContainerNode : public Node { +public: + virtual ~ContainerNode(); + + Node* firstChild() const { return m_firstChild; } + Node* lastChild() const { return m_lastChild; } + + bool insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode&, bool shouldLazyAttach = false); + bool replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode&, bool shouldLazyAttach = false); + bool removeChild(Node* child, ExceptionCode&); + bool appendChild(PassRefPtr<Node> newChild, ExceptionCode&, bool shouldLazyAttach = false); + + // These methods are only used during parsing. + // They don't send DOM mutation events or handle reparenting. + // However, arbitrary code may be run by beforeload handlers. + void parserAddChild(PassRefPtr<Node>); + void parserRemoveChild(Node*); + void parserInsertBefore(PassRefPtr<Node> newChild, Node* refChild); + + bool hasChildNodes() const { return m_firstChild; } + 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); + unsigned childNodeCount() const; + 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); + + // FIXME: It's not good to have two functions with such similar names, especially public functions. + // How do removeChildren and removeAllChildren differ? + void removeChildren(); + void removeAllChildren(); + void takeAllChildrenFrom(ContainerNode*); + + void cloneChildNodes(ContainerNode* clone); + + bool dispatchBeforeLoadEvent(const String& sourceURL); + + static void queuePostAttachCallback(NodeCallback, Node*); + static bool postAttachCallbacksAreSuspended(); + +protected: + ContainerNode(Document*, ConstructionType = CreateContainer); + + void suspendPostAttachCallbacks(); + void resumePostAttachCallbacks(); + + template<class GenericNode, class GenericNodeContainer> + friend void appendChildToContainer(GenericNode* child, GenericNodeContainer* container); + + template<class GenericNode, class GenericNodeContainer> + friend void Private::addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer* container); + + void setFirstChild(Node* child) { m_firstChild = child; } + void setLastChild(Node* child) { m_lastChild = child; } + +private: + // Never call this function directly. If you're trying to call this + // function, your code is either wrong or you're supposed to call + // parserAddChild. Please do not call parserAddChild unless you are the + // parser! + virtual void deprecatedParserAddChild(PassRefPtr<Node>); + + void removeBetween(Node* previousChild, Node* nextChild, Node* oldChild); + void insertBeforeCommon(Node* nextChild, Node* oldChild); + + static void dispatchPostAttachCallbacks(); + + bool getUpperLeftCorner(FloatPoint&) const; + bool getLowerRightCorner(FloatPoint&) const; + + Node* m_firstChild; + Node* m_lastChild; +}; + +inline ContainerNode* toContainerNode(Node* node) +{ + ASSERT(!node || node->isContainerNode()); + return static_cast<ContainerNode*>(node); +} + +inline const ContainerNode* toContainerNode(const Node* node) +{ + ASSERT(!node || node->isContainerNode()); + return static_cast<const ContainerNode*>(node); +} + +// This will catch anyone doing an unnecessary cast. +void toContainerNode(const ContainerNode*); + +inline ContainerNode::ContainerNode(Document* document, ConstructionType type) + : Node(document, type) + , m_firstChild(0) + , m_lastChild(0) +{ +} + +inline unsigned Node::childNodeCount() const +{ + if (!isContainerNode()) + return 0; + return toContainerNode(this)->childNodeCount(); +} + +inline Node* Node::childNode(unsigned index) const +{ + if (!isContainerNode()) + return 0; + return toContainerNode(this)->childNode(index); +} + +inline Node* Node::firstChild() const +{ + if (!isContainerNode()) + return 0; + return toContainerNode(this)->firstChild(); +} + +inline Node* Node::lastChild() const +{ + if (!isContainerNode()) + return 0; + return toContainerNode(this)->lastChild(); +} + +} // namespace WebCore + +#endif // ContainerNode_h diff --git a/Source/WebCore/dom/ContainerNodeAlgorithms.h b/Source/WebCore/dom/ContainerNodeAlgorithms.h new file mode 100644 index 0000000..55095a7 --- /dev/null +++ b/Source/WebCore/dom/ContainerNodeAlgorithms.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * (C) 2008 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 ContainerNodeAlgorithms_h +#define ContainerNodeAlgorithms_h + +#include <wtf/Assertions.h> + +namespace WebCore { + +class Node; + +namespace Private { + + template<class GenericNode, class GenericNodeContainer> + void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer* container); + +}; + +// Helper functions for TreeShared-derived classes, which have a 'Node' style interface +// This applies to 'ContainerNode' and 'SVGElementInstance' +template<class GenericNode, class GenericNodeContainer> +void removeAllChildrenInContainer(GenericNodeContainer* container) +{ + // List of nodes to be deleted. + GenericNode* head = 0; + GenericNode* tail = 0; + + Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, container); + + GenericNode* n; + GenericNode* 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()) + Private::addChildNodesToDeletionQueue<GenericNode, GenericNodeContainer>(head, tail, static_cast<GenericNodeContainer*>(n)); + + delete n; + } +} + +template<class GenericNode, class GenericNodeContainer> +void appendChildToContainer(GenericNode* child, GenericNodeContainer* container) +{ + child->setParent(container); + + GenericNode* lastChild = container->lastChild(); + if (lastChild) { + child->setPreviousSibling(lastChild); + lastChild->setNextSibling(child); + } else + container->setFirstChild(child); + + container->setLastChild(child); +} + +// Helper methods for removeAllChildrenInContainer, hidden from WebCore namespace +namespace Private { + + template<class GenericNode, bool dispatchRemovalNotification> + struct NodeRemovalDispatcher { + static void dispatch(GenericNode*) + { + // no-op, by default + } + }; + + template<class GenericNode> + struct NodeRemovalDispatcher<GenericNode, true> { + static void dispatch(GenericNode* node) + { + if (node->inDocument()) + node->removedFromDocument(); + } + }; + + template<class GenericNode> + struct ShouldDispatchRemovalNotification { + static const bool value = false; + }; + + template<> + struct ShouldDispatchRemovalNotification<Node> { + static const bool value = true; + }; + + template<class GenericNode, class GenericNodeContainer> + void addChildNodesToDeletionQueue(GenericNode*& head, GenericNode*& tail, GenericNodeContainer* container) + { + // We have to tell all children that their parent has died. + GenericNode* next = 0; + for (GenericNode* 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 + NodeRemovalDispatcher<GenericNode, ShouldDispatchRemovalNotification<GenericNode>::value>::dispatch(n); + } + + container->setFirstChild(0); + container->setLastChild(0); + } +}; + +} // namespace WebCore + +#endif // ContainerNodeAlgorithms_h diff --git a/Source/WebCore/dom/CrossThreadTask.h b/Source/WebCore/dom/CrossThreadTask.h new file mode 100644 index 0000000..ee05bec --- /dev/null +++ b/Source/WebCore/dom/CrossThreadTask.h @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2009-2010 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 CrossThreadTask_h +#define CrossThreadTask_h + +#include "CrossThreadCopier.h" +#include "ScriptExecutionContext.h" +#include <memory> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/TypeTraits.h> + +namespace WebCore { + +// Traits for the CrossThreadTask. +template<typename T> struct CrossThreadTaskTraits { + typedef const T& ParamType; +}; + +template<typename T> struct CrossThreadTaskTraits<T*> { + typedef T* ParamType; +}; + +template<typename T> struct CrossThreadTaskTraits<PassRefPtr<T> > { + typedef PassRefPtr<T> ParamType; +}; + +template<typename T> struct CrossThreadTaskTraits<PassOwnPtr<T> > { + typedef PassOwnPtr<T> ParamType; +}; + +template<typename P1, typename MP1> +class CrossThreadTask1 : public ScriptExecutionContext::Task { +public: + typedef void (*Method)(ScriptExecutionContext*, MP1); + typedef CrossThreadTask1<P1, MP1> CrossThreadTask; + typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; + + static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1) + { + return new CrossThreadTask(method, parameter1); + } + +private: + CrossThreadTask1(Method method, Param1 parameter1) + : m_method(method) + , m_parameter1(parameter1) + { + } + + virtual void performTask(ScriptExecutionContext* context) + { + (*m_method)(context, m_parameter1); + } + +private: + Method m_method; + P1 m_parameter1; +}; + +template<typename P1, typename MP1, typename P2, typename MP2> +class CrossThreadTask2 : public ScriptExecutionContext::Task { +public: + typedef void (*Method)(ScriptExecutionContext*, MP1, MP2); + typedef CrossThreadTask2<P1, MP1, P2, MP2> CrossThreadTask; + typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; + typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; + + static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2) + { + return new CrossThreadTask(method, parameter1, parameter2); + } + +private: + CrossThreadTask2(Method method, Param1 parameter1, Param2 parameter2) + : m_method(method) + , m_parameter1(parameter1) + , m_parameter2(parameter2) + { + } + + virtual void performTask(ScriptExecutionContext* context) + { + (*m_method)(context, m_parameter1, m_parameter2); + } + +private: + Method m_method; + P1 m_parameter1; + P2 m_parameter2; +}; + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3> +class CrossThreadTask3 : public ScriptExecutionContext::Task { +public: + typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3); + typedef CrossThreadTask3<P1, MP1, P2, MP2, P3, MP3> CrossThreadTask; + typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; + typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; + typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; + + static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3) + { + return new CrossThreadTask(method, parameter1, parameter2, parameter3); + } + +private: + CrossThreadTask3(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3) + : m_method(method) + , m_parameter1(parameter1) + , m_parameter2(parameter2) + , m_parameter3(parameter3) + { + } + + virtual void performTask(ScriptExecutionContext* context) + { + (*m_method)(context, m_parameter1, m_parameter2, m_parameter3); + } + +private: + Method m_method; + P1 m_parameter1; + P2 m_parameter2; + P3 m_parameter3; +}; + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4> +class CrossThreadTask4 : public ScriptExecutionContext::Task { +public: + typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4); + typedef CrossThreadTask4<P1, MP1, P2, MP2, P3, MP3, P4, MP4> CrossThreadTask; + typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; + typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; + typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; + typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; + + static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4) + { + return new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4); + } + +private: + CrossThreadTask4(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4) + : m_method(method) + , m_parameter1(parameter1) + , m_parameter2(parameter2) + , m_parameter3(parameter3) + , m_parameter4(parameter4) + { + } + + virtual void performTask(ScriptExecutionContext* context) + { + (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4); + } + +private: + Method m_method; + P1 m_parameter1; + P2 m_parameter2; + P3 m_parameter3; + P4 m_parameter4; +}; + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5> +class CrossThreadTask5 : public ScriptExecutionContext::Task { +public: + typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5); + typedef CrossThreadTask5<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5> CrossThreadTask; + typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; + typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; + typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; + typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; + typedef typename CrossThreadTaskTraits<P5>::ParamType Param5; + + static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5) + { + return new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4, parameter5); + } + +private: + CrossThreadTask5(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5) + : m_method(method) + , m_parameter1(parameter1) + , m_parameter2(parameter2) + , m_parameter3(parameter3) + , m_parameter4(parameter4) + , m_parameter5(parameter5) + { + } + + virtual void performTask(ScriptExecutionContext* context) + { + (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4, m_parameter5); + } + +private: + Method m_method; + P1 m_parameter1; + P2 m_parameter2; + P3 m_parameter3; + P4 m_parameter4; + P5 m_parameter5; +}; + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6> +class CrossThreadTask6 : public ScriptExecutionContext::Task { +public: + typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6); + typedef CrossThreadTask6<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5, P6, MP6> CrossThreadTask; + typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; + typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; + typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; + typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; + typedef typename CrossThreadTaskTraits<P5>::ParamType Param5; + typedef typename CrossThreadTaskTraits<P6>::ParamType Param6; + + static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6) + { + return new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4, parameter5, parameter6); + } + +private: + CrossThreadTask6(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6) + : m_method(method) + , m_parameter1(parameter1) + , m_parameter2(parameter2) + , m_parameter3(parameter3) + , m_parameter4(parameter4) + , m_parameter5(parameter5) + , m_parameter6(parameter6) + { + } + + virtual void performTask(ScriptExecutionContext* context) + { + (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4, m_parameter5, m_parameter6); + } + +private: + Method m_method; + P1 m_parameter1; + P2 m_parameter2; + P3 m_parameter3; + P4 m_parameter4; + P5 m_parameter5; + P6 m_parameter6; +}; + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6, typename P7, typename MP7> +class CrossThreadTask7 : public ScriptExecutionContext::Task { +public: + typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6, MP7); + typedef CrossThreadTask7<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5, P6, MP6, P7, MP7> CrossThreadTask; + typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; + typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; + typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; + typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; + typedef typename CrossThreadTaskTraits<P5>::ParamType Param5; + typedef typename CrossThreadTaskTraits<P6>::ParamType Param6; + typedef typename CrossThreadTaskTraits<P7>::ParamType Param7; + + static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6, Param7 parameter7) + { + return new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4, parameter5, parameter6, parameter7); + } + +private: + CrossThreadTask7(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6, Param7 parameter7) + : m_method(method) + , m_parameter1(parameter1) + , m_parameter2(parameter2) + , m_parameter3(parameter3) + , m_parameter4(parameter4) + , m_parameter5(parameter5) + , m_parameter6(parameter6) + , m_parameter7(parameter7) + { + } + + virtual void performTask(ScriptExecutionContext* context) + { + (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4, m_parameter5, m_parameter6, m_parameter7); + } + +private: + Method m_method; + P1 m_parameter1; + P2 m_parameter2; + P3 m_parameter3; + P4 m_parameter4; + P5 m_parameter5; + P6 m_parameter6; + P7 m_parameter7; +}; + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6, typename P7, typename MP7, typename P8, typename MP8> +class CrossThreadTask8 : public ScriptExecutionContext::Task { +public: + typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6, MP7, MP8); + typedef CrossThreadTask8<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5, P6, MP6, P7, MP7, P8, MP8> CrossThreadTask; + typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; + typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; + typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; + typedef typename CrossThreadTaskTraits<P4>::ParamType Param4; + typedef typename CrossThreadTaskTraits<P5>::ParamType Param5; + typedef typename CrossThreadTaskTraits<P6>::ParamType Param6; + typedef typename CrossThreadTaskTraits<P7>::ParamType Param7; + typedef typename CrossThreadTaskTraits<P8>::ParamType Param8; + + static PassOwnPtr<CrossThreadTask> create(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6, Param7 parameter7, Param8 parameter8) + { + return new CrossThreadTask(method, parameter1, parameter2, parameter3, parameter4, parameter5, parameter6, parameter7, parameter8); + } + +private: + CrossThreadTask8(Method method, Param1 parameter1, Param2 parameter2, Param3 parameter3, Param4 parameter4, Param5 parameter5, Param6 parameter6, Param7 parameter7, Param8 parameter8) + : m_method(method) + , m_parameter1(parameter1) + , m_parameter2(parameter2) + , m_parameter3(parameter3) + , m_parameter4(parameter4) + , m_parameter5(parameter5) + , m_parameter6(parameter6) + , m_parameter7(parameter7) + , m_parameter8(parameter8) + { + } + + virtual void performTask(ScriptExecutionContext* context) + { + (*m_method)(context, m_parameter1, m_parameter2, m_parameter3, m_parameter4, m_parameter5, m_parameter6, m_parameter7, m_parameter8); + } + +private: + Method m_method; + P1 m_parameter1; + P2 m_parameter2; + P3 m_parameter3; + P4 m_parameter4; + P5 m_parameter5; + P6 m_parameter6; + P7 m_parameter7; + P8 m_parameter8; +}; + +template<typename P1, typename MP1> +PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( + void (*method)(ScriptExecutionContext*, MP1), + const P1& parameter1) +{ + return CrossThreadTask1<typename CrossThreadCopier<P1>::Type, MP1>::create( + method, + CrossThreadCopier<P1>::copy(parameter1)); +} + +template<typename P1, typename MP1, typename P2, typename MP2> +PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( + void (*method)(ScriptExecutionContext*, MP1, MP2), + const P1& parameter1, const P2& parameter2) +{ + return CrossThreadTask2<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2>::create( + method, + CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2)); +} + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3> +PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( + void (*method)(ScriptExecutionContext*, MP1, MP2, MP3), + const P1& parameter1, const P2& parameter2, const P3& parameter3) +{ + return CrossThreadTask3<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3>::create( + method, + CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), + CrossThreadCopier<P3>::copy(parameter3)); +} + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4> +PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( + void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4), + const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4) +{ + return CrossThreadTask4<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, + typename CrossThreadCopier<P4>::Type, MP4>::create( + method, + CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), + CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4)); +} + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5> +PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( + void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5), + const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5) +{ + return CrossThreadTask5<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, + typename CrossThreadCopier<P4>::Type, MP4, typename CrossThreadCopier<P5>::Type, MP5>::create( + method, + CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), + CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4), + CrossThreadCopier<P5>::copy(parameter5)); +} + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6> +PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( + void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6), + const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5, const P6& parameter6) +{ + return CrossThreadTask6<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, + typename CrossThreadCopier<P4>::Type, MP4, typename CrossThreadCopier<P5>::Type, MP5, typename CrossThreadCopier<P6>::Type, MP6>::create( + method, + CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), + CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4), + CrossThreadCopier<P5>::copy(parameter5), CrossThreadCopier<P6>::copy(parameter6)); +} + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6, typename P7, typename MP7> +PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( + void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6, MP7), + const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5, const P6& parameter6, const P7& parameter7) +{ + return CrossThreadTask7<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, + typename CrossThreadCopier<P4>::Type, MP4, typename CrossThreadCopier<P5>::Type, MP5, typename CrossThreadCopier<P6>::Type, MP6, + typename CrossThreadCopier<P7>::Type, MP7>::create( + method, + CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), + CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4), + CrossThreadCopier<P5>::copy(parameter5), CrossThreadCopier<P6>::copy(parameter6), + CrossThreadCopier<P7>::copy(parameter7)); +} + +template<typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3, typename P4, typename MP4, typename P5, typename MP5, typename P6, typename MP6, typename P7, typename MP7, typename P8, typename MP8> +PassOwnPtr<ScriptExecutionContext::Task> createCallbackTask( + void (*method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6, MP7, MP8), + const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5, const P6& parameter6, const P7& parameter7, const P8& parameter8) +{ + return CrossThreadTask8<typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3, + typename CrossThreadCopier<P4>::Type, MP4, typename CrossThreadCopier<P5>::Type, MP5, typename CrossThreadCopier<P6>::Type, MP6, + typename CrossThreadCopier<P7>::Type, MP7, typename CrossThreadCopier<P8>::Type, MP8>::create( + method, + CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2), + CrossThreadCopier<P3>::copy(parameter3), CrossThreadCopier<P4>::copy(parameter4), + CrossThreadCopier<P5>::copy(parameter5), CrossThreadCopier<P6>::copy(parameter6), + CrossThreadCopier<P7>::copy(parameter7), CrossThreadCopier<P8>::copy(parameter8)); +} + +} // namespace WebCore + +#endif // CrossThreadTask_h diff --git a/Source/WebCore/dom/CustomEvent.cpp b/Source/WebCore/dom/CustomEvent.cpp new file mode 100644 index 0000000..53e255d --- /dev/null +++ b/Source/WebCore/dom/CustomEvent.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "CustomEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +CustomEvent::CustomEvent() +{ +} + +CustomEvent::~CustomEvent() +{ +} + +void CustomEvent::initCustomEvent(const AtomicString& type, bool canBubble, bool cancelable, ScriptValue detail) +{ + if (dispatched()) + return; + + initEvent(type, canBubble, cancelable); + + m_detail = detail; +} + +bool CustomEvent::isCustomEvent() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/CustomEvent.h b/Source/WebCore/dom/CustomEvent.h new file mode 100644 index 0000000..65c3f04 --- /dev/null +++ b/Source/WebCore/dom/CustomEvent.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 CustomEvent_h +#define CustomEvent_h + +#include "Event.h" +#include "ScriptValue.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +class CustomEvent : public Event { +public: + virtual ~CustomEvent(); + + static PassRefPtr<CustomEvent> create() + { + return adoptRef(new CustomEvent); + } + + void initCustomEvent(const AtomicString& type, bool canBubble, bool cancelable, ScriptValue detail); + + virtual bool isCustomEvent() const; + + ScriptValue detail() const { return m_detail; } + +private: + CustomEvent(); + + ScriptValue m_detail; +}; + +} // namespace WebCore + +#endif // CustomEvent_h diff --git a/Source/WebCore/dom/CustomEvent.idl b/Source/WebCore/dom/CustomEvent.idl new file mode 100644 index 0000000..aa673b8 --- /dev/null +++ b/Source/WebCore/dom/CustomEvent.idl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 { + +#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP + // Introduced in DOM Level 3: + interface CustomEvent : Event { + + readonly attribute DOMObject detail; + + void initCustomEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in DOMObject detailArg); + }; +#endif + +} diff --git a/Source/WebCore/dom/DOMAllInOne.cpp b/Source/WebCore/dom/DOMAllInOne.cpp new file mode 100644 index 0000000..aa85bfb --- /dev/null +++ b/Source/WebCore/dom/DOMAllInOne.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2010 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. + */ + +// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build. + +#include "ActiveDOMObject.cpp" +#include "AsyncScriptRunner.cpp" +#include "Attr.cpp" +#include "Attribute.cpp" +#include "BeforeProcessEvent.cpp" +#include "BeforeTextInsertedEvent.cpp" +#include "BeforeUnloadEvent.cpp" +#include "CDATASection.cpp" +#include "CSSMappedAttributeDeclaration.cpp" +#include "CharacterData.cpp" +#include "CheckedRadioButtons.cpp" +#include "ChildNodeList.cpp" +#include "ClassNodeList.cpp" +#include "ClientRect.cpp" +#include "ClientRectList.cpp" +#include "Clipboard.cpp" +#include "ClipboardEvent.cpp" +#include "Comment.cpp" +#include "CompositionEvent.cpp" +#include "ContainerNode.cpp" +#include "CustomEvent.cpp" +#include "DOMImplementation.cpp" +#include "DOMStringList.cpp" +#include "DOMStringMap.cpp" +#include "DatasetDOMStringMap.cpp" +#include "DecodedDataDocumentParser.cpp" +#include "DeviceMotionController.cpp" +#include "DeviceMotionData.cpp" +#include "DeviceMotionEvent.cpp" +#include "DeviceOrientation.cpp" +#include "DeviceOrientationController.cpp" +#include "DeviceOrientationEvent.cpp" +#include "Document.cpp" +#include "DocumentFragment.cpp" +#include "DocumentMarkerController.cpp" +#include "DocumentParser.cpp" +#include "DocumentType.cpp" +#include "DynamicNodeList.cpp" +#include "EditingText.cpp" +#include "Element.cpp" +#include "EntityReference.cpp" +#include "ErrorEvent.cpp" +#include "Event.cpp" +#include "EventContext.cpp" +#include "EventNames.cpp" +#include "EventQueue.cpp" +#include "EventTarget.cpp" +#include "ExceptionBase.cpp" +#include "ExceptionCode.cpp" +#include "InputElement.cpp" +#include "KeyboardEvent.cpp" +#include "MessageChannel.cpp" +#include "MessageEvent.cpp" +#include "MessagePort.cpp" +#include "MessagePortChannel.cpp" +#include "MouseEvent.cpp" +#include "MouseRelatedEvent.cpp" +#include "MutationEvent.cpp" +#include "NameNodeList.cpp" +#include "NodeFilter.cpp" +#include "NodeFilterCondition.cpp" +#include "NodeIterator.cpp" +#include "Notation.cpp" +#include "OptionElement.cpp" +#include "OptionGroupElement.cpp" +#include "OverflowEvent.cpp" +#include "PageTransitionEvent.cpp" +#include "PendingScript.cpp" +#include "PopStateEvent.cpp" +#include "Position.cpp" +#include "PositionIterator.cpp" +#include "ProcessingInstruction.cpp" +#include "ProgressEvent.cpp" +#include "Range.cpp" +#include "RegisteredEventListener.cpp" +#include "ScopedEventQueue.cpp" +#include "ScriptElement.cpp" +#include "ScriptExecutionContext.cpp" +#include "ScriptableDocumentParser.cpp" +#include "SelectElement.cpp" +#include "SelectorNodeList.cpp" +#include "SpaceSplitString.cpp" +#include "StaticHashSetNodeList.cpp" +#include "StaticNodeList.cpp" +#include "StyleElement.cpp" +#include "StyledElement.cpp" +#include "TagNodeList.cpp" +#include "Text.cpp" +#include "TextEvent.cpp" +#include "Touch.cpp" +#include "TouchEvent.cpp" +#include "TouchList.cpp" +#include "TransformSourceLibxslt.cpp" +#include "Traversal.cpp" +#include "TreeWalker.cpp" +#include "UIEvent.cpp" +#include "UIEventWithKeyState.cpp" +#include "UserGestureIndicator.cpp" +#include "UserTypingGestureIndicator.cpp" +#include "ViewportArguments.cpp" +#include "WebKitAnimationEvent.cpp" +#include "WebKitTransitionEvent.cpp" +#include "WheelEvent.cpp" +#include "WindowEventContext.cpp" +#include "XMLDocumentParser.cpp" +#include "XMLDocumentParserScope.cpp" diff --git a/Source/WebCore/dom/DOMCoreException.h b/Source/WebCore/dom/DOMCoreException.h new file mode 100644 index 0000000..3c89694 --- /dev/null +++ b/Source/WebCore/dom/DOMCoreException.h @@ -0,0 +1,52 @@ +/* + * 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. + * 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 DOMCoreException_h + +#include "ExceptionBase.h" + +namespace WebCore { + + class DOMCoreException : public ExceptionBase { + public: + static PassRefPtr<DOMCoreException> create(const ExceptionCodeDescription& description) + { + return adoptRef(new DOMCoreException(description)); + } + + private: + DOMCoreException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + }; + +} // namespace WebCore + +#endif // DOMCoreException_h diff --git a/Source/WebCore/dom/DOMCoreException.idl b/Source/WebCore/dom/DOMCoreException.idl new file mode 100644 index 0000000..945712d --- /dev/null +++ b/Source/WebCore/dom/DOMCoreException.idl @@ -0,0 +1,79 @@ +/* + * 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 [ + NoStaticTables, + DontCheckEnums + ] DOMCoreException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(LANGUAGE_JAVASCRIPT) && 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; + // Introduced as an XHR extension: + const unsigned short SECURITY_ERR = 18; + // Introduced in HTML5: + const unsigned short NETWORK_ERR = 19; + const unsigned short ABORT_ERR = 20; + const unsigned short URL_MISMATCH_ERR = 21; + const unsigned short QUOTA_EXCEEDED_ERR = 22; + }; + +} diff --git a/Source/WebCore/dom/DOMImplementation.cpp b/Source/WebCore/dom/DOMImplementation.cpp new file mode 100644 index 0000000..30e889f --- /dev/null +++ b/Source/WebCore/dom/DOMImplementation.cpp @@ -0,0 +1,372 @@ +/* + * 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) + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "DOMImplementation.h" + +#include "ContentType.h" +#include "CSSStyleSheet.h" +#include "DocumentType.h" +#include "Element.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "FTPDirectoryDocument.h" +#include "HTMLDocument.h" +#include "HTMLNames.h" +#include "HTMLViewSourceDocument.h" +#include "Image.h" +#include "ImageDocument.h" +#include "MediaDocument.h" +#include "MediaList.h" +#include "MediaPlayer.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "PluginData.h" +#include "PluginDocument.h" +#include "RegularExpression.h" +#include "Settings.h" +#include "TextDocument.h" +#include "XMLNames.h" +#include <wtf/StdLibExtras.h> + +#if ENABLE(SVG) +#include "SVGNames.h" +#include "SVGDocument.h" +#endif + +#if ENABLE(WML) +#include "WMLNames.h" +#include "WMLDocument.h" +#endif + +namespace WebCore { + +#if ENABLE(SVG) + +typedef HashSet<String, CaseFoldingHash> FeatureSet; + +static void addString(FeatureSet& set, const char* string) +{ + set.add(string); +} + +static bool isSVG10Feature(const String &feature) +{ + static bool initialized = false; + DEFINE_STATIC_LOCAL(FeatureSet, svgFeatures, ()); + if (!initialized) { +#if ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT) && ENABLE(FILTERS) && 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(FILTERS) && 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; + DEFINE_STATIC_LOCAL(FeatureSet, 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(FILTERS) && ENABLE(SVG_FONTS) + addString(svgFeatures, "SVG"); + addString(svgFeatures, "SVGDOM"); + addString(svgFeatures, "SVG-static"); + addString(svgFeatures, "SVGDOM-static"); +#endif +#if ENABLE(SVG_ANIMATION) + addString(svgFeatures, "SVG-animation"); + addString(svgFeatures, "SVGDOM-animation"); +#endif +// 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(FILTERS) +// 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"); +#if ENABLE(SVG_ANIMATION) + addString(svgFeatures, "Animation"); +#endif +#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 + +bool DOMImplementation::hasFeature(const String& feature, const String& version) +{ + 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) +{ + String prefix, localName; + if (!Document::parseQualifiedName(qualifiedName, prefix, localName, ec)) + return 0; + + return DocumentType::create(0, qualifiedName, publicId, systemId); +} + +DOMImplementation* DOMImplementation::getInterface(const String& /*feature*/) +{ + return 0; +} + +PassRefPtr<Document> DOMImplementation::createDocument(const String& namespaceURI, + const String& qualifiedName, DocumentType* doctype, ExceptionCode& ec) +{ + RefPtr<Document> doc; +#if ENABLE(SVG) + if (namespaceURI == SVGNames::svgNamespaceURI) + doc = SVGDocument::create(0, KURL()); + else +#endif +#if ENABLE(WML) + if (namespaceURI == WMLNames::wmlNamespaceURI) + doc = WMLDocument::create(0, KURL()); + else +#endif + if (namespaceURI == HTMLNames::xhtmlNamespaceURI) + doc = Document::createXHTML(0, KURL()); + else + doc = Document::create(0, KURL()); + + RefPtr<Node> documentElement; + if (!qualifiedName.isEmpty()) { + documentElement = doc->createElementNS(namespaceURI, qualifiedName, ec); + if (ec) + return 0; + } + + // WRONG_DOCUMENT_ERR: Raised if doctype has already been used with a different document or was + // created from a different implementation. + // Hixie's interpretation of the DOM Core spec suggests we should prefer + // other exceptions to WRONG_DOCUMENT_ERR (based on order mentioned in spec). + if (doctype && doctype->document()) { + ec = WRONG_DOCUMENT_ERR; + return 0; + } + + // FIXME: Shouldn't this call appendChild instead? + if (doctype) + doc->parserAddChild(doctype); + if (documentElement) + doc->parserAddChild(documentElement.release()); + + return doc.release(); +} + +PassRefPtr<CSSStyleSheet> DOMImplementation::createCSSStyleSheet(const String&, const String& media, ExceptionCode&) +{ + // FIXME: Title should be set. + // FIXME: Media could have wrong syntax, in which case we should generate an exception. + RefPtr<CSSStyleSheet> sheet = CSSStyleSheet::create(); + sheet->setMedia(MediaList::createAllowingDescriptionSyntax(sheet.get(), media)); + return sheet.release(); +} + +bool DOMImplementation::isXMLMIMEType(const String& mimeType) +{ + if (mimeType == "text/xml" || mimeType == "application/xml" || mimeType == "text/xsl") + return true; + static const char* const validChars = "[0-9a-zA-Z_\\-+~!$\\^{}|.%'`#&*]"; // per RFCs: 3023, 2045 + DEFINE_STATIC_LOCAL(RegularExpression, xmlTypeRegExp, (String("^") + validChars + "+/" + validChars + "+\\+xml$", TextCaseSensitive)); + return xmlTypeRegExp.match(mimeType) > -1; +} + +bool DOMImplementation::isTextMIMEType(const String& mimeType) +{ + if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) + || mimeType == "application/json" // Render JSON as text/plain. + || (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 = HTMLDocument::create(0, KURL()); + d->open(); + d->write("<!doctype html><html><body></body></html>"); + d->setTitle(title); + return d.release(); +} + +PassRefPtr<Document> DOMImplementation::createDocument(const String& type, Frame* frame, const KURL& url, bool inViewSourceMode) +{ + if (inViewSourceMode) + return HTMLViewSourceDocument::create(frame, url, 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 HTMLDocument::create(frame, url); + if (type == "application/xhtml+xml" +#if ENABLE(XHTMLMP) + || type == "application/vnd.wap.xhtml+xml" +#endif + ) + return Document::createXHTML(frame, url); + +#if ENABLE(WML) + if (type == "text/vnd.wap.wml" || type == "application/vnd.wap.wmlc") + return WMLDocument::create(frame, url); +#endif + +#if ENABLE(FTPDIR) + // Plugins cannot take FTP from us either + if (type == "application/x-ftp-directory") + return FTPDirectoryDocument::create(frame, url); +#endif + + PluginData* pluginData = 0; + if (frame && frame->page() && frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin)) + pluginData = frame->page()->pluginData(); + + // 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") && pluginData && pluginData->supportsMimeType(type)) + return PluginDocument::create(frame, url); + if (Image::supportsType(type)) + return ImageDocument::create(frame, url); + +#if ENABLE(VIDEO) + // Check to see if the type can be played by our MediaPlayer, if so create a MediaDocument + if (MediaPlayer::supportsType(ContentType(type))) + return MediaDocument::create(frame, url); +#endif + + // 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" && pluginData && pluginData->supportsMimeType(type)) + return PluginDocument::create(frame, url); + if (isTextMIMEType(type)) + return TextDocument::create(frame, url); + +#if ENABLE(SVG) + if (type == "image/svg+xml") { +#if ENABLE(DASHBOARD_SUPPORT) + Settings* settings = frame ? frame->settings() : 0; + if (!settings || !settings->usesDashboardBackwardCompatibilityMode()) +#endif + return SVGDocument::create(frame, url); + } +#endif + if (isXMLMIMEType(type)) + return Document::create(frame, url); + + return HTMLDocument::create(frame, url); +} + +} diff --git a/Source/WebCore/dom/DOMImplementation.h b/Source/WebCore/dom/DOMImplementation.h new file mode 100644 index 0000000..a20f7d2 --- /dev/null +++ b/Source/WebCore/dom/DOMImplementation.h @@ -0,0 +1,71 @@ +/* + * 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. + * + */ + +#ifndef DOMImplementation_h +#define DOMImplementation_h + +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class CSSStyleSheet; +class Document; +class DocumentType; +class Frame; +class HTMLDocument; +class KURL; + +typedef int ExceptionCode; + +class DOMImplementation : public RefCounted<DOMImplementation> { +public: + static PassRefPtr<DOMImplementation> create() { return adoptRef(new DOMImplementation); } + + // DOM methods & attributes for DOMImplementation + static bool hasFeature(const String& feature, const String& version); + static PassRefPtr<DocumentType> createDocumentType(const String& qualifiedName, const String& publicId, const String &systemId, ExceptionCode&); + static PassRefPtr<Document> createDocument(const String& namespaceURI, const String& qualifiedName, DocumentType*, ExceptionCode&); + + DOMImplementation* getInterface(const String& feature); + + // From the DOMImplementationCSS interface + static PassRefPtr<CSSStyleSheet> createCSSStyleSheet(const String& title, const String& media, ExceptionCode&); + + // From the HTMLDOMImplementation interface + static PassRefPtr<HTMLDocument> createHTMLDocument(const String& title); + + // Other methods (not part of DOM) + static PassRefPtr<Document> createDocument(const String& MIMEType, Frame*, const KURL&, bool inViewSourceMode); + + static bool isXMLMIMEType(const String& MIMEType); + static bool isTextMIMEType(const String& MIMEType); + +private: + DOMImplementation() { } +}; + +} //namespace + +#endif diff --git a/Source/WebCore/dom/DOMImplementation.idl b/Source/WebCore/dom/DOMImplementation.idl new file mode 100644 index 0000000..81df6c8 --- /dev/null +++ b/Source/WebCore/dom/DOMImplementation.idl @@ -0,0 +1,52 @@ +/* + * 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 DOMImplementation { + + // DOM Level 1 + + [OldStyleObjC] boolean hasFeature(in DOMString feature, + in [ConvertNullToNullString] DOMString version); + + // DOM Level 2 + + [OldStyleObjC] DocumentType createDocumentType(in [ConvertUndefinedOrNullToNullString] DOMString qualifiedName, + in [ConvertUndefinedOrNullToNullString] DOMString publicId, + in [ConvertUndefinedOrNullToNullString] 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 + + [OldStyleObjC] CSSStyleSheet createCSSStyleSheet(in DOMString title, + in DOMString media) + raises(DOMException); + + // HTMLDOMImplementation interface from DOM Level 2 HTML + + HTMLDocument createHTMLDocument(in DOMString title); + }; + +} diff --git a/Source/WebCore/dom/DOMStringList.cpp b/Source/WebCore/dom/DOMStringList.cpp new file mode 100644 index 0000000..430265a --- /dev/null +++ b/Source/WebCore/dom/DOMStringList.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Google 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 "DOMStringList.h" + +namespace WebCore { + +String DOMStringList::item(unsigned index) const +{ + if (index >= m_strings.size()) + return String(); + return m_strings[index]; +} + +bool DOMStringList::contains(const String& string) const +{ + // FIXME: Currently, all consumers of DOMStringList store fairly small lists and thus an O(n) + // algorithm is OK. But this may need to be optimized if larger amounts of data are + // stored in m_strings. + size_t count = m_strings.size(); + for (size_t i = 0; i < count; ++i) { + if (m_strings[i] == string) + return true; + } + return false; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DOMStringList.h b/Source/WebCore/dom/DOMStringList.h new file mode 100644 index 0000000..6719f17 --- /dev/null +++ b/Source/WebCore/dom/DOMStringList.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Google 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 DOMStringList_h +#define DOMStringList_h + +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// FIXME: Some consumers of this class may benefit from lazily fetching items rather +// than creating the list statically as is currently the only option. +class DOMStringList : public RefCounted<DOMStringList> { +public: + static PassRefPtr<DOMStringList> create() + { + return adoptRef(new DOMStringList()); + } + + bool isEmpty() const { return m_strings.isEmpty(); } + void clear() { m_strings.clear(); } + void append(const String& string) { m_strings.append(string); } + + // Implements the IDL. + size_t length() const { return m_strings.size(); } + String item(unsigned index) const; + bool contains(const String& str) const; + +private: + DOMStringList() { } + + Vector<String> m_strings; +}; + +} // namespace WebCore + +#endif // DOMStringList_h + diff --git a/Source/WebCore/dom/DOMStringList.idl b/Source/WebCore/dom/DOMStringList.idl new file mode 100644 index 0000000..c9e5c29 --- /dev/null +++ b/Source/WebCore/dom/DOMStringList.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 Google 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. + */ + +module core { + + interface [ + GenerateConstructor, + HasIndexGetter + ] DOMStringList { + readonly attribute unsigned long length; + [ConvertNullStringTo=Null] DOMString item(in [IsIndex] unsigned long index); + boolean contains(in DOMString string); + }; + +} + diff --git a/Source/WebCore/dom/DOMStringMap.cpp b/Source/WebCore/dom/DOMStringMap.cpp new file mode 100644 index 0000000..e63dabe --- /dev/null +++ b/Source/WebCore/dom/DOMStringMap.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 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. 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 INC. 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 "DOMStringMap.h" + +namespace WebCore { + +DOMStringMap::~DOMStringMap() +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DOMStringMap.h b/Source/WebCore/dom/DOMStringMap.h new file mode 100644 index 0000000..86a22b0 --- /dev/null +++ b/Source/WebCore/dom/DOMStringMap.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 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. 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 INC. 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 DOMStringMap_h +#define DOMStringMap_h + +#include "PlatformString.h" +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Element; +typedef int ExceptionCode; + +class DOMStringMap : public Noncopyable { +public: + virtual ~DOMStringMap(); + + virtual void ref() = 0; + virtual void deref() = 0; + + virtual void getNames(Vector<String>&) = 0; + virtual String item(const String& name) = 0; + virtual bool contains(const String& name) = 0; + virtual void setItem(const String& name, const String& value, ExceptionCode&) = 0; + virtual void deleteItem(const String& name, ExceptionCode&) = 0; + + virtual Element* element() = 0; + +protected: + DOMStringMap() + { + } +}; + +} // namespace WebCore + +#endif // DOMStringMap_h diff --git a/Source/WebCore/dom/DOMStringMap.idl b/Source/WebCore/dom/DOMStringMap.idl new file mode 100644 index 0000000..0320b3b --- /dev/null +++ b/Source/WebCore/dom/DOMStringMap.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 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. 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 INC. 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 [ + HasNameGetter, + CustomDeleteProperty, + CustomGetPropertyNames, + DelegatingPutFunction, + ] DOMStringMap { + }; + +} diff --git a/Source/WebCore/dom/DOMTimeStamp.h b/Source/WebCore/dom/DOMTimeStamp.h new file mode 100644 index 0000000..ff61520 --- /dev/null +++ b/Source/WebCore/dom/DOMTimeStamp.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 DOMTimeStamp_h +#define DOMTimeStamp_h + +namespace WebCore { + +typedef unsigned long long DOMTimeStamp; + +inline DOMTimeStamp convertSecondsToDOMTimeStamp(double seconds) +{ + return static_cast<DOMTimeStamp>(seconds * 1000.0); +} + +inline double convertDOMTimeStampToSeconds(DOMTimeStamp milliseconds) +{ + return milliseconds / 1000.0; +} + +} // namespace WebCore + +#endif // DOMTimeStamp_h diff --git a/Source/WebCore/dom/DatasetDOMStringMap.cpp b/Source/WebCore/dom/DatasetDOMStringMap.cpp new file mode 100644 index 0000000..6359d55 --- /dev/null +++ b/Source/WebCore/dom/DatasetDOMStringMap.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2010 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. 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 INC. 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 "DatasetDOMStringMap.h" + +#include "Attribute.h" +#include "Element.h" +#include "ExceptionCode.h" +#include "NamedNodeMap.h" +#include <wtf/ASCIICType.h> + +namespace WebCore { + +static bool isValidAttributeName(const String& name) +{ + if (!name.startsWith("data-")) + return false; + + const UChar* characters = name.characters(); + unsigned length = name.length(); + for (unsigned i = 5; i < length; ++i) { + if (isASCIIUpper(characters[i])) + return false; + } + + return true; +} + +static String convertAttributeNameToPropertyName(const String& name) +{ + Vector<UChar> newStringBuffer; + + const UChar* characters = name.characters(); + unsigned length = name.length(); + for (unsigned i = 5; i < length; ++i) { + if (characters[i] != '-') + newStringBuffer.append(characters[i]); + else { + if ((i + 1 < length) && isASCIILower(characters[i + 1])) { + newStringBuffer.append(toASCIIUpper(characters[i + 1])); + ++i; + } else + newStringBuffer.append(characters[i]); + } + } + + return String::adopt(newStringBuffer); +} + +static bool propertyNameMatchesAttributeName(const String& propertyName, const String& attributeName) +{ + // FIXME: This should be able to match without creating a new string. + + if (!isValidAttributeName(attributeName)) + return false; + + String convertedName = convertAttributeNameToPropertyName(attributeName); + return (convertedName == propertyName); +} + +static bool isValidPropertyName(const String& name) +{ + const UChar* characters = name.characters(); + unsigned length = name.length(); + for (unsigned i = 0; i < length; ++i) { + if (characters[i] == '-' && (i + 1 < length) && isASCIILower(characters[i + 1])) + return false; + } + return true; +} + +static String convertPropertyNameToAttributeName(const String& name) +{ + Vector<UChar> newStringBuffer; + + newStringBuffer.append('d'); + newStringBuffer.append('a'); + newStringBuffer.append('t'); + newStringBuffer.append('a'); + newStringBuffer.append('-'); + + const UChar* characters = name.characters(); + unsigned length = name.length(); + for (unsigned i = 0; i < length; ++i) { + if (isASCIIUpper(characters[i])) { + newStringBuffer.append('-'); + newStringBuffer.append(toASCIILower(characters[i])); + } else + newStringBuffer.append(characters[i]); + } + + return String::adopt(newStringBuffer); +} + + +void DatasetDOMStringMap::ref() +{ + m_element->ref(); +} + +void DatasetDOMStringMap::deref() +{ + m_element->deref(); +} + +void DatasetDOMStringMap::getNames(Vector<String>& names) +{ + NamedNodeMap* attributeMap = m_element->attributes(true); + if (attributeMap) { + unsigned length = attributeMap->length(); + for (unsigned i = 0; i < length; i++) { + Attribute* attribute = attributeMap->attributeItem(i); + if (isValidAttributeName(attribute->localName())) + names.append(convertAttributeNameToPropertyName(attribute->localName())); + } + } +} + +String DatasetDOMStringMap::item(const String& name) +{ + NamedNodeMap* attributeMap = m_element->attributes(true); + if (attributeMap) { + unsigned length = attributeMap->length(); + for (unsigned i = 0; i < length; i++) { + Attribute* attribute = attributeMap->attributeItem(i); + if (propertyNameMatchesAttributeName(name, attribute->localName())) + return attribute->value(); + } + } + + return String(); +} + +bool DatasetDOMStringMap::contains(const String& name) +{ + NamedNodeMap* attributeMap = m_element->attributes(true); + if (attributeMap) { + unsigned length = attributeMap->length(); + for (unsigned i = 0; i < length; i++) { + Attribute* attribute = attributeMap->attributeItem(i); + if (propertyNameMatchesAttributeName(name, attribute->localName())) + return true; + } + } + return false; +} + +void DatasetDOMStringMap::setItem(const String& name, const String& value, ExceptionCode& ec) +{ + if (!isValidPropertyName(name)) { + ec = SYNTAX_ERR; + return; + } + + m_element->setAttribute(convertPropertyNameToAttributeName(name), value, ec); +} + +void DatasetDOMStringMap::deleteItem(const String& name, ExceptionCode& ec) +{ + if (!isValidPropertyName(name)) { + ec = SYNTAX_ERR; + return; + } + + ExceptionCode dummy; + m_element->removeAttribute(convertPropertyNameToAttributeName(name), dummy); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DatasetDOMStringMap.h b/Source/WebCore/dom/DatasetDOMStringMap.h new file mode 100644 index 0000000..632e365 --- /dev/null +++ b/Source/WebCore/dom/DatasetDOMStringMap.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 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. 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 INC. 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 DatasetDOMStringMap_h +#define DatasetDOMStringMap_h + +#include "DOMStringMap.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class Element; + +class DatasetDOMStringMap : public DOMStringMap { +public: + static PassOwnPtr<DatasetDOMStringMap> create(Element* element) + { + return new DatasetDOMStringMap(element); + } + + virtual void ref(); + virtual void deref(); + + virtual void getNames(Vector<String>&); + virtual String item(const String& name); + virtual bool contains(const String& name); + virtual void setItem(const String& name, const String& value, ExceptionCode&); + virtual void deleteItem(const String& name, ExceptionCode&); + + virtual Element* element() { return m_element; } + +private: + DatasetDOMStringMap(Element* element) + : m_element(element) + { + } + + Element* m_element; +}; + +} // namespace WebCore + +#endif // DatasetDOMStringMap_h diff --git a/Source/WebCore/dom/DecodedDataDocumentParser.cpp b/Source/WebCore/dom/DecodedDataDocumentParser.cpp new file mode 100644 index 0000000..975b1b5 --- /dev/null +++ b/Source/WebCore/dom/DecodedDataDocumentParser.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Google, 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 "DecodedDataDocumentParser.h" + +#include "DocumentWriter.h" +#include "SegmentedString.h" +#include "TextResourceDecoder.h" + +namespace WebCore { + +DecodedDataDocumentParser::DecodedDataDocumentParser(Document* document) + : DocumentParser(document) +{ +} + +void DecodedDataDocumentParser::appendBytes(DocumentWriter* writer , const char* data, int length, bool shouldFlush) +{ + if (!length && !shouldFlush) + return; + + TextResourceDecoder* decoder = writer->createDecoderIfNeeded(); + String decoded = decoder->decode(data, length); + if (shouldFlush) + decoded += decoder->flush(); + if (decoded.isEmpty()) + return; + + writer->reportDataReceived(); + + append(decoded); +} + +}; + diff --git a/Source/WebCore/dom/DecodedDataDocumentParser.h b/Source/WebCore/dom/DecodedDataDocumentParser.h new file mode 100644 index 0000000..40f3f19 --- /dev/null +++ b/Source/WebCore/dom/DecodedDataDocumentParser.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google, 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 DecodedDataDocumentParser_h +#define DecodedDataDocumentParser_h + +#include "DocumentParser.h" + +namespace WebCore { + +class DecodedDataDocumentParser : public DocumentParser { +public: + // Only used by the XMLDocumentParser to communicate back to + // XMLHttpRequest if the responseXML was well formed. + virtual bool wellFormed() const { return true; } + +protected: + explicit DecodedDataDocumentParser(Document*); + +private: + // append is used by DocumentWriter::replaceDocument + virtual void append(const SegmentedString&) = 0; + + // appendBytes is used by DocumentWriter (the loader) + virtual void appendBytes(DocumentWriter*, const char* bytes, int length, bool flush); +}; + +} + +#endif // DecodedDataDocumentParser_h diff --git a/Source/WebCore/dom/DeviceMotionClient.h b/Source/WebCore/dom/DeviceMotionClient.h new file mode 100644 index 0000000..45bf11a --- /dev/null +++ b/Source/WebCore/dom/DeviceMotionClient.h @@ -0,0 +1,46 @@ +/* + * Copyright 2010 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 DeviceMotionClient_h +#define DeviceMotionClient_h + +namespace WebCore { + +class DeviceMotionController; +class DeviceMotionData; + +class DeviceMotionClient { +public: + virtual ~DeviceMotionClient() {} + virtual void setController(DeviceMotionController*) = 0; + virtual void startUpdating() = 0; + virtual void stopUpdating() = 0; + virtual DeviceMotionData* currentDeviceMotion() const = 0; + virtual void deviceMotionControllerDestroyed() = 0; +}; + +} // namespace WebCore + +#endif // DeviceMotionClient_h diff --git a/Source/WebCore/dom/DeviceMotionController.cpp b/Source/WebCore/dom/DeviceMotionController.cpp new file mode 100644 index 0000000..3385167 --- /dev/null +++ b/Source/WebCore/dom/DeviceMotionController.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2010 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "DeviceMotionController.h" + +#include "DeviceMotionClient.h" +#include "DeviceMotionData.h" +#include "DeviceMotionEvent.h" + +namespace WebCore { + +DeviceMotionController::DeviceMotionController(DeviceMotionClient* client) + : m_client(client) + , m_timer(this, &DeviceMotionController::timerFired) +{ + ASSERT(m_client); + m_client->setController(this); +} + +DeviceMotionController::~DeviceMotionController() +{ + m_client->deviceMotionControllerDestroyed(); +} + +void DeviceMotionController::timerFired(Timer<DeviceMotionController>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_timer); + ASSERT(!m_client || m_client->currentDeviceMotion()); + m_timer.stop(); + + RefPtr<DeviceMotionData> deviceMotionData = m_client ? m_client->currentDeviceMotion() : DeviceMotionData::create(); + RefPtr<DeviceMotionEvent> event = DeviceMotionEvent::create(eventNames().devicemotionEvent, deviceMotionData.get()); + + Vector<DOMWindow*> listenersVector; + copyToVector(m_newListeners, listenersVector); + m_newListeners.clear(); + for (size_t i = 0; i < listenersVector.size(); ++i) + listenersVector[i]->dispatchEvent(event); +} + +void DeviceMotionController::addListener(DOMWindow* window) +{ + // If no client is present or the client already has motion data, + // immediately trigger an asynchronous response. + if (!m_client || m_client->currentDeviceMotion()) { + m_newListeners.add(window); + if (!m_timer.isActive()) + m_timer.startOneShot(0); + } + + bool wasEmpty = m_listeners.isEmpty(); + m_listeners.add(window); + if (wasEmpty && m_client) + m_client->startUpdating(); +} + +void DeviceMotionController::removeListener(DOMWindow* window) +{ + m_listeners.remove(window); + m_newListeners.remove(window); + if (m_listeners.isEmpty() && m_client) + m_client->stopUpdating(); +} + +void DeviceMotionController::removeAllListeners(DOMWindow* window) +{ + // May be called with a DOMWindow that's not a listener. + if (!m_listeners.contains(window)) + return; + + m_listeners.removeAll(window); + m_newListeners.remove(window); + if (m_listeners.isEmpty() && m_client) + m_client->stopUpdating(); +} + +void DeviceMotionController::didChangeDeviceMotion(DeviceMotionData* deviceMotionData) +{ + RefPtr<DeviceMotionEvent> event = DeviceMotionEvent::create(eventNames().devicemotionEvent, deviceMotionData); + Vector<DOMWindow*> listenersVector; + copyToVector(m_listeners, listenersVector); + for (size_t i = 0; i < listenersVector.size(); ++i) + listenersVector[i]->dispatchEvent(event); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DeviceMotionController.h b/Source/WebCore/dom/DeviceMotionController.h new file mode 100644 index 0000000..70c948e --- /dev/null +++ b/Source/WebCore/dom/DeviceMotionController.h @@ -0,0 +1,64 @@ +/* + * Copyright 2010 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 DeviceMotionController_h +#define DeviceMotionController_h + +#include "DOMWindow.h" +#include "Timer.h" +#include <wtf/HashCountedSet.h> + +namespace WebCore { + +class DeviceMotionData; +class DeviceMotionClient; + +class DeviceMotionController { +public: + DeviceMotionController(DeviceMotionClient*); + ~DeviceMotionController(); + + void addListener(DOMWindow*); + void removeListener(DOMWindow*); + void removeAllListeners(DOMWindow*); + + void didChangeDeviceMotion(DeviceMotionData*); + + bool isActive() { return !m_listeners.isEmpty(); } + +private: + void timerFired(Timer<DeviceMotionController>*); + + DeviceMotionClient* m_client; + typedef HashCountedSet<DOMWindow*> ListenersCountedSet; + ListenersCountedSet m_listeners; + typedef HashSet<DOMWindow*> ListenersSet; + ListenersSet m_newListeners; + Timer<DeviceMotionController> m_timer; +}; + +} // namespace WebCore + +#endif // DeviceMotionController_h diff --git a/Source/WebCore/dom/DeviceMotionData.cpp b/Source/WebCore/dom/DeviceMotionData.cpp new file mode 100644 index 0000000..7344c1c --- /dev/null +++ b/Source/WebCore/dom/DeviceMotionData.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "DeviceMotionData.h" + +namespace WebCore { + +PassRefPtr<DeviceMotionData::Acceleration> DeviceMotionData::Acceleration::create(bool canProvideX, double x, + bool canProvideY, double y, + bool canProvideZ, double z) +{ + return adoptRef(new DeviceMotionData::Acceleration(canProvideX, x, canProvideY, y, canProvideZ, z)); +} + +DeviceMotionData::Acceleration::Acceleration(bool canProvideX, double x, bool canProvideY, double y, bool canProvideZ, double z) + : m_x(x) + , m_y(y) + , m_z(z) + , m_canProvideX(canProvideX) + , m_canProvideY(canProvideY) + , m_canProvideZ(canProvideZ) + +{ +} + +PassRefPtr<DeviceMotionData::RotationRate> DeviceMotionData::RotationRate::create(bool canProvideAlpha, double alpha, + bool canProvideBeta, double beta, + bool canProvideGamma, double gamma) +{ + return adoptRef(new DeviceMotionData::RotationRate(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma)); +} + +DeviceMotionData::RotationRate::RotationRate(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma) + : m_alpha(alpha) + , m_beta(beta) + , m_gamma(gamma) + , m_canProvideAlpha(canProvideAlpha) + , m_canProvideBeta(canProvideBeta) + , m_canProvideGamma(canProvideGamma) +{ +} + +PassRefPtr<DeviceMotionData> DeviceMotionData::create() +{ + return adoptRef(new DeviceMotionData); +} + +PassRefPtr<DeviceMotionData> DeviceMotionData::create(PassRefPtr<Acceleration> acceleration, PassRefPtr<Acceleration> accelerationIncludingGravity, + PassRefPtr<RotationRate> rotationRate, bool canProvideInterval, double interval) +{ + return adoptRef(new DeviceMotionData(acceleration, accelerationIncludingGravity, rotationRate, canProvideInterval, interval)); +} + +DeviceMotionData::DeviceMotionData() + : m_canProvideInterval(false) +{ +} + +DeviceMotionData::DeviceMotionData(PassRefPtr<Acceleration> acceleration, PassRefPtr<Acceleration> accelerationIncludingGravity, + PassRefPtr<RotationRate> rotationRate, bool canProvideInterval, double interval) + : m_acceleration(acceleration) + , m_accelerationIncludingGravity(accelerationIncludingGravity) + , m_rotationRate(rotationRate) + , m_canProvideInterval(canProvideInterval) + , m_interval(interval) +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DeviceMotionData.h b/Source/WebCore/dom/DeviceMotionData.h new file mode 100644 index 0000000..1d53b53 --- /dev/null +++ b/Source/WebCore/dom/DeviceMotionData.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 DeviceMotionData_h +#define DeviceMotionData_h + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class DeviceMotionData : public RefCounted<DeviceMotionData> { +public: + class Acceleration : public RefCounted<DeviceMotionData::Acceleration> { + public: + static PassRefPtr<Acceleration> create(bool canProvideX, double x, bool canProvideY, double y, bool canProvideZ, double z); + + bool canProvideX() const { return m_canProvideX; } + bool canProvideY() const { return m_canProvideY; } + bool canProvideZ() const { return m_canProvideZ; } + + double x() const { return m_x; } + double y() const { return m_y; } + double z() const { return m_z; } + + private: + Acceleration(bool canProvideX, double x, bool canProvideY, double y, bool canProvideZ, double z); + + double m_x; + double m_y; + double m_z; + + bool m_canProvideX; + bool m_canProvideY; + bool m_canProvideZ; + }; + + class RotationRate : public RefCounted<DeviceMotionData::RotationRate> { + public: + static PassRefPtr<RotationRate> create(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma); + + bool canProvideAlpha() const { return m_canProvideAlpha; } + bool canProvideBeta() const { return m_canProvideBeta; } + bool canProvideGamma() const { return m_canProvideGamma; } + + double alpha() const { return m_alpha; } + double beta() const { return m_beta; } + double gamma() const { return m_gamma; } + + private: + RotationRate(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma); + + double m_alpha; + double m_beta; + double m_gamma; + + bool m_canProvideAlpha; + bool m_canProvideBeta; + bool m_canProvideGamma; + }; + + static PassRefPtr<DeviceMotionData> create(); + static PassRefPtr<DeviceMotionData> create(PassRefPtr<Acceleration> acceleration, PassRefPtr<Acceleration> accelerationIncludingGravity, + PassRefPtr<RotationRate> rotationRate, bool canProvideInterval, double interval); + + const Acceleration* acceleration() const { return m_acceleration.get(); } + const Acceleration* accelerationIncludingGravity() const { return m_accelerationIncludingGravity.get(); } + const RotationRate* rotationRate() const { return m_rotationRate.get(); } + double interval() const { return m_interval; } + bool canProvideInterval() const { return m_canProvideInterval; } + +private: + DeviceMotionData(); + DeviceMotionData(PassRefPtr<Acceleration> acceleration, PassRefPtr<Acceleration> accelerationIncludingGravity, + PassRefPtr<RotationRate> rotationRate, bool canProvideInterval, double interval); + + RefPtr<Acceleration> m_acceleration; + RefPtr<Acceleration> m_accelerationIncludingGravity; + RefPtr<RotationRate> m_rotationRate; + bool m_canProvideInterval; + double m_interval; +}; + +} // namespace WebCore + +#endif // DeviceMotionData_h diff --git a/Source/WebCore/dom/DeviceMotionEvent.cpp b/Source/WebCore/dom/DeviceMotionEvent.cpp new file mode 100644 index 0000000..860cca2 --- /dev/null +++ b/Source/WebCore/dom/DeviceMotionEvent.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 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 "DeviceMotionEvent.h" + +#include "DeviceMotionData.h" + +namespace WebCore { + +DeviceMotionEvent::~DeviceMotionEvent() +{ +} + +DeviceMotionEvent::DeviceMotionEvent() + : m_deviceMotionData(DeviceMotionData::create()) +{ +} + +DeviceMotionEvent::DeviceMotionEvent(const AtomicString& eventType, DeviceMotionData* deviceMotionData) + : Event(eventType, false, false) // Can't bubble, not cancelable + , m_deviceMotionData(deviceMotionData) +{ +} + +void DeviceMotionEvent::initDeviceMotionEvent(const AtomicString& type, bool bubbles, bool cancelable, DeviceMotionData* deviceMotionData) +{ + if (dispatched()) + return; + + initEvent(type, bubbles, cancelable); + m_deviceMotionData = deviceMotionData; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DeviceMotionEvent.h b/Source/WebCore/dom/DeviceMotionEvent.h new file mode 100644 index 0000000..f32c93b --- /dev/null +++ b/Source/WebCore/dom/DeviceMotionEvent.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 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 DeviceMotionEvent_h +#define DeviceMotionEvent_h + +#include "Event.h" + +namespace WebCore { + +class DeviceMotionData; + +class DeviceMotionEvent : public Event { +public: + ~DeviceMotionEvent(); + static PassRefPtr<DeviceMotionEvent> create() + { + return adoptRef(new DeviceMotionEvent); + } + static PassRefPtr<DeviceMotionEvent> create(const AtomicString& eventType, DeviceMotionData* deviceMotionData) + { + return adoptRef(new DeviceMotionEvent(eventType, deviceMotionData)); + } + + void initDeviceMotionEvent(const AtomicString& type, bool bubbles, bool cancelable, DeviceMotionData*); + + virtual bool isDeviceMotionEvent() const { return true; } + + DeviceMotionData* deviceMotionData() const { return m_deviceMotionData.get(); } + +private: + DeviceMotionEvent(); + DeviceMotionEvent(const AtomicString& eventType, DeviceMotionData*); + + RefPtr<DeviceMotionData> m_deviceMotionData; +}; + +} // namespace WebCore + +#endif // DeviceMotionEvent_h diff --git a/Source/WebCore/dom/DeviceMotionEvent.idl b/Source/WebCore/dom/DeviceMotionEvent.idl new file mode 100644 index 0000000..437ba4c --- /dev/null +++ b/Source/WebCore/dom/DeviceMotionEvent.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 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 core { + + interface [ + Conditional=DEVICE_ORIENTATION + ] DeviceMotionEvent : Event { + readonly attribute [Custom] Acceleration acceleration; + readonly attribute [Custom] Acceleration accelerationIncludingGravity; + readonly attribute [Custom] RotationRate rotationRate; + readonly attribute [Custom] double interval; + [Custom] void initDeviceMotionEvent(in DOMString type, in boolean bubbles, in boolean cancelable, in Acceleration acceleration, in Acceleration accelerationIncludingGravity, in RotationRate rotationRate, in double interval); + }; + +} diff --git a/Source/WebCore/dom/DeviceOrientation.cpp b/Source/WebCore/dom/DeviceOrientation.cpp new file mode 100644 index 0000000..f1c28b1 --- /dev/null +++ b/Source/WebCore/dom/DeviceOrientation.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2010 Google 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "DeviceOrientation.h" + +namespace WebCore { + +PassRefPtr<DeviceOrientation> DeviceOrientation::create() +{ + return adoptRef(new DeviceOrientation); +} + +PassRefPtr<DeviceOrientation> DeviceOrientation::create(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma) +{ + return adoptRef(new DeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma)); +} + + +DeviceOrientation::DeviceOrientation() + : m_canProvideAlpha(false) + , m_canProvideBeta(false) + , m_canProvideGamma(false) +{ +} + +DeviceOrientation::DeviceOrientation(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma) + : m_canProvideAlpha(canProvideAlpha) + , m_canProvideBeta(canProvideBeta) + , m_canProvideGamma(canProvideGamma) + , m_alpha(alpha) + , m_beta(beta) + , m_gamma(gamma) +{ +} + +double DeviceOrientation::alpha() const +{ + return m_alpha; +} + +double DeviceOrientation::beta() const +{ + return m_beta; +} + +double DeviceOrientation::gamma() const +{ + return m_gamma; +} + +bool DeviceOrientation::canProvideAlpha() const +{ + return m_canProvideAlpha; +} + +bool DeviceOrientation::canProvideBeta() const +{ + return m_canProvideBeta; +} + +bool DeviceOrientation::canProvideGamma() const +{ + return m_canProvideGamma; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DeviceOrientation.h b/Source/WebCore/dom/DeviceOrientation.h new file mode 100644 index 0000000..c652736 --- /dev/null +++ b/Source/WebCore/dom/DeviceOrientation.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Google 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 DeviceOrientation_h +#define DeviceOrientation_h + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class DeviceOrientation : public RefCounted<DeviceOrientation> { +public: + static PassRefPtr<DeviceOrientation> create(); + static PassRefPtr<DeviceOrientation> create(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma); + + double alpha() const; + double beta() const; + double gamma() const; + bool canProvideAlpha() const; + bool canProvideBeta() const; + bool canProvideGamma() const; + +private: + DeviceOrientation(); + DeviceOrientation(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma); + + bool m_canProvideAlpha; + bool m_canProvideBeta; + bool m_canProvideGamma; + double m_alpha; + double m_beta; + double m_gamma; +}; + +} // namespace WebCore + +#endif // DeviceOrientation_h diff --git a/Source/WebCore/dom/DeviceOrientationClient.h b/Source/WebCore/dom/DeviceOrientationClient.h new file mode 100644 index 0000000..347c3b3 --- /dev/null +++ b/Source/WebCore/dom/DeviceOrientationClient.h @@ -0,0 +1,47 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 DeviceOrientationClient_h +#define DeviceOrientationClient_h + +namespace WebCore { + +class DeviceOrientation; +class DeviceOrientationController; + +class DeviceOrientationClient { +public: + virtual ~DeviceOrientationClient() {} + + virtual void setController(DeviceOrientationController*) = 0; + virtual void startUpdating() = 0; + virtual void stopUpdating() = 0; + virtual DeviceOrientation* lastOrientation() const = 0; + virtual void deviceOrientationControllerDestroyed() = 0; +}; + +} // namespace WebCore + +#endif // DeviceOrientationClient_h diff --git a/Source/WebCore/dom/DeviceOrientationController.cpp b/Source/WebCore/dom/DeviceOrientationController.cpp new file mode 100644 index 0000000..60fcf13 --- /dev/null +++ b/Source/WebCore/dom/DeviceOrientationController.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "DeviceOrientationController.h" + +#include "DeviceOrientation.h" +#include "DeviceOrientationClient.h" +#include "DeviceOrientationEvent.h" + +namespace WebCore { + +DeviceOrientationController::DeviceOrientationController(Page* page, DeviceOrientationClient* client) + : m_page(page) + , m_client(client) + , m_timer(this, &DeviceOrientationController::timerFired) +{ + ASSERT(m_client); + m_client->setController(this); +} + +DeviceOrientationController::~DeviceOrientationController() +{ + m_client->deviceOrientationControllerDestroyed(); +} + +void DeviceOrientationController::timerFired(Timer<DeviceOrientationController>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_timer); + ASSERT(m_client->lastOrientation()); + + RefPtr<DeviceOrientation> orientation = m_client->lastOrientation(); + RefPtr<DeviceOrientationEvent> event = DeviceOrientationEvent::create(eventNames().deviceorientationEvent, orientation.get()); + + Vector<DOMWindow*> listenersVector; + copyToVector(m_newListeners, listenersVector); + m_newListeners.clear(); + for (size_t i = 0; i < listenersVector.size(); ++i) + listenersVector[i]->dispatchEvent(event); +} + +void DeviceOrientationController::addListener(DOMWindow* window) +{ + // If the client already has an orientation, we should fire an event with that + // orientation. The event is fired asynchronously, but without + // waiting for the client to get a new orientation. + if (m_client->lastOrientation()) { + m_newListeners.add(window); + if (!m_timer.isActive()) + m_timer.startOneShot(0); + } + + // The client must not call back synchronously. + bool wasEmpty = m_listeners.isEmpty(); + m_listeners.add(window); + if (wasEmpty) + m_client->startUpdating(); +} + +void DeviceOrientationController::removeListener(DOMWindow* window) +{ + m_listeners.remove(window); + m_newListeners.remove(window); + if (m_listeners.isEmpty()) + m_client->stopUpdating(); +} + +void DeviceOrientationController::removeAllListeners(DOMWindow* window) +{ + // May be called with a DOMWindow that's not a listener. + if (!m_listeners.contains(window)) + return; + + m_listeners.removeAll(window); + m_newListeners.remove(window); + if (m_listeners.isEmpty()) + m_client->stopUpdating(); +} + +void DeviceOrientationController::didChangeDeviceOrientation(DeviceOrientation* orientation) +{ + RefPtr<DeviceOrientationEvent> event = DeviceOrientationEvent::create(eventNames().deviceorientationEvent, orientation); + Vector<DOMWindow*> listenersVector; + copyToVector(m_listeners, listenersVector); + for (size_t i = 0; i < listenersVector.size(); ++i) + listenersVector[i]->dispatchEvent(event); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DeviceOrientationController.h b/Source/WebCore/dom/DeviceOrientationController.h new file mode 100644 index 0000000..4fa9006 --- /dev/null +++ b/Source/WebCore/dom/DeviceOrientationController.h @@ -0,0 +1,67 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 DeviceOrientationController_h +#define DeviceOrientationController_h + +#include "DOMWindow.h" +#include "Timer.h" + +#include <wtf/HashCountedSet.h> + +namespace WebCore { + +class DeviceOrientation; +class DeviceOrientationClient; +class Page; + +class DeviceOrientationController { +public: + DeviceOrientationController(Page*, DeviceOrientationClient*); + ~DeviceOrientationController(); + + void addListener(DOMWindow*); + void removeListener(DOMWindow*); + void removeAllListeners(DOMWindow*); + + void didChangeDeviceOrientation(DeviceOrientation*); + + bool isActive() { return !m_listeners.isEmpty(); } + +private: + void timerFired(Timer<DeviceOrientationController>*); + + Page* m_page; + DeviceOrientationClient* m_client; + typedef HashCountedSet<DOMWindow*> ListenersCountedSet; + ListenersCountedSet m_listeners; + typedef HashSet<DOMWindow*> ListenersSet; + ListenersSet m_newListeners; + Timer<DeviceOrientationController> m_timer; +}; + +} // namespace WebCore + +#endif // DeviceOrientationController_h diff --git a/Source/WebCore/dom/DeviceOrientationEvent.cpp b/Source/WebCore/dom/DeviceOrientationEvent.cpp new file mode 100644 index 0000000..932cb75 --- /dev/null +++ b/Source/WebCore/dom/DeviceOrientationEvent.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "DeviceOrientationEvent.h" + +#include "DeviceOrientation.h" + +namespace WebCore { + +DeviceOrientationEvent::~DeviceOrientationEvent() +{ +} + +DeviceOrientationEvent::DeviceOrientationEvent() + : m_orientation(DeviceOrientation::create()) +{ +} + +DeviceOrientationEvent::DeviceOrientationEvent(const AtomicString& eventType, DeviceOrientation* orientation) + : Event(eventType, false, false) // Can't bubble, not cancelable + , m_orientation(orientation) +{ +} + +void DeviceOrientationEvent::initDeviceOrientationEvent(const AtomicString& type, bool bubbles, bool cancelable, DeviceOrientation* orientation) +{ + if (dispatched()) + return; + + initEvent(type, bubbles, cancelable); + m_orientation = orientation; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DeviceOrientationEvent.h b/Source/WebCore/dom/DeviceOrientationEvent.h new file mode 100644 index 0000000..bfdf8fb --- /dev/null +++ b/Source/WebCore/dom/DeviceOrientationEvent.h @@ -0,0 +1,62 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 DeviceOrientationEvent_h +#define DeviceOrientationEvent_h + +#include "Event.h" + +namespace WebCore { + +class DeviceOrientation; + +class DeviceOrientationEvent : public Event { +public: + ~DeviceOrientationEvent(); + static PassRefPtr<DeviceOrientationEvent> create() + { + return adoptRef(new DeviceOrientationEvent); + } + static PassRefPtr<DeviceOrientationEvent> create(const AtomicString& eventType, DeviceOrientation* orientation) + { + return adoptRef(new DeviceOrientationEvent(eventType, orientation)); + } + + void initDeviceOrientationEvent(const AtomicString& type, bool bubbles, bool cancelable, DeviceOrientation*); + + virtual bool isDeviceOrientationEvent() const { return true; } + + DeviceOrientation* orientation() const { return m_orientation.get(); } + +private: + DeviceOrientationEvent(); + DeviceOrientationEvent(const AtomicString& eventType, DeviceOrientation*); + + RefPtr<DeviceOrientation> m_orientation; +}; + +} // namespace WebCore + +#endif // DeviceOrientationEvent_h diff --git a/Source/WebCore/dom/DeviceOrientationEvent.idl b/Source/WebCore/dom/DeviceOrientationEvent.idl new file mode 100644 index 0000000..5582f0d --- /dev/null +++ b/Source/WebCore/dom/DeviceOrientationEvent.idl @@ -0,0 +1,37 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 core { + + interface [ + Conditional=DEVICE_ORIENTATION + ] DeviceOrientationEvent : Event { + readonly attribute [Custom] double alpha; + readonly attribute [Custom] double beta; + readonly attribute [Custom] double gamma; + [Custom] void initDeviceOrientationEvent(in DOMString type, in boolean bubbles, in boolean cancelable, in double alpha, in double beta, in double gamma); + }; + +} diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp new file mode 100644 index 0000000..0c865c9 --- /dev/null +++ b/Source/WebCore/dom/Document.cpp @@ -0,0 +1,5058 @@ +/* + * 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, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2008, 2009 Google Inc. All rights reserved. + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "AnimationController.h" +#include "AsyncScriptRunner.h" +#include "Attr.h" +#include "Attribute.h" +#include "CDATASection.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "CSSValueKeywords.h" +#include "CachedCSSStyleSheet.h" +#include "CachedResourceLoader.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "Comment.h" +#include "Console.h" +#include "CookieJar.h" +#include "CustomEvent.h" +#include "DateComponents.h" +#include "DOMImplementation.h" +#include "DOMWindow.h" +#include "DeviceMotionEvent.h" +#include "DeviceOrientationEvent.h" +#include "DocumentFragment.h" +#include "DocumentLoader.h" +#include "DocumentMarkerController.h" +#include "DocumentType.h" +#include "EditingText.h" +#include "Editor.h" +#include "EntityReference.h" +#include "Event.h" +#include "EventHandler.h" +#include "EventListener.h" +#include "EventNames.h" +#include "EventQueue.h" +#include "ExceptionCode.h" +#include "FocusController.h" +#include "FormAssociatedElement.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "HashChangeEvent.h" +#include "HTMLAllCollection.h" +#include "HTMLAnchorElement.h" +#include "HTMLBodyElement.h" +#include "HTMLCanvasElement.h" +#include "HTMLCollection.h" +#include "HTMLDocument.h" +#include "HTMLElementFactory.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLHeadElement.h" +#include "HTMLIFrameElement.h" +#include "HTMLInputElement.h" +#include "HTMLLinkElement.h" +#include "HTMLMapElement.h" +#include "HTMLNameCollection.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "HTMLStyleElement.h" +#include "HTMLTitleElement.h" +#include "HTTPParsers.h" +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "ImageLoader.h" +#include "InspectorController.h" +#include "InspectorInstrumentation.h" +#include "KeyboardEvent.h" +#include "Logging.h" +#include "MediaQueryList.h" +#include "MediaQueryMatcher.h" +#include "MessageEvent.h" +#include "MouseEvent.h" +#include "MouseEventWithHitTestResults.h" +#include "MutationEvent.h" +#include "NameNodeList.h" +#include "NodeFilter.h" +#include "NodeIterator.h" +#include "NodeWithIndex.h" +#include "OverflowEvent.h" +#include "Page.h" +#include "PageGroup.h" +#include "PageTransitionEvent.h" +#include "PlatformKeyboardEvent.h" +#include "PopStateEvent.h" +#include "ProcessingInstruction.h" +#include "ProgressEvent.h" +#include "RegisteredEventListener.h" +#include "RenderArena.h" +#include "RenderLayer.h" +#include "RenderTextControl.h" +#include "RenderView.h" +#include "RenderWidget.h" +#include "ScriptController.h" +#include "ScriptElement.h" +#include "ScriptEventListener.h" +#include "SecurityOrigin.h" +#include "SegmentedString.h" +#include "SelectionController.h" +#include "Settings.h" +#include "StaticHashSetNodeList.h" +#include "StyleSheetList.h" +#include "TextEvent.h" +#include "TextResourceDecoder.h" +#include "Timer.h" +#include "TransformSource.h" +#include "TreeWalker.h" +#include "UIEvent.h" +#include "UserContentURLPattern.h" +#include "WebKitAnimationEvent.h" +#include "WebKitTransitionEvent.h" +#include "WheelEvent.h" +#include "XMLDocumentParser.h" +#include "XMLHttpRequest.h" +#include "XMLNSNames.h" +#include "XMLNames.h" +#include "XSSAuditor.h" +#include "htmlediting.h" +#include <wtf/CurrentTime.h> +#include <wtf/HashFunctions.h> +#include <wtf/MainThread.h> +#include <wtf/PassRefPtr.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringBuffer.h> + +#if ENABLE(SHARED_WORKERS) +#include "SharedWorkerRepository.h" +#endif + +#if ENABLE(DOM_STORAGE) +#include "StorageEvent.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(SVG) +#include "SVGDocumentExtensions.h" +#include "SVGElementFactory.h" +#include "SVGNames.h" +#include "SVGStyleElement.h" +#include "SVGZoomEvent.h" +#endif + +#ifdef ANDROID_META_SUPPORT +#include "PlatformBridge.h" +#include "Settings.h" +#endif + +#ifdef ANDROID_RESET_SELECTION +#include "CacheBuilder.h" +#include "HTMLTextAreaElement.h" +#endif + +#ifdef ANDROID_INSTRUMENT +#include "TimeCounter.h" +#endif + +#if ENABLE(TOUCH_EVENTS) +#if USE(V8) +#include "RuntimeEnabledFeatures.h" +#endif +#include "TouchEvent.h" +#endif + +#if ENABLE(WML) +#include "WMLDocument.h" +#include "WMLElement.h" +#include "WMLElementFactory.h" +#include "WMLNames.h" +#endif + +#if ENABLE(MATHML) +#include "MathMLElement.h" +#include "MathMLElementFactory.h" +#include "MathMLNames.h" +#endif + +#if ENABLE(XHTMLMP) +#include "HTMLNoScriptElement.h" +#endif + +#if ENABLE(FULLSCREEN_API) +#include "RenderFullScreen.h" +#endif + +using namespace std; +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +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; + +// These functions can't have internal linkage because they are used as template arguments. +bool keyMatchesId(AtomicStringImpl*, Element*); +bool keyMatchesMapName(AtomicStringImpl*, Element*); +bool keyMatchesLowercasedMapName(AtomicStringImpl*, Element*); + +// 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 toRenderWidget(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()); +} + +static bool disableRangeMutation(Page* page) +{ +#if PLATFORM(MAC) + // Disable Range mutation on document modifications in Tiger and Leopard Mail + // See <rdar://problem/5865171> + return page && (page->settings()->needsLeopardMailQuirks() || page->settings()->needsTigerMailQuirks()); +#else + return false; +#endif +} + +static HashSet<Document*>* documentsThatNeedStyleRecalc = 0; + +class DocumentWeakReference : public ThreadSafeShared<DocumentWeakReference> { +public: + static PassRefPtr<DocumentWeakReference> create(Document* document) + { + return adoptRef(new DocumentWeakReference(document)); + } + + Document* document() + { + ASSERT(isMainThread()); + return m_document; + } + + void clear() + { + ASSERT(isMainThread()); + m_document = 0; + } + +private: + DocumentWeakReference(Document* document) + : m_document(document) + { + ASSERT(isMainThread()); + } + + Document* m_document; +}; + +Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML, const KURL& baseURL) + : ContainerNode(0) + , m_compatibilityMode(NoQuirksMode) + , m_compatibilityModeLocked(false) + , m_domTreeVersion(0) +#ifdef ANDROID_STYLE_VERSION + , m_styleVersion(0) +#endif + , m_styleSheets(StyleSheetList::create(this)) + , m_readyState(Complete) + , m_styleRecalcTimer(this, &Document::styleRecalcTimerFired) + , m_pendingStyleRecalcShouldForce(false) + , m_frameElementsShouldIgnoreScrolling(false) + , m_containsValidityStyleRules(false) + , m_updateFocusAppearanceRestoresSelection(false) + , m_ignoreDestructiveWriteCount(0) + , m_title("") + , m_rawTitle("") + , m_titleSetExplicitly(false) + , m_updateFocusAppearanceTimer(this, &Document::updateFocusAppearanceTimerFired) + , m_startTime(currentTime()) + , m_overMinimumLayoutThreshold(false) + , m_extraLayoutDelay(0) + , m_asyncScriptRunner(AsyncScriptRunner::create(this)) + , m_xmlVersion("1.0") + , m_xmlStandalone(false) + , m_savedRenderer(0) + , m_designMode(inherit) + , m_selfOnlyRefCount(0) +#if ENABLE(SVG) + , m_svgExtensions(0) +#endif +#if ENABLE(DASHBOARD_SUPPORT) + , m_hasDashboardRegions(false) + , m_dashboardRegionsDirty(false) +#endif + , m_accessKeyMapValid(false) + , m_createRenderers(true) + , m_inPageCache(false) + , m_useSecureKeyboardEntryWhenActive(false) + , m_isXHTML(isXHTML) + , m_isHTML(isHTML) + , m_numNodeListCaches(0) +#if USE(JSC) + , m_normalWorldWrapperCache(0) +#endif + , m_usingGeolocation(false) + , m_eventQueue(adoptPtr(new EventQueue)) +#if ENABLE(WML) + , m_containsWMLContent(false) +#endif + , m_weakReference(DocumentWeakReference::create(this)) + , m_idAttributeName(idAttr) +#if ENABLE(FULLSCREEN_API) + , m_isFullScreen(0) + , m_areKeysEnabledInFullScreen(0) + , m_fullScreenRenderer(0) + , m_fullScreenChangeDelayTimer(this, &Document::fullScreenChangeDelayTimerFired) +#endif + , m_loadEventDelayCount(0) + , m_loadEventDelayTimer(this, &Document::loadEventDelayTimerFired) + , m_directionSetOnDocumentElement(false) + , m_writingModeSetOnDocumentElement(false) +{ + m_document = this; + + m_pageGroupUserSheetCacheValid = false; + + m_printing = false; + m_paginatedForScreen = false; + + m_ignoreAutofocus = false; + + m_frame = frame; + + if (frame || !url.isEmpty()) + setURL(url); + + // Setting of m_baseURL needs to happen after the setURL call, since that + // calls updateBaseURL, which would clobber the passed in value. + if (!baseURL.isNull()) + m_baseURL = baseURL; + +#if !PLATFORM(ANDROID) + m_axObjectCache = 0; +#endif + + m_markers = new DocumentMarkerController(); + + m_cachedResourceLoader = new CachedResourceLoader(this); + + m_visuallyOrdered = false; + m_bParsing = false; + m_wellFormed = false; + + m_textColor = Color::black; + m_listenerTypes = 0; + setInDocument(); + m_inStyleRecalc = false; + m_closeAfterStyleRecalc = false; + + m_usesDescendantRules = false; + m_usesSiblingRules = false; + m_usesFirstLineRules = false; + m_usesFirstLetterRules = false; + m_usesBeforeAfterRules = false; + m_usesRemUnits = false; + m_usesLinkRules = false; + + m_gotoAnchorNeededAfterStylesheetsLoad = false; + + m_didCalculateStyleSelector = false; + m_pendingStylesheets = 0; + m_ignorePendingStylesheets = false; + m_hasNodesWithPlaceholderStyle = false; + m_pendingSheetLayout = NoLayoutWithPendingSheets; + + m_cssTarget = 0; + + resetLinkColor(); + resetVisitedLinkColor(); + resetActiveLinkColor(); + + m_processingLoadEvent = false; + + initSecurityContext(); + initDNSPrefetch(); + + static int docID = 0; + m_docID = docID++; +#if ENABLE(XHTMLMP) + m_shouldProcessNoScriptElement = !(m_frame && m_frame->script()->canExecuteScripts(NotAboutToExecuteScript)); +#endif +} + +inline void Document::DocumentOrderedMap::clear() +{ + m_map.clear(); + m_duplicateCounts.clear(); +} + +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. + selfOnlyRef(); + + // 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; +#if ENABLE(FULLSCREEN_API) + m_fullScreenElement = 0; +#endif + + // removeAllChildren() doesn't always unregister IDs, do it upfront to avoid having stale references in the map. + m_elementsById.clear(); + + removeAllChildren(); + + m_markers->detach(); + + detachParser(); + + m_cssCanvasElements.clear(); + +#ifndef NDEBUG + m_inRemovedLastRefFunction = false; +#endif + + selfOnlyDeref(); + } else { +#ifndef NDEBUG + m_deletionHasBegun = true; +#endif + delete this; + } +} + +Document::~Document() +{ + ASSERT(!renderer()); + ASSERT(!m_inPageCache); + ASSERT(!m_savedRenderer); + ASSERT(m_ranges.isEmpty()); + ASSERT(!m_styleRecalcTimer.isActive()); + + m_asyncScriptRunner.clear(); + + removeAllEventListeners(); + +#if USE(JSC) + destroyAllWrapperCaches(); +#endif + + // Currently we believe that Document can never outlive the parser. + // Although the Document may be replaced synchronously, DocumentParsers + // generally keep at least one reference to an Element which would in turn + // has a reference to the Document. If you hit this ASSERT, then that + // assumption is wrong. DocumentParser::detach() should ensure that even + // if the DocumentParser outlives the Document it won't cause badness. + ASSERT(!m_parser || m_parser->refCount() == 1); + detachParser(); + m_document = 0; + m_cachedResourceLoader.clear(); + + m_renderArena.clear(); + + clearAXObjectCache(); + + m_decoder = 0; + + for (size_t i = 0; i < m_nameCollectionInfo.size(); ++i) + deleteAllValues(m_nameCollectionInfo[i]); + + if (m_styleSheets) + m_styleSheets->documentDestroyed(); + + if (m_elemSheet) + m_elemSheet->clearOwnerNode(); + if (m_mappedElementSheet) + m_mappedElementSheet->clearOwnerNode(); + if (m_pageUserSheet) + m_pageUserSheet->clearOwnerNode(); + if (m_pageGroupUserSheets) { + for (size_t i = 0; i < m_pageGroupUserSheets->size(); ++i) + (*m_pageGroupUserSheets)[i]->clearOwnerNode(); + } + + m_weakReference->clear(); + + if (m_mediaQueryMatcher) + m_mediaQueryMatcher->documentDestroyed(); +} + +MediaQueryMatcher* Document::mediaQueryMatcher() +{ + if (!m_mediaQueryMatcher) + m_mediaQueryMatcher = MediaQueryMatcher::create(this); + return m_mediaQueryMatcher.get(); +} + +#if USE(JSC) +Document::JSWrapperCache* Document::createWrapperCache(DOMWrapperWorld* world) +{ + JSWrapperCache* wrapperCache = new JSWrapperCache; + m_wrapperCacheMap.set(world, wrapperCache); + if (world->isNormal()) { + ASSERT(!m_normalWorldWrapperCache); + m_normalWorldWrapperCache = wrapperCache; + } + world->didCreateWrapperCache(this); + return wrapperCache; +} + +void Document::destroyWrapperCache(DOMWrapperWorld* world) +{ + Document::JSWrapperCache* wrappers = wrapperCacheMap().take(world); + ASSERT(wrappers); + delete wrappers; + world->didDestroyWrapperCache(this); +} + +void Document::destroyAllWrapperCaches() +{ + JSWrapperCacheMap& wrapperCacheMap = this->wrapperCacheMap(); + while (!wrapperCacheMap.isEmpty()) + destroyWrapperCache(wrapperCacheMap.begin()->first); +} +#endif + +void Document::setCompatibilityMode(CompatibilityMode mode) +{ + if (m_compatibilityModeLocked || mode == m_compatibilityMode) + return; + ASSERT(!documentElement() && !m_styleSheets->length()); + bool wasInQuirksMode = inQuirksMode(); + m_compatibilityMode = mode; + if (inQuirksMode() != wasInQuirksMode) { + // All user stylesheets have to reparse using the different mode. + clearPageUserSheet(); + clearPageGroupUserSheets(); + } +} + +String Document::compatMode() const +{ + return inQuirksMode() ? "BackCompat" : "CSS1Compat"; +} + +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. + ASSERT(!m_docType || !docType); + m_docType = docType; + if (m_docType) + m_docType->setDocument(this); +#ifdef ANDROID_META_SUPPORT + if (m_docType && !ownerElement() + && m_docType->publicId().startsWith("-//wapforum//dtd xhtml mobile 1.", false)) { + // fit mobile sites directly in the screen + if (Frame *f = frame()) + f->settings()->setMetadataSettings("width", "device-width"); + if (FrameView* frameView = view()) + PlatformBridge::updateViewport(frameView); + } +#endif +} + +DOMImplementation* Document::implementation() const +{ + if (!m_implementation) + m_implementation = DOMImplementation::create(); + 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; +} + +void Document::cacheDocumentElement() const +{ + ASSERT(!m_documentElement); + Node* n = firstChild(); + while (n && !n->isElementNode()) + n = n->nextSibling(); + m_documentElement = static_cast<Element*>(n); +} + +PassRefPtr<Element> Document::createElement(const AtomicString& name, ExceptionCode& ec) +{ + if (!isValidName(name)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + + if (m_isXHTML) + return HTMLElementFactory::createHTMLElement(QualifiedName(nullAtom, name, xhtmlNamespaceURI), this, 0, false); + + return createElement(QualifiedName(nullAtom, name, nullAtom), false); +} + +PassRefPtr<DocumentFragment> Document::createDocumentFragment() +{ + return DocumentFragment::create(document()); +} + +PassRefPtr<Text> Document::createTextNode(const String& data) +{ + return Text::create(this, data); +} + +PassRefPtr<Comment> Document::createComment(const String& data) +{ + return Comment::create(this, data); +} + +PassRefPtr<CDATASection> Document::createCDATASection(const String& data, ExceptionCode& ec) +{ + if (isHTMLDocument()) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + return CDATASection::create(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 ProcessingInstruction::create(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 EntityReference::create(this, name); +} + +PassRefPtr<EditingText> Document::createEditingTextNode(const String& text) +{ + return EditingText::create(this, text); +} + +PassRefPtr<CSSStyleDeclaration> Document::createCSSStyleDeclaration() +{ + return CSSMutableStyleDeclaration::create(); +} + +PassRefPtr<Node> Document::importNode(Node* importedNode, bool deep, ExceptionCode& ec) +{ + ec = 0; + + if (!importedNode +#if ENABLE(SVG) && ENABLE(DASHBOARD_SUPPORT) + || (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; + + NamedNodeMap* 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: + return Attr::create(0, this, static_cast<Attr*>(importedNode)->attr()->clone()); + 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->setSpecified(true); + break; + } + default: + if (source->hasTagName(iframeTag)) { + HTMLIFrameElement* iframe = static_cast<HTMLIFrameElement*>(source.get()); + if (frame() && frame()->tree()->isDescendantOf(iframe->contentFrame())) { + ec = HIERARCHY_REQUEST_ERR; + return 0; + } + iframe->setRemainsAliveOnRemovalFromTree(attached() && source->attached()); + } + + 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; +} + +bool Document::hasPrefixNamespaceMismatch(const QualifiedName& qName) +{ + // These checks are from DOM Core Level 2, createElementNS + // http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrElNS + if (!qName.prefix().isEmpty() && qName.namespaceURI().isNull()) // createElementNS(null, "html:div") + return true; + if (qName.prefix() == xmlAtom && qName.namespaceURI() != XMLNames::xmlNamespaceURI) // createElementNS("http://www.example.com", "xml:lang") + return true; + + // Required by DOM Level 3 Core and unspecified by DOM Level 2 Core: + // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS + // createElementNS("http://www.w3.org/2000/xmlns/", "foo:bar"), createElementNS(null, "xmlns:bar") + if ((qName.prefix() == xmlnsAtom && qName.namespaceURI() != XMLNSNames::xmlnsNamespaceURI) || (qName.prefix() != xmlnsAtom && qName.namespaceURI() == XMLNSNames::xmlnsNamespaceURI)) + return true; + + return false; +} + +// FIXME: This should really be in a possible ElementFactory class +PassRefPtr<Element> Document::createElement(const QualifiedName& qName, bool createdByParser) +{ + 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, this, 0, createdByParser); +#if ENABLE(SVG) + else if (qName.namespaceURI() == SVGNames::svgNamespaceURI) + e = SVGElementFactory::createSVGElement(qName, this, createdByParser); +#endif +#if ENABLE(WML) + else if (qName.namespaceURI() == WMLNames::wmlNamespaceURI) + e = WMLElementFactory::createWMLElement(qName, this, createdByParser); + else if (isWMLDocument()) + e = WMLElementFactory::createWMLElement(QualifiedName(nullAtom, qName.localName(), WMLNames::wmlNamespaceURI), this, createdByParser); +#endif +#if ENABLE(MATHML) + else if (qName.namespaceURI() == MathMLNames::mathmlNamespaceURI) + e = MathMLElementFactory::createMathMLElement(qName, this, createdByParser); +#endif + + if (!e) + e = Element::create(qName, document()); + + // <image> uses imgTag so we need a special rule. +#if ENABLE(WML) + if (!isWMLDocument()) +#endif + ASSERT((qName.matches(imageTag) && e->tagQName().matches(imgTag) && e->tagQName().prefix() == qName.prefix()) || qName == e->tagQName()); + + return e.release(); +} + +PassRefPtr<Element> Document::createElementNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec) +{ + String prefix, localName; + if (!parseQualifiedName(qualifiedName, prefix, localName, ec)) + return 0; + + QualifiedName qName(prefix, localName, namespaceURI); + if (hasPrefixNamespaceMismatch(qName)) { + ec = NAMESPACE_ERR; + return 0; + } + + return createElement(qName, false); +} + +inline void Document::DocumentOrderedMap::add(AtomicStringImpl* key, Element* element) +{ + ASSERT(key); + ASSERT(element); + + if (!m_duplicateCounts.contains(key)) { + // Fast path. The key is not already in m_duplicateCounts, so we assume that it's + // also not already in m_map and try to add it. If that add succeeds, we're done. + pair<Map::iterator, bool> addResult = m_map.add(key, element); + if (addResult.second) + return; + + // The add failed, so this key was already cached in m_map. + // There are multiple elements with this key. Remove the m_map + // cache for this key so get searches for it next time it is called. + m_map.remove(addResult.first); + m_duplicateCounts.add(key); + } else { + // There are multiple elements with this key. Remove the m_map + // cache for this key so get will search for it next time it is called. + Map::iterator cachedItem = m_map.find(key); + if (cachedItem != m_map.end()) { + m_map.remove(cachedItem); + m_duplicateCounts.add(key); + } + } + + m_duplicateCounts.add(key); +} + +inline void Document::DocumentOrderedMap::remove(AtomicStringImpl* key, Element* element) +{ + ASSERT(key); + ASSERT(element); + + m_map.checkConsistency(); + Map::iterator cachedItem = m_map.find(key); + if (cachedItem != m_map.end() && cachedItem->second == element) + m_map.remove(cachedItem); + else + m_duplicateCounts.remove(key); +} + +template<bool keyMatches(AtomicStringImpl*, Element*)> inline Element* Document::DocumentOrderedMap::get(AtomicStringImpl* key, const Document* document) const +{ + ASSERT(key); + + m_map.checkConsistency(); + + Element* element = m_map.get(key); + if (element) + return element; + + if (m_duplicateCounts.contains(key)) { + // We know there's at least one node that matches; iterate to find the first one. + for (Node* node = document->firstChild(); node; node = node->traverseNextNode()) { + if (!node->isElementNode()) + continue; + element = static_cast<Element*>(node); + if (!keyMatches(key, element)) + continue; + m_duplicateCounts.remove(key); + m_map.set(key, element); + return element; + } + ASSERT_NOT_REACHED(); + } + + return 0; +} + +inline bool keyMatchesId(AtomicStringImpl* key, Element* element) +{ + return element->hasID() && element->getIdAttribute().impl() == key; +} + +Element* Document::getElementById(const AtomicString& elementId) const +{ + if (elementId.isEmpty()) + return 0; + return m_elementsById.get<keyMatchesId>(elementId.impl(), this); +} + +String Document::readyState() const +{ + DEFINE_STATIC_LOCAL(const String, loading, ("loading")); + DEFINE_STATIC_LOCAL(const String, interactive, ("interactive")); + DEFINE_STATIC_LOCAL(const String, complete, ("complete")); + + switch (m_readyState) { + case Loading: + return loading; + case Interactive: + return interactive; + case Complete: + return complete; + } + + ASSERT_NOT_REACHED(); + return String(); +} + +void Document::setReadyState(ReadyState readyState) +{ + if (readyState == m_readyState) + return; + + switch (readyState) { + case Loading: + if (!m_documentTiming.domLoading) + m_documentTiming.domLoading = currentTime(); + break; + case Interactive: + if (!m_documentTiming.domInteractive) + m_documentTiming.domInteractive = currentTime(); + break; + case Complete: + if (!m_documentTiming.domComplete) + m_documentTiming.domComplete = currentTime(); + break; + } + + m_readyState = readyState; + dispatchEvent(Event::create(eventNames().readystatechangeEvent, false, false)); +} + +String Document::encoding() const +{ + if (TextResourceDecoder* d = decoder()) + return d->encoding().domName(); + 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) +{ + if (!implementation()->hasFeature("XML", String())) { + ec = NOT_SUPPORTED_ERR; + return; + } + + if (!XMLDocumentParser::supportsXMLVersion(version)) { + 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; +} + +void Document::setDocumentURI(const String& uri) +{ + m_documentURI = uri; + updateBaseURL(); +} + +KURL Document::baseURI() const +{ + return m_baseURL; +} + +// FIXME: We need to discuss the DOM API here at some point. Ideas: +// * making it receive a rect as parameter, i.e. nodesFromRect(x, y, w, h); +// * making it receive the expading size of each direction separately, +// i.e. nodesFromRect(x, y, topSize, rightSize, bottomSize, leftSize); +PassRefPtr<NodeList> Document::nodesFromRect(int centerX, int centerY, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding, bool ignoreClipping) const +{ + // FIXME: Share code between this, elementFromPoint and caretRangeFromPoint. + if (!renderer()) + return 0; + Frame* frame = this->frame(); + if (!frame) + return 0; + FrameView* frameView = frame->view(); + if (!frameView) + return 0; + + float zoomFactor = frame->pageZoomFactor(); + IntPoint point = roundedIntPoint(FloatPoint(centerX * zoomFactor + view()->scrollX(), centerY * zoomFactor + view()->scrollY())); + + int type = HitTestRequest::ReadOnly | HitTestRequest::Active; + + // When ignoreClipping is false, this method returns null for coordinates outside of the viewport. + if (ignoreClipping) + type |= HitTestRequest::IgnoreClipping; + else if (!frameView->visibleContentRect().intersects(HitTestResult::rectForPoint(point, topPadding, rightPadding, bottomPadding, leftPadding))) + return 0; + + HitTestRequest request(type); + + // Passing a zero padding will trigger a rect hit test, however for the purposes of nodesFromRect, + // we special handle this case in order to return a valid NodeList. + if (!topPadding && !rightPadding && !bottomPadding && !leftPadding) { + HitTestResult result(point); + return handleZeroPadding(request, result); + } + + HitTestResult result(point, topPadding, rightPadding, bottomPadding, leftPadding); + renderView()->layer()->hitTest(request, result); + + return StaticHashSetNodeList::adopt(result.rectBasedTestResult()); +} + +PassRefPtr<NodeList> Document::handleZeroPadding(const HitTestRequest& request, HitTestResult& result) const +{ + renderView()->layer()->hitTest(request, result); + + Node* node = result.innerNode(); + if (!node) + return 0; + + node = node->shadowAncestorNode(); + ListHashSet<RefPtr<Node> > list; + list.add(node); + return StaticHashSetNodeList::adopt(list); +} + +Element* Document::elementFromPoint(int x, int y) const +{ + // FIXME: Share code between this and caretRangeFromPoint. + if (!renderer()) + return 0; + Frame* frame = this->frame(); + if (!frame) + return 0; + FrameView* frameView = frame->view(); + if (!frameView) + return 0; + + float zoomFactor = frame->pageZoomFactor(); + IntPoint point = roundedIntPoint(FloatPoint(x * zoomFactor + view()->scrollX(), y * zoomFactor + view()->scrollY())); + + if (!frameView->visibleContentRect().contains(point)) + return 0; + + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); + HitTestResult result(point); + renderView()->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); +} + +PassRefPtr<Range> Document::caretRangeFromPoint(int x, int y) +{ + // FIXME: Share code between this and elementFromPoint. + if (!renderer()) + return 0; + Frame* frame = this->frame(); + if (!frame) + return 0; + FrameView* frameView = frame->view(); + if (!frameView) + return 0; + + float zoomFactor = frame->pageZoomFactor(); + IntPoint point = roundedIntPoint(FloatPoint(x * zoomFactor + view()->scrollX(), y * zoomFactor + view()->scrollY())); + + if (!frameView->visibleContentRect().contains(point)) + return 0; + + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); + HitTestResult result(point); + renderView()->layer()->hitTest(request, result); + + Node* node = result.innerNode(); + if (!node) + return 0; + + Node* shadowAncestorNode = node->shadowAncestorNode(); + if (shadowAncestorNode != node) { + unsigned offset = shadowAncestorNode->nodeIndex(); + ContainerNode* container = shadowAncestorNode->parentNode(); + return Range::create(this, container, offset, container, offset); + } + + RenderObject* renderer = node->renderer(); + if (!renderer) + return 0; + VisiblePosition visiblePosition = renderer->positionForPoint(result.localPoint()); + if (visiblePosition.isNull()) + return 0; + + Position rangeCompliantPosition = rangeCompliantEquivalent(visiblePosition); + return Range::create(this, rangeCompliantPosition, rangeCompliantPosition); +} + +void Document::addElementById(const AtomicString& elementId, Element* element) +{ + m_elementsById.add(elementId.impl(), element); +} + +void Document::removeElementById(const AtomicString& elementId, Element* element) +{ + m_elementsById.remove(elementId.impl(), element); +} + +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()); +} + +/* + * Performs three operations: + * 1. Convert control characters to spaces + * 2. Trim leading and trailing spaces + * 3. Collapse internal whitespace. + */ +static inline String canonicalizedTitle(Document* document, const String& title) +{ + const UChar* characters = title.characters(); + unsigned length = title.length(); + unsigned i; + + StringBuffer buffer(length); + unsigned builderIndex = 0; + + // Skip leading spaces and leading characters that would convert to spaces + for (i = 0; i < length; ++i) { + UChar c = characters[i]; + if (!(c <= 0x20 || c == 0x7F)) + break; + } + + if (i == length) + return ""; + + // Replace control characters with spaces, and backslashes with currency symbols, and collapse whitespace. + bool previousCharWasWS = false; + for (; i < length; ++i) { + UChar c = characters[i]; + if (c <= 0x20 || c == 0x7F || (WTF::Unicode::category(c) & (WTF::Unicode::Separator_Line | WTF::Unicode::Separator_Paragraph))) { + if (previousCharWasWS) + continue; + buffer[builderIndex++] = ' '; + previousCharWasWS = true; + } else { + buffer[builderIndex++] = c; + previousCharWasWS = false; + } + } + + // Strip trailing spaces + while (builderIndex > 0) { + --builderIndex; + if (buffer[builderIndex] != ' ') + break; + } + + if (!builderIndex && buffer[builderIndex] == ' ') + return ""; + + buffer.shrink(builderIndex + 1); + + // Replace the backslashes with currency symbols if the encoding requires it. + document->displayBufferModifiedByEncoding(buffer.characters(), buffer.length()); + + return String::adopt(buffer); +} + +void Document::updateTitle() +{ + m_title = canonicalizedTitle(this, m_rawTitle); + 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()) { + m_titleElement = createElement(titleTag, false); + ExceptionCode ec = 0; + 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_rawTitle == title) + return; + + m_rawTitle = title; + updateTitle(); + + if (m_titleSetExplicitly && m_titleElement && m_titleElement->hasTagName(titleTag) && !titleElement) + 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_rawTitle.isEmpty()) { + m_rawTitle = ""; + 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 Range::create(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 NodeIterator::create(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 TreeWalker::create(root, whatToShow, filter, expandEntityReferences); +} + +void Document::scheduleForcedStyleRecalc() +{ + m_pendingStyleRecalcShouldForce = true; + scheduleStyleRecalc(); +} + +void Document::scheduleStyleRecalc() +{ + if (m_styleRecalcTimer.isActive() || inPageCache()) + return; + + ASSERT(childNeedsStyleRecalc() || m_pendingStyleRecalcShouldForce); + + if (!documentsThatNeedStyleRecalc) + documentsThatNeedStyleRecalc = new HashSet<Document*>; + documentsThatNeedStyleRecalc->add(this); + + // FIXME: Why on earth is this here? This is clearly misplaced. + if (m_accessKeyMapValid) { + m_accessKeyMapValid = false; + m_elementsByAccessKey.clear(); + } + + m_styleRecalcTimer.startOneShot(0); +} + +void Document::unscheduleStyleRecalc() +{ + ASSERT(!childNeedsStyleRecalc()); + + if (documentsThatNeedStyleRecalc) + documentsThatNeedStyleRecalc->remove(this); + + m_styleRecalcTimer.stop(); + m_pendingStyleRecalcShouldForce = false; +} + +bool Document::isPendingStyleRecalc() const +{ + return m_styleRecalcTimer.isActive() && !m_inStyleRecalc; +} + +void Document::styleRecalcTimerFired(Timer<Document>*) +{ + updateStyleIfNeeded(); +} + +bool Document::childNeedsAndNotInStyleRecalc() +{ + return childNeedsStyleRecalc() && !m_inStyleRecalc; +} + +void Document::recalcStyle(StyleChange change) +{ + // we should not enter style recalc while painting + if (view() && view()->isPainting()) { + ASSERT(!view()->isPainting()); + return; + } + + if (m_inStyleRecalc) + return; // Guard against re-entrancy. -dwh + + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRecalculateStyle(this); + + m_inStyleRecalc = true; + suspendPostAttachCallbacks(); + RenderWidget::suspendWidgetHierarchyUpdates(); + if (view()) + view()->pauseScheduledEvents(); + +#ifdef ANDROID_INSTRUMENT + android::TimeCounter::start(android::TimeCounter::CalculateStyleTimeCounter); +#endif + + ASSERT(!renderer() || renderArena()); + if (!renderer() || !renderArena()) + goto bail_out; + + if (m_pendingStyleRecalcShouldForce) + change = Force; + + if (change == Force) { + // style selector may set this again during recalc + m_hasNodesWithPlaceholderStyle = false; + + RefPtr<RenderStyle> documentStyle = CSSStyleSelector::styleForDocument(this); + StyleChange ch = diff(documentStyle.get(), renderer()->style()); + if (renderer() && ch != NoChange) + renderer()->setStyle(documentStyle.release()); + } + + for (Node* n = firstChild(); n; n = n->nextSibling()) + if (change >= Inherit || n->childNeedsStyleRecalc() || n->needsStyleRecalc()) + n->recalcStyle(change); + +#ifdef ANDROID_INSTRUMENT + android::TimeCounter::record(android::TimeCounter::CalculateStyleTimeCounter, __FUNCTION__); +#endif + +#if USE(ACCELERATED_COMPOSITING) + if (view()) { + bool layoutPending = view()->layoutPending() || renderer()->needsLayout(); + // If we didn't update compositing layers because of layout(), we need to do so here. + if (!layoutPending) + view()->updateCompositingLayers(); + } +#endif + +bail_out: + clearNeedsStyleRecalc(); + clearChildNeedsStyleRecalc(); + unscheduleStyleRecalc(); + + if (view()) + view()->resumeScheduledEvents(); + RenderWidget::resumeWidgetHierarchyUpdates(); + 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(); + } + + InspectorInstrumentation::didRecalculateStyle(cookie); +} + +void Document::updateStyleIfNeeded() +{ + ASSERT(isMainThread()); + ASSERT(!view() || (!view()->isInLayout() && !view()->isPainting())); + + if ((!m_pendingStyleRecalcShouldForce && !childNeedsStyleRecalc()) || inPageCache()) + return; + + if (m_frame) + m_frame->animation()->beginAnimationUpdate(); + + recalcStyle(NoChange); + + // Tell the animation controller that updateStyleIfNeeded is finished and it can do any post-processing + if (m_frame) + m_frame->animation()->endAnimationUpdate(); +} + +void Document::updateStyleForAllDocuments() +{ + ASSERT(isMainThread()); + if (!documentsThatNeedStyleRecalc) + return; + + while (documentsThatNeedStyleRecalc->size()) { + HashSet<Document*>::iterator it = documentsThatNeedStyleRecalc->begin(); + Document* doc = *it; + documentsThatNeedStyleRecalc->remove(doc); + doc->updateStyleIfNeeded(); + } +} + +void Document::updateLayout() +{ + ASSERT(isMainThread()); + if (Element* oe = ownerElement()) + oe->document()->updateLayout(); + + updateStyleIfNeeded(); + + // 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; + styleSelectorChanged(RecalcStyleImmediately); + } 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 immediately. + recalcStyle(Force); + } + + updateLayout(); + + m_ignorePendingStylesheets = oldIgnore; +} + +PassRefPtr<RenderStyle> Document::styleForElementIgnoringPendingStylesheets(Element* element) +{ + ASSERT_ARG(element, element->document() == this); + + bool oldIgnore = m_ignorePendingStylesheets; + m_ignorePendingStylesheets = true; + RefPtr<RenderStyle> style = styleSelector()->styleForElement(element, element->parentNode() ? element->parentNode()->computedStyle() : 0); + m_ignorePendingStylesheets = oldIgnore; + return style.release(); +} + +PassRefPtr<RenderStyle> Document::styleForPage(int pageIndex) +{ + RefPtr<RenderStyle> style = styleSelector()->styleForPage(pageIndex); + return style.release(); +} + +bool Document::isPageBoxVisible(int pageIndex) +{ + RefPtr<RenderStyle> style = styleForPage(pageIndex); + return style->visibility() != HIDDEN; // display property doesn't apply to @page. +} + +void Document::pageSizeAndMarginsInPixels(int pageIndex, IntSize& pageSize, int& marginTop, int& marginRight, int& marginBottom, int& marginLeft) +{ + RefPtr<RenderStyle> style = styleForPage(pageIndex); + + int width = pageSize.width(); + int height = pageSize.height(); + switch (style->pageSizeType()) { + case PAGE_SIZE_AUTO: + break; + case PAGE_SIZE_AUTO_LANDSCAPE: + if (width < height) + std::swap(width, height); + break; + case PAGE_SIZE_AUTO_PORTRAIT: + if (width > height) + std::swap(width, height); + break; + case PAGE_SIZE_RESOLVED: { + LengthSize size = style->pageSize(); + ASSERT(size.width().isFixed()); + ASSERT(size.height().isFixed()); + width = size.width().calcValue(0); + height = size.height().calcValue(0); + break; + } + default: + ASSERT_NOT_REACHED(); + } + pageSize = IntSize(width, height); + + // The percentage is calculated with respect to the width even for margin top and bottom. + // http://www.w3.org/TR/CSS2/box.html#margin-properties + marginTop = style->marginTop().isAuto() ? marginTop : style->marginTop().calcValue(width); + marginRight = style->marginRight().isAuto() ? marginRight : style->marginRight().calcValue(width); + marginBottom = style->marginBottom().isAuto() ? marginBottom : style->marginBottom().calcValue(width); + marginLeft = style->marginLeft().isAuto() ? marginLeft : style->marginLeft().calcValue(width); +} + +void Document::createStyleSelector() +{ + bool matchAuthorAndUserStyles = true; + if (Settings* docSettings = settings()) + matchAuthorAndUserStyles = docSettings->authorAndUserStylesEnabled(); + m_styleSelector.set(new CSSStyleSelector(this, m_styleSheets.get(), m_mappedElementSheet.get(), pageUserSheet(), pageGroupUserSheets(), + !inQuirksMode(), matchAuthorAndUserStyles)); +} + +void Document::attach() +{ + ASSERT(!attached()); + ASSERT(!m_inPageCache); +#if !PLATFORM(ANDROID) + ASSERT(!m_axObjectCache); +#endif + + if (!m_renderArena) + m_renderArena = new RenderArena(); + + // Create the rendering tree + setRenderer(new (m_renderArena.get()) RenderView(this, view())); +#if USE(ACCELERATED_COMPOSITING) + renderView()->didMoveOnscreen(); +#endif + + recalcStyle(Force); + + RenderObject* render = renderer(); + setRenderer(0); + + ContainerNode::attach(); + + setRenderer(render); +} + +void Document::detach() +{ + ASSERT(attached()); + ASSERT(!m_inPageCache); + + clearAXObjectCache(); + stopActiveDOMObjects(); + + RenderObject* render = renderer(); + + // Send out documentWillBecomeInactive() notifications to registered elements, + // in order to stop media elements + documentWillBecomeInactive(); + +#if ENABLE(SHARED_WORKERS) + SharedWorkerRepository::documentDetached(this); +#endif + + if (m_frame) { + FrameView* view = m_frame->view(); + if (view) + view->detachCustomScrollbars(); + } + + // indicate destruction mode, i.e. attached() but renderer == 0 + setRenderer(0); + +#if ENABLE(FULLSCREEN_API) + if (m_fullScreenRenderer) + setFullScreenRenderer(0); +#endif + + m_hoverNode = 0; + m_focusedNode = 0; + m_activeNode = 0; + + ContainerNode::detach(); + + unscheduleStyleRecalc(); + + if (render) + render->destroy(); + + // This is required, as our Frame might delete itself as soon as it detaches + // us. However, this violates Node::detach() semantics, as it's never + // possible to re-attach. Eventually Document::detach() should be renamed, + // or this setting of the frame to 0 could be made explicit in each of the + // callers of Document::detach(). + m_frame = 0; + m_renderArena.clear(); +} + +void Document::removeAllEventListeners() +{ +#if ENABLE(TOUCH_EVENTS) + Page* ownerPage = page(); + if (!m_inPageCache && ownerPage && (m_frame == ownerPage->mainFrame()) && hasListenerType(Document::TOUCH_LISTENER)) { + // Inform the Chrome Client that it no longer needs to forward touch + // events to WebCore as the document removes all the event listeners. + ownerPage->chrome()->client()->needTouchEvents(false); + } +#endif + + EventTarget::removeAllEventListeners(); + + if (DOMWindow* domWindow = this->domWindow()) + domWindow->removeAllEventListeners(); + for (Node* node = firstChild(); node; node = node->traverseNextNode()) + node->removeAllEventListeners(); +} + +RenderView* Document::renderView() const +{ + return toRenderView(renderer()); +} + +void Document::clearAXObjectCache() +{ +#if PLATFORM(ANDROID) + return; +#else + // clear cache in top document + if (m_axObjectCache) { + // Clear the cache member variable before calling delete because attempts + // are made to access it during destruction. + AXObjectCache* axObjectCache = m_axObjectCache; + m_axObjectCache = 0; + delete axObjectCache; + return; + } + + // ask the top-level document to clear its cache + Document* doc = topDocument(); + if (doc != this) + doc->clearAXObjectCache(); +#endif +} + +bool Document::axObjectCacheExists() const +{ +#if PLATFORM(ANDROID) + return false; +#else + if (m_axObjectCache) + return true; + + Document* doc = topDocument(); + if (doc != this) + return doc->axObjectCacheExists(); + + return false; +#endif +} + +AXObjectCache* Document::axObjectCache() const +{ +#if PLATFORM(ANDROID) + return 0; +#else + // 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. + // NOTE: This recovery may no longer be needed. I have been unable to trigger + // it again. See rdar://5794454 + // FIXME: Can this be fixed when inserting the subframe instead of now? + // FIXME: If this function was called to get the cache in order to remove + // an AXObject, we are now deleting the cache as a whole and returning a + // new empty cache that does not contain the AXObject! That should actually + // be OK. I am concerned about other cases like this where accessing the + // cache blows away the AXObject being operated on. + 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(this); + return m_axObjectCache; +#endif // ANDROID +} + +void Document::setVisuallyOrdered() +{ + m_visuallyOrdered = true; + if (renderer()) + renderer()->style()->setVisuallyOrdered(true); +} + +PassRefPtr<DocumentParser> Document::createParser() +{ + // FIXME: this should probably pass the frame instead + return XMLDocumentParser::create(this, view()); +} + +ScriptableDocumentParser* Document::scriptableDocumentParser() const +{ + return parser() ? parser()->asScriptableDocumentParser() : 0; +} + +void Document::open(Document* ownerDocument) +{ + if (ownerDocument) { + setURL(ownerDocument->url()); + m_cookieURL = ownerDocument->cookieURL(); + ScriptExecutionContext::setSecurityOrigin(ownerDocument->securityOrigin()); + } + + if (m_frame) { + ScriptableDocumentParser* parser = scriptableDocumentParser(); + if (m_frame->loader()->isLoadingMainResource() || (parser && parser->isParsing() && parser->isExecutingScript())) + return; + + if (m_frame->loader()->state() == FrameStateProvisional) + m_frame->loader()->stopAllLoaders(); + } + + removeAllEventListeners(); + implicitOpen(); + + if (DOMWindow* domWindow = this->domWindow()) + domWindow->removeAllEventListeners(); + + if (m_frame) + m_frame->loader()->didExplicitOpen(); +} + +void Document::detachParser() +{ + if (!m_parser) + return; + m_parser->detach(); + m_parser.clear(); +} + +void Document::cancelParsing() +{ + if (m_parser) { + // We have to clear the parser 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 + detachParser(); + close(); + } +} + +void Document::implicitOpen() +{ + cancelParsing(); + + removeChildren(); + + setCompatibilityMode(NoQuirksMode); + + m_parser = createParser(); + setParsing(true); + setReadyState(Loading); + + ScriptableDocumentParser* parser = scriptableDocumentParser(); + if (m_frame && parser) + parser->setXSSAuditor(m_frame->script()->xssAuditor()); + + // If we reload, the animation controller sticks around and has + // a stale animation time. We need to update it here. + if (m_frame && m_frame->animation()) + m_frame->animation()->beginAnimationUpdate(); +} + +HTMLElement* Document::body() const +{ + 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) + body = i; + } + return static_cast<HTMLElement*>(body); +} + +void Document::setBody(PassRefPtr<HTMLElement> newBody, ExceptionCode& ec) +{ + if (!newBody || !documentElement()) { + 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->writer()->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()->navigationScheduler()->locationChangePending(); + bool doload = !parsing() && m_parser && !m_processingLoadEvent && !wasLocationChangePending; + + if (!doload) + return; + + m_processingLoadEvent = true; + + ScriptableDocumentParser* parser = scriptableDocumentParser(); + m_wellFormed = parser && parser->wellFormed(); + + // We have to clear the parser, in case someone document.write()s from the + // onLoad event handler, as in Radar 3206524. + detachParser(); + + // Parser should have picked up all preloads by now + m_cachedResourceLoader->clearPreloads(); + + // FIXME: We kick off the icon loader when the Document is done parsing. + // There are earlier opportunities we could start it: + // -When the <head> finishes parsing + // -When any new HTMLLinkElement is inserted into the document + // But those add a dynamic component to the favicon that has UI + // ramifications, and we need to decide what is the Right Thing To Do(tm) + Frame* f = frame(); + if (f) + f->loader()->startIconLoader(); + + // Resume the animations (or start them) + if (f) + f->animation()->resumeAnimationsForDocument(this); + + ImageLoader::dispatchPendingBeforeLoadEvents(); + ImageLoader::dispatchPendingLoadEvents(); + dispatchWindowLoadEvent(); + enqueuePageshowEvent(PageshowEventNotPersisted); + enqueuePopstateEvent(m_pendingStateObject ? m_pendingStateObject.release() : SerializedScriptValue::nullValue()); + + if (f) + 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()->navigationScheduler()->locationChangePending() && 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(); + RenderObject* renderObject = renderer(); + + // We used to force a synchronous display and flush here. This really isn't + // necessary and can in fact be actively harmful if pages are loading at a rate of > 60fps + // (if your platform is syncing flushes and limiting them to 60fps). + m_overMinimumLayoutThreshold = true; + if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) { + updateStyleIfNeeded(); + + // Always do a layout after loading if needed. + if (view() && renderObject && (!renderObject->firstChild() || renderObject->needsLayout())) + view()->layout(); + } + +#if PLATFORM(MAC) || PLATFORM(CHROMIUM) + if (f && renderObject && this == topDocument() && AXObjectCache::accessibilityEnabled()) { + // The AX cache may have been cleared at this point, but we need to make sure it contains an + // AX object to send the notification to. getOrCreate will make sure that an valid AX object + // exists in the cache (we ignore the return value because we don't need it here). This is + // only safe to call when a layout is not in progress, so it can not be used in postNotification. + axObjectCache()->getOrCreate(renderObject); + axObjectCache()->postNotification(renderObject, AXObjectCache::AXLoadComplete, true); + } +#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() +{ + // This function will only be called when FrameView thinks a layout is needed. + // This enforces a couple extra rules. + // + // (a) Only schedule a layout once the stylesheets are loaded. + // (b) Only schedule layout once we have a body element. + + return (haveStylesheetsLoaded() && body()) + || (documentElement() && !documentElement()->hasTagName(htmlTag)); +} + +int Document::minimumLayoutDelay() +{ + if (m_overMinimumLayoutThreshold) + return m_extraLayoutDelay; + + 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) + m_extraLayoutDelay; +} + +int Document::elapsedTime() const +{ + return static_cast<int>((currentTime() - m_startTime) * 1000); +} + +void Document::write(const SegmentedString& text, Document* ownerDocument) +{ +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Beginning a document.write at %d\n", elapsedTime()); +#endif + + bool hasInsertionPoint = m_parser && m_parser->hasInsertionPoint(); + if (!hasInsertionPoint && m_ignoreDestructiveWriteCount) + return; + + if (!hasInsertionPoint) + open(ownerDocument); + + ASSERT(m_parser); + m_parser->insert(text); + +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Ending a document.write at %d\n", elapsedTime()); +#endif +} + +void Document::write(const String& text, Document* ownerDocument) +{ + write(SegmentedString(text), ownerDocument); +} + +void Document::writeln(const String& text, Document* ownerDocument) +{ + write(text, ownerDocument); + write("\n", ownerDocument); +} + +void Document::finishParsing() +{ +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Received all data at %d\n", elapsedTime()); +#endif + + // Let the parser 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, parser gets deleted + // (3) Data is still remaining to be parsed. + if (m_parser) + m_parser->finish(); +} + +const KURL& Document::virtualURL() const +{ + return m_url; +} + +KURL Document::virtualCompleteURL(const String& url) const +{ + return completeURL(url); +} + +void Document::setURL(const KURL& url) +{ + const KURL& newURL = url.isEmpty() ? blankURL() : url; + if (newURL == m_url) + return; + + m_url = newURL; + m_documentURI = m_url.string(); + updateBaseURL(); +} + +void Document::updateBaseURL() +{ + // DOM 3 Core: When the Document supports the feature "HTML" [DOM Level 2 HTML], the base URI is computed using + // first the value of the href attribute of the HTML BASE element if any, and the value of the documentURI attribute + // from the Document interface otherwise. + if (m_baseElementURL.isEmpty()) { + // The documentURI attribute is an arbitrary string. DOM 3 Core does not specify how it should be resolved, + // so we use a null base URL. + m_baseURL = KURL(KURL(), documentURI()); + } else + m_baseURL = m_baseElementURL; + if (!m_baseURL.isValid()) + m_baseURL = KURL(); + + if (m_elemSheet) + m_elemSheet->setFinalURL(m_baseURL); + if (m_mappedElementSheet) + m_mappedElementSheet->setFinalURL(m_baseURL); +} + +void Document::processBaseElement() +{ + // Find the first href attribute in a base element and the first target attribute in a base element. + const AtomicString* href = 0; + const AtomicString* target = 0; + for (Node* node = document()->firstChild(); node && (!href || !target); node = node->traverseNextNode()) { + if (node->hasTagName(baseTag)) { + if (!href) { + const AtomicString& value = static_cast<Element*>(node)->fastGetAttribute(hrefAttr); + if (!value.isNull()) + href = &value; + } + if (!target) { + const AtomicString& value = static_cast<Element*>(node)->fastGetAttribute(targetAttr); + if (!value.isNull()) + target = &value; + } + } + } + + // FIXME: Since this doesn't share code with completeURL it may not handle encodings correctly. + KURL baseElementURL; + if (href) { + String strippedHref = stripLeadingAndTrailingHTMLSpaces(*href); + if (!strippedHref.isEmpty() && (!frame() || frame()->script()->xssAuditor()->canSetBaseElementURL(*href))) + baseElementURL = KURL(url(), strippedHref); + } + if (m_baseElementURL != baseElementURL) { + m_baseElementURL = baseElementURL; + updateBaseURL(); + } + + m_baseTarget = target ? *target : nullAtom; +} + +String Document::userAgent(const KURL& url) const +{ + return frame() ? frame()->loader()->userAgent(url) : String(); +} + +CSSStyleSheet* Document::pageUserSheet() +{ + if (m_pageUserSheet) + return m_pageUserSheet.get(); + + Page* owningPage = page(); + if (!owningPage) + return 0; + + String userSheetText = owningPage->userStyleSheet(); + if (userSheetText.isEmpty()) + return 0; + + // Parse the sheet and cache it. + m_pageUserSheet = CSSStyleSheet::createInline(this, settings()->userStyleSheetLocation()); + m_pageUserSheet->setIsUserStyleSheet(true); + m_pageUserSheet->parseString(userSheetText, !inQuirksMode()); + return m_pageUserSheet.get(); +} + +void Document::clearPageUserSheet() +{ + if (m_pageUserSheet) { + m_pageUserSheet = 0; + styleSelectorChanged(DeferRecalcStyle); + } +} + +void Document::updatePageUserSheet() +{ + clearPageUserSheet(); + if (pageUserSheet()) + styleSelectorChanged(RecalcStyleImmediately); +} + +const Vector<RefPtr<CSSStyleSheet> >* Document::pageGroupUserSheets() const +{ + if (m_pageGroupUserSheetCacheValid) + return m_pageGroupUserSheets.get(); + + m_pageGroupUserSheetCacheValid = true; + + Page* owningPage = page(); + if (!owningPage) + return 0; + + const PageGroup& pageGroup = owningPage->group(); + const UserStyleSheetMap* sheetsMap = pageGroup.userStyleSheets(); + if (!sheetsMap) + return 0; + + UserStyleSheetMap::const_iterator end = sheetsMap->end(); + for (UserStyleSheetMap::const_iterator it = sheetsMap->begin(); it != end; ++it) { + const UserStyleSheetVector* sheets = it->second; + for (unsigned i = 0; i < sheets->size(); ++i) { + const UserStyleSheet* sheet = sheets->at(i).get(); + if (sheet->injectedFrames() == InjectInTopFrameOnly && ownerElement()) + continue; + if (!UserContentURLPattern::matchesPatterns(url(), sheet->whitelist(), sheet->blacklist())) + continue; + RefPtr<CSSStyleSheet> parsedSheet = CSSStyleSheet::createInline(const_cast<Document*>(this), sheet->url()); + parsedSheet->setIsUserStyleSheet(sheet->level() == UserStyleUserLevel); + parsedSheet->parseString(sheet->source(), !inQuirksMode()); + if (!m_pageGroupUserSheets) + m_pageGroupUserSheets.set(new Vector<RefPtr<CSSStyleSheet> >); + m_pageGroupUserSheets->append(parsedSheet.release()); + } + } + + return m_pageGroupUserSheets.get(); +} + +void Document::clearPageGroupUserSheets() +{ + m_pageGroupUserSheetCacheValid = false; + if (m_pageGroupUserSheets && m_pageGroupUserSheets->size()) { + m_pageGroupUserSheets->clear(); + styleSelectorChanged(DeferRecalcStyle); + } +} + +void Document::updatePageGroupUserSheets() +{ + clearPageGroupUserSheets(); + if (pageGroupUserSheets() && pageGroupUserSheets()->size()) + styleSelectorChanged(RecalcStyleImmediately); +} + +CSSStyleSheet* Document::elementSheet() +{ + if (!m_elemSheet) + m_elemSheet = CSSStyleSheet::createInline(this, m_baseURL); + return m_elemSheet.get(); +} + +CSSStyleSheet* Document::mappedElementSheet() +{ + if (!m_mappedElementSheet) + m_mappedElementSheet = CSSStyleSheet::createInline(this, m_baseURL); + 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) { + // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order + if (start->tabIndex() < 0) { + for (Node* n = start->traverseNextNode(); n; n = n->traverseNextNode()) + if (n->isKeyboardFocusable(event) && n->tabIndex() >= 0) + return n; + } + + // 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()) + // 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()) { } + + // 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; + } + + // However, if a node is excluded from the normal tabbing cycle, the previous focusable node is determined by tree order + if (startingTabIndex < 0) { + for (Node* n = startingNode; n; n = n->traversePreviousNode()) + if (n->isKeyboardFocusable(event) && n->tabIndex() >= 0) + return n; + } + + 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; +} + +#ifdef ANDROID_META_SUPPORT +void Document::processMetadataSettings(const String& content) +{ + ASSERT(!content.isNull()); + processArguments(content, 0, 0); +} +#endif + +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; + styleSelectorChanged(DeferRecalcStyle); + } 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->navigationScheduler()->scheduleRedirect(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()) { + ExceptionCode ec; // Exception (for sandboxed documents) ignored. + static_cast<HTMLDocument*>(this)->setCookie(content, ec); + } + } else if (equalIgnoringCase(equiv, "content-language")) + setContentLanguage(content); + else if (equalIgnoringCase(equiv, "x-dns-prefetch-control")) + parseDNSPrefetchControlHeader(content); + else if (equalIgnoringCase(equiv, "x-frame-options")) { + if (frame) { + FrameLoader* frameLoader = frame->loader(); + if (frameLoader->shouldInterruptLoadForXFrameOptions(content, url())) { + frameLoader->stopAllLoaders(); + frame->navigationScheduler()->scheduleLocationChange(securityOrigin(), blankURL(), String()); + + DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to display document because display forbidden by X-Frame-Options.\n")); + frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, consoleMessage, 1, String()); + } + } + } +} + +// Though isspace() considers \t and \v to be whitespace, Win IE doesn't. +static bool isSeparator(UChar c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0'; +} + +void Document::processArguments(const String& features, void* data, ArgumentsCallback callback) +{ + // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior. + int keyBegin, keyEnd; + int valueBegin, valueEnd; + + int i = 0; + int length = features.length(); + String buffer = features.lower(); + while (i < length) { + // skip to first non-separator, but don't skip past the end of the string + while (isSeparator(buffer[i])) { + if (i >= length) + break; + i++; + } + keyBegin = i; + + // skip to first separator + while (!isSeparator(buffer[i])) + i++; + keyEnd = i; + + // skip to first '=', but don't skip past a ',' or the end of the string + while (buffer[i] != '=') { + if (buffer[i] == ',' || i >= length) + break; + i++; + } + + // skip to first non-separator, but don't skip past a ',' or the end of the string + while (isSeparator(buffer[i])) { + if (buffer[i] == ',' || i >= length) + break; + i++; + } + valueBegin = i; + + // skip to first separator + while (!isSeparator(buffer[i])) + i++; + valueEnd = i; + + ASSERT(i <= length); + + String keyString = buffer.substring(keyBegin, keyEnd - keyBegin); + String valueString = buffer.substring(valueBegin, valueEnd - valueBegin); +#ifdef ANDROID_META_SUPPORT + if (frame()) + frame()->settings()->setMetadataSettings(keyString, valueString); +#else + callback(keyString, valueString, this, data); +#endif + } +} + +void Document::processViewport(const String& features) +{ + ASSERT(!features.isNull()); + + m_viewportArguments = ViewportArguments(); + processArguments(features, (void*)&m_viewportArguments, &setViewportFeature); + + Frame* frame = this->frame(); + if (!frame || !frame->page()) + return; + + frame->page()->updateViewportArguments(); +} + +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); + renderView()->layer()->hitTest(request, result); + + if (!request.readOnly()) + updateStyleIfNeeded(); + + 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; + styleSelectorChanged(DeferRecalcStyle); +} + +// 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 + + if (m_pendingStylesheets) + return; + + styleSelectorChanged(RecalcStyleImmediately); + + ScriptableDocumentParser* parser = scriptableDocumentParser(); + if (parser) + parser->executeScriptsWaitingForStylesheets(); + + if (m_gotoAnchorNeededAfterStylesheetsLoad && view()) + view()->scrollToFragment(m_frame->loader()->url()); +} + +void Document::styleSelectorChanged(StyleSelectorUpdateFlag updateFlag) +{ + // 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 (!attached() || (!m_didCalculateStyleSelector && !haveStylesheetsLoaded())) + return; + +#ifdef INSTRUMENT_LAYOUT_SCHEDULING + if (!ownerElement()) + printf("Beginning update of style selector at time %d.\n", elapsedTime()); +#endif + + recalcStyleSelector(); + + if (updateFlag == DeferRecalcStyle) { + scheduleForcedStyleRecalc(); + return; + } + + if (didLayoutWithPendingStylesheets() && m_pendingStylesheets <= 0) { + m_pendingSheetLayout = IgnoreLayoutWithPendingSheets; + if (renderer()) + renderer()->repaint(); + } + + // This recalcStyle initiates a new recalc cycle. We need to bracket it to + // make sure animations get the correct update time + if (m_frame) + m_frame->animation()->beginAnimationUpdate(); + recalcStyle(Force); + if (m_frame) + m_frame->animation()->endAnimationUpdate(); + +#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(); + } + + if (m_mediaQueryMatcher) + m_mediaQueryMatcher->styleSelectorChanged(); +} + +void Document::addStyleSheetCandidateNode(Node* node, bool createdByParser) +{ + // Until the <body> exists, we have no choice but to compare document positions, + // since styles outside of the body and head continue to be shunted into the head + // (and thus can shift to end up before dynamically added DOM content that is also + // outside the body). + if ((createdByParser && body()) || m_styleSheetCandidateNodes.isEmpty()) { + m_styleSheetCandidateNodes.add(node); + return; + } + + // Determine an appropriate insertion point. + StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin(); + StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end(); + StyleSheetCandidateListHashSet::iterator it = end; + Node* followingNode = 0; + do { + --it; + Node* n = *it; + unsigned short position = n->compareDocumentPosition(node); + if (position == DOCUMENT_POSITION_FOLLOWING) { + m_styleSheetCandidateNodes.insertBefore(followingNode, node); + return; + } + followingNode = n; + } while (it != begin); + + m_styleSheetCandidateNodes.insertBefore(followingNode, node); +} + +void Document::removeStyleSheetCandidateNode(Node* node) +{ + m_styleSheetCandidateNodes.remove(node); +} + +void Document::recalcStyleSelector() +{ + if (!renderer() || !attached()) + return; + + StyleSheetVector sheets; + + bool matchAuthorAndUserStyles = true; + if (Settings* settings = this->settings()) + matchAuthorAndUserStyles = settings->authorAndUserStylesEnabled(); + + StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin(); + StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end(); + if (!matchAuthorAndUserStyles) + end = begin; + for (StyleSheetCandidateListHashSet::iterator it = begin; it != end; ++it) { + Node* n = *it; + + StyleSheet* sheet = 0; + + if (n->nodeType() == PROCESSING_INSTRUCTION_NODE) { + // Processing instruction (XML documents only). + // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion. + 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 + } 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* linkElement = static_cast<HTMLLinkElement*>(n); + if (linkElement->isDisabled()) + continue; + enabledViaScript = linkElement->isEnabledViaScript(); + if (linkElement->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 (!linkElement->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) + sheets.append(sheet); + } + + m_styleSheets->swap(sheets); + + m_styleSelector.clear(); + 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->parentNode()))) + return; + + m_hoverNode = node->parentNode(); + while (m_hoverNode && !m_hoverNode->renderer()) + m_hoverNode = m_hoverNode->parentNode(); + if (frame()) + frame()->eventHandler()->scheduleHoverStateUpdate(); +} + +void Document::activeChainNodeDetached(Node* node) +{ + if (!m_activeNode || (node != m_activeNode && (!m_activeNode->isTextNode() || node != m_activeNode->parentNode()))) + return; + + m_activeNode = node->parentNode(); + while (m_activeNode && !m_activeNode->renderer()) + m_activeNode = m_activeNode->parentNode(); +} + +#if ENABLE(DASHBOARD_SUPPORT) +const Vector<DashboardRegionValue>& Document::dashboardRegions() const +{ + return m_dashboardRegions; +} + +void Document::setDashboardRegions(const Vector<DashboardRegionValue>& regions) +{ + m_dashboardRegions = regions; + setDashboardRegionsDirty(false); +} +#endif + +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->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 = oldFocusedNode->renderer(); + if (r && r->isTextControl() && toRenderTextControl(r)->wasChangedSinceLastChangeEvent()) { + static_cast<Element*>(oldFocusedNode.get())->dispatchFormControlChangeEvent(); + r = oldFocusedNode->renderer(); + if (r && r->isTextControl()) + toRenderTextControl(r)->setChangedSinceLastChangeEvent(false); + } + + // Dispatch the blur event and let the node do any other blur related activities (important for text fields) + oldFocusedNode->dispatchBlurEvent(); + + if (m_focusedNode) { + // handler shifted focus + focusChangeBlocked = true; + newFocusedNode = 0; + } + + oldFocusedNode->dispatchUIEvent(eventNames().focusoutEvent, 0, 0); // DOM level 3 name for the bubbling blur event. + // FIXME: We should remove firing DOMFocusOutEvent event when we are sure no content depends + // on it, probably when <rdar://problem/8503958> is resolved. + oldFocusedNode->dispatchUIEvent(eventNames().DOMFocusOutEvent, 0, 0); // DOM level 2 name for compatibility. + + if (m_focusedNode) { + // handler shifted focus + focusChangeBlocked = true; + newFocusedNode = 0; + } + if (oldFocusedNode == this && oldFocusedNode->hasOneRef()) + return true; + + if (oldFocusedNode == oldFocusedNode->rootEditableElement()) + frame()->editor()->didEndEditing(); + + if (view()) { + Widget* oldWidget = widgetForNode(oldFocusedNode.get()); + if (oldWidget) + oldWidget->setFocus(false); + else + view()->setFocus(false); + } + } + + 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) + m_focusedNode->dispatchFocusEvent(); + + if (m_focusedNode != newFocusedNode) { + // handler shifted focus + focusChangeBlocked = true; + goto SetFocusedNodeDone; + } + + m_focusedNode->dispatchUIEvent(eventNames().focusinEvent, 0, 0); // DOM level 3 bubbling focus event. + // FIXME: We should remove firing DOMFocusInEvent event when we are sure no content depends + // on it, probably when <rdar://problem/8503958> is resolved. + m_focusedNode->dispatchUIEvent(eventNames().DOMFocusInEvent, 0, 0); // DOM level 2 for compatibility. + + if (m_focusedNode != newFocusedNode) { + // handler shifted focus + focusChangeBlocked = true; + goto SetFocusedNodeDone; + } + m_focusedNode->setFocus(true); + + if (m_focusedNode == 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(true); + else + view()->setFocus(true); + } + } + +#if PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(CHROMIUM) + if (!focusChangeBlocked && m_focusedNode && AXObjectCache::accessibilityEnabled()) { + RenderObject* oldFocusedRenderer = 0; + RenderObject* newFocusedRenderer = 0; + + if (oldFocusedNode) + oldFocusedRenderer = oldFocusedNode->renderer(); + if (newFocusedNode) + newFocusedRenderer = newFocusedNode->renderer(); + + axObjectCache()->handleFocusedUIElementChanged(oldFocusedRenderer, newFocusedRenderer); + } +#endif + if (!focusChangeBlocked) + page()->chrome()->focusedNodeChanged(m_focusedNode.get()); + +SetFocusedNodeDone: + updateStyleIfNeeded(); + return !focusChangeBlocked; +} + +void Document::getFocusableNodes(Vector<RefPtr<Node> >& nodes) +{ + updateLayout(); + + for (Node* node = firstChild(); node; node = node->traverseNextNode()) { + if (node->isFocusable()) + nodes.append(node); + } +} + +void Document::setCSSTarget(Element* n) +{ + if (m_cssTarget) + m_cssTarget->setNeedsStyleRecalc(); + m_cssTarget = n; + if (n) + n->setNeedsStyleRecalc(); +} + +void Document::attachNodeIterator(NodeIterator* ni) +{ + m_nodeIterators.add(ni); +} + +void Document::detachNodeIterator(NodeIterator* ni) +{ + // The node iterator can be detached without having been attached if its root node didn't have a document + // when the iterator was created, but has it now. + m_nodeIterators.remove(ni); +} + +void Document::moveNodeIteratorsToNewDocument(Node* node, Document* newDocument) +{ + HashSet<NodeIterator*> nodeIteratorsList = m_nodeIterators; + HashSet<NodeIterator*>::const_iterator nodeIteratorsEnd = nodeIteratorsList.end(); + for (HashSet<NodeIterator*>::const_iterator it = nodeIteratorsList.begin(); it != nodeIteratorsEnd; ++it) { + if ((*it)->root() == node) { + detachNodeIterator(*it); + newDocument->attachNodeIterator(*it); + } + } +} + +void Document::nodeChildrenChanged(ContainerNode* container) +{ + if (!disableRangeMutation(page())) { + HashSet<Range*>::const_iterator end = m_ranges.end(); + for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->nodeChildrenChanged(container); + } +} + +void Document::nodeChildrenWillBeRemoved(ContainerNode* container) +{ + if (!disableRangeMutation(page())) { + HashSet<Range*>::const_iterator end = m_ranges.end(); + for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->nodeChildrenWillBeRemoved(container); + } + + HashSet<NodeIterator*>::const_iterator nodeIteratorsEnd = m_nodeIterators.end(); + for (HashSet<NodeIterator*>::const_iterator it = m_nodeIterators.begin(); it != nodeIteratorsEnd; ++it) { + for (Node* n = container->firstChild(); n; n = n->nextSibling()) + (*it)->nodeWillBeRemoved(n); + } + + if (Frame* frame = this->frame()) { + for (Node* n = container->firstChild(); n; n = n->nextSibling()) { + frame->selection()->nodeWillBeRemoved(n); + frame->page()->dragCaretController()->nodeWillBeRemoved(n); + } + } +} + +void Document::nodeWillBeRemoved(Node* n) +{ + HashSet<NodeIterator*>::const_iterator nodeIteratorsEnd = m_nodeIterators.end(); + for (HashSet<NodeIterator*>::const_iterator it = m_nodeIterators.begin(); it != nodeIteratorsEnd; ++it) + (*it)->nodeWillBeRemoved(n); + + if (!disableRangeMutation(page())) { + HashSet<Range*>::const_iterator rangesEnd = m_ranges.end(); + for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != rangesEnd; ++it) + (*it)->nodeWillBeRemoved(n); + } + + if (Frame* frame = this->frame()) { + frame->selection()->nodeWillBeRemoved(n); + frame->page()->dragCaretController()->nodeWillBeRemoved(n); + } + +#if ENABLE(FULLSCREEN_API) + // If the current full screen element or any of its ancestors is removed, set the current + // full screen element to the document root, and fire a fullscreenchange event to inform + // clients of the DOM. + ASSERT(n); + if (n->contains(m_fullScreenElement.get())) { + ASSERT(n != documentElement()); + setFullScreenRenderer(0); + m_fullScreenElement = documentElement(); + m_fullScreenElement->setNeedsStyleRecalc(); + m_fullScreenElement->detach(); + updateStyleIfNeeded(); + m_fullScreenChangeDelayTimer.startOneShot(0); + } +#endif +} + +void Document::textInserted(Node* text, unsigned offset, unsigned length) +{ + if (!disableRangeMutation(page())) { + HashSet<Range*>::const_iterator end = m_ranges.end(); + for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->textInserted(text, offset, length); + } + + // Update the markers for spelling and grammar checking. + m_markers->shiftMarkers(text, offset, length); +} + +void Document::textRemoved(Node* text, unsigned offset, unsigned length) +{ + if (!disableRangeMutation(page())) { + HashSet<Range*>::const_iterator end = m_ranges.end(); + for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->textRemoved(text, offset, length); + } + + // Update the markers for spelling and grammar checking. + m_markers->removeMarkers(text, offset, length); + m_markers->shiftMarkers(text, offset + length, 0 - length); +} + +void Document::textNodesMerged(Text* oldNode, unsigned offset) +{ + if (!disableRangeMutation(page())) { + NodeWithIndex oldNodeWithIndex(oldNode); + HashSet<Range*>::const_iterator end = m_ranges.end(); + for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->textNodesMerged(oldNodeWithIndex, offset); + } + + // FIXME: This should update markers for spelling and grammar checking. +} + +void Document::textNodeSplit(Text* oldNode) +{ + if (!disableRangeMutation(page())) { + HashSet<Range*>::const_iterator end = m_ranges.end(); + for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->textNodeSplit(oldNode); + } + + // FIXME: This should update markers for spelling and grammar checking. +} + +// FIXME: eventually, this should return a DOMWindow stored in the document. +DOMWindow* Document::domWindow() const +{ + if (!frame()) + return 0; + + // The m_frame pointer is not (not always?) zeroed out when the document is put into b/f cache, so the frame can hold an unrelated document/window pair. + // FIXME: We should always zero out the frame pointer on navigation to avoid accidentally accessing the new frame content. + if (m_frame->document() != this) + return 0; + + return frame()->domWindow(); +} + +void Document::setWindowAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener) +{ + DOMWindow* domWindow = this->domWindow(); + if (!domWindow) + return; + domWindow->setAttributeEventListener(eventType, listener); +} + +EventListener* Document::getWindowAttributeEventListener(const AtomicString& eventType) +{ + DOMWindow* domWindow = this->domWindow(); + if (!domWindow) + return 0; + return domWindow->getAttributeEventListener(eventType); +} + +void Document::dispatchWindowEvent(PassRefPtr<Event> event, PassRefPtr<EventTarget> target) +{ + ASSERT(!eventDispatchForbidden()); + DOMWindow* domWindow = this->domWindow(); + if (!domWindow) + return; + domWindow->dispatchEvent(event, target); +} + +void Document::dispatchWindowLoadEvent() +{ + ASSERT(!eventDispatchForbidden()); + DOMWindow* domWindow = this->domWindow(); + if (!domWindow) + return; + domWindow->dispatchLoadEvent(); +} + +void Document::enqueueWindowEvent(PassRefPtr<Event> event) +{ + event->setTarget(domWindow()); + m_eventQueue->enqueueEvent(event); +} + +PassRefPtr<Event> Document::createEvent(const String& eventType, ExceptionCode& ec) +{ + RefPtr<Event> event; + if (eventType == "Event" || eventType == "Events" || eventType == "HTMLEvents") + event = Event::create(); + else if (eventType == "CustomEvent") + event = CustomEvent::create(); + else if (eventType == "KeyboardEvent" || eventType == "KeyboardEvents") + event = KeyboardEvent::create(); + else if (eventType == "MessageEvent") + event = MessageEvent::create(); + else if (eventType == "MouseEvent" || eventType == "MouseEvents") + event = MouseEvent::create(); + else if (eventType == "MutationEvent" || eventType == "MutationEvents") + event = MutationEvent::create(); + else if (eventType == "OverflowEvent") + event = OverflowEvent::create(); + else if (eventType == "PageTransitionEvent") + event = PageTransitionEvent::create(); + else if (eventType == "ProgressEvent") + event = ProgressEvent::create(); +#if ENABLE(DOM_STORAGE) + else if (eventType == "StorageEvent") + event = StorageEvent::create(); +#endif + else if (eventType == "TextEvent") + event = TextEvent::create(); + else if (eventType == "UIEvent" || eventType == "UIEvents") + event = UIEvent::create(); + else if (eventType == "WebKitAnimationEvent") + event = WebKitAnimationEvent::create(); + else if (eventType == "WebKitTransitionEvent") + event = WebKitTransitionEvent::create(); + else if (eventType == "WheelEvent") + event = WheelEvent::create(); +#if ENABLE(SVG) + else if (eventType == "SVGEvents") + event = Event::create(); + else if (eventType == "SVGZoomEvents") + event = SVGZoomEvent::create(); +#endif +#if ENABLE(TOUCH_EVENTS) +#if USE(V8) + else if (eventType == "TouchEvent" && RuntimeEnabledFeatures::touchEnabled()) +#else + else if (eventType == "TouchEvent") +#endif + event = TouchEvent::create(); +#endif +#if ENABLE(DEVICE_ORIENTATION) + else if (eventType == "DeviceMotionEvent") + event = DeviceMotionEvent::create(); + else if (eventType == "DeviceOrientationEvent") + event = DeviceOrientationEvent::create(); +#endif +#if ENABLE(ORIENTATION_EVENTS) + else if (eventType == "OrientationEvent") + event = Event::create(); +#endif + if (event) + return event.release(); + + ec = NOT_SUPPORTED_ERR; + return 0; +} + +void Document::addListenerTypeIfNeeded(const AtomicString& eventType) +{ + if (eventType == eventNames().DOMSubtreeModifiedEvent) + addListenerType(DOMSUBTREEMODIFIED_LISTENER); + else if (eventType == eventNames().DOMNodeInsertedEvent) + addListenerType(DOMNODEINSERTED_LISTENER); + else if (eventType == eventNames().DOMNodeRemovedEvent) + addListenerType(DOMNODEREMOVED_LISTENER); + else if (eventType == eventNames().DOMNodeRemovedFromDocumentEvent) + addListenerType(DOMNODEREMOVEDFROMDOCUMENT_LISTENER); + else if (eventType == eventNames().DOMNodeInsertedIntoDocumentEvent) + addListenerType(DOMNODEINSERTEDINTODOCUMENT_LISTENER); + else if (eventType == eventNames().DOMAttrModifiedEvent) + addListenerType(DOMATTRMODIFIED_LISTENER); + else if (eventType == eventNames().DOMCharacterDataModifiedEvent) + addListenerType(DOMCHARACTERDATAMODIFIED_LISTENER); + else if (eventType == eventNames().overflowchangedEvent) + addListenerType(OVERFLOWCHANGED_LISTENER); + else if (eventType == eventNames().webkitAnimationStartEvent) + addListenerType(ANIMATIONSTART_LISTENER); + else if (eventType == eventNames().webkitAnimationEndEvent) + addListenerType(ANIMATIONEND_LISTENER); + else if (eventType == eventNames().webkitAnimationIterationEvent) + addListenerType(ANIMATIONITERATION_LISTENER); + else if (eventType == eventNames().webkitTransitionEndEvent) + addListenerType(TRANSITIONEND_LISTENER); + else if (eventType == eventNames().beforeloadEvent) + addListenerType(BEFORELOAD_LISTENER); + else if (eventType == eventNames().beforeprocessEvent) + addListenerType(BEFOREPROCESS_LISTENER); +#if ENABLE(TOUCH_EVENTS) + else if (eventType == eventNames().touchstartEvent + || eventType == eventNames().touchmoveEvent + || eventType == eventNames().touchendEvent + || eventType == eventNames().touchcancelEvent) { + addListenerType(TOUCH_LISTENER); + if (Page* page = this->page()) + page->chrome()->client()->needTouchEvents(true); + } +#endif +} + +CSSStyleDeclaration* Document::getOverrideStyle(Element*, const String&) +{ + return 0; +} + +HTMLFrameOwnerElement* Document::ownerElement() const +{ + if (!frame()) + return 0; + return frame()->ownerElement(); +} + +String Document::cookie(ExceptionCode& ec) const +{ + if (page() && !page()->cookieEnabled()) + return String(); + + // FIXME: The HTML5 DOM spec states that this attribute can raise an + // INVALID_STATE_ERR exception on getting if the Document has no + // browsing context. + + if (!securityOrigin()->canAccessCookies()) { + ec = SECURITY_ERR; + return String(); + } + + KURL cookieURL = this->cookieURL(); + if (cookieURL.isEmpty()) + return String(); + + return cookies(this, cookieURL); +} + +void Document::setCookie(const String& value, ExceptionCode& ec) +{ + if (page() && !page()->cookieEnabled()) + return; + + // FIXME: The HTML5 DOM spec states that this attribute can raise an + // INVALID_STATE_ERR exception on setting if the Document has no + // browsing context. + + if (!securityOrigin()->canAccessCookies()) { + ec = SECURITY_ERR; + return; + } + + KURL cookieURL = this->cookieURL(); + if (cookieURL.isEmpty()) + return; + + setCookies(this, cookieURL, value); +} + +String Document::referrer() const +{ + if (frame()) + return frame()->loader()->referrer(); + return String(); +} + +String Document::domain() const +{ + return securityOrigin()->domain(); +} + +void Document::setDomain(const String& newDomain, ExceptionCode& ec) +{ + if (SecurityOrigin::isDomainRelaxationForbiddenForURLScheme(securityOrigin()->protocol())) { + ec = SECURITY_ERR; + return; + } + + // 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 + // 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)) { + securityOrigin()->setDomainFromDOM(newDomain); + if (m_frame) + m_frame->script()->updateSecurityOrigin(); + return; + } + + int oldLength = domain().length(); + int newLength = newDomain.length(); + // e.g. newDomain = webkit.org (10) and domain() = www.webkit.org (14) + if (newLength >= oldLength) { + ec = SECURITY_ERR; + return; + } + + String test = domain(); + // Check that it's a subdomain, not e.g. "ebkit.org" + if (test[oldLength - newLength - 1] != '.') { + ec = SECURITY_ERR; + 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) { + ec = SECURITY_ERR; + return; + } + + securityOrigin()->setDomainFromDOM(newDomain); + if (m_frame) + m_frame->script()->updateSecurityOrigin(); +} + +// http://www.whatwg.org/specs/web-apps/current-work/#dom-document-lastmodified +String Document::lastModified() const +{ + DateComponents date; + bool foundDate = false; + if (m_frame) { + String httpLastModified = m_frame->loader()->documentLoader()->response().httpHeaderField("Last-Modified"); + if (!httpLastModified.isEmpty()) { + date.setMillisecondsSinceEpochForDateTime(parseDate(httpLastModified)); + foundDate = true; + } + } + // FIXME: If this document came from the file system, the HTML5 + // specificiation tells us to read the last modification date from the file + // system. + if (!foundDate) + date.setMillisecondsSinceEpochForDateTime(currentTimeMS()); + return String::format("%02d/%02d/%04d %02d:%02d:%02d", date.month() + 1, date.monthDay(), date.fullYear(), date.hour(), date.minute(), date.second()); +} + +static bool isValidNameNonASCII(const UChar* characters, unsigned length) +{ + unsigned i = 0; + + UChar32 c; + U16_NEXT(characters, i, length, c) + if (!isValidNameStart(c)) + return false; + + while (i < length) { + U16_NEXT(characters, i, length, c) + if (!isValidNamePart(c)) + return false; + } + + return true; +} + +static inline bool isValidNameASCII(const UChar* characters, unsigned length) +{ + UChar c = characters[0]; + if (!(isASCIIAlpha(c) || c == ':' || c == '_')) + return false; + + for (unsigned i = 1; i < length; ++i) { + c = characters[i]; + if (!(isASCIIAlphanumeric(c) || c == ':' || c == '_' || c == '-' || c == '.')) + return false; + } + + return true; +} + +bool Document::isValidName(const String& name) +{ + unsigned length = name.length(); + if (!length) + return false; + + const UChar* characters = name.characters(); + return isValidNameASCII(characters, length) || isValidNameNonASCII(characters, length); +} + +bool Document::parseQualifiedName(const String& qualifiedName, String& prefix, String& localName, ExceptionCode& ec) +{ + unsigned length = qualifiedName.length(); + + if (!length) { + ec = INVALID_CHARACTER_ERR; + return false; + } + + bool nameStart = true; + bool sawColon = false; + int colonPos = 0; + + const UChar* s = qualifiedName.characters(); + for (unsigned i = 0; i < length;) { + UChar32 c; + U16_NEXT(s, i, length, c) + if (c == ':') { + if (sawColon) { + ec = NAMESPACE_ERR; + return false; // multiple colons: not allowed + } + nameStart = true; + sawColon = true; + colonPos = i - 1; + } else if (nameStart) { + if (!isValidNameStart(c)) { + ec = INVALID_CHARACTER_ERR; + return false; + } + nameStart = false; + } else { + if (!isValidNamePart(c)) { + ec = INVALID_CHARACTER_ERR; + return false; + } + } + } + + if (!sawColon) { + prefix = String(); + localName = qualifiedName; + } else { + prefix = qualifiedName.substring(0, colonPos); + if (prefix.isEmpty()) { + ec = NAMESPACE_ERR; + return false; + } + localName = qualifiedName.substring(colonPos + 1); + } + + if (localName.isEmpty()) { + ec = NAMESPACE_ERR; + return false; + } + + return true; +} + +void Document::addImageMap(HTMLMapElement* imageMap) +{ + AtomicStringImpl* name = imageMap->getName().impl(); + if (!name) + return; + m_imageMapsByName.add(name, imageMap); +} + +void Document::removeImageMap(HTMLMapElement* imageMap) +{ + AtomicStringImpl* name = imageMap->getName().impl(); + if (!name) + return; + m_imageMapsByName.remove(name, imageMap); +} + +inline bool keyMatchesMapName(AtomicStringImpl* key, Element* element) +{ + return element->hasTagName(mapTag) && static_cast<HTMLMapElement*>(element)->getName().impl() == key; +} + +inline bool keyMatchesLowercasedMapName(AtomicStringImpl* key, Element* element) +{ + return element->hasTagName(mapTag) && static_cast<HTMLMapElement*>(element)->getName().lower().impl() == key; +} + +HTMLMapElement* Document::getImageMap(const String& url) const +{ + if (url.isNull()) + return 0; + size_t hashPos = url.find('#'); + String name = (hashPos == notFound ? url : url.substring(hashPos + 1)).impl(); + if (isHTMLDocument()) + return static_cast<HTMLMapElement*>(m_imageMapsByName.get<keyMatchesLowercasedMapName>(AtomicString(name.lower()).impl(), this)); + return static_cast<HTMLMapElement*>(m_imageMapsByName.get<keyMatchesMapName>(AtomicString(name).impl(), this)); +} + +void Document::setDecoder(PassRefPtr<TextResourceDecoder> decoder) +{ + m_decoder = decoder; +} + +KURL Document::completeURL(const String& url) const +{ + // Always return a null URL when passed a null string. + // FIXME: Should we change the KURL constructor to have this behavior? + // See also [CSS]StyleSheet::completeURL(const String&) + if (url.isNull()) + return KURL(); + const KURL& baseURL = ((m_baseURL.isEmpty() || m_baseURL == blankURL()) && parentDocument()) ? parentDocument()->baseURL() : m_baseURL; + if (!m_decoder) + return KURL(baseURL, url); + return KURL(baseURL, url, m_decoder->encoding()); +} + +void Document::setInPageCache(bool flag) +{ + if (m_inPageCache == flag) + return; + + m_inPageCache = flag; + if (flag) { + ASSERT(!m_savedRenderer); + m_savedRenderer = renderer(); + if (FrameView* v = view()) + v->resetScrollbars(); + m_styleRecalcTimer.stop(); + } else { + ASSERT(!renderer() || renderer() == m_savedRenderer); + ASSERT(m_renderArena); + setRenderer(m_savedRenderer); + m_savedRenderer = 0; + + if (frame() && frame()->page()) + frame()->page()->updateViewportArguments(); + + if (childNeedsStyleRecalc()) + scheduleStyleRecalc(); + } +} + +void Document::documentWillBecomeInactive() +{ +#if USE(ACCELERATED_COMPOSITING) + if (renderer()) + renderView()->willMoveOffscreen(); +#endif + + HashSet<Element*>::iterator end = m_documentActivationCallbackElements.end(); + for (HashSet<Element*>::iterator i = m_documentActivationCallbackElements.begin(); i != end; ++i) + (*i)->documentWillBecomeInactive(); +} + +void Document::documentDidBecomeActive() +{ + HashSet<Element*>::iterator end = m_documentActivationCallbackElements.end(); + for (HashSet<Element*>::iterator i = m_documentActivationCallbackElements.begin(); i != end; ++i) + (*i)->documentDidBecomeActive(); + +#if USE(ACCELERATED_COMPOSITING) + if (renderer()) + renderView()->didMoveOnscreen(); +#endif + + ASSERT(m_frame); + m_frame->loader()->client()->dispatchDidBecomeFrameset(isFrameSet()); +} + +void Document::registerForDocumentActivationCallbacks(Element* e) +{ + m_documentActivationCallbackElements.add(e); +} + +void Document::unregisterForDocumentActivationCallbacks(Element* e) +{ + m_documentActivationCallbackElements.remove(e); +} + +void Document::mediaVolumeDidChange() +{ + HashSet<Element*>::iterator end = m_mediaVolumeCallbackElements.end(); + for (HashSet<Element*>::iterator i = m_mediaVolumeCallbackElements.begin(); i != end; ++i) + (*i)->mediaVolumeDidChange(); +} + +void Document::registerForMediaVolumeCallbacks(Element* e) +{ + m_mediaVolumeCallbackElements.add(e); +} + +void Document::unregisterForMediaVolumeCallbacks(Element* e) +{ + m_mediaVolumeCallbackElements.remove(e); +} + +void Document::setShouldCreateRenderers(bool f) +{ + m_createRenderers = f; +} + +bool Document::shouldCreateRenderers() +{ + return m_createRenderers; +} + +// 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(); + + document->updateStyleIfNeeded(); + + 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() == TrueTriState; +} + +bool Document::queryCommandSupported(const String& commandName) +{ + return command(this, commandName).isSupported(); +} + +String Document::queryCommandValue(const String& commandName) +{ + return command(this, commandName).value(); +} + +#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(PassOwnPtr<TransformSource> source) +{ + if (m_transformSource == source) + return; + m_transformSource = source; +} + +#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 +{ + if (!m_frame) + return 0; + Frame* parent = m_frame->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::createAttribute(const String& name, ExceptionCode& ec) +{ + return createAttributeNS(String(), name, ec, true); +} + +PassRefPtr<Attr> Document::createAttributeNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec, bool shouldIgnoreNamespaceChecks) +{ + String prefix, localName; + if (!parseQualifiedName(qualifiedName, prefix, localName, ec)) + return 0; + + QualifiedName qName(prefix, localName, namespaceURI); + if (!shouldIgnoreNamespaceChecks && hasPrefixNamespaceMismatch(qName)) { + ec = NAMESPACE_ERR; + return 0; + } + + // Spec: DOM Level 2 Core: http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrAttrNS + if (!shouldIgnoreNamespaceChecks && qName.localName() == xmlnsAtom && qName.namespaceURI() != XMLNSNames::xmlnsNamespaceURI) { + ec = NAMESPACE_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 Attr::create(0, this, Attribute::createMapped(qName, StringImpl::empty())); +} + +#if ENABLE(SVG) +const SVGDocumentExtensions* Document::svgExtensions() +{ + return m_svgExtensions.get(); +} + +SVGDocumentExtensions* Document::accessSVGExtensions() +{ + if (!m_svgExtensions) + m_svgExtensions = adoptPtr(new SVGDocumentExtensions(this)); + return m_svgExtensions.get(); +} + +bool Document::hasSVGRootNode() const +{ + return documentElement() && documentElement()->hasTagName(SVGNames::svgTag); +} +#endif + +PassRefPtr<HTMLCollection> Document::images() +{ + return HTMLCollection::create(this, DocImages); +} + +PassRefPtr<HTMLCollection> Document::applets() +{ + return HTMLCollection::create(this, DocApplets); +} + +PassRefPtr<HTMLCollection> Document::embeds() +{ + return HTMLCollection::create(this, DocEmbeds); +} + +PassRefPtr<HTMLCollection> Document::plugins() +{ + // This is an alias for embeds() required for the JS DOM bindings. + return HTMLCollection::create(this, DocEmbeds); +} + +PassRefPtr<HTMLCollection> Document::objects() +{ + return HTMLCollection::create(this, DocObjects); +} + +PassRefPtr<HTMLCollection> Document::scripts() +{ + return HTMLCollection::create(this, DocScripts); +} + +PassRefPtr<HTMLCollection> Document::links() +{ + return HTMLCollection::create(this, DocLinks); +} + +PassRefPtr<HTMLCollection> Document::forms() +{ + return HTMLCollection::create(this, DocForms); +} + +PassRefPtr<HTMLCollection> Document::anchors() +{ + return HTMLCollection::create(this, DocAnchors); +} + +PassRefPtr<HTMLAllCollection> Document::all() +{ + return HTMLAllCollection::create(this); +} + +PassRefPtr<HTMLCollection> Document::windowNamedItems(const String &name) +{ + return HTMLNameCollection::create(this, WindowNamedItems, name); +} + +PassRefPtr<HTMLCollection> Document::documentNamedItems(const String &name) +{ + return HTMLNameCollection::create(this, DocumentNamedItems, name); +} + +CollectionCache* Document::nameCollectionInfo(CollectionType type, const AtomicString& name) +{ + ASSERT(type >= FirstNamedDocumentCachedType); + unsigned index = type - FirstNamedDocumentCachedType; + ASSERT(index < NumNamedDocumentCachedTypes); + + NamedCollectionMap& map = m_nameCollectionInfo[index]; + NamedCollectionMap::iterator iter = map.find(name.impl()); + if (iter == map.end()) + iter = map.add(name.impl(), new CollectionCache).first; + iter->second->checkConsistency(); + return iter->second; +} + +void Document::finishedParsing() +{ + ASSERT(!scriptableDocumentParser() || !m_parser->isParsing()); + ASSERT(!scriptableDocumentParser() || m_readyState != Loading); + setParsing(false); + if (!m_documentTiming.domContentLoadedEventStart) + m_documentTiming.domContentLoadedEventStart = currentTime(); + dispatchEvent(Event::create(eventNames().DOMContentLoadedEvent, true, false)); + if (!m_documentTiming.domContentLoadedEventEnd) + m_documentTiming.domContentLoadedEventEnd = currentTime(); + + if (Frame* f = frame()) { + // FrameLoader::finishedParsing() might end up calling Document::implicitClose() if all + // resource loads are complete. HTMLObjectElements can start loading their resources from + // post attach callbacks triggered by recalcStyle(). This means if we parse out an <object> + // tag and then reach the end of the document without updating styles, we might not have yet + // started the resource load and might fire the window load event too early. To avoid this + // we force the styles to be up to date before calling FrameLoader::finishedParsing(). + // See https://bugs.webkit.org/show_bug.cgi?id=36864 starting around comment 35. + updateStyleIfNeeded(); + + f->loader()->finishedParsing(); + +#if ENABLE(INSPECTOR) + if (!page()) + return; + + if (InspectorController* controller = page()->inspectorController()) + controller->mainResourceFiredDOMContentEvent(f->loader()->documentLoader(), url()); +#endif + } +} + +Vector<String> Document::formElementsState() const +{ + Vector<String> stateVector; + stateVector.reserveInitialCapacity(m_formElementsWithState.size() * 3); + typedef FormElementListHashSet::const_iterator Iterator; + Iterator end = m_formElementsWithState.end(); + for (Iterator it = m_formElementsWithState.begin(); it != end; ++it) { + Element* elementWithState = *it; + String value; + if (!elementWithState->shouldSaveAndRestoreFormControlState()) + continue; + if (!elementWithState->saveFormControlState(value)) + continue; + stateVector.append(elementWithState->formControlName().string()); + stateVector.append(elementWithState->formControlType().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()->ref(); + if (type()) + type()->ref(); +} + +void FormElementKey::deref() const +{ + if (name()) + name()->deref(); + if (type()) + type()->deref(); +} + +unsigned FormElementKeyHash::hash(const FormElementKey& key) +{ + return WTF::StringHasher::createBlobHash<sizeof(FormElementKey)>(&key); +} + +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; + if (Frame* f = frame()) + f->loader()->setIconURL(m_iconURL); +} + +void Document::registerFormElementWithFormAttribute(FormAssociatedElement* element) +{ + ASSERT(toHTMLElement(element)->fastHasAttribute(formAttr)); + m_formElementsWithFormAttribute.add(element); +} + +void Document::unregisterFormElementWithFormAttribute(FormAssociatedElement* element) +{ + m_formElementsWithFormAttribute.remove(element); +} + +void Document::resetFormElementsOwner(HTMLFormElement* form) +{ + typedef FormAssociatedElementListHashSet::iterator Iterator; + Iterator end = m_formElementsWithFormAttribute.end(); + for (Iterator it = m_formElementsWithFormAttribute.begin(); it != end; ++it) + (*it)->resetFormOwner(form); +} + +void Document::setUseSecureKeyboardEntryWhenActive(bool usesSecureKeyboard) +{ + if (m_useSecureKeyboardEntryWhenActive == usesSecureKeyboard) + return; + + m_useSecureKeyboardEntryWhenActive = usesSecureKeyboard; + m_frame->selection()->updateSecureKeyboardEntryIfActive(); +} + +bool Document::useSecureKeyboardEntryWhenActive() const +{ + return m_useSecureKeyboardEntryWhenActive; +} + +void Document::initSecurityContext() +{ + if (securityOrigin() && !securityOrigin()->isEmpty()) + return; // m_securityOrigin has already been initialized. + + if (!m_frame) { + // No source for a security context. + // This can occur via document.implementation.createDocument(). + m_cookieURL = KURL(ParsedURLString, ""); + ScriptExecutionContext::setSecurityOrigin(SecurityOrigin::createEmpty()); + return; + } + + // In the common case, create the security context from the currently + // loading URL. + m_cookieURL = m_url; + ScriptExecutionContext::setSecurityOrigin(SecurityOrigin::create(m_url, m_frame->loader()->sandboxFlags())); + + if (SecurityOrigin::allowSubstituteDataAccessToLocal()) { + // If this document was loaded with substituteData, then the document can + // load local resources. See https://bugs.webkit.org/show_bug.cgi?id=16756 + // and https://bugs.webkit.org/show_bug.cgi?id=19760 for further + // discussion. + DocumentLoader* documentLoader = m_frame->loader()->documentLoader(); + if (documentLoader && documentLoader->substituteData().isValid()) + securityOrigin()->grantLoadLocalResources(); + } + + if (Settings* settings = this->settings()) { + if (!settings->isWebSecurityEnabled()) { + // Web security is turned off. We should let this document access every + // other document. This is used primary by testing harnesses for web + // sites. + securityOrigin()->grantUniversalAccess(); + + } else if (settings->allowUniversalAccessFromFileURLs() && securityOrigin()->isLocal()) { + // Some clients want file:// URLs to have universal access, but that + // setting is dangerous for other clients. + securityOrigin()->grantUniversalAccess(); + } else if (!settings->allowFileAccessFromFileURLs() && securityOrigin()->isLocal()) { + // Some clients want file:// URLs to have even tighter restrictions by + // default, and not be able to access other local files. + securityOrigin()->enforceFilePathSeparation(); + } + } + + if (!securityOrigin()->isEmpty()) + return; + + // If we do not obtain a meaningful origin from the URL, then we try to + // find one via the frame hierarchy. + + Frame* ownerFrame = m_frame->tree()->parent(); + if (!ownerFrame) + ownerFrame = m_frame->loader()->opener(); + + if (ownerFrame) { + m_cookieURL = ownerFrame->document()->cookieURL(); + // We alias the SecurityOrigins to match Firefox, see Bug 15313 + // https://bugs.webkit.org/show_bug.cgi?id=15313 + ScriptExecutionContext::setSecurityOrigin(ownerFrame->document()->securityOrigin()); + } +} + +void Document::setSecurityOrigin(SecurityOrigin* securityOrigin) +{ + ScriptExecutionContext::setSecurityOrigin(securityOrigin); + // FIXME: Find a better place to enable DNS prefetch, which is a loader concept, + // not applicable to arbitrary documents. + initDNSPrefetch(); +} + +#if ENABLE(DATABASE) + +bool Document::allowDatabaseAccess() const +{ + if (!page() || page()->settings()->privateBrowsingEnabled()) + return false; + return true; +} + +void Document::databaseExceededQuota(const String& name) +{ + Page* currentPage = page(); + if (currentPage) + currentPage->chrome()->client()->exceededDatabaseQuota(document()->frame(), name); +} + +#endif + +bool Document::isContextThread() const +{ + return isMainThread(); +} + +void Document::updateURLForPushOrReplaceState(const KURL& url) +{ + Frame* f = frame(); + if (!f) + return; + + // FIXME: Eliminate this redundancy. + setURL(url); + f->loader()->setURL(url); + f->loader()->documentLoader()->replaceRequestURLForSameDocumentNavigation(url); +} + +void Document::statePopped(SerializedScriptValue* stateObject) +{ + if (!frame()) + return; + + // Per step 11 of section 6.5.9 (history traversal) of the HTML5 spec, we + // defer firing of popstate until we're in the complete state. + if (m_readyState == Complete) + enqueuePopstateEvent(stateObject); + else + m_pendingStateObject = stateObject; +} + +void Document::updateFocusAppearanceSoon(bool restorePreviousSelection) +{ + m_updateFocusAppearanceRestoresSelection = restorePreviousSelection; + 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(m_updateFocusAppearanceRestoresSelection); +} + +// FF method for accessing the selection added for compatibility. +DOMSelection* Document::getSelection() const +{ + return frame() ? frame()->domWindow()->getSelection() : 0; +} + +#if ENABLE(WML) +void Document::resetWMLPageState() +{ + if (WMLPageState* pageState = wmlPageStateForDocument(this)) + pageState->reset(); +} + +void Document::initializeWMLPageState() +{ + if (!isWMLDocument()) + return; + + static_cast<WMLDocument*>(this)->initialize(); +} +#endif + +void Document::attachRange(Range* range) +{ + ASSERT(!m_ranges.contains(range)); + m_ranges.add(range); +} + +void Document::detachRange(Range* range) +{ + // We don't ASSERT m_ranges.contains(range) to allow us to call this + // unconditionally to fix: https://bugs.webkit.org/show_bug.cgi?id=26044 + m_ranges.remove(range); +} + +CanvasRenderingContext* Document::getCSSCanvasContext(const String& type, const String& name, int width, int height) +{ + HTMLCanvasElement* result = getCSSCanvasElement(name); + if (!result) + return 0; + result->setSize(IntSize(width, height)); + return result->getContext(type); +} + +HTMLCanvasElement* Document::getCSSCanvasElement(const String& name) +{ + RefPtr<HTMLCanvasElement> result = m_cssCanvasElements.get(name).get(); + if (!result) { + result = HTMLCanvasElement::create(this); + m_cssCanvasElements.set(name, result); + } + return result.get(); +} + +void Document::initDNSPrefetch() +{ + Settings* settings = this->settings(); + + m_haveExplicitlyDisabledDNSPrefetch = false; + m_isDNSPrefetchEnabled = settings && settings->dnsPrefetchingEnabled() && securityOrigin()->protocol() == "http"; + + // Inherit DNS prefetch opt-out from parent frame + if (Document* parent = parentDocument()) { + if (!parent->isDNSPrefetchEnabled()) + m_isDNSPrefetchEnabled = false; + } +} + +void Document::parseDNSPrefetchControlHeader(const String& dnsPrefetchControl) +{ + if (equalIgnoringCase(dnsPrefetchControl, "on") && !m_haveExplicitlyDisabledDNSPrefetch) { + m_isDNSPrefetchEnabled = true; + return; + } + + m_isDNSPrefetchEnabled = false; + m_haveExplicitlyDisabledDNSPrefetch = true; +} + +void Document::reportException(const String& errorMessage, int lineNumber, const String& sourceURL) +{ + addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, errorMessage, lineNumber, sourceURL); +} + +void Document::addMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL) +{ + if (DOMWindow* window = domWindow()) + window->console()->addMessage(source, type, level, message, lineNumber, sourceURL); +} + +struct PerformTaskContext : Noncopyable { + PerformTaskContext(PassRefPtr<DocumentWeakReference> documentReference, PassOwnPtr<ScriptExecutionContext::Task> task) + : documentReference(documentReference) + , task(task) + { + } + + RefPtr<DocumentWeakReference> documentReference; + OwnPtr<ScriptExecutionContext::Task> task; +}; + +static void performTask(void* ctx) +{ + ASSERT(isMainThread()); + + PerformTaskContext* context = reinterpret_cast<PerformTaskContext*>(ctx); + ASSERT(context); + + if (Document* document = context->documentReference->document()) + context->task->performTask(document); + + delete context; +} + +void Document::postTask(PassOwnPtr<Task> task) +{ + callOnMainThread(performTask, new PerformTaskContext(m_weakReference, task)); +} + +Element* Document::findAnchor(const String& name) +{ + if (name.isEmpty()) + return 0; + if (Element* element = getElementById(name)) + return element; + for (Node* node = this; node; node = node->traverseNextNode()) { + if (node->hasTagName(aTag)) { + HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(node); + if (inQuirksMode()) { + // Quirks mode, case insensitive comparison of names. + if (equalIgnoringCase(anchor->name(), name)) + return anchor; + } else { + // Strict mode, names need to match exactly. + if (anchor->name() == name) + return anchor; + } + } + } + return 0; +} + +String Document::displayStringModifiedByEncoding(const String& str) const +{ + if (m_decoder) + return m_decoder->encoding().displayString(str.impl()); + return str; +} + +PassRefPtr<StringImpl> Document::displayStringModifiedByEncoding(PassRefPtr<StringImpl> str) const +{ + if (m_decoder) + return m_decoder->encoding().displayString(str); + return str; +} + +void Document::displayBufferModifiedByEncoding(UChar* buffer, unsigned len) const +{ + if (m_decoder) + m_decoder->encoding().displayBuffer(buffer, len); +} + +void Document::enqueuePageshowEvent(PageshowEventPersistence persisted) +{ + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=36334 Pageshow event needs to fire asynchronously. + dispatchWindowEvent(PageTransitionEvent::create(eventNames().pageshowEvent, persisted), this); +} + +void Document::enqueueHashchangeEvent(const String& oldURL, const String& newURL) +{ + enqueueWindowEvent(HashChangeEvent::create(oldURL, newURL)); +} + +void Document::enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject) +{ + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=36202 Popstate event needs to fire asynchronously + dispatchWindowEvent(PopStateEvent::create(stateObject)); +} + +void Document::addMediaCanStartListener(MediaCanStartListener* listener) +{ + ASSERT(!m_mediaCanStartListeners.contains(listener)); + m_mediaCanStartListeners.add(listener); +} + +void Document::removeMediaCanStartListener(MediaCanStartListener* listener) +{ + ASSERT(m_mediaCanStartListeners.contains(listener)); + m_mediaCanStartListeners.remove(listener); +} + +MediaCanStartListener* Document::takeAnyMediaCanStartListener() +{ + HashSet<MediaCanStartListener*>::iterator slot = m_mediaCanStartListeners.begin(); + if (slot == m_mediaCanStartListeners.end()) + return 0; + MediaCanStartListener* listener = *slot; + m_mediaCanStartListeners.remove(slot); + return listener; +} + +#if ENABLE(XHTMLMP) +bool Document::isXHTMLMPDocument() const +{ + if (!frame() || !frame()->loader()) + return false; + // As per section 7.2 of OMA-WAP-XHTMLMP-V1_1-20061020-A.pdf, a conforming user agent + // MUST accept XHTMLMP document identified as "application/vnd.wap.xhtml+xml" + // and SHOULD accept it identified as "application/xhtml+xml" , "application/xhtml+xml" is a + // general MIME type for all XHTML documents, not only for XHTMLMP + return frame()->loader()->writer()->mimeType() == "application/vnd.wap.xhtml+xml"; +} +#endif + +#if ENABLE(INSPECTOR) +InspectorController* Document::inspectorController() const +{ + return page() ? page()->inspectorController() : 0; +} +#endif + +#if ENABLE(FULLSCREEN_API) +void Document::webkitRequestFullScreenForElement(Element* element, unsigned short flags) +{ + if (!page() || !page()->settings()->fullScreenEnabled()) + return; + + if (!element) + element = documentElement(); + + if (!page()->chrome()->client()->supportsFullScreenForElement(element)) + return; + + m_areKeysEnabledInFullScreen = flags & Element::ALLOW_KEYBOARD_INPUT; + page()->chrome()->client()->enterFullScreenForElement(element); +} + +void Document::webkitCancelFullScreen() +{ + if (!page() || !m_fullScreenElement) + return; + + page()->chrome()->client()->exitFullScreenForElement(m_fullScreenElement.get()); +} + +void Document::webkitWillEnterFullScreenForElement(Element* element) +{ + ASSERT(element); + ASSERT(page() && page()->settings()->fullScreenEnabled()); + + m_fullScreenElement = element; + m_isFullScreen = true; + + if (m_fullScreenElement != documentElement()) + m_fullScreenElement->detach(); + + recalcStyle(Force); + + if (m_fullScreenRenderer) + m_fullScreenRenderer->setAnimating(true); +} + +void Document::webkitDidEnterFullScreenForElement(Element*) +{ + if (m_fullScreenRenderer) + m_fullScreenRenderer->setAnimating(false); + m_fullScreenChangeDelayTimer.startOneShot(0); +} + +void Document::webkitWillExitFullScreenForElement(Element*) +{ + if (m_fullScreenRenderer) + m_fullScreenRenderer->setAnimating(true); + + recalcStyle(Force); +} + +void Document::webkitDidExitFullScreenForElement(Element*) +{ + m_isFullScreen = false; + m_areKeysEnabledInFullScreen = false; + + if (m_fullScreenRenderer) + m_fullScreenRenderer->remove(); + + if (m_fullScreenElement != documentElement()) + m_fullScreenElement->detach(); + + setFullScreenRenderer(0); + recalcStyle(Force); + + m_fullScreenChangeDelayTimer.startOneShot(0); +} + +void Document::setFullScreenRenderer(RenderFullScreen* renderer) +{ + m_fullScreenRenderer = renderer; + + // This notification can come in after the page has been destroyed. + if (page()) + page()->chrome()->client()->fullScreenRendererChanged(m_fullScreenRenderer); +} + +void Document::setFullScreenRendererSize(const IntSize& size) +{ + ASSERT(m_fullScreenRenderer); + if (!m_fullScreenRenderer) + return; + + if (m_fullScreenRenderer) { + RefPtr<RenderStyle> newStyle = RenderStyle::clone(m_fullScreenRenderer->style()); + newStyle->setWidth(Length(size.width(), WebCore::Fixed)); + newStyle->setHeight(Length(size.height(), WebCore::Fixed)); + newStyle->setTop(Length(0, WebCore::Fixed)); + newStyle->setLeft(Length(0, WebCore::Fixed)); + m_fullScreenRenderer->setStyle(newStyle); + } +} + +void Document::setFullScreenRendererBackgroundColor(Color backgroundColor) +{ + if (!m_fullScreenRenderer) + return; + + RefPtr<RenderStyle> newStyle = RenderStyle::clone(m_fullScreenRenderer->style()); + newStyle->setBackgroundColor(backgroundColor); + m_fullScreenRenderer->setStyle(newStyle); +} + +void Document::fullScreenChangeDelayTimerFired(Timer<Document>*) +{ + Element* element = m_fullScreenElement.get(); + if (!element) + element = documentElement(); + + element->dispatchEvent(Event::create(eventNames().webkitfullscreenchangeEvent, true, false)); +} + +#endif + +void Document::decrementLoadEventDelayCount() +{ + ASSERT(m_loadEventDelayCount); + --m_loadEventDelayCount; + + if (frame() && !m_loadEventDelayCount && !m_loadEventDelayTimer.isActive()) + m_loadEventDelayTimer.startOneShot(0); +} + +void Document::loadEventDelayTimerFired(Timer<Document>*) +{ + if (frame()) + frame()->loader()->checkCompleted(); +} + +#if ENABLE(TOUCH_EVENTS) +PassRefPtr<Touch> Document::createTouch(DOMWindow* window, EventTarget* target, int identifier, int pageX, int pageY, int screenX, int screenY, ExceptionCode&) const +{ + // FIXME: It's not clear from the documentation at + // http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/DocumentAdditionsReference/DocumentAdditions/DocumentAdditions.html + // when this method should throw and nor is it by inspection of iOS behavior. It would be nice to verify any cases where it throws under iOS + // and implement them here. See https://bugs.webkit.org/show_bug.cgi?id=47819 + // Ditto for the createTouchList method below. + Frame* frame = window ? window->frame() : this->frame(); + return Touch::create(frame, target, identifier, screenX, screenY, pageX, pageY); +} + +PassRefPtr<TouchList> Document::createTouchList(ExceptionCode&) const +{ + return TouchList::create(); +} +#endif + +static bool hasHeadSibling(const Document* document) +{ + Node* de = document->documentElement(); + if (!de) + return false; + + for (Node* i = de->firstChild(); i; i = i->nextSibling()) { + // A child of the document element which is rather than <head> is + // typically visible and FOUC safe. So we return true here. + if (!i->hasTagName(headTag)) + return true; + } + + return false; +} + +bool Document::mayCauseFlashOfUnstyledContent() const +{ + // Some kind of FOUC is caused by a repaint request before page's <body> arrival + // because page authors often give background styles to <body>, not to <html>. + // (And these styles are unavailable before <style> or <link> is given.) + // This functions is used for checking such possibility of FOUCs. + // Note that the implementation considers only empty or <head> only contents as a FOUC cause + // rather than missing <body>, because non-HTML document like SVG and arbitrary XML from foreign namespace + // should be painted even if there is no <body>. + if (didLayoutWithPendingStylesheets()) + return true; + return !hasHeadSibling(this); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h new file mode 100644 index 0000000..e114385 --- /dev/null +++ b/Source/WebCore/dom/Document.h @@ -0,0 +1,1458 @@ +/* + * 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, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "CheckedRadioButtons.h" +#include "CollectionCache.h" +#include "CollectionType.h" +#include "Color.h" +#include "ContainerNode.h" +#include "DocumentTiming.h" +#include "QualifiedName.h" +#include "ScriptExecutionContext.h" +#include "Timer.h" +#include "ViewportArguments.h" +#include <wtf/FixedArray.h> +#include <wtf/HashCountedSet.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> + +#if USE(JSC) +#include <runtime/WeakGCMap.h> +#endif + +namespace WebCore { + +class AsyncScriptRunner; +class Attr; +class AXObjectCache; +class CDATASection; +class CachedCSSStyleSheet; +class CachedResourceLoader; +class CachedScript; +class CanvasRenderingContext; +class CharacterData; +class CSSStyleDeclaration; +class CSSStyleSelector; +class CSSStyleSheet; +class Comment; +class DOMImplementation; +class DOMSelection; +class DOMWindow; +class Database; +class DatabaseThread; +class DocumentFragment; +class DocumentMarkerController; +class DocumentType; +class DocumentWeakReference; +class EditingText; +class Element; +class EntityReference; +class Event; +class EventListener; +class EventQueue; +class FormAssociatedElement; +class Frame; +class FrameView; +class HTMLCanvasElement; +class HTMLCollection; +class HTMLAllCollection; +class HTMLDocument; +class HTMLElement; +class HTMLFormElement; +class HTMLFrameOwnerElement; +class HTMLHeadElement; +class HTMLInputElement; +class HTMLMapElement; +class HitTestRequest; +class HitTestResult; +class InspectorController; +class IntPoint; +class DOMWrapperWorld; +class JSNode; +class MediaCanStartListener; +class MediaQueryList; +class MediaQueryMatcher; +class MouseEventWithHitTestResults; +class NodeFilter; +class NodeIterator; +class Page; +class PlatformMouseEvent; +class ProcessingInstruction; +class Range; +class RegisteredEventListener; +class RenderArena; +class RenderView; +class RenderFullScreen; +class ScriptableDocumentParser; +class ScriptElementData; +class SecurityOrigin; +class SerializedScriptValue; +class SegmentedString; +class Settings; +class StyleSheet; +class StyleSheetList; +class Text; +class TextResourceDecoder; +class DocumentParser; +class TreeWalker; +class XMLHttpRequest; + +#if ENABLE(SVG) +class SVGDocumentExtensions; +#endif + +#if ENABLE(XSLT) +class TransformSource; +#endif + +#if ENABLE(XPATH) +class XPathEvaluator; +class XPathExpression; +class XPathNSResolver; +class XPathResult; +#endif + +#if ENABLE(DASHBOARD_SUPPORT) +struct DashboardRegionValue; +#endif + +#if ENABLE(TOUCH_EVENTS) +class Touch; +class TouchList; +#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; } + + // Hash table deleted values, which are only constructed and never copied or destroyed. + FormElementKey(WTF::HashTableDeletedValueType) : m_name(hashTableDeletedValue()) { } + bool isHashTableDeletedValue() const { return m_name == hashTableDeletedValue(); } + +private: + void ref() const; + void deref() const; + + static AtomicStringImpl* hashTableDeletedValue() { return reinterpret_cast<AtomicStringImpl*>(-1); } + + 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 void constructDeletedValue(FormElementKey& slot) { new (&slot) FormElementKey(WTF::HashTableDeletedValue); } + static bool isDeletedValue(const FormElementKey& value) { return value.isHashTableDeletedValue(); } +}; + +enum PageshowEventPersistence { + PageshowEventNotPersisted = 0, + PageshowEventPersisted = 1 +}; + +enum StyleSelectorUpdateFlag { RecalcStyleImmediately, DeferRecalcStyle }; + +class Document : public ContainerNode, public ScriptExecutionContext { +public: + static PassRefPtr<Document> create(Frame* frame, const KURL& url) + { + return adoptRef(new Document(frame, url, false, false)); + } + static PassRefPtr<Document> createXHTML(Frame* frame, const KURL& url) + { + return adoptRef(new Document(frame, url, true, false)); + } + virtual ~Document(); + + MediaQueryMatcher* mediaQueryMatcher(); + + using ContainerNode::ref; + using ContainerNode::deref; + + // 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 + + DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); + DEFINE_ATTRIBUTE_EVENT_LISTENER(change); + DEFINE_ATTRIBUTE_EVENT_LISTENER(click); + DEFINE_ATTRIBUTE_EVENT_LISTENER(contextmenu); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dblclick); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragenter); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragover); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragleave); + DEFINE_ATTRIBUTE_EVENT_LISTENER(drop); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragstart); + DEFINE_ATTRIBUTE_EVENT_LISTENER(drag); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragend); + DEFINE_ATTRIBUTE_EVENT_LISTENER(input); + DEFINE_ATTRIBUTE_EVENT_LISTENER(invalid); + DEFINE_ATTRIBUTE_EVENT_LISTENER(keydown); + DEFINE_ATTRIBUTE_EVENT_LISTENER(keypress); + DEFINE_ATTRIBUTE_EVENT_LISTENER(keyup); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mousedown); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mousemove); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseout); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseover); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseup); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mousewheel); + DEFINE_ATTRIBUTE_EVENT_LISTENER(scroll); + DEFINE_ATTRIBUTE_EVENT_LISTENER(select); + DEFINE_ATTRIBUTE_EVENT_LISTENER(submit); + + DEFINE_ATTRIBUTE_EVENT_LISTENER(blur); + DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + DEFINE_ATTRIBUTE_EVENT_LISTENER(focus); + DEFINE_ATTRIBUTE_EVENT_LISTENER(load); + DEFINE_ATTRIBUTE_EVENT_LISTENER(readystatechange); + + // WebKit extensions + DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecut); + DEFINE_ATTRIBUTE_EVENT_LISTENER(cut); + DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecopy); + DEFINE_ATTRIBUTE_EVENT_LISTENER(copy); + DEFINE_ATTRIBUTE_EVENT_LISTENER(beforepaste); + DEFINE_ATTRIBUTE_EVENT_LISTENER(paste); + DEFINE_ATTRIBUTE_EVENT_LISTENER(reset); + DEFINE_ATTRIBUTE_EVENT_LISTENER(search); + DEFINE_ATTRIBUTE_EVENT_LISTENER(selectstart); +#if ENABLE(TOUCH_EVENTS) + DEFINE_ATTRIBUTE_EVENT_LISTENER(touchstart); + DEFINE_ATTRIBUTE_EVENT_LISTENER(touchmove); + DEFINE_ATTRIBUTE_EVENT_LISTENER(touchend); + DEFINE_ATTRIBUTE_EVENT_LISTENER(touchcancel); +#endif +#if ENABLE(FULLSCREEN_API) + DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitfullscreenchange); +#endif + + ViewportArguments viewportArguments() const { return m_viewportArguments; } + + DocumentType* doctype() const { return m_docType.get(); } + + DOMImplementation* implementation() const; + + Element* documentElement() const + { + if (!m_documentElement) + cacheDocumentElement(); + return m_documentElement.get(); + } + + virtual PassRefPtr<Element> createElement(const AtomicString& 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&); + PassRefPtr<Attr> createAttributeNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode&, bool shouldIgnoreNamespaceChecks = false); + 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); + Element* getElementById(const AtomicString&) const; + bool hasElementWithId(AtomicStringImpl* id) const; + bool containsMultipleElementsWithId(const AtomicString& id) const; + + /** + * Retrieve all nodes that intersect a rect in the window's document, until it is fully enclosed by + * the boundaries of a node. + * + * @param centerX x reference for the rectangle in CSS pixels + * @param centerY y reference for the rectangle in CSS pixels + * @param topPadding How much to expand the top of the rectangle + * @param rightPadding How much to expand the right of the rectangle + * @param bottomPadding How much to expand the bottom of the rectangle + * @param leftPadding How much to expand the left of the rectangle + * @param ignoreClipping whether or not to ignore the root scroll frame when retrieving the element. + * If false, this method returns null for coordinates outside of the viewport. + */ + PassRefPtr<NodeList> nodesFromRect(int centerX, int centerY, unsigned topPadding, unsigned rightPadding, + unsigned bottomPadding, unsigned leftPadding, bool ignoreClipping) const; + Element* elementFromPoint(int x, int y) const; + PassRefPtr<Range> caretRangeFromPoint(int x, int y); + + String readyState() const; + + String defaultCharset() const; + + // Synonyms backing similar DOM attributes. Use Document::encoding() to avoid virtual dispatch. + String inputEncoding() const { return Document::encoding(); } + String charset() const { return Document::encoding(); } + String characterSet() const { return Document::encoding(); } + + 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 XMLDocumentParser + void setXMLVersion(const String&, ExceptionCode&); + void setXMLStandalone(bool, ExceptionCode&); + + String documentURI() const { return m_documentURI; } + 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> objects(); + PassRefPtr<HTMLCollection> scripts(); + PassRefPtr<HTMLCollection> windowNamedItems(const String& name); + PassRefPtr<HTMLCollection> documentNamedItems(const String& name); + + PassRefPtr<HTMLAllCollection> all(); + + // Find first anchor with the given name. + // First searches for an element with the given ID, but if that fails, then looks + // for an anchor with the given name. ID matching is always case sensitive, but + // Anchor name matching is case sensitive in strict mode and not case sensitive in + // quirks mode for historical compatibility reasons. + Element* findAnchor(const String& name); + + CollectionCache* collectionInfo(CollectionType type) + { + ASSERT(type >= FirstUnnamedDocumentCachedType); + unsigned index = type - FirstUnnamedDocumentCachedType; + ASSERT(index < NumUnnamedDocumentCachedTypes); + m_collectionInfo[index].checkConsistency(); + return &m_collectionInfo[index]; + } + + CollectionCache* nameCollectionInfo(CollectionType, const AtomicString& name); + + // Other methods (not part of DOM) + bool isHTMLDocument() const { return m_isHTML; } + bool isXHTMLDocument() const { return m_isXHTML; } + virtual bool isImageDocument() const { return false; } +#if ENABLE(SVG) + virtual bool isSVGDocument() const { return false; } + bool hasSVGRootNode() const; +#else + static bool isSVGDocument() { return false; } + static bool hasSVGRootNode() { return false; } +#endif + virtual bool isPluginDocument() const { return false; } + virtual bool isMediaDocument() const { return false; } +#if ENABLE(WML) + virtual bool isWMLDocument() const { return false; } +#endif +#if ENABLE(XHTMLMP) + bool isXHTMLMPDocument() const; + bool shouldProcessNoscriptElement() const { return m_shouldProcessNoScriptElement; } + void setShouldProcessNoscriptElement(bool shouldDo) { m_shouldProcessNoScriptElement = shouldDo; } +#endif + virtual bool isFrameSet() const { return false; } + + CSSStyleSelector* styleSelector() + { + if (!m_styleSelector) + createStyleSelector(); + return m_styleSelector.get(); + } + + 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; + } + + /** + * Increments the number of pending sheets. The <link> elements + * invoke this to add themselves to the loading list. + */ + void addPendingSheet() { m_pendingStylesheets++; } + + void addStyleSheetCandidateNode(Node*, bool createdByParser); + void removeStyleSheetCandidateNode(Node*); + + 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 styleSelectorChanged(StyleSelectorUpdateFlag); + 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; } + bool usesBeforeAfterRules() const { return m_usesBeforeAfterRules; } + void setUsesBeforeAfterRules(bool b) { m_usesBeforeAfterRules = b; } + bool usesRemUnits() const { return m_usesRemUnits; } + void setUsesRemUnits(bool b) { m_usesRemUnits = b; } + bool usesLinkRules() const { return linkColor() != visitedLinkColor() || m_usesLinkRules; } + void setUsesLinkRules(bool b) { m_usesLinkRules = b; } + + // Machinery for saving and restoring state when you leave and then go back to a page. + void registerFormElementWithState(Element* e) { m_formElementsWithState.add(e); } + void unregisterFormElementWithState(Element* 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); + + void registerFormElementWithFormAttribute(FormAssociatedElement*); + void unregisterFormElementWithFormAttribute(FormAssociatedElement*); + void resetFormElementsOwner(HTMLFormElement*); + + 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 +#if ENABLE(INSPECTOR) + virtual InspectorController* inspectorController() const; // can be NULL +#endif + + 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); + bool childNeedsAndNotInStyleRecalc(); + virtual void updateStyleIfNeeded(); + void updateLayout(); + void updateLayoutIgnorePendingStylesheets(); + PassRefPtr<RenderStyle> styleForElementIgnoringPendingStylesheets(Element*); + PassRefPtr<RenderStyle> styleForPage(int pageIndex); + + // Returns true if page box (margin boxes and page borders) is visible. + bool isPageBoxVisible(int pageIndex); + + // Returns the preferred page size and margins in pixels, assuming 96 + // pixels per inch. pageSize, marginTop, marginRight, marginBottom, + // marginLeft must be initialized to the default values that are used if + // auto is specified. + void pageSizeAndMarginsInPixels(int pageIndex, IntSize& pageSize, int& marginTop, int& marginRight, int& marginBottom, int& marginLeft); + + static void updateStyleForAllDocuments(); // FIXME: Try to reduce the # of calls to this function. + CachedResourceLoader* cachedResourceLoader() { return m_cachedResourceLoader.get(); } + + virtual void attach(); + virtual void detach(); + + RenderArena* renderArena() { return m_renderArena.get(); } + + RenderView* renderView() const; + + void clearAXObjectCache(); + AXObjectCache* axObjectCache() const; + bool axObjectCacheExists() const; + + // to get visually ordered hebrew and arabic pages right + void setVisuallyOrdered(); + bool visuallyOrdered() const { return m_visuallyOrdered; } + + void open(Document* ownerDocument = 0); + void implicitOpen(); + void close(); + void implicitClose(); + void cancelParsing(); + + void write(const SegmentedString& text, Document* ownerDocument = 0); + void write(const String& text, Document* ownerDocument = 0); + void writeln(const String& text, Document* ownerDocument = 0); + void finishParsing(); + + bool wellFormed() const { return m_wellFormed; } + + const KURL& url() const { return m_url; } + void setURL(const KURL&); + + const KURL& baseURL() const { return m_baseURL; } + const String& baseTarget() const { return m_baseTarget; } + void processBaseElement(); + + KURL completeURL(const String&) const; + + virtual String userAgent(const KURL&) const; + + CSSStyleSheet* pageUserSheet(); + void clearPageUserSheet(); + void updatePageUserSheet(); + + const Vector<RefPtr<CSSStyleSheet> >* pageGroupUserSheets() const; + void clearPageGroupUserSheets(); + void updatePageGroupUserSheets(); + + CSSStyleSheet* elementSheet(); + CSSStyleSheet* mappedElementSheet(); + + virtual PassRefPtr<DocumentParser> createParser(); + DocumentParser* parser() const { return m_parser.get(); } + ScriptableDocumentParser* scriptableDocumentParser() const; + + bool printing() const { return m_printing; } + void setPrinting(bool p) { m_printing = p; } + + bool paginatedForScreen() const { return m_paginatedForScreen; } + void setPaginatedForScreen(bool p) { m_paginatedForScreen = p; } + + bool paginated() const { return printing() || paginatedForScreen(); } + + enum CompatibilityMode { QuirksMode, LimitedQuirksMode, NoQuirksMode }; + + virtual void setCompatibilityModeFromDoctype() { } + void setCompatibilityMode(CompatibilityMode m); + void lockCompatibilityMode() { m_compatibilityModeLocked = true; } + CompatibilityMode compatibilityMode() const { return m_compatibilityMode; } + + String compatMode() const; + + bool inQuirksMode() const { return m_compatibilityMode == QuirksMode; } + bool inLimitedQuirksMode() const { return m_compatibilityMode == LimitedQuirksMode; } + bool inNoQuirksMode() const { return m_compatibilityMode == NoQuirksMode; } + + enum ReadyState { + Loading, + Interactive, + Complete + }; + void setReadyState(ReadyState); + void setParsing(bool); + bool parsing() const { return m_bParsing; } + int minimumLayoutDelay(); + + // This method is used by Android. + void setExtraLayoutDelay(int delay) { m_extraLayoutDelay = delay; } + + 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&); + + 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 getFocusableNodes(Vector<RefPtr<Node> >&); + + // The m_ignoreAutofocus flag specifies whether or not the document has been changed by the user enough + // for WebCore to ignore the autofocus attribute on any form controls + bool ignoreAutofocus() const { return m_ignoreAutofocus; }; + void setIgnoreAutofocus(bool shouldIgnore = true) { m_ignoreAutofocus = shouldIgnore; }; + + 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(Element*); + Element* cssTarget() const { return m_cssTarget; } + + void scheduleForcedStyleRecalc(); + void scheduleStyleRecalc(); + void unscheduleStyleRecalc(); + bool isPendingStyleRecalc() const; + void styleRecalcTimerFired(Timer<Document>*); + + void attachNodeIterator(NodeIterator*); + void detachNodeIterator(NodeIterator*); + void moveNodeIteratorsToNewDocument(Node*, Document*); + + void attachRange(Range*); + void detachRange(Range*); + + void nodeChildrenChanged(ContainerNode*); + // nodeChildrenWillBeRemoved is used when removing all node children at once. + void nodeChildrenWillBeRemoved(ContainerNode*); + // nodeWillBeRemoved is only safe when removing one node at a time. + void nodeWillBeRemoved(Node*); + + void textInserted(Node*, unsigned offset, unsigned length); + void textRemoved(Node*, unsigned offset, unsigned length); + void textNodesMerged(Text* oldNode, unsigned offset); + void textNodeSplit(Text* oldNode); + + DOMWindow* defaultView() const { return domWindow(); } + DOMWindow* domWindow() const; + + // Helper functions for forwarding DOMWindow event related tasks to the DOMWindow if it exists. + void setWindowAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener>); + EventListener* getWindowAttributeEventListener(const AtomicString& eventType); + void dispatchWindowEvent(PassRefPtr<Event>, PassRefPtr<EventTarget> = 0); + void dispatchWindowLoadEvent(); + + 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, + ANIMATIONEND_LISTENER = 0x100, + ANIMATIONSTART_LISTENER = 0x200, + ANIMATIONITERATION_LISTENER = 0x400, + TRANSITIONEND_LISTENER = 0x800, + BEFORELOAD_LISTENER = 0x1000, + TOUCH_LISTENER = 0x2000, + BEFOREPROCESS_LISTENER = 0x4000 + }; + + bool hasListenerType(ListenerType listenerType) const { return (m_listenerTypes & listenerType); } + void addListenerType(ListenerType listenerType) { m_listenerTypes = m_listenerTypes | listenerType; } + void addListenerTypeIfNeeded(const AtomicString& eventType); + + CSSStyleDeclaration* getOverrideStyle(Element*, const String& pseudoElt); + + /** + * 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 processViewport(const String& features); + +#ifdef ANDROID_META_SUPPORT + /** + * Handles viewport like <meta name = "viewport" content = "width = device-width"> + * or format-detection like <meta name = "format-detection" content = "telephone=no"> + */ + void processMetadataSettings(const String& content); +#endif + + // Returns the owning element in the parent document. + // Returns 0 if this is the top level document. + HTMLFrameOwnerElement* ownerElement() const; + + String title() const { return m_title; } + void setTitle(const String&, Element* titleElement = 0); + void removeTitle(Element* titleElement); + + String cookie(ExceptionCode&) const; + void setCookie(const String&, ExceptionCode&); + + String referrer() const; + + String domain() const; + void setDomain(const String& newDomain, ExceptionCode&); + + String lastModified() const; + + // The cookieURL is used to query the cookie database for this document's + // cookies. For example, if the cookie URL is http://example.com, we'll + // use the non-Secure cookies for example.com when computing + // document.cookie. + // + // Q: How is the cookieURL different from the document's URL? + // A: The two URLs are the same almost all the time. However, if one + // document inherits the security context of another document, it + // inherits its cookieURL but not its URL. + // + const KURL& cookieURL() const { return m_cookieURL; } + + // The firstPartyForCookies is used to compute whether this document + // appears in a "third-party" context for the purpose of third-party + // cookie blocking. The document is in a third-party context if the + // cookieURL and the firstPartyForCookies are from different hosts. + // + // Note: Some ports (including possibly Apple's) only consider the + // document in a third-party context if the cookieURL and the + // firstPartyForCookies have a different registry-controlled + // domain. + // + const KURL& firstPartyForCookies() const { return m_firstPartyForCookies; } + void setFirstPartyForCookies(const KURL& url) { m_firstPartyForCookies = 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. It also sets ExceptionCode when name is invalid. + static bool parseQualifiedName(const String& qualifiedName, String& prefix, String& localName, ExceptionCode&); + + // Checks to make sure prefix and namespace do not conflict (per DOM Core 3) + static bool hasPrefixNamespaceMismatch(const QualifiedName&); + + 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() const; + void setBody(PassRefPtr<HTMLElement>, ExceptionCode&); + + HTMLHeadElement* head(); + + DocumentMarkerController* markers() const { return m_markers.get(); } + + bool directionSetOnDocumentElement() const { return m_directionSetOnDocumentElement; } + bool writingModeSetOnDocumentElement() const { return m_writingModeSetOnDocumentElement; } + void setDirectionSetOnDocumentElement(bool b) { m_directionSetOnDocumentElement = b; } + void setWritingModeSetOnDocumentElement(bool b) { m_writingModeSetOnDocumentElement = b; } + + 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); + + // 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; } + + AsyncScriptRunner* asyncScriptRunner() { return m_asyncScriptRunner.get(); } + +#if ENABLE(XSLT) + void applyXSLTransform(ProcessingInstruction* pi); + PassRefPtr<Document> transformSourceDocument() { return m_transformSourceDocument; } + void setTransformSourceDocument(Document* doc) { m_transformSourceDocument = doc; } + + void setTransformSource(PassOwnPtr<TransformSource>); + TransformSource* transformSource() const { return m_transformSource.get(); } +#endif + + void incDOMTreeVersion() { ++m_domTreeVersion; } + unsigned domTreeVersion() const { return m_domTreeVersion; } + +#ifdef ANDROID_STYLE_VERSION + void incStyleVersion() { ++m_styleVersion; } + unsigned styleVersion() const { return m_styleVersion; } +#endif + + void setDocType(PassRefPtr<DocumentType>); + +#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; } + + const String& iconURL() const { return m_iconURL; } + void setIconURL(const String& iconURL, const String& type); + + void setUseSecureKeyboardEntryWhenActive(bool); + bool useSecureKeyboardEntryWhenActive() const; + + void addNodeListCache() { ++m_numNodeListCaches; } + void removeNodeListCache() { ASSERT(m_numNodeListCaches > 0); --m_numNodeListCaches; } + bool hasNodeListCaches() const { return m_numNodeListCaches; } + + void updateFocusAppearanceSoon(bool restorePreviousSelection); + void cancelFocusAppearanceUpdate(); + + // FF method for accessing the selection added for compatibility. + DOMSelection* getSelection() const; + + // Extension for manipulating canvas drawing contexts for use in CSS + CanvasRenderingContext* getCSSCanvasContext(const String& type, const String& name, int width, int height); + HTMLCanvasElement* getCSSCanvasElement(const String& name); + + bool isDNSPrefetchEnabled() const { return m_isDNSPrefetchEnabled; } + void parseDNSPrefetchControlHeader(const String&); + + virtual void reportException(const String& errorMessage, int lineNumber, const String& sourceURL); + virtual void addMessage(MessageSource, MessageType, MessageLevel, const String& message, unsigned lineNumber, const String& sourceURL); + virtual void postTask(PassOwnPtr<Task>); // Executes the task on context's thread asynchronously. + +#if USE(JSC) + typedef JSC::WeakGCMap<WebCore::Node*, JSNode*> JSWrapperCache; + typedef HashMap<DOMWrapperWorld*, JSWrapperCache*> JSWrapperCacheMap; + JSWrapperCacheMap& wrapperCacheMap() { return m_wrapperCacheMap; } + JSWrapperCache* getWrapperCache(DOMWrapperWorld* world); + JSWrapperCache* createWrapperCache(DOMWrapperWorld*); + void destroyWrapperCache(DOMWrapperWorld*); + void destroyAllWrapperCaches(); +#endif + + virtual void finishedParsing(); + + bool inPageCache() const { return m_inPageCache; } + void setInPageCache(bool flag); + + // Elements can register themselves for the "documentWillBecomeInactive()" and + // "documentDidBecomeActive()" callbacks + void registerForDocumentActivationCallbacks(Element*); + void unregisterForDocumentActivationCallbacks(Element*); + void documentWillBecomeInactive(); + void documentDidBecomeActive(); + + void registerForMediaVolumeCallbacks(Element*); + void unregisterForMediaVolumeCallbacks(Element*); + void mediaVolumeDidChange(); + + void setShouldCreateRenderers(bool); + bool shouldCreateRenderers(); + + void setDecoder(PassRefPtr<TextResourceDecoder>); + TextResourceDecoder* decoder() const { return m_decoder.get(); } + + String displayStringModifiedByEncoding(const String&) const; + PassRefPtr<StringImpl> displayStringModifiedByEncoding(PassRefPtr<StringImpl>) const; + void displayBufferModifiedByEncoding(UChar* buffer, unsigned len) const; + + // Quirk for the benefit of Apple's Dictionary application. + void setFrameElementsShouldIgnoreScrolling(bool ignore) { m_frameElementsShouldIgnoreScrolling = ignore; } + bool frameElementsShouldIgnoreScrolling() const { return m_frameElementsShouldIgnoreScrolling; } + +#if ENABLE(DASHBOARD_SUPPORT) + 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>&); +#endif + + virtual void removeAllEventListeners(); + + CheckedRadioButtons& checkedRadioButtons() { return m_checkedRadioButtons; } + +#if ENABLE(SVG) + const SVGDocumentExtensions* svgExtensions(); + SVGDocumentExtensions* accessSVGExtensions(); +#endif + + void initSecurityContext(); + + // 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*); + + void updateURLForPushOrReplaceState(const KURL&); + void statePopped(SerializedScriptValue*); + + bool processingLoadEvent() const { return m_processingLoadEvent; } + +#if ENABLE(DATABASE) + virtual bool allowDatabaseAccess() const; + virtual void databaseExceededQuota(const String& name); +#endif + + virtual bool isContextThread() const; + virtual bool isJSExecutionTerminated() const { return false; } + + void setUsingGeolocation(bool f) { m_usingGeolocation = f; } + bool usingGeolocation() const { return m_usingGeolocation; }; + +#if ENABLE(WML) + void setContainsWMLContent(bool value) { m_containsWMLContent = value; } + bool containsWMLContent() const { return m_containsWMLContent; } + + void resetWMLPageState(); + void initializeWMLPageState(); +#endif + + bool containsValidityStyleRules() const { return m_containsValidityStyleRules; } + void setContainsValidityStyleRules() { m_containsValidityStyleRules = true; } + + void enqueueWindowEvent(PassRefPtr<Event>); + void enqueuePageshowEvent(PageshowEventPersistence); + void enqueueHashchangeEvent(const String& oldURL, const String& newURL); + void enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject); + EventQueue* eventQueue() const { return m_eventQueue.get(); } + + void addMediaCanStartListener(MediaCanStartListener*); + void removeMediaCanStartListener(MediaCanStartListener*); + MediaCanStartListener* takeAnyMediaCanStartListener(); + + const QualifiedName& idAttributeName() const { return m_idAttributeName; } + +#if ENABLE(FULLSCREEN_API) + bool webkitIsFullScreen() const { return m_isFullScreen; } + bool webkitFullScreenKeyboardInputAllowed() const { return m_isFullScreen && m_areKeysEnabledInFullScreen; } + Element* webkitCurrentFullScreenElement() const { return m_fullScreenElement.get(); } + void webkitRequestFullScreenForElement(Element*, unsigned short flags); + void webkitCancelFullScreen(); + + void webkitWillEnterFullScreenForElement(Element*); + void webkitDidEnterFullScreenForElement(Element*); + void webkitWillExitFullScreenForElement(Element*); + void webkitDidExitFullScreenForElement(Element*); + + void setFullScreenRenderer(RenderFullScreen*); + RenderFullScreen* fullScreenRenderer() const { return m_fullScreenRenderer; } + + void setFullScreenRendererSize(const IntSize&); + void setFullScreenRendererBackgroundColor(Color); + + void fullScreenChangeDelayTimerFired(Timer<Document>*); +#endif + + // Used to allow element that loads data without going through a FrameLoader to delay the 'load' event. + void incrementLoadEventDelayCount() { ++m_loadEventDelayCount; } + void decrementLoadEventDelayCount(); + bool isDelayingLoadEvent() const { return m_loadEventDelayCount; } + +#if ENABLE(TOUCH_EVENTS) + PassRefPtr<Touch> createTouch(DOMWindow*, EventTarget*, int identifier, int pageX, int pageY, int screenX, int screenY, ExceptionCode&) const; + PassRefPtr<TouchList> createTouchList(ExceptionCode&) const; +#endif + + const DocumentTiming* timing() const { return &m_documentTiming; } + + bool mayCauseFlashOfUnstyledContent() const; + + void initDNSPrefetch(); + +protected: + Document(Frame*, const KURL& url, bool isXHTML, bool isHTML, const KURL& baseURL = KURL()); + + void clearXMLVersion() { m_xmlVersion = String(); } + + +private: + class DocumentOrderedMap { + public: + void add(AtomicStringImpl*, Element*); + void remove(AtomicStringImpl*, Element*); + void clear(); + + bool contains(AtomicStringImpl*) const; + bool containsMultiple(AtomicStringImpl*) const; + template<bool keyMatches(AtomicStringImpl*, Element*)> Element* get(AtomicStringImpl*, const Document*) const; + + void checkConsistency() const; + + private: + typedef HashMap<AtomicStringImpl*, Element*> Map; + + // We maintain the invariant that m_duplicateCounts is the count of all elements with a given key + // excluding the one referenced in m_map, if any. This means it one less than the total count + // when the first node with a given key is cached, otherwise the same as the total count. + mutable Map m_map; + mutable HashCountedSet<AtomicStringImpl*> m_duplicateCounts; + }; + + friend class IgnoreDestructiveWriteCountIncrementer; + + void detachParser(); + + typedef void (*ArgumentsCallback)(const String& keyString, const String& valueString, Document*, void* data); + void processArguments(const String& features, void* data, ArgumentsCallback); + + virtual bool isDocument() const { return true; } + virtual void removedLastRef(); + + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual bool childTypeAllowed(NodeType); + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool canReplaceChild(Node* newChild, Node* oldChild); + + virtual void refScriptExecutionContext() { ref(); } + virtual void derefScriptExecutionContext() { deref(); } + + virtual const KURL& virtualURL() const; // Same as url(), but needed for ScriptExecutionContext to implement it without a performance loss for direct calls. + virtual KURL virtualCompleteURL(const String&) const; // Same as completeURL() for the same reason as above. + + String encoding() const; + + void updateTitle(); + void updateFocusAppearanceTimerFired(Timer<Document>*); + void updateBaseURL(); + + void cacheDocumentElement() const; + + void createStyleSelector(); + + PassRefPtr<NodeList> handleZeroPadding(const HitTestRequest&, HitTestResult&) const; + + void loadEventDelayTimerFired(Timer<Document>*); + + OwnPtr<CSSStyleSelector> m_styleSelector; + bool m_didCalculateStyleSelector; + + Frame* m_frame; + OwnPtr<CachedResourceLoader> m_cachedResourceLoader; + RefPtr<DocumentParser> m_parser; + bool m_wellFormed; + + // Document URLs. + KURL m_url; // Document.URL: The URL from which this document was retrieved. + KURL m_baseURL; // Node.baseURI: The URL to use when resolving relative URLs. + KURL m_baseElementURL; // The URL set by the <base> element. + KURL m_cookieURL; // The URL to use for cookie access. + KURL m_firstPartyForCookies; // The policy URL for third-party cookie blocking. + + // Document.documentURI: + // Although URL-like, Document.documentURI can actually be set to any + // string by content. Document.documentURI affects m_baseURL unless the + // document contains a <base> element, in which case the <base> element + // takes precedence. + String m_documentURI; + + String m_baseTarget; + + RefPtr<DocumentType> m_docType; + mutable RefPtr<DOMImplementation> m_implementation; + + // Track the number of currently loading top-level stylesheets needed for rendering. + // 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 and when it is safe to execute scripts. + 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; + RefPtr<CSSStyleSheet> m_pageUserSheet; + mutable OwnPtr<Vector<RefPtr<CSSStyleSheet> > > m_pageGroupUserSheets; + mutable bool m_pageGroupUserSheetCacheValid; + + bool m_printing; + bool m_paginatedForScreen; + + bool m_ignoreAutofocus; + + CompatibilityMode m_compatibilityMode; + bool m_compatibilityModeLocked; // This is cheaper than making setCompatibilityMode virtual. + + Color m_textColor; + + RefPtr<Node> m_focusedNode; + RefPtr<Node> m_hoverNode; + RefPtr<Node> m_activeNode; + mutable RefPtr<Element> m_documentElement; + + unsigned m_domTreeVersion; +#ifdef ANDROID_STYLE_VERSION + unsigned m_styleVersion; +#endif + + HashSet<NodeIterator*> m_nodeIterators; + HashSet<Range*> m_ranges; + + unsigned short m_listenerTypes; + + RefPtr<StyleSheetList> m_styleSheets; // All of the stylesheets that are currently in effect for our media type and stylesheet set. + + typedef ListHashSet<Node*, 32> StyleSheetCandidateListHashSet; + StyleSheetCandidateListHashSet m_styleSheetCandidateNodes; // All of the nodes that could potentially provide stylesheets to the document (<link>, <style>, <?xml-stylesheet>) + + typedef ListHashSet<Element*, 64> FormElementListHashSet; + FormElementListHashSet m_formElementsWithState; + typedef ListHashSet<FormAssociatedElement*, 32> FormAssociatedElementListHashSet; + FormAssociatedElementListHashSet m_formElementsWithFormAttribute; + + typedef HashMap<FormElementKey, Vector<String>, FormElementKeyHash, FormElementKeyHashTraits> FormElementStateMap; + FormElementStateMap m_stateForNewFormElements; + + Color m_linkColor; + Color m_visitedLinkColor; + Color m_activeLinkColor; + + String m_preferredStylesheetSet; + String m_selectedStylesheetSet; + + bool m_loadingSheet; + bool m_visuallyOrdered; + ReadyState m_readyState; + bool m_bParsing; + + Timer<Document> m_styleRecalcTimer; + bool m_pendingStyleRecalcShouldForce; + bool m_inStyleRecalc; + bool m_closeAfterStyleRecalc; + + bool m_usesDescendantRules; + bool m_usesSiblingRules; + bool m_usesFirstLineRules; + bool m_usesFirstLetterRules; + bool m_usesBeforeAfterRules; + bool m_usesRemUnits; + bool m_usesLinkRules; + bool m_gotoAnchorNeededAfterStylesheetsLoad; + bool m_isDNSPrefetchEnabled; + bool m_haveExplicitlyDisabledDNSPrefetch; + bool m_frameElementsShouldIgnoreScrolling; + bool m_containsValidityStyleRules; + bool m_updateFocusAppearanceRestoresSelection; + + // http://www.whatwg.org/specs/web-apps/current-work/#ignore-destructive-writes-counter + unsigned m_ignoreDestructiveWriteCount; + + String m_title; + String m_rawTitle; + bool m_titleSetExplicitly; + RefPtr<Element> m_titleElement; + + OwnPtr<RenderArena> m_renderArena; + +#if !PLATFORM(ANDROID) + mutable AXObjectCache* m_axObjectCache; +#endif + OwnPtr<DocumentMarkerController> m_markers; + + Timer<Document> m_updateFocusAppearanceTimer; + + Element* m_cssTarget; + + bool m_processingLoadEvent; + RefPtr<SerializedScriptValue> m_pendingStateObject; + double m_startTime; + bool m_overMinimumLayoutThreshold; + // This is used to increase the minimum delay between re-layouts. It is set + // using setExtraLayoutDelay to modify the minimum delay used at different + // points during the lifetime of the Document. + int m_extraLayoutDelay; + + OwnPtr<AsyncScriptRunner> m_asyncScriptRunner; + +#if ENABLE(XSLT) + OwnPtr<TransformSource> m_transformSource; + RefPtr<Document> m_transformSourceDocument; +#endif + + DocumentOrderedMap m_imageMapsByName; + + 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; + +#if ENABLE(XHTMLMP) + bool m_shouldProcessNoScriptElement; +#endif + + RenderObject* m_savedRenderer; + + RefPtr<TextResourceDecoder> m_decoder; + + DocumentOrderedMap m_elementsById; + + mutable HashMap<StringImpl*, Element*, CaseFoldingHash> m_elementsByAccessKey; + + InheritedBool m_designMode; + + int m_selfOnlyRefCount; + + CheckedRadioButtons m_checkedRadioButtons; + + typedef HashMap<AtomicStringImpl*, CollectionCache*> NamedCollectionMap; + FixedArray<CollectionCache, NumUnnamedDocumentCachedTypes> m_collectionInfo; + FixedArray<NamedCollectionMap, NumNamedDocumentCachedTypes> m_nameCollectionInfo; + +#if ENABLE(XPATH) + RefPtr<XPathEvaluator> m_xpathEvaluator; +#endif + +#if ENABLE(SVG) + OwnPtr<SVGDocumentExtensions> m_svgExtensions; +#endif + +#if ENABLE(DASHBOARD_SUPPORT) + Vector<DashboardRegionValue> m_dashboardRegions; + bool m_hasDashboardRegions; + bool m_dashboardRegionsDirty; +#endif + + HashMap<String, RefPtr<HTMLCanvasElement> > m_cssCanvasElements; + + mutable bool m_accessKeyMapValid; + bool m_createRenderers; + bool m_inPageCache; + String m_iconURL; + + HashSet<Element*> m_documentActivationCallbackElements; + HashSet<Element*> m_mediaVolumeCallbackElements; + + bool m_useSecureKeyboardEntryWhenActive; + + bool m_isXHTML; + bool m_isHTML; + + unsigned m_numNodeListCaches; + +#if USE(JSC) + JSWrapperCacheMap m_wrapperCacheMap; + JSWrapperCache* m_normalWorldWrapperCache; +#endif + + bool m_usingGeolocation; + + OwnPtr<EventQueue> m_eventQueue; + +#if ENABLE(WML) + bool m_containsWMLContent; +#endif + + RefPtr<DocumentWeakReference> m_weakReference; + + HashSet<MediaCanStartListener*> m_mediaCanStartListeners; + + QualifiedName m_idAttributeName; + +#if ENABLE(FULLSCREEN_API) + bool m_isFullScreen; + bool m_areKeysEnabledInFullScreen; + RefPtr<Element> m_fullScreenElement; + RenderFullScreen* m_fullScreenRenderer; + Timer<Document> m_fullScreenChangeDelayTimer; +#endif + + int m_loadEventDelayCount; + Timer<Document> m_loadEventDelayTimer; + + ViewportArguments m_viewportArguments; + + bool m_directionSetOnDocumentElement; + bool m_writingModeSetOnDocumentElement; + + DocumentTiming m_documentTiming; + RefPtr<MediaQueryMatcher> m_mediaQueryMatcher; +}; + +inline bool Document::DocumentOrderedMap::contains(AtomicStringImpl* id) const +{ + return m_map.contains(id) || m_duplicateCounts.contains(id); +} + +inline bool Document::DocumentOrderedMap::containsMultiple(AtomicStringImpl* id) const +{ + return m_duplicateCounts.contains(id); +} + +inline bool Document::hasElementWithId(AtomicStringImpl* id) const +{ + ASSERT(id); + return m_elementsById.contains(id); +} + +inline bool Document::containsMultipleElementsWithId(const AtomicString& id) const +{ + return m_elementsById.containsMultiple(id.impl()); +} + +inline bool Node::isDocumentNode() const +{ + return this == m_document; +} + +// here because it uses a Document method but we really want to inline it +inline Node::Node(Document* document, ConstructionType type) + : m_document(document) + , m_previous(0) + , m_next(0) + , m_renderer(0) + , m_nodeFlags(type) +{ + if (m_document) + m_document->selfOnlyRef(); +#if !defined(NDEBUG) || (defined(DUMP_NODE_STATISTICS) && DUMP_NODE_STATISTICS) + trackForDebugging(); +#endif +} + +} // namespace WebCore + +#endif // Document_h diff --git a/Source/WebCore/dom/Document.idl b/Source/WebCore/dom/Document.idl new file mode 100644 index 0000000..89f53ec --- /dev/null +++ b/Source/WebCore/dom/Document.idl @@ -0,0 +1,347 @@ +/* + * 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 [ + CustomToJS, + GenerateNativeConverter, + CustomMarkFunction, + InlineGetOwnPropertySlot + ] Document : Node { + + // DOM Level 1 Core + readonly attribute DocumentType doctype; + readonly attribute [V8Custom] DOMImplementation implementation; + readonly attribute Element documentElement; + + [ReturnsNew] Element createElement(in [ConvertNullToNullString] DOMString tagName) + raises (DOMException); + DocumentFragment createDocumentFragment(); + [ReturnsNew] Text createTextNode(in DOMString data); + [ReturnsNew] Comment createComment(in DOMString data); + [ReturnsNew] CDATASection createCDATASection(in DOMString data) + raises(DOMException); + [OldStyleObjC, ReturnsNew] ProcessingInstruction createProcessingInstruction(in DOMString target, + in DOMString data) + raises (DOMException); + [ReturnsNew] Attr createAttribute(in DOMString name) + raises (DOMException); + [ReturnsNew] EntityReference createEntityReference(in DOMString name) + raises(DOMException); + NodeList getElementsByTagName(in DOMString tagname); + + // Introduced in DOM Level 2: + + [OldStyleObjC, ReturnsNew] Node importNode(in Node importedNode, + in boolean deep) + raises (DOMException); + [OldStyleObjC, ReturnsNew] Element createElementNS(in [ConvertNullToNullString] DOMString namespaceURI, + in [ConvertNullToNullString] DOMString qualifiedName) + raises (DOMException); + [OldStyleObjC, ReturnsNew] Attr createAttributeNS(in [ConvertNullToNullString] DOMString namespaceURI, + in [ConvertNullToNullString] 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; + + // 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) + + [OldStyleObjC] NodeIterator createNodeIterator(in Node root, + in unsigned long whatToShow, + in NodeFilter filter, + in boolean expandEntityReferences) + raises(DOMException); + [OldStyleObjC] TreeWalker createTreeWalker(in Node root, + in unsigned long whatToShow, + in NodeFilter filter, + in boolean expandEntityReferences) + raises(DOMException); + + // 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); +#if defined(ENABLE_XPATH) && 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, V8Custom] XPathResult evaluate(in DOMString expression, + in Node contextNode, + in XPathNSResolver resolver, + in unsigned short type, + in XPathResult inResult) + raises(DOMException); +#endif // ENABLE_XPATH + + // Common extensions + + boolean execCommand(in DOMString command, + in boolean userInterface, + in [ConvertUndefinedOrNullToNullString] DOMString value); + +#if defined(LANGUAGE_OBJECTIVE_C) && 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) && LANGUAGE_JAVASCRIPT + attribute [ConvertNullToNullString] DOMString domain + setter raises (DOMException); +#else + readonly attribute DOMString domain; +#endif + readonly attribute DOMString URL; + + attribute [ConvertNullToNullString] DOMString cookie + setter raises (DOMException), + getter raises (DOMException); + + // FIXME: the DOM spec does NOT have this attribute + // raising an exception. + attribute HTMLElement body + setter raises (DOMException); + + readonly attribute HTMLHeadElement head; + 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) && 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); + Range caretRangeFromPoint(in long x, in long y); + + // Mozilla extensions +#if defined(LANGUAGE_JAVASCRIPT) && 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_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT + CSSStyleDeclaration createCSSStyleDeclaration(); +#endif + +#if defined(LANGUAGE_OBJECTIVE_C) && 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 + +#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C + [V8Custom] DOMObject getCSSCanvasContext(in DOMString contextId, in DOMString name, in long width, in long height); +#endif +#endif + + // HTML 5 + NodeList getElementsByClassName(in DOMString tagname); + + readonly attribute DOMString compatMode; + + // NodeSelector - Selector API + [RequiresAllArguments=Raise] Element querySelector(in DOMString selectors) + raises(DOMException); + [RequiresAllArguments=Raise] NodeList querySelectorAll(in DOMString selectors) + raises(DOMException); + +#if defined(ENABLE_WML) && ENABLE_WML + // Only used from within WML layout tests, WML doesn't have JS support at all. + [DontEnum] void resetWMLPageState(); + [DontEnum] void initializeWMLPageState(); +#endif + +#if defined(ENABLE_FULLSCREEN_API) && ENABLE_FULLSCREEN_API + readonly attribute boolean webkitIsFullScreen; + readonly attribute boolean webkitFullScreenKeyboardInputAllowed; + readonly attribute Element webkitCurrentFullScreenElement; + void webkitCancelFullScreen(); +#endif + +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C + // Event handler DOM attributes + attribute [DontEnum] EventListener onabort; + attribute [DontEnum] EventListener onblur; + attribute [DontEnum] EventListener onchange; + attribute [DontEnum] EventListener onclick; + attribute [DontEnum] EventListener oncontextmenu; + attribute [DontEnum] EventListener ondblclick; + attribute [DontEnum] EventListener ondrag; + attribute [DontEnum] EventListener ondragend; + attribute [DontEnum] EventListener ondragenter; + attribute [DontEnum] EventListener ondragleave; + attribute [DontEnum] EventListener ondragover; + attribute [DontEnum] EventListener ondragstart; + attribute [DontEnum] EventListener ondrop; + attribute [DontEnum] EventListener onerror; + attribute [DontEnum] EventListener onfocus; + attribute [DontEnum] EventListener oninput; + attribute [DontEnum] EventListener oninvalid; + attribute [DontEnum] EventListener onkeydown; + attribute [DontEnum] EventListener onkeypress; + attribute [DontEnum] EventListener onkeyup; + attribute [DontEnum] EventListener onload; + attribute [DontEnum] EventListener onmousedown; + attribute [DontEnum] EventListener onmousemove; + attribute [DontEnum] EventListener onmouseout; + attribute [DontEnum] EventListener onmouseover; + attribute [DontEnum] EventListener onmouseup; + attribute [DontEnum] EventListener onmousewheel; + attribute [DontEnum] EventListener onreadystatechange; + attribute [DontEnum] EventListener onscroll; + attribute [DontEnum] EventListener onselect; + attribute [DontEnum] EventListener onsubmit; + + // attribute [DontEnum] EventListener oncanplay; + // attribute [DontEnum] EventListener oncanplaythrough; + // attribute [DontEnum] EventListener ondurationchange; + // attribute [DontEnum] EventListener onemptied; + // attribute [DontEnum] EventListener onended; + // attribute [DontEnum] EventListener onformchange; + // attribute [DontEnum] EventListener onforminput; + // attribute [DontEnum] EventListener onloadeddata; + // attribute [DontEnum] EventListener onloadedmetadata; + // attribute [DontEnum] EventListener onloadstart; + // attribute [DontEnum] EventListener onpause; + // attribute [DontEnum] EventListener onplay; + // attribute [DontEnum] EventListener onplaying; + // attribute [DontEnum] EventListener onprogress; + // attribute [DontEnum] EventListener onratechange; + // attribute [DontEnum] EventListener onseeked; + // attribute [DontEnum] EventListener onseeking; + // attribute [DontEnum] EventListener onshow; + // attribute [DontEnum] EventListener onstalled; + // attribute [DontEnum] EventListener onsuspend; + // attribute [DontEnum] EventListener ontimeupdate; + // attribute [DontEnum] EventListener onvolumechange; + // attribute [DontEnum] EventListener onwaiting; + + // WebKit extensions + attribute [DontEnum] EventListener onbeforecut; + attribute [DontEnum] EventListener oncut; + attribute [DontEnum] EventListener onbeforecopy; + attribute [DontEnum] EventListener oncopy; + attribute [DontEnum] EventListener onbeforepaste; + attribute [DontEnum] EventListener onpaste; + attribute [DontEnum] EventListener onreset; + attribute [DontEnum] EventListener onsearch; + attribute [DontEnum] EventListener onselectstart; + attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchstart; + attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchmove; + attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchend; + attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchcancel; + attribute [DontEnum, Conditional=FULLSCREEN_API] EventListener onwebkitfullscreenchange; +#endif + +#if defined(ENABLE_TOUCH_EVENTS) && ENABLE_TOUCH_EVENTS + [ReturnsNew, EnabledAtRuntime] Touch createTouch(in DOMWindow window, + in EventTarget target, + in long identifier, + in long pageX, + in long pageY, + in long ScreenX, + in long screenY) + raises (DOMException); + [ReturnsNew, EnabledAtRuntime] TouchList createTouchList() + raises (DOMException); +#endif + +#if defined(LANGUAGE_CPP) && LANGUAGE_CPP + // Extra WebCore methods exposed to allow compile-time casting in C++ + boolean isHTMLDocument(); +#endif + + }; + +} diff --git a/Source/WebCore/dom/DocumentFragment.cpp b/Source/WebCore/dom/DocumentFragment.cpp new file mode 100644 index 0000000..c9c3020 --- /dev/null +++ b/Source/WebCore/dom/DocumentFragment.cpp @@ -0,0 +1,88 @@ +/* + * 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, 2009 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 "DocumentFragment.h" + +#include "Document.h" +#include "HTMLDocumentParser.h" +#include "Page.h" +#include "Settings.h" +#include "XMLDocumentParser.h" + +namespace WebCore { + +inline DocumentFragment::DocumentFragment(Document* document) + : ContainerNode(document) +{ + ASSERT(document); +} + +PassRefPtr<DocumentFragment> DocumentFragment::create(Document* document) +{ + return adoptRef(new DocumentFragment(document)); +} + +String DocumentFragment::nodeName() const +{ + return "#document-fragment"; +} + +Node::NodeType DocumentFragment::nodeType() const +{ + return DOCUMENT_FRAGMENT_NODE; +} + +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; + } +} + +PassRefPtr<Node> DocumentFragment::cloneNode(bool deep) +{ + RefPtr<DocumentFragment> clone = create(document()); + if (deep) + cloneChildNodes(clone.get()); + return clone.release(); +} + +void DocumentFragment::parseHTML(const String& source, Element* contextElement, FragmentScriptingPermission scriptingPermission) +{ + HTMLDocumentParser::parseDocumentFragment(source, this, contextElement, scriptingPermission); +} + +bool DocumentFragment::parseXML(const String& source, Element* contextElement, FragmentScriptingPermission scriptingPermission) +{ + return XMLDocumentParser::parseDocumentFragment(source, this, contextElement, scriptingPermission); +} + +} diff --git a/Source/WebCore/dom/DocumentFragment.h b/Source/WebCore/dom/DocumentFragment.h new file mode 100644 index 0000000..d588b4e --- /dev/null +++ b/Source/WebCore/dom/DocumentFragment.h @@ -0,0 +1,50 @@ +/* + * 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, 2009 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 DocumentFragment_h +#define DocumentFragment_h + +#include "ContainerNode.h" +#include "FragmentScriptingPermission.h" + +namespace WebCore { + +class DocumentFragment : public ContainerNode { +public: + static PassRefPtr<DocumentFragment> create(Document*); + + void parseHTML(const String&, Element* contextElement, FragmentScriptingPermission = FragmentScriptingAllowed); + bool parseXML(const String&, Element* contextElement, FragmentScriptingPermission = FragmentScriptingAllowed); + +private: + DocumentFragment(Document*); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/dom/DocumentFragment.idl b/Source/WebCore/dom/DocumentFragment.idl new file mode 100644 index 0000000..ffdcce1 --- /dev/null +++ b/Source/WebCore/dom/DocumentFragment.idl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 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. + */ + +module core { + + interface DocumentFragment : Node { + // NodeSelector - Selector API + [RequiresAllArguments=Raise] Element querySelector(in DOMString selectors) + raises(DOMException); + [RequiresAllArguments=Raise] NodeList querySelectorAll(in DOMString selectors) + raises(DOMException); + }; + +} diff --git a/Source/WebCore/dom/DocumentMarker.h b/Source/WebCore/dom/DocumentMarker.h new file mode 100644 index 0000000..2be60f8 --- /dev/null +++ b/Source/WebCore/dom/DocumentMarker.h @@ -0,0 +1,65 @@ +/* + * 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" +#include <wtf/Forward.h> + +namespace WebCore { + +// 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. +// It also optionally includes a flag specifying whether the match is active, which is ignored +// for all types other than type TextMatch. +struct DocumentMarker { + enum MarkerType { + Spelling = 1 << 0, + Grammar = 1 << 1, + TextMatch = 1 << 2, + Replacement = 1 << 3, + CorrectionIndicator = 1 << 4, + RejectedCorrection = 1 << 5, + AllMarkers = Spelling | Grammar | TextMatch | Replacement | CorrectionIndicator | RejectedCorrection + }; + MarkerType type; + typedef unsigned MarkerTypes; + unsigned startOffset; + unsigned endOffset; + String description; + bool activeMatch; + + 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/Source/WebCore/dom/DocumentMarkerController.cpp b/Source/WebCore/dom/DocumentMarkerController.cpp new file mode 100644 index 0000000..5f88631 --- /dev/null +++ b/Source/WebCore/dom/DocumentMarkerController.cpp @@ -0,0 +1,597 @@ +/* + * 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, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) Research In Motion Limited 2010. 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 "DocumentMarkerController.h" + +#include "Node.h" +#include "Range.h" +#include "TextIterator.h" + +namespace WebCore { + +static IntRect placeholderRectForMarker() +{ + return IntRect(-1, -1, -1, -1); +} + +void DocumentMarkerController::detach() +{ + if (m_markers.isEmpty()) + return; + deleteAllValues(m_markers); + m_markers.clear(); +} + +void DocumentMarkerController::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, false}; + addMarker(textPiece->startContainer(exception), marker); + } +} + +void DocumentMarkerController::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* pastLastNode = range->pastLastNode(); + for (Node* node = range->firstNode(); node != pastLastNode; 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 start offset. +// Markers of the same type do not overlap each other. + +void DocumentMarkerController::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; + size_t numMarkers = markers.size(); + ASSERT(numMarkers == rects.size()); + size_t i; + // Iterate over all markers whose start offset is less than or equal to the new marker's. + // If one of them is of the same type as the new marker and touches it or intersects with it + // (there is at most one), remove it and adjust the new marker's start offset to encompass it. + for (i = 0; i < numMarkers; ++i) { + DocumentMarker marker = markers[i]; + if (marker.startOffset > newMarker.startOffset) + break; + if (marker.type == newMarker.type && marker.endOffset >= newMarker.startOffset) { + newMarker.startOffset = marker.startOffset; + markers.remove(i); + rects.remove(i); + numMarkers--; + break; + } + } + size_t j = i; + // Iterate over all markers whose end offset is less than or equal to the new marker's, + // removing markers of the same type as the new marker which touch it or intersect with it, + // adjusting the new marker's end offset to cover them if necessary. + while (j < numMarkers) { + DocumentMarker marker = markers[j]; + if (marker.startOffset > newMarker.endOffset) + break; + if (marker.type == newMarker.type) { + markers.remove(j); + rects.remove(j); + if (newMarker.endOffset <= marker.endOffset) { + newMarker.endOffset = marker.endOffset; + break; + } + numMarkers--; + } else + j++; + } + // 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 DocumentMarkerController::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 DocumentMarkerController::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* DocumentMarkerController::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> DocumentMarkerController::markersForNode(Node* node) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (vectorPair) + return vectorPair->first; + return Vector<DocumentMarker>(); +} + +Vector<IntRect> DocumentMarkerController::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 DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerType markerType) +{ + MarkerMap::iterator iterator = m_markers.find(node); + if (iterator != m_markers.end()) + removeMarkersFromMarkerMapVectorPair(node, iterator->second, markerType); +} + +void DocumentMarkerController::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(); + MarkerMapVectorPair* vectorPair = i->second; + removeMarkersFromMarkerMapVectorPair(node, vectorPair, markerType); + } +} + +// This function may release node and vectorPair. +void DocumentMarkerController::removeMarkersFromMarkerMapVectorPair(Node* node, MarkerMapVectorPair* vectorPair, DocumentMarker::MarkerType markerType) +{ + if (markerType == DocumentMarker::AllMarkers) { + delete vectorPair; + m_markers.remove(node); + if (RenderObject* renderer = node->renderer()) + renderer->repaint(); + } else { + bool needsRepaint = false; + 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) { + ++i; + continue; + } + + // pitch the old marker + markers.remove(i); + rects.remove(i); + needsRepaint = true; + // i now is the index of the next marker + } + + // 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 (needsRepaint) { + 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 DocumentMarkerController::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 DocumentMarkerController::setRenderedRectForMarker(Node* node, const 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 DocumentMarkerController::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 DocumentMarkerController::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(); +} + +void DocumentMarkerController::setMarkersActive(Range* range, bool active) +{ + if (m_markers.isEmpty()) + return; + + ExceptionCode ec = 0; + Node* startContainer = range->startContainer(ec); + Node* endContainer = range->endContainer(ec); + + Node* pastLastNode = range->pastLastNode(); + for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { + int startOffset = node == startContainer ? range->startOffset(ec) : 0; + int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; + setMarkersActive(node, startOffset, endOffset, active); + } +} + +void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) + return; + + Vector<DocumentMarker>& markers = vectorPair->first; + ASSERT(markers.size() == vectorPair->second.size()); + + bool docDirty = false; + for (size_t i = 0; i != markers.size(); ++i) { + 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 != DocumentMarker::TextMatch) + continue; + + marker.activeMatch = active; + docDirty = true; + } + + // repaint the affected node + if (docDirty && node->renderer()) + node->renderer()->repaint(); +} + +bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes) +{ + if (m_markers.isEmpty()) + return false; + + Node* startContainer = range->startContainer(); + ASSERT(startContainer); + Node* endContainer = range->endContainer(); + ASSERT(endContainer); + + Node* pastLastNode = range->pastLastNode(); + for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { + Vector<DocumentMarker> markers = markersForNode(node); + Vector<DocumentMarker>::const_iterator end = markers.end(); + for (Vector<DocumentMarker>::const_iterator it = markers.begin(); it != end; ++it) { + if (!(markerTypes & it->type)) + continue; + if (node == startContainer && node == endContainer) { + // The range spans only one node. + if (it->endOffset > static_cast<unsigned>(range->startOffset()) && it->startOffset < static_cast<unsigned>(range->endOffset())) + return true; + } else { + if (node == startContainer) { + if (it->endOffset > static_cast<unsigned>(range->startOffset())) + return true; + } else if (node == endContainer) { + if (it->startOffset < static_cast<unsigned>(range->endOffset())) + return true; + } else + return true; + } + } + } + return false; +} + +#ifndef NDEBUG +void DocumentMarkerController::showMarkers() const +{ + fprintf(stderr, "%d nodes have markers:\n", m_markers.size()); + MarkerMap::const_iterator end = m_markers.end(); + for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { + Node* node = nodeIterator->first.get(); + fprintf(stderr, "%p", node); + MarkerMapVectorPair* vectorPair = nodeIterator->second; + Vector<DocumentMarker>& markers = vectorPair->first; + unsigned markerCount = markers.size(); + for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) + fprintf(stderr, " %d:[%d:%d](%d)", markers[markerIndex].type, markers[markerIndex].startOffset, markers[markerIndex].endOffset, markers[markerIndex].activeMatch); + fprintf(stderr, "\n"); + } +} +#endif + +} // namespace WebCore + + +#ifndef NDEBUG +void showDocumentMarkers(const WebCore::DocumentMarkerController* controller) +{ + if (controller) + controller->showMarkers(); +} +#endif diff --git a/Source/WebCore/dom/DocumentMarkerController.h b/Source/WebCore/dom/DocumentMarkerController.h new file mode 100644 index 0000000..83177fc --- /dev/null +++ b/Source/WebCore/dom/DocumentMarkerController.h @@ -0,0 +1,82 @@ +/* + * 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, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) Research In Motion Limited 2010. 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 DocumentMarkerController_h +#define DocumentMarkerController_h + +#include "DocumentMarker.h" +#include <wtf/HashMap.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class IntPoint; +class IntRect; +class Node; +class Range; + +class DocumentMarkerController : public Noncopyable { +public: + ~DocumentMarkerController() { detach(); } + + void detach(); + 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); + bool hasMarkers(Range*, DocumentMarker::MarkerTypes = 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*, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void repaintMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void setRenderedRectForMarker(Node*, const DocumentMarker&, const IntRect&); + void invalidateRenderedRectsForMarkersInRect(const IntRect&); + void shiftMarkers(Node*, unsigned startOffset, int delta, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void setMarkersActive(Range*, bool); + void setMarkersActive(Node*, unsigned startOffset, unsigned endOffset, bool); + + DocumentMarker* markerContainingPoint(const IntPoint&, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + Vector<DocumentMarker> markersForNode(Node*); + Vector<IntRect> renderedRectsForMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + +#ifndef NDEBUG + void showMarkers() const; +#endif + +private: + typedef std::pair<Vector<DocumentMarker>, Vector<IntRect> > MarkerMapVectorPair; + typedef HashMap<RefPtr<Node>, MarkerMapVectorPair*> MarkerMap; + MarkerMap m_markers; + void removeMarkersFromMarkerMapVectorPair(Node*, MarkerMapVectorPair*, DocumentMarker::MarkerType); +}; + +} // namespace WebCore + +#ifndef NDEBUG +void showDocumentMarkers(const WebCore::DocumentMarkerController*); +#endif + +#endif // DocumentMarkerController_h diff --git a/Source/WebCore/dom/DocumentParser.cpp b/Source/WebCore/dom/DocumentParser.cpp new file mode 100644 index 0000000..355b1cf --- /dev/null +++ b/Source/WebCore/dom/DocumentParser.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Google, 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 "DocumentParser.h" + +#include <wtf/Assertions.h> + +namespace WebCore { + +DocumentParser::DocumentParser(Document* document) + : m_state(ParsingState) + , m_documentWasLoadedAsPartOfNavigation(false) + , m_document(document) +{ + ASSERT(document); +} + +DocumentParser::~DocumentParser() +{ + // Document is expected to call detach() before releasing its ref. + // This ASSERT is slightly awkward for parsers with a fragment case + // as there is no Document to release the ref. + ASSERT(!m_document); +} + +void DocumentParser::startParsing() +{ + m_state = ParsingState; +} + +void DocumentParser::prepareToStopParsing() +{ + ASSERT(m_state == ParsingState); + m_state = StoppingState; +} + +void DocumentParser::stopParsing() +{ + m_state = StoppedState; +} + +void DocumentParser::detach() +{ + m_state = DetachedState; + m_document = 0; +} + +void DocumentParser::suspendScheduledTasks() +{ +} + +void DocumentParser::resumeScheduledTasks() +{ +} + +}; + diff --git a/Source/WebCore/dom/DocumentParser.h b/Source/WebCore/dom/DocumentParser.h new file mode 100644 index 0000000..6f78a38 --- /dev/null +++ b/Source/WebCore/dom/DocumentParser.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) + * Copyright (C) 2010 Google, 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 DocumentParser_h +#define DocumentParser_h + +#include <wtf/RefCounted.h> + +namespace WebCore { + +class Document; +class DocumentWriter; +class SegmentedString; +class ScriptableDocumentParser; + +class DocumentParser : public RefCounted<DocumentParser> { +public: + virtual ~DocumentParser(); + + virtual ScriptableDocumentParser* asScriptableDocumentParser() { return 0; } + + // http://www.whatwg.org/specs/web-apps/current-work/#insertion-point + virtual bool hasInsertionPoint() { return true; } + + // insert is used by document.write + virtual void insert(const SegmentedString&) = 0; + + // appendBytes is used by DocumentWriter (the loader) + virtual void appendBytes(DocumentWriter*, const char* bytes, int length, bool flush) = 0; + + // FIXME: append() should be private, but DocumentWriter::replaceDocument + // uses it for now. + virtual void append(const SegmentedString&) = 0; + + virtual void finish() = 0; + virtual bool finishWasCalled() = 0; + + // FIXME: processingData() is only used by DocumentLoader::isLoadingInAPISense + // and is very unclear as to what it actually means. The LegacyHTMLDocumentParser + // used to implement it. + virtual bool processingData() const { return false; } + + // document() will return 0 after detach() is called. + Document* document() const { ASSERT(m_document); return m_document; } + + bool isParsing() const { return m_state == ParsingState; } + bool isStopping() const { return m_state == StoppingState; } + bool isStopped() const { return m_state >= StoppedState; } + bool isDetached() const { return m_state == DetachedState; } + + // FIXME: Is this necessary? Does XMLDocumentParserLibxml2 really need to set this? + virtual void startParsing(); + + // prepareToStop() is used when the EOF token is encountered and parsing is to be + // stopped normally. + virtual void prepareToStopParsing(); + + // stopParsing() is used when a load is canceled/stopped. + // stopParsing() is currently different from detach(), but shouldn't be. + // It should NOT be ok to call any methods on DocumentParser after either + // detach() or stopParsing() but right now only detach() will ASSERT. + virtual void stopParsing(); + + // Document is expected to detach the parser before releasing its ref. + // After detach, m_document is cleared. The parser will unwind its + // callstacks, but not produce any more nodes. + // It is impossible for the parser to touch the rest of WebCore after + // detach is called. + virtual void detach(); + + void setDocumentWasLoadedAsPartOfNavigation() { m_documentWasLoadedAsPartOfNavigation = true; } + bool documentWasLoadedAsPartOfNavigation() const { return m_documentWasLoadedAsPartOfNavigation; } + + // FIXME: The names are not very accurate :( + virtual void suspendScheduledTasks(); + virtual void resumeScheduledTasks(); + +protected: + DocumentParser(Document*); + +private: + enum ParserState { + ParsingState, + StoppingState, + StoppedState, + DetachedState + }; + ParserState m_state; + bool m_documentWasLoadedAsPartOfNavigation; + + // Every DocumentParser needs a pointer back to the document. + // m_document will be 0 after the parser is stopped. + Document* m_document; +}; + +} // namespace WebCore + +#endif // DocumentParser_h diff --git a/Source/WebCore/dom/DocumentTiming.h b/Source/WebCore/dom/DocumentTiming.h new file mode 100644 index 0000000..89e2fb9 --- /dev/null +++ b/Source/WebCore/dom/DocumentTiming.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 Google, 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 GOOGLE 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 GOOGLE 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 DocumentTiming_h +#define DocumentTiming_h + +namespace WebCore { + +struct DocumentTiming { + DocumentTiming() + : domLoading(0.0) + , domInteractive(0.0) + , domContentLoadedEventStart(0.0) + , domContentLoadedEventEnd(0.0) + , domComplete(0.0) + { + } + + double domLoading; + double domInteractive; + double domContentLoadedEventStart; + double domContentLoadedEventEnd; + double domComplete; +}; + +} + +#endif diff --git a/Source/WebCore/dom/DocumentType.cpp b/Source/WebCore/dom/DocumentType.cpp new file mode 100644 index 0000000..ed4b96c --- /dev/null +++ b/Source/WebCore/dom/DocumentType.cpp @@ -0,0 +1,78 @@ +/* + * 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, 2009 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 "Document.h" +#include "NamedNodeMap.h" + +namespace WebCore { + +DocumentType::DocumentType(Document* document, const String& name, const String& publicId, const String& systemId) + : Node(document, CreateOther) + , m_name(name) + , m_publicId(publicId) + , m_systemId(systemId) +{ +} + +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 create(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(parentNode() && parentNode()->isDocumentNode()); + if (parentNode() && parentNode()->isDocumentNode()) { + Document* doc = static_cast<Document*>(parentNode()); + if (!doc->doctype()) + doc->setDocType(this); + } + Node::insertedIntoDocument(); +} + +void DocumentType::removedFromDocument() +{ + if (document() && document()->doctype() == this) + document()->setDocType(0); + Node::removedFromDocument(); +} + +} diff --git a/Source/WebCore/dom/DocumentType.h b/Source/WebCore/dom/DocumentType.h new file mode 100644 index 0000000..4f89d01 --- /dev/null +++ b/Source/WebCore/dom/DocumentType.h @@ -0,0 +1,70 @@ +/* + * 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, 2009 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 DocumentType_h +#define DocumentType_h + +#include "Node.h" + +namespace WebCore { + +class NamedNodeMap; + +class DocumentType : public Node { +public: + static PassRefPtr<DocumentType> create(Document* document, const String& name, const String& publicId, const String& systemId) + { + return adoptRef(new DocumentType(document, name, publicId, systemId)); + } + + NamedNodeMap* entities() const { return m_entities.get(); } + NamedNodeMap* notations() const { return m_notations.get(); } + + const String& name() const { return m_name; } + const String& publicId() const { return m_publicId; } + const String& systemId() const { return m_systemId; } + const String& internalSubset() const { return m_subset; } + +private: + DocumentType(Document*, const String& name, const String& publicId, const String& systemId); + + virtual KURL baseURI() const; + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + RefPtr<NamedNodeMap> m_entities; + RefPtr<NamedNodeMap> m_notations; + + String m_name; + String m_publicId; + String m_systemId; + String m_subset; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/dom/DocumentType.idl b/Source/WebCore/dom/DocumentType.idl new file mode 100644 index 0000000..7992dc5 --- /dev/null +++ b/Source/WebCore/dom/DocumentType.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 core { + + interface [ + GenerateNativeConverter + ] 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/Source/WebCore/dom/DynamicNodeList.cpp b/Source/WebCore/dom/DynamicNodeList.cpp new file mode 100644 index 0000000..3538b60 --- /dev/null +++ b/Source/WebCore/dom/DynamicNodeList.cpp @@ -0,0 +1,174 @@ +/* + * 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, 2008, 2010 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) + : m_rootNode(rootNode) + , m_caches(Caches::create()) + , m_ownsCaches(true) +{ + m_rootNode->registerDynamicNodeList(this); +} + +DynamicNodeList::DynamicNodeList(PassRefPtr<Node> rootNode, DynamicNodeList::Caches* caches) + : m_rootNode(rootNode) + , m_caches(caches) + , m_ownsCaches(false) +{ + m_rootNode->registerDynamicNodeList(this); +} + +DynamicNodeList::~DynamicNodeList() +{ + m_rootNode->unregisterDynamicNodeList(this); +} + +unsigned DynamicNodeList::length() const +{ + if (m_caches->isLengthCacheValid) + return m_caches->cachedLength; + + unsigned length = 0; + + for (Node* n = m_rootNode->firstChild(); n; n = n->traverseNextNode(m_rootNode.get())) + length += n->isElementNode() && nodeMatches(static_cast<Element*>(n)); + + m_caches->cachedLength = length; + m_caches->isLengthCacheValid = true; + + return length; +} + +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() && nodeMatches(static_cast<Element*>(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() && nodeMatches(static_cast<Element*>(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()) { + Element* node = m_rootNode->document()->getElementById(elementId); + if (node && nodeMatches(node)) { + for (ContainerNode* p = node->parentNode(); p; p = p->parentNode()) { + if (p == m_rootNode) + return node; + } + } + if (!node) + return 0; + // In the case of multiple nodes with the same name, just fall through. + } + + unsigned length = this->length(); + for (unsigned i = 0; i < length; i++) { + Node* node = item(i); + // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution. + if (node->hasID() && static_cast<Element*>(node)->idForStyleResolution() == elementId) + return node; + } + + return 0; +} + +void DynamicNodeList::invalidateCache() +{ + // This should only be called for node lists that own their own caches. + ASSERT(m_ownsCaches); + m_caches->reset(); +} + +DynamicNodeList::Caches::Caches() + : lastItem(0) + , isLengthCacheValid(false) + , isItemCacheValid(false) +{ +} + +PassRefPtr<DynamicNodeList::Caches> DynamicNodeList::Caches::create() +{ + return adoptRef(new Caches()); +} + +void DynamicNodeList::Caches::reset() +{ + lastItem = 0; + isLengthCacheValid = false; + isItemCacheValid = false; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/DynamicNodeList.h b/Source/WebCore/dom/DynamicNodeList.h new file mode 100644 index 0000000..db133b7 --- /dev/null +++ b/Source/WebCore/dom/DynamicNodeList.h @@ -0,0 +1,81 @@ +/* + * 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 Element; + class Node; + + class DynamicNodeList : public NodeList { + public: + struct Caches : RefCounted<Caches> { + static PassRefPtr<Caches> create(); + void reset(); + + unsigned cachedLength; + Node* lastItem; + unsigned lastItemOffset; + bool isLengthCacheValid : 1; + bool isItemCacheValid : 1; + protected: + Caches(); + }; + + virtual ~DynamicNodeList(); + + bool hasOwnCaches() const { return m_ownsCaches; } + + // 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) + void invalidateCache(); + + protected: + DynamicNodeList(PassRefPtr<Node> rootNode); + DynamicNodeList(PassRefPtr<Node> rootNode, Caches*); + + virtual bool nodeMatches(Element*) const = 0; + + RefPtr<Node> m_rootNode; + mutable RefPtr<Caches> m_caches; + bool m_ownsCaches; + + 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/Source/WebCore/dom/EditingText.cpp b/Source/WebCore/dom/EditingText.cpp new file mode 100644 index 0000000..e412ad6 --- /dev/null +++ b/Source/WebCore/dom/EditingText.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003, 2009 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 "EditingText.h" + +#include "Document.h" + +// FIXME: Does this really require a class? Perhaps instead any text node +// inside an editable element could have the "always create a renderer" behavior. + +namespace WebCore { + +inline EditingText::EditingText(Document* document, const String& data) + : Text(document, data) +{ +} + +PassRefPtr<EditingText> EditingText::create(Document* document, const String& data) +{ + return adoptRef(new EditingText(document, data)); +} + +bool EditingText::rendererIsNeeded(RenderStyle*) +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/EditingText.h b/Source/WebCore/dom/EditingText.h new file mode 100644 index 0000000..08223c2 --- /dev/null +++ b/Source/WebCore/dom/EditingText.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2003, 2009 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 EditingText_h +#define EditingText_h + +#include "Text.h" + +namespace WebCore { + +class EditingText : public Text { +public: + static PassRefPtr<EditingText> create(Document*, const String&); + +private: + virtual bool rendererIsNeeded(RenderStyle *); + + EditingText(Document*, const String&); +}; + +} // namespace WebCore + +#endif // EditingText_h diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp new file mode 100644 index 0000000..4c10a32 --- /dev/null +++ b/Source/WebCore/dom/Element.cpp @@ -0,0 +1,1836 @@ +/* + * 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, 2009, 2010 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 "AXObjectCache.h" +#include "Attr.h" +#include "CSSParser.h" +#include "CSSSelectorList.h" +#include "CSSStyleSelector.h" +#include "ClassList.h" +#include "ClientRect.h" +#include "ClientRectList.h" +#include "DOMTokenList.h" +#include "DatasetDOMStringMap.h" +#include "Document.h" +#include "DocumentFragment.h" +#include "ElementRareData.h" +#include "ExceptionCode.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "InspectorInstrumentation.h" +#include "NodeList.h" +#include "NodeRenderStyle.h" +#include "Page.h" +#include "RenderLayer.h" +#include "RenderView.h" +#include "RenderWidget.h" +#include "Settings.h" +#include "TextIterator.h" +#include "XMLNames.h" +#include <wtf/text/CString.h> + +#if ENABLE(SVG) +#include "SVGElement.h" +#include "SVGNames.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; +using namespace XMLNames; + +PassRefPtr<Element> Element::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new Element(tagName, document, CreateElement)); +} + +Element::~Element() +{ + if (m_attributeMap) + m_attributeMap->detachFromElement(); +} + +inline ElementRareData* Element::rareData() const +{ + ASSERT(hasRareData()); + return static_cast<ElementRareData*>(NodeRareData::rareDataFromMap(this)); +} + +inline ElementRareData* Element::ensureRareData() +{ + return static_cast<ElementRareData*>(Node::ensureRareData()); +} + +NodeRareData* Element::createRareData() +{ + return new ElementRareData; +} + +DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(Element, blur); +DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(Element, error); +DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(Element, focus); +DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(Element, load); + +PassRefPtr<DocumentFragment> Element::deprecatedCreateContextualFragment(const String& markup, FragmentScriptingPermission scriptingPermission) +{ + RefPtr<DocumentFragment> fragment = document()->createDocumentFragment(); + + if (document()->isHTMLDocument()) + fragment->parseHTML(markup, this, scriptingPermission); + else { + if (!fragment->parseXML(markup, this, scriptingPermission)) + // FIXME: We should propagate a syntax error exception out here. + return 0; + } + + // Exceptions are ignored because none ought to happen here. + ExceptionCode ignoredExceptionCode; + + // We need to pop <html> and <body> elements and remove <head> to + // accommodate folks passing complete HTML documents to make the + // child of an element. + + RefPtr<Node> nextNode; + for (RefPtr<Node> node = fragment->firstChild(); node; node = nextNode) { + nextNode = node->nextSibling(); + if (node->hasTagName(htmlTag) || node->hasTagName(bodyTag)) { + HTMLElement* element = static_cast<HTMLElement*>(node.get()); + Node* firstChild = element->firstChild(); + if (firstChild) + nextNode = firstChild; + RefPtr<Node> nextChild; + for (RefPtr<Node> child = firstChild; child; child = nextChild) { + nextChild = child->nextSibling(); + element->removeChild(child.get(), ignoredExceptionCode); + ASSERT(!ignoredExceptionCode); + fragment->insertBefore(child, element, ignoredExceptionCode); + ASSERT(!ignoredExceptionCode); + } + fragment->removeChild(element, ignoredExceptionCode); + ASSERT(!ignoredExceptionCode); + } else if (node->hasTagName(headTag)) { + fragment->removeChild(node.get(), ignoredExceptionCode); + ASSERT(!ignoredExceptionCode); + } + } + return fragment.release(); +} + +PassRefPtr<Node> Element::cloneNode(bool deep) +{ + return deep ? cloneElementWithChildren() : cloneElementWithoutChildren(); +} + +PassRefPtr<Element> Element::cloneElementWithChildren() +{ + RefPtr<Element> clone = cloneElementWithoutChildren(); + cloneChildNodes(clone.get()); + return clone.release(); +} + +PassRefPtr<Element> Element::cloneElementWithoutChildren() +{ + RefPtr<Element> clone = cloneElementWithoutAttributesAndChildren(); + // This will catch HTML elements in the wrong namespace that are not correctly copied. + // This is a sanity check as HTML overloads some of the DOM methods. + ASSERT(isHTMLElement() == clone->isHTMLElement()); + + // Call attributes(true) to force attribute synchronization to occur for SVG and style attributes. + if (NamedNodeMap* attributeMap = attributes(true)) + clone->attributes()->setAttributes(*attributeMap); + + clone->copyNonAttributeProperties(this); + + return clone.release(); +} + +PassRefPtr<Element> Element::cloneElementWithoutAttributesAndChildren() const +{ + return document()->createElement(tagQName(), false); +} + +void Element::removeAttribute(const QualifiedName& name, ExceptionCode& ec) +{ + if (m_attributeMap) { + ec = 0; + m_attributeMap->removeNamedItem(name, ec); + if (ec == NOT_FOUND_ERR) + ec = 0; + } +} + +void Element::setAttribute(const QualifiedName& name, const AtomicString& value) +{ + ExceptionCode ec; + setAttribute(name, value, ec); +} + +void Element::setCStringAttribute(const QualifiedName& name, const char* cStringValue) +{ + ExceptionCode ec; + setAttribute(name, AtomicString(cStringValue), ec); +} + +void Element::setBooleanAttribute(const QualifiedName& name, bool b) +{ + if (b) + setAttribute(name, emptyAtom); + else { + ExceptionCode ex; + removeAttribute(name, ex); + } +} + +Node::NodeType Element::nodeType() const +{ + return ELEMENT_NODE; +} + +bool Element::hasAttribute(const QualifiedName& name) const +{ + return hasAttributeNS(name.namespaceURI(), name.localName()); +} + +const AtomicString& Element::getAttribute(const QualifiedName& name) const +{ + if (UNLIKELY(name == styleAttr) && !isStyleAttributeValid()) + updateStyleAttribute(); + +#if ENABLE(SVG) + if (UNLIKELY(!areSVGAttributesValid())) + updateAnimatedSVGAttribute(name); +#endif + + return fastGetAttribute(name); +} + +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, false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways); + else + renderer()->enclosingLayer()->scrollRectToVisible(bounds, false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignBottomAlways); + } +} + +void Element::scrollIntoViewIfNeeded(bool centerIfNeeded) +{ + document()->updateLayoutIgnorePendingStylesheets(); + IntRect bounds = getRect(); + if (renderer()) { + if (centerIfNeeded) + renderer()->enclosingLayer()->scrollRectToVisible(bounds, false, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded); + else + renderer()->enclosingLayer()->scrollRectToVisible(bounds, false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); + } +} + +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; + } + toRenderBox(rend)->layer()->scroll(direction, granularity, units); + } + } +} + +void Element::scrollByLines(int lines) +{ + scrollByUnits(lines, ScrollByLine); +} + +void Element::scrollByPages(int pages) +{ + scrollByUnits(pages, ScrollByPage); +} + +static float localZoomForRenderer(RenderObject* renderer) +{ + // FIXME: This does the wrong thing if two opposing zooms are in effect and canceled each + // other out, but the alternative is that we'd have to crawl up the whole render tree every + // time (or store an additional bit in the RenderStyle to indicate that a zoom was specified). + float zoomFactor = 1; + if (renderer->style()->effectiveZoom() != 1) { + // Need to find the nearest enclosing RenderObject that set up + // a differing zoom, and then we divide our result by it to eliminate the zoom. + RenderObject* prev = renderer; + for (RenderObject* curr = prev->parent(); curr; curr = curr->parent()) { + if (curr->style()->effectiveZoom() != prev->style()->effectiveZoom()) { + zoomFactor = prev->style()->zoom(); + break; + } + prev = curr; + } + if (prev->isRenderView()) + zoomFactor = prev->style()->zoom(); + } + return zoomFactor; +} + +static int adjustForLocalZoom(int value, RenderObject* renderer) +{ + float zoomFactor = localZoomForRenderer(renderer); + if (zoomFactor == 1) + return value; + // Needed because computeLengthInt truncates (rather than rounds) when scaling up. + if (zoomFactor > 1) + value++; + return static_cast<int>(value / zoomFactor); +} + +int Element::offsetLeft() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBoxModelObject* rend = renderBoxModelObject()) + return adjustForLocalZoom(rend->offsetLeft(), rend); + return 0; +} + +int Element::offsetTop() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBoxModelObject* rend = renderBoxModelObject()) + return adjustForLocalZoom(rend->offsetTop(), rend); + return 0; +} + +int Element::offsetWidth() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBoxModelObject* rend = renderBoxModelObject()) + return adjustForAbsoluteZoom(rend->offsetWidth(), rend); + return 0; +} + +int Element::offsetHeight() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBoxModelObject* rend = renderBoxModelObject()) + return adjustForAbsoluteZoom(rend->offsetHeight(), rend); + return 0; +} + +Element* Element::offsetParent() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderObject* rend = renderer()) + if (RenderObject* offsetParent = rend->offsetParent()) + return static_cast<Element*>(offsetParent->node()); + return 0; +} + +int Element::clientLeft() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + if (RenderBox* rend = renderBox()) + return adjustForAbsoluteZoom(rend->clientLeft(), rend); + return 0; +} + +int Element::clientTop() +{ + document()->updateLayoutIgnorePendingStylesheets(); + + if (RenderBox* rend = renderBox()) + return adjustForAbsoluteZoom(rend->clientTop(), rend); + 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 inQuirksMode = document()->inQuirksMode(); + if ((!inQuirksMode && document()->documentElement() == this) || + (inQuirksMode && isHTMLElement() && document()->body() == this)) { + if (FrameView* view = document()->view()) { + if (RenderView* renderView = document()->renderView()) + return adjustForAbsoluteZoom(view->layoutWidth(), renderView); + } + } + + if (RenderBox* rend = renderBox()) + return adjustForAbsoluteZoom(rend->clientWidth(), rend); + 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 inQuirksMode = document()->inQuirksMode(); + + if ((!inQuirksMode && document()->documentElement() == this) || + (inQuirksMode && isHTMLElement() && document()->body() == this)) { + if (FrameView* view = document()->view()) { + if (RenderView* renderView = document()->renderView()) + return adjustForAbsoluteZoom(view->layoutHeight(), renderView); + } + } + + if (RenderBox* rend = renderBox()) + return adjustForAbsoluteZoom(rend->clientHeight(), rend); + return 0; +} + +int Element::scrollLeft() const +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBox* rend = renderBox()) + return adjustForAbsoluteZoom(rend->scrollLeft(), rend); + return 0; +} + +int Element::scrollTop() const +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBox* rend = renderBox()) + return adjustForAbsoluteZoom(rend->scrollTop(), rend); + return 0; +} + +void Element::setScrollLeft(int newLeft) +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBox* rend = renderBox()) + rend->setScrollLeft(static_cast<int>(newLeft * rend->style()->effectiveZoom())); +} + +void Element::setScrollTop(int newTop) +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBox* rend = renderBox()) + rend->setScrollTop(static_cast<int>(newTop * rend->style()->effectiveZoom())); +} + +int Element::scrollWidth() const +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBox* rend = renderBox()) + return adjustForAbsoluteZoom(rend->scrollWidth(), rend); + return 0; +} + +int Element::scrollHeight() const +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (RenderBox* rend = renderBox()) + return adjustForAbsoluteZoom(rend->scrollHeight(), rend); + return 0; +} + +IntRect Element::boundsInWindowSpace() const +{ + document()->updateLayoutIgnorePendingStylesheets(); + + FrameView* view = document()->view(); + if (!view) + return IntRect(); + + Vector<FloatQuad> quads; +#if ENABLE(SVG) + if (isSVGElement() && renderer()) { + // Get the bounding rectangle from the SVG model. + const SVGElement* svgElement = static_cast<const SVGElement*>(this); + FloatRect localRect; + if (svgElement->boundingBox(localRect)) + quads.append(renderer()->localToAbsoluteQuad(localRect)); + } else +#endif + { + // Get the bounding rectangle from the box model. + if (renderBoxModelObject()) + renderBoxModelObject()->absoluteQuads(quads); + } + + if (quads.isEmpty()) + return IntRect(); + + IntRect result = quads[0].enclosingBoundingBox(); + for (size_t i = 1; i < quads.size(); ++i) + result.unite(quads[i].enclosingBoundingBox()); + + result = view->contentsToWindow(result); + return result; +} + +PassRefPtr<ClientRectList> Element::getClientRects() const +{ + document()->updateLayoutIgnorePendingStylesheets(); + + RenderBoxModelObject* renderBoxModelObject = this->renderBoxModelObject(); + if (!renderBoxModelObject) + return ClientRectList::create(); + + // FIXME: Handle SVG elements. + // FIXME: Handle table/inline-table with a caption. + + Vector<FloatQuad> quads; + renderBoxModelObject->absoluteQuads(quads); + + if (FrameView* view = document()->view()) { + IntRect visibleContentRect = view->visibleContentRect(); + for (size_t i = 0; i < quads.size(); ++i) { + quads[i].move(-visibleContentRect.x(), -visibleContentRect.y()); + adjustFloatQuadForAbsoluteZoom(quads[i], renderBoxModelObject); + } + } + + return ClientRectList::create(quads); +} + +PassRefPtr<ClientRect> Element::getBoundingClientRect() const +{ + document()->updateLayoutIgnorePendingStylesheets(); + + Vector<FloatQuad> quads; +#if ENABLE(SVG) + if (isSVGElement() && renderer()) { + // Get the bounding rectangle from the SVG model. + const SVGElement* svgElement = static_cast<const SVGElement*>(this); + FloatRect localRect; + if (svgElement->boundingBox(localRect)) + quads.append(renderer()->localToAbsoluteQuad(localRect)); + } else +#endif + { + // Get the bounding rectangle from the box model. + if (renderBoxModelObject()) + renderBoxModelObject()->absoluteQuads(quads); + } + + if (quads.isEmpty()) + return ClientRect::create(); + + FloatRect result = quads[0].boundingBox(); + for (size_t i = 1; i < quads.size(); ++i) + result.unite(quads[i].boundingBox()); + + if (FrameView* view = document()->view()) { + IntRect visibleContentRect = view->visibleContentRect(); + result.move(-visibleContentRect.x(), -visibleContentRect.y()); + } + + adjustFloatRectForAbsoluteZoom(result, renderer()); + return ClientRect::create(result); +} + +IntRect Element::screenRect() const +{ + if (!renderer()) + return IntRect(); + return renderer()->view()->frameView()->contentsToScreen(renderer()->absoluteBoundingBoxRect()); +} + +static inline bool shouldIgnoreAttributeCase(const Element* e) +{ + return e && e->document()->isHTMLDocument() && e->isHTMLElement(); +} + +const AtomicString& Element::getAttribute(const String& name) const +{ + bool ignoreCase = shouldIgnoreAttributeCase(this); + + // Update the 'style' attribute if it's invalid and being requested: + if (!isStyleAttributeValid() && equalPossiblyIgnoringCase(name, styleAttr.localName(), ignoreCase)) + updateStyleAttribute(); + +#if ENABLE(SVG) + if (!areSVGAttributesValid()) { + // We're not passing a namespace argument on purpose. SVGNames::*Attr are defined w/o namespaces as well. + updateAnimatedSVGAttribute(QualifiedName(nullAtom, name, nullAtom)); + } +#endif + + if (m_attributeMap) { + if (Attribute* attribute = m_attributeMap->getAttributeItem(name, ignoreCase)) + return attribute->value(); + } + + return nullAtom; +} + +const AtomicString& Element::getAttributeNS(const String& namespaceURI, const String& localName) const +{ + return getAttribute(QualifiedName(nullAtom, localName, namespaceURI)); +} + +void Element::setAttribute(const AtomicString& name, const AtomicString& value, ExceptionCode& ec) +{ + if (!Document::isValidName(name)) { + ec = INVALID_CHARACTER_ERR; + return; + } + +#if ENABLE(INSPECTOR) + if (!isSynchronizingStyleAttribute()) + InspectorInstrumentation::willModifyDOMAttr(document(), this); +#endif + + const AtomicString& localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; + + // Allocate attribute map if necessary. + Attribute* old = attributes(false)->getAttributeItem(localName, false); + + document()->incDOMTreeVersion(); + + // FIXME: This check is probably not correct for the case where the document has an id attribute + // with a non-null namespace, because it will return true if the local name happens to match + // but the namespace does not. + if (localName == document()->idAttributeName().localName()) + updateId(old ? old->value() : nullAtom, value); + + if (old && value.isNull()) + m_attributeMap->removeAttribute(old->name()); + else if (!old && !value.isNull()) + m_attributeMap->addAttribute(createAttribute(QualifiedName(nullAtom, localName, nullAtom), value)); + else if (old && !value.isNull()) { + if (Attr* attrNode = old->attr()) + attrNode->setValue(value); + else + old->setValue(value); + attributeChanged(old); + } + +#if ENABLE(INSPECTOR) + if (!isSynchronizingStyleAttribute()) + InspectorInstrumentation::didModifyDOMAttr(document(), this); +#endif +} + +void Element::setAttribute(const QualifiedName& name, const AtomicString& value, ExceptionCode&) +{ +#if ENABLE(INSPECTOR) + if (!isSynchronizingStyleAttribute()) + InspectorInstrumentation::willModifyDOMAttr(document(), this); +#endif + + document()->incDOMTreeVersion(); + + // Allocate attribute map if necessary. + Attribute* old = attributes(false)->getAttributeItem(name); + + if (isIdAttributeName(name)) + updateId(old ? old->value() : nullAtom, value); + + if (old && value.isNull()) + m_attributeMap->removeAttribute(name); + else if (!old && !value.isNull()) + m_attributeMap->addAttribute(createAttribute(name, value)); + else if (old) { + if (Attr* attrNode = old->attr()) + attrNode->setValue(value); + else + old->setValue(value); + attributeChanged(old); + } + +#if ENABLE(INSPECTOR) + if (!isSynchronizingStyleAttribute()) + InspectorInstrumentation::didModifyDOMAttr(document(), this); +#endif +} + +PassRefPtr<Attribute> Element::createAttribute(const QualifiedName& name, const AtomicString& value) +{ + return Attribute::create(name, value); +} + +void Element::attributeChanged(Attribute* attr, bool) +{ + recalcStyleIfNeededAfterAttributeChanged(attr); + updateAfterAttributeChanged(attr); +} + +void Element::updateAfterAttributeChanged(Attribute* attr) +{ + if (!AXObjectCache::accessibilityEnabled()) + return; + + const QualifiedName& attrName = attr->name(); + if (attrName == aria_activedescendantAttr) { + // any change to aria-activedescendant attribute triggers accessibility focus change, but document focus remains intact + document()->axObjectCache()->handleActiveDescendantChanged(renderer()); + } else if (attrName == roleAttr) { + // the role attribute can change at any time, and the AccessibilityObject must pick up these changes + document()->axObjectCache()->handleAriaRoleChanged(renderer()); + } else if (attrName == aria_valuenowAttr) { + // If the valuenow attribute changes, AX clients need to be notified. + document()->axObjectCache()->postNotification(renderer(), AXObjectCache::AXValueChanged, true); + } else if (attrName == aria_labelAttr || attrName == aria_labeledbyAttr || attrName == altAttr || attrName == titleAttr) { + // If the content of an element changes due to an attribute change, notify accessibility. + document()->axObjectCache()->contentChanged(renderer()); + } else if (attrName == aria_selectedAttr) + document()->axObjectCache()->selectedChildrenChanged(renderer()); + else if (attrName == aria_expandedAttr) + document()->axObjectCache()->handleAriaExpandedChange(renderer()); + else if (attrName == aria_hiddenAttr) + document()->axObjectCache()->childrenChanged(renderer()); + else if (attrName == aria_invalidAttr) + document()->axObjectCache()->postNotification(renderer(), AXObjectCache::AXInvalidStatusChanged, true); +} + +void Element::recalcStyleIfNeededAfterAttributeChanged(Attribute* attr) +{ + if (document()->attached() && document()->styleSelector()->hasSelectorForAttribute(attr->name().localName())) + setNeedsStyleRecalc(); +} + +// Returns true is the given attribute is an event handler. +// We consider an event handler any attribute that begins with "on". +// It is a simple solution that has the advantage of not requiring any +// code or configuration change if a new event handler is defined. + +static bool isEventHandlerAttribute(const QualifiedName& name) +{ + return name.namespaceURI().isNull() && name.localName().startsWith("on"); +} + +static bool isAttributeToRemove(const QualifiedName& name, const AtomicString& value) +{ + return (name.localName().endsWith(hrefAttr.localName()) || name == srcAttr || name == actionAttr) && protocolIsJavaScript(stripLeadingAndTrailingHTMLSpaces(value)); +} + +void Element::setAttributeMap(PassRefPtr<NamedNodeMap> list, FragmentScriptingPermission scriptingPermission) +{ + document()->incDOMTreeVersion(); + + // If setting the whole map changes the id attribute, we need to call updateId. + + const QualifiedName& idName = document()->idAttributeName(); + Attribute* oldId = m_attributeMap ? m_attributeMap->getAttributeItem(idName) : 0; + Attribute* newId = list ? list->getAttributeItem(idName) : 0; + + if (oldId || newId) + updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom); + + if (m_attributeMap) + m_attributeMap->m_element = 0; + + m_attributeMap = list; + + if (m_attributeMap) { + m_attributeMap->m_element = this; + // If the element is created as result of a paste or drag-n-drop operation + // we want to remove all the script and event handlers. + if (scriptingPermission == FragmentScriptingNotAllowed) { + unsigned i = 0; + while (i < m_attributeMap->length()) { + const QualifiedName& attributeName = m_attributeMap->m_attributes[i]->name(); + if (isEventHandlerAttribute(attributeName)) { + m_attributeMap->m_attributes.remove(i); + continue; + } + + if (isAttributeToRemove(attributeName, m_attributeMap->m_attributes[i]->value())) + m_attributeMap->m_attributes[i]->setValue(nullAtom); + i++; + } + } + // Store the set of attributes that changed on the stack in case + // attributeChanged mutates m_attributeMap. + Vector<RefPtr<Attribute> > attributes; + m_attributeMap->copyAttributesToVector(attributes); + for (Vector<RefPtr<Attribute> >::iterator iter = attributes.begin(); iter != attributes.end(); ++iter) + attributeChanged(iter->get()); + // FIXME: What about attributes that were in the old map that are not in the new map? + } +} + +bool Element::hasAttributes() const +{ + if (!isStyleAttributeValid()) + updateStyleAttribute(); + +#if ENABLE(SVG) + if (!areSVGAttributesValid()) + updateAnimatedSVGAttribute(anyQName()); +#endif + + return m_attributeMap && m_attributeMap->length(); +} + +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.isEmpty() ? AtomicString() : prefix); +} + +KURL Element::baseURI() const +{ + const AtomicString& baseAttribute = getAttribute(baseAttr); + KURL base(KURL(), baseAttribute); + if (!base.protocol().isEmpty()) + return base; + + ContainerNode* parent = parentNode(); + if (!parent) + return base; + + const KURL& parentBase = parent->baseURI(); + if (parentBase.isNull()) + return base; + + return KURL(parentBase, baseAttribute); +} + +void Element::createAttributeMap() const +{ + m_attributeMap = NamedNodeMap::create(const_cast<Element*>(this)); +} + +bool Element::isURLAttribute(Attribute*) const +{ + return false; +} + +const QualifiedName& Element::imageSourceAttributeName() const +{ + return srcAttr; +} + +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 (Node* shadow = shadowRoot()) + shadow->insertedIntoDocument(); + + if (hasID()) { + if (m_attributeMap) { + Attribute* idItem = m_attributeMap->getAttributeItem(document()->idAttributeName()); + if (idItem && !idItem->isNull()) + updateId(nullAtom, idItem->value()); + } + } +} + +void Element::removedFromDocument() +{ + if (hasID()) { + if (m_attributeMap) { + Attribute* idItem = m_attributeMap->getAttributeItem(document()->idAttributeName()); + if (idItem && !idItem->isNull()) + updateId(idItem->value(), nullAtom); + } + } + + ContainerNode::removedFromDocument(); + if (Node* shadow = shadowRoot()) + shadow->removedFromDocument(); +} + +void Element::insertedIntoTree(bool deep) +{ + ContainerNode::insertedIntoTree(deep); + if (!deep) + return; + if (Node* shadow = shadowRoot()) + shadow->insertedIntoTree(true); +} + +void Element::removedFromTree(bool deep) +{ + ContainerNode::removedFromTree(deep); + if (!deep) + return; + if (Node* shadow = shadowRoot()) + shadow->removedFromTree(true); +} + +void Element::attach() +{ + suspendPostAttachCallbacks(); + RenderWidget::suspendWidgetHierarchyUpdates(); + + createRendererIfNeeded(); + ContainerNode::attach(); + if (Node* shadow = shadowRoot()) + shadow->attach(); + if (hasRareData()) { + ElementRareData* data = rareData(); + if (data->needsFocusAppearanceUpdateSoonAfterAttach()) { + if (isFocusable() && document()->focusedNode() == this) + document()->updateFocusAppearanceSoon(false /* don't restore selection */); + data->setNeedsFocusAppearanceUpdateSoonAfterAttach(false); + } + } + + RenderWidget::resumeWidgetHierarchyUpdates(); + resumePostAttachCallbacks(); +} + +void Element::detach() +{ + RenderWidget::suspendWidgetHierarchyUpdates(); + + cancelFocusAppearanceUpdate(); + if (hasRareData()) + rareData()->resetComputedStyle(); + ContainerNode::detach(); + if (Node* shadow = shadowRoot()) + shadow->detach(); + + RenderWidget::resumeWidgetHierarchyUpdates(); +} + +bool Element::pseudoStyleCacheIsInvalid(const RenderStyle* currentStyle, RenderStyle* newStyle) +{ + ASSERT(currentStyle == renderStyle()); + + if (!renderer() || !currentStyle) + return false; + + const PseudoStyleCache* pseudoStyleCache = currentStyle->cachedPseudoStyles(); + if (!pseudoStyleCache) + return false; + + size_t cacheSize = pseudoStyleCache->size(); + for (size_t i = 0; i < cacheSize; ++i) { + RefPtr<RenderStyle> newPseudoStyle; + PseudoId pseudoId = pseudoStyleCache->at(i)->styleType(); + if (pseudoId == VISITED_LINK) { + newPseudoStyle = newStyle->getCachedPseudoStyle(VISITED_LINK); // This pseudo-style was aggressively computed already when we first called styleForElement on the new style. + if (!newPseudoStyle || *newPseudoStyle != *pseudoStyleCache->at(i)) + return true; + } else if (pseudoId == FIRST_LINE || pseudoId == FIRST_LINE_INHERITED) + newPseudoStyle = renderer()->uncachedFirstLineStyle(newStyle); + else + newPseudoStyle = renderer()->getUncachedPseudoStyle(pseudoId, newStyle, newStyle); + if (!newPseudoStyle) + return true; + if (*newPseudoStyle != *pseudoStyleCache->at(i)) { + if (pseudoId < FIRST_INTERNAL_PSEUDOID) + newStyle->setHasPseudoStyle(pseudoId); + newStyle->addCachedPseudoStyle(newPseudoStyle); + if (pseudoId == FIRST_LINE || pseudoId == FIRST_LINE_INHERITED) { + // FIXME: We should do an actual diff to determine whether a repaint vs. layout + // is needed, but for now just assume a layout will be required. The diff code + // in RenderObject::setStyle would need to be factored out so that it could be reused. + renderer()->setNeedsLayoutAndPrefWidthsRecalc(); + } + return true; + } + } + return false; +} + +#ifdef ANDROID_STYLE_VERSION +static bool displayDiff(const RenderStyle* s1, const RenderStyle* s2) +{ + if (!s1 || !s2) + return false; + return s1->display() != s2->display() + || s1->left() != s2->left() || s1->top() != s2->top() + || s1->right() != s2->right() || s1->bottom() != s2->bottom(); +} +#endif + +void Element::recalcStyle(StyleChange change) +{ + // Ref currentStyle in case it would otherwise be deleted when setRenderStyle() is called. + RefPtr<RenderStyle> currentStyle(renderStyle()); + bool hasParentStyle = parentOrHostNode() ? parentOrHostNode()->renderStyle() : false; + bool hasPositionalRules = needsStyleRecalc() && currentStyle && currentStyle->childrenAffectedByPositionalRules(); + bool hasDirectAdjacentRules = currentStyle && currentStyle->childrenAffectedByDirectAdjacentRules(); + + if ((change > NoChange || needsStyleRecalc())) { +#ifdef ANDROID_STYLE_VERSION + RefPtr<RenderStyle> newStyle = document()->styleForElementIgnoringPendingStylesheets(this); + if (displayDiff(currentStyle.get(), newStyle.get())) + document()->incStyleVersion(); +#endif + if (hasRareData()) + rareData()->resetComputedStyle(); + } + if (hasParentStyle && (change >= Inherit || needsStyleRecalc())) { + RefPtr<RenderStyle> newStyle = document()->styleSelector()->styleForElement(this); + StyleChange ch = diff(currentStyle.get(), newStyle.get()); + if (ch == Detach || !currentStyle) { + if (attached()) + detach(); + attach(); // FIXME: The style gets computed twice by calling attach. We could do better if we passed the style along. + // attach recalulates the style for all children. No need to do it twice. + clearNeedsStyleRecalc(); + clearChildNeedsStyleRecalc(); + 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 || pseudoStyleCacheIsInvalid(currentStyle.get(), newStyle.get()) || (change == Force && renderer() && renderer()->requiresForcedStyleRecalcPropagation())) { + setRenderStyle(newStyle); + } else if (needsStyleRecalc() && (styleChangeType() != SyntheticStyleChange) && (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.get()); + else + setRenderStyle(newStyle); + } else if (styleChangeType() == SyntheticStyleChange) + setRenderStyle(newStyle); + + if (change != Force) { + // If "rem" units are used anywhere in the document, and if the document element's font size changes, then go ahead and force font updating + // all the way down the tree. This is simpler than having to maintain a cache of objects (and such font size changes should be rare anyway). + if (document()->usesRemUnits() && ch != NoChange && currentStyle && newStyle && currentStyle->fontSize() != newStyle->fontSize() && document()->documentElement() == this) + change = Force; + else if ((document()->usesDescendantRules() || hasPositionalRules) && styleChangeType() >= FullStyleChange) + change = Force; + else + change = ch; + } + } + + // FIXME: This check is good enough for :hover + foo, but it is not good enough for :hover + foo + bar. + // For now we will just worry about the common case, since it's a lot trickier to get the second case right + // without doing way too much re-resolution. + bool forceCheckOfNextElementSibling = false; + for (Node *n = firstChild(); n; n = n->nextSibling()) { + bool childRulesChanged = n->needsStyleRecalc() && n->styleChangeType() == FullStyleChange; + if (forceCheckOfNextElementSibling && n->isElementNode()) + n->setNeedsStyleRecalc(); + if (change >= Inherit || n->isTextNode() || n->childNeedsStyleRecalc() || n->needsStyleRecalc()) + n->recalcStyle(change); + if (n->isElementNode()) + forceCheckOfNextElementSibling = childRulesChanged && hasDirectAdjacentRules; + } + // FIXME: This does not care about sibling combinators. Will be necessary in XBL2 world. + if (Node* shadow = shadowRoot()) { + if (change >= Inherit || shadow->isTextNode() || shadow->childNeedsStyleRecalc() || shadow->needsStyleRecalc()) + shadow->recalcStyle(change); + } + + clearNeedsStyleRecalc(); + clearChildNeedsStyleRecalc(); +} + +Node* Element::shadowRoot() +{ + return hasRareData() ? rareData()->m_shadowRoot.get() : 0; +} + +void Element::setShadowRoot(PassRefPtr<Node> node) +{ + // FIXME: Because today this is never called from script directly, we don't have to worry + // about compromising DOM tree integrity (eg. node being a parent of this). However, + // once we implement XBL2, we will have to add integrity checks here. + removeShadowRoot(); + RefPtr<Node> newRoot = node; + if (!newRoot) + return; + + ensureRareData()->m_shadowRoot = newRoot; + newRoot->setShadowHost(this); +} + +void Element::removeShadowRoot() +{ + if (!hasRareData()) + return; + + if (RefPtr<Node> oldRoot = rareData()->m_shadowRoot.release()) { + document()->removeFocusedNodeOfSubtree(oldRoot.get()); + oldRoot->setShadowHost(0); + if (oldRoot->inDocument()) + oldRoot->removedFromDocument(); + else + oldRoot->removedFromTree(true); + } +} + +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; + default: + break; + } + return false; +} + +static void checkForSiblingStyleChanges(Element* e, RenderStyle* style, bool finishedParsingCallback, + Node* beforeChange, Node* afterChange, int childCountDelta) +{ + if (!style || (e->needsStyleRecalc() && 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->setNeedsStyleRecalc(); + + // We also have to handle node removal. + if (childCountDelta < 0 && newFirstChild == firstElementAfterInsertion && newFirstChild && newFirstChild->renderStyle() && !newFirstChild->renderStyle()->firstChildState()) + newFirstChild->setNeedsStyleRecalc(); + } + + // :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->setNeedsStyleRecalc(); + + // 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->setNeedsStyleRecalc(); + } + + // 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->setNeedsStyleRecalc(); + } + + // 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->setNeedsStyleRecalc(); + + // :empty selector. + if (style->affectedByEmpty() && (!style->emptyState() || e->hasChildNodes())) + e->setNeedsStyleRecalc(); +} + +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(); + setIsParsingChildrenFinished(); + 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; + dispatchScopedEvent(MutationEvent::create(DOMAttrModifiedEvent, true, attr, attr->value(), + attr->value(), document()->attrName(attr->id()), MutationEvent::REMOVAL), ec); +#endif +} + +void Element::dispatchAttrAdditionEvent(Attribute*) +{ + ASSERT(!eventDispatchForbidden()); + +#if 0 + if (!document()->hasListenerType(Document::DOMATTRMODIFIED_LISTENER)) + return; + ExceptionCode ec = 0; + dispatchScopedEvent(MutationEvent::create(DOMAttrModifiedEvent, true, attr, attr->value(), + attr->value(), document()->attrName(attr->id()), MutationEvent::ADDITION), ec); +#endif +} + +String Element::openTagStartToString() const +{ + String result = "<" + nodeName(); + + NamedNodeMap* 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; +} + +#ifndef NDEBUG +void Element::formatForDebugger(char* buffer, unsigned length) const +{ + String result; + String s; + + s = nodeName(); + if (s.length() > 0) { + result += s; + } + + s = getIdAttribute(); + 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) +{ + if (!attr) { + ec = TYPE_MISMATCH_ERR; + return 0; + } + return static_pointer_cast<Attr>(attributes(false)->setNamedItem(attr, ec)); +} + +PassRefPtr<Attr> Element::setAttributeNodeNS(Attr* attr, ExceptionCode& ec) +{ + if (!attr) { + ec = TYPE_MISMATCH_ERR; + return 0; + } + return static_pointer_cast<Attr>(attributes(false)->setNamedItem(attr, ec)); +} + +PassRefPtr<Attr> Element::removeAttributeNode(Attr* attr, ExceptionCode& ec) +{ + if (!attr) { + ec = TYPE_MISMATCH_ERR; + return 0; + } + if (attr->ownerElement() != this) { + ec = NOT_FOUND_ERR; + return 0; + } + if (document() != attr->document()) { + ec = WRONG_DOCUMENT_ERR; + return 0; + } + + NamedNodeMap* attrs = attributes(true); + if (!attrs) + return 0; + + return static_pointer_cast<Attr>(attrs->removeNamedItem(attr->qualifiedName(), ec)); +} + +void Element::setAttributeNS(const AtomicString& namespaceURI, const AtomicString& qualifiedName, const AtomicString& value, ExceptionCode& ec, FragmentScriptingPermission scriptingPermission) +{ + String prefix, localName; + if (!Document::parseQualifiedName(qualifiedName, prefix, localName, ec)) + return; + + if (namespaceURI.isNull() && !prefix.isNull()) { + ec = NAMESPACE_ERR; + return; + } + + QualifiedName qName(prefix, localName, namespaceURI); + + if (scriptingPermission == FragmentScriptingNotAllowed && (isEventHandlerAttribute(qName) || isAttributeToRemove(qName, value))) + return; + + setAttribute(qName, value, ec); +} + +void Element::removeAttribute(const String& name, ExceptionCode& ec) +{ + InspectorInstrumentation::willModifyDOMAttr(document(), this); + + String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; + + if (m_attributeMap) { + m_attributeMap->removeNamedItem(localName, ec); + if (ec == NOT_FOUND_ERR) + ec = 0; + } + + InspectorInstrumentation::didModifyDOMAttr(document(), this); +} + +void Element::removeAttributeNS(const String& namespaceURI, const String& localName, ExceptionCode& ec) +{ + removeAttribute(QualifiedName(nullAtom, localName, namespaceURI), ec); +} + +PassRefPtr<Attr> Element::getAttributeNode(const String& name) +{ + NamedNodeMap* 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) +{ + NamedNodeMap* 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 +{ + NamedNodeMap* attrs = attributes(true); + if (!attrs) + return false; + + // This call to String::lower() seems to be required but + // there may be a way to remove it. + String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name; + return attrs->getAttributeItem(localName, false); +} + +bool Element::hasAttributeNS(const String& namespaceURI, const String& localName) const +{ + NamedNodeMap* 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; + + // If the stylesheets have already been loaded we can reliably check isFocusable. + // If not, we continue and set the focused node on the focus controller below so + // that it can be updated soon after attach. + if (doc->haveStylesheetsLoaded()) { + doc->updateLayoutIgnorePendingStylesheets(); + if (!isFocusable()) + return; + } + + if (!supportsFocus()) + return; + + RefPtr<Node> protect; + if (Page* page = doc->page()) { + // Focus and change event handlers can cause us to lose our last ref. + // If a focus event handler changes the focus to a different node it + // does not make sense to continue and update appearence. + protect = this; + if (!page->focusController()->setFocusedNode(this, doc->frame())) + return; + } + + // Setting the focused node above might have invalidated the layout due to scripts. + doc->updateLayoutIgnorePendingStylesheets(); + + if (!isFocusable()) { + ensureRareData()->setNeedsFocusAppearanceUpdateSoonAfterAttach(true); + return; + } + + cancelFocusAppearanceUpdate(); + updateFocusAppearance(restorePreviousSelection); +} + +void Element::updateFocusAppearance(bool /*restorePreviousSelection*/) +{ + if (this == rootEditableElement()) { + Frame* frame = document()->frame(); + if (!frame) + return; + + // When focusing an editable element in an iframe, don't reset the selection if it already contains a selection. + if (this == frame->selection()->rootEditableElement()) + return; + + // FIXME: We should restore the previous selection if there is one. + VisibleSelection newSelection = VisibleSelection(Position(this, 0), DOWNSTREAM); + + if (frame->selection()->shouldChangeSelection(newSelection)) { + frame->selection()->setSelection(newSelection); + frame->selection()->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 +{ + return hasRareData() ? rareData()->m_minimumSizeForResizing : defaultMinimumSizeForResizing(); +} + +void Element::setMinimumSizeForResizing(const IntSize& size) +{ + if (size == defaultMinimumSizeForResizing() && !hasRareData()) + return; + ensureRareData()->m_minimumSizeForResizing = size; +} + +RenderStyle* Element::computedStyle(PseudoId pseudoElementSpecifier) +{ + // FIXME: Find and use the renderer from the pseudo element instead of the actual element so that the 'length' + // properties, which are only known by the renderer because it did the layout, will be correct and so that the + // values returned for the ":selection" pseudo-element will be correct. + if (RenderStyle* usedStyle = renderStyle()) + return pseudoElementSpecifier ? usedStyle->getCachedPseudoStyle(pseudoElementSpecifier) : 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* data = ensureRareData(); + if (!data->m_computedStyle) + data->m_computedStyle = document()->styleForElementIgnoringPendingStylesheets(this); + return pseudoElementSpecifier ? data->m_computedStyle->getCachedPseudoStyle(pseudoElementSpecifier) : data->m_computedStyle.get(); +} + +AtomicString Element::computeInheritedLanguage() const +{ + const Node* n = this; + AtomicString value; + // The language property is inherited, so we iterate over the parents to find the first language. + while (n && value.isNull()) { + if (n->isElementNode()) { + // Spec: xml:lang takes precedence -- http://www.w3.org/TR/xhtml1/#C_7 + value = static_cast<const Element*>(n)->fastGetAttribute(XMLNames::langAttr); + if (value.isNull()) + value = static_cast<const Element*>(n)->fastGetAttribute(HTMLNames::langAttr); + } else if (n->isDocumentNode()) { + // checking the MIME content-language + value = static_cast<const Document*>(n)->contentLanguage(); + } + + n = n->parentNode(); + } + + return value; +} + +void Element::cancelFocusAppearanceUpdate() +{ + if (hasRareData()) + rareData()->setNeedsFocusAppearanceUpdateSoonAfterAttach(false); + if (document()->focusedNode() == this) + document()->cancelFocusAppearanceUpdate(); +} + +void Element::normalizeAttributes() +{ + // Normalize attributes. + NamedNodeMap* attrs = attributes(true); + if (!attrs) + return; + + if (attrs->isEmpty()) + return; + + Vector<RefPtr<Attribute> > attributeVector; + attrs->copyAttributesToVector(attributeVector); + size_t numAttrs = attributeVector.size(); + for (size_t i = 0; i < numAttrs; ++i) { + if (Attr* attr = attributeVector[i]->attr()) + attr->normalize(); + } +} + +// ElementTraversal API +Element* Element::firstElementChild() const +{ + Node* n = firstChild(); + while (n && !n->isElementNode()) + n = n->nextSibling(); + return static_cast<Element*>(n); +} + +Element* Element::lastElementChild() const +{ + Node* n = lastChild(); + while (n && !n->isElementNode()) + n = n->previousSibling(); + return static_cast<Element*>(n); +} + +Element* Element::previousElementSibling() const +{ + Node* n = previousSibling(); + while (n && !n->isElementNode()) + n = n->previousSibling(); + return static_cast<Element*>(n); +} + +Element* Element::nextElementSibling() const +{ + Node* n = nextSibling(); + while (n && !n->isElementNode()) + n = n->nextSibling(); + return static_cast<Element*>(n); +} + +unsigned Element::childElementCount() const +{ + unsigned count = 0; + Node* n = firstChild(); + while (n) { + count += n->isElementNode(); + n = n->nextSibling(); + } + return count; +} + +bool Element::webkitMatchesSelector(const String& selector, ExceptionCode& ec) +{ + if (selector.isEmpty()) { + ec = SYNTAX_ERR; + return false; + } + + bool strictParsing = !document()->inQuirksMode(); + CSSParser p(strictParsing); + + CSSSelectorList selectorList; + p.parseSelector(selector, document(), selectorList); + + if (!selectorList.first()) { + ec = SYNTAX_ERR; + return false; + } + + // Throw a NAMESPACE_ERR if the selector includes any namespace prefixes. + if (selectorList.selectorsNeedNamespaceResolution()) { + ec = NAMESPACE_ERR; + return false; + } + + CSSStyleSelector::SelectorChecker selectorChecker(document(), strictParsing); + for (CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) { + if (selectorChecker.checkSelector(selector, this)) + return true; + } + + return false; +} + +DOMTokenList* Element::classList() +{ + ElementRareData* data = ensureRareData(); + if (!data->m_classList) + data->m_classList = ClassList::create(this); + return data->m_classList.get(); +} + +DOMTokenList* Element::optionalClassList() const +{ + if (!hasRareData()) + return 0; + return rareData()->m_classList.get(); +} + +DOMStringMap* Element::dataset() +{ + ElementRareData* data = ensureRareData(); + if (!data->m_datasetDOMStringMap) + data->m_datasetDOMStringMap = DatasetDOMStringMap::create(this); + return data->m_datasetDOMStringMap.get(); +} + +DOMStringMap* Element::optionalDataset() const +{ + if (!hasRareData()) + return 0; + return rareData()->m_datasetDOMStringMap.get(); +} + +KURL Element::getURLAttribute(const QualifiedName& name) const +{ +#if !ASSERT_DISABLED + if (m_attributeMap) { + if (Attribute* attribute = m_attributeMap->getAttributeItem(name)) + ASSERT(isURLAttribute(attribute)); + } +#endif + return document()->completeURL(stripLeadingAndTrailingHTMLSpaces(getAttribute(name))); +} + +KURL Element::getNonEmptyURLAttribute(const QualifiedName& name) const +{ +#if !ASSERT_DISABLED + if (m_attributeMap) { + if (Attribute* attribute = m_attributeMap->getAttributeItem(name)) + ASSERT(isURLAttribute(attribute)); + } +#endif + String value = stripLeadingAndTrailingHTMLSpaces(getAttribute(name)); + if (value.isEmpty()) + return KURL(); + return document()->completeURL(value); +} + +int Element::getIntegralAttribute(const QualifiedName& attributeName) const +{ + return getAttribute(attributeName).string().toInt(); +} + +void Element::setIntegralAttribute(const QualifiedName& attributeName, int value) +{ + // FIXME: Need an AtomicString version of String::number. + ExceptionCode ec; + setAttribute(attributeName, String::number(value), ec); +} + +unsigned Element::getUnsignedIntegralAttribute(const QualifiedName& attributeName) const +{ + return getAttribute(attributeName).string().toUInt(); +} + +void Element::setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value) +{ + // FIXME: Need an AtomicString version of String::number. + ExceptionCode ec; + setAttribute(attributeName, String::number(value), ec); +} + +#if ENABLE(SVG) +bool Element::childShouldCreateRenderer(Node* child) const +{ + // Only create renderers for SVG elements whose parents are SVG elements, or for proper <svg xmlns="svgNS"> subdocuments. + if (child->isSVGElement()) + return child->hasTagName(SVGNames::svgTag) || isSVGElement(); + + return Node::childShouldCreateRenderer(child); +} +#endif + +#if ENABLE(FULLSCREEN_API) +void Element::webkitRequestFullScreen(unsigned short flags) +{ + document()->webkitRequestFullScreenForElement(this, flags); +} +#endif + +SpellcheckAttributeState Element::spellcheckAttributeState() const +{ + if (!hasAttribute(HTMLNames::spellcheckAttr)) + return SpellcheckAttributeDefault; + + const AtomicString& value = getAttribute(HTMLNames::spellcheckAttr); + if (equalIgnoringCase(value, "true") || equalIgnoringCase(value, "")) + return SpellcheckAttributeTrue; + if (equalIgnoringCase(value, "false")) + return SpellcheckAttributeFalse; + + return SpellcheckAttributeDefault; +} + +bool Element::isSpellCheckingEnabled() const +{ + const Element* element = this; + while (element) { + switch (element->spellcheckAttributeState()) { + case SpellcheckAttributeTrue: + return true; + case SpellcheckAttributeFalse: + return false; + case SpellcheckAttributeDefault: + break; + } + + ContainerNode* parent = const_cast<Element*>(element)->parentOrHostNode(); + element = (parent && parent->isElementNode()) ? toElement(parent) : 0; + } + + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/Element.h b/Source/WebCore/dom/Element.h new file mode 100644 index 0000000..e552376 --- /dev/null +++ b/Source/WebCore/dom/Element.h @@ -0,0 +1,512 @@ +/* + * 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, 2009, 2010 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 "Document.h" +#include "FragmentScriptingPermission.h" +#include "NamedNodeMap.h" +#include "ScrollTypes.h" + +namespace WebCore { + +class Attribute; +class ClientRect; +class ClientRectList; +class DOMStringMap; +class DOMTokenList; +class ElementRareData; +class IntSize; + +enum SpellcheckAttributeState { + SpellcheckAttributeTrue, + SpellcheckAttributeFalse, + SpellcheckAttributeDefault +}; + +class Element : public ContainerNode { +public: + static PassRefPtr<Element> create(const QualifiedName&, Document*); + virtual ~Element(); + + DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); + DEFINE_ATTRIBUTE_EVENT_LISTENER(change); + DEFINE_ATTRIBUTE_EVENT_LISTENER(click); + DEFINE_ATTRIBUTE_EVENT_LISTENER(contextmenu); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dblclick); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragenter); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragover); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragleave); + DEFINE_ATTRIBUTE_EVENT_LISTENER(drop); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragstart); + DEFINE_ATTRIBUTE_EVENT_LISTENER(drag); + DEFINE_ATTRIBUTE_EVENT_LISTENER(dragend); + DEFINE_ATTRIBUTE_EVENT_LISTENER(input); + DEFINE_ATTRIBUTE_EVENT_LISTENER(invalid); + DEFINE_ATTRIBUTE_EVENT_LISTENER(keydown); + DEFINE_ATTRIBUTE_EVENT_LISTENER(keypress); + DEFINE_ATTRIBUTE_EVENT_LISTENER(keyup); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mousedown); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mousemove); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseout); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseover); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseup); + DEFINE_ATTRIBUTE_EVENT_LISTENER(mousewheel); + DEFINE_ATTRIBUTE_EVENT_LISTENER(scroll); + DEFINE_ATTRIBUTE_EVENT_LISTENER(select); + DEFINE_ATTRIBUTE_EVENT_LISTENER(submit); + + // These four attribute event handler attributes are overridden by HTMLBodyElement + // and HTMLFrameSetElement to forward to the DOMWindow. + DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(blur); + DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(error); + DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(focus); + DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(load); + + // WebKit extensions + DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecut); + DEFINE_ATTRIBUTE_EVENT_LISTENER(cut); + DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecopy); + DEFINE_ATTRIBUTE_EVENT_LISTENER(copy); + DEFINE_ATTRIBUTE_EVENT_LISTENER(beforepaste); + DEFINE_ATTRIBUTE_EVENT_LISTENER(paste); + DEFINE_ATTRIBUTE_EVENT_LISTENER(reset); + DEFINE_ATTRIBUTE_EVENT_LISTENER(search); + DEFINE_ATTRIBUTE_EVENT_LISTENER(selectstart); +#if ENABLE(TOUCH_EVENTS) + DEFINE_ATTRIBUTE_EVENT_LISTENER(touchstart); + DEFINE_ATTRIBUTE_EVENT_LISTENER(touchmove); + DEFINE_ATTRIBUTE_EVENT_LISTENER(touchend); + DEFINE_ATTRIBUTE_EVENT_LISTENER(touchcancel); +#endif +#if ENABLE(FULLSCREEN_API) + DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitfullscreenchange); +#endif + + virtual PassRefPtr<DocumentFragment> deprecatedCreateContextualFragment(const String&, FragmentScriptingPermission = FragmentScriptingAllowed); + + bool hasAttribute(const QualifiedName&) const; + const AtomicString& getAttribute(const QualifiedName&) const; + void setAttribute(const QualifiedName&, const AtomicString& value, ExceptionCode&); + void removeAttribute(const QualifiedName&, ExceptionCode&); + + // Typed getters and setters for language bindings. + int getIntegralAttribute(const QualifiedName& attributeName) const; + void setIntegralAttribute(const QualifiedName& attributeName, int value); + unsigned getUnsignedIntegralAttribute(const QualifiedName& attributeName) const; + void setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value); + + // Call this to get the value of an attribute that is known not to be the style + // attribute or one of the SVG animatable attributes. + bool fastHasAttribute(const QualifiedName&) const; + const AtomicString& fastGetAttribute(const QualifiedName&) const; + + 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 AtomicString& name, const AtomicString& value, ExceptionCode&); + void setAttributeNS(const AtomicString& namespaceURI, const AtomicString& qualifiedName, const AtomicString& value, ExceptionCode&, FragmentScriptingPermission = FragmentScriptingAllowed); + + bool isIdAttributeName(const QualifiedName&) const; + const AtomicString& getIdAttribute() const; + void setIdAttribute(const AtomicString&); + + // Call this to get the value of the id attribute for style resolution purposes. + // The value will already be lowercased if the document is in compatibility mode, + // so this function is not suitable for non-style uses. + const AtomicString& idForStyleResolution() const; + + void scrollIntoView(bool alignToTop = true); + void scrollIntoViewIfNeeded(bool centerIfNeeded = true); + + 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(); + virtual int scrollLeft() const; + virtual int scrollTop() const; + virtual void setScrollLeft(int); + virtual void setScrollTop(int); + virtual int scrollWidth() const; + virtual int scrollHeight() const; + + IntRect boundsInWindowSpace() const; + + PassRefPtr<ClientRectList> getClientRects() const; + PassRefPtr<ClientRect> getBoundingClientRect() const; + + // Returns the absolute bounding box translated into screen coordinates: + IntRect screenRect() const; + + 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(); } + + const AtomicString& localName() const { return m_tagName.localName(); } + const AtomicString& prefix() const { return m_tagName.prefix(); } + const AtomicString& namespaceURI() const { return m_tagName.namespaceURI(); } + + virtual KURL baseURI() const; + + virtual String nodeName() const; + + PassRefPtr<Element> cloneElementWithChildren(); + PassRefPtr<Element> cloneElementWithoutChildren(); + + void normalizeAttributes(); + String nodeNamePreservingCase() const; + + // convenience methods which ignore exceptions + void setAttribute(const QualifiedName&, const AtomicString& value); + void setBooleanAttribute(const QualifiedName& name, bool); + // Please don't use setCStringAttribute in performance-sensitive code; + // use a static AtomicString value instead to avoid the conversion overhead. + void setCStringAttribute(const QualifiedName&, const char* cStringValue); + + NamedNodeMap* attributes(bool readonly = false) const; + + // This method is called whenever an attribute is added, changed or removed. + virtual void attributeChanged(Attribute*, bool preserveDecls = false); + + void setAttributeMap(PassRefPtr<NamedNodeMap>, FragmentScriptingPermission = FragmentScriptingAllowed); + NamedNodeMap* attributeMap() const { return m_attributeMap.get(); } + + virtual void copyNonAttributeProperties(const Element* /*source*/) { } + + virtual void attach(); + virtual void detach(); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void recalcStyle(StyleChange = NoChange); + + Node* shadowRoot(); + void setShadowRoot(PassRefPtr<Node>); + + RenderStyle* computedStyle(PseudoId = NOPSEUDO); + + AtomicString computeInheritedLanguage() const; + + void dispatchAttrRemovalEvent(Attribute*); + void dispatchAttrAdditionEvent(Attribute*); + + virtual void accessKeyAction(bool /*sendToAnyEvent*/) { } + + virtual bool isURLAttribute(Attribute*) const; + + KURL getURLAttribute(const QualifiedName&) const; + KURL getNonEmptyURLAttribute(const QualifiedName&) 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(); + + 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::registerForDocumentActivationCallbacks() to subscribe to these + virtual void documentWillBecomeInactive() { } + virtual void documentDidBecomeActive() { } + + // Use Document::registerForMediaVolumeCallbacks() to subscribe to this + virtual void mediaVolumeDidChange() { } + + bool isFinishedParsingChildren() const { return isParsingChildrenFinished(); } + virtual void finishParsingChildren(); + virtual void beginParsingChildren() { clearIsParsingChildrenFinished(); } + + // ElementTraversal API + Element* firstElementChild() const; + Element* lastElementChild() const; + Element* previousElementSibling() const; + Element* nextElementSibling() const; + unsigned childElementCount() const; + + bool webkitMatchesSelector(const String& selectors, ExceptionCode&); + + DOMTokenList* classList(); + DOMTokenList* optionalClassList() const; + + DOMStringMap* dataset(); + DOMStringMap* optionalDataset() const; + +#if ENABLE(MATHML) + virtual bool isMathMLElement() const { return false; } +#else + static bool isMathMLElement() { return false; } +#endif + + virtual bool isFormControlElement() const { return false; } + virtual bool isEnabledFormControl() const { return true; } + virtual bool isReadOnlyFormControl() const { return false; } + virtual bool isSpinButtonElement() const { return false; } + virtual bool isTextFormControl() const { return false; } + virtual bool isOptionalFormControl() const { return false; } + virtual bool isRequiredFormControl() const { return false; } + virtual bool isDefaultButtonForForm() const { return false; } + virtual bool willValidate() const { return false; } + virtual bool isValidFormControlElement() { return false; } + virtual bool hasUnacceptableValue() const { return false; } + virtual bool isInRange() const { return false; } + virtual bool isOutOfRange() const { return false; } + + virtual bool formControlValueMatchesRenderer() const { return false; } + virtual void setFormControlValueMatchesRenderer(bool) { } + + virtual const AtomicString& formControlName() const { return nullAtom; } + virtual const AtomicString& formControlType() const { return nullAtom; } + + virtual bool shouldSaveAndRestoreFormControlState() const { return true; } + virtual bool saveFormControlState(String&) const { return false; } + virtual void restoreFormControlState(const String&) { } + + virtual void dispatchFormControlChangeEvent() { } + +#if ENABLE(SVG) + virtual bool childShouldCreateRenderer(Node*) const; +#endif + +#if ENABLE(FULLSCREEN_API) + enum { + ALLOW_KEYBOARD_INPUT = 1 + }; + + void webkitRequestFullScreen(unsigned short flags); +#endif + + virtual bool isSpellCheckingEnabled() const; + +protected: + Element(const QualifiedName& tagName, Document* document, ConstructionType type) + : ContainerNode(document, type) + , m_tagName(tagName) + { + } + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void insertedIntoTree(bool); + virtual void removedFromTree(bool); + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + // The implementation of Element::attributeChanged() calls the following two functions. + // They are separated to allow a different flow of control in StyledElement::attributeChanged(). + void recalcStyleIfNeededAfterAttributeChanged(Attribute*); + void updateAfterAttributeChanged(Attribute*); + +private: + void scrollByUnits(int units, ScrollGranularity); + + virtual void setPrefix(const AtomicString&, ExceptionCode&); + virtual NodeType nodeType() const; + virtual bool childTypeAllowed(NodeType); + + virtual PassRefPtr<Attribute> createAttribute(const QualifiedName&, const AtomicString& value); + +#ifndef NDEBUG + virtual void formatForDebugger(char* buffer, unsigned length) const; +#endif + + bool pseudoStyleCacheIsInvalid(const RenderStyle* currentStyle, RenderStyle* newStyle); + + void createAttributeMap() const; + + virtual void updateStyleAttribute() const { } + +#if ENABLE(SVG) + virtual void updateAnimatedSVGAttribute(const QualifiedName&) const { } +#endif + + void cancelFocusAppearanceUpdate(); + + virtual const AtomicString& virtualPrefix() const { return prefix(); } + virtual const AtomicString& virtualLocalName() const { return localName(); } + virtual const AtomicString& virtualNamespaceURI() const { return namespaceURI(); } + virtual RenderStyle* virtualComputedStyle(PseudoId pseudoElementSpecifier = NOPSEUDO) { return computedStyle(pseudoElementSpecifier); } + + // cloneNode is private so that non-virtual cloneElementWithChildren and cloneElementWithoutChildren + // are used instead. + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const; + + QualifiedName m_tagName; + virtual NodeRareData* createRareData(); + + ElementRareData* rareData() const; + ElementRareData* ensureRareData(); + + SpellcheckAttributeState spellcheckAttributeState() const; + void removeShadowRoot(); + +private: + mutable RefPtr<NamedNodeMap> m_attributeMap; +}; + +inline Element* toElement(Node* node) +{ + ASSERT(!node || node->isElementNode()); + return static_cast<Element*>(node); +} + +inline const Element* toElement(const Node* node) +{ + ASSERT(!node || node->isElementNode()); + return static_cast<const Element*>(node); +} + +// This will catch anyone doing an unnecessary cast. +void toElement(const Element*); + +inline bool Node::hasTagName(const QualifiedName& name) const +{ + return isElementNode() && toElement(this)->hasTagName(name); +} + +inline bool Node::hasAttributes() const +{ + return isElementNode() && toElement(this)->hasAttributes(); +} + +inline NamedNodeMap* Node::attributes() const +{ + return isElementNode() ? toElement(this)->attributes() : 0; +} + +inline Element* Node::parentElement() const +{ + ContainerNode* parent = parentNode(); + return parent && parent->isElementNode() ? toElement(parent) : 0; +} + +inline NamedNodeMap* Element::attributes(bool readonly) const +{ + if (!isStyleAttributeValid()) + updateStyleAttribute(); + +#if ENABLE(SVG) + if (!areSVGAttributesValid()) + updateAnimatedSVGAttribute(anyQName()); +#endif + + if (!readonly && !m_attributeMap) + createAttributeMap(); + return m_attributeMap.get(); +} + +inline 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); +} + +inline bool Element::fastHasAttribute(const QualifiedName& name) const +{ + return m_attributeMap && m_attributeMap->getAttributeItem(name); +} + +inline const AtomicString& Element::fastGetAttribute(const QualifiedName& name) const +{ + if (m_attributeMap) { + if (Attribute* attribute = m_attributeMap->getAttributeItem(name)) + return attribute->value(); + } + return nullAtom; +} + +inline const AtomicString& Element::idForStyleResolution() const +{ + ASSERT(hasID()); + return m_attributeMap->idForStyleResolution(); +} + +inline bool Element::isIdAttributeName(const QualifiedName& attributeName) const +{ + // FIXME: This check is probably not correct for the case where the document has an id attribute + // with a non-null namespace, because it will return false, a false negative, if the prefixes + // don't match but the local name and namespace both do. However, since this has been like this + // for a while and the code paths may be hot, we'll have to measure performance if we fix it. + return attributeName == document()->idAttributeName(); +} + +inline const AtomicString& Element::getIdAttribute() const +{ + return fastGetAttribute(document()->idAttributeName()); +} + +inline void Element::setIdAttribute(const AtomicString& value) +{ + setAttribute(document()->idAttributeName(), value); +} + +} // namespace + +#endif diff --git a/Source/WebCore/dom/Element.idl b/Source/WebCore/dom/Element.idl new file mode 100644 index 0000000..7cb63c2 --- /dev/null +++ b/Source/WebCore/dom/Element.idl @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2006, 2007, 2009 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, + GenerateNativeConverter, + InlineGetOwnPropertySlot + ] Element : Node { + + // DOM Level 1 Core + + readonly attribute [ConvertNullStringTo=Null] DOMString tagName; + + [ConvertNullStringTo=Null] DOMString getAttribute(in DOMString name); + [OldStyleObjC] void setAttribute(in DOMString name, + in DOMString value) + raises(DOMException); + void removeAttribute(in DOMString name) + raises(DOMException); + Attr getAttributeNode(in DOMString name); + 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] 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); + Attr setAttributeNodeNS(in Attr newAttr) + raises(DOMException); + boolean hasAttribute(in DOMString name); + [OldStyleObjC] boolean hasAttributeNS(in [ConvertNullToNullString] DOMString namespaceURI, + in DOMString localName); + + readonly attribute CSSStyleDeclaration style; + + // 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 + + 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); + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + readonly attribute DOMStringMap dataset; +#endif + + // NodeSelector - Selector API + [RequiresAllArguments=Raise] Element querySelector(in DOMString selectors) + raises(DOMException); + [RequiresAllArguments=Raise] NodeList querySelectorAll(in DOMString selectors) + raises(DOMException); + + // WebKit extension, pending specification. + boolean webkitMatchesSelector(in DOMString selectors) + raises(DOMException); + + // ElementTraversal API + readonly attribute Element firstElementChild; + readonly attribute Element lastElementChild; + readonly attribute Element previousElementSibling; + readonly attribute Element nextElementSibling; + readonly attribute unsigned long childElementCount; + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + // CSSOM View Module API + ClientRectList getClientRects(); + ClientRect getBoundingClientRect(); +#endif + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + // Objective-C extensions + readonly attribute DOMString innerText; +#endif + +#if defined(ENABLE_FULLSCREEN_API) && ENABLE_FULLSCREEN_API + const unsigned short ALLOW_KEYBOARD_INPUT = 1; + void webkitRequestFullScreen(in unsigned short flags); +#endif + +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C + // Event handler DOM attributes + attribute [DontEnum] EventListener onabort; + attribute [DontEnum] EventListener onblur; + attribute [DontEnum] EventListener onchange; + attribute [DontEnum] EventListener onclick; + attribute [DontEnum] EventListener oncontextmenu; + attribute [DontEnum] EventListener ondblclick; + attribute [DontEnum] EventListener ondrag; + attribute [DontEnum] EventListener ondragend; + attribute [DontEnum] EventListener ondragenter; + attribute [DontEnum] EventListener ondragleave; + attribute [DontEnum] EventListener ondragover; + attribute [DontEnum] EventListener ondragstart; + attribute [DontEnum] EventListener ondrop; + attribute [DontEnum] EventListener onerror; + attribute [DontEnum] EventListener onfocus; + attribute [DontEnum] EventListener oninput; + attribute [DontEnum] EventListener oninvalid; + attribute [DontEnum] EventListener onkeydown; + attribute [DontEnum] EventListener onkeypress; + attribute [DontEnum] EventListener onkeyup; + attribute [DontEnum] EventListener onload; + attribute [DontEnum] EventListener onmousedown; + attribute [DontEnum] EventListener onmousemove; + attribute [DontEnum] EventListener onmouseout; + attribute [DontEnum] EventListener onmouseover; + attribute [DontEnum] EventListener onmouseup; + attribute [DontEnum] EventListener onmousewheel; + attribute [DontEnum] EventListener onscroll; + attribute [DontEnum] EventListener onselect; + attribute [DontEnum] EventListener onsubmit; + + // attribute [DontEnum] EventListener oncanplay; + // attribute [DontEnum] EventListener oncanplaythrough; + // attribute [DontEnum] EventListener ondurationchange; + // attribute [DontEnum] EventListener onemptied; + // attribute [DontEnum] EventListener onended; + // attribute [DontEnum] EventListener onformchange; + // attribute [DontEnum] EventListener onforminput; + // attribute [DontEnum] EventListener onloadeddata; + // attribute [DontEnum] EventListener onloadedmetadata; + // attribute [DontEnum] EventListener onloadstart; + // attribute [DontEnum] EventListener onpause; + // attribute [DontEnum] EventListener onplay; + // attribute [DontEnum] EventListener onplaying; + // attribute [DontEnum] EventListener onprogress; + // attribute [DontEnum] EventListener onratechange; + // attribute [DontEnum] EventListener onreadystatechange; + // attribute [DontEnum] EventListener onseeked; + // attribute [DontEnum] EventListener onseeking; + // attribute [DontEnum] EventListener onshow; + // attribute [DontEnum] EventListener onstalled; + // attribute [DontEnum] EventListener onsuspend; + // attribute [DontEnum] EventListener ontimeupdate; + // attribute [DontEnum] EventListener onvolumechange; + // attribute [DontEnum] EventListener onwaiting; + + // WebKit extensions + attribute [DontEnum] EventListener onbeforecut; + attribute [DontEnum] EventListener oncut; + attribute [DontEnum] EventListener onbeforecopy; + attribute [DontEnum] EventListener oncopy; + attribute [DontEnum] EventListener onbeforepaste; + attribute [DontEnum] EventListener onpaste; + attribute [DontEnum] EventListener onreset; + attribute [DontEnum] EventListener onsearch; + attribute [DontEnum] EventListener onselectstart; + attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchstart; + attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchmove; + attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchend; + attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchcancel; + attribute [DontEnum, Conditional=FULLSCREEN_API] EventListener onwebkitfullscreenchange; +#endif + }; + +} diff --git a/Source/WebCore/dom/ElementRareData.h b/Source/WebCore/dom/ElementRareData.h new file mode 100644 index 0000000..f7f30bb --- /dev/null +++ b/Source/WebCore/dom/ElementRareData.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 David Smith <catfish.man@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. + * + */ + +#ifndef ElementRareData_h +#define ElementRareData_h + +#include "ClassList.h" +#include "DatasetDOMStringMap.h" +#include "Element.h" +#include "NodeRareData.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class ElementRareData : public NodeRareData { +public: + ElementRareData(); + + void resetComputedStyle(); + + using NodeRareData::needsFocusAppearanceUpdateSoonAfterAttach; + using NodeRareData::setNeedsFocusAppearanceUpdateSoonAfterAttach; + + IntSize m_minimumSizeForResizing; + RefPtr<RenderStyle> m_computedStyle; + RefPtr<Node> m_shadowRoot; + + OwnPtr<DatasetDOMStringMap> m_datasetDOMStringMap; + OwnPtr<ClassList> m_classList; +}; + +inline IntSize defaultMinimumSizeForResizing() +{ + return IntSize(INT_MAX, INT_MAX); +} + +inline ElementRareData::ElementRareData() + : m_minimumSizeForResizing(defaultMinimumSizeForResizing()) +{ +} + +inline void ElementRareData::resetComputedStyle() +{ + m_computedStyle.clear(); +} + +} +#endif // ElementRareData_h diff --git a/Source/WebCore/dom/Entity.h b/Source/WebCore/dom/Entity.h new file mode 100644 index 0000000..9ee26a2 --- /dev/null +++ b/Source/WebCore/dom/Entity.h @@ -0,0 +1,43 @@ +/* + * 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. + * + */ + +#ifndef Entity_h +#define Entity_h + +#include "ContainerNode.h" + +namespace WebCore { + +// FIXME: This abstract class is only here so that the JavaScript and Objective-C bindings +// can continue to be compiled. +class Entity : public ContainerNode { +public: + String publicId() const { ASSERT_NOT_REACHED(); return String(); } + String systemId() const { ASSERT_NOT_REACHED(); return String(); } + String notationName() const { ASSERT_NOT_REACHED(); return String(); } + +private: + Entity() : ContainerNode(0) {} +}; + +} //namespace + +#endif diff --git a/Source/WebCore/dom/Entity.idl b/Source/WebCore/dom/Entity.idl new file mode 100644 index 0000000..8dacbe9 --- /dev/null +++ b/Source/WebCore/dom/Entity.idl @@ -0,0 +1,28 @@ +/* + * 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 Entity : Node { + readonly attribute [ConvertNullStringTo=Null] DOMString publicId; + readonly attribute [ConvertNullStringTo=Null] DOMString systemId; + readonly attribute [ConvertNullStringTo=Null] DOMString notationName; + }; + +} diff --git a/Source/WebCore/dom/EntityReference.cpp b/Source/WebCore/dom/EntityReference.cpp new file mode 100644 index 0000000..72944ec --- /dev/null +++ b/Source/WebCore/dom/EntityReference.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006, 2008, 2009 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 "EntityReference.h" + +#include "Document.h" + +namespace WebCore { + +inline EntityReference::EntityReference(Document* document, const String& entityName) + : ContainerNode(document) + , m_entityName(entityName) +{ +} + +PassRefPtr<EntityReference> EntityReference::create(Document* document, const String& entityName) +{ + return adoptRef(new EntityReference(document, entityName)); +} + +String EntityReference::nodeName() const +{ + return m_entityName; +} + +Node::NodeType EntityReference::nodeType() const +{ + return ENTITY_REFERENCE_NODE; +} + +PassRefPtr<Node> EntityReference::cloneNode(bool) +{ + return create(document(), m_entityName); +} + +} // namespace diff --git a/Source/WebCore/dom/EntityReference.h b/Source/WebCore/dom/EntityReference.h new file mode 100644 index 0000000..7a6f6c3 --- /dev/null +++ b/Source/WebCore/dom/EntityReference.h @@ -0,0 +1,45 @@ +/* + * 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. + * + */ + +#ifndef EntityReference_h +#define EntityReference_h + +#include "ContainerNode.h" + +namespace WebCore { + +class EntityReference : public ContainerNode { +public: + static PassRefPtr<EntityReference> create(Document*, const String& entityName); + +private: + EntityReference(Document*, const String& entityName); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + + String m_entityName; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/dom/EntityReference.idl b/Source/WebCore/dom/EntityReference.idl new file mode 100644 index 0000000..f652d9a --- /dev/null +++ b/Source/WebCore/dom/EntityReference.idl @@ -0,0 +1,25 @@ +/* + * 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 EntityReference : Node { + }; + +} diff --git a/Source/WebCore/dom/ErrorEvent.cpp b/Source/WebCore/dom/ErrorEvent.cpp new file mode 100644 index 0000000..2627d01 --- /dev/null +++ b/Source/WebCore/dom/ErrorEvent.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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(WORKERS) + +#include "ErrorEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +ErrorEvent::ErrorEvent() +{ +} + +ErrorEvent::ErrorEvent(const String& message, const String& fileName, unsigned lineNumber) + : Event(eventNames().errorEvent, false, true) + , m_message(message) + , m_fileName(fileName) + , m_lineNumber(lineNumber) +{ +} + +ErrorEvent::~ErrorEvent() +{ +} + +void ErrorEvent::initErrorEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& message, const String& fileName, unsigned lineNumber) +{ + if (dispatched()) + return; + + initEvent(type, canBubble, cancelable); + + m_message = message; + m_fileName = fileName; + m_lineNumber = lineNumber; +} + +bool ErrorEvent::isErrorEvent() const +{ + return true; +} + +} // namespace WebCore + +#endif // ENABLE(WORKERS) diff --git a/Source/WebCore/dom/ErrorEvent.h b/Source/WebCore/dom/ErrorEvent.h new file mode 100644 index 0000000..f81530a --- /dev/null +++ b/Source/WebCore/dom/ErrorEvent.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2009 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 ErrorEvent_h +#define ErrorEvent_h + +#if ENABLE(WORKERS) + +#include "Event.h" +#include "PlatformString.h" + +namespace WebCore { + + class ErrorEvent : public Event { + public: + static PassRefPtr<ErrorEvent> create() + { + return adoptRef(new ErrorEvent); + } + static PassRefPtr<ErrorEvent> create(const String& message, const String& fileName, unsigned lineNumber) + { + return adoptRef(new ErrorEvent(message, fileName, lineNumber)); + } + virtual ~ErrorEvent(); + + void initErrorEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& message, const String& fileName, unsigned lineNumber); + + const String& message() const { return m_message; } + const String& filename() const { return m_fileName; } + unsigned lineno() const { return m_lineNumber; } + + virtual bool isErrorEvent() const; + + private: + ErrorEvent(); + ErrorEvent(const String& message, const String& fileName, unsigned lineNumber); + + String m_message; + String m_fileName; + unsigned m_lineNumber; + }; + +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // ErrorEvent_h diff --git a/Source/WebCore/dom/ErrorEvent.idl b/Source/WebCore/dom/ErrorEvent.idl new file mode 100644 index 0000000..ad13193 --- /dev/null +++ b/Source/WebCore/dom/ErrorEvent.idl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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=WORKERS, + NoStaticTables + ] ErrorEvent : Event { + + readonly attribute DOMString message; + readonly attribute DOMString filename; + readonly attribute unsigned long lineno; + + void initErrorEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString messageArg, in DOMString filenameArg, in unsigned long linenoArg); + }; + +} diff --git a/Source/WebCore/dom/Event.cpp b/Source/WebCore/dom/Event.cpp new file mode 100644 index 0000000..a949fd5 --- /dev/null +++ b/Source/WebCore/dom/Event.cpp @@ -0,0 +1,299 @@ +/* + * 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, 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 "Event.h" + +#include "UserGestureIndicator.h" +#include <wtf/CurrentTime.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +Event::Event() + : m_canBubble(false) + , m_cancelable(false) + , m_propagationStopped(false) + , m_immediatePropagationStopped(false) + , m_defaultPrevented(false) + , m_defaultHandled(false) + , m_cancelBubble(false) + , m_eventPhase(0) + , m_currentTarget(0) + , m_createTime(convertSecondsToDOMTimeStamp(currentTime())) +{ +} + +Event::Event(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg) + : m_type(eventType) + , m_canBubble(canBubbleArg) + , m_cancelable(cancelableArg) + , m_propagationStopped(false) + , m_immediatePropagationStopped(false) + , m_defaultPrevented(false) + , m_defaultHandled(false) + , m_cancelBubble(false) + , m_eventPhase(0) + , m_currentTarget(0) + , m_createTime(convertSecondsToDOMTimeStamp(currentTime())) +{ +} + +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::isCustomEvent() const +{ + return false; +} + +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::isCompositionEvent() const +{ + return false; +} + +bool Event::isDragEvent() const +{ + return false; +} + +bool Event::isClipboardEvent() const +{ + return false; +} + +bool Event::isWheelEvent() const +{ + return false; +} + +bool Event::isMessageEvent() const +{ + return false; +} + +bool Event::isBeforeTextInsertedEvent() const +{ + return false; +} + +bool Event::isOverflowEvent() const +{ + return false; +} + +bool Event::isPageTransitionEvent() const +{ + return false; +} + +bool Event::isPopStateEvent() const +{ + return false; +} + +bool Event::isProgressEvent() const +{ + return false; +} + +bool Event::isWebKitAnimationEvent() const +{ + return false; +} + +bool Event::isWebKitTransitionEvent() const +{ + return false; +} + +bool Event::isXMLHttpRequestProgressEvent() const +{ + return false; +} + +bool Event::isBeforeLoadEvent() const +{ + return false; +} + +bool Event::isHashChangeEvent() const +{ + return false; +} + +#if ENABLE(SVG) +bool Event::isSVGZoomEvent() const +{ + return false; +} +#endif + +#if ENABLE(DOM_STORAGE) +bool Event::isStorageEvent() const +{ + return false; +} +#endif + +#if ENABLE(INDEXED_DATABASE) +bool Event::isIDBErrorEvent() const +{ + return false; +} + +bool Event::isIDBSuccessEvent() const +{ + return false; +} +#endif + +#if ENABLE(WORKERS) +bool Event::isErrorEvent() const +{ + return false; +} +#endif + +#if ENABLE(TOUCH_EVENTS) +bool Event::isTouchEvent() const +{ + return false; +} +#endif + +#if ENABLE(DEVICE_ORIENTATION) +bool Event::isDeviceMotionEvent() const +{ + return false; +} + +bool Event::isDeviceOrientationEvent() const +{ + return false; +} +#endif + +#if ENABLE(WEB_AUDIO) +bool Event::isAudioProcessingEvent() const +{ + return false; +} +#endif + +#if ENABLE(INPUT_SPEECH) +bool Event::isSpeechInputEvent() const +{ + return false; +} +#endif + +bool Event::fromUserGesture() +{ + if (!UserGestureIndicator::processingUserGesture()) + return false; + + const AtomicString& type = this->type(); + return + // mouse events + type == eventNames().clickEvent || type == eventNames().mousedownEvent + || type == eventNames().mouseupEvent || type == eventNames().dblclickEvent + // keyboard events + || type == eventNames().keydownEvent || type == eventNames().keypressEvent + || type == eventNames().keyupEvent +#if ENABLE(TOUCH_EVENTS) + // touch events + || type == eventNames().touchstartEvent || type == eventNames().touchmoveEvent + || type == eventNames().touchendEvent || type == eventNames().touchcancelEvent +#endif + // other accepted events + || type == eventNames().selectEvent || type == eventNames().changeEvent + || type == eventNames().focusEvent || type == eventNames().blurEvent + || type == eventNames().submitEvent; +} + +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/Source/WebCore/dom/Event.h b/Source/WebCore/dom/Event.h new file mode 100644 index 0000000..f05d36c --- /dev/null +++ b/Source/WebCore/dom/Event.h @@ -0,0 +1,199 @@ +/* + * 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, 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 Event_h +#define Event_h + +#include "DOMTimeStamp.h" +#include "EventTarget.h" +#include <wtf/RefCounted.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + + class Clipboard; + + 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 + }; + + static PassRefPtr<Event> create() + { + return adoptRef(new Event); + } + static PassRefPtr<Event> create(const AtomicString& type, bool canBubble, bool cancelable) + { + return adoptRef(new Event(type, canBubble, 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() const { return m_createTime; } + + void stopPropagation() { m_propagationStopped = true; } + void stopImmediatePropagation() { m_immediatePropagationStopped = 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 isCustomEvent() const; + virtual bool isUIEvent() const; + virtual bool isMouseEvent() const; + virtual bool isMutationEvent() const; + virtual bool isKeyboardEvent() const; + virtual bool isTextEvent() const; + virtual bool isCompositionEvent() const; + virtual bool isDragEvent() const; // a subset of mouse events + virtual bool isClipboardEvent() const; + virtual bool isMessageEvent() const; + virtual bool isWheelEvent() const; + virtual bool isBeforeTextInsertedEvent() const; + virtual bool isOverflowEvent() const; + virtual bool isPageTransitionEvent() const; + virtual bool isPopStateEvent() const; + virtual bool isProgressEvent() const; + virtual bool isXMLHttpRequestProgressEvent() const; + virtual bool isWebKitAnimationEvent() const; + virtual bool isWebKitTransitionEvent() const; + virtual bool isBeforeLoadEvent() const; + virtual bool isHashChangeEvent() const; +#if ENABLE(SVG) + virtual bool isSVGZoomEvent() const; +#endif +#if ENABLE(DOM_STORAGE) + virtual bool isStorageEvent() const; +#endif +#if ENABLE(INDEXED_DATABASE) + virtual bool isIDBErrorEvent() const; + virtual bool isIDBSuccessEvent() const; +#endif +#if ENABLE(WEB_AUDIO) + virtual bool isAudioProcessingEvent() const; +#endif +#if ENABLE(WORKERS) + virtual bool isErrorEvent() const; +#endif +#if ENABLE(TOUCH_EVENTS) + virtual bool isTouchEvent() const; +#endif +#if ENABLE(DEVICE_ORIENTATION) + virtual bool isDeviceMotionEvent() const; + virtual bool isDeviceOrientationEvent() const; +#endif +#if ENABLE(INPUT_SPEECH) + virtual bool isSpeechInputEvent() const; +#endif + bool fromUserGesture(); + + bool propagationStopped() const { return m_propagationStopped || m_immediatePropagationStopped; } + bool immediatePropagationStopped() const { return m_immediatePropagationStopped; } + + 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: + Event(); + Event(const AtomicString& type, bool canBubble, bool cancelable); + + virtual void receivedTarget(); + bool dispatched() const { return m_target; } + + private: + AtomicString m_type; + bool m_canBubble; + bool m_cancelable; + + bool m_propagationStopped; + bool m_immediatePropagationStopped; + bool m_defaultPrevented; + bool m_defaultHandled; + bool m_cancelBubble; + + unsigned short m_eventPhase; + EventTarget* m_currentTarget; + RefPtr<EventTarget> m_target; + DOMTimeStamp m_createTime; + + RefPtr<Event> m_underlyingEvent; + }; + +} // namespace WebCore + +#endif // Event_h diff --git a/Source/WebCore/dom/Event.idl b/Source/WebCore/dom/Event.idl new file mode 100644 index 0000000..b8b590f --- /dev/null +++ b/Source/WebCore/dom/Event.idl @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2006, 2007, 2009 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 [ + CustomToJS, + NoStaticTables, + Polymorphic + ] 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) || !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; + readonly attribute DOMTimeStamp timeStamp; + + void stopPropagation(); + void preventDefault(); + [OldStyleObjC] void initEvent(in DOMString eventTypeArg, + in boolean canBubbleArg, + in boolean cancelableArg); + + // DOM Level 3 Additions. + readonly attribute boolean defaultPrevented; + void stopImmediatePropagation(); + + // IE Extensions + readonly attribute EventTarget srcElement; + attribute boolean returnValue; + attribute boolean cancelBubble; + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + readonly attribute [Custom] Clipboard clipboardData; +#endif + +#if defined(LANGUAGE_CPP) && LANGUAGE_CPP + // Extra WebCore methods exposed to allow compile-time casting in C++ + boolean isMutationEvent(); + boolean isMouseEvent(); + boolean isUIEvent(); +#endif + + }; + +} diff --git a/Source/WebCore/dom/EventContext.cpp b/Source/WebCore/dom/EventContext.cpp new file mode 100644 index 0000000..2a5c521 --- /dev/null +++ b/Source/WebCore/dom/EventContext.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Google 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 "EventContext.h" + +#include "DOMWindow.h" +#include "Document.h" +#include "Event.h" +#include "Node.h" + +namespace WebCore { + +EventContext::EventContext(PassRefPtr<Node> node, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target) + : m_node(node) + , m_currentTarget(currentTarget) + , m_target(target) +{ +} + +void EventContext::handleLocalEvents(Event* event) const +{ + event->setTarget(m_target.get()); + event->setCurrentTarget(m_currentTarget.get()); + m_node->handleLocalEvents(event); +} + +} diff --git a/Source/WebCore/dom/EventContext.h b/Source/WebCore/dom/EventContext.h new file mode 100644 index 0000000..9bab9d4 --- /dev/null +++ b/Source/WebCore/dom/EventContext.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Google 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 EventContext_h +#define EventContext_h + +#include <wtf/RefPtr.h> + +namespace WebCore { + +class EventTarget; +class Event; +class Node; + +class EventContext { +public: + // FIXME: Use ContainerNode instead of Node. + EventContext(PassRefPtr<Node>, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target); + + Node* node() const; + EventTarget* target() const; + void handleLocalEvents(Event*) const; + +private: + RefPtr<Node> m_node; + RefPtr<EventTarget> m_currentTarget; + RefPtr<EventTarget> m_target; +}; + +inline Node* EventContext::node() const +{ + return m_node.get(); +} + +inline EventTarget* EventContext::target() const +{ + return m_target.get(); +} + +} + +#endif // EventContext_h diff --git a/Source/WebCore/dom/EventException.h b/Source/WebCore/dom/EventException.h new file mode 100644 index 0000000..dd37ba5 --- /dev/null +++ b/Source/WebCore/dom/EventException.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. + * 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: + static PassRefPtr<EventException> create(const ExceptionCodeDescription& description) + { + return adoptRef(new EventException(description)); + } + + static const int EventExceptionOffset = 100; + static const int EventExceptionMax = 199; + + enum EventExceptionCode { + UNSPECIFIED_EVENT_TYPE_ERR = EventExceptionOffset + }; + + private: + EventException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + }; + +} // namespace WebCore + +#endif // EventException_h diff --git a/Source/WebCore/dom/EventException.idl b/Source/WebCore/dom/EventException.idl new file mode 100644 index 0000000..c8f2bde --- /dev/null +++ b/Source/WebCore/dom/EventException.idl @@ -0,0 +1,51 @@ +/* + * 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 [ + NoStaticTables, + DontCheckEnums + ] EventException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + // Override in a Mozilla compatible format + [DontEnum] DOMString toString(); +#endif + + // EventExceptionCode + const unsigned short UNSPECIFIED_EVENT_TYPE_ERR = 0; + + }; + +} diff --git a/Source/WebCore/dom/EventListener.h b/Source/WebCore/dom/EventListener.h new file mode 100644 index 0000000..96d0beb --- /dev/null +++ b/Source/WebCore/dom/EventListener.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2006, 2008, 2009 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 EventListener_h +#define EventListener_h + +#include <wtf/RefCounted.h> + +namespace JSC { + class JSObject; + class MarkStack; +} + +namespace WebCore { + + class ScriptExecutionContext; + class Event; + + class EventListener : public RefCounted<EventListener> { + public: + enum Type { + JSEventListenerType, + ImageEventListenerType, + InspectorDOMAgentType, + InspectorDOMStorageResourceType, + ObjCEventListenerType, + CPPEventListenerType, + ConditionEventListenerType, + GObjectEventListenerType, + NativeEventListenerType + }; + + virtual ~EventListener() { } + virtual bool operator==(const EventListener&) = 0; + virtual void handleEvent(ScriptExecutionContext*, Event*) = 0; + virtual bool wasCreatedFromMarkup() const { return false; } + +#if USE(JSC) + virtual void markJSFunction(JSC::MarkStack&) { } + virtual void invalidateJSFunction(JSC::JSObject*) { } +#endif + + bool isAttribute() const { return virtualisAttribute(); } + Type type() const { return m_type; } + + protected: + EventListener(Type type) + : m_type(type) + { + } + + private: + virtual bool virtualisAttribute() const { return false; } + + Type m_type; + }; + +} + +#endif diff --git a/Source/WebCore/dom/EventListener.idl b/Source/WebCore/dom/EventListener.idl new file mode 100644 index 0000000..023777b --- /dev/null +++ b/Source/WebCore/dom/EventListener.idl @@ -0,0 +1,33 @@ +/* + * 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 [ + NoStaticTables, + ObjCProtocol, + PureInterface, + OmitConstructor + ] EventListener { + void handleEvent(in Event evt); + }; + +} diff --git a/Source/WebCore/dom/EventNames.cpp b/Source/WebCore/dom/EventNames.cpp new file mode 100644 index 0000000..900b8ef --- /dev/null +++ b/Source/WebCore/dom/EventNames.cpp @@ -0,0 +1,34 @@ +/* + * 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" +#include "EventNames.h" + +namespace WebCore { + +#define INITIALIZE_EVENT_NAME(name) \ + , name##Event(#name) +EventNames::EventNames() + : dummy(0) +DOM_EVENT_NAMES_FOR_EACH(INITIALIZE_EVENT_NAME) +{ +} + +} diff --git a/Source/WebCore/dom/EventNames.h b/Source/WebCore/dom/EventNames.h new file mode 100644 index 0000000..d19f043 --- /dev/null +++ b/Source/WebCore/dom/EventNames.h @@ -0,0 +1,204 @@ +/* + * 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 "ThreadGlobalData.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +#define DOM_EVENT_NAMES_FOR_EACH(macro) \ + \ + macro(abort) \ + macro(beforecopy) \ + macro(beforecut) \ + macro(beforeload) \ + macro(beforepaste) \ + macro(beforeprocess) \ + macro(beforeunload) \ + macro(blur) \ + macro(cached) \ + macro(change) \ + macro(checking) \ + macro(click) \ + macro(close) \ + macro(complete) \ + macro(compositionend) \ + macro(compositionstart) \ + macro(compositionupdate) \ + macro(connect) \ + macro(contextmenu) \ + macro(copy) \ + macro(cut) \ + macro(dblclick) \ + macro(devicemotion) \ + macro(deviceorientation) \ + macro(display) \ + macro(downloading) \ + macro(drag) \ + macro(dragend) \ + macro(dragenter) \ + macro(dragleave) \ + macro(dragover) \ + macro(dragstart) \ + macro(drop) \ + macro(error) \ + macro(focus) \ + macro(focusin) \ + macro(focusout) \ + macro(hashchange) \ + macro(input) \ + macro(invalid) \ + macro(keydown) \ + macro(keypress) \ + macro(keyup) \ + macro(load) \ + macro(loadstart) \ + macro(message) \ + macro(mousedown) \ + macro(mousemove) \ + macro(mouseout) \ + macro(mouseover) \ + macro(mouseup) \ + macro(mousewheel) \ + macro(noupdate) \ + macro(obsolete) \ + macro(offline) \ + macro(online) \ + macro(open) \ + macro(overflowchanged) \ + macro(pagehide) \ + macro(pageshow) \ + macro(paste) \ + macro(popstate) \ + macro(readystatechange) \ + macro(reset) \ + macro(resize) \ + macro(scroll) \ + macro(search) \ + macro(select) \ + macro(selectstart) \ + macro(storage) \ + macro(submit) \ + macro(textInput) \ + macro(unload) \ + macro(updateready) \ + macro(write) \ + macro(writeend) \ + macro(writestart) \ + macro(zoom) \ + \ + macro(DOMActivate) \ + macro(DOMFocusIn) \ + macro(DOMFocusOut) \ + macro(DOMAttrModified) \ + macro(DOMCharacterDataModified) \ + macro(DOMNodeInserted) \ + macro(DOMNodeInsertedIntoDocument) \ + macro(DOMNodeRemoved) \ + macro(DOMNodeRemovedFromDocument) \ + macro(DOMSubtreeModified) \ + macro(DOMContentLoaded) \ + \ + macro(webkitBeforeTextInserted) \ + macro(webkitEditableContentChanged) \ + \ + macro(canplay) \ + macro(canplaythrough) \ + macro(durationchange) \ + macro(emptied) \ + macro(ended) \ + macro(loadeddata) \ + macro(loadedmetadata) \ + macro(pause) \ + macro(play) \ + macro(playing) \ + macro(ratechange) \ + macro(seeked) \ + macro(seeking) \ + macro(timeupdate) \ + macro(volumechange) \ + macro(waiting) \ + \ + macro(webkitbeginfullscreen) \ + macro(webkitendfullscreen) \ + \ + macro(progress) \ + macro(stalled) \ + macro(suspend) \ + \ + macro(webkitAnimationEnd) \ + macro(webkitAnimationStart) \ + macro(webkitAnimationIteration) \ + \ + macro(webkitTransitionEnd) \ + \ + macro(orientationchange) \ + \ + macro(timeout) \ + \ + macro(touchstart) \ + macro(touchmove) \ + macro(touchend) \ + macro(touchcancel) \ +/* #if PLATFORM(ANDROID) */ \ + macro(touchlongpress) \ + macro(touchdoubletap) \ +/* #endif */ \ + \ + macro(success) \ + \ + macro(loadend) \ + \ + macro(webkitfullscreenchange) \ + \ + macro(webkitspeechchange) \ + \ + macro(webglcontextlost) \ + macro(webglcontextrestored) \ + macro(webglcontextcreationerror) \ + \ + macro(audioprocess) \ + \ +// end of DOM_EVENT_NAMES_FOR_EACH + + class EventNames : public Noncopyable { + int dummy; // Needed to make initialization macro work. + // Private to prevent accidental call to EventNames() instead of eventNames() + EventNames(); + friend class ThreadGlobalData; + + public: + #define DOM_EVENT_NAMES_DECLARE(name) AtomicString name##Event; + DOM_EVENT_NAMES_FOR_EACH(DOM_EVENT_NAMES_DECLARE) + #undef DOM_EVENT_NAMES_DECLARE + }; + + inline EventNames& eventNames() + { + return threadGlobalData().eventNames(); + } + +} + +#endif diff --git a/Source/WebCore/dom/EventQueue.cpp b/Source/WebCore/dom/EventQueue.cpp new file mode 100644 index 0000000..27cd802 --- /dev/null +++ b/Source/WebCore/dom/EventQueue.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 Google 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 "EventQueue.h" + +#include "DOMWindow.h" +#include "Document.h" +#include "Event.h" +#include "EventNames.h" + +namespace WebCore { + +EventQueue::EventQueue() + : m_pendingEventTimer(this, &EventQueue::pendingEventTimerFired) +{ +} + +void EventQueue::enqueueEvent(PassRefPtr<Event> event) +{ + ASSERT(event->target()->toNode() || event->target()->toDOMWindow()); + m_queuedEvents.append(event); + + if (!m_pendingEventTimer.isActive()) + m_pendingEventTimer.startOneShot(0); +} + +void EventQueue::pendingEventTimerFired(Timer<EventQueue>*) +{ + ASSERT(!m_pendingEventTimer.isActive()); + + Vector<RefPtr<Event> > queuedEvents; + queuedEvents.swap(m_queuedEvents); + + for (size_t i = 0; i < queuedEvents.size(); i++) + dispatchEvent(queuedEvents[i].release()); +} + +void EventQueue::dispatchEvent(PassRefPtr<Event> event) +{ + EventTarget* eventTarget = event->target(); + if (eventTarget->toNode()) + eventTarget->dispatchEvent(event); + else if (eventTarget->toDOMWindow()) + eventTarget->toDOMWindow()->dispatchEvent(event, 0); + else + ASSERT_NOT_REACHED(); +} + +} diff --git a/Source/WebCore/dom/EventQueue.h b/Source/WebCore/dom/EventQueue.h new file mode 100644 index 0000000..8dd7ec9 --- /dev/null +++ b/Source/WebCore/dom/EventQueue.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Google 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 EventQueue_h +#define EventQueue_h + +#include "Timer.h" +#include <wtf/Noncopyable.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Event; +class Node; + +class EventQueue { + WTF_MAKE_NONCOPYABLE(EventQueue); + +public: + EventQueue(); + + void enqueueEvent(PassRefPtr<Event>); + +private: + void pendingEventTimerFired(Timer<EventQueue>*); + void dispatchEvent(PassRefPtr<Event>); + + Timer<EventQueue> m_pendingEventTimer; + Vector<RefPtr<Event> > m_queuedEvents; +}; + +} + +#endif // EventQueue_h diff --git a/Source/WebCore/dom/EventTarget.cpp b/Source/WebCore/dom/EventTarget.cpp new file mode 100644 index 0000000..5f2f8a7 --- /dev/null +++ b/Source/WebCore/dom/EventTarget.cpp @@ -0,0 +1,376 @@ +/* + * 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, 2008 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 "Event.h" +#include "EventException.h" +#include <wtf/StdLibExtras.h> + +using namespace WTF; + +namespace WebCore { + +#ifndef NDEBUG +static int gEventDispatchForbidden = 0; + +void forbidEventDispatch() +{ + if (!isMainThread()) + return; + ++gEventDispatchForbidden; +} + +void allowEventDispatch() +{ + if (!isMainThread()) + return; + if (gEventDispatchForbidden > 0) + --gEventDispatchForbidden; +} + +bool eventDispatchForbidden() +{ + if (!isMainThread()) + return false; + return gEventDispatchForbidden > 0; +} +#endif // NDEBUG + +EventTargetData::EventTargetData() +{ +} + +EventTargetData::~EventTargetData() +{ + deleteAllValues(eventListenerMap); +} + +EventTarget::~EventTarget() +{ +} + +EventSource* EventTarget::toEventSource() +{ + return 0; +} + +Node* EventTarget::toNode() +{ + return 0; +} + +DOMWindow* EventTarget::toDOMWindow() +{ + return 0; +} + +XMLHttpRequest* EventTarget::toXMLHttpRequest() +{ + return 0; +} + +XMLHttpRequestUpload* EventTarget::toXMLHttpRequestUpload() +{ + return 0; +} + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +DOMApplicationCache* EventTarget::toDOMApplicationCache() +{ + return 0; +} +#endif + +#if ENABLE(SVG) +SVGElementInstance* EventTarget::toSVGElementInstance() +{ + return 0; +} +#endif + +#if ENABLE(WEB_AUDIO) +JavaScriptAudioNode* EventTarget::toJavaScriptAudioNode() +{ + return 0; +} +#endif + +#if ENABLE(WEB_SOCKETS) +WebSocket* EventTarget::toWebSocket() +{ + return 0; +} +#endif + +MessagePort* EventTarget::toMessagePort() +{ + return 0; +} + +#if ENABLE(WORKERS) +Worker* EventTarget::toWorker() +{ + return 0; +} + +DedicatedWorkerContext* EventTarget::toDedicatedWorkerContext() +{ + return 0; +} +#endif + +#if ENABLE(SHARED_WORKERS) +SharedWorker* EventTarget::toSharedWorker() +{ + return 0; +} +SharedWorkerContext* EventTarget::toSharedWorkerContext() +{ + return 0; +} +#endif + +#if ENABLE(NOTIFICATIONS) +Notification* EventTarget::toNotification() +{ + return 0; +} +#endif + +#if ENABLE(BLOB) +FileReader* EventTarget::toFileReader() +{ + return 0; +} +#endif +#if ENABLE(FILE_SYSTEM) +FileWriter* EventTarget::toFileWriter() +{ + return 0; +} +#endif + +#if ENABLE(INDEXED_DATABASE) +IDBRequest* EventTarget::toIDBRequest() +{ + return 0; +} +IDBTransaction* EventTarget::toIDBTransaction() +{ + return 0; +} +#endif + +bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) +{ + EventTargetData* d = ensureEventTargetData(); + + pair<EventListenerMap::iterator, bool> result = d->eventListenerMap.add(eventType, 0); + EventListenerVector*& entry = result.first->second; + const bool isNewEntry = result.second; + if (isNewEntry) + entry = new EventListenerVector(); + + RegisteredEventListener registeredListener(listener, useCapture); + if (!isNewEntry) { + if (entry->find(registeredListener) != notFound) // duplicate listener + return false; + } + + entry->append(registeredListener); + return true; +} + +bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) +{ + EventTargetData* d = eventTargetData(); + if (!d) + return false; + + EventListenerMap::iterator result = d->eventListenerMap.find(eventType); + if (result == d->eventListenerMap.end()) + return false; + EventListenerVector* entry = result->second; + + RegisteredEventListener registeredListener(listener, useCapture); + size_t index = entry->find(registeredListener); + if (index == notFound) + return false; + + entry->remove(index); + if (entry->isEmpty()) { + delete entry; + d->eventListenerMap.remove(result); + } + + // Notify firing events planning to invoke the listener at 'index' that + // they have one less listener to invoke. + for (size_t i = 0; i < d->firingEventIterators.size(); ++i) { + if (eventType != d->firingEventIterators[i].eventType) + continue; + + if (index >= d->firingEventIterators[i].end) + continue; + + --d->firingEventIterators[i].end; + if (index <= d->firingEventIterators[i].iterator) + --d->firingEventIterators[i].iterator; + } + + return true; +} + +bool EventTarget::setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener) +{ + clearAttributeEventListener(eventType); + if (!listener) + return false; + return addEventListener(eventType, listener, false); +} + +EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType) +{ + const EventListenerVector& entry = getEventListeners(eventType); + for (size_t i = 0; i < entry.size(); ++i) { + if (entry[i].listener->isAttribute()) + return entry[i].listener.get(); + } + return 0; +} + +bool EventTarget::clearAttributeEventListener(const AtomicString& eventType) +{ + EventListener* listener = getAttributeEventListener(eventType); + if (!listener) + return false; + return removeEventListener(eventType, listener, false); +} + +bool EventTarget::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) +{ + if (!event || event->type().isEmpty()) { + ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + return false; + } + + if (!scriptExecutionContext()) + return false; + + return dispatchEvent(event); +} + +bool EventTarget::dispatchEvent(PassRefPtr<Event> event) +{ + event->setTarget(this); + event->setCurrentTarget(this); + event->setEventPhase(Event::AT_TARGET); + return fireEventListeners(event.get()); +} + +bool EventTarget::fireEventListeners(Event* event) +{ + ASSERT(!eventDispatchForbidden()); + ASSERT(event && !event->type().isEmpty()); + + EventTargetData* d = eventTargetData(); + if (!d) + return true; + + EventListenerMap::iterator result = d->eventListenerMap.find(event->type()); + if (result != d->eventListenerMap.end()) + fireEventListeners(event, d, *result->second); + + return !event->defaultPrevented(); +} + +void EventTarget::fireEventListeners(Event* event, EventTargetData* d, EventListenerVector& entry) +{ + RefPtr<EventTarget> protect = this; + + // Fire all listeners registered for this event. Don't fire listeners removed + // during event dispatch. Also, don't fire event listeners added during event + // dispatch. Conveniently, all new event listeners will be added after 'end', + // so iterating to 'end' naturally excludes new event listeners. + + size_t i = 0; + size_t end = entry.size(); + d->firingEventIterators.append(FiringEventIterator(event->type(), i, end)); + for ( ; i < end; ++i) { + RegisteredEventListener& registeredListener = entry[i]; + if (event->eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture) + continue; + if (event->eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture) + continue; + + // If stopImmediatePropagation has been called, we just break out immediately, without + // handling any more events on this target. + if (event->immediatePropagationStopped()) + break; + + // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling + // event listeners, even though that violates some versions of the DOM spec. + registeredListener.listener->handleEvent(scriptExecutionContext(), event); + } + d->firingEventIterators.removeLast(); +} + +const EventListenerVector& EventTarget::getEventListeners(const AtomicString& eventType) +{ + DEFINE_STATIC_LOCAL(EventListenerVector, emptyVector, ()); + + EventTargetData* d = eventTargetData(); + if (!d) + return emptyVector; + EventListenerMap::iterator it = d->eventListenerMap.find(eventType); + if (it == d->eventListenerMap.end()) + return emptyVector; + return *it->second; +} + +void EventTarget::removeAllEventListeners() +{ + EventTargetData* d = eventTargetData(); + if (!d) + return; + deleteAllValues(d->eventListenerMap); + d->eventListenerMap.clear(); + + // Notify firing events planning to invoke the listener at 'index' that + // they have one less listener to invoke. + for (size_t i = 0; i < d->firingEventIterators.size(); ++i) { + d->firingEventIterators[i].iterator = 0; + d->firingEventIterators[i].end = 0; + } +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/EventTarget.h b/Source/WebCore/dom/EventTarget.h new file mode 100644 index 0000000..ddcb663 --- /dev/null +++ b/Source/WebCore/dom/EventTarget.h @@ -0,0 +1,272 @@ +/* + * 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 Alexey Proskuryakov (ap@webkit.org) + * (C) 2007, 2008 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 "EventNames.h" +#include "RegisteredEventListener.h" +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/text/AtomicStringHash.h> + +namespace WebCore { + + class AbstractWorker; + class DedicatedWorkerContext; + class DOMApplicationCache; + class DOMWindow; + class Event; + class EventListener; + class EventSource; + class FileReader; + class FileWriter; + class IDBRequest; + class IDBTransaction; + class JavaScriptAudioNode; + class MessagePort; + class Node; + class Notification; + class SVGElementInstance; + class ScriptExecutionContext; + class SharedWorker; + class SharedWorkerContext; + class WebSocket; + class Worker; + class XMLHttpRequest; + class XMLHttpRequestUpload; + + typedef int ExceptionCode; + + struct FiringEventIterator { + FiringEventIterator(const AtomicString& eventType, size_t& iterator, size_t& end) + : eventType(eventType) + , iterator(iterator) + , end(end) + { + } + + const AtomicString& eventType; + size_t& iterator; + size_t& end; + }; + typedef Vector<FiringEventIterator, 1> FiringEventIteratorVector; + + typedef Vector<RegisteredEventListener, 1> EventListenerVector; + typedef HashMap<AtomicString, EventListenerVector*> EventListenerMap; + + struct EventTargetData : Noncopyable { + EventTargetData(); + ~EventTargetData(); + + EventListenerMap eventListenerMap; + FiringEventIteratorVector firingEventIterators; + }; + + class EventTarget { + public: + void ref() { refEventTarget(); } + void deref() { derefEventTarget(); } + + virtual EventSource* toEventSource(); + virtual MessagePort* toMessagePort(); + virtual Node* toNode(); + virtual DOMWindow* toDOMWindow(); + virtual XMLHttpRequest* toXMLHttpRequest(); + virtual XMLHttpRequestUpload* toXMLHttpRequestUpload(); +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + virtual DOMApplicationCache* toDOMApplicationCache(); +#endif +#if ENABLE(SVG) + virtual SVGElementInstance* toSVGElementInstance(); +#endif +#if ENABLE(WORKERS) + virtual Worker* toWorker(); + virtual DedicatedWorkerContext* toDedicatedWorkerContext(); +#endif +#if ENABLE(SHARED_WORKERS) + virtual SharedWorker* toSharedWorker(); + virtual SharedWorkerContext* toSharedWorkerContext(); +#endif + +#if ENABLE(WEB_AUDIO) + virtual JavaScriptAudioNode* toJavaScriptAudioNode(); +#endif + +#if ENABLE(WEB_SOCKETS) + virtual WebSocket* toWebSocket(); +#endif + +#if ENABLE(NOTIFICATIONS) + virtual Notification* toNotification(); +#endif +#if ENABLE(BLOB) + virtual FileReader* toFileReader(); +#endif +#if ENABLE(FILE_SYSTEM) + virtual FileWriter* toFileWriter(); +#endif + +#if ENABLE(INDEXED_DATABASE) + virtual IDBRequest* toIDBRequest(); + virtual IDBTransaction* toIDBTransaction(); +#endif + + virtual ScriptExecutionContext* scriptExecutionContext() const = 0; + + virtual bool addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + virtual bool removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + virtual void removeAllEventListeners(); + virtual bool dispatchEvent(PassRefPtr<Event>); + bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); // DOM API + + // Used for legacy "onEvent" attribute APIs. + bool setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener>); + bool clearAttributeEventListener(const AtomicString& eventType); + EventListener* getAttributeEventListener(const AtomicString& eventType); + + bool hasEventListeners(); + bool hasEventListeners(const AtomicString& eventType); + const EventListenerVector& getEventListeners(const AtomicString& eventType); + + bool fireEventListeners(Event*); + bool isFiringEventListeners(); + +#if USE(JSC) + void markJSEventListeners(JSC::MarkStack&); + void invalidateJSEventListeners(JSC::JSObject*); +#endif + + protected: + virtual ~EventTarget(); + + virtual EventTargetData* eventTargetData() = 0; + virtual EventTargetData* ensureEventTargetData() = 0; + + private: + virtual void refEventTarget() = 0; + virtual void derefEventTarget() = 0; + + void fireEventListeners(Event*, EventTargetData*, EventListenerVector&); + }; + + // FIXME: These macros should be split into separate DEFINE and DECLARE + // macros to avoid causing so many header includes. + #define DEFINE_ATTRIBUTE_EVENT_LISTENER(attribute) \ + EventListener* on##attribute() { return getAttributeEventListener(eventNames().attribute##Event); } \ + void setOn##attribute(PassRefPtr<EventListener> listener) { setAttributeEventListener(eventNames().attribute##Event, listener); } \ + + #define DECLARE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(attribute) \ + virtual EventListener* on##attribute(); \ + virtual void setOn##attribute(PassRefPtr<EventListener> listener); \ + + #define DEFINE_VIRTUAL_ATTRIBUTE_EVENT_LISTENER(type, attribute) \ + EventListener* type::on##attribute() { return getAttributeEventListener(eventNames().attribute##Event); } \ + void type::setOn##attribute(PassRefPtr<EventListener> listener) { setAttributeEventListener(eventNames().attribute##Event, listener); } \ + + #define DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(attribute) \ + EventListener* on##attribute() { return document()->getWindowAttributeEventListener(eventNames().attribute##Event); } \ + void setOn##attribute(PassRefPtr<EventListener> listener) { document()->setWindowAttributeEventListener(eventNames().attribute##Event, listener); } \ + + #define DEFINE_MAPPED_ATTRIBUTE_EVENT_LISTENER(attribute, eventName) \ + EventListener* on##attribute() { return getAttributeEventListener(eventNames().eventName##Event); } \ + void setOn##attribute(PassRefPtr<EventListener> listener) { setAttributeEventListener(eventNames().eventName##Event, listener); } \ + + #define DEFINE_FORWARDING_ATTRIBUTE_EVENT_LISTENER(recipient, attribute) \ + EventListener* on##attribute() { return recipient ? recipient->getAttributeEventListener(eventNames().attribute##Event) : 0; } \ + void setOn##attribute(PassRefPtr<EventListener> listener) { if (recipient) recipient->setAttributeEventListener(eventNames().attribute##Event, listener); } \ + +#ifndef NDEBUG + void forbidEventDispatch(); + void allowEventDispatch(); + bool eventDispatchForbidden(); +#else + inline void forbidEventDispatch() { } + inline void allowEventDispatch() { } +#endif + +#if USE(JSC) + inline void EventTarget::markJSEventListeners(JSC::MarkStack& markStack) + { + EventTargetData* d = eventTargetData(); + if (!d) + return; + + EventListenerMap::iterator end = d->eventListenerMap.end(); + for (EventListenerMap::iterator it = d->eventListenerMap.begin(); it != end; ++it) { + EventListenerVector& entry = *it->second; + for (size_t i = 0; i < entry.size(); ++i) + entry[i].listener->markJSFunction(markStack); + } + } + + inline void EventTarget::invalidateJSEventListeners(JSC::JSObject* wrapper) + { + EventTargetData* d = eventTargetData(); + if (!d) + return; + + EventListenerMap::iterator end = d->eventListenerMap.end(); + for (EventListenerMap::iterator it = d->eventListenerMap.begin(); it != end; ++it) { + EventListenerVector& entry = *it->second; + for (size_t i = 0; i < entry.size(); ++i) + entry[i].listener->invalidateJSFunction(wrapper); + } + } +#endif + + inline bool EventTarget::isFiringEventListeners() + { + EventTargetData* d = eventTargetData(); + if (!d) + return false; + return d->firingEventIterators.size() != 0; + } + + inline bool EventTarget::hasEventListeners() + { + EventTargetData* d = eventTargetData(); + if (!d) + return false; + return !d->eventListenerMap.isEmpty(); + } + + inline bool EventTarget::hasEventListeners(const AtomicString& eventType) + { + EventTargetData* d = eventTargetData(); + if (!d) + return false; + return d->eventListenerMap.contains(eventType); + } + +} // namespace WebCore + +#endif // EventTarget_h diff --git a/Source/WebCore/dom/EventTarget.idl b/Source/WebCore/dom/EventTarget.idl new file mode 100644 index 0000000..0cdb6b3 --- /dev/null +++ b/Source/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, + OmitConstructor + ] EventTarget { + [OldStyleObjC] void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [OldStyleObjC] void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event event) + raises(EventException); + }; + +} diff --git a/Source/WebCore/dom/ExceptionBase.cpp b/Source/WebCore/dom/ExceptionBase.cpp new file mode 100644 index 0000000..edca706 --- /dev/null +++ b/Source/WebCore/dom/ExceptionBase.cpp @@ -0,0 +1,52 @@ +/* + * 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. + * 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" + +#include <wtf/text/StringConcatenate.h> + +namespace WebCore { + +ExceptionBase::ExceptionBase(const ExceptionCodeDescription& description) + : m_code(description.code) + , m_name(description.name) + , m_description(description.description) +{ + if (description.name) + m_message = makeString(description.name, ": ", description.typeName, " Exception ", String::number(description.code)); + else + m_message = makeString(description.typeName, " Exception ", String::number(description.code)); +} + +String ExceptionBase::toString() const +{ + return "Error: " + m_message; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ExceptionBase.h b/Source/WebCore/dom/ExceptionBase.h new file mode 100644 index 0000000..81e2d7f --- /dev/null +++ b/Source/WebCore/dom/ExceptionBase.h @@ -0,0 +1,59 @@ +/* + * 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: + unsigned short code() const { return m_code; } + String name() const { return m_name; } + String message() const { return m_message; } + String description() const { return m_description; } + + String toString() const; + + protected: + ExceptionBase(const ExceptionCodeDescription&); + + private: + unsigned short m_code; + String m_name; + String m_message; + String m_description; + }; + +} // namespace WebCore + +#endif // ExceptionBase_h diff --git a/Source/WebCore/dom/ExceptionCode.cpp b/Source/WebCore/dom/ExceptionCode.cpp new file mode 100644 index 0000000..7e7e0c9 --- /dev/null +++ b/Source/WebCore/dom/ExceptionCode.cpp @@ -0,0 +1,351 @@ +/* + * 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 "IDBDatabaseException.h" +#include "RangeException.h" +#include "XMLHttpRequestException.h" + +#if ENABLE(SVG) +#include "SVGException.h" +#endif + +#if ENABLE(XPATH) +#include "XPathException.h" +#endif + +#if ENABLE(DATABASE) +#include "SQLException.h" +#endif + +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) +#include "FileException.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", + "SECURITY_ERR", + "NETWORK_ERR", + "ABORT_ERR", + "URL_MISMATCH_ERR", + "QUOTA_EXCEEDED_ERR" +}; + +static const char* const exceptionDescriptions[] = { + "Index or size was negative, or greater than the allowed value.", + "The specified range of text did not fit into a DOMString.", + "A Node was inserted somewhere it doesn't belong.", + "A Node was used in a different document than the one that created it (that doesn't support it).", + "An invalid or illegal character was specified, such as in an XML name.", + "Data was specified for a Node which does not support data.", + "An attempt was made to modify an object where modifications are not allowed.", + "An attempt was made to reference a Node in a context where it does not exist.", + "The implementation did not support the requested type of object or operation.", + "An attempt was made to add an attribute that is already in use elsewhere.", + "An attempt was made to use an object that is not, or is no longer, usable.", + "An invalid or illegal string was specified.", + "An attempt was made to modify the type of the underlying object.", + "An attempt was made to create or change an object in a way which is incorrect with regard to namespaces.", + "A parameter or an operation was not supported by the underlying object.", + "A call to a method such as insertBefore or removeChild would make the Node invalid with respect to \"partial validity\", this exception would be raised and the operation would not be done.", + "The type of an object was incompatible with the expected type of the parameter associated to the object.", + "An attempt was made to break through the security policy of the user agent.", + // FIXME: Couldn't find a description in the HTML/DOM specifications for NETWORK_ERR, ABORT_ERR, URL_MISMATCH_ERR, and QUOTA_EXCEEDED_ERR + "A network error occured.", + "The user aborted a request.", + "A worker global scope represented an absolute URL that is not equal to the resulting absolute URL.", + "An attempt was made to add something to storage that exceeded the quota." +}; + +static const char* const rangeExceptionNames[] = { + "BAD_BOUNDARYPOINTS_ERR", + "INVALID_NODE_TYPE_ERR" +}; + +static const char* const rangeExceptionDescriptions[] = { + "The boundary-points of a Range did not meet specific requirements.", + "The container of an boundary-point of a Range was being set to either a node of an invalid type or a node with an ancestor of an invalid type." +}; + +static const char* const eventExceptionNames[] = { + "UNSPECIFIED_EVENT_TYPE_ERR" +}; + +static const char* const eventExceptionDescriptions[] = { + "The Event's type was not specified by initializing the event before the method was called." +}; + +static const char* const xmlHttpRequestExceptionNames[] = { + "NETWORK_ERR", + "ABORT_ERR" +}; + +static const char* const xmlHttpRequestExceptionDescriptions[] = { + "A network error occured in synchronous requests.", + "The user aborted a request in synchronous requests." +}; + +#if ENABLE(XPATH) +static const char* const xpathExceptionNames[] = { + "INVALID_EXPRESSION_ERR", + "TYPE_ERR" +}; + +static const char* const xpathExceptionDescriptions[] = { + "The expression had a syntax error or otherwise is not a legal expression according to the rules of the specific XPathEvaluator.", + "The expression could not be converted to return the specified type." +}; +#endif + +#if ENABLE(SVG) +static const char* const svgExceptionNames[] = { + "SVG_WRONG_TYPE_ERR", + "SVG_INVALID_VALUE_ERR", + "SVG_MATRIX_NOT_INVERTABLE" +}; + +static const char* const svgExceptionDescriptions[] = { + "An object of the wrong type was passed to an operation.", + "An invalid value was passed to an operation or assigned to an attribute.", + "An attempt was made to invert a matrix that is not invertible." +}; +#endif + +#if ENABLE(DATABASE) +static const char* const sqlExceptionNames[] = { + "UNKNOWN_ERR", + "DATABASE_ERR", + "VERSION_ERR", + "TOO_LARGE_ERR", + "QUOTA_ERR", + "SYNTAX_ERR", + "CONSTRAINT_ERR", + "TIMEOUT_ERR" +}; + +static const char* const sqlExceptionDescriptions[] = { + "The operation failed for reasons unrelated to the database.", + "The operation failed for some reason related to the database.", + "The actual database version did not match the expected version.", + "Data returned from the database is too large.", + "Quota was exceeded.", + "Invalid or unauthorized statement; or the number of arguments did not match the number of ? placeholders.", + "A constraint was violated.", + "A transaction lock could not be acquired in a reasonable time." +}; +#endif + +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) +static const char* const fileExceptionNames[] = { + "NOT_FOUND_ERR", + "SECURITY_ERR", + "ABORT_ERR", + "NOT_READABLE_ERR", + "ENCODING_ERR", + "NO_MODIFICATION_ALLOWED_ERR", + "INVALID_STATE_ERR", + "SYNTAX_ERR", + "INVALID_MODIFICATION_ERR", + "QUOTA_EXCEEDED_ERR", + "TYPE_MISMATCH_ERR", + "PATH_EXISTS_ERR" +}; + +static const char* const fileExceptionDescriptions[] = { + "A requested file or directory could not be found at the time an operation was processed.", + "It was determined that certain files are unsafe for access within a Web application, or that too many calls are being made on file resources.", + "An ongoing operation was aborted, typically with a call to abort().", + "The requested file could not be read, typically due to permission problems that have occured after a reference to a file was acquired.", + "A URI supplied to the API was malformed, or the resulting Data URL has exceeded the URL length limitations for Data URLs.", + "An attempt was made to write to a file or directory which could not be modified due to the state of the underlying filesystem.", + "An operation that depends on state cached in an interface object was made but the state had changed since it was read from disk.", + "An invalid or unsupported argument was given, like an invalid line ending specifier.", + "The modification request was illegal.", + "The operation failed because it would cause the application to exceed its storage quota.", + "The path supplied exists, but was not an entry of requested type.", + "An attempt was made to create a file or directory where an element already exists." +}; +#endif + +#if ENABLE(INDEXED_DATABASE) +static const char* const idbDatabaseExceptionNames[] = { + "UNKNOWN_ERR", + "NON_TRANSIENT_ERR", + "NOT_FOUND_ERR", + "CONSTRAINT_ERR", + "DATA_ERR", + "NOT_ALLOWED_ERR", + "SERIAL_ERR", + "RECOVERABLE_ERR", + "TRANSIENT_ERR", + "TIMEOUT_ERR", + "DEADLOCK_ERR" +}; + +static const char* const idbDatabaseExceptionDescriptions[] = { + "An unknown error occurred within Indexed Database.", + "NON_TRANSIENT_ERR", // FIXME: Write a better message if it's ever possible this is thrown. + "The name supplied does not match any existing item.", + "The request cannot be completed due to a failed constraint.", + "The data provided does not meet the requirements of the function.", + "This function is not allowed to be called in such a context.", + "The data supplied cannot be serialized according to the structured cloning algorithm.", + "RECOVERABLE_ERR", // FIXME: This isn't even used. + "TRANSIENT_ERR", // FIXME: This isn't even used. + "TIMEOUT_ERR", // This can't be thrown. + "DEADLOCK_ERR" // This can't be thrown. +}; +#endif + +void getExceptionCodeDescription(ExceptionCode ec, ExceptionCodeDescription& description) +{ + ASSERT(ec); + + const char* typeName; + int code = ec; + const char* const* nameTable; + const char* const* descriptionTable; + int nameTableSize; + int nameTableOffset; + ExceptionType type; + + if (code >= RangeException::RangeExceptionOffset && code <= RangeException::RangeExceptionMax) { + type = RangeExceptionType; + typeName = "DOM Range"; + code -= RangeException::RangeExceptionOffset; + nameTable = rangeExceptionNames; + descriptionTable = rangeExceptionDescriptions; + nameTableSize = WTF_ARRAY_LENGTH(rangeExceptionNames); + nameTableOffset = RangeException::BAD_BOUNDARYPOINTS_ERR; + } else if (code >= EventException::EventExceptionOffset && code <= EventException::EventExceptionMax) { + type = EventExceptionType; + typeName = "DOM Events"; + code -= EventException::EventExceptionOffset; + nameTable = eventExceptionNames; + descriptionTable = eventExceptionDescriptions; + nameTableSize = WTF_ARRAY_LENGTH(eventExceptionNames); + nameTableOffset = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + } else if (code >= XMLHttpRequestException::XMLHttpRequestExceptionOffset && code <= XMLHttpRequestException::XMLHttpRequestExceptionMax) { + type = XMLHttpRequestExceptionType; + typeName = "XMLHttpRequest"; + code -= XMLHttpRequestException::XMLHttpRequestExceptionOffset; + nameTable = xmlHttpRequestExceptionNames; + descriptionTable = xmlHttpRequestExceptionDescriptions; + nameTableSize = WTF_ARRAY_LENGTH(xmlHttpRequestExceptionNames); + // 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; + descriptionTable = xpathExceptionDescriptions; + nameTableSize = WTF_ARRAY_LENGTH(xpathExceptionNames); + // 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; + descriptionTable = svgExceptionDescriptions; + nameTableSize = WTF_ARRAY_LENGTH(svgExceptionNames); + nameTableOffset = SVGException::SVG_WRONG_TYPE_ERR; +#endif +#if ENABLE(DATABASE) + } else if (code >= SQLException::SQLExceptionOffset && code <= SQLException::SQLExceptionMax) { + type = SQLExceptionType; + typeName = "DOM SQL"; + code -= SQLException::SQLExceptionOffset; + nameTable = sqlExceptionNames; + descriptionTable = sqlExceptionDescriptions; + nameTableSize = WTF_ARRAY_LENGTH(sqlExceptionNames); + nameTableOffset = SQLException::UNKNOWN_ERR; +#endif +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) + } else if (code >= FileException::FileExceptionOffset && code <= FileException::FileExceptionMax) { + type = FileExceptionType; + typeName = "DOM File"; + code -= FileException::FileExceptionOffset; + nameTable = fileExceptionNames; + descriptionTable = fileExceptionDescriptions; + nameTableSize = WTF_ARRAY_LENGTH(fileExceptionNames); + nameTableOffset = FileException::NOT_FOUND_ERR; +#endif +#if ENABLE(INDEXED_DATABASE) + } else if (code >= IDBDatabaseException::IDBDatabaseExceptionOffset && code <= IDBDatabaseException::IDBDatabaseExceptionMax) { + type = IDBDatabaseExceptionType; + typeName = "DOM IDBDatabase"; + code -= IDBDatabaseException::IDBDatabaseExceptionOffset; + nameTable = idbDatabaseExceptionNames; + descriptionTable = idbDatabaseExceptionDescriptions; + nameTableSize = WTF_ARRAY_LENGTH(idbDatabaseExceptionNames); + nameTableOffset = IDBDatabaseException::UNKNOWN_ERR; +#endif + } else { + type = DOMExceptionType; + typeName = "DOM"; + nameTable = exceptionNames; + descriptionTable = exceptionDescriptions; + nameTableSize = WTF_ARRAY_LENGTH(exceptionNames); + nameTableOffset = INDEX_SIZE_ERR; + } + + description.typeName = typeName; + description.name = (ec >= nameTableOffset && ec - nameTableOffset < nameTableSize) ? nameTable[ec - nameTableOffset] : 0; + description.description = (ec >= nameTableOffset && ec - nameTableOffset < nameTableSize) ? descriptionTable[ec - nameTableOffset] : 0; + description.code = code; + description.type = type; + + // All exceptions used in the DOM code should have names. + ASSERT(description.name); + ASSERT(description.description); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ExceptionCode.h b/Source/WebCore/dom/ExceptionCode.h new file mode 100644 index 0000000..dd976c7 --- /dev/null +++ b/Source/WebCore/dom/ExceptionCode.h @@ -0,0 +1,104 @@ +/* + * 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, + + // XMLHttpRequest extension: + SECURITY_ERR = 18, + + // Others introduced in HTML5: + NETWORK_ERR = 19, + ABORT_ERR = 20, + URL_MISMATCH_ERR = 21, + QUOTA_EXCEEDED_ERR = 22, + + // Introduced in File API: + // http://www.w3.org/TR/file-upload/#dfn-fileerror +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) + NOT_READABLE_ERR = 24, + ENCODING_ERR = 26, +#endif + }; + + enum ExceptionType { + DOMExceptionType, + RangeExceptionType, + EventExceptionType, + XMLHttpRequestExceptionType +#if ENABLE(XPATH) + , XPathExceptionType +#endif +#if ENABLE(SVG) + , SVGExceptionType +#endif +#if ENABLE(DATABASE) + , SQLExceptionType +#endif +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) + , FileExceptionType +#endif +#if ENABLE(INDEXED_DATABASE) + , IDBDatabaseExceptionType +#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 + const char* description; // exception description, intended for use in exception strings; more readable explanation of error + 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/Source/WebCore/dom/FragmentScriptingPermission.h b/Source/WebCore/dom/FragmentScriptingPermission.h new file mode 100644 index 0000000..6371011 --- /dev/null +++ b/Source/WebCore/dom/FragmentScriptingPermission.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Google, 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 FragmentScriptingPermission_h +#define FragmentScriptingPermission_h + +namespace WebCore { + +// FIXME: This enum is poorly named. It is used to remove script contents when +// generating DocumentFragments for paste in platform/*/Pasteboard.*. +enum FragmentScriptingPermission { + FragmentScriptingAllowed, + FragmentScriptingNotAllowed, +}; + +}; + +#endif // FragmentScriptingPermission_h diff --git a/Source/WebCore/dom/HashChangeEvent.h b/Source/WebCore/dom/HashChangeEvent.h new file mode 100644 index 0000000..f1fa6af --- /dev/null +++ b/Source/WebCore/dom/HashChangeEvent.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 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 HashChangeEvent_h +#define HashChangeEvent_h + +#include "Event.h" +#include "EventNames.h" + +namespace WebCore { + +class HashChangeEvent : public Event { +public: + virtual bool isHashChangeEvent() const { return true; } + + static PassRefPtr<HashChangeEvent> create(const String& oldURL, const String& newURL) + { + return adoptRef(new HashChangeEvent(oldURL, newURL)); + } + + void initHashChangeEvent(const AtomicString& eventType, bool canBubble, bool cancelable, const String& oldURL, const String& newURL) + { + if (dispatched()) + return; + + initEvent(eventType, canBubble, cancelable); + + m_oldURL = oldURL; + m_newURL = newURL; + } + + const String& oldURL() const { return m_oldURL; } + const String& newURL() const { return m_newURL; } + +private: + HashChangeEvent(const String& oldURL, const String& newURL) + : Event(eventNames().hashchangeEvent, false, false) + , m_oldURL(oldURL) + , m_newURL(newURL) + {} + + String m_oldURL; + String m_newURL; +}; + +} // namespace WebCore + +#endif // HashChangeEvent_h diff --git a/Source/WebCore/dom/HashChangeEvent.idl b/Source/WebCore/dom/HashChangeEvent.idl new file mode 100644 index 0000000..63073c4 --- /dev/null +++ b/Source/WebCore/dom/HashChangeEvent.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 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 http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#event-hashchange + interface [ + GenerateConstructor + ] HashChangeEvent : Event { + void initHashChangeEvent(in DOMString type, + in boolean canBubble, + in boolean cancelable, + in DOMString oldURL, + in DOMString newURL); + readonly attribute DOMString oldURL; + readonly attribute DOMString newURL; + }; + +} diff --git a/Source/WebCore/dom/IgnoreDestructiveWriteCountIncrementer.h b/Source/WebCore/dom/IgnoreDestructiveWriteCountIncrementer.h new file mode 100644 index 0000000..9d1835a --- /dev/null +++ b/Source/WebCore/dom/IgnoreDestructiveWriteCountIncrementer.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Google, 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 GOOGLE 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 GOOGLE 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 IgnoreDestructiveWriteCountIncrementer_h +#define IgnoreDestructiveWriteCountIncrementer_h + +#include "Document.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class IgnoreDestructiveWriteCountIncrementer : public Noncopyable { +public: + explicit IgnoreDestructiveWriteCountIncrementer(Document* document) + : m_count(document ? &document->m_ignoreDestructiveWriteCount : 0) + { + if (!m_count) + return; + ++(*m_count); + } + + ~IgnoreDestructiveWriteCountIncrementer() + { + if (!m_count) + return; + --(*m_count); + } + +private: + unsigned* m_count; +}; + +} + +#endif diff --git a/Source/WebCore/dom/InputElement.cpp b/Source/WebCore/dom/InputElement.cpp new file mode 100644 index 0000000..37211d8 --- /dev/null +++ b/Source/WebCore/dom/InputElement.cpp @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "InputElement.h" + +#include "BeforeTextInsertedEvent.h" + +#if ENABLE(WCSS) +#include "CSSPropertyNames.h" +#include "CSSRule.h" +#include "CSSRuleList.h" +#include "CSSStyleRule.h" +#include "CSSStyleSelector.h" +#endif + +#include "Attribute.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "Document.h" +#include "Event.h" +#include "EventNames.h" +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "Page.h" +#include "RenderTextControlSingleLine.h" +#include "SelectionController.h" +#include "TextIterator.h" + +#if ENABLE(WML) +#include "WMLInputElement.h" +#include "WMLNames.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +// FIXME: According to HTML4, the length attribute's value can be arbitrarily +// large. However, due to https://bugs.webkit.org/show_bug.cgi?id=14536 things +// get rather sluggish when a text field has a larger number of characters than +// this, even when just clicking in the text field. +const int InputElement::s_maximumLength = 524288; +const int InputElement::s_defaultSize = 20; + +void InputElement::dispatchFocusEvent(InputElement* inputElement, Element* element) +{ + if (!inputElement->isTextField()) + return; + + Document* document = element->document(); + if (inputElement->isPasswordField() && document->frame()) + document->setUseSecureKeyboardEntryWhenActive(true); +} + +void InputElement::dispatchBlurEvent(InputElement* inputElement, Element* element) +{ + if (!inputElement->isTextField()) + return; + + Document* document = element->document(); + Frame* frame = document->frame(); + if (!frame) + return; + + if (inputElement->isPasswordField()) + document->setUseSecureKeyboardEntryWhenActive(false); + + frame->editor()->textFieldDidEndEditing(element); +} + +void InputElement::updateFocusAppearance(InputElementData& data, InputElement* inputElement, Element* element, bool restorePreviousSelection) +{ + ASSERT(inputElement->isTextField()); + + if (!restorePreviousSelection || data.cachedSelectionStart() == -1) + inputElement->select(); + else + // Restore the cached selection. + updateSelectionRange(inputElement, element, data.cachedSelectionStart(), data.cachedSelectionEnd()); + + Document* document = element->document(); + if (document && document->frame()) + document->frame()->selection()->revealSelection(); +} + +void InputElement::updateSelectionRange(InputElement* inputElement, Element* element, int start, int end) +{ + if (!inputElement->isTextField()) + return; + + setSelectionRange(element, start, end); +} + +void InputElement::aboutToUnload(InputElement* inputElement, Element* element) +{ + if (!inputElement->isTextField() || !element->focused()) + return; + + Document* document = element->document(); + Frame* frame = document->frame(); + if (!frame) + return; + + frame->editor()->textFieldDidEndEditing(element); +} + +void InputElement::setValueFromRenderer(InputElementData& data, InputElement* inputElement, Element* element, const String& value) +{ + // Renderer and our event handler are responsible for sanitizing values. + ASSERT_UNUSED(inputElement, value == inputElement->sanitizeValue(value) || inputElement->sanitizeValue(value).isEmpty()); + + // Workaround for bug where trailing \n is included in the result of textContent. + // The assert macro above may also be simplified to: value == constrainValue(value) + // http://bugs.webkit.org/show_bug.cgi?id=9661 + if (value == "\n") + data.setValue(""); + else + data.setValue(value); + + element->setFormControlValueMatchesRenderer(true); + + // Input event is fired by the Node::defaultEventHandler for editable controls. + if (!inputElement->isTextField()) + element->dispatchEvent(Event::create(eventNames().inputEvent, true, false)); + notifyFormStateChanged(element); +} + +static String replaceEOLAndLimitLength(const InputElement* inputElement, const String& proposedValue, int maxLength) +{ + if (!inputElement->isTextField()) + return proposedValue; + + String string = proposedValue; + string.replace("\r\n", " "); + string.replace('\r', ' '); + string.replace('\n', ' '); + + unsigned newLength = numCharactersInGraphemeClusters(string, maxLength); + for (unsigned i = 0; i < newLength; ++i) { + const UChar current = string[i]; + if (current < ' ' && current != '\t') { + newLength = i; + break; + } + } + return string.left(newLength); +} + +String InputElement::sanitizeValueForTextField(const InputElement* inputElement, const String& proposedValue) +{ +#if ENABLE(WCSS) + InputElementData data = const_cast<InputElement*>(inputElement)->data(); + if (!isConformToInputMask(data, proposedValue)) { + if (isConformToInputMask(data, data.value())) + return data.value(); + return String(); + } +#endif + return replaceEOLAndLimitLength(inputElement, proposedValue, s_maximumLength); +} + +String InputElement::sanitizeUserInputValue(const InputElement* inputElement, const String& proposedValue, int maxLength) +{ + return replaceEOLAndLimitLength(inputElement, proposedValue, maxLength); +} + +void InputElement::handleBeforeTextInsertedEvent(InputElementData& data, InputElement* inputElement, Element* element, Event* event) +{ + ASSERT(event->isBeforeTextInsertedEvent()); + // Make sure that the text to be inserted will not violate the maxLength. + + // We use RenderTextControlSingleLine::text() instead of InputElement::value() + // because they can be mismatched by sanitizeValue() in + // RenderTextControlSingleLine::subtreeHasChanged() in some cases. + unsigned oldLength = numGraphemeClusters(toRenderTextControlSingleLine(element->renderer())->text()); + + // selectionLength represents the selection length of this text field to be + // removed by this insertion. + // If the text field has no focus, we don't need to take account of the + // selection length. The selection is the source of text drag-and-drop in + // that case, and nothing in the text field will be removed. + unsigned selectionLength = element->focused() ? numGraphemeClusters(plainText(element->document()->frame()->selection()->selection().toNormalizedRange().get())) : 0; + ASSERT(oldLength >= selectionLength); + + // Selected characters will be removed by the next text event. + unsigned baseLength = oldLength - selectionLength; + unsigned maxLength = static_cast<unsigned>(inputElement->supportsMaxLength() ? data.maxLength() : s_maximumLength); // maxLength() can never be negative. + unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0; + + // Truncate the inserted text to avoid violating the maxLength and other constraints. + BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(event); +#if ENABLE(WCSS) + RefPtr<Range> range = element->document()->frame()->selection()->selection().toNormalizedRange(); + String candidateString = toRenderTextControlSingleLine(element->renderer())->text(); + if (selectionLength) + candidateString.replace(range->startOffset(), range->endOffset(), textEvent->text()); + else + candidateString.insert(textEvent->text(), range->startOffset()); + if (!isConformToInputMask(inputElement->data(), candidateString)) { + textEvent->setText(""); + return; + } +#endif + textEvent->setText(sanitizeUserInputValue(inputElement, textEvent->text(), appendableLength)); +} + +void InputElement::parseSizeAttribute(InputElementData& data, Element* element, Attribute* attribute) +{ + data.setSize(attribute->isNull() ? InputElement::s_defaultSize : attribute->value().toInt()); + + if (RenderObject* renderer = element->renderer()) + renderer->setNeedsLayoutAndPrefWidthsRecalc(); +} + +void InputElement::parseMaxLengthAttribute(InputElementData& data, InputElement* inputElement, Element* element, Attribute* attribute) +{ + int maxLength = attribute->isNull() ? InputElement::s_maximumLength : attribute->value().toInt(); + if (maxLength <= 0 || maxLength > InputElement::s_maximumLength) + maxLength = InputElement::s_maximumLength; + + int oldMaxLength = data.maxLength(); + data.setMaxLength(maxLength); + + if (oldMaxLength != maxLength) + updateValueIfNeeded(data, inputElement); + + element->setNeedsStyleRecalc(); +} + +void InputElement::updateValueIfNeeded(InputElementData& data, InputElement* inputElement) +{ + String oldValue = data.value(); + String newValue = inputElement->sanitizeValue(oldValue); + if (newValue != oldValue) + inputElement->setValue(newValue); +} + +void InputElement::notifyFormStateChanged(Element* element) +{ + Document* document = element->document(); + Frame* frame = document->frame(); + if (!frame) + return; + + if (Page* page = frame->page()) + page->chrome()->client()->formStateDidChange(element); +} + +// InputElementData +InputElementData::InputElementData() + : m_size(InputElement::s_defaultSize) + , m_maxLength(InputElement::s_maximumLength) + , m_cachedSelectionStart(-1) + , m_cachedSelectionEnd(-1) +#if ENABLE(WCSS) + , m_inputFormatMask("*m") + , m_maxInputCharsAllowed(InputElement::s_maximumLength) +#endif +{ +} + +InputElementData::~InputElementData() +{ +} + +const AtomicString& InputElementData::name() const +{ + return m_name.isNull() ? emptyAtom : m_name; +} + +InputElement* toInputElement(Element* element) +{ + if (element->isHTMLElement() && (element->hasTagName(inputTag) || element->hasTagName(isindexTag))) + return static_cast<HTMLInputElement*>(element); + +#if ENABLE(WML) + if (element->isWMLElement() && element->hasTagName(WMLNames::inputTag)) + return static_cast<WMLInputElement*>(element); +#endif + + return 0; +} + +#if ENABLE(WCSS) +static inline const AtomicString& formatCodes() +{ + DEFINE_STATIC_LOCAL(AtomicString, codes, ("AaNnXxMm")); + return codes; +} + +static unsigned cursorPositionToMaskIndex(const String& inputFormatMask, unsigned cursorPosition) +{ + UChar mask; + int index = -1; + do { + mask = inputFormatMask[++index]; + if (mask == '\\') + ++index; + else if (mask == '*' || (isASCIIDigit(mask) && mask != '0')) { + index = inputFormatMask.length() - 1; + break; + } + } while (cursorPosition--); + + return index; +} + +bool InputElement::isConformToInputMask(const InputElementData& data, const String& inputChars) +{ + for (unsigned i = 0; i < inputChars.length(); ++i) + if (!isConformToInputMask(data, inputChars[i], i)) + return false; + return true; +} + +bool InputElement::isConformToInputMask(const InputElementData& data, UChar inChar, unsigned cursorPosition) +{ + String inputFormatMask = data.inputFormatMask(); + + if (inputFormatMask.isEmpty() || inputFormatMask == "*M" || inputFormatMask == "*m") + return true; + + if (cursorPosition >= data.maxInputCharsAllowed()) + return false; + + unsigned maskIndex = cursorPositionToMaskIndex(inputFormatMask, cursorPosition); + bool ok = true; + UChar mask = inputFormatMask[maskIndex]; + // Match the inputed character with input mask + switch (mask) { + case 'A': + ok = !isASCIIDigit(inChar) && !isASCIILower(inChar) && isASCIIPrintable(inChar); + break; + case 'a': + ok = !isASCIIDigit(inChar) && !isASCIIUpper(inChar) && isASCIIPrintable(inChar); + break; + case 'N': + ok = isASCIIDigit(inChar); + break; + case 'n': + ok = !isASCIIAlpha(inChar) && isASCIIPrintable(inChar); + break; + case 'X': + ok = !isASCIILower(inChar) && isASCIIPrintable(inChar); + break; + case 'x': + ok = !isASCIIUpper(inChar) && isASCIIPrintable(inChar); + break; + case 'M': + case 'm': + ok = isASCIIPrintable(inChar); + break; + default: + ok = (mask == inChar); + break; + } + + return ok; +} + +String InputElement::validateInputMask(InputElementData& data, String& inputMask) +{ + inputMask.replace("\\\\", "\\"); + + bool isValid = true; + bool hasWildcard = false; + unsigned escapeCharCount = 0; + unsigned maskLength = inputMask.length(); + UChar formatCode; + for (unsigned i = 0; i < maskLength; ++i) { + formatCode = inputMask[i]; + if (formatCodes().find(formatCode) == -1) { + if (formatCode == '*' || (isASCIIDigit(formatCode) && formatCode != '0')) { + // Validate codes which ends with '*f' or 'nf' + formatCode = inputMask[++i]; + if ((i + 1 != maskLength) || formatCodes().find(formatCode) == -1) { + isValid = false; + break; + } + hasWildcard = true; + } else if (formatCode == '\\') { + // skip over the next mask character + ++i; + ++escapeCharCount; + } else { + isValid = false; + break; + } + } + } + + if (!isValid) + return String(); + // calculate the number of characters allowed to be entered by input mask + unsigned allowedLength = maskLength; + if (escapeCharCount) + allowedLength -= escapeCharCount; + + if (hasWildcard) { + formatCode = inputMask[maskLength - 2]; + if (formatCode == '*') + allowedLength = data.maxInputCharsAllowed(); + else { + unsigned leftLen = String(&formatCode).toInt(); + allowedLength = leftLen + allowedLength - 2; + } + } + + if (allowedLength < data.maxInputCharsAllowed()) + data.setMaxInputCharsAllowed(allowedLength); + + return inputMask; +} + +#endif + +} diff --git a/Source/WebCore/dom/InputElement.h b/Source/WebCore/dom/InputElement.h new file mode 100644 index 0000000..2d5606d --- /dev/null +++ b/Source/WebCore/dom/InputElement.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.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. + * + */ + +#ifndef InputElement_h +#define InputElement_h + +#include "PlatformString.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +class Attribute; +class Document; +class Element; +class Event; +class InputElementData; + +class InputElement { +public: + virtual ~InputElement() { } + + virtual bool isAutofilled() const = 0; + virtual bool isChecked() const = 0; + virtual bool isIndeterminate() const = 0; + virtual bool isInputTypeHidden() const = 0; + virtual bool isPasswordField() const = 0; + virtual bool isSearchField() const = 0; + virtual bool isTextField() const = 0; + virtual bool isRadioButton() const = 0; + virtual bool isCheckbox() const = 0; + + virtual bool supportsMaxLength() const = 0; + virtual bool hasSpinButton() const { return false; } +#if ENABLE(INPUT_SPEECH) + virtual bool isSpeechEnabled() const = 0; +#endif + + virtual bool searchEventsShouldBeDispatched() const = 0; + + virtual int size() const = 0; + virtual const String& suggestedValue() const = 0; + virtual String value() const = 0; + virtual void setValue(const String&, bool sendChangeEvent = false) = 0; + virtual void setValueForUser(const String&) = 0; + + // Returns true if the specified string can be set as the value of InputElement. + virtual bool isAcceptableValue(const String&) const = 0; + virtual String sanitizeValue(const String&) const = 0; + virtual void setValueFromRenderer(const String&) = 0; + + virtual void cacheSelection(int start, int end) = 0; + virtual void select() = 0; + +#if ENABLE(WCSS) + virtual InputElementData data() const = 0; +#endif + + static const int s_maximumLength; + static const int s_defaultSize; + + // Replaces CRs and LFs, shrinks the value for s_maximumLength. + // This should be applied to values from the HTML value attribute and the DOM value property. + // This function should be called only by sanitizeValue() implementations. + // Public so it can be called by InputType. + static String sanitizeValueForTextField(const InputElement*, const String&); + +protected: + static void dispatchFocusEvent(InputElement*, Element*); + static void dispatchBlurEvent(InputElement*, Element*); + static void updateFocusAppearance(InputElementData&, InputElement*, Element*, bool restorePreviousSelection); + static void updateSelectionRange(InputElement*, Element*, int start, int end); + static void aboutToUnload(InputElement*, Element*); + static void setValueFromRenderer(InputElementData&, InputElement*, Element*, const String&); + // Replaces CRs and LFs, shrinks the value for the specified maximum length. + // This should be applied to values specified by users. + // The input string may be a fragment of the whole value. + static String sanitizeUserInputValue(const InputElement*, const String&, int); + static void handleBeforeTextInsertedEvent(InputElementData&, InputElement*, Element*, Event*); + static void parseSizeAttribute(InputElementData&, Element*, Attribute*); + static void parseMaxLengthAttribute(InputElementData&, InputElement*, Element*, Attribute*); + static void updateValueIfNeeded(InputElementData&, InputElement*); + static void notifyFormStateChanged(Element*); +#if ENABLE(WCSS) + static bool isConformToInputMask(const InputElementData&, const String&); + static bool isConformToInputMask(const InputElementData&, UChar, unsigned); + static String validateInputMask(InputElementData&, String&); +#endif +}; + +// HTML/WMLInputElement hold this struct as member variable +// and pass it to the static helper functions in InputElement +class InputElementData { +public: + InputElementData(); + ~InputElementData(); + + const AtomicString& name() const; + void setName(const AtomicString& value) { m_name = value; } + + String value() const { return m_value; } + void setValue(const String& value) { m_value = value; } + + const String& suggestedValue() const { return m_suggestedValue; } + void setSuggestedValue(const String& value) { m_suggestedValue = value; } + + int size() const { return m_size; } + void setSize(int value) { m_size = value; } + + int maxLength() const { return m_maxLength; } + void setMaxLength(int value) { m_maxLength = value; } + + int cachedSelectionStart() const { return m_cachedSelectionStart; } + void setCachedSelectionStart(int value) { m_cachedSelectionStart = value; } + + int cachedSelectionEnd() const { return m_cachedSelectionEnd; } + void setCachedSelectionEnd(int value) { m_cachedSelectionEnd = value; } + +#if ENABLE(WCSS) + String inputFormatMask() const { return m_inputFormatMask; } + void setInputFormatMask(const String& mask) { m_inputFormatMask = mask; } + + unsigned maxInputCharsAllowed() const { return m_maxInputCharsAllowed; } + void setMaxInputCharsAllowed(unsigned maxLength) { m_maxInputCharsAllowed = maxLength; } + +#endif + +private: + AtomicString m_name; + String m_value; + String m_suggestedValue; + int m_size; + int m_maxLength; + int m_cachedSelectionStart; + int m_cachedSelectionEnd; +#if ENABLE(WCSS) + String m_inputFormatMask; + unsigned m_maxInputCharsAllowed; +#endif +}; + +InputElement* toInputElement(Element*); + +} + +#endif diff --git a/Source/WebCore/dom/KeyboardEvent.cpp b/Source/WebCore/dom/KeyboardEvent.cpp new file mode 100644 index 0000000..7b0f3af --- /dev/null +++ b/Source/WebCore/dom/KeyboardEvent.cpp @@ -0,0 +1,163 @@ +/** + * 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 { + +static inline const AtomicString& eventTypeForKeyboardEventType(PlatformKeyboardEvent::Type type) +{ + switch (type) { + case PlatformKeyboardEvent::KeyUp: + return eventNames().keyupEvent; + case PlatformKeyboardEvent::RawKeyDown: + return eventNames().keydownEvent; + case PlatformKeyboardEvent::Char: + return eventNames().keypressEvent; + case PlatformKeyboardEvent::KeyDown: + // The caller should disambiguate the combined event into RawKeyDown or Char events. + break; + } + ASSERT_NOT_REACHED(); + return eventNames().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() +{ +} + +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() == eventNames().keydownEvent || type() == eventNames().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() && view()->frame()) + backwardCompatibilityMode = view()->frame()->eventHandler()->needsKeyboardEventDisambiguationQuirks(); + + if (!m_keyEvent || (type() != eventNames().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/Source/WebCore/dom/KeyboardEvent.h b/Source/WebCore/dom/KeyboardEvent.h new file mode 100644 index 0000000..eeaef80 --- /dev/null +++ b/Source/WebCore/dom/KeyboardEvent.h @@ -0,0 +1,117 @@ +/* + * 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, 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 KeyboardEvent_h +#define KeyboardEvent_h + +#include "UIEventWithKeyState.h" +#include <wtf/Vector.h> + +namespace WebCore { + + class PlatformKeyboardEvent; + +#if PLATFORM(MAC) + struct KeypressCommand { + KeypressCommand() { } + KeypressCommand(const String& commandName) : commandName(commandName) { } + KeypressCommand(const String& commandName, const String& text) : commandName(commandName), text(text) { } + + 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 + }; + + static PassRefPtr<KeyboardEvent> create() + { + return adoptRef(new KeyboardEvent); + } + static PassRefPtr<KeyboardEvent> create(const PlatformKeyboardEvent& platformEvent, AbstractView* view) + { + return adoptRef(new KeyboardEvent(platformEvent, view)); + } + static PassRefPtr<KeyboardEvent> create(const AtomicString& type, bool canBubble, bool cancelable, AbstractView* view, + const String& keyIdentifier, unsigned keyLocation, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool altGraphKey) + { + return adoptRef(new KeyboardEvent(type, canBubble, cancelable, view, keyIdentifier, keyLocation, + ctrlKey, altKey, shiftKey, metaKey, 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); + + const 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.get(); } + + 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: + 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); + + OwnPtr<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/Source/WebCore/dom/KeyboardEvent.idl b/Source/WebCore/dom/KeyboardEvent.idl new file mode 100644 index 0000000..1af3d02 --- /dev/null +++ b/Source/WebCore/dom/KeyboardEvent.idl @@ -0,0 +1,78 @@ +/* + * 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 KeyboardEvent : UIEvent { + +#if !defined(LANGUAGE_JAVASCRIPT) || !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) || !LANGUAGE_JAVASCRIPT + boolean getModifierState(in DOMString keyIdentifierArg); +#endif + + // FIXME: this does not match the version in the DOM spec. + void initKeyboardEvent(in DOMString 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) || !LANGUAGE_JAVASCRIPT + readonly attribute long keyCode; + readonly attribute long charCode; + + void initKeyboardEvent(in DOMString 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/Source/WebCore/dom/MappedAttributeEntry.h b/Source/WebCore/dom/MappedAttributeEntry.h new file mode 100644 index 0000000..aa1cd1f --- /dev/null +++ b/Source/WebCore/dom/MappedAttributeEntry.h @@ -0,0 +1,57 @@ +/* + * 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. + * Copyright (C) 2010 Franois Sausset (sausset@gmail.com). 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 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 +#if ENABLE(MATHML) + , eMathML +#endif +// When adding new entries, make sure to keep eLastEntry at the end of the list. + , eLastEntry +}; + +} + +#endif diff --git a/Source/WebCore/dom/MessageChannel.cpp b/Source/WebCore/dom/MessageChannel.cpp new file mode 100644 index 0000000..73c016f --- /dev/null +++ b/Source/WebCore/dom/MessageChannel.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 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 "MessageChannel.h" + +#include "MessagePort.h" +#include "MessagePortChannel.h" + +namespace WebCore { + +MessageChannel::MessageChannel(ScriptExecutionContext* context) + : m_port1(MessagePort::create(*context)) + , m_port2(MessagePort::create(*context)) +{ + MessagePortChannel::createChannel(m_port1.get(), m_port2.get()); +} + +MessageChannel::~MessageChannel() +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/MessageChannel.h b/Source/WebCore/dom/MessageChannel.h new file mode 100644 index 0000000..8e3ef9f --- /dev/null +++ b/Source/WebCore/dom/MessageChannel.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 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 MessageChannel_h +#define MessageChannel_h + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class MessagePort; + class ScriptExecutionContext; + + class MessageChannel : public RefCounted<MessageChannel> { + public: + static PassRefPtr<MessageChannel> create(ScriptExecutionContext* context) { return adoptRef(new MessageChannel(context)); } + ~MessageChannel(); + + MessagePort* port1() const { return m_port1.get(); } + MessagePort* port2() const { return m_port2.get(); } + + private: + MessageChannel(ScriptExecutionContext*); + + RefPtr<MessagePort> m_port1; + RefPtr<MessagePort> m_port2; + }; + +} // namespace WebCore + +#endif // MessageChannel_h diff --git a/Source/WebCore/dom/MessageChannel.idl b/Source/WebCore/dom/MessageChannel.idl new file mode 100644 index 0000000..a3f3c94 --- /dev/null +++ b/Source/WebCore/dom/MessageChannel.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008, 2010 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 [ + CanBeConstructed, + CustomConstructFunction, + V8CustomConstructor, + CustomMarkFunction, + NoStaticTables + ] MessageChannel { + + readonly attribute MessagePort port1; + readonly attribute MessagePort port2; + + }; + +} diff --git a/Source/WebCore/dom/MessageEvent.cpp b/Source/WebCore/dom/MessageEvent.cpp new file mode 100644 index 0000000..3c84642 --- /dev/null +++ b/Source/WebCore/dom/MessageEvent.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 Henry Mason (hmason@mac.com) + * Copyright (C) 2003, 2005, 2006, 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 "MessageEvent.h" + +#include "DOMWindow.h" +#include "EventNames.h" + +namespace WebCore { + +MessageEvent::MessageEvent() + : m_data(SerializedScriptValue::create()) +{ +} + +MessageEvent::MessageEvent(PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, PassRefPtr<DOMWindow> source, PassOwnPtr<MessagePortArray> ports) + : Event(eventNames().messageEvent, false, false) + , m_data(data) + , m_origin(origin) + , m_lastEventId(lastEventId) + , m_source(source) + , m_ports(ports) +{ +} + +MessageEvent::~MessageEvent() +{ +} + +void MessageEvent::initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, DOMWindow* source, PassOwnPtr<MessagePortArray> ports) +{ + if (dispatched()) + return; + + initEvent(type, canBubble, cancelable); + + m_data = data; + m_origin = origin; + m_lastEventId = lastEventId; + m_source = source; + m_ports = ports; +} + +// FIXME: remove this when we update the ObjC bindings (bug #28774). +MessagePort* MessageEvent::messagePort() +{ + if (!m_ports) + return 0; + ASSERT(m_ports->size() == 1); + return (*m_ports)[0].get(); +} + +void MessageEvent::initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, DOMWindow* source, MessagePort* port) +{ + MessagePortArray* ports = 0; + if (port) { + ports = new MessagePortArray(); + ports->append(port); + } + initMessageEvent(type, canBubble, cancelable, data, origin, lastEventId, source, ports); +} + +bool MessageEvent::isMessageEvent() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/MessageEvent.h b/Source/WebCore/dom/MessageEvent.h new file mode 100644 index 0000000..b7f9b02 --- /dev/null +++ b/Source/WebCore/dom/MessageEvent.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2007 Henry Mason (hmason@mac.com) + * Copyright (C) 2003, 2004, 2005, 2006, 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 MessageEvent_h +#define MessageEvent_h + +#include "DOMWindow.h" +#include "Event.h" +#include "MessagePort.h" +#include "SerializedScriptValue.h" + +namespace WebCore { + + class DOMWindow; + + class MessageEvent : public Event { + public: + static PassRefPtr<MessageEvent> create() + { + return adoptRef(new MessageEvent); + } + static PassRefPtr<MessageEvent> create(PassOwnPtr<MessagePortArray> ports, PassRefPtr<SerializedScriptValue> data = 0, const String& origin = "", const String& lastEventId = "", PassRefPtr<DOMWindow> source = 0) + { + return adoptRef(new MessageEvent(data, origin, lastEventId, source, ports)); + } + virtual ~MessageEvent(); + + void initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, DOMWindow* source, PassOwnPtr<MessagePortArray>); + + SerializedScriptValue* data() const { return m_data.get(); } + const String& origin() const { return m_origin; } + const String& lastEventId() const { return m_lastEventId; } + DOMWindow* source() const { return m_source.get(); } + MessagePortArray* ports() const { return m_ports.get(); } + + // FIXME: remove this when we update the ObjC bindings (bug #28774). + MessagePort* messagePort(); + // FIXME: remove this when we update the ObjC bindings (bug #28774). + void initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, DOMWindow* source, MessagePort*); + + virtual bool isMessageEvent() const; + + private: + MessageEvent(); + MessageEvent(PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, PassRefPtr<DOMWindow> source, PassOwnPtr<MessagePortArray>); + + RefPtr<SerializedScriptValue> m_data; + String m_origin; + String m_lastEventId; + RefPtr<DOMWindow> m_source; + OwnPtr<MessagePortArray> m_ports; + }; + +} // namespace WebCore + +#endif // MessageEvent_h diff --git a/Source/WebCore/dom/MessageEvent.idl b/Source/WebCore/dom/MessageEvent.idl new file mode 100644 index 0000000..2b47374 --- /dev/null +++ b/Source/WebCore/dom/MessageEvent.idl @@ -0,0 +1,50 @@ +/* + * 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 [ + NoStaticTables + ] MessageEvent : Event { + readonly attribute [CachedAttribute] SerializedScriptValue data; + + readonly attribute DOMString origin; + readonly attribute DOMString lastEventId; + readonly attribute DOMWindow source; +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + readonly attribute [CustomGetter] Array ports; + + [Custom] void initMessageEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in SerializedScriptValue dataArg, in DOMString originArg, in DOMString lastEventIdArg, in DOMWindow sourceArg, in Array messagePorts); +#else + // There's no good way to expose an array via the ObjC bindings, so for now just expose a single port. + readonly attribute MessagePort messagePort; + + void initMessageEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in SerializedScriptValue dataArg, in DOMString originArg, in DOMString lastEventIdArg, in DOMWindow sourceArg, in MessagePort messagePort); +#endif + + }; + +} diff --git a/Source/WebCore/dom/MessagePort.cpp b/Source/WebCore/dom/MessagePort.cpp new file mode 100644 index 0000000..1b7aea7 --- /dev/null +++ b/Source/WebCore/dom/MessagePort.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (C) 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 "MessagePort.h" + +#include "DOMWindow.h" +#include "Document.h" +#include "EventException.h" +#include "EventNames.h" +#include "MessageEvent.h" +#include "SecurityOrigin.h" +#include "Timer.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext) + : m_entangledChannel(0) + , m_started(false) + , m_closed(false) + , m_scriptExecutionContext(&scriptExecutionContext) +{ + m_scriptExecutionContext->createdMessagePort(this); + + // Don't need to call processMessagePortMessagesSoon() here, because the port will not be opened until start() is invoked. +} + +MessagePort::~MessagePort() +{ + close(); + if (m_scriptExecutionContext) + m_scriptExecutionContext->destroyedMessagePort(this); +} + +// FIXME: remove this when we update the ObjC bindings (bug #28774). +void MessagePort::postMessage(PassRefPtr<SerializedScriptValue> message, MessagePort* port, ExceptionCode& ec) +{ + MessagePortArray ports; + if (port) + ports.append(port); + postMessage(message, &ports, ec); +} + +void MessagePort::postMessage(PassRefPtr<SerializedScriptValue> message, ExceptionCode& ec) +{ + postMessage(message, static_cast<MessagePortArray*>(0), ec); +} + +void MessagePort::postMessage(PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, ExceptionCode& ec) +{ + if (!isEntangled()) + return; + ASSERT(m_scriptExecutionContext); + + OwnPtr<MessagePortChannelArray> channels; + // Make sure we aren't connected to any of the passed-in ports. + if (ports) { + for (unsigned int i = 0; i < ports->size(); ++i) { + MessagePort* dataPort = (*ports)[i].get(); + if (dataPort == this || m_entangledChannel->isConnectedTo(dataPort)) { + ec = INVALID_STATE_ERR; + return; + } + } + channels = MessagePort::disentanglePorts(ports, ec); + if (ec) + return; + } + m_entangledChannel->postMessageToRemote(MessagePortChannel::EventData::create(message, channels.release())); +} + +PassOwnPtr<MessagePortChannel> MessagePort::disentangle(ExceptionCode& ec) +{ + if (!m_entangledChannel) + ec = INVALID_STATE_ERR; + else { + m_entangledChannel->disentangle(); + + // We can't receive any messages or generate any events, so remove ourselves from the list of active ports. + ASSERT(m_scriptExecutionContext); + m_scriptExecutionContext->destroyedMessagePort(this); + m_scriptExecutionContext = 0; + } + return m_entangledChannel.release(); +} + +// Invoked to notify us that there are messages available for this port. +// This code may be called from another thread, and so should not call any non-threadsafe APIs (i.e. should not call into the entangled channel or access mutable variables). +void MessagePort::messageAvailable() +{ + ASSERT(m_scriptExecutionContext); + m_scriptExecutionContext->processMessagePortMessagesSoon(); +} + +void MessagePort::start() +{ + // Do nothing if we've been cloned or closed. + if (!isEntangled()) + return; + + ASSERT(m_scriptExecutionContext); + if (m_started) + return; + + m_started = true; + m_scriptExecutionContext->processMessagePortMessagesSoon(); +} + +void MessagePort::close() +{ + m_closed = true; + if (!isEntangled()) + return; + m_entangledChannel->close(); +} + +void MessagePort::entangle(PassOwnPtr<MessagePortChannel> remote) +{ + // Only invoked to set our initial entanglement. + ASSERT(!m_entangledChannel); + ASSERT(m_scriptExecutionContext); + + // Don't entangle the ports if the channel is closed. + if (remote->entangleIfOpen(this)) + m_entangledChannel = remote; +} + +void MessagePort::contextDestroyed() +{ + ASSERT(m_scriptExecutionContext); + // Must be closed before blowing away the cached context, to ensure that we get no more calls to messageAvailable(). + // ScriptExecutionContext::closeMessagePorts() takes care of that. + ASSERT(m_closed); + m_scriptExecutionContext = 0; +} + +ScriptExecutionContext* MessagePort::scriptExecutionContext() const +{ + return m_scriptExecutionContext; +} + +void MessagePort::dispatchMessages() +{ + // Messages for contexts that are not fully active get dispatched too, but JSAbstractEventListener::handleEvent() doesn't call handlers for these. + // The HTML5 spec specifies that any messages sent to a document that is not fully active should be dropped, so this behavior is OK. + ASSERT(started()); + + OwnPtr<MessagePortChannel::EventData> eventData; + while (m_entangledChannel && m_entangledChannel->tryGetMessageFromRemote(eventData)) { + OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*m_scriptExecutionContext, eventData->channels()); + RefPtr<Event> evt = MessageEvent::create(ports.release(), eventData->message()); + + ExceptionCode ec = 0; + dispatchEvent(evt.release(), ec); + ASSERT(!ec); + } +} + +bool MessagePort::hasPendingActivity() +{ + // The spec says that entangled message ports should always be treated as if they have a strong reference. + // We'll also stipulate that the queue needs to be open (if the app drops its reference to the port before start()-ing it, then it's not really entangled as it's unreachable). + return m_started && m_entangledChannel && m_entangledChannel->hasPendingActivity(); +} + +MessagePort* MessagePort::locallyEntangledPort() +{ + return m_entangledChannel ? m_entangledChannel->locallyEntangledPort(m_scriptExecutionContext) : 0; +} + +PassOwnPtr<MessagePortChannelArray> MessagePort::disentanglePorts(const MessagePortArray* ports, ExceptionCode& ec) +{ + if (!ports || !ports->size()) + return 0; + + // HashSet used to efficiently check for duplicates in the passed-in array. + HashSet<MessagePort*> portSet; + + // Walk the incoming array - if there are any duplicate ports, or null ports or cloned ports, throw an error (per section 8.3.3 of the HTML5 spec). + for (unsigned int i = 0; i < ports->size(); ++i) { + MessagePort* port = (*ports)[i].get(); + if (!port || port->isCloned() || portSet.contains(port)) { + ec = INVALID_STATE_ERR; + return 0; + } + portSet.add(port); + } + + // Passed-in ports passed validity checks, so we can disentangle them. + MessagePortChannelArray* portArray = new MessagePortChannelArray(ports->size()); + for (unsigned int i = 0 ; i < ports->size() ; ++i) { + OwnPtr<MessagePortChannel> channel = (*ports)[i]->disentangle(ec); + ASSERT(!ec); // Can't generate exception here if passed above checks. + (*portArray)[i] = channel.release(); + } + return portArray; +} + +PassOwnPtr<MessagePortArray> MessagePort::entanglePorts(ScriptExecutionContext& context, PassOwnPtr<MessagePortChannelArray> channels) +{ + if (!channels || !channels->size()) + return 0; + + MessagePortArray* portArray = new MessagePortArray(channels->size()); + for (unsigned int i = 0; i < channels->size(); ++i) { + RefPtr<MessagePort> port = MessagePort::create(context); + port->entangle((*channels)[i].release()); + (*portArray)[i] = port.release(); + } + return portArray; +} + +EventTargetData* MessagePort::eventTargetData() +{ + return &m_eventTargetData; +} + +EventTargetData* MessagePort::ensureEventTargetData() +{ + return &m_eventTargetData; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/MessagePort.h b/Source/WebCore/dom/MessagePort.h new file mode 100644 index 0000000..1287834 --- /dev/null +++ b/Source/WebCore/dom/MessagePort.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 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 MessagePort_h +#define MessagePort_h + +#include "EventListener.h" +#include "EventTarget.h" +#include "MessagePortChannel.h" +#include <wtf/Forward.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/AtomicStringHash.h> + +namespace WebCore { + + class Event; + class Frame; + class MessagePort; + class ScriptExecutionContext; + + // The overwhelmingly common case is sending a single port, so handle that efficiently with an inline buffer of size 1. + typedef Vector<RefPtr<MessagePort>, 1> MessagePortArray; + + class MessagePort : public RefCounted<MessagePort>, public EventTarget { + public: + static PassRefPtr<MessagePort> create(ScriptExecutionContext& scriptExecutionContext) { return adoptRef(new MessagePort(scriptExecutionContext)); } + ~MessagePort(); + + void postMessage(PassRefPtr<SerializedScriptValue> message, ExceptionCode&); + void postMessage(PassRefPtr<SerializedScriptValue> message, const MessagePortArray*, ExceptionCode&); + // FIXME: remove this when we update the ObjC bindings (bug #28774). + void postMessage(PassRefPtr<SerializedScriptValue> message, MessagePort*, ExceptionCode&); + + void start(); + void close(); + + void entangle(PassOwnPtr<MessagePortChannel>); + PassOwnPtr<MessagePortChannel> disentangle(ExceptionCode&); + + // Disentangle an array of ports, returning the entangled channels. + // Per section 8.3.3 of the HTML5 spec, generates an INVALID_STATE_ERR exception if any of the passed ports are null or not entangled. + // Returns 0 if there is an exception, or if the passed-in array is 0/empty. + static PassOwnPtr<MessagePortChannelArray> disentanglePorts(const MessagePortArray*, ExceptionCode&); + + // Entangles an array of channels, returning an array of MessagePorts in matching order. + // Returns 0 if the passed array is 0/empty. + static PassOwnPtr<MessagePortArray> entanglePorts(ScriptExecutionContext&, PassOwnPtr<MessagePortChannelArray>); + + void messageAvailable(); + bool started() const { return m_started; } + + void contextDestroyed(); + + virtual ScriptExecutionContext* scriptExecutionContext() const; + + virtual MessagePort* toMessagePort() { return this; } + + void dispatchMessages(); + + using RefCounted<MessagePort>::ref; + using RefCounted<MessagePort>::deref; + + bool hasPendingActivity(); + + void setOnmessage(PassRefPtr<EventListener> listener) + { + setAttributeEventListener(eventNames().messageEvent, listener); + start(); + } + EventListener* onmessage() { return getAttributeEventListener(eventNames().messageEvent); } + + // Returns null if there is no entangled port, or if the entangled port is run by a different thread. + // Returns null otherwise. + // NOTE: This is used solely to enable a GC optimization. Some platforms may not be able to determine ownership of the remote port (since it may live cross-process) - those platforms may always return null. + MessagePort* locallyEntangledPort(); + // A port starts out its life entangled, and remains entangled until it is closed or is cloned. + bool isEntangled() { return !m_closed && !isCloned(); } + // A port is cloned if its entangled channel has been removed and sent to a new owner via postMessage(). + bool isCloned() { return !m_entangledChannel; } + + private: + MessagePort(ScriptExecutionContext&); + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); + + OwnPtr<MessagePortChannel> m_entangledChannel; + + bool m_started; + bool m_closed; + + ScriptExecutionContext* m_scriptExecutionContext; + EventTargetData m_eventTargetData; + }; + +} // namespace WebCore + +#endif // MessagePort_h diff --git a/Source/WebCore/dom/MessagePort.idl b/Source/WebCore/dom/MessagePort.idl new file mode 100644 index 0000000..7bde45e --- /dev/null +++ b/Source/WebCore/dom/MessagePort.idl @@ -0,0 +1,57 @@ +/* + * Copyright (C) 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. + * + */ + +module events { + + interface [ + CustomMarkFunction, + EventTarget, + NoStaticTables + ] MessagePort { +// We need to have something as an ObjC binding, because MessagePort is used in MessageEvent, which already has one, +// but we don't want to actually expose the API while it is in flux. +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + [Custom] void postMessage(in DOMString message, in [Optional] Array messagePorts) + raises(DOMException); + void start(); + void close(); + + // event handler attributes + attribute EventListener onmessage; + + // EventTarget interface + void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); +#endif + }; + +} diff --git a/Source/WebCore/dom/MessagePortChannel.cpp b/Source/WebCore/dom/MessagePortChannel.cpp new file mode 100644 index 0000000..cb431a1 --- /dev/null +++ b/Source/WebCore/dom/MessagePortChannel.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "MessagePortChannel.h" + +namespace WebCore { + + +PassOwnPtr<MessagePortChannel::EventData> MessagePortChannel::EventData::create(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) +{ + return new EventData(message, channels); +} + +MessagePortChannel::EventData::EventData(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray> channels) + : m_message(message) + , m_channels(channels) +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/MessagePortChannel.h b/Source/WebCore/dom/MessagePortChannel.h new file mode 100644 index 0000000..f308a29 --- /dev/null +++ b/Source/WebCore/dom/MessagePortChannel.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2009 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 MessagePortChannel_h +#define MessagePortChannel_h + +#include "PlatformString.h" + +#include "SerializedScriptValue.h" + +#include <wtf/OwnPtr.h> +#include <wtf/Forward.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class MessagePort; + class MessagePortChannel; + class PlatformMessagePortChannel; + class ScriptExecutionContext; + class SerializedScriptValue; + + // The overwhelmingly common case is sending a single port, so handle that efficiently with an inline buffer of size 1. + typedef Vector<OwnPtr<MessagePortChannel>, 1> MessagePortChannelArray; + + // MessagePortChannel is a platform-independent interface to the remote side of a message channel. + // It acts as a wrapper around the platform-dependent PlatformMessagePortChannel implementation which ensures that the platform-dependent close() method is invoked before destruction. + class MessagePortChannel : public Noncopyable { + public: + static void createChannel(PassRefPtr<MessagePort>, PassRefPtr<MessagePort>); + + // Creates a new wrapper for the passed channel. + static PassOwnPtr<MessagePortChannel> create(PassRefPtr<PlatformMessagePortChannel>); + + // Entangles the channel with a port (called when a port has been cloned, after the clone has been marshaled to its new owning thread and is ready to receive messages). + // Returns false if the entanglement failed because the port was closed. + bool entangleIfOpen(MessagePort*); + + // Disentangles the channel from a given port so it no longer forwards messages to the port. Called when the port is being cloned and no new owning thread has yet been established. + void disentangle(); + + // Closes the port (ensures that no further messages can be added to either queue). + void close(); + + // Used by MessagePort.postMessage() to prevent callers from passing a port's own entangled port. + bool isConnectedTo(MessagePort*); + + // Returns true if the proxy currently contains messages for this port. + bool hasPendingActivity(); + + class EventData : public Noncopyable { + public: + static PassOwnPtr<EventData> create(PassRefPtr<SerializedScriptValue>, PassOwnPtr<MessagePortChannelArray>); + + SerializedScriptValue* message() { return m_message.get(); } + PassOwnPtr<MessagePortChannelArray> channels() { return m_channels.release(); } + + private: + EventData(PassRefPtr<SerializedScriptValue> message, PassOwnPtr<MessagePortChannelArray>); + RefPtr<SerializedScriptValue> m_message; + OwnPtr<MessagePortChannelArray> m_channels; + }; + + // Sends a message and optional cloned port to the remote port. + void postMessageToRemote(PassOwnPtr<EventData>); + + // Extracts a message from the message queue for this port. + bool tryGetMessageFromRemote(OwnPtr<EventData>&); + + // Returns the entangled port if run by the same thread (see MessagePort::locallyEntangledPort() for more details). + MessagePort* locallyEntangledPort(const ScriptExecutionContext*); + + ~MessagePortChannel(); + + PlatformMessagePortChannel* channel() const { return m_channel.get(); } + + private: + MessagePortChannel(PassRefPtr<PlatformMessagePortChannel>); + RefPtr<PlatformMessagePortChannel> m_channel; + }; + +} // namespace WebCore + +#endif // MessagePortChannel_h diff --git a/Source/WebCore/dom/MouseEvent.cpp b/Source/WebCore/dom/MouseEvent.cpp new file mode 100644 index 0000000..bdd39d3 --- /dev/null +++ b/Source/WebCore/dom/MouseEvent.cpp @@ -0,0 +1,118 @@ +/* + * 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, 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 "MouseEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +MouseEvent::MouseEvent() + : m_button(0) + , m_buttonDown(false) +{ +} + +MouseEvent::MouseEvent(const AtomicString& eventType, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, + int detail, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + unsigned short button, PassRefPtr<EventTarget> relatedTarget, + PassRefPtr<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, PassRefPtr<AbstractView> view, + int detail, int screenX, int screenY, int clientX, int clientY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + unsigned short button, PassRefPtr<EventTarget> 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 == eventNames().dragenterEvent || t == eventNames().dragoverEvent || t == eventNames().dragleaveEvent || t == eventNames().dropEvent + || t == eventNames().dragstartEvent|| t == eventNames().dragEvent || t == eventNames().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() == eventNames().mouseoutEvent) + return relatedTarget() ? relatedTarget()->toNode() : 0; + + 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() != eventNames().mouseoutEvent) + return relatedTarget() ? relatedTarget()->toNode() : 0; + + return target() ? target()->toNode() : 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/MouseEvent.h b/Source/WebCore/dom/MouseEvent.h new file mode 100644 index 0000000..7454b04 --- /dev/null +++ b/Source/WebCore/dom/MouseEvent.h @@ -0,0 +1,86 @@ +/* + * 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, 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 MouseEvent_h +#define MouseEvent_h + +#include "Clipboard.h" +#include "MouseRelatedEvent.h" + +namespace WebCore { + + // Introduced in DOM Level 2 + class MouseEvent : public MouseRelatedEvent { + public: + static PassRefPtr<MouseEvent> create() + { + return adoptRef(new MouseEvent); + } + static PassRefPtr<MouseEvent> create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, + int detail, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, + PassRefPtr<EventTarget> relatedTarget, PassRefPtr<Clipboard> clipboard = 0, bool isSimulated = false) + { + return adoptRef(new MouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, pageX, pageY, + ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget, clipboard, isSimulated)); + } + virtual ~MouseEvent(); + + void initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, + int detail, int screenX, int screenY, int clientX, int clientY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + unsigned short button, PassRefPtr<EventTarget> 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; } + EventTarget* 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: + MouseEvent(); + MouseEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, + int detail, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, + PassRefPtr<EventTarget> relatedTarget, PassRefPtr<Clipboard> clipboard, bool isSimulated); + + unsigned short m_button; + bool m_buttonDown; + RefPtr<EventTarget> m_relatedTarget; + RefPtr<Clipboard> m_clipboard; + }; + +} // namespace WebCore + +#endif // MouseEvent_h diff --git a/Source/WebCore/dom/MouseEvent.idl b/Source/WebCore/dom/MouseEvent.idl new file mode 100644 index 0000000..49385f1 --- /dev/null +++ b/Source/WebCore/dom/MouseEvent.idl @@ -0,0 +1,64 @@ +/* + * 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 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 DOMString 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) && LANGUAGE_JAVASCRIPT + readonly attribute Clipboard dataTransfer; +#endif + }; + +} diff --git a/Source/WebCore/dom/MouseRelatedEvent.cpp b/Source/WebCore/dom/MouseRelatedEvent.cpp new file mode 100644 index 0000000..072656e --- /dev/null +++ b/Source/WebCore/dom/MouseRelatedEvent.cpp @@ -0,0 +1,206 @@ +/* + * 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, 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 "MouseRelatedEvent.h" + +#include "DOMWindow.h" +#include "Document.h" +#include "Frame.h" +#include "FrameView.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->scrollX() / frame->pageZoomFactor(); +} + +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->scrollY() / frame->pageZoomFactor(); +} + +MouseRelatedEvent::MouseRelatedEvent(const AtomicString& eventType, bool canBubble, bool cancelable, PassRefPtr<AbstractView> viewArg, + int detail, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool isSimulated) + : UIEventWithKeyState(eventType, canBubble, cancelable, viewArg, 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; + + computePageLocation(); +} + +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; + + computePageLocation(); +} + +static float pageZoomFactor(UIEvent* event) +{ + DOMWindow* window = event->view(); + if (!window) + return 1; + Frame* frame = window->frame(); + if (!frame) + return 1; + return frame->pageZoomFactor(); +} + +void MouseRelatedEvent::computePageLocation() +{ + float zoomFactor = pageZoomFactor(this); + setAbsoluteLocation(roundedIntPoint(FloatPoint(pageX() * zoomFactor, pageY() * zoomFactor))); +} + +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()->updateStyleIfNeeded(); + + // Adjust offsetX/Y to be relative to the target's position. + if (!isSimulated()) { + if (RenderObject* r = targ->renderer()) { + FloatPoint localPos = r->absoluteToLocal(absoluteLocation(), false, true); + float zoomFactor = pageZoomFactor(this); + m_offsetX = lroundf(localPos.x() / zoomFactor); + m_offsetY = lroundf(localPos.y() / zoomFactor); + } + } + + // Adjust layerX/Y to be relative to the layer. + // FIXME: We're pretty sure this is the wrong definition 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->parentNode(); + if (n) { + RenderLayer* layer = n->renderer()->enclosingLayer(); + layer->updateLayerPosition(); + for (; layer; layer = layer->parent()) { + m_layerX -= layer->x(); + m_layerY -= layer->y(); + } + } +} + +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/Source/WebCore/dom/MouseRelatedEvent.h b/Source/WebCore/dom/MouseRelatedEvent.h new file mode 100644 index 0000000..fc494d1 --- /dev/null +++ b/Source/WebCore/dom/MouseRelatedEvent.h @@ -0,0 +1,87 @@ +/* + * 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 "IntPoint.h" +#include "UIEventWithKeyState.h" + +namespace WebCore { + + // Internal only: Helper class for what's common between mouse and wheel events. + class MouseRelatedEvent : public UIEventWithKeyState { + public: + // Note that these values are adjusted to counter the effects of zoom, so that values + // exposed via DOM APIs are invariant under zooming. + 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; + + // Page point in "absolute" coordinates (i.e. post-zoomed, page-relative coords, + // usable with RenderObject::absoluteToLocal). + IntPoint absoluteLocation() const { return m_absoluteLocation; } + void setAbsoluteLocation(const IntPoint& p) { m_absoluteLocation = p; } + + protected: + MouseRelatedEvent(); + MouseRelatedEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, + int detail, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool isSimulated = false); + + void initCoordinates(); + void initCoordinates(int clientX, int clientY); + virtual void receivedTarget(); + + void computePageLocation(); + + // 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; + IntPoint m_absoluteLocation; + bool m_isSimulated; + }; + +} // namespace WebCore + +#endif // MouseRelatedEvent_h diff --git a/Source/WebCore/dom/MutationEvent.cpp b/Source/WebCore/dom/MutationEvent.cpp new file mode 100644 index 0000000..890742f --- /dev/null +++ b/Source/WebCore/dom/MutationEvent.cpp @@ -0,0 +1,70 @@ +/* + * 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, 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 "MutationEvent.h" + +namespace WebCore { + +MutationEvent::MutationEvent() + : m_attrChange(0) +{ +} + +MutationEvent::MutationEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<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) +{ +} + +MutationEvent::~MutationEvent() +{ +} + +void MutationEvent::initMutationEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<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/Source/WebCore/dom/MutationEvent.h b/Source/WebCore/dom/MutationEvent.h new file mode 100644 index 0000000..f2ba759 --- /dev/null +++ b/Source/WebCore/dom/MutationEvent.h @@ -0,0 +1,80 @@ +/* + * 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, 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 MutationEvent_h +#define MutationEvent_h + +#include "Event.h" +#include "Node.h" + +namespace WebCore { + + class MutationEvent : public Event { + public: + virtual ~MutationEvent(); + + enum attrChangeType { + MODIFICATION = 1, + ADDITION = 2, + REMOVAL = 3 + }; + + static PassRefPtr<MutationEvent> create() + { + return adoptRef(new MutationEvent); + } + + static PassRefPtr<MutationEvent> create(const AtomicString& type, bool canBubble, PassRefPtr<Node> relatedNode = 0, + const String& prevValue = String(), const String& newValue = String(), const String& attrName = String(), unsigned short attrChange = 0) + { + return adoptRef(new MutationEvent(type, canBubble, false, relatedNode, prevValue, newValue, attrName, attrChange)); + } + + void initMutationEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<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: + MutationEvent(); + MutationEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<Node> relatedNode, + const String& prevValue, const String& newValue, + const String& attrName, unsigned short attrChange); + + 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/Source/WebCore/dom/MutationEvent.idl b/Source/WebCore/dom/MutationEvent.idl new file mode 100644 index 0000000..99a6aaa --- /dev/null +++ b/Source/WebCore/dom/MutationEvent.idl @@ -0,0 +1,47 @@ +/* + * 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 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 DOMString 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/Source/WebCore/dom/NameNodeList.cpp b/Source/WebCore/dom/NameNodeList.cpp new file mode 100644 index 0000000..2ffa577 --- /dev/null +++ b/Source/WebCore/dom/NameNodeList.cpp @@ -0,0 +1,50 @@ +/** + * 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(rootNode) + , m_nodeName(name) +{ +} + +NameNodeList::~NameNodeList() +{ + m_rootNode->removeCachedNameNodeList(this, m_nodeName); +} + +bool NameNodeList::nodeMatches(Element* testNode) const +{ + return testNode->getAttribute(nameAttr) == m_nodeName; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/NameNodeList.h b/Source/WebCore/dom/NameNodeList.h new file mode 100644 index 0000000..6a1b22e --- /dev/null +++ b/Source/WebCore/dom/NameNodeList.h @@ -0,0 +1,53 @@ +/* + * 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, 2007m 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 NameNodeList_h +#define NameNodeList_h + +#include "DynamicNodeList.h" +#include <wtf/Forward.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + + // NodeList which lists all Nodes in a Element with a given "name" attribute + class NameNodeList : public DynamicNodeList { + public: + static PassRefPtr<NameNodeList> create(PassRefPtr<Node> rootNode, const String& name) + { + return adoptRef(new NameNodeList(rootNode, name)); + } + + virtual ~NameNodeList(); + + private: + NameNodeList(PassRefPtr<Node> rootNode, const String& name); + + virtual bool nodeMatches(Element*) const; + + AtomicString m_nodeName; + }; + +} // namespace WebCore + +#endif // NameNodeList_h diff --git a/Source/WebCore/dom/NamedNodeMap.cpp b/Source/WebCore/dom/NamedNodeMap.cpp new file mode 100644 index 0000000..bc56f7e --- /dev/null +++ b/Source/WebCore/dom/NamedNodeMap.cpp @@ -0,0 +1,367 @@ +/* + * 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, 2008, 2009 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 "NamedNodeMap.h" + +#include "Attr.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(); +} + +inline void NamedNodeMap::detachAttributesFromElement() +{ + size_t size = m_attributes.size(); + for (size_t i = 0; i < size; i++) { + if (Attr* attr = m_attributes[i]->attr()) + attr->m_element = 0; + } +} + +NamedNodeMap::~NamedNodeMap() +{ + detachAttributesFromElement(); +} + +PassRefPtr<Node> NamedNodeMap::getNamedItem(const String& name) const +{ + Attribute* a = getAttributeItem(name, shouldIgnoreAttributeCase(m_element)); + if (!a) + return 0; + + return a->createAttrIfNeeded(m_element); +} + +PassRefPtr<Node> NamedNodeMap::getNamedItemNS(const String& namespaceURI, const String& localName) const +{ + return getNamedItem(QualifiedName(nullAtom, localName, namespaceURI)); +} + +PassRefPtr<Node> NamedNodeMap::removeNamedItem(const String& name, ExceptionCode& ec) +{ + Attribute* a = getAttributeItem(name, shouldIgnoreAttributeCase(m_element)); + if (!a) { + ec = NOT_FOUND_ERR; + return 0; + } + + return removeNamedItem(a->name(), ec); +} + +PassRefPtr<Node> NamedNodeMap::removeNamedItemNS(const String& namespaceURI, const String& localName, ExceptionCode& ec) +{ + return removeNamedItem(QualifiedName(nullAtom, localName, namespaceURI), ec); +} + +PassRefPtr<Node> NamedNodeMap::getNamedItem(const QualifiedName& name) const +{ + Attribute* a = getAttributeItem(name); + if (!a) + return 0; + + return a->createAttrIfNeeded(m_element); +} + +PassRefPtr<Node> NamedNodeMap::setNamedItem(Node* arg, ExceptionCode& ec) +{ + if (!m_element || !arg) { + ec = NOT_FOUND_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() != m_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 (attr->isId()) + m_element->updateId(old ? old->value() : nullAtom, a->value()); + + // ### slightly inefficient - resizes attribute array twice. + RefPtr<Node> r; + if (old) { + r = old->createAttrIfNeeded(m_element); + removeAttribute(a->name()); + } + + addAttribute(a); + return r.release(); +} + +PassRefPtr<Node> NamedNodeMap::setNamedItemNS(Node* node, ExceptionCode& ec) +{ + return setNamedItem(node, ec); +} + +// 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> NamedNodeMap::removeNamedItem(const QualifiedName& name, ExceptionCode& ec) +{ + Attribute* a = getAttributeItem(name); + if (!a) { + ec = NOT_FOUND_ERR; + return 0; + } + + RefPtr<Attr> r = a->createAttrIfNeeded(m_element); + + if (r->isId()) + m_element->updateId(a->value(), nullAtom); + + removeAttribute(name); + return r.release(); +} + +PassRefPtr<Node> NamedNodeMap::item(unsigned index) const +{ + if (index >= length()) + return 0; + + return m_attributes[index]->createAttrIfNeeded(m_element); +} + +void NamedNodeMap::copyAttributesToVector(Vector<RefPtr<Attribute> >& copy) +{ + copy = m_attributes; +} + +Attribute* NamedNodeMap::getAttributeItemSlowCase(const String& name, bool shouldIgnoreAttributeCase) const +{ + unsigned len = length(); + + // Continue to checking case-insensitively and/or full namespaced names if necessary: + for (unsigned i = 0; i < len; ++i) { + const QualifiedName& attrName = m_attributes[i]->name(); + if (!attrName.hasPrefix()) { + if (shouldIgnoreAttributeCase && equalIgnoringCase(name, attrName.localName())) + return m_attributes[i].get(); + } else { + // FIXME: Would be faster to do this comparison without calling toString, which + // generates a temporary string by concatenation. But this branch is only reached + // if the attribute name has a prefix, which is rare in HTML. + if (equalPossiblyIgnoringCase(name, attrName.toString(), shouldIgnoreAttributeCase)) + return m_attributes[i].get(); + } + } + return 0; +} + +void NamedNodeMap::clearAttributes() +{ + m_classNames.clear(); + m_mappedAttributeCount = 0; + + detachAttributesFromElement(); + m_attributes.clear(); +} + +void NamedNodeMap::detachFromElement() +{ + // This can't happen if the holder of the map is JavaScript, because we mark the + // element if the map is alive. So it has no impact on web page behavior. Because + // of that, we can simply clear all the attributes to avoid accessing stale + // pointers to do things like create Attr objects. + m_element = 0; + clearAttributes(); +} + +void NamedNodeMap::setAttributes(const NamedNodeMap& other) +{ + // clone all attributes in the other map, but attach to our element + if (!m_element) + return; + + // If assigning the map changes the id attribute, we need to call + // updateId. + Attribute* oldId = getAttributeItem(m_element->document()->idAttributeName()); + Attribute* newId = other.getAttributeItem(m_element->document()->idAttributeName()); + + if (oldId || newId) + m_element->updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom); + + clearAttributes(); + unsigned newLength = other.length(); + m_attributes.resize(newLength); + for (unsigned i = 0; i < newLength; i++) + m_attributes[i] = other.m_attributes[i]->clone(); + + // 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, HTMLNamedNodeMap, 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 < newLength; i++) + m_element->attributeChanged(m_attributes[i].get(), true); +} + +void NamedNodeMap::addAttribute(PassRefPtr<Attribute> prpAttribute) +{ + RefPtr<Attribute> attribute = prpAttribute; + + // Add the attribute to the list + m_attributes.append(attribute); + + if (Attr* attr = attribute->attr()) + attr->m_element = m_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 insertAttribute() during parsing + if (m_element) { + m_element->attributeChanged(attribute.get()); + // Because of our updateStyleAttribute() style modification events are never sent at the right time, so don't bother sending them. + if (attribute->name() != styleAttr) { + m_element->dispatchAttrAdditionEvent(attribute.get()); + m_element->dispatchSubtreeModifiedEvent(); + } + } +} + +void NamedNodeMap::removeAttribute(const QualifiedName& name) +{ + unsigned len = length(); + unsigned index = len; + for (unsigned i = 0; i < len; ++i) { + if (m_attributes[i]->name().matches(name)) { + index = i; + break; + } + } + + if (index >= len) + return; + + // Remove the attribute from the list + RefPtr<Attribute> attr = m_attributes[index].get(); + if (Attr* a = m_attributes[index]->attr()) + a->m_element = 0; + + m_attributes.remove(index); + + // Notify the element that the attribute has been removed + // dispatch appropriate mutation events + if (m_element && !attr->m_value.isNull()) { + AtomicString value = attr->m_value; + attr->m_value = nullAtom; + m_element->attributeChanged(attr.get()); + attr->m_value = value; + } + if (m_element) { + m_element->dispatchAttrRemovalEvent(attr.get()); + m_element->dispatchSubtreeModifiedEvent(); + } +} + +void NamedNodeMap::setClass(const String& classStr) +{ + if (!element()->hasClass()) { + m_classNames.clear(); + return; + } + + m_classNames.set(classStr, element()->document()->inQuirksMode()); +} + +int NamedNodeMap::declCount() const +{ + int result = 0; + for (unsigned i = 0; i < length(); i++) { + Attribute* attr = attributeItem(i); + if (attr->decl()) { + ASSERT(attr->isMappedAttribute()); + result++; + } + } + return result; +} + +bool NamedNodeMap::mapsEquivalent(const NamedNodeMap* otherMap) const +{ + if (!otherMap) + return false; + + unsigned len = length(); + if (len != otherMap->length()) + return false; + + for (unsigned i = 0; i < len; i++) { + Attribute* attr = attributeItem(i); + Attribute* otherAttr = otherMap->getAttributeItem(attr->name()); + if (!otherAttr || attr->value() != otherAttr->value()) + return false; + } + + return true; +} + +bool NamedNodeMap::mappedMapsEquivalent(const NamedNodeMap* 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++) { + Attribute* attr = attributeItem(i); + if (attr->decl()) { + ASSERT(attr->isMappedAttribute()); + + Attribute* otherAttr = otherMap->getAttributeItem(attr->name()); + if (!otherAttr || attr->value() != otherAttr->value()) + return false; + } + } + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/NamedNodeMap.h b/Source/WebCore/dom/NamedNodeMap.h new file mode 100644 index 0000000..c3c2cd9 --- /dev/null +++ b/Source/WebCore/dom/NamedNodeMap.h @@ -0,0 +1,160 @@ +/* + * 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, 2008, 2010 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 NamedNodeMap_h +#define NamedNodeMap_h + +#include "Attribute.h" +#include "SpaceSplitString.h" + +namespace WebCore { + +class Node; + +typedef int ExceptionCode; + +class NamedNodeMap : public RefCounted<NamedNodeMap> { + friend class Element; +public: + static PassRefPtr<NamedNodeMap> create(Element* element = 0) + { + return adoptRef(new NamedNodeMap(element)); + } + + ~NamedNodeMap(); + + // Public DOM interface. + + PassRefPtr<Node> getNamedItem(const String& name) const; + PassRefPtr<Node> removeNamedItem(const String& name, ExceptionCode&); + + PassRefPtr<Node> getNamedItemNS(const String& namespaceURI, const String& localName) const; + PassRefPtr<Node> removeNamedItemNS(const String& namespaceURI, const String& localName, ExceptionCode&); + + PassRefPtr<Node> getNamedItem(const QualifiedName& name) const; + PassRefPtr<Node> removeNamedItem(const QualifiedName& name, ExceptionCode&); + PassRefPtr<Node> setNamedItem(Node*, ExceptionCode&); + PassRefPtr<Node> setNamedItemNS(Node*, ExceptionCode&); + + PassRefPtr<Node> item(unsigned index) const; + size_t length() const { return m_attributes.size(); } + bool isEmpty() const { return !length(); } + + // Internal interface. + + void setAttributes(const NamedNodeMap&); + + Attribute* attributeItem(unsigned index) const { return m_attributes[index].get(); } + Attribute* getAttributeItem(const QualifiedName&) const; + + void copyAttributesToVector(Vector<RefPtr<Attribute> >&); + + void shrinkToLength() { m_attributes.shrinkCapacity(length()); } + void reserveInitialCapacity(unsigned capacity) { m_attributes.reserveInitialCapacity(capacity); } + + // Used during parsing: only inserts if not already there. No error checking! + void insertAttribute(PassRefPtr<Attribute> newAttribute, bool allowDuplicates) + { + ASSERT(!m_element); + if (allowDuplicates || !getAttributeItem(newAttribute->name())) + addAttribute(newAttribute); + } + + const AtomicString& idForStyleResolution() const { return m_idForStyleResolution; } + void setIdForStyleResolution(const AtomicString& newId) { m_idForStyleResolution = newId; } + + // FIXME: These two functions should be merged if possible. + bool mapsEquivalent(const NamedNodeMap* otherMap) const; + bool mappedMapsEquivalent(const NamedNodeMap* otherMap) const; + + // These functions do no error checking. + void addAttribute(PassRefPtr<Attribute>); + void removeAttribute(const QualifiedName&); + + Element* element() const { return m_element; } + + void clearClass() { m_classNames.clear(); } + void setClass(const String&); + const SpaceSplitString& classNames() const { return m_classNames; } + + bool hasMappedAttributes() const { return m_mappedAttributeCount > 0; } + void declRemoved() { m_mappedAttributeCount--; } + void declAdded() { m_mappedAttributeCount++; } + +private: + NamedNodeMap(Element* element) + : m_mappedAttributeCount(0) + , m_element(element) + { + } + + void detachAttributesFromElement(); + void detachFromElement(); + Attribute* getAttributeItem(const String& name, bool shouldIgnoreAttributeCase) const; + Attribute* getAttributeItemSlowCase(const String& name, bool shouldIgnoreAttributeCase) const; + void clearAttributes(); + int declCount() const; + + int m_mappedAttributeCount; + SpaceSplitString m_classNames; + Element* m_element; + Vector<RefPtr<Attribute> > m_attributes; + AtomicString m_idForStyleResolution; +}; + +inline Attribute* NamedNodeMap::getAttributeItem(const QualifiedName& name) const +{ + unsigned len = length(); + for (unsigned i = 0; i < len; ++i) { + if (m_attributes[i]->name().matches(name)) + return m_attributes[i].get(); + } + return 0; +} + +// We use a boolean parameter instead of calling shouldIgnoreAttributeCase so that the caller +// can tune the behavior (hasAttribute is case sensitive whereas getAttribute is not). +inline Attribute* NamedNodeMap::getAttributeItem(const String& name, bool shouldIgnoreAttributeCase) const +{ + unsigned len = length(); + bool doSlowCheck = shouldIgnoreAttributeCase; + + // Optimize for the case where the attribute exists and its name exactly matches. + for (unsigned i = 0; i < len; ++i) { + const QualifiedName& attrName = m_attributes[i]->name(); + if (!attrName.hasPrefix()) { + if (name == attrName.localName()) + return m_attributes[i].get(); + } else + doSlowCheck = true; + } + + if (doSlowCheck) + return getAttributeItemSlowCase(name, shouldIgnoreAttributeCase); + return 0; +} + +} // namespace WebCore + +#endif // NamedNodeMap_h diff --git a/Source/WebCore/dom/NamedNodeMap.idl b/Source/WebCore/dom/NamedNodeMap.idl new file mode 100644 index 0000000..4d36577 --- /dev/null +++ b/Source/WebCore/dom/NamedNodeMap.idl @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * Copyright (C) 2007, 2009 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 [ + CustomMarkFunction, + HasIndexGetter, + HasNameGetter + ] 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/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp new file mode 100644 index 0000000..8c12285 --- /dev/null +++ b/Source/WebCore/dom/Node.cpp @@ -0,0 +1,3087 @@ +/* + * 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, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "Node.h" + +#ifdef ANDROID_DOM_LOGGING +#define LOG_TAG "webcore" +#include "AndroidLog.h" +#endif + +#include "AXObjectCache.h" +#include "Attr.h" +#include "Attribute.h" +#include "CSSParser.h" +#include "CSSRule.h" +#include "CSSRuleList.h" +#include "CSSSelector.h" +#include "CSSSelectorList.h" +#include "CSSStyleRule.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "ChildNodeList.h" +#include "ClassNodeList.h" +#include "ContextMenuController.h" +#include "DOMImplementation.h" +#include "Document.h" +#include "DocumentType.h" +#include "DynamicNodeList.h" +#include "Element.h" +#include "Event.h" +#include "EventContext.h" +#include "EventException.h" +#include "EventHandler.h" +#include "EventListener.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLNames.h" +#include "InspectorInstrumentation.h" +#include "KeyboardEvent.h" +#include "LabelsNodeList.h" +#include "Logging.h" +#include "MouseEvent.h" +#include "MutationEvent.h" +#include "NameNodeList.h" +#include "NamedNodeMap.h" +#include "NodeRareData.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PlatformWheelEvent.h" +#include "ProcessingInstruction.h" +#include "ProgressEvent.h" +#include "RegisteredEventListener.h" +#include "RenderBlock.h" +#include "RenderBox.h" +#include "RenderFullScreen.h" +#include "RenderView.h" +#include "ScopedEventQueue.h" +#include "ScriptController.h" +#include "SelectorNodeList.h" +#include "StaticNodeList.h" +#include "TagNodeList.h" +#include "Text.h" +#include "TextEvent.h" +#include "UIEvent.h" +#include "UIEventWithKeyState.h" +#include "WebKitAnimationEvent.h" +#include "WebKitTransitionEvent.h" +#include "WheelEvent.h" +#include "WindowEventContext.h" +#include "XMLNames.h" +#include "htmlediting.h" +#include <wtf/HashSet.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/UnusedParam.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> + +#if ENABLE(DOM_STORAGE) +#include "StorageEvent.h" +#endif + +#if ENABLE(SVG) +#include "SVGElementInstance.h" +#include "SVGNames.h" +#include "SVGUseElement.h" +#endif + +#if ENABLE(XHTMLMP) +#include "HTMLNoScriptElement.h" +#endif + +#if USE(JSC) +#include <runtime/JSGlobalData.h> +#endif + +#define DUMP_NODE_STATISTICS 0 + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +static HashSet<Node*>* gNodesDispatchingSimulatedClicks = 0; + +bool Node::isSupported(const String& feature, const String& version) +{ + return DOMImplementation::hasFeature(feature, version); +} + +#if DUMP_NODE_STATISTICS +static HashSet<Node*> liveNodeSet; +#endif + +void Node::dumpStatistics() +{ +#if DUMP_NODE_STATISTICS + size_t nodesWithRareData = 0; + + size_t elementNodes = 0; + size_t attrNodes = 0; + size_t textNodes = 0; + size_t cdataNodes = 0; + size_t commentNodes = 0; + size_t entityReferenceNodes = 0; + size_t entityNodes = 0; + size_t piNodes = 0; + size_t documentNodes = 0; + size_t docTypeNodes = 0; + size_t fragmentNodes = 0; + size_t notationNodes = 0; + size_t xpathNSNodes = 0; + + HashMap<String, size_t> perTagCount; + + size_t attributes = 0; + size_t mappedAttributes = 0; + size_t mappedAttributesWithStyleDecl = 0; + size_t attributesWithAttr = 0; + size_t attrMaps = 0; + + for (HashSet<Node*>::iterator it = liveNodeSet.begin(); it != liveNodeSet.end(); ++it) { + Node* node = *it; + + if (node->hasRareData()) + ++nodesWithRareData; + + switch (node->nodeType()) { + case ELEMENT_NODE: { + ++elementNodes; + + // Tag stats + Element* element = static_cast<Element*>(node); + pair<HashMap<String, size_t>::iterator, bool> result = perTagCount.add(element->tagName(), 1); + if (!result.second) + result.first->second++; + + // AttributeMap stats + if (NamedNodeMap* attrMap = element->attributes(true)) { + attributes += attrMap->length(); + ++attrMaps; + for (unsigned i = 0; i < attrMap->length(); ++i) { + Attribute* attr = attrMap->attributeItem(i); + if (attr->attr()) + ++attributesWithAttr; + if (attr->isMappedAttribute()) { + ++mappedAttributes; + if (attr->style()) + ++mappedAttributesWithStyleDecl; + } + } + } + break; + } + case ATTRIBUTE_NODE: { + ++attrNodes; + break; + } + case TEXT_NODE: { + ++textNodes; + break; + } + case CDATA_SECTION_NODE: { + ++cdataNodes; + break; + } + case COMMENT_NODE: { + ++commentNodes; + break; + } + case ENTITY_REFERENCE_NODE: { + ++entityReferenceNodes; + break; + } + case ENTITY_NODE: { + ++entityNodes; + break; + } + case PROCESSING_INSTRUCTION_NODE: { + ++piNodes; + break; + } + case DOCUMENT_NODE: { + ++documentNodes; + break; + } + case DOCUMENT_TYPE_NODE: { + ++docTypeNodes; + break; + } + case DOCUMENT_FRAGMENT_NODE: { + ++fragmentNodes; + break; + } + case NOTATION_NODE: { + ++notationNodes; + break; + } + case XPATH_NAMESPACE_NODE: { + ++xpathNSNodes; + break; + } + } + } + + printf("Number of Nodes: %d\n\n", liveNodeSet.size()); + printf("Number of Nodes with RareData: %zu\n\n", nodesWithRareData); + + printf("NodeType distrubution:\n"); + printf(" Number of Element nodes: %zu\n", elementNodes); + printf(" Number of Attribute nodes: %zu\n", attrNodes); + printf(" Number of Text nodes: %zu\n", textNodes); + printf(" Number of CDATASection nodes: %zu\n", cdataNodes); + printf(" Number of Comment nodes: %zu\n", commentNodes); + printf(" Number of EntityReference nodes: %zu\n", entityReferenceNodes); + printf(" Number of Entity nodes: %zu\n", entityNodes); + printf(" Number of ProcessingInstruction nodes: %zu\n", piNodes); + printf(" Number of Document nodes: %zu\n", documentNodes); + printf(" Number of DocumentType nodes: %zu\n", docTypeNodes); + printf(" Number of DocumentFragment nodes: %zu\n", fragmentNodes); + printf(" Number of Notation nodes: %zu\n", notationNodes); + printf(" Number of XPathNS nodes: %zu\n", xpathNSNodes); + + printf("Element tag name distibution:\n"); + for (HashMap<String, size_t>::iterator it = perTagCount.begin(); it != perTagCount.end(); ++it) + printf(" Number of <%s> tags: %zu\n", it->first.utf8().data(), it->second); + + printf("Attribute Maps:\n"); + printf(" Number of Attributes (non-Node and Node): %zu [%zu]\n", attributes, sizeof(Attribute)); + printf(" Number of Attributes that are mapped: %zu\n", mappedAttributes); + printf(" Number of Attributes with a StyleDeclaration: %zu\n", mappedAttributesWithStyleDecl); + printf(" Number of Attributes with an Attr: %zu\n", attributesWithAttr); + printf(" Number of NamedNodeMaps: %zu [%zu]\n", attrMaps, sizeof(NamedNodeMap)); +#endif +} + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter nodeCounter("WebCoreNode"); + +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::StyleChange Node::diff(const RenderStyle* s1, const RenderStyle* s2) +{ + // 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(FIRST_LETTER); + EDisplay display2 = s2 ? s2->display() : NONE; + bool fl2 = s2 && s2->hasPseudoStyle(FIRST_LETTER); + + // We just detach if a renderer acquires or loses a column-span, since spanning elements + // typically won't contain much content. + bool colSpan1 = s1 && s1->columnSpan(); + bool colSpan2 = s2 && s2->columnSpan(); + + if (display1 != display2 || fl1 != fl2 || colSpan1 != colSpan2 || (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; + + // For nth-child and other positional rules, treat styles as different if they have + // changed positionally in the DOM. This way subsequent sibling resolutions won't be confused + // by the wrong child index and evaluate to incorrect results. + if (ch == NoChange && s1->childIndex() != s2->childIndex()) + ch = NoInherit; + + // 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->hasAnyPublicPseudoStyles()) { + for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; ch == NoChange && pseudoId < FIRST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) { + if (s1->hasPseudoStyle(pseudoId)) { + RenderStyle* ps2 = s2->getCachedPseudoStyle(pseudoId); + if (!ps2) + ch = NoInherit; + else { + RenderStyle* ps1 = s1->getCachedPseudoStyle(pseudoId); + ch = ps1 && *ps1 == *ps2 ? NoChange : NoInherit; + } + } + } + } + + return ch; +} + +void Node::trackForDebugging() +{ +#ifndef NDEBUG + if (shouldIgnoreLeaks) + ignoreSet.add(this); + else + nodeCounter.increment(); +#endif + +#if DUMP_NODE_STATISTICS + liveNodeSet.add(this); +#endif +} + +Node::~Node() +{ +#ifndef NDEBUG + HashSet<Node*>::iterator it = ignoreSet.find(this); + if (it != ignoreSet.end()) + ignoreSet.remove(it); + else + nodeCounter.decrement(); +#endif + +#if DUMP_NODE_STATISTICS + liveNodeSet.remove(this); +#endif + + if (!hasRareData()) + ASSERT(!NodeRareData::rareDataMap().contains(this)); + else { + if (m_document && rareData()->nodeLists()) + m_document->removeNodeListCache(); + + NodeRareData::NodeRareDataMap& dataMap = NodeRareData::rareDataMap(); + NodeRareData::NodeRareDataMap::iterator it = dataMap.find(this); + ASSERT(it != dataMap.end()); + delete it->second; + dataMap.remove(it); + } + + if (renderer()) + detach(); + + if (AXObjectCache::accessibilityEnabled() && m_document && m_document->axObjectCacheExists()) + m_document->axObjectCache()->removeNodeForUse(this); + + if (m_previous) + m_previous->setNextSibling(0); + if (m_next) + m_next->setPreviousSibling(0); + + if (m_document) + m_document->selfOnlyDeref(); +} + +#ifdef NDEBUG + +static inline void setWillMoveToNewOwnerDocumentWasCalled(bool) +{ +} + +static inline void setDidMoveToNewOwnerDocumentWasCalled(bool) +{ +} + +#else + +static bool willMoveToNewOwnerDocumentWasCalled; +static bool didMoveToNewOwnerDocumentWasCalled; + +static void setWillMoveToNewOwnerDocumentWasCalled(bool wasCalled) +{ + willMoveToNewOwnerDocumentWasCalled = wasCalled; +} + +static void setDidMoveToNewOwnerDocumentWasCalled(bool wasCalled) +{ + didMoveToNewOwnerDocumentWasCalled = wasCalled; +} + +#endif + +void Node::setDocument(Document* document) +{ + ASSERT(!inDocument() || m_document == document); + if (inDocument() || m_document == document) + return; + + document->selfOnlyRef(); + + setWillMoveToNewOwnerDocumentWasCalled(false); + willMoveToNewOwnerDocument(); + ASSERT(willMoveToNewOwnerDocumentWasCalled); + +#if USE(JSC) + updateDOMNodeDocument(this, m_document, document); +#endif + + if (hasRareData() && rareData()->nodeLists()) { + if (m_document) + m_document->removeNodeListCache(); + document->addNodeListCache(); + } + + if (m_document) { + m_document->moveNodeIteratorsToNewDocument(this, document); + m_document->selfOnlyDeref(); + } + + m_document = document; + + setDidMoveToNewOwnerDocumentWasCalled(false); + didMoveToNewOwnerDocument(); + ASSERT(didMoveToNewOwnerDocumentWasCalled); +} + +NodeRareData* Node::rareData() const +{ + ASSERT(hasRareData()); + return NodeRareData::rareDataFromMap(this); +} + +NodeRareData* Node::ensureRareData() +{ + if (hasRareData()) + return rareData(); + + ASSERT(!NodeRareData::rareDataMap().contains(this)); + NodeRareData* data = createRareData(); + NodeRareData::rareDataMap().set(this, data); + setFlag(HasRareDataFlag); + return data; +} + +NodeRareData* Node::createRareData() +{ + return new NodeRareData; +} + +Element* Node::shadowHost() const +{ + return toElement(getFlag(IsShadowRootFlag) ? parent() : 0); +} + +void Node::setShadowHost(Element* host) +{ + if (host) + setFlag(IsShadowRootFlag); + else + clearFlag(IsShadowRootFlag); + + setParent(host); +} + +short Node::tabIndex() const +{ + return hasRareData() ? rareData()->tabIndex() : 0; +} + +void Node::setTabIndexExplicitly(short i) +{ + ensureRareData()->setTabIndexExplicitly(i); +} + +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, setting nodeValue has no effect. +} + +PassRefPtr<NodeList> Node::childNodes() +{ + NodeRareData* data = ensureRareData(); + if (!data->nodeLists()) { + data->setNodeLists(NodeListsNodeData::create()); + if (document()) + document()->addNodeListCache(); + } + + return ChildNodeList::create(this, data->nodeLists()->m_childNodeListCaches.get()); +} + +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> newChild, Node* refChild, ExceptionCode& ec, bool shouldLazyAttach) +{ + if (!isContainerNode()) { + ec = HIERARCHY_REQUEST_ERR; + return false; + } + return toContainerNode(this)->insertBefore(newChild, refChild, ec, shouldLazyAttach); +} + +bool Node::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec, bool shouldLazyAttach) +{ + if (!isContainerNode()) { + ec = HIERARCHY_REQUEST_ERR; + return false; + } + return toContainerNode(this)->replaceChild(newChild, oldChild, ec, shouldLazyAttach); +} + +bool Node::removeChild(Node* oldChild, ExceptionCode& ec) +{ + if (!isContainerNode()) { + ec = NOT_FOUND_ERR; + return false; + } + return toContainerNode(this)->removeChild(oldChild, ec); +} + +bool Node::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bool shouldLazyAttach) +{ + if (!isContainerNode()) { + ec = HIERARCHY_REQUEST_ERR; + return false; + } + return toContainerNode(this)->appendChild(newChild, ec, shouldLazyAttach); +} + +void Node::remove(ExceptionCode& ec) +{ + if (ContainerNode* parent = parentNode()) + parent->removeChild(this, ec); + else + ec = HIERARCHY_REQUEST_ERR; +} + +void Node::normalize() +{ + // Go through the subtree beneath us, normalizing all nodes. This means that + // any two adjacent text nodes are merged and any empty text nodes are removed. + + RefPtr<Node> node = this; + while (Node* firstChild = node->firstChild()) + node = firstChild; + while (node) { + NodeType type = node->nodeType(); + if (type == ELEMENT_NODE) + static_cast<Element*>(node.get())->normalizeAttributes(); + + if (node == this) + break; + + if (type != TEXT_NODE) { + node = node->traverseNextNodePostOrder(); + continue; + } + + Text* text = static_cast<Text*>(node.get()); + + // Remove empty text nodes. + if (!text->length()) { + // Care must be taken to get the next node before removing the current node. + node = node->traverseNextNodePostOrder(); + ExceptionCode ec; + text->remove(ec); + continue; + } + + // Merge text nodes. + while (Node* nextSibling = node->nextSibling()) { + if (nextSibling->nodeType() != TEXT_NODE) + break; + RefPtr<Text> nextText = static_cast<Text*>(nextSibling); + + // Remove empty text nodes. + if (!nextText->length()) { + ExceptionCode ec; + nextText->remove(ec); + continue; + } + + // Both non-empty text nodes. Merge them. + unsigned offset = text->length(); + ExceptionCode ec; + text->appendData(nextText->data(), ec); + document()->textNodesMerged(nextText.get(), offset); + nextText->remove(ec); + } + + node = node->traverseNextNodePostOrder(); + } +} + +const AtomicString& Node::virtualPrefix() 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::virtualLocalName() const +{ + return nullAtom; +} + +const AtomicString& Node::virtualNamespaceURI() const +{ + return nullAtom; +} + +void Node::deprecatedParserAddChild(PassRefPtr<Node>) +{ +} + +bool Node::isContentEditable() const +{ + return parentOrHostNode() && parentOrHostNode()->isContentEditable(); +} + +bool Node::isContentRichlyEditable() const +{ + return parentOrHostNode() && parentOrHostNode()->isContentRichlyEditable(); +} + +bool Node::shouldUseInputMethod() const +{ + return isContentEditable(); +} + +RenderBox* Node::renderBox() const +{ + return m_renderer && m_renderer->isBox() ? toRenderBox(m_renderer) : 0; +} + +RenderBoxModelObject* Node::renderBoxModelObject() const +{ + return m_renderer && m_renderer->isBoxModelObject() ? toRenderBoxModelObject(m_renderer) : 0; +} + +IntRect Node::getRect() const +{ + if (renderer()) + return renderer()->absoluteBoundingBoxRect(true); + return IntRect(); +} + +IntRect Node::renderRect(bool* isReplaced) +{ + RenderObject* hitRenderer = this->renderer(); + ASSERT(hitRenderer); + RenderObject* renderer = hitRenderer; + while (renderer && !renderer->isBody() && !renderer->isRoot()) { + if (renderer->isRenderBlock() || renderer->isInlineBlockOrInlineTable() || renderer->isReplaced()) { + *isReplaced = renderer->isReplaced(); + return renderer->absoluteBoundingBoxRect(true); + } + renderer = renderer->parent(); + } + return IntRect(); +} + +bool Node::hasNonEmptyBoundingBox() const +{ + // Before calling absoluteRects, check for the common case where the renderer + // is non-empty, since this is a faster check and almost always returns true. + RenderBoxModelObject* box = renderBoxModelObject(); + if (!box) + return false; + if (!box->borderBoundingBox().isEmpty()) + return true; + + Vector<IntRect> rects; + FloatPoint absPos = renderer()->localToAbsolute(); + renderer()->absoluteRects(rects, absPos.x(), absPos.y()); + size_t n = rects.size(); + for (size_t i = 0; i < n; ++i) + if (!rects[i].isEmpty()) + return true; + + return false; +} + +inline void Node::setStyleChange(StyleChangeType changeType) +{ + m_nodeFlags = (m_nodeFlags & ~StyleChangeMask) | changeType; +} + +inline void Node::markAncestorsWithChildNeedsStyleRecalc() +{ + for (ContainerNode* p = parentOrHostNode(); p && !p->childNeedsStyleRecalc(); p = p->parentOrHostNode()) + p->setChildNeedsStyleRecalc(); + + if (document()->childNeedsStyleRecalc()) + document()->scheduleStyleRecalc(); +} + +void Node::refEventTarget() +{ + ref(); +} + +void Node::derefEventTarget() +{ + deref(); +} + +void Node::setNeedsStyleRecalc(StyleChangeType changeType) +{ + ASSERT(changeType != NoStyleChange); + if (!attached()) // changed compared to what? + return; + + StyleChangeType existingChangeType = styleChangeType(); + if (changeType > existingChangeType) + setStyleChange(changeType); + + if (existingChangeType == NoStyleChange) + markAncestorsWithChildNeedsStyleRecalc(); +} + +void Node::lazyAttach(ShouldSetAttached shouldSetAttached) +{ + for (Node* n = this; n; n = n->traverseNextNode(this)) { + if (n->firstChild()) + n->setChildNeedsStyleRecalc(); + n->setStyleChange(FullStyleChange); + if (shouldSetAttached == SetAttached) + n->setAttached(); + } + markAncestorsWithChildNeedsStyleRecalc(); +} + +void Node::setFocus(bool b) +{ + if (b || hasRareData()) + ensureRareData()->setFocused(b); +} + +bool Node::rareDataFocused() const +{ + ASSERT(hasRareData()); + return rareData()->isFocused(); +} + +bool Node::supportsFocus() const +{ + return hasRareData() && rareData()->tabIndexSetExplicitly(); +} + +bool Node::isFocusable() const +{ + if (!inDocument() || !supportsFocus()) + return false; + + if (renderer()) + ASSERT(!renderer()->needsLayout()); + else + // If the node is in a display:none tree it might say it needs style recalc but + // the whole document is actually up to date. + ASSERT(!document()->childNeedsStyleRecalc()); + + // FIXME: Even if we are not visible, we might have a child that is visible. + // Hyatt wants to fix that some day with a "has visible content" flag or the like. + if (!renderer() || renderer()->style()->visibility() != VISIBLE) + return false; + + return true; +} + +bool Node::isKeyboardFocusable(KeyboardEvent*) const +{ + return isFocusable() && tabIndex() >= 0; +} + +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) +{ + NodeRareData* data = ensureRareData(); + if (!data->nodeLists()) { + data->setNodeLists(NodeListsNodeData::create()); + document()->addNodeListCache(); + } else if (!m_document || !m_document->hasNodeListCaches()) { + // We haven't been receiving notifications while there were no registered lists, so the cache is invalid now. + data->nodeLists()->invalidateCaches(); + } + + if (list->hasOwnCaches()) + data->nodeLists()->m_listsWithCaches.add(list); +} + +void Node::unregisterDynamicNodeList(DynamicNodeList* list) +{ + ASSERT(rareData()); + ASSERT(rareData()->nodeLists()); + if (list->hasOwnCaches()) { + NodeRareData* data = rareData(); + data->nodeLists()->m_listsWithCaches.remove(list); + if (data->nodeLists()->isEmpty()) { + data->clearNodeLists(); + if (document()) + document()->removeNodeListCache(); + } + } +} + +void Node::notifyLocalNodeListsAttributeChanged() +{ + if (!hasRareData()) + return; + NodeRareData* data = rareData(); + if (!data->nodeLists()) + return; + + if (!isAttributeNode()) + data->nodeLists()->invalidateCachesThatDependOnAttributes(); + else + data->nodeLists()->invalidateCaches(); + + if (data->nodeLists()->isEmpty()) { + data->clearNodeLists(); + document()->removeNodeListCache(); + } +} + +void Node::notifyNodeListsAttributeChanged() +{ + for (Node *n = this; n; n = n->parentNode()) + n->notifyLocalNodeListsAttributeChanged(); +} + +void Node::notifyLocalNodeListsChildrenChanged() +{ + if (!hasRareData()) + return; + NodeRareData* data = rareData(); + if (!data->nodeLists()) + return; + + data->nodeLists()->invalidateCaches(); + + NodeListsNodeData::NodeListSet::iterator end = data->nodeLists()->m_listsWithCaches.end(); + for (NodeListsNodeData::NodeListSet::iterator i = data->nodeLists()->m_listsWithCaches.begin(); i != end; ++i) + (*i)->invalidateCache(); + + if (data->nodeLists()->isEmpty()) { + data->clearNodeLists(); + document()->removeNodeListCache(); + } +} + +void Node::notifyNodeListsChildrenChanged() +{ + for (Node* n = this; n; n = n->parentNode()) + n->notifyLocalNodeListsChildrenChanged(); +} + +void Node::notifyLocalNodeListsLabelChanged() +{ + if (!hasRareData()) + return; + NodeRareData* data = rareData(); + if (!data->nodeLists()) + return; + + if (data->nodeLists()->m_labelsNodeListCache) + data->nodeLists()->m_labelsNodeListCache->invalidateCache(); +} + +void Node::removeCachedClassNodeList(ClassNodeList* list, const String& className) +{ + ASSERT(rareData()); + ASSERT(rareData()->nodeLists()); + ASSERT_UNUSED(list, list->hasOwnCaches()); + + NodeListsNodeData* data = rareData()->nodeLists(); + ASSERT_UNUSED(list, list == data->m_classNodeListCache.get(className)); + data->m_classNodeListCache.remove(className); +} + +void Node::removeCachedNameNodeList(NameNodeList* list, const String& nodeName) +{ + ASSERT(rareData()); + ASSERT(rareData()->nodeLists()); + ASSERT_UNUSED(list, list->hasOwnCaches()); + + NodeListsNodeData* data = rareData()->nodeLists(); + ASSERT_UNUSED(list, list == data->m_nameNodeListCache.get(nodeName)); + data->m_nameNodeListCache.remove(nodeName); +} + +void Node::removeCachedTagNodeList(TagNodeList* list, const QualifiedName& name) +{ + ASSERT(rareData()); + ASSERT(rareData()->nodeLists()); + ASSERT_UNUSED(list, list->hasOwnCaches()); + + NodeListsNodeData* data = rareData()->nodeLists(); + ASSERT_UNUSED(list, list == data->m_tagNodeListCache.get(name.impl())); + data->m_tagNodeListCache.remove(name.impl()); +} + +void Node::removeCachedLabelsNodeList(DynamicNodeList* list) +{ + ASSERT(rareData()); + ASSERT(rareData()->nodeLists()); + ASSERT_UNUSED(list, list->hasOwnCaches()); + + NodeListsNodeData* data = rareData()->nodeLists(); + data->m_labelsNodeListCache = 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::traverseNextNodePostOrder() const +{ + Node* next = nextSibling(); + if (!next) + return parentNode(); + while (Node* firstChild = next->firstChild()) + next = firstChild; + return next; +} + +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. + + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // FIXME: Raise NAMESPACE_ERR if prefix is malformed per the Namespaces in XML specification. + + const AtomicString& nodeNamespaceURI = namespaceURI(); + if ((nodeNamespaceURI.isEmpty() && !prefix.isEmpty()) + || (prefix == xmlAtom && nodeNamespaceURI != XMLNames::xmlNamespaceURI)) { + ec = NAMESPACE_ERR; + return; + } + // Attribute-specific checks are in Attr::setPrefix(). +} + +static bool isChildTypeAllowed(Node* newParent, Node* child) +{ + if (child->nodeType() != Node::DOCUMENT_FRAGMENT_NODE) { + if (!newParent->childTypeAllowed(child->nodeType())) + return false; + return true; + } + + for (Node *n = child->firstChild(); n; n = n->nextSibling()) { + if (!newParent->childTypeAllowed(n->nodeType())) + return false; + } + return true; +} + +bool Node::canReplaceChild(Node* newChild, Node*) +{ + return isChildTypeAllowed(this, newChild); +} + +static void checkAcceptChild(Node* newParent, Node* newChild, ExceptionCode& ec) +{ + // Perform error checking as required by spec for adding a new child. Used by replaceChild(). + + // 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 (newParent->isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // 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() != newParent->document() && newChild->inDocument()) { + // 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. + 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 == newParent || newParent->isDescendantOf(newChild)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } +} + +static void transferOwnerDocument(Document* newDocument, Node* root) +{ + // FIXME: To match Gecko, we should do this for nodes that are already in the document as well. + if (root->document() != newDocument && !root->inDocument()) { + for (Node* node = root; node; node = node->traverseNextNode(root)) + node->setDocument(newDocument); + } +} + +void Node::checkReplaceChild(Node* newChild, Node* oldChild, ExceptionCode& ec) +{ + checkAcceptChild(this, newChild, ec); + if (ec) + return; + + if (!canReplaceChild(newChild, oldChild)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + transferOwnerDocument(document(), newChild); +} + +void Node::checkAddChild(Node *newChild, ExceptionCode& ec) +{ + checkAcceptChild(this, newChild, ec); + if (ec) + return; + + if (!isChildTypeAllowed(this, newChild)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + transferOwnerDocument(document(), newChild); +} + +bool Node::isDescendantOf(const Node *other) const +{ + // Return true if other is an ancestor of this, otherwise false + if (!other) + return false; + for (const ContainerNode* n = parentNode(); n; n = n->parentNode()) { + if (n == other) + return true; + } + return false; +} + +bool Node::contains(const Node* node) const +{ + if (!node) + return false; + return this == node || node->isDescendantOf(this); +} + +bool Node::containsIncludingShadowDOM(Node* node) +{ + if (!node) + return false; + for (Node* n = node; n; n = n->parentOrHostNode()) { + if (n == this) + return true; + } + return false; +} + +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(); + } + } + + setAttached(); + clearNeedsStyleRecalc(); +} + +void Node::willRemove() +{ +} + +void Node::detach() +{ + setFlag(InDetachFlag); + + if (renderer()) + renderer()->destroy(); + setRenderer(0); + + Document* doc = document(); + if (hovered()) + doc->hoveredNodeDetached(this); + if (inActiveChain()) + doc->activeChainNodeDetached(this); + + clearFlag(IsActiveFlag); + clearFlag(IsHoveredFlag); + clearFlag(InActiveChainFlag); + clearFlag(IsAttachedFlag); + + clearFlag(InDetachFlag); +} + +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 (parentOrHostNode() && !parentOrHostNode()->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()); + + ContainerNode* parent = parentOrHostNode(); + ASSERT(parent); + + RenderObject* parentRenderer = parent->renderer(); + RenderObject* nextRenderer = this->nextRenderer(); + +#if ENABLE(FULLSCREEN_API) + // If this node is a fullscreen node, create a new anonymous full screen + // renderer. + if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) { + RenderFullScreen* fullscreenRenderer = new (document()->renderArena()) RenderFullScreen(document()); + fullscreenRenderer->setStyle(RenderFullScreen::createFullScreenStyle()); + parentRenderer->addChild(fullscreenRenderer, 0); + parentRenderer = fullscreenRenderer; + nextRenderer = 0; + document()->setFullScreenRenderer(fullscreenRenderer); + } +#endif + + if (parentRenderer && parentRenderer->canHaveChildren() && parent->childShouldCreateRenderer(this)) { + RefPtr<RenderStyle> style = styleForRenderer(); + if (rendererIsNeeded(style.get())) { + if (RenderObject* r = createRenderer(document()->renderArena(), style.get())) { + if (!parentRenderer->isChildAllowed(r, style.get())) + r->destroy(); + else { + setRenderer(r); + renderer()->setAnimatableStyle(style.release()); + parentRenderer->addChild(renderer(), nextRenderer); + } + } + } + } +} + +PassRefPtr<RenderStyle> Node::styleForRenderer() +{ + if (isElementNode()) { + bool allowSharing = true; +#if ENABLE(XHTMLMP) + // noscript needs the display property protected - it's a special case + allowSharing = localName() != HTMLNames::noscriptTag.localName(); +#endif + return document()->styleSelector()->styleForElement(static_cast<Element*>(this), 0, allowSharing); + } + return parentNode() && parentNode()->renderer() ? parentNode()->renderer()->style() : 0; +} + +bool Node::rendererIsNeeded(RenderStyle *style) +{ + return (document()->documentElement() == this) || (style->display() != NONE); +} + +RenderObject* Node::createRenderer(RenderArena*, RenderStyle*) +{ + ASSERT(false); + return 0; +} + +RenderStyle* Node::nonRendererRenderStyle() const +{ + return 0; +} + +void Node::setRenderStyle(PassRefPtr<RenderStyle> s) +{ + if (m_renderer) + m_renderer->setAnimatableStyle(s); +} + +RenderStyle* Node::virtualComputedStyle(PseudoId pseudoElementSpecifier) +{ + return parentOrHostNode() ? parentOrHostNode()->computedStyle(pseudoElementSpecifier) : 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; + + if (renderer()) { + RenderStyle* style = renderer()->style(); + // We allow selections to begin within an element that has -webkit-user-select: none set, + // but if the element is draggable then dragging should take priority over selection. + if (style->userDrag() == DRAG_ELEMENT && style->userSelect() == SELECT_NONE) + return false; + } + return parentOrHostNode() ? parentOrHostNode()->canStartSelection() : true; +} + +Node* Node::shadowAncestorNode() +{ +#if ENABLE(SVG) + // SVG elements living in a shadow tree only occur 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* root = shadowTreeRootNode(); + if (root) + return root->shadowHost(); + return this; +} + +Node* Node::shadowTreeRootNode() +{ + Node* root = this; + while (root) { + if (root->isShadowRoot()) + return root; + root = root->parentNodeGuaranteedHostFree(); + } + return 0; +} + +bool Node::isInShadowTree() +{ + for (Node* n = this; n; n = n->parentNode()) + if (n->isShadowRoot()) + return true; + return false; +} + +bool Node::isBlockFlow() const +{ + return renderer() && renderer()->isBlockFlow(); +} + +bool Node::isBlockFlowOrBlockTable() const +{ + return renderer() && (renderer()->isBlockFlow() || (renderer()->isTable() && !renderer()->isInline())); +} + +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::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 AtomicString& name) +{ + return getElementsByTagNameNS(starAtom, name); +} + +PassRefPtr<NodeList> Node::getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName) +{ + if (localName.isNull()) + return 0; + + NodeRareData* data = ensureRareData(); + if (!data->nodeLists()) { + data->setNodeLists(NodeListsNodeData::create()); + document()->addNodeListCache(); + } + + String name = localName; + if (document()->isHTMLDocument()) + name = localName.lower(); + + AtomicString localNameAtom = name; + + pair<NodeListsNodeData::TagNodeListCache::iterator, bool> result = data->nodeLists()->m_tagNodeListCache.add(QualifiedName(nullAtom, localNameAtom, namespaceURI).impl(), 0); + if (!result.second) + return PassRefPtr<TagNodeList>(result.first->second); + + RefPtr<TagNodeList> list = TagNodeList::create(this, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localNameAtom); + result.first->second = list.get(); + return list.release(); +} + +PassRefPtr<NodeList> Node::getElementsByName(const String& elementName) +{ + NodeRareData* data = ensureRareData(); + if (!data->nodeLists()) { + data->setNodeLists(NodeListsNodeData::create()); + document()->addNodeListCache(); + } + + pair<NodeListsNodeData::NameNodeListCache::iterator, bool> result = data->nodeLists()->m_nameNodeListCache.add(elementName, 0); + if (!result.second) + return PassRefPtr<NodeList>(result.first->second); + + RefPtr<NameNodeList> list = NameNodeList::create(this, elementName); + result.first->second = list.get(); + return list.release(); +} + +PassRefPtr<NodeList> Node::getElementsByClassName(const String& classNames) +{ + NodeRareData* data = ensureRareData(); + if (!data->nodeLists()) { + data->setNodeLists(NodeListsNodeData::create()); + document()->addNodeListCache(); + } + + pair<NodeListsNodeData::ClassNodeListCache::iterator, bool> result = data->nodeLists()->m_classNodeListCache.add(classNames, 0); + if (!result.second) + return PassRefPtr<NodeList>(result.first->second); + + RefPtr<ClassNodeList> list = ClassNodeList::create(this, classNames); + result.first->second = list.get(); + return list.release(); +} + +PassRefPtr<Element> Node::querySelector(const String& selectors, ExceptionCode& ec) +{ + if (selectors.isEmpty()) { + ec = SYNTAX_ERR; + return 0; + } + bool strictParsing = !document()->inQuirksMode(); + CSSParser p(strictParsing); + + CSSSelectorList querySelectorList; + p.parseSelector(selectors, document(), querySelectorList); + + if (!querySelectorList.first()) { + ec = SYNTAX_ERR; + return 0; + } + + // throw a NAMESPACE_ERR if the selector includes any namespace prefixes. + if (querySelectorList.selectorsNeedNamespaceResolution()) { + ec = NAMESPACE_ERR; + return 0; + } + + CSSStyleSelector::SelectorChecker selectorChecker(document(), strictParsing); + + // FIXME: we could also optimize for the the [id="foo"] case + if (strictParsing && inDocument() && querySelectorList.hasOneSelector() && querySelectorList.first()->m_match == CSSSelector::Id) { + Element* element = document()->getElementById(querySelectorList.first()->m_value); + if (element && (isDocumentNode() || element->isDescendantOf(this)) && selectorChecker.checkSelector(querySelectorList.first(), element)) + return element; + return 0; + } + + // 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); + for (CSSSelector* selector = querySelectorList.first(); selector; selector = CSSSelectorList::next(selector)) { + if (selectorChecker.checkSelector(selector, element)) + return element; + } + } + } + + return 0; +} + +PassRefPtr<NodeList> Node::querySelectorAll(const String& selectors, ExceptionCode& ec) +{ + if (selectors.isEmpty()) { + ec = SYNTAX_ERR; + return 0; + } + bool strictParsing = !document()->inQuirksMode(); + CSSParser p(strictParsing); + + CSSSelectorList querySelectorList; + p.parseSelector(selectors, document(), querySelectorList); + + if (!querySelectorList.first()) { + ec = SYNTAX_ERR; + return 0; + } + + // Throw a NAMESPACE_ERR if the selector includes any namespace prefixes. + if (querySelectorList.selectorsNeedNamespaceResolution()) { + ec = NAMESPACE_ERR; + return 0; + } + + return createSelectorNodeList(this, querySelectorList); +} + +Document *Node::ownerDocument() const +{ + Document *doc = document(); + return doc == this ? 0 : doc; +} + +KURL Node::baseURI() const +{ + return parentNode() ? parentNode()->baseURI() : KURL(); +} + +bool Node::isEqualNode(Node* other) const +{ + if (!other) + return false; + + NodeType nodeType = this->nodeType(); + 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; + + NamedNodeMap* attributes = this->attributes(); + NamedNodeMap* otherAttributes = other->attributes(); + + if (!attributes && otherAttributes) + return false; + + if (attributes && !attributes->mapsEquivalent(otherAttributes)) + 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; + + if (nodeType == DOCUMENT_TYPE_NODE) { + const DocumentType* documentTypeThis = static_cast<const DocumentType*>(this); + const DocumentType* documentTypeOther = static_cast<const DocumentType*>(other); + + if (documentTypeThis->publicId() != documentTypeOther->publicId()) + return false; + + if (documentTypeThis->systemId() != documentTypeOther->systemId()) + return false; + + if (documentTypeThis->internalSubset() != documentTypeOther->internalSubset()) + return false; + + NamedNodeMap* entities = documentTypeThis->entities(); + NamedNodeMap* otherEntities = documentTypeOther->entities(); + if (!entities && otherEntities) + return false; + if (entities && !entities->mapsEquivalent(otherEntities)) + return false; + + NamedNodeMap* notations = documentTypeThis->notations(); + NamedNodeMap* otherNotations = documentTypeOther->notations(); + if (!notations && otherNotations) + return false; + if (notations && !notations->mapsEquivalent(otherNotations)) + return false; + } + + return true; +} + +bool Node::isDefaultNamespace(const AtomicString& namespaceURIMaybeEmpty) const +{ + const AtomicString& namespaceURI = namespaceURIMaybeEmpty.isEmpty() ? nullAtom : namespaceURIMaybeEmpty; + + switch (nodeType()) { + case ELEMENT_NODE: { + const Element* elem = static_cast<const Element*>(this); + + if (elem->prefix().isNull()) + return elem->namespaceURI() == namespaceURI; + + if (elem->hasAttributes()) { + NamedNodeMap* attrs = elem->attributes(); + + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute* attr = attrs->attributeItem(i); + + if (attr->localName() == xmlnsAtom) + return attr->value() == namespaceURI; + } + } + + if (Element* ancestor = ancestorElement()) + return ancestor->isDefaultNamespace(namespaceURI); + + return false; + } + case DOCUMENT_NODE: + if (Element* de = static_cast<const Document*>(this)->documentElement()) + return de->isDefaultNamespace(namespaceURI); + return false; + 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 AtomicString &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: + if (Element* de = static_cast<const Document*>(this)->documentElement()) + return de->lookupPrefix(namespaceURI); + return String(); + 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()) { + NamedNodeMap *attrs = elem->attributes(); + + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute *attr = attrs->attributeItem(i); + + if (attr->prefix() == xmlnsAtom && attr->localName() == prefix) { + if (!attr->value().isEmpty()) + return attr->value(); + + return String(); + } else if (attr->localName() == xmlnsAtom && prefix.isNull()) { + if (!attr->value().isEmpty()) + return attr->value(); + + return String(); + } + } + } + if (Element* ancestor = ancestorElement()) + return ancestor->lookupNamespaceURI(prefix); + return String(); + } + case DOCUMENT_NODE: + if (Element* de = static_cast<const Document*>(this)->documentElement()) + return de->lookupNamespaceURI(prefix); + return String(); + 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 AtomicString &_namespaceURI, const Element *originalElement) const +{ + if (_namespaceURI.isNull()) + return String(); + + if (originalElement->lookupNamespaceURI(prefix()) == _namespaceURI) + return prefix(); + + if (hasAttributes()) { + NamedNodeMap *attrs = attributes(); + + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute *attr = attrs->attributeItem(i); + + if (attr->prefix() == xmlnsAtom && + attr->value() == _namespaceURI && + originalElement->lookupNamespaceURI(attr->localName()) == _namespaceURI) + return attr->localName(); + } + } + + if (Element* ancestor = ancestorElement()) + return ancestor->lookupNamespacePrefix(_namespaceURI, originalElement); + return String(); +} + +static void appendTextContent(const Node* node, bool convertBRsToNewlines, bool& isNullString, StringBuilder& content) +{ + switch (node->nodeType()) { + case Node::TEXT_NODE: + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + isNullString = false; + content.append(static_cast<const CharacterData*>(node)->data()); + break; + + case Node::PROCESSING_INSTRUCTION_NODE: + isNullString = false; + content.append(static_cast<const ProcessingInstruction*>(node)->data()); + break; + + case Node::ELEMENT_NODE: + if (node->hasTagName(brTag) && convertBRsToNewlines) { + isNullString = false; + content.append('\n'); + break; + } + // Fall through. + case Node::ATTRIBUTE_NODE: + case Node::ENTITY_NODE: + case Node::ENTITY_REFERENCE_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + isNullString = false; + for (Node* child = node->firstChild(); child; child = child->nextSibling()) { + if (child->nodeType() == Node::COMMENT_NODE || child->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) + continue; + appendTextContent(child, convertBRsToNewlines, isNullString, content); + } + break; + + case Node::DOCUMENT_NODE: + case Node::DOCUMENT_TYPE_NODE: + case Node::NOTATION_NODE: + case Node::XPATH_NAMESPACE_NODE: + break; + } +} + +String Node::textContent(bool convertBRsToNewlines) const +{ + StringBuilder content; + bool isNullString = true; + appendTextContent(this, convertBRsToNewlines, isNullString, content); + return isNullString ? String() : content.toString(); +} + +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); + return; + case ELEMENT_NODE: + case ATTRIBUTE_NODE: + case ENTITY_NODE: + case ENTITY_REFERENCE_NODE: + case DOCUMENT_FRAGMENT_NODE: { + ContainerNode* container = toContainerNode(this); + container->removeChildren(); + if (!text.isEmpty()) + container->appendChild(document()->createTextNode(text), ec); + return; + } + case DOCUMENT_NODE: + case DOCUMENT_TYPE_NODE: + case NOTATION_NODE: + case XPATH_NAMESPACE_NODE: + // Do nothing. + return; + } + ASSERT_NOT_REACHED(); +} + +Element* Node::ancestorElement() const +{ + // In theory, there can be EntityReference nodes between elements, but this is currently not supported. + for (ContainerNode* n = parentNode(); n; n = n->parentNode()) { + if (n->isElementNode()) + return static_cast<Element*>(n); + } + return 0; +} + +bool Node::offsetInCharacters() const +{ + return false; +} + +unsigned short Node::compareDocumentPosition(Node* otherNode) +{ + // It is not clear what should be done if |otherNode| is 0. + if (!otherNode) + return DOCUMENT_POSITION_DISCONNECTED; + + if (otherNode == this) + return DOCUMENT_POSITION_EQUIVALENT; + + Attr* attr1 = nodeType() == ATTRIBUTE_NODE ? static_cast<Attr*>(this) : 0; + Attr* attr2 = otherNode->nodeType() == ATTRIBUTE_NODE ? static_cast<Attr*>(otherNode) : 0; + + Node* start1 = attr1 ? attr1->ownerElement() : this; + Node* start2 = attr2 ? attr2->ownerElement() : otherNode; + + // If either of start1 or start2 is null, then we are disconnected, since one of the nodes is + // an orphaned attribute node. + if (!start1 || !start2) + return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; + + Vector<Node*, 16> chain1; + Vector<Node*, 16> chain2; + if (attr1) + chain1.append(attr1); + if (attr2) + chain2.append(attr2); + + if (attr1 && attr2 && start1 == start2 && start1) { + // We are comparing two attributes on the same node. Crawl our attribute map + // and see which one we hit first. + NamedNodeMap* map = attr1->ownerElement()->attributes(true); + unsigned length = map->length(); + for (unsigned i = 0; i < length; ++i) { + // If neither of the two determining nodes is a child node and nodeType is the same for both determining nodes, then an + // implementation-dependent order between the determining nodes is returned. This order is stable as long as no nodes of + // the same nodeType are inserted into or removed from the direct container. This would be the case, for example, + // when comparing two attributes of the same element, and inserting or removing additional attributes might change + // the order between existing attributes. + Attribute* attr = map->attributeItem(i); + if (attr1->attr() == attr) + return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_FOLLOWING; + if (attr2->attr() == attr) + return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_PRECEDING; + } + + ASSERT_NOT_REACHED(); + return DOCUMENT_POSITION_DISCONNECTED; + } + + // If one node is in the document and the other is not, we must be disconnected. + // If the nodes have different owning documents, they must be disconnected. Note that we avoid + // comparing Attr nodes here, since they return false from inDocument() all the time (which seems like a bug). + if (start1->inDocument() != start2->inDocument() || + start1->document() != start2->document()) + return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; + + // We need to find a common ancestor container, and then compare the indices of the two immediate children. + Node* current; + for (current = start1; current; current = current->parentNode()) + chain1.append(current); + for (current = start2; current; current = current->parentNode()) + chain2.append(current); + + // Walk the two chains backwards and look for the first difference. + unsigned index1 = chain1.size(); + unsigned index2 = chain2.size(); + for (unsigned i = min(index1, index2); i; --i) { + Node* child1 = chain1[--index1]; + Node* child2 = chain2[--index2]; + if (child1 != child2) { + // If one of the children is an attribute, it wins. + if (child1->nodeType() == ATTRIBUTE_NODE) + return DOCUMENT_POSITION_FOLLOWING; + if (child2->nodeType() == ATTRIBUTE_NODE) + return DOCUMENT_POSITION_PRECEDING; + + if (!child2->nextSibling()) + return DOCUMENT_POSITION_FOLLOWING; + if (!child1->nextSibling()) + return DOCUMENT_POSITION_PRECEDING; + + // Otherwise we need to see which node occurs first. Crawl backwards from child2 looking for child1. + for (Node* child = child2->previousSibling(); child; child = child->previousSibling()) { + if (child == child1) + return DOCUMENT_POSITION_FOLLOWING; + } + return DOCUMENT_POSITION_PRECEDING; + } + } + + // There was no difference between the two parent chains, i.e., one was a subset of the other. The shorter + // chain is the ancestor. + return index1 < index2 ? + DOCUMENT_POSITION_FOLLOWING | DOCUMENT_POSITION_CONTAINED_BY : + DOCUMENT_POSITION_PRECEDING | DOCUMENT_POSITION_CONTAINS; +} + +FloatPoint Node::convertToPage(const FloatPoint& p) const +{ + // If there is a renderer, just ask it to do the conversion + if (renderer()) + return renderer()->localToAbsolute(p, false, true); + + // Otherwise go up the tree looking for a renderer + Element *parent = ancestorElement(); + if (parent) + return parent->convertToPage(p); + + // No parent - no conversion needed + return p; +} + +FloatPoint Node::convertFromPage(const FloatPoint& p) const +{ + // If there is a renderer, just ask it to do the conversion + if (renderer()) + return renderer()->absoluteToLocal(p, false, true); + + // Otherwise go up the tree looking for a renderer + Element *parent = ancestorElement(); + if (parent) + return parent->convertFromPage(p); + + // No parent - no conversion needed + return p; +} + +#if !defined(NDEBUG) || defined(ANDROID_DOM_LOGGING) + +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(); +#ifdef ANDROID_DOM_LOGGING + bool hasNoneWhitespace = false; + for (int i = value.length()-1; i >= 0; i--) + if (!isSpaceOrNewline(value[i])) { + hasNoneWhitespace = true; + break; + } +#endif + value.replace('\\', "\\\\"); + value.replace('\n', "\\n"); +#ifdef ANDROID_DOM_LOGGING + if (hasNoneWhitespace) + DUMP_DOM_LOGD("%s%s\t%p \"%s\"\n", prefix, nodeName().utf8().data(), this, value.utf8().data()); +#else + fprintf(stderr, "%s%s\t%p \"%s\"\n", prefix, nodeName().utf8().data(), this, value.utf8().data()); +#endif + } else { + String attrs = ""; + appendAttributeDesc(this, attrs, classAttr, " CLASS="); + appendAttributeDesc(this, attrs, styleAttr, " STYLE="); +#ifdef ANDROID_DOM_LOGGING + appendAttributeDesc(this, attrs, idAttr, " ID="); + appendAttributeDesc(this, attrs, nameAttr, " NAME="); + DUMP_DOM_LOGD("%s%s\t%p%s\n", prefix, nodeName().utf8().data(), this, attrs.utf8().data()); +#else + fprintf(stderr, "%s%s\t%p%s\n", prefix, nodeName().utf8().data(), this, attrs.utf8().data()); +#endif + } +} + +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()) { +#ifdef ANDROID_DOM_LOGGING + String prefix = ""; +#endif + if (node == markedNode1) +#ifdef ANDROID_DOM_LOGGING + prefix.append(markedLabel1); +#else + fprintf(stderr, "%s", markedLabel1); +#endif + if (node == markedNode2) +#ifdef ANDROID_DOM_LOGGING + prefix.append(markedLabel2); +#else + fprintf(stderr, "%s", markedLabel2); +#endif + +#ifdef ANDROID_DOM_LOGGING + for (const Node* tmpNode = node; tmpNode && tmpNode != rootNode; tmpNode = tmpNode->parentNode()) + prefix.append("\t"); + node->showNode(prefix.utf8().data()); +#else + for (const Node* tmpNode = node; tmpNode && tmpNode != rootNode; tmpNode = tmpNode->parentNode()) + fprintf(stderr, "\t"); + node->showNode(); +#endif + } +} + +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 + +// -------- + +void NodeListsNodeData::invalidateCaches() +{ + m_childNodeListCaches->reset(); + + if (m_labelsNodeListCache) + m_labelsNodeListCache->invalidateCache(); + TagNodeListCache::const_iterator tagCacheEnd = m_tagNodeListCache.end(); + for (TagNodeListCache::const_iterator it = m_tagNodeListCache.begin(); it != tagCacheEnd; ++it) + it->second->invalidateCache(); + invalidateCachesThatDependOnAttributes(); +} + +void NodeListsNodeData::invalidateCachesThatDependOnAttributes() +{ + ClassNodeListCache::iterator classCacheEnd = m_classNodeListCache.end(); + for (ClassNodeListCache::iterator it = m_classNodeListCache.begin(); it != classCacheEnd; ++it) + it->second->invalidateCache(); + + NameNodeListCache::iterator nameCacheEnd = m_nameNodeListCache.end(); + for (NameNodeListCache::iterator it = m_nameNodeListCache.begin(); it != nameCacheEnd; ++it) + it->second->invalidateCache(); + if (m_labelsNodeListCache) + m_labelsNodeListCache->invalidateCache(); +} + +bool NodeListsNodeData::isEmpty() const +{ + if (!m_listsWithCaches.isEmpty()) + return false; + + if (m_childNodeListCaches->refCount()) + return false; + + TagNodeListCache::const_iterator tagCacheEnd = m_tagNodeListCache.end(); + for (TagNodeListCache::const_iterator it = m_tagNodeListCache.begin(); it != tagCacheEnd; ++it) { + if (it->second->refCount()) + return false; + } + + ClassNodeListCache::const_iterator classCacheEnd = m_classNodeListCache.end(); + for (ClassNodeListCache::const_iterator it = m_classNodeListCache.begin(); it != classCacheEnd; ++it) { + if (it->second->refCount()) + return false; + } + + NameNodeListCache::const_iterator nameCacheEnd = m_nameNodeListCache.end(); + for (NameNodeListCache::const_iterator it = m_nameNodeListCache.begin(); it != nameCacheEnd; ++it) { + if (it->second->refCount()) + return false; + } + + if (m_labelsNodeListCache) + return false; + + return true; +} + +void Node::getSubresourceURLs(ListHashSet<KURL>& urls) const +{ + addSubresourceAttributeURLs(urls); +} + +Node* Node::enclosingLinkEventParentOrSelf() +{ + for (Node* node = this; node; node = node->parentOrHostNode()) { + // For imagemaps, the enclosing link node is the associated area element not the image itself. + // So we don't let images be the enclosingLinkNode, even though isLink sometimes returns true + // for them. + if (node->isLink() && !node->hasTagName(imgTag)) + return node; + } + + return 0; +} + +#ifdef ANDROID_INSTRUMENT +static size_t nodeSize = 0; + +void* Node::operator new(size_t size) +{ + nodeSize += size; + return ::operator new(size); +} + +void* Node::operator new[](size_t size) +{ + nodeSize += size; + return ::operator new[](size); +} + +void Node::operator delete(void* p, size_t size) +{ + nodeSize -= size; + ::operator delete(p); +} + +void Node::operator delete[](void* p, size_t size) +{ + nodeSize -= size; + ::operator delete[](p); +} + +size_t Node::reportDOMNodesSize() +{ + return nodeSize; +} +#endif + +// -------- + +ScriptExecutionContext* Node::scriptExecutionContext() const +{ + return document(); +} + +void Node::insertedIntoDocument() +{ + setInDocument(); +} + +void Node::removedFromDocument() +{ + clearInDocument(); +} + +void Node::willMoveToNewOwnerDocument() +{ + ASSERT(!willMoveToNewOwnerDocumentWasCalled); + setWillMoveToNewOwnerDocumentWasCalled(true); +} + +void Node::didMoveToNewOwnerDocument() +{ + ASSERT(!didMoveToNewOwnerDocumentWasCalled); + setDidMoveToNewOwnerDocumentWasCalled(true); +} + +#if ENABLE(SVG) +static inline HashSet<SVGElementInstance*> instancesForSVGElement(Node* node) +{ + HashSet<SVGElementInstance*> instances; + + ASSERT(node); + if (!node->isSVGElement() || node->shadowTreeRootNode()) + return HashSet<SVGElementInstance*>(); + + SVGElement* element = static_cast<SVGElement*>(node); + if (!element->isStyled()) + return HashSet<SVGElementInstance*>(); + + SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(element); + ASSERT(!styledElement->instanceUpdatesBlocked()); + + return styledElement->instancesForElement(); +} +#endif + +static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) +{ + if (!targetNode->EventTarget::addEventListener(eventType, listener, useCapture)) + return false; + + if (Document* document = targetNode->document()) + document->addListenerTypeIfNeeded(eventType); + + return true; +} + +bool Node::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) +{ +#if !ENABLE(SVG) + return tryAddEventListener(this, eventType, listener, useCapture); +#else + if (!isSVGElement()) + return tryAddEventListener(this, eventType, listener, useCapture); + + HashSet<SVGElementInstance*> instances = instancesForSVGElement(this); + if (instances.isEmpty()) + return tryAddEventListener(this, eventType, listener, useCapture); + + RefPtr<EventListener> listenerForRegularTree = listener; + RefPtr<EventListener> listenerForShadowTree = listenerForRegularTree; + + // Add event listener to regular DOM element + if (!tryAddEventListener(this, eventType, listenerForRegularTree.release(), useCapture)) + return false; + + // Add event listener to all shadow tree DOM element instances + const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); + for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { + ASSERT((*it)->shadowTreeElement()); + ASSERT((*it)->correspondingElement() == this); + + RefPtr<EventListener> listenerForCurrentShadowTreeElement = listenerForShadowTree; + bool result = tryAddEventListener((*it)->shadowTreeElement(), eventType, listenerForCurrentShadowTreeElement.release(), useCapture); + ASSERT_UNUSED(result, result); + } + + return true; +#endif +} + +static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& eventType, EventListener* listener, bool useCapture) +{ + if (!targetNode->EventTarget::removeEventListener(eventType, listener, useCapture)) + return false; + + // FIXME: Notify Document that the listener has vanished. We need to keep track of a number of + // listeners for each type, not just a bool - see https://bugs.webkit.org/show_bug.cgi?id=33861 + + return true; +} + +bool Node::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) +{ +#if !ENABLE(SVG) + return tryRemoveEventListener(this, eventType, listener, useCapture); +#else + if (!isSVGElement()) + return tryRemoveEventListener(this, eventType, listener, useCapture); + + HashSet<SVGElementInstance*> instances = instancesForSVGElement(this); + if (instances.isEmpty()) + return tryRemoveEventListener(this, eventType, listener, useCapture); + + // EventTarget::removeEventListener creates a PassRefPtr around the given EventListener + // object when creating a temporary RegisteredEventListener object used to look up the + // event listener in a cache. If we want to be able to call removeEventListener() multiple + // times on different nodes, we have to delay its immediate destruction, which would happen + // after the first call below. + RefPtr<EventListener> protector(listener); + + // Remove event listener from regular DOM element + if (!tryRemoveEventListener(this, eventType, listener, useCapture)) + return false; + + // Remove event listener from all shadow tree DOM element instances + const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); + for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { + ASSERT((*it)->correspondingElement() == this); + + SVGElement* shadowTreeElement = (*it)->shadowTreeElement(); + ASSERT(shadowTreeElement); + + if (tryRemoveEventListener(shadowTreeElement, eventType, listener, useCapture)) + continue; + + // This case can only be hit for event listeners created from markup + ASSERT(listener->wasCreatedFromMarkup()); + + // If the event listener 'listener' has been created from markup and has been fired before + // then JSLazyEventListener::parseCode() has been called and m_jsFunction of that listener + // has been created (read: it's not 0 anymore). During shadow tree creation, the event + // listener DOM attribute has been cloned, and another event listener has been setup in + // the shadow tree. If that event listener has not been used yet, m_jsFunction is still 0, + // and tryRemoveEventListener() above will fail. Work around that very seldom problem. + EventTargetData* data = shadowTreeElement->eventTargetData(); + ASSERT(data); + + EventListenerMap::iterator result = data->eventListenerMap.find(eventType); + ASSERT(result != data->eventListenerMap.end()); + + EventListenerVector* entry = result->second; + ASSERT(entry); + + unsigned int index = 0; + bool foundListener = false; + + EventListenerVector::iterator end = entry->end(); + for (EventListenerVector::iterator it = entry->begin(); it != end; ++it) { + if (!(*it).listener->wasCreatedFromMarkup()) { + ++index; + continue; + } + + foundListener = true; + entry->remove(index); + break; + } + + ASSERT(foundListener); + + if (entry->isEmpty()) { + delete entry; + data->eventListenerMap.remove(result); + } + } + + return true; +#endif +} + +EventTargetData* Node::eventTargetData() +{ + return hasRareData() ? rareData()->eventTargetData() : 0; +} + +EventTargetData* Node::ensureEventTargetData() +{ + return ensureRareData()->ensureEventTargetData(); +} + +#if USE(JSC) + +template <class NodeListMap> +void markNodeLists(const NodeListMap& map, JSC::MarkStack& markStack, JSC::JSGlobalData& globalData) +{ + for (typename NodeListMap::const_iterator it = map.begin(); it != map.end(); ++it) + markDOMObjectWrapper(markStack, globalData, it->second); +} + +void Node::markCachedNodeListsSlow(JSC::MarkStack& markStack, JSC::JSGlobalData& globalData) +{ + NodeListsNodeData* nodeLists = rareData()->nodeLists(); + if (!nodeLists) + return; + + markNodeLists(nodeLists->m_classNodeListCache, markStack, globalData); + markNodeLists(nodeLists->m_nameNodeListCache, markStack, globalData); + markNodeLists(nodeLists->m_tagNodeListCache, markStack, globalData); +} + +#endif + +void Node::handleLocalEvents(Event* event) +{ + if (!hasRareData() || !rareData()->eventTargetData()) + return; + + if (disabled() && event->isMouseEvent()) + return; + + fireEventListeners(event); +} + +static inline EventTarget* eventTargetRespectingSVGTargetRules(Node* referenceNode) +{ + ASSERT(referenceNode); + +#if ENABLE(SVG) + if (!referenceNode->isSVGElement()) + return referenceNode; + + // Spec: The event handling for the non-exposed tree works as if the referenced element had been textually included + // as a deeply cloned child of the 'use' element, except that events are dispatched to the SVGElementInstance objects + for (Node* n = referenceNode; n; n = n->parentNode()) { + if (!n->isShadowRoot() || !n->isSVGElement()) + continue; + + Element* shadowTreeParentElement = n->shadowHost(); + ASSERT(shadowTreeParentElement->hasTagName(SVGNames::useTag)); + + if (SVGElementInstance* instance = static_cast<SVGUseElement*>(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode)) + return instance; + } +#endif + + return referenceNode; +} + +void Node::getEventAncestors(Vector<EventContext>& ancestors, EventTarget* originalTarget, EventDispatchBehavior behavior) +{ + if (!inDocument()) + return; + + EventTarget* target = originalTarget; + Node* ancestor = this; + bool shouldSkipNextAncestor = false; + while (true) { + if (ancestor->isShadowRoot()) { + if (behavior == StayInsideShadowDOM) + return; + ancestor = ancestor->shadowHost(); + if (!shouldSkipNextAncestor) + target = ancestor; + } else + ancestor = ancestor->parentNodeGuaranteedHostFree(); + + if (!ancestor) + return; + +#if ENABLE(SVG) + // Skip SVGShadowTreeRootElement. + shouldSkipNextAncestor = ancestor->isSVGElement() && ancestor->isShadowRoot(); + if (shouldSkipNextAncestor) + continue; +#endif + // FIXME: Unroll the extra loop inside eventTargetRespectingSVGTargetRules into this loop. + ancestors.append(EventContext(ancestor, eventTargetRespectingSVGTargetRules(ancestor), target)); + + } +} + +bool Node::dispatchEvent(PassRefPtr<Event> prpEvent) +{ + RefPtr<EventTarget> protect = this; + RefPtr<Event> event = prpEvent; + + event->setTarget(eventTargetRespectingSVGTargetRules(this)); + + RefPtr<FrameView> view = document()->view(); + return dispatchGenericEvent(event.release()); +} + +void Node::dispatchScopedEvent(PassRefPtr<Event> event) +{ + // We need to set the target here because it can go away by the time we actually fire the event. + event->setTarget(eventTargetRespectingSVGTargetRules(this)); + + ScopedEventQueue::instance()->enqueueEvent(event); +} + +static const EventContext* topEventContext(const Vector<EventContext>& ancestors) +{ + return ancestors.isEmpty() ? 0 : &ancestors.last(); +} + +bool Node::dispatchGenericEvent(PassRefPtr<Event> prpEvent) +{ + RefPtr<Event> event(prpEvent); + + ASSERT(!eventDispatchForbidden()); + ASSERT(event->target()); + ASSERT(!event->type().isNull()); // JavaScript code can create an event with an empty name, but not null. + + // Make a vector of ancestors to send the event to. + // If the node is not in a document just send the event to it. + // Be sure to ref all of nodes since event handlers could result in the last reference going away. + RefPtr<Node> thisNode(this); + RefPtr<EventTarget> originalTarget = event->target(); + Vector<EventContext> ancestors; + getEventAncestors(ancestors, originalTarget.get(), event->isMutationEvent() ? StayInsideShadowDOM : RetargetEvent); + + WindowEventContext windowContext(event.get(), this, topEventContext(ancestors)); + + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEvent(document(), *event, windowContext.window(), this, ancestors); + + // Give the target node a chance to do some work before DOM event handlers get a crack. + void* data = preDispatchEventHandler(event.get()); + if (event->propagationStopped()) + goto doneDispatching; + + // Trigger capturing event handlers, starting at the top and working our way down. + event->setEventPhase(Event::CAPTURING_PHASE); + + if (windowContext.handleLocalEvents(event.get()) && event->propagationStopped()) + goto doneDispatching; + + for (size_t i = ancestors.size(); i; --i) { + ancestors[i - 1].handleLocalEvents(event.get()); + if (event->propagationStopped()) + goto doneDispatching; + } + + event->setEventPhase(Event::AT_TARGET); + event->setTarget(originalTarget.get()); + event->setCurrentTarget(eventTargetRespectingSVGTargetRules(this)); + handleLocalEvents(event.get()); + if (event->propagationStopped()) + goto doneDispatching; + + if (event->bubbles() && !event->cancelBubble()) { + // Trigger bubbling event handlers, starting at the bottom and working our way up. + event->setEventPhase(Event::BUBBLING_PHASE); + + size_t size = ancestors.size(); + for (size_t i = 0; i < size; ++i) { + ancestors[i].handleLocalEvents(event.get()); + if (event->propagationStopped() || event->cancelBubble()) + goto doneDispatching; + } + windowContext.handleLocalEvents(event.get()); + } + +doneDispatching: + event->setTarget(originalTarget.get()); + event->setCurrentTarget(0); + event->setEventPhase(0); + + // Pass the data from the preDispatchEventHandler to the postDispatchEventHandler. + postDispatchEventHandler(event.get(), data); + + // Call default event handlers. While the DOM does have a concept of preventing + // default handling, the detail of which handlers are called is an internal + // implementation detail and not part of the DOM. + if (!event->defaultPrevented() && !event->defaultHandled()) { + // Non-bubbling events call only one default event handler, the one for the target. + defaultEventHandler(event.get()); + ASSERT(!event->defaultPrevented()); + if (event->defaultHandled()) + goto doneWithDefault; + // For bubbling events, call default event handlers on the same targets in the + // same order as the bubbling phase. + if (event->bubbles()) { + size_t size = ancestors.size(); + for (size_t i = 0; i < size; ++i) { + ancestors[i].node()->defaultEventHandler(event.get()); + ASSERT(!event->defaultPrevented()); + if (event->defaultHandled()) + goto doneWithDefault; + } + } + } + +doneWithDefault: + + // Ensure that after event dispatch, the event's target object is the + // outermost shadow DOM boundary. + event->setTarget(windowContext.target()); + event->setCurrentTarget(0); + InspectorInstrumentation::didDispatchEvent(cookie); + + return !event->defaultPrevented(); +} + +void Node::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; + + dispatchScopedEvent(MutationEvent::create(eventNames().DOMSubtreeModifiedEvent, true)); +} + +void Node::dispatchUIEvent(const AtomicString& eventType, int detail, PassRefPtr<Event> underlyingEvent) +{ + ASSERT(!eventDispatchForbidden()); + ASSERT(eventType == eventNames().focusinEvent || eventType == eventNames().focusoutEvent || + eventType == eventNames().DOMFocusInEvent || eventType == eventNames().DOMFocusOutEvent || eventType == eventNames().DOMActivateEvent); + + bool cancelable = eventType == eventNames().DOMActivateEvent; + + RefPtr<UIEvent> event = UIEvent::create(eventType, true, cancelable, document()->defaultView(), detail); + event->setUnderlyingEvent(underlyingEvent); + dispatchScopedEvent(event.release()); +} + +bool Node::dispatchKeyEvent(const PlatformKeyboardEvent& key) +{ + RefPtr<KeyboardEvent> keyboardEvent = KeyboardEvent::create(key, document()->defaultView()); + bool r = dispatchEvent(keyboardEvent); + + // 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 Node::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, 0); +} + +void Node::dispatchSimulatedMouseEvent(const AtomicString& eventType, + PassRefPtr<Event> underlyingEvent) +{ + ASSERT(!eventDispatchForbidden()); + + 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(); + } + + // 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); +} + +void Node::dispatchSimulatedClick(PassRefPtr<Event> event, bool sendMouseEvents, bool showPressedLook) +{ + if (!gNodesDispatchingSimulatedClicks) + gNodesDispatchingSimulatedClicks = new HashSet<Node*>; + else if (gNodesDispatchingSimulatedClicks->contains(this)) + return; + + gNodesDispatchingSimulatedClicks->add(this); + + // send mousedown and mouseup before the click, if requested + if (sendMouseEvents) + dispatchSimulatedMouseEvent(eventNames().mousedownEvent, event.get()); + setActive(true, showPressedLook); + if (sendMouseEvents) + dispatchSimulatedMouseEvent(eventNames().mouseupEvent, event.get()); + setActive(false); + + // always send click + dispatchSimulatedMouseEvent(eventNames().clickEvent, event); + + gNodesDispatchingSimulatedClicks->remove(this); +} + +bool Node::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 != eventNames().mousemoveEvent; + + bool swallowEvent = false; + + // Attempting to dispatch with a non-EventTarget relatedTarget causes the relatedTarget to be silently ignored. + RefPtr<Node> relatedTarget = relatedTargetArg; + + int adjustedPageX = pageX; + int adjustedPageY = pageY; + if (Frame* frame = document()->frame()) { + float pageZoom = frame->pageZoomFactor(); + if (pageZoom != 1.0f) { + // Adjust our pageX and pageY to account for the page zoom. + adjustedPageX = lroundf(pageX / pageZoom); + adjustedPageY = lroundf(pageY / pageZoom); + } + } + + RefPtr<MouseEvent> mouseEvent = MouseEvent::create(eventType, + true, cancelable, document()->defaultView(), + detail, screenX, screenY, adjustedPageX, adjustedPageY, + ctrlKey, altKey, shiftKey, metaKey, button, + relatedTarget, 0, isSimulated); + mouseEvent->setUnderlyingEvent(underlyingEvent.get()); + mouseEvent->setAbsoluteLocation(IntPoint(pageX, pageY)); + + dispatchEvent(mouseEvent); + 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 == eventNames().clickEvent && detail == 2) { + RefPtr<Event> doubleClickEvent = MouseEvent::create(eventNames().dblclickEvent, + true, cancelable, document()->defaultView(), + detail, screenX, screenY, adjustedPageX, adjustedPageY, + ctrlKey, altKey, shiftKey, metaKey, button, + relatedTarget, 0, isSimulated); + doubleClickEvent->setUnderlyingEvent(underlyingEvent.get()); + if (defaultHandled) + doubleClickEvent->setDefaultHandled(); + dispatchEvent(doubleClickEvent); + if (doubleClickEvent->defaultHandled() || doubleClickEvent->defaultPrevented()) + swallowEvent = true; + } + + return swallowEvent; +} + +void Node::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()); + + int adjustedPageX = pos.x(); + int adjustedPageY = pos.y(); + if (Frame* frame = document()->frame()) { + float pageZoom = frame->pageZoomFactor(); + if (pageZoom != 1.0f) { + // Adjust our pageX and pageY to account for the page zoom. + adjustedPageX = lroundf(pos.x() / pageZoom); + adjustedPageY = lroundf(pos.y() / pageZoom); + } + } + + WheelEvent::Granularity granularity; + switch (e.granularity()) { + case ScrollByPageWheelEvent: + granularity = WheelEvent::Page; + break; + case ScrollByPixelWheelEvent: + default: + granularity = WheelEvent::Pixel; + break; + } + + RefPtr<WheelEvent> we = WheelEvent::create(e.wheelTicksX(), e.wheelTicksY(), e.deltaX(), e.deltaY(), granularity, + document()->defaultView(), e.globalX(), e.globalY(), adjustedPageX, adjustedPageY, + e.ctrlKey(), e.altKey(), e.shiftKey(), e.metaKey()); + + we->setAbsoluteLocation(IntPoint(pos.x(), pos.y())); + + if (!dispatchEvent(we) || we->defaultHandled()) + e.accept(); + + we.release(); +} + +void Node::dispatchFocusEvent() +{ + dispatchEvent(Event::create(eventNames().focusEvent, false, false)); +} + +void Node::dispatchBlurEvent() +{ + dispatchEvent(Event::create(eventNames().blurEvent, false, false)); +} + +bool Node::disabled() const +{ + return false; +} + +void Node::defaultEventHandler(Event* event) +{ + if (event->target() != this) + return; + const AtomicString& eventType = event->type(); + if (eventType == eventNames().keydownEvent || eventType == eventNames().keypressEvent) { + if (event->isKeyboardEvent()) + if (Frame* frame = document()->frame()) + frame->eventHandler()->defaultKeyboardEventHandler(static_cast<KeyboardEvent*>(event)); + } else if (eventType == eventNames().clickEvent) { + int detail = event->isUIEvent() ? static_cast<UIEvent*>(event)->detail() : 0; + dispatchUIEvent(eventNames().DOMActivateEvent, detail, event); +#if ENABLE(CONTEXT_MENUS) + } else if (eventType == eventNames().contextmenuEvent) { + if (Frame* frame = document()->frame()) + if (Page* page = frame->page()) + page->contextMenuController()->handleContextMenuEvent(event); +#endif + } else if (eventType == eventNames().textInputEvent) { + if (event->isTextEvent()) + if (Frame* frame = document()->frame()) + frame->eventHandler()->defaultTextInputEventHandler(static_cast<TextEvent*>(event)); +#if ENABLE(PAN_SCROLLING) + } else if (eventType == eventNames().mousedownEvent && event->isMouseEvent()) { + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + if (mouseEvent->button() == MiddleButton) { + if (enclosingLinkEventParentOrSelf()) + return; + + RenderObject* renderer = this->renderer(); + while (renderer && (!renderer->isBox() || !toRenderBox(renderer)->canBeScrolledAndHasScrollableArea())) + renderer = renderer->parent(); + + if (renderer) { + if (Frame* frame = document()->frame()) + frame->eventHandler()->startPanScrolling(renderer); + } + } +#endif + } else if (eventType == eventNames().mousewheelEvent && event->isWheelEvent()) { + WheelEvent* wheelEvent = static_cast<WheelEvent*>(event); + + // If we don't have a renderer, send the wheel event to the first node we find with a renderer. + // This is needed for <option> and <optgroup> elements so that <select>s get a wheel scroll. + Node* startNode = this; + while (startNode && !startNode->renderer()) + startNode = startNode->parentOrHostNode(); + + if (startNode && startNode->renderer()) + if (Frame* frame = document()->frame()) + frame->eventHandler()->defaultWheelEventHandler(startNode, wheelEvent); + } else if (event->type() == eventNames().webkitEditableContentChangedEvent) { + dispatchEvent(Event::create(eventNames().inputEvent, true, false)); + } +} + +} // namespace WebCore + +#ifndef NDEBUG + +void showTree(const WebCore::Node* node) +{ + if (node) + node->showTreeForThis(); +} + +#endif diff --git a/Source/WebCore/dom/Node.h b/Source/WebCore/dom/Node.h new file mode 100644 index 0000000..31a4a65 --- /dev/null +++ b/Source/WebCore/dom/Node.h @@ -0,0 +1,748 @@ +/* + * 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, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.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. + * + */ + +#ifndef Node_h +#define Node_h + +#include "EventTarget.h" +#include "KURLHash.h" +#include "RenderStyleConstants.h" +#include "ScriptWrappable.h" +#include "TreeShared.h" +#include <wtf/Forward.h> +#include <wtf/ListHashSet.h> + +#if USE(JSC) +namespace JSC { + class JSGlobalData; + class MarkStack; +} +#endif + +namespace WebCore { + +class Attribute; +class ClassNodeList; +class ContainerNode; +class Document; +class DynamicNodeList; +class Element; +class Event; +class EventContext; +class EventListener; +class FloatPoint; +class Frame; +class IntRect; +class KeyboardEvent; +class NSResolver; +class NamedNodeMap; +class NameNodeList; +class NodeList; +class NodeRareData; +class PlatformKeyboardEvent; +class PlatformMouseEvent; +class PlatformWheelEvent; +class QualifiedName; +class RegisteredEventListener; +class RenderArena; +class RenderBox; +class RenderBoxModelObject; +class RenderObject; +class RenderStyle; +class TagNodeList; + +typedef int ExceptionCode; + +const int nodeStyleChangeShift = 25; + +// SyntheticStyleChange means that we need to go through the entire style change logic even though +// no style property has actually changed. It is used to restructure the tree when, for instance, +// RenderLayers are created or destroyed due to animation changes. +enum StyleChangeType { + NoStyleChange = 0, + InlineStyleChange = 1 << nodeStyleChangeShift, + FullStyleChange = 2 << nodeStyleChangeShift, + SyntheticStyleChange = 3 << nodeStyleChangeShift +}; + +enum EventDispatchBehavior { + RetargetEvent, + StayInsideShadowDOM +}; + +class Node : public EventTarget, public TreeShared<ContainerNode>, public ScriptWrappable { + 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 + }; + enum DocumentPosition { + DOCUMENT_POSITION_EQUIVALENT = 0x00, + DOCUMENT_POSITION_DISCONNECTED = 0x01, + DOCUMENT_POSITION_PRECEDING = 0x02, + DOCUMENT_POSITION_FOLLOWING = 0x04, + DOCUMENT_POSITION_CONTAINS = 0x08, + DOCUMENT_POSITION_CONTAINED_BY = 0x10, + DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20, + }; + + static bool isSupported(const String& feature, const String& version); + + static void startIgnoringLeaks(); + static void stopIgnoringLeaks(); + + static void dumpStatistics(); + + enum StyleChange { NoChange, NoInherit, Inherit, Detach, Force }; + static StyleChange diff(const RenderStyle*, const RenderStyle*); + + virtual ~Node(); + + // DOM methods & attributes for Node + + bool hasTagName(const QualifiedName&) const; + virtual String nodeName() const = 0; + virtual String nodeValue() const; + virtual void setNodeValue(const String&, ExceptionCode&); + virtual NodeType nodeType() const = 0; + ContainerNode* parentNode() const; + Element* parentElement() const; + Node* previousSibling() const { return m_previous; } + Node* nextSibling() const { return m_next; } + PassRefPtr<NodeList> childNodes(); + Node* firstChild() const; + Node* lastChild() const; + bool hasAttributes() const; + NamedNodeMap* attributes() const; + + virtual KURL baseURI() const; + + void getSubresourceURLs(ListHashSet<KURL>&) 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 + bool insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode&, bool shouldLazyAttach = false); + bool replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode&, bool shouldLazyAttach = false); + bool removeChild(Node* child, ExceptionCode&); + bool appendChild(PassRefPtr<Node> newChild, ExceptionCode&, bool shouldLazyAttach = false); + + void remove(ExceptionCode&); + bool hasChildNodes() const { return firstChild(); } + virtual PassRefPtr<Node> cloneNode(bool deep) = 0; + const AtomicString& localName() const { return virtualLocalName(); } + const AtomicString& namespaceURI() const { return virtualNamespaceURI(); } + const AtomicString& prefix() const { return virtualPrefix(); } + virtual void setPrefix(const AtomicString&, ExceptionCode&); + void normalize(); + + bool isSameNode(Node* other) const { return this == other; } + bool isEqualNode(Node*) const; + bool isDefaultNamespace(const AtomicString& namespaceURI) const; + String lookupPrefix(const AtomicString& namespaceURI) const; + String lookupNamespaceURI(const String& prefix) const; + String lookupNamespacePrefix(const AtomicString& 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) + + bool isElementNode() const { return getFlag(IsElementFlag); } + bool isContainerNode() const { return getFlag(IsContainerFlag); } + bool isTextNode() const { return getFlag(IsTextFlag); } + + bool isHTMLElement() const { return getFlag(IsHTMLFlag); } + + bool isSVGElement() const { return getFlag(IsSVGFlag); } + +#if ENABLE(WML) + virtual bool isWMLElement() const { return false; } +#else + static bool isWMLElement() { return false; } +#endif + + virtual bool isMediaControlElement() const { return false; } + bool isStyledElement() const { return getFlag(IsStyledElementFlag); } + virtual bool isFrameOwnerElement() const { return false; } + virtual bool isAttributeNode() const { return false; } + bool isCommentNode() const { return getFlag(IsCommentFlag); } + virtual bool isCharacterDataNode() const { return false; } + bool isDocumentNode() const; + bool isShadowRoot() const { return getFlag(IsShadowRootFlag); } + Node* shadowAncestorNode(); + Node* shadowTreeRootNode(); + bool isInShadowTree(); + // Node's parent or shadow tree host. + ContainerNode* parentOrHostNode() const; + // Use when it's guaranteed to that shadowHost is 0. + ContainerNode* parentNodeGuaranteedHostFree() const; + + Element* shadowHost() const; + void setShadowHost(Element*); + + // Returns the enclosing event parent node (or self) that, when clicked, would trigger a navigation. + Node* enclosingLinkEventParentOrSelf(); + + // Node ancestors when concerned about event flow. + void getEventAncestors(Vector<EventContext>& ancestors, EventTarget*, EventDispatchBehavior = RetargetEvent); + + bool isBlockFlow() const; + bool isBlockFlowOrBlockTable() const; + + // 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; + + // Returns the next leaf node or 0 if there are no more. + // Delivers leaf nodes as if the whole DOM tree were a linear chain of its leaf nodes. + // Uses an editing-specific concept of what a leaf node is, and should probably be moved + // out of the Node class into an editing-specific source file. + Node* nextLeafNode() const; + + // Returns the previous leaf node or 0 if there are no more. + // Delivers leaf nodes as if the whole DOM tree were a linear chain of its leaf nodes. + // Uses an editing-specific concept of what a leaf node is, and should probably be moved + // out of the Node class into an editing-specific source file. + Node* previousLeafNode() const; + + // enclosingBlockFlowElement() is deprecated. Use enclosingBlock instead. + Element* enclosingBlockFlowElement() const; + + Element* rootEditableElement() const; + + bool inSameContainingBlockFlowElement(Node*); + + // FIXME: All callers of this function are almost certainly wrong! + virtual void deprecatedParserAddChild(PassRefPtr<Node>); + + // Called by the parser when this element's close tag is reached, + // signaling 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 on the focused node right before dispatching an unload event. + virtual void aboutToUnload() { } + + // For <link> and <style> elements. + virtual bool sheetLoaded() { return true; } + + bool hasID() const { return getFlag(HasIDFlag); } + bool hasClass() const { return getFlag(HasClassFlag); } + bool active() const { return getFlag(IsActiveFlag); } + bool inActiveChain() const { return getFlag(InActiveChainFlag); } + bool inDetach() const { return getFlag(InDetachFlag); } + bool hovered() const { return getFlag(IsHoveredFlag); } + bool focused() const { return hasRareData() ? rareDataFocused() : false; } + bool attached() const { return getFlag(IsAttachedFlag); } + void setAttached() { setFlag(IsAttachedFlag); } + bool needsStyleRecalc() const { return styleChangeType() != NoStyleChange; } + StyleChangeType styleChangeType() const { return static_cast<StyleChangeType>(m_nodeFlags & StyleChangeMask); } + bool childNeedsStyleRecalc() const { return getFlag(ChildNeedsStyleRecalcFlag); } + bool isLink() const { return getFlag(IsLinkFlag); } + + void setHasID(bool f) { setFlag(f, HasIDFlag); } + void setHasClass(bool f) { setFlag(f, HasClassFlag); } + void setChildNeedsStyleRecalc() { setFlag(ChildNeedsStyleRecalcFlag); } + void clearChildNeedsStyleRecalc() { clearFlag(ChildNeedsStyleRecalcFlag); } + void setInDocument() { setFlag(InDocumentFlag); } + void clearInDocument() { clearFlag(InDocumentFlag); } + + void setInActiveChain() { setFlag(InActiveChainFlag); } + void clearInActiveChain() { clearFlag(InActiveChainFlag); } + + void setNeedsStyleRecalc(StyleChangeType changeType = FullStyleChange); + void clearNeedsStyleRecalc() { m_nodeFlags &= ~StyleChangeMask; } + + void setIsLink(bool f) { setFlag(f, IsLinkFlag); } + void setIsLink() { setFlag(IsLinkFlag); } + void clearIsLink() { clearFlag(IsLinkFlag); } + + enum ShouldSetAttached { + SetAttached, + DoNotSetAttached + }; + void lazyAttach(ShouldSetAttached = SetAttached); + + virtual void setFocus(bool = true); + virtual void setActive(bool f = true, bool /*pause*/ = false) { setFlag(f, IsActiveFlag); } + virtual void setHovered(bool f = true) { setFlag(f, IsHoveredFlag); } + + virtual short tabIndex() const; + + // Whether this kind of node can receive focus by default. Most nodes are + // not focusable but some elements, such as form controls and links, are. + virtual bool supportsFocus() const; + // Whether the node can actually be focused. + virtual bool isFocusable() const; + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual bool isMouseFocusable() const; + + virtual bool isContentEditable() const; + virtual bool isContentRichlyEditable() const; + virtual bool shouldUseInputMethod() const; + virtual IntRect getRect() const; + IntRect renderRect(bool* isReplaced); + + // Returns true if the node has a non-empty bounding box in layout. + // This does not 100% guarantee the user can see it, but is pretty close. + // Note: This method only works properly after layout has occurred. + bool hasNonEmptyBoundingBox() const; + + virtual void recalcStyle(StyleChange = NoChange) { } + + 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; + } + + // Do not use this method to change the document of a node until after the node has been + // removed from its previous document. + 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 || !getFlag(InDocumentFlag)); + return getFlag(InDocumentFlag); + } + + bool isReadOnlyNode() const { return nodeType() == ENTITY_REFERENCE_NODE; } + virtual bool childTypeAllowed(NodeType) { return false; } + unsigned childNodeCount() const; + Node* childNode(unsigned index) const; + + // Does a pre-order traversal of the tree to find the next node after this one. + // This uses the same order that tags appear in the source file. If the stayWithin + // argument is non-null, the traversal will stop once the specified node is reached. + // This can be used to restrict traversal to a particular sub-tree. + 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 + Node* traversePreviousNode(const Node* stayWithin = 0) const; + + // Like traverseNextNode, but visits parents after their children. + Node* traverseNextNodePostOrder() const; + + // Like traversePreviousNode, but visits parents before their children. + Node* traversePreviousNodePostOrder(const Node* stayWithin = 0) const; + Node* traversePreviousSiblingPostOrder(const Node* stayWithin = 0) const; + + void checkSetPrefix(const AtomicString& prefix, ExceptionCode&); + bool isDescendantOf(const Node*) const; + bool contains(const Node*) const; + bool containsIncludingShadowDOM(Node*); + + // This method is used to do strict error-checking when adding children via + // the public DOM API (e.g., appendChild()). + void checkAddChild(Node* newChild, ExceptionCode&); // Error-checking when adding via the DOM API + + 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; + + // Getting points into and out of screen space + FloatPoint convertToPage(const FloatPoint&) const; + FloatPoint convertFromPage(const FloatPoint&) const; + + // ----------------------------------------------------------------------------- + // Integration with rendering tree + + RenderObject* renderer() const { return m_renderer; } + RenderObject* nextRenderer(); + RenderObject* previousRenderer(); + void setRenderer(RenderObject* renderer) { m_renderer = renderer; } + + // Use these two methods with caution. + RenderBox* renderBox() const; + RenderBoxModelObject* renderBoxModelObject() const; + + // 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(); + PassRefPtr<RenderStyle> styleForRenderer(); + virtual bool rendererIsNeeded(RenderStyle*); + virtual bool childShouldCreateRenderer(Node*) const { return true; } + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + // Wrapper for nodes that don't have a renderer, but still cache the style (like HTMLOptionElement). + RenderStyle* renderStyle() const; + virtual void setRenderStyle(PassRefPtr<RenderStyle>); + + RenderStyle* computedStyle(PseudoId pseudoElementSpecifier = NOPSEUDO) { return virtualComputedStyle(pseudoElementSpecifier); } + + // ----------------------------------------------------------------------------- + // 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) { } + +#if !defined(NDEBUG) || defined(ANDROID_DOM_LOGGING) + 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(); + void notifyLocalNodeListsLabelChanged(); + void removeCachedClassNodeList(ClassNodeList*, const String&); + void removeCachedNameNodeList(NameNodeList*, const String&); + void removeCachedTagNodeList(TagNodeList*, const QualifiedName&); + void removeCachedLabelsNodeList(DynamicNodeList*); + + PassRefPtr<NodeList> getElementsByTagName(const AtomicString&); + PassRefPtr<NodeList> getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& 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&); + + unsigned short compareDocumentPosition(Node*); + +#ifdef ANDROID_INSTRUMENT + // Overridden to prevent the normal new from being called. + void* operator new(size_t size); + void* operator new[](size_t size); + + // Overridden to prevent the normal delete from being called. + void operator delete(void* p, size_t size); + void operator delete[](void* p, size_t size); + + static size_t reportDOMNodesSize(); +#endif + + virtual Node* toNode() { return this; } + + virtual ScriptExecutionContext* scriptExecutionContext() const; + + virtual bool addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + virtual bool removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + + // 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*/) { } + + using EventTarget::dispatchEvent; + bool dispatchEvent(PassRefPtr<Event>); + void dispatchScopedEvent(PassRefPtr<Event>); + + bool dispatchGenericEvent(PassRefPtr<Event>); + virtual void handleLocalEvents(Event*); + + void dispatchSubtreeModifiedEvent(); + void dispatchUIEvent(const AtomicString& eventType, int detail, PassRefPtr<Event> underlyingEvent); + 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, Node* relatedTarget, PassRefPtr<Event> underlyingEvent); + void dispatchSimulatedMouseEvent(const AtomicString& eventType, PassRefPtr<Event> underlyingEvent); + void dispatchSimulatedClick(PassRefPtr<Event> underlyingEvent, bool sendMouseEvents = false, bool showPressedLook = true); + + virtual void dispatchFocusEvent(); + virtual void dispatchBlurEvent(); + + // Perform the default action for an event. + 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; + + using TreeShared<ContainerNode>::ref; + using TreeShared<ContainerNode>::deref; + + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); + +#if USE(JSC) + void markCachedNodeLists(JSC::MarkStack& markStack, JSC::JSGlobalData& globalData) + { + // NodeLists may be present. If so, they need to be marked. + if (!hasRareData()) + return; + + markCachedNodeListsSlow(markStack, globalData); + } +#endif + +private: + enum NodeFlags { + IsTextFlag = 1, + IsCommentFlag = 1 << 1, + IsContainerFlag = 1 << 2, + IsElementFlag = 1 << 3, + IsStyledElementFlag = 1 << 4, + IsHTMLFlag = 1 << 5, + IsSVGFlag = 1 << 6, + HasIDFlag = 1 << 7, + HasClassFlag = 1 << 8, + IsAttachedFlag = 1 << 9, + ChildNeedsStyleRecalcFlag = 1 << 10, + InDocumentFlag = 1 << 11, + IsLinkFlag = 1 << 12, + IsActiveFlag = 1 << 13, + IsHoveredFlag = 1 << 14, + InActiveChainFlag = 1 << 15, + InDetachFlag = 1 << 16, + HasRareDataFlag = 1 << 17, + IsShadowRootFlag = 1 << 18, + + // These bits are used by derived classes, pulled up here so they can + // be stored in the same memory word as the Node bits above. + IsParsingChildrenFinishedFlag = 1 << 19, // Element + IsStyleAttributeValidFlag = 1 << 20, // StyledElement + IsSynchronizingStyleAttributeFlag = 1 << 21, // StyledElement +#if ENABLE(SVG) + AreSVGAttributesValidFlag = 1 << 22, // Element + IsSynchronizingSVGAttributesFlag = 1 << 23, // SVGElement + HasSVGRareDataFlag = 1 << 24, // SVGElement +#endif + + StyleChangeMask = 1 << nodeStyleChangeShift | 1 << (nodeStyleChangeShift + 1), + +#if ENABLE(SVG) + DefaultNodeFlags = IsParsingChildrenFinishedFlag | IsStyleAttributeValidFlag | AreSVGAttributesValidFlag +#else + DefaultNodeFlags = IsParsingChildrenFinishedFlag | IsStyleAttributeValidFlag +#endif + }; + + // 4 bits remaining + + bool getFlag(NodeFlags mask) const { return m_nodeFlags & mask; } + void setFlag(bool f, NodeFlags mask) const { m_nodeFlags = (m_nodeFlags & ~mask) | (-(int32_t)f & mask); } + void setFlag(NodeFlags mask) const { m_nodeFlags |= mask; } + void clearFlag(NodeFlags mask) const { m_nodeFlags &= ~mask; } + +protected: + enum ConstructionType { + CreateOther = DefaultNodeFlags, + CreateText = DefaultNodeFlags | IsTextFlag, + CreateComment = DefaultNodeFlags | IsCommentFlag, + CreateContainer = DefaultNodeFlags | IsContainerFlag, + CreateElement = CreateContainer | IsElementFlag, + CreateStyledElement = CreateElement | IsStyledElementFlag, + CreateHTMLElement = CreateStyledElement | IsHTMLFlag, + CreateSVGElement = CreateStyledElement | IsSVGFlag, + }; + Node(Document*, ConstructionType); + + virtual void willMoveToNewOwnerDocument(); + virtual void didMoveToNewOwnerDocument(); + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const { } + void setTabIndexExplicitly(short); + + bool hasRareData() const { return getFlag(HasRareDataFlag); } + + NodeRareData* rareData() const; + NodeRareData* ensureRareData(); + +private: +#if USE(JSC) + void markCachedNodeListsSlow(JSC::MarkStack&, JSC::JSGlobalData&); +#endif + + void setStyleChange(StyleChangeType); + + // Used to share code between lazyAttach and setNeedsStyleRecalc. + void markAncestorsWithChildNeedsStyleRecalc(); + + virtual void refEventTarget(); + virtual void derefEventTarget(); + + virtual NodeRareData* createRareData(); + bool rareDataFocused() const; + + virtual RenderStyle* nonRendererRenderStyle() const; + + virtual const AtomicString& virtualPrefix() const; + virtual const AtomicString& virtualLocalName() const; + virtual const AtomicString& virtualNamespaceURI() const; + virtual RenderStyle* virtualComputedStyle(PseudoId = NOPSEUDO); + + Element* ancestorElement() const; + + // Use Node::parentNode as the consistent way of querying a parent node. + // This method is made private to ensure a compiler error on call sites that + // don't follow this rule. + using TreeShared<ContainerNode>::parent; + + void trackForDebugging(); + + Document* m_document; + Node* m_previous; + Node* m_next; + RenderObject* m_renderer; + mutable uint32_t m_nodeFlags; + +protected: + bool isParsingChildrenFinished() const { return getFlag(IsParsingChildrenFinishedFlag); } + void setIsParsingChildrenFinished() { setFlag(IsParsingChildrenFinishedFlag); } + void clearIsParsingChildrenFinished() { clearFlag(IsParsingChildrenFinishedFlag); } + bool isStyleAttributeValid() const { return getFlag(IsStyleAttributeValidFlag); } + void setIsStyleAttributeValid(bool f) { setFlag(f, IsStyleAttributeValidFlag); } + void setIsStyleAttributeValid() const { setFlag(IsStyleAttributeValidFlag); } + void clearIsStyleAttributeValid() { clearFlag(IsStyleAttributeValidFlag); } + bool isSynchronizingStyleAttribute() const { return getFlag(IsSynchronizingStyleAttributeFlag); } + void setIsSynchronizingStyleAttribute(bool f) { setFlag(f, IsSynchronizingStyleAttributeFlag); } + void setIsSynchronizingStyleAttribute() const { setFlag(IsSynchronizingStyleAttributeFlag); } + void clearIsSynchronizingStyleAttribute() const { clearFlag(IsSynchronizingStyleAttributeFlag); } + +#if ENABLE(SVG) + bool areSVGAttributesValid() const { return getFlag(AreSVGAttributesValidFlag); } + void setAreSVGAttributesValid() const { setFlag(AreSVGAttributesValidFlag); } + void clearAreSVGAttributesValid() { clearFlag(AreSVGAttributesValidFlag); } + bool isSynchronizingSVGAttributes() const { return getFlag(IsSynchronizingSVGAttributesFlag); } + void setIsSynchronizingSVGAttributes() const { setFlag(IsSynchronizingSVGAttributesFlag); } + void clearIsSynchronizingSVGAttributes() const { clearFlag(IsSynchronizingSVGAttributesFlag); } + bool hasRareSVGData() const { return getFlag(HasSVGRareDataFlag); } + void setHasRareSVGData() { setFlag(HasSVGRareDataFlag); } + void clearHasRareSVGData() { clearFlag(HasSVGRareDataFlag); } +#endif +}; + +// Used in Node::addSubresourceAttributeURLs() and in addSubresourceStyleURLs() +inline void addSubresourceURL(ListHashSet<KURL>& urls, const KURL& url) +{ + if (!url.isNull()) + urls.add(url); +} + +inline ContainerNode* Node::parentNode() const +{ + return getFlag(IsShadowRootFlag) ? 0 : parent(); +} + +inline ContainerNode* Node::parentOrHostNode() const +{ + return parent(); +} + +inline ContainerNode* Node::parentNodeGuaranteedHostFree() const +{ + ASSERT(!getFlag(IsShadowRootFlag)); + return parentOrHostNode(); +} + +} //namespace + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::Node*); +#endif + +#endif diff --git a/Source/WebCore/dom/Node.idl b/Source/WebCore/dom/Node.idl new file mode 100644 index 0000000..07046d1 --- /dev/null +++ b/Source/WebCore/dom/Node.idl @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 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 [ + CustomHeader, + CustomMarkFunction, + CustomPushEventHandlerScope, + CustomToJS, + EventTarget, + GenerateNativeConverter, + InlineGetOwnPropertySlot, + Polymorphic + ] Node +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + : Object, EventTarget +#endif /* defined(LANGUAGE_OBJECTIVE_C) */ + { + // 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(DOMExceptionJSC); + [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); + + // 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); + +#if 0 + 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 extensions + readonly attribute Element parentElement; + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + // Objective-C extensions + readonly attribute boolean isContentEditable; +#endif /* defined(LANGUAGE_OBJECTIVE_C) */ + +#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C + void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event event) + raises(EventException); +#endif +#endif + +#if defined(LANGUAGE_CPP) && LANGUAGE_CPP + [Custom] void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [Custom] void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event event) + raises(EventException); +#endif + + }; + +} diff --git a/Source/WebCore/dom/NodeFilter.cpp b/Source/WebCore/dom/NodeFilter.cpp new file mode 100644 index 0000000..c5d855a --- /dev/null +++ b/Source/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" + +#include "Node.h" + +namespace WebCore { + +short NodeFilter::acceptNode(ScriptState* state, Node* node) const +{ + // cast to short silences "enumeral and non-enumeral types in return" warning + return m_condition ? m_condition->acceptNode(state, node) : static_cast<short>(FILTER_ACCEPT); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/NodeFilter.h b/Source/WebCore/dom/NodeFilter.h new file mode 100644 index 0000000..5ce2866 --- /dev/null +++ b/Source/WebCore/dom/NodeFilter.h @@ -0,0 +1,88 @@ +/* + * 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, 2009 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 "DOMWrapperWorld.h" +#include "NodeFilterCondition.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 + }; + + static PassRefPtr<NodeFilter> create(PassRefPtr<NodeFilterCondition> condition) + { + return adoptRef(new NodeFilter(condition)); + } + + short acceptNode(ScriptState*, Node*) const; + void markAggregate(JSC::MarkStack& markStack) { m_condition->markAggregate(markStack); }; + + // Do not call these functions. They are just scaffolding to support the Objective-C bindings. + // They operate in the main thread normal world, and they swallow JS exceptions. + short acceptNode(Node* node) const { return acceptNode(scriptStateFromNode(mainThreadNormalWorld(), node), node); } + + private: + NodeFilter(PassRefPtr<NodeFilterCondition> condition) : m_condition(condition) { } + + RefPtr<NodeFilterCondition> m_condition; + }; + +} // namespace WebCore + +#endif // NodeFilter_h diff --git a/Source/WebCore/dom/NodeFilter.idl b/Source/WebCore/dom/NodeFilter.idl new file mode 100644 index 0000000..40c5bad --- /dev/null +++ b/Source/WebCore/dom/NodeFilter.idl @@ -0,0 +1,54 @@ +/* + * 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 [ + CustomMarkFunction, + CustomNativeConverter, + ObjCProtocol, + PureInterface + ] 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; + + [CallWith=ScriptState] short acceptNode(in Node n); + + }; + +} diff --git a/Source/WebCore/dom/NodeFilterCondition.cpp b/Source/WebCore/dom/NodeFilterCondition.cpp new file mode 100644 index 0000000..1d2e1e1 --- /dev/null +++ b/Source/WebCore/dom/NodeFilterCondition.cpp @@ -0,0 +1,37 @@ +/* + * 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" + +namespace WebCore { + +short NodeFilterCondition::acceptNode(ScriptState*, Node*) const +{ + return NodeFilter::FILTER_ACCEPT; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/NodeFilterCondition.h b/Source/WebCore/dom/NodeFilterCondition.h new file mode 100644 index 0000000..0d6313f --- /dev/null +++ b/Source/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, 2009 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 "ScriptState.h" +#include <wtf/RefCounted.h> + +namespace JSC { + class MarkStack; +} + +namespace WebCore { + + class Node; + + class NodeFilterCondition : public RefCounted<NodeFilterCondition> { + public: + virtual ~NodeFilterCondition() { } + virtual short acceptNode(ScriptState*, Node*) const = 0; + virtual void markAggregate(JSC::MarkStack&) { } + }; + +} // namespace WebCore + +#endif // NodeFilterCondition_h diff --git a/Source/WebCore/dom/NodeIterator.cpp b/Source/WebCore/dom/NodeIterator.cpp new file mode 100644 index 0000000..ce3103c --- /dev/null +++ b/Source/WebCore/dom/NodeIterator.cpp @@ -0,0 +1,234 @@ +/* + * 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" +#include "ScriptState.h" + +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) +{ + // Document type nodes may have a null document. But since they can't have children, there is no need to listen for modifications to these. + ASSERT(root()->document() || root()->nodeType() == Node::DOCUMENT_TYPE_NODE); + if (Document* ownerDocument = root()->document()) + ownerDocument->attachNodeIterator(this); +} + +NodeIterator::~NodeIterator() +{ + if (Document* ownerDocument = root()->document()) + ownerDocument->detachNodeIterator(this); +} + +PassRefPtr<Node> NodeIterator::nextNode(ScriptState* state, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + RefPtr<Node> result; + + 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. + RefPtr<Node> provisionalResult = m_candidateNode.node; + bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; + if (state && state->hadException()) + break; + if (nodeWasAccepted) { + m_referenceNode = m_candidateNode; + result = provisionalResult.release(); + break; + } + } + + m_candidateNode.clear(); + return result.release(); +} + +PassRefPtr<Node> NodeIterator::previousNode(ScriptState* state, ExceptionCode& ec) +{ + if (m_detached) { + ec = INVALID_STATE_ERR; + return 0; + } + + RefPtr<Node> result; + + 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. + RefPtr<Node> provisionalResult = m_candidateNode.node; + bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; + if (state && state->hadException()) + break; + if (nodeWasAccepted) { + m_referenceNode = m_candidateNode; + result = provisionalResult.release(); + break; + } + } + + m_candidateNode.clear(); + return result.release(); +} + +void NodeIterator::detach() +{ + if (Document* ownerDocument = root()->document()) + ownerDocument->detachNodeIterator(this); + m_detached = true; + m_referenceNode.node.clear(); +} + +void NodeIterator::nodeWillBeRemoved(Node* removedNode) +{ + updateForNodeRemoval(removedNode, m_candidateNode); + updateForNodeRemoval(removedNode, m_referenceNode); +} + +void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenceNode) const +{ + ASSERT(!m_detached); + ASSERT(removedNode); + ASSERT(root()->document()); + 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/Source/WebCore/dom/NodeIterator.h b/Source/WebCore/dom/NodeIterator.h new file mode 100644 index 0000000..3eec49a --- /dev/null +++ b/Source/WebCore/dom/NodeIterator.h @@ -0,0 +1,82 @@ +/* + * 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 "NodeFilter.h" +#include "Traversal.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + typedef int ExceptionCode; + + class NodeIterator : public RefCounted<NodeIterator>, public Traversal { + public: + static PassRefPtr<NodeIterator> create(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) + { + return adoptRef(new NodeIterator(rootNode, whatToShow, filter, expandEntityReferences)); + } + ~NodeIterator(); + + PassRefPtr<Node> nextNode(ScriptState*, ExceptionCode&); + PassRefPtr<Node> previousNode(ScriptState*, ExceptionCode&); + 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 nodeWillBeRemoved(Node*); + + // Do not call these functions. They are just scaffolding to support the Objective-C bindings. + // They operate in the main thread normal world, and they swallow JS exceptions. + PassRefPtr<Node> nextNode(ExceptionCode& ec) { return nextNode(scriptStateFromNode(mainThreadNormalWorld(), referenceNode()), ec); } + PassRefPtr<Node> previousNode(ExceptionCode& ec) { return previousNode(scriptStateFromNode(mainThreadNormalWorld(), referenceNode()), ec); } + + private: + NodeIterator(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); + + 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/Source/WebCore/dom/NodeIterator.idl b/Source/WebCore/dom/NodeIterator.idl new file mode 100644 index 0000000..9f59ae1 --- /dev/null +++ b/Source/WebCore/dom/NodeIterator.idl @@ -0,0 +1,41 @@ +/* + * 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 NodeFilter filter; + readonly attribute boolean expandEntityReferences; + readonly attribute Node referenceNode; + readonly attribute boolean pointerBeforeReferenceNode; + + [CallWith=ScriptState] Node nextNode() + raises (DOMException); + [CallWith=ScriptState] Node previousNode() + raises (DOMException); + void detach(); + }; + +} diff --git a/Source/WebCore/dom/NodeList.h b/Source/WebCore/dom/NodeList.h new file mode 100644 index 0000000..d4e18aa --- /dev/null +++ b/Source/WebCore/dom/NodeList.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, 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/Forward.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class Node; + + class NodeList : public RefCounted<NodeList> { + public: + 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/Source/WebCore/dom/NodeList.idl b/Source/WebCore/dom/NodeList.idl new file mode 100644 index 0000000..edb2dc7 --- /dev/null +++ b/Source/WebCore/dom/NodeList.idl @@ -0,0 +1,35 @@ +/* + * 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 [ + HasIndexGetter, + HasNameGetter, + CustomCall + ] NodeList { + + Node item(in [IsIndex] unsigned long index); + + readonly attribute unsigned long length; + + }; + +} diff --git a/Source/WebCore/dom/NodeRareData.h b/Source/WebCore/dom/NodeRareData.h new file mode 100644 index 0000000..ab208d2 --- /dev/null +++ b/Source/WebCore/dom/NodeRareData.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 David Smith <catfish.man@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. + * + */ + +#ifndef NodeRareData_h +#define NodeRareData_h + +#include "ClassNodeList.h" +#include "DynamicNodeList.h" +#include "NameNodeList.h" +#include "QualifiedName.h" +#include "TagNodeList.h" +#include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +struct NodeListsNodeData : Noncopyable { + typedef HashSet<DynamicNodeList*> NodeListSet; + NodeListSet m_listsWithCaches; + + RefPtr<DynamicNodeList::Caches> m_childNodeListCaches; + + typedef HashMap<String, ClassNodeList*> ClassNodeListCache; + ClassNodeListCache m_classNodeListCache; + + typedef HashMap<String, NameNodeList*> NameNodeListCache; + NameNodeListCache m_nameNodeListCache; + + typedef HashMap<RefPtr<QualifiedName::QualifiedNameImpl>, TagNodeList*> TagNodeListCache; + TagNodeListCache m_tagNodeListCache; + + RefPtr<DynamicNodeList> m_labelsNodeListCache; + + static PassOwnPtr<NodeListsNodeData> create() + { + return new NodeListsNodeData; + } + + void invalidateCaches(); + void invalidateCachesThatDependOnAttributes(); + bool isEmpty() const; + +private: + NodeListsNodeData() + : m_childNodeListCaches(DynamicNodeList::Caches::create()), m_labelsNodeListCache(0) + { + } +}; + +class NodeRareData : public Noncopyable { +public: + NodeRareData() + : m_tabIndex(0) + , m_tabIndexWasSetExplicitly(false) + , m_isFocused(false) + , m_needsFocusAppearanceUpdateSoonAfterAttach(false) + { + } + + virtual ~NodeRareData() + { + } + + typedef HashMap<const Node*, NodeRareData*> NodeRareDataMap; + + static NodeRareDataMap& rareDataMap() + { + static NodeRareDataMap* dataMap = new NodeRareDataMap; + return *dataMap; + } + + static NodeRareData* rareDataFromMap(const Node* node) + { + return rareDataMap().get(node); + } + + void clearNodeLists() { m_nodeLists.clear(); } + void setNodeLists(PassOwnPtr<NodeListsNodeData> lists) { m_nodeLists = lists; } + NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); } + + short tabIndex() const { return m_tabIndex; } + void setTabIndexExplicitly(short index) { m_tabIndex = index; m_tabIndexWasSetExplicitly = true; } + bool tabIndexSetExplicitly() const { return m_tabIndexWasSetExplicitly; } + + EventTargetData* eventTargetData() { return m_eventTargetData.get(); } + EventTargetData* ensureEventTargetData() + { + if (!m_eventTargetData) + m_eventTargetData.set(new EventTargetData); + return m_eventTargetData.get(); + } + + bool isFocused() const { return m_isFocused; } + void setFocused(bool focused) { m_isFocused = focused; } + +protected: + // for ElementRareData + bool needsFocusAppearanceUpdateSoonAfterAttach() const { return m_needsFocusAppearanceUpdateSoonAfterAttach; } + void setNeedsFocusAppearanceUpdateSoonAfterAttach(bool needs) { m_needsFocusAppearanceUpdateSoonAfterAttach = needs; } + +private: + OwnPtr<NodeListsNodeData> m_nodeLists; + OwnPtr<EventTargetData> m_eventTargetData; + short m_tabIndex; + bool m_tabIndexWasSetExplicitly : 1; + bool m_isFocused : 1; + bool m_needsFocusAppearanceUpdateSoonAfterAttach : 1; +}; + +} // namespace WebCore + +#endif // NodeRareData_h diff --git a/Source/WebCore/dom/NodeRenderStyle.h b/Source/WebCore/dom/NodeRenderStyle.h new file mode 100644 index 0000000..3a67e02 --- /dev/null +++ b/Source/WebCore/dom/NodeRenderStyle.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2008 David Smith (catfish.man@gmail.com) + * 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 NodeRenderStyle_h +#define NodeRenderStyle_h + +#include "RenderObject.h" +#include "RenderStyle.h" +#include "Node.h" + +namespace WebCore { + +inline RenderStyle* Node::renderStyle() const +{ + return m_renderer ? m_renderer->style() : nonRendererRenderStyle(); +} + +} +#endif diff --git a/Source/WebCore/dom/NodeWithIndex.h b/Source/WebCore/dom/NodeWithIndex.h new file mode 100644 index 0000000..9ad6566 --- /dev/null +++ b/Source/WebCore/dom/NodeWithIndex.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 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 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 NodeWithIndex_h +#define NodeWithIndex_h + +#include "Node.h" + +namespace WebCore { + +// For use when you want to get the index for a node repeatedly and +// only want to walk the child list to figure out the index once. +class NodeWithIndex { +public: + NodeWithIndex(Node* node) + : m_node(node) + , m_haveIndex(false) + { + ASSERT(node); + } + + Node* node() const { return m_node; } + + int index() const + { + if (!m_haveIndex) { + m_index = m_node->nodeIndex(); + m_haveIndex = true; + } + ASSERT(m_index == static_cast<int>(m_node->nodeIndex())); + return m_index; + } + +private: + Node* m_node; + mutable bool m_haveIndex; + mutable int m_index; +}; + +} + +#endif diff --git a/Source/WebCore/dom/Notation.cpp b/Source/WebCore/dom/Notation.cpp new file mode 100644 index 0000000..4b3ab28 --- /dev/null +++ b/Source/WebCore/dom/Notation.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006, 2009 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 "Notation.h" + +#include "Document.h" + +namespace WebCore { + +Notation::Notation(Document* document, const String& name, const String& publicId, const String& systemId) + : ContainerNode(document) + , 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; +} + +bool Notation::childTypeAllowed(NodeType) +{ + return false; +} + +} // namespace diff --git a/Source/WebCore/dom/Notation.h b/Source/WebCore/dom/Notation.h new file mode 100644 index 0000000..547c9e7 --- /dev/null +++ b/Source/WebCore/dom/Notation.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006, 2009 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 Notation_h +#define Notation_h + +#include "ContainerNode.h" + +namespace WebCore { + +// FIXME: This class is never instantiated. Maybe it should be removed. + +class Notation : public ContainerNode { +public: + const String& publicId() const { return m_publicId; } + const String& systemId() const { return m_systemId; } + +private: + Notation(Document*, const String& name, const String& publicId, const String& systemId); + + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool childTypeAllowed(NodeType); + + String m_name; + String m_publicId; + String m_systemId; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/dom/Notation.idl b/Source/WebCore/dom/Notation.idl new file mode 100644 index 0000000..96351d2 --- /dev/null +++ b/Source/WebCore/dom/Notation.idl @@ -0,0 +1,27 @@ +/* + * 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 Notation : Node { + readonly attribute [ConvertNullStringTo=Null] DOMString publicId; + readonly attribute [ConvertNullStringTo=Null] DOMString systemId; + }; + +} diff --git a/Source/WebCore/dom/OptionElement.cpp b/Source/WebCore/dom/OptionElement.cpp new file mode 100644 index 0000000..ab7863e --- /dev/null +++ b/Source/WebCore/dom/OptionElement.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "OptionElement.h" + +#include "Document.h" +#include "Element.h" +#include "HTMLNames.h" +#include "HTMLOptionElement.h" +#include "OptionGroupElement.h" +#include "ScriptElement.h" +#include "SelectElement.h" +#include <wtf/Assertions.h> + +#if ENABLE(WML) +#include "WMLOptionElement.h" +#include "WMLNames.h" +#endif + +namespace WebCore { + +void OptionElement::setSelectedState(OptionElementData& data, Element* element, bool selected) +{ + if (data.selected() == selected) + return; + + data.setSelected(selected); + element->setNeedsStyleRecalc(); +} + +int OptionElement::optionIndex(SelectElement* selectElement, const Element* element) +{ + if (!selectElement) + return 0; + + // Let's do this dynamically. Might be a bit slow, but we're sure + // we won't forget to update a member variable in some cases... + const Vector<Element*>& items = selectElement->listItems(); + int length = items.size(); + int optionIndex = 0; + for (int i = 0; i < length; ++i) { + if (!isOptionElement(items[i])) + continue; + if (items[i] == element) + return optionIndex; + ++optionIndex; + } + + return 0; +} + +String OptionElement::collectOptionLabelOrText(const OptionElementData& data, const Element* element) +{ + Document* document = element->document(); + String text; + + // WinIE does not use the label attribute, so as a quirk, we ignore it. + if (!document->inQuirksMode()) + text = data.label(); + if (text.isEmpty()) + text = collectOptionInnerText(element); + return normalizeText(document, text); +} + +String OptionElement::collectOptionInnerText(const Element* element) +{ + String text; + Node* n = element->firstChild(); + while (n) { + if (n->nodeType() == Node::TEXT_NODE || n->nodeType() == Node::CDATA_SECTION_NODE) + text += n->nodeValue(); + + // skip script content + if (n->isElementNode() && toScriptElement(static_cast<Element*>(n))) + n = n->traverseNextSibling(element); + else + n = n->traverseNextNode(element); + } + return text; +} + +String OptionElement::normalizeText(const Document* document, const String& src) +{ + String text = document->displayStringModifiedByEncoding(src); + + // In WinIE, leading and trailing whitespace is ignored in options and optgroups. We match this behavior. + text = text.stripWhiteSpace(); + + // We want to collapse our whitespace too. This will match other browsers. + text = text.simplifyWhiteSpace(); + return text; +} + +String OptionElement::collectOptionTextRespectingGroupLabel(const OptionElementData& data, const Element* element) +{ + Element* parentElement = static_cast<Element*>(element->parentNode()); + if (parentElement && toOptionGroupElement(parentElement)) + return " " + collectOptionLabelOrText(data, element); + + return collectOptionLabelOrText(data, element); +} + +String OptionElement::collectOptionValue(const OptionElementData& data, const Element* element) +{ + String value = data.value(); + if (!value.isNull()) + return value; + + // Use the text if the value wasn't set. + return collectOptionInnerText(element).stripWhiteSpace(); +} + +// OptionElementData +OptionElementData::OptionElementData() + : m_selected(false) +{ +} + +OptionElementData::~OptionElementData() +{ +} + +OptionElement* toOptionElement(Element* element) +{ + if (element->isHTMLElement() && element->hasTagName(HTMLNames::optionTag)) + return static_cast<HTMLOptionElement*>(element); + +#if ENABLE(WML) + if (element->isWMLElement() && element->hasTagName(WMLNames::optionTag)) + return static_cast<WMLOptionElement*>(element); +#endif + + return 0; +} + +bool isOptionElement(Element* element) +{ + return element->hasLocalName(HTMLNames::optionTag) +#if ENABLE(WML) + || element->hasLocalName(WMLNames::optionTag) +#endif + ; +} + +} diff --git a/Source/WebCore/dom/OptionElement.h b/Source/WebCore/dom/OptionElement.h new file mode 100644 index 0000000..9ff5cd5 --- /dev/null +++ b/Source/WebCore/dom/OptionElement.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.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. + * + */ + +#ifndef OptionElement_h +#define OptionElement_h + +#include "PlatformString.h" + +namespace WebCore { + +class Element; +class Document; +class OptionElementData; +class SelectElement; + +class OptionElement { +public: + virtual ~OptionElement() { } + + virtual bool disabled() const = 0; + virtual bool selected() const = 0; + virtual void setSelectedState(bool) = 0; + + virtual String text() const = 0; + virtual String textIndentedToRespectGroupLabel() const = 0; + virtual String value() const = 0; + +protected: + static void setSelectedState(OptionElementData&, Element*, bool selected); + static int optionIndex(SelectElement*, const Element*); + static String collectOptionLabelOrText(const OptionElementData&, const Element*); + static String collectOptionTextRespectingGroupLabel(const OptionElementData&, const Element*); + static String collectOptionValue(const OptionElementData&, const Element*); +private: + static String collectOptionInnerText(const Element*); + static String normalizeText(const Document*, const String&); +}; + +// HTML/WMLOptionElement hold this struct as member variable +// and pass it to the static helper functions in OptionElement +class OptionElementData { +public: + OptionElementData(); + ~OptionElementData(); + + String value() const { return m_value; } + void setValue(const String& value) { m_value = value; } + + String label() const { return m_label; } + void setLabel(const String& label) { m_label = label; } + + bool selected() const { return m_selected; } + void setSelected(bool selected) { m_selected = selected; } + +private: + String m_value; + String m_label; + bool m_selected; +}; + +OptionElement* toOptionElement(Element*); +bool isOptionElement(Element*); + +} + +#endif diff --git a/Source/WebCore/dom/OptionGroupElement.cpp b/Source/WebCore/dom/OptionGroupElement.cpp new file mode 100644 index 0000000..8a001bc --- /dev/null +++ b/Source/WebCore/dom/OptionGroupElement.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "OptionGroupElement.h" + +#include "Element.h" +#include "HTMLNames.h" +#include "HTMLOptGroupElement.h" +#include <wtf/Assertions.h> + +#if ENABLE(WML) +#include "WMLOptGroupElement.h" +#include "WMLNames.h" +#endif + +namespace WebCore { + +OptionGroupElement* toOptionGroupElement(Element* element) +{ + if (element->isHTMLElement() && element->hasTagName(HTMLNames::optgroupTag)) + return static_cast<HTMLOptGroupElement*>(element); + +#if ENABLE(WML) + if (element->isWMLElement() && element->hasTagName(WMLNames::optgroupTag)) + return static_cast<WMLOptGroupElement*>(element); +#endif + + return 0; +} + +bool isOptionGroupElement(Element* element) +{ + return element->hasLocalName(HTMLNames::optgroupTag) +#if ENABLE(WML) + || element->hasLocalName(WMLNames::optgroupTag) +#endif + ; +} + +} diff --git a/Source/WebCore/dom/OptionGroupElement.h b/Source/WebCore/dom/OptionGroupElement.h new file mode 100644 index 0000000..b98a3f2 --- /dev/null +++ b/Source/WebCore/dom/OptionGroupElement.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.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. + * + */ + +#ifndef OptionGroupElement_h +#define OptionGroupElement_h + +#include <wtf/Forward.h> + +namespace WebCore { + +class Element; + +class OptionGroupElement { +public: + virtual ~OptionGroupElement() { } + + virtual String groupLabelText() const = 0; +}; + +OptionGroupElement* toOptionGroupElement(Element*); +bool isOptionGroupElement(Element*); + +} + +#endif diff --git a/Source/WebCore/dom/OverflowEvent.cpp b/Source/WebCore/dom/OverflowEvent.cpp new file mode 100644 index 0000000..1626b67 --- /dev/null +++ b/Source/WebCore/dom/OverflowEvent.cpp @@ -0,0 +1,71 @@ +/* + * 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 { + +OverflowEvent::OverflowEvent() + : Event(eventNames().overflowchangedEvent, false, false) + , m_orient(VERTICAL) + , m_horizontalOverflow(false) + , m_verticalOverflow(false) +{ +} + +OverflowEvent::OverflowEvent(bool horizontalOverflowChanged, bool horizontalOverflow, bool verticalOverflowChanged, bool verticalOverflow) + : Event(eventNames().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/Source/WebCore/dom/OverflowEvent.h b/Source/WebCore/dom/OverflowEvent.h new file mode 100644 index 0000000..f1cd2f3 --- /dev/null +++ b/Source/WebCore/dom/OverflowEvent.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006, 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 OverflowEvent_h +#define OverflowEvent_h + +#include "Event.h" + +namespace WebCore { + + class OverflowEvent : public Event { + public: + enum orientType { + HORIZONTAL = 0, + VERTICAL = 1, + BOTH = 2 + }; + + static PassRefPtr<OverflowEvent> create() + { + return adoptRef(new OverflowEvent); + } + static PassRefPtr<OverflowEvent> create(bool horizontalOverflowChanged, bool horizontalOverflow, bool verticalOverflowChanged, bool verticalOverflow) + { + return adoptRef(new OverflowEvent(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, 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: + OverflowEvent(); + OverflowEvent(bool horizontalOverflowChanged, bool horizontalOverflow, bool verticalOverflowChanged, bool verticalOverflow); + + unsigned short m_orient; + bool m_horizontalOverflow; + bool m_verticalOverflow; + }; +} + +#endif // OverflowEvent_h + diff --git a/Source/WebCore/dom/OverflowEvent.idl b/Source/WebCore/dom/OverflowEvent.idl new file mode 100644 index 0000000..0c1fd50 --- /dev/null +++ b/Source/WebCore/dom/OverflowEvent.idl @@ -0,0 +1,40 @@ +/* + * 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 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/Source/WebCore/dom/PageTransitionEvent.cpp b/Source/WebCore/dom/PageTransitionEvent.cpp new file mode 100644 index 0000000..f9c487d --- /dev/null +++ b/Source/WebCore/dom/PageTransitionEvent.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009 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 "PageTransitionEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +PageTransitionEvent::PageTransitionEvent() + : m_persisted(false) +{ +} + +PageTransitionEvent::PageTransitionEvent(const AtomicString& type, bool persisted) + : Event(type, true, true) + , m_persisted(persisted) +{ +} + +PageTransitionEvent::~PageTransitionEvent() +{ +} + +void PageTransitionEvent::initPageTransitionEvent(const AtomicString& type, + bool canBubbleArg, + bool cancelableArg, + bool persisted) +{ + if (dispatched()) + return; + + initEvent(type, canBubbleArg, cancelableArg); + + m_persisted = persisted; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/PageTransitionEvent.h b/Source/WebCore/dom/PageTransitionEvent.h new file mode 100644 index 0000000..33c5a0c --- /dev/null +++ b/Source/WebCore/dom/PageTransitionEvent.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2009 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 PageTransitionEvent_h +#define PageTransitionEvent_h + +#include "Event.h" + +namespace WebCore { + + class PageTransitionEvent : public Event { + public: + static PassRefPtr<PageTransitionEvent> create() + { + return adoptRef(new PageTransitionEvent); + } + static PassRefPtr<PageTransitionEvent> create(const AtomicString& type, bool persisted) + { + return adoptRef(new PageTransitionEvent(type, persisted)); + } + + virtual ~PageTransitionEvent(); + + void initPageTransitionEvent(const AtomicString& type, + bool canBubbleArg, + bool cancelableArg, + bool persisted); + + virtual bool isPageTransitionEvent() const { return true; } + + bool persisted() const { return m_persisted; } + + private: + PageTransitionEvent(); + PageTransitionEvent(const AtomicString& type, bool persisted); + + bool m_persisted; + }; + +} // namespace WebCore + +#endif // PageTransitionEvent_h diff --git a/Source/WebCore/dom/PageTransitionEvent.idl b/Source/WebCore/dom/PageTransitionEvent.idl new file mode 100644 index 0000000..8d70f67 --- /dev/null +++ b/Source/WebCore/dom/PageTransitionEvent.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 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. + */ + +module events { + + interface PageTransitionEvent : Event { + + readonly attribute boolean persisted; + + void initPageTransitionEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in boolean persisted); + }; + +} diff --git a/Source/WebCore/dom/PendingScript.cpp b/Source/WebCore/dom/PendingScript.cpp new file mode 100644 index 0000000..615fa7d --- /dev/null +++ b/Source/WebCore/dom/PendingScript.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Google, 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 "PendingScript.h" + +#include "CachedScript.h" +#include "Element.h" + +namespace WebCore { + +PendingScript::~PendingScript() +{ + if (m_cachedScript) + m_cachedScript->removeClient(this); +} + +PassRefPtr<Element> PendingScript::releaseElementAndClear() +{ + setCachedScript(0); + m_watchingForLoad = false; + m_startingPosition = TextPosition1::belowRangePosition(); + return m_element.release(); +} + +void PendingScript::setCachedScript(CachedScript* cachedScript) +{ + if (m_cachedScript == cachedScript) + return; + if (m_cachedScript) + m_cachedScript->removeClient(this); + m_cachedScript = cachedScript; + if (m_cachedScript) + m_cachedScript->addClient(this); +} + +CachedScript* PendingScript::cachedScript() const +{ + return m_cachedScript.get(); +} + +void PendingScript::notifyFinished(CachedResource*) +{ +} + +} diff --git a/Source/WebCore/dom/PendingScript.h b/Source/WebCore/dom/PendingScript.h new file mode 100644 index 0000000..fcbe06e --- /dev/null +++ b/Source/WebCore/dom/PendingScript.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 Google, 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 PendingScript_h +#define PendingScript_h + +#include "CachedResourceClient.h" +#include "CachedResourceHandle.h" +#include <wtf/text/TextPosition.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class CachedScript; +class Element; + +// A container for an external script which may be loaded and executed. +// +// A CachedResourceHandle alone does not prevent the underlying CachedResource +// from purging its data buffer. This class holds a dummy client open for its +// lifetime in order to guarantee that the data buffer will not be purged. +class PendingScript : public CachedResourceClient { +public: + PendingScript() + : m_watchingForLoad(false) + , m_startingPosition(TextPosition1::belowRangePosition()) + { + } + + PendingScript(Element* element, CachedScript* cachedScript) + : m_watchingForLoad(false) + , m_element(element) + { + setCachedScript(cachedScript); + } + + PendingScript(const PendingScript& other) + : CachedResourceClient(other) + , m_watchingForLoad(other.m_watchingForLoad) + , m_element(other.m_element) + , m_startingPosition(other.m_startingPosition) + { + setCachedScript(other.cachedScript()); + } + + ~PendingScript(); + + PendingScript& operator=(const PendingScript& other) + { + if (this == &other) + return *this; + + m_watchingForLoad = other.m_watchingForLoad; + m_element = other.m_element; + m_startingPosition = other.m_startingPosition; + setCachedScript(other.cachedScript()); + + return *this; + } + + TextPosition1 startingPosition() const { return m_startingPosition; } + void setStartingPosition(const TextPosition1& position) { m_startingPosition = position; } + + bool watchingForLoad() const { return m_watchingForLoad; } + void setWatchingForLoad(bool b) { m_watchingForLoad = b; } + + Element* element() const { return m_element.get(); } + void setElement(Element* element) { m_element = element; } + PassRefPtr<Element> releaseElementAndClear(); + + CachedScript* cachedScript() const; + void setCachedScript(CachedScript*); + + virtual void notifyFinished(CachedResource*); + +private: + bool m_watchingForLoad; + RefPtr<Element> m_element; + TextPosition1 m_startingPosition; // Only used for inline script tags. + CachedResourceHandle<CachedScript> m_cachedScript; +}; + +} + +#endif diff --git a/Source/WebCore/dom/PopStateEvent.cpp b/Source/WebCore/dom/PopStateEvent.cpp new file mode 100644 index 0000000..bac676a --- /dev/null +++ b/Source/WebCore/dom/PopStateEvent.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 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 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 "PopStateEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +PopStateEvent::PopStateEvent(PassRefPtr<SerializedScriptValue> stateObject) + : Event(eventNames().popstateEvent, false, true) + , m_stateObject(stateObject) +{ +} + +PopStateEvent::~PopStateEvent() +{ +} + +void PopStateEvent::initPopStateEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> stateObject) +{ + if (dispatched()) + return; + + initEvent(type, canBubble, cancelable); + + m_stateObject = stateObject; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/PopStateEvent.h b/Source/WebCore/dom/PopStateEvent.h new file mode 100644 index 0000000..42fbd45 --- /dev/null +++ b/Source/WebCore/dom/PopStateEvent.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 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 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 PopStateEvent_h +#define PopStateEvent_h + +#include "Event.h" +#include "SerializedScriptValue.h" + +namespace WebCore { + +class SerializedScriptValue; + +class PopStateEvent : public Event { +public: + virtual ~PopStateEvent(); + + static PassRefPtr<PopStateEvent> create(PassRefPtr<SerializedScriptValue> stateObject) + { + return adoptRef(new PopStateEvent(stateObject)); + } + + void initPopStateEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue>); + bool isPopStateEvent() const { return true; } + + SerializedScriptValue* state() const { return m_stateObject.get(); } + +private: + explicit PopStateEvent(PassRefPtr<SerializedScriptValue>); + + RefPtr<SerializedScriptValue> m_stateObject; +}; + +} // namespace WebCore + +#endif // PopStateEvent_h diff --git a/Source/WebCore/dom/PopStateEvent.idl b/Source/WebCore/dom/PopStateEvent.idl new file mode 100644 index 0000000..adc9074 --- /dev/null +++ b/Source/WebCore/dom/PopStateEvent.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009 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 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 { + +#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP + interface PopStateEvent : Event { + void initPopStateEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in SerializedScriptValue stateArg); + + readonly attribute [V8CustomGetter] any state; + }; +#endif + +} diff --git a/Source/WebCore/dom/Position.cpp b/Source/WebCore/dom/Position.cpp new file mode 100644 index 0000000..be175aa --- /dev/null +++ b/Source/WebCore/dom/Position.cpp @@ -0,0 +1,1243 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2009 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 "Position.h" + +#include "CSSComputedStyleDeclaration.h" +#include "CharacterNames.h" +#include "Logging.h" +#include "PositionIterator.h" +#include "RenderBlock.h" +#include "Text.h" +#include "TextIterator.h" +#include "VisiblePosition.h" +#include "htmlediting.h" +#include "visible_units.h" +#include <stdio.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +using namespace HTMLNames; + +static Node* nextRenderedEditable(Node* node) +{ + while ((node = node->nextLeafNode())) { + if (!node->isContentEditable()) + continue; + RenderObject* renderer = node->renderer(); + if (!renderer) + continue; + if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox())) + return node; + } + return 0; +} + +static Node* previousRenderedEditable(Node* node) +{ + while ((node = node->previousLeafNode())) { + if (!node->isContentEditable()) + continue; + RenderObject* renderer = node->renderer(); + if (!renderer) + continue; + if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox())) + return node; + } + return 0; +} + +Position::Position(PassRefPtr<Node> anchorNode, int offset) + : m_anchorNode(anchorNode) + , m_offset(offset) + , m_anchorType(anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset)) + , m_isLegacyEditingPosition(true) +{ +} + +Position::Position(PassRefPtr<Node> anchorNode, AnchorType anchorType) + : m_anchorNode(anchorNode) + , m_offset(0) + , m_anchorType(anchorType) + , m_isLegacyEditingPosition(false) +{ + ASSERT(anchorType != PositionIsOffsetInAnchor); +} + +Position::Position(PassRefPtr<Node> anchorNode, int offset, AnchorType anchorType) + : m_anchorNode(anchorNode) + , m_offset(offset) + , m_anchorType(anchorType) + , m_isLegacyEditingPosition(false) +{ + ASSERT(anchorType == PositionIsOffsetInAnchor); +} + +void Position::moveToPosition(PassRefPtr<Node> node, int offset) +{ + ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition); + m_anchorNode = node; + m_offset = offset; + if (m_isLegacyEditingPosition) + m_anchorType = anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset); +} +void Position::moveToOffset(int offset) +{ + ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition); + m_offset = offset; + if (m_isLegacyEditingPosition) + m_anchorType = anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset); +} + +Node* Position::containerNode() const +{ + if (!m_anchorNode) + return 0; + + switch (anchorType()) { + case PositionIsOffsetInAnchor: + return m_anchorNode.get(); + case PositionIsBeforeAnchor: + case PositionIsAfterAnchor: + return m_anchorNode->parentNode(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +int Position::computeOffsetInContainerNode() const +{ + if (!m_anchorNode) + return 0; + + switch (anchorType()) { + case PositionIsOffsetInAnchor: + return std::min(lastOffsetInNode(m_anchorNode.get()), m_offset); + case PositionIsBeforeAnchor: + return m_anchorNode->nodeIndex(); + case PositionIsAfterAnchor: + return m_anchorNode->nodeIndex() + 1; + } + ASSERT_NOT_REACHED(); + return 0; +} + +Node* Position::computeNodeBeforePosition() const +{ + if (!m_anchorNode) + return 0; + + switch (anchorType()) { + case PositionIsOffsetInAnchor: + return m_anchorNode->childNode(m_offset - 1); // -1 converts to childNode((unsigned)-1) and returns null. + case PositionIsBeforeAnchor: + return m_anchorNode->previousSibling(); + case PositionIsAfterAnchor: + return m_anchorNode.get(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +Node* Position::computeNodeAfterPosition() const +{ + if (!m_anchorNode) + return 0; + + switch (anchorType()) { + case PositionIsOffsetInAnchor: + return m_anchorNode->childNode(m_offset); + case PositionIsBeforeAnchor: + return m_anchorNode.get(); + case PositionIsAfterAnchor: + return m_anchorNode->nextSibling(); + } + ASSERT_NOT_REACHED(); + return 0; +} + +Position::AnchorType Position::anchorTypeForLegacyEditingPosition(Node* anchorNode, int offset) +{ + if (anchorNode && editingIgnoresContent(anchorNode)) { + if (offset == 0) + return Position::PositionIsBeforeAnchor; + return Position::PositionIsAfterAnchor; + } + return Position::PositionIsOffsetInAnchor; +} + +// FIXME: This method is confusing (does it return anchorNode() or containerNode()?) and should be renamed or removed +Element* Position::element() const +{ + Node* n = anchorNode(); + while (n && !n->isElementNode()) + n = n->parentNode(); + return static_cast<Element*>(n); +} + +PassRefPtr<CSSComputedStyleDeclaration> Position::computedStyle() const +{ + Element* elem = element(); + if (!elem) + return 0; + return WebCore::computedStyle(elem); +} + +Position Position::previous(PositionMoveType moveType) const +{ + Node* n = node(); + if (!n) + return *this; + + int o = m_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 lastDeepEditingPositionForNode(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. + switch (moveType) { + case CodePoint: + return Position(n, o - 1); + case Character: + return Position(n, uncheckedPreviousOffset(n, o)); + case BackwardDeletion: + return Position(n, uncheckedPreviousOffsetForBackwardDeletion(n, o)); + } + } + + ContainerNode* parent = n->parentNode(); + if (!parent) + return *this; + + return Position(parent, n->nodeIndex()); +} + +Position Position::next(PositionMoveType moveType) const +{ + ASSERT(moveType != BackwardDeletion); + + Node* n = node(); + if (!n) + return *this; + + int o = m_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 < lastOffsetForEditing(n))) { + if (child) + return firstDeepEditingPositionForNode(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 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, (moveType == Character) ? uncheckedNextOffset(n, o) : o + 1); + } + + ContainerNode* 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::uncheckedPreviousOffsetForBackwardDeletion(const Node* n, int current) +{ + return n->renderer() ? n->renderer()->previousOffsetForBackwardDeletion(current) : current - 1; +} + +int Position::uncheckedNextOffset(const Node* n, int current) +{ + return n->renderer() ? n->renderer()->nextOffset(current) : current + 1; +} + +bool Position::atFirstEditingPositionForNode() const +{ + if (isNull()) + return true; + return m_offset <= 0; +} + +bool Position::atLastEditingPositionForNode() const +{ + if (isNull()) + return true; + return m_offset >= lastOffsetForEditing(node()); +} + +// A position is considered at editing boundary if one of the following is true: +// 1. It is the first position in the node and the next visually equivalent position +// is non editable. +// 2. It is the last position in the node and the previous visually equivalent position +// is non editable. +// 3. It is an editable position and both the next and previous visually equivalent +// positions are both non editable. +bool Position::atEditingBoundary() const +{ + Position nextPosition = downstream(CanCrossEditingBoundary); + if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.node()->isContentEditable()) + return true; + + Position prevPosition = upstream(CanCrossEditingBoundary); + if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.node()->isContentEditable()) + return true; + + return nextPosition.isNotNull() && !nextPosition.node()->isContentEditable() + && prevPosition.isNotNull() && !prevPosition.node()->isContentEditable(); +} + +Node* Position::parentEditingBoundary() const +{ + if (!m_anchorNode || !m_anchorNode->document()) + return 0; + + Node* documentElement = m_anchorNode->document()->documentElement(); + if (!documentElement) + return 0; + + Node* boundary = m_anchorNode.get(); + while (boundary != documentElement && boundary->parentNode() && m_anchorNode->isContentEditable() == boundary->parentNode()->isContentEditable()) + boundary = boundary->parentNode(); + + return boundary; +} + + +bool Position::atStartOfTree() const +{ + if (isNull()) + return true; + return !node()->parentNode() && m_offset <= 0; +} + +bool Position::atEndOfTree() const +{ + if (isNull()) + return true; + return !node()->parentNode() && m_offset >= lastOffsetForEditing(node()); +} + +int Position::renderedOffset() const +{ + if (!node()->isTextNode()) + return m_offset; + + if (!node()->renderer()) + return m_offset; + + int result = 0; + RenderText *textRenderer = toRenderText(node()->renderer()); + for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + int start = box->start(); + int end = box->start() + box->len(); + if (m_offset < start) + return result; + if (m_offset <= end) { + result += m_offset - start; + return result; + } + result += box->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.atStartOfTree()) { + 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.atEndOfTree()) { + 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; +} + +// Whether or not [node, 0] and [node, lastOffsetForEditing(node)] are their own VisiblePositions. +// If true, adjacent candidates are visually distinct. +// FIXME: Disregard nodes with renderers that have no height, as we do in isCandidate. +// FIXME: Share code with isCandidate, if possible. +static bool endsOfNodeAreVisuallyDistinctPositions(Node* node) +{ + if (!node || !node->renderer()) + return false; + + if (!node->renderer()->isInline()) + return true; + + // Don't include inline tables. + if (node->hasTagName(tableTag)) + return false; + + // There is a VisiblePosition inside an empty inline-block container. + return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && toRenderBox(node->renderer())->height() != 0 && !node->firstChild(); +} + +static Node* enclosingVisualBoundary(Node* node) +{ + while (node && !endsOfNodeAreVisuallyDistinctPositions(node)) + node = node->parentNode(); + + return node; +} + +// 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(); +} + +// This function and downstream() are used for moving back and forth between visually equivalent candidates. +// For example, for the text node "foo bar" where whitespace is collapsible, there are two candidates +// that map to the VisiblePosition between 'b' and the space. This function will return the left candidate +// and downstream() will return the right one. +// Also, upstream() will return [boundary, 0] for any of the positions from [boundary, 0] to the first candidate +// in boundary, where endsOfNodeAreVisuallyDistinctPositions(boundary) is true. +Position Position::upstream(EditingBoundaryCrossingRule rule) const +{ + Node* startNode = node(); + if (!startNode) + return Position(); + + // iterate backward from there, looking for a qualified position + Node* boundary = enclosingVisualBoundary(startNode); + PositionIterator lastVisible = *this; + PositionIterator currentPos = lastVisible; + bool startEditable = startNode->isContentEditable(); + Node* lastNode = startNode; + bool boundaryCrossed = false; + 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) { + if (rule == CannotCrossEditingBoundary) + break; + boundaryCrossed = true; + } + lastNode = currentNode; + } + + // If we've moved to a position that is visually distinct, return the last saved position. There + // is code below that terminates early if we're *about* to move to a visually distinct position. + if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary) + return lastVisible; + + // skip position in unrendered or invisible node + RenderObject* renderer = currentNode->renderer(); + if (!renderer || renderer->style()->visibility() != VISIBLE) + continue; + + if (rule == CanCrossEditingBoundary && boundaryCrossed) { + lastVisible = currentPos; + break; + } + + // track last visible streamer position + if (isStreamer(currentPos)) + lastVisible = currentPos; + + // Don't move past a position that is visually distinct. 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 (endsOfNodeAreVisuallyDistinctPositions(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 lastDeepEditingPositionForNode(currentNode); + continue; + } + + // return current position if it is in rendered text + if (renderer->isText() && toRenderText(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 = toRenderText(renderer); + InlineTextBox* lastTextBox = textRenderer->lastTextBox(); + for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (textOffset <= box->start() + box->len()) { + if (textOffset > box->start()) + return currentPos; + continue; + } + + if (box == lastTextBox || textOffset != box->start() + box->len() + 1) + continue; + + // The text continues on the next line only if the last text box is not on this line and + // none of the boxes on this line have a larger start offset. + + bool continuesOnNextLine = true; + InlineBox* otherBox = box; + while (continuesOnNextLine) { + otherBox = otherBox->nextLeafChild(); + if (!otherBox) + break; + if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && static_cast<InlineTextBox*>(otherBox)->start() > textOffset)) + continuesOnNextLine = false; + } + + otherBox = box; + while (continuesOnNextLine) { + otherBox = otherBox->prevLeafChild(); + if (!otherBox) + break; + if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && static_cast<InlineTextBox*>(otherBox)->start() > textOffset)) + continuesOnNextLine = false; + } + + if (continuesOnNextLine) + return currentPos; + } + } + } + + return lastVisible; +} + +// This function and upstream() are used for moving back and forth between visually equivalent candidates. +// For example, for the text node "foo bar" where whitespace is collapsible, there are two candidates +// that map to the VisiblePosition between 'b' and the space. This function will return the right candidate +// and upstream() will return the left one. +// Also, downstream() will return the last position in the last atomic node in boundary for all of the positions +// in boundary after the last candidate, where endsOfNodeAreVisuallyDistinctPositions(boundary). +Position Position::downstream(EditingBoundaryCrossingRule rule) const +{ + Node* startNode = node(); + if (!startNode) + return Position(); + + // iterate forward from there, looking for a qualified position + Node* boundary = enclosingVisualBoundary(startNode); + PositionIterator lastVisible = *this; + PositionIterator currentPos = lastVisible; + bool startEditable = startNode->isContentEditable(); + Node* lastNode = startNode; + bool boundaryCrossed = false; + 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) { + if (rule == CannotCrossEditingBoundary) + break; + boundaryCrossed = true; + } + + 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 move to a visually distinct position. + if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary) + return lastVisible; + // Do not move past a visually disinct position. + // Note: The first position after the last in a node whose ends are visually distinct + // positions will be [boundary->parentNode(), originalBlock->nodeIndex() + 1]. + if (boundary && boundary->parentNode() == currentNode) + return lastVisible; + + // skip position in unrendered or invisible node + RenderObject* renderer = currentNode->renderer(); + if (!renderer || renderer->style()->visibility() != VISIBLE) + continue; + + if (rule == CanCrossEditingBoundary && boundaryCrossed) { + lastVisible = currentPos; + break; + } + + // 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() && toRenderText(renderer)->firstTextBox()) { + if (currentNode != startNode) { + ASSERT(currentPos.atStartOfNode()); + return Position(currentNode, renderer->caretMinOffset()); + } + + unsigned textOffset = currentPos.offsetInLeafNode(); + RenderText* textRenderer = toRenderText(renderer); + InlineTextBox* lastTextBox = textRenderer->lastTextBox(); + for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (textOffset <= box->end()) { + if (textOffset >= box->start()) + return currentPos; + continue; + } + + if (box == lastTextBox || textOffset != box->start() + box->len()) + continue; + + // The text continues on the next line only if the last text box is not on this line and + // none of the boxes on this line have a larger start offset. + + bool continuesOnNextLine = true; + InlineBox* otherBox = box; + while (continuesOnNextLine) { + otherBox = otherBox->nextLeafChild(); + if (!otherBox) + break; + if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && static_cast<InlineTextBox*>(otherBox)->start() >= textOffset)) + continuesOnNextLine = false; + } + + otherBox = box; + while (continuesOnNextLine) { + otherBox = otherBox->prevLeafChild(); + if (!otherBox) + break; + if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && static_cast<InlineTextBox*>(otherBox)->start() >= textOffset)) + continuesOnNextLine = false; + } + + if (continuesOnNextLine) + 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->node()) { + if ((o->isText() && toRenderText(o)->linesBoundingBox().height()) || + (o->isBox() && toRenderBox(o)->borderBoundingBox().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 !m_offset && !nodeIsUserSelectNone(node()->parentNode()); + + if (renderer->isText()) + return !nodeIsUserSelectNone(node()) && inRenderedText(); + + if (isTableElement(node()) || editingIgnoresContent(node())) + return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(node()->parentNode()); + + if (m_anchorNode->hasTagName(htmlTag)) + return false; + + if (renderer->isBlockFlow()) { + if (toRenderBlock(renderer)->height() || m_anchorNode->hasTagName(bodyTag)) { + if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer)) + return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(node()); + return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(node()) && atEditingBoundary(); + } + } else + return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(node()) && atEditingBoundary(); + + return false; +} + +bool Position::inRenderedText() const +{ + if (isNull() || !node()->isTextNode()) + return false; + + RenderObject *renderer = node()->renderer(); + if (!renderer) + return false; + + RenderText *textRenderer = toRenderText(renderer); + for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (m_offset < static_cast<int>(box->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(m_offset)) + // Return false for offsets inside composed characters. + return m_offset == 0 || m_offset == textRenderer->nextOffset(textRenderer->previousOffset(m_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 = toRenderText(renderer); + for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (m_offset < static_cast<int>(box->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 (m_offset >= static_cast<int>(box->start()) && m_offset < static_cast<int>(box->start() + box->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 (m_offset == pos.deprecatedEditingOffset()) + return false; + + if (!node()->isTextNode() && !pos.node()->isTextNode()) { + if (m_offset != pos.deprecatedEditingOffset()) + 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; + + int ignoredCaretOffset; + InlineBox* b1; + getInlineBoxAndOffset(DOWNSTREAM, b1, ignoredCaretOffset); + InlineBox* b2; + pos.getInlineBoxAndOffset(DOWNSTREAM, b2, ignoredCaretOffset); + + LOG(Editing, "renderer: %p [%p]\n", renderer, b1); + LOG(Editing, "thisRenderedOffset: %d\n", thisRenderedOffset); + LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, b2); + 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"); + + 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.deprecatedEditingOffset()]; + 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, 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::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox, int& caretOffset) const +{ + getInlineBoxAndOffset(affinity, primaryDirection(), inlineBox, caretOffset); +} + +static bool isNonTextLeafChild(RenderObject* object) +{ + if (object->firstChild()) + return false; + if (object->isText()) + return false; + return true; +} + +static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer) +{ + RenderBlock* container = renderer->containingBlock(); + RenderObject* next = renderer; + while ((next = next->nextInPreOrder(container))) { + if (next->isRenderBlock()) + return 0; + if (next->isBR()) + return 0; + if (isNonTextLeafChild(next)) + return 0; + if (next->isText()) { + InlineTextBox* match = 0; + int minOffset = INT_MAX; + for (InlineTextBox* box = toRenderText(next)->firstTextBox(); box; box = box->nextTextBox()) { + int caretMinOffset = box->caretMinOffset(); + if (caretMinOffset < minOffset) { + match = box; + minOffset = caretMinOffset; + } + } + if (match) + return match; + } + } + return 0; +} + +static Position downstreamIgnoringEditingBoundaries(Position position) +{ + Position lastPosition; + while (position != lastPosition) { + lastPosition = position; + position = position.downstream(CanCrossEditingBoundary); + } + return position; +} + +static Position upstreamIgnoringEditingBoundaries(Position position) +{ + Position lastPosition; + while (position != lastPosition) { + lastPosition = position; + position = position.upstream(CanCrossEditingBoundary); + } + return position; +} + +void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const +{ + caretOffset = m_offset; + RenderObject* renderer = node()->renderer(); + + if (!renderer->isText()) { + inlineBox = 0; + if (canHaveChildrenForEditing(node()) && renderer->isBlockFlow() && hasRenderedNonAnonymousDescendantsWithHeight(renderer)) { + // Try a visually equivalent position with possibly opposite editability. This helps in case |this| is in + // an editable block but surrounded by non-editable positions. It acts to negate the logic at the beginning + // of RenderObject::createVisiblePosition(). + Position equivalent = downstreamIgnoringEditingBoundaries(*this); + if (equivalent == *this) { + equivalent = upstreamIgnoringEditingBoundaries(*this); + if (equivalent == *this || downstreamIgnoringEditingBoundaries(equivalent) == *this) + return; + } + + equivalent.getInlineBoxAndOffset(UPSTREAM, primaryDirection, inlineBox, caretOffset); + return; + } + if (renderer->isBox()) { + inlineBox = toRenderBox(renderer)->inlineBoxWrapper(); + if (!inlineBox || (caretOffset > inlineBox->caretMinOffset() && caretOffset < inlineBox->caretMaxOffset())) + return; + } + } else { + RenderText* textRenderer = toRenderText(renderer); + + InlineTextBox* box; + InlineTextBox* candidate = 0; + + for (box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + int caretMinOffset = box->caretMinOffset(); + int caretMaxOffset = box->caretMaxOffset(); + + if (caretOffset < caretMinOffset || caretOffset > caretMaxOffset || (caretOffset == caretMaxOffset && box->isLineBreak())) + continue; + + if (caretOffset > caretMinOffset && caretOffset < caretMaxOffset) { + inlineBox = box; + return; + } + + if (((caretOffset == caretMaxOffset) ^ (affinity == DOWNSTREAM)) + || ((caretOffset == caretMinOffset) ^ (affinity == UPSTREAM))) + break; + + candidate = box; + } + if (candidate && candidate == textRenderer->lastTextBox() && affinity == DOWNSTREAM) { + box = searchAheadForBetterMatch(textRenderer); + if (box) + caretOffset = box->caretMinOffset(); + } + inlineBox = box ? box : candidate; + } + + if (!inlineBox) + return; + + unsigned char level = inlineBox->bidiLevel(); + + if (inlineBox->direction() == primaryDirection) { + if (caretOffset == inlineBox->caretRightmostOffset()) { + InlineBox* nextBox = inlineBox->nextLeafChild(); + if (!nextBox || nextBox->bidiLevel() >= level) + return; + + level = nextBox->bidiLevel(); + InlineBox* prevBox = inlineBox; + do { + prevBox = prevBox->prevLeafChild(); + } while (prevBox && prevBox->bidiLevel() > level); + + if (prevBox && prevBox->bidiLevel() == level) // For example, abc FED 123 ^ CBA + return; + + // For example, abc 123 ^ CBA + while (InlineBox* nextBox = inlineBox->nextLeafChild()) { + if (nextBox->bidiLevel() < level) + break; + inlineBox = nextBox; + } + caretOffset = inlineBox->caretRightmostOffset(); + } else { + InlineBox* prevBox = inlineBox->prevLeafChild(); + if (!prevBox || prevBox->bidiLevel() >= level) + return; + + level = prevBox->bidiLevel(); + InlineBox* nextBox = inlineBox; + do { + nextBox = nextBox->nextLeafChild(); + } while (nextBox && nextBox->bidiLevel() > level); + + if (nextBox && nextBox->bidiLevel() == level) + return; + + while (InlineBox* prevBox = inlineBox->prevLeafChild()) { + if (prevBox->bidiLevel() < level) + break; + inlineBox = prevBox; + } + caretOffset = inlineBox->caretLeftmostOffset(); + } + return; + } + + if (caretOffset == inlineBox->caretLeftmostOffset()) { + InlineBox* prevBox = inlineBox->prevLeafChild(); + if (!prevBox || prevBox->bidiLevel() < level) { + // Left edge of a secondary run. Set to the right edge of the entire run. + while (InlineBox* nextBox = inlineBox->nextLeafChild()) { + if (nextBox->bidiLevel() < level) + break; + inlineBox = nextBox; + } + caretOffset = inlineBox->caretRightmostOffset(); + } else if (prevBox->bidiLevel() > level) { + // Right edge of a "tertiary" run. Set to the left edge of that run. + while (InlineBox* tertiaryBox = inlineBox->prevLeafChild()) { + if (tertiaryBox->bidiLevel() <= level) + break; + inlineBox = tertiaryBox; + } + caretOffset = inlineBox->caretLeftmostOffset(); + } + } else { + InlineBox* nextBox = inlineBox->nextLeafChild(); + if (!nextBox || nextBox->bidiLevel() < level) { + // Right edge of a secondary run. Set to the left edge of the entire run. + while (InlineBox* prevBox = inlineBox->prevLeafChild()) { + if (prevBox->bidiLevel() < level) + break; + inlineBox = prevBox; + } + caretOffset = inlineBox->caretLeftmostOffset(); + } else if (nextBox->bidiLevel() > level) { + // Left edge of a "tertiary" run. Set to the right edge of that run. + while (InlineBox* tertiaryBox = inlineBox->nextLeafChild()) { + if (tertiaryBox->bidiLevel() <= level) + break; + inlineBox = tertiaryBox; + } + caretOffset = inlineBox->caretRightmostOffset(); + } + } +} + +TextDirection Position::primaryDirection() const +{ + TextDirection primaryDirection = LTR; + for (const RenderObject* r = m_anchorNode->renderer(); r; r = r->parent()) { + if (r->isBlockFlow()) { + primaryDirection = r->style()->direction(); + break; + } + } + + return primaryDirection; +} + + +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(), m_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 "; + node()->formatForDebugger(s, sizeof(s)); + result += s; + } + + strncpy(buffer, result.utf8().data(), length - 1); +} + +void Position::showTreeForThis() const +{ + if (node()) { + node()->showTreeForThis(); + fprintf(stderr, "offset: %d\n", m_offset); + } +} + +#endif + + + +} // 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/Source/WebCore/dom/Position.h b/Source/WebCore/dom/Position.h new file mode 100644 index 0000000..4d3b5fe --- /dev/null +++ b/Source/WebCore/dom/Position.h @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2004, 2006, 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 Position_h +#define Position_h + +#include "ContainerNode.h" +#include "EditingBoundary.h" +#include "TextAffinity.h" +#include "TextDirection.h" +#include <wtf/Assertions.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class CSSComputedStyleDeclaration; +class Element; +class InlineBox; +class Node; +class Range; +class RenderObject; + +enum PositionMoveType { + CodePoint, // Move by a single code point. + Character, // Move to the next Unicode character break. + BackwardDeletion // Subject to platform conventions. +}; + +class Position { +public: + enum AnchorType { + PositionIsOffsetInAnchor, + PositionIsAfterAnchor, + PositionIsBeforeAnchor + }; + + Position() + : m_offset(0) + , m_anchorType(PositionIsOffsetInAnchor) + , m_isLegacyEditingPosition(false) + { + } + + // For creating legacy editing positions: (Anchor type will be determined from editingIgnoresContent(node)) + Position(PassRefPtr<Node> anchorNode, int offset); + + // For creating before/after positions: + Position(PassRefPtr<Node> anchorNode, AnchorType); + // For creating offset positions: + Position(PassRefPtr<Node> anchorNode, int offset, AnchorType); + + AnchorType anchorType() const { return static_cast<AnchorType>(m_anchorType); } + + void clear() { m_anchorNode.clear(); m_offset = 0; m_anchorType = PositionIsOffsetInAnchor; m_isLegacyEditingPosition = false; } + + // These are always DOM compliant values. Editing positions like [img, 0] (aka [img, before]) + // will return img->parentNode() and img->nodeIndex() from these functions. + Node* containerNode() const; // NULL for a before/after position anchored to a node with no parent + int computeOffsetInContainerNode() const; // O(n) for before/after-anchored positions, O(1) for parent-anchored positions + + // Inline O(1) access for Positions which callers know to be parent-anchored + int offsetInContainerNode() const + { + ASSERT(anchorType() == PositionIsOffsetInAnchor); + return m_offset; + } + + // New code should not use this function. + int deprecatedEditingOffset() const + { + // This should probably ASSERT(m_isLegacyEditingPosition); + return m_offset; + } + + // These are convenience methods which are smart about whether the position is neighbor anchored or parent anchored + Node* computeNodeBeforePosition() const; + Node* computeNodeAfterPosition() const; + + Node* anchorNode() const { return m_anchorNode.get(); } + + // FIXME: Callers should be moved off of node(), node() is not always the container for this position. + // For nodes which editingIgnoresContent(node()) returns true, positions like [ignoredNode, 0] + // will be treated as before ignoredNode (thus node() is really after the position, not containing it). + Node* node() const { return m_anchorNode.get(); } + + // These should only be used for PositionIsOffsetInAnchor positions, unless + // the position is a legacy editing position. + void moveToPosition(PassRefPtr<Node> anchorNode, int offset); + void moveToOffset(int offset); + + bool isNull() const { return !m_anchorNode; } + bool isNotNull() const { return m_anchorNode; } + bool isOrphan() const { return m_anchorNode && !m_anchorNode->inDocument(); } + + 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(PositionMoveType = CodePoint) const; + Position next(PositionMoveType = CodePoint) const; + static int uncheckedPreviousOffset(const Node*, int current); + static int uncheckedPreviousOffsetForBackwardDeletion(const Node*, int current); + static int uncheckedNextOffset(const Node*, int current); + + // These can be either inside or just before/after the node, depending on + // if the node is ignored by editing or not. + bool atFirstEditingPositionForNode() const; + bool atLastEditingPositionForNode() const; + + // Returns true if the visually equivalent positions around have different editability + bool atEditingBoundary() const; + Node* parentEditingBoundary() const; + + bool atStartOfTree() const; + bool atEndOfTree() 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; + + // These return useful visually equivalent positions. + Position upstream(EditingBoundaryCrossingRule = CannotCrossEditingBoundary) const; + Position downstream(EditingBoundaryCrossingRule = CannotCrossEditingBoundary) const; + + bool isCandidate() const; + bool inRenderedText() const; + bool isRenderedCharacter() const; + bool rendersInDifferentPosition(const Position&) const; + + void getInlineBoxAndOffset(EAffinity, InlineBox*&, int& caretOffset) const; + void getInlineBoxAndOffset(EAffinity, TextDirection primaryDirection, InlineBox*&, int& caretOffset) const; + + TextDirection primaryDirection() 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; + + static AnchorType anchorTypeForLegacyEditingPosition(Node* anchorNode, int offset); + + RefPtr<Node> m_anchorNode; + // m_offset can be the offset inside m_anchorNode, or if editingIgnoresContent(m_anchorNode) + // returns true, then other places in editing will treat m_offset == 0 as "before the anchor" + // and m_offset > 0 as "after the anchor node". See rangeCompliantEquivalent for more info. + int m_offset; + unsigned m_anchorType : 2; + bool m_isLegacyEditingPosition : 1; +}; + +inline bool operator==(const Position& a, const Position& b) +{ + // FIXME: In <div><img></div> [div, 0] != [img, 0] even though most of the + // editing code will treat them as identical. + return a.anchorNode() == b.anchorNode() && a.deprecatedEditingOffset() == b.deprecatedEditingOffset(); +} + +inline bool operator!=(const Position& a, const Position& b) +{ + return !(a == b); +} + +// We define position creation functions to make callsites more readable. +// These are inline to prevent ref-churn when returning a Position object. +// If we ever add a PassPosition we can make these non-inline. + +inline Position positionInParentBeforeNode(const Node* node) +{ + // FIXME: This should ASSERT(node->parentNode()) + // At least one caller currently hits this ASSERT though, which indicates + // that the caller is trying to make a position relative to a disconnected node (which is likely an error) + // Specifically, editing/deleting/delete-ligature-001.html crashes with ASSERT(node->parentNode()) + return Position(node->parentNode(), node->nodeIndex(), Position::PositionIsOffsetInAnchor); +} + +inline Position positionInParentAfterNode(const Node* node) +{ + ASSERT(node->parentNode()); + return Position(node->parentNode(), node->nodeIndex() + 1, Position::PositionIsOffsetInAnchor); +} + +// positionBeforeNode and positionAfterNode return neighbor-anchored positions, construction is O(1) +inline Position positionBeforeNode(Node* anchorNode) +{ + ASSERT(anchorNode); + return Position(anchorNode, Position::PositionIsBeforeAnchor); +} + +inline Position positionAfterNode(Node* anchorNode) +{ + ASSERT(anchorNode); + return Position(anchorNode, Position::PositionIsAfterAnchor); +} + +inline int lastOffsetInNode(Node* node) +{ + return node->offsetInCharacters() ? node->maxCharacterOffset() : static_cast<int>(node->childNodeCount()); +} + +// firstPositionInNode and lastPositionInNode return parent-anchored positions, lastPositionInNode construction is O(n) due to childNodeCount() +inline Position firstPositionInNode(Node* anchorNode) +{ + return Position(anchorNode, 0, Position::PositionIsOffsetInAnchor); +} + +inline Position lastPositionInNode(Node* anchorNode) +{ + return Position(anchorNode, lastOffsetInNode(anchorNode), Position::PositionIsOffsetInAnchor); +} + +} // 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/Source/WebCore/dom/PositionCreationFunctions.h b/Source/WebCore/dom/PositionCreationFunctions.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Source/WebCore/dom/PositionCreationFunctions.h diff --git a/Source/WebCore/dom/PositionIterator.cpp b/Source/WebCore/dom/PositionIterator.cpp new file mode 100644 index 0000000..5de9d9d --- /dev/null +++ b/Source/WebCore/dom/PositionIterator.cpp @@ -0,0 +1,176 @@ +/* + * 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 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 "RenderBlock.h" +#include "htmlediting.h" + +namespace WebCore { + +using namespace HTMLNames; + +PositionIterator::operator Position() const +{ + if (m_nodeAfterPositionInAnchor) { + ASSERT(m_nodeAfterPositionInAnchor->parentNode() == m_anchorNode); + return positionInParentBeforeNode(m_nodeAfterPositionInAnchor); + } + if (m_anchorNode->hasChildNodes()) + return lastDeepEditingPositionForNode(m_anchorNode); + return Position(m_anchorNode, m_offsetInAnchor); +} + +void PositionIterator::increment() +{ + if (!m_anchorNode) + return; + + if (m_nodeAfterPositionInAnchor) { + m_anchorNode = m_nodeAfterPositionInAnchor; + m_nodeAfterPositionInAnchor = m_anchorNode->firstChild(); + m_offsetInAnchor = 0; + return; + } + + if (!m_anchorNode->hasChildNodes() && m_offsetInAnchor < lastOffsetForEditing(m_anchorNode)) + m_offsetInAnchor = Position::uncheckedNextOffset(m_anchorNode, m_offsetInAnchor); + else { + m_nodeAfterPositionInAnchor = m_anchorNode; + m_anchorNode = m_nodeAfterPositionInAnchor->parentNode(); + m_nodeAfterPositionInAnchor = m_nodeAfterPositionInAnchor->nextSibling(); + m_offsetInAnchor = 0; + } +} + +void PositionIterator::decrement() +{ + if (!m_anchorNode) + return; + + if (m_nodeAfterPositionInAnchor) { + m_anchorNode = m_nodeAfterPositionInAnchor->previousSibling(); + if (m_anchorNode) { + m_nodeAfterPositionInAnchor = 0; + m_offsetInAnchor = m_anchorNode->hasChildNodes() ? 0 : lastOffsetForEditing(m_anchorNode); + } else { + m_nodeAfterPositionInAnchor = m_nodeAfterPositionInAnchor->parentNode(); + m_anchorNode = m_nodeAfterPositionInAnchor->parentNode(); + m_offsetInAnchor = 0; + } + return; + } + + if (m_anchorNode->hasChildNodes()) { + m_anchorNode = m_anchorNode->lastChild(); + m_offsetInAnchor = m_anchorNode->hasChildNodes()? 0: lastOffsetForEditing(m_anchorNode); + } else { + if (m_offsetInAnchor) + m_offsetInAnchor = Position::uncheckedPreviousOffset(m_anchorNode, m_offsetInAnchor); + else { + m_nodeAfterPositionInAnchor = m_anchorNode; + m_anchorNode = m_anchorNode->parentNode(); + } + } +} + +void PositionIterator::setOffsetInLeafNode(int offset) +{ + ASSERT(!m_anchorNode->hasChildNodes()); + ASSERT(0 <= offset && offset <= lastOffsetForEditing(m_anchorNode)); + m_offsetInAnchor = offset; +} + +bool PositionIterator::atStart() const +{ + if (!m_anchorNode) + return true; + if (m_anchorNode->parentNode()) + return false; + return (!m_anchorNode->hasChildNodes() && !m_offsetInAnchor) || (m_nodeAfterPositionInAnchor && !m_nodeAfterPositionInAnchor->previousSibling()); +} + +bool PositionIterator::atEnd() const +{ + if (!m_anchorNode) + return true; + if (m_nodeAfterPositionInAnchor) + return false; + return !m_anchorNode->parentNode() && (m_anchorNode->hasChildNodes() || m_offsetInAnchor >= lastOffsetForEditing(m_anchorNode)); +} + +bool PositionIterator::atStartOfNode() const +{ + if (!m_anchorNode) + return true; + if (!m_nodeAfterPositionInAnchor) + return !m_anchorNode->hasChildNodes() && !m_offsetInAnchor; + return !m_nodeAfterPositionInAnchor->previousSibling(); +} + +bool PositionIterator::atEndOfNode() const +{ + if (!m_anchorNode) + return true; + if (m_nodeAfterPositionInAnchor) + return false; + return m_anchorNode->hasChildNodes() || m_offsetInAnchor >= lastOffsetForEditing(m_anchorNode); +} + +bool PositionIterator::isCandidate() const +{ + if (!m_anchorNode) + return false; + + RenderObject* renderer = m_anchorNode->renderer(); + if (!renderer) + return false; + + if (renderer->style()->visibility() != VISIBLE) + return false; + + if (renderer->isBR()) + return !m_offsetInAnchor && !Position::nodeIsUserSelectNone(m_anchorNode->parentNode()); + + if (renderer->isText()) + return !Position::nodeIsUserSelectNone(m_anchorNode) && Position(*this).inRenderedText(); + + if (isTableElement(m_anchorNode) || editingIgnoresContent(m_anchorNode)) + return (atStartOfNode() || atEndOfNode()) && !Position::nodeIsUserSelectNone(m_anchorNode->parentNode()); + + if (!m_anchorNode->hasTagName(htmlTag) && renderer->isBlockFlow()) { + if (toRenderBlock(renderer)->height() || m_anchorNode->hasTagName(bodyTag)) { + if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer)) + return atStartOfNode() && !Position::nodeIsUserSelectNone(m_anchorNode); + return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(m_anchorNode) && Position(*this).atEditingBoundary(); + } + } + + return false; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/PositionIterator.h b/Source/WebCore/dom/PositionIterator.h new file mode 100644 index 0000000..08329bb --- /dev/null +++ b/Source/WebCore/dom/PositionIterator.h @@ -0,0 +1,75 @@ +/* + * 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 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 "Node.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_anchorNode(0) + , m_nodeAfterPositionInAnchor(0) + , m_offsetInAnchor(0) + { + } + + PositionIterator(const Position& pos) + : m_anchorNode(pos.anchorNode()) + , m_nodeAfterPositionInAnchor(m_anchorNode->childNode(pos.deprecatedEditingOffset())) + , m_offsetInAnchor(m_nodeAfterPositionInAnchor ? 0 : pos.deprecatedEditingOffset()) + { + } + operator Position() const; + + void increment(); + void decrement(); + + Node* node() const { return m_anchorNode; } + int offsetInLeafNode() const { return m_offsetInAnchor; } + void setOffsetInLeafNode(int offset); + + bool atStart() const; + bool atEnd() const; + bool atStartOfNode() const; + bool atEndOfNode() const; + bool isCandidate() const; + +private: + Node* m_anchorNode; + Node* m_nodeAfterPositionInAnchor; // If this is non-null, m_nodeAfterPositionInAnchor->parentNode() == m_anchorNode; + int m_offsetInAnchor; +}; + +} // namespace WebCore + +#endif // PositionIterator_h diff --git a/Source/WebCore/dom/ProcessingInstruction.cpp b/Source/WebCore/dom/ProcessingInstruction.cpp new file mode 100644 index 0000000..e7dea48 --- /dev/null +++ b/Source/WebCore/dom/ProcessingInstruction.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006, 2008, 2009 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 "CachedResourceLoader.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "XSLStyleSheet.h" +#include "XMLDocumentParser.h" // for parseAttributes() +#include "MediaList.h" + +namespace WebCore { + +inline ProcessingInstruction::ProcessingInstruction(Document* document, const String& target, const String& data) + : ContainerNode(document) + , m_target(target) + , m_data(data) + , m_cachedSheet(0) + , m_loading(false) + , m_alternate(false) + , m_createdByParser(false) +#if ENABLE(XSLT) + , m_isXSL(false) +#endif +{ +} + +PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document* document, const String& target, const String& data) +{ + return adoptRef(new ProcessingInstruction(document, target, data)); +} + +ProcessingInstruction::~ProcessingInstruction() +{ + if (m_sheet) + m_sheet->clearOwnerNode(); + + if (m_cachedSheet) + m_cachedSheet->removeClient(this); +} + +void ProcessingInstruction::setData(const String& data, ExceptionCode&) +{ + int oldLength = m_data.length(); + m_data = data; + document()->textRemoved(this, 0, oldLength); + checkStyleSheet(); +} + +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*/) +{ + // FIXME: Is it a problem that this does not copy m_localHref? + // What about other data members? + return create(document(), m_target, m_data); +} + +// DOM Section 1.1.1 +bool ProcessingInstruction::childTypeAllowed(NodeType) +{ + return false; +} + +void ProcessingInstruction::checkStyleSheet() +{ + if (m_target == "xml-stylesheet" && document()->frame() && parentNode() == document()) { + // 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; + 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; + + String href = attrs.get("href"); + String alternate = attrs.get("alternate"); + m_alternate = alternate == "yes"; + m_title = attrs.get("title"); + m_media = attrs.get("media"); + + if (href.length() > 1 && 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) { + KURL finalURL(ParsedURLString, m_localHref); + m_sheet = XSLStyleSheet::createEmbedded(this, finalURL); + m_loading = false; + } +#endif + } else { + if (m_cachedSheet) { + m_cachedSheet->removeClient(this); + m_cachedSheet = 0; + } + + String url = document()->completeURL(href).string(); + if (!dispatchBeforeLoadEvent(url)) + return; + + m_loading = true; + document()->addPendingSheet(); + +#if ENABLE(XSLT) + if (m_isXSL) + m_cachedSheet = document()->cachedResourceLoader()->requestXSLStyleSheet(url); + else +#endif + { + String charset = attrs.get("charset"); + if (charset.isEmpty()) + charset = document()->frame()->loader()->writer()->encoding(); + + m_cachedSheet = document()->cachedResourceLoader()->requestCSSStyleSheet(url, charset); + } + if (m_cachedSheet) + m_cachedSheet->addClient(this); + else { + // The request may have been denied if (for example) the stylesheet is local and the document is remote. + m_loading = false; + document()->removePendingSheet(); + } + } + } +} + +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& href, const KURL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet) +{ + if (!inDocument()) { + ASSERT(!m_sheet); + return; + } + +#if ENABLE(XSLT) + ASSERT(!m_isXSL); +#endif + RefPtr<CSSStyleSheet> newSheet = CSSStyleSheet::create(this, href, baseURL, charset); + m_sheet = newSheet; + // We don't need the cross-origin security check here because we are + // getting the sheet text in "strict" mode. This enforces a valid CSS MIME + // type. + parseStyleSheet(sheet->sheetText(true)); + newSheet->setTitle(m_title); + newSheet->setMedia(MediaList::create(newSheet.get(), m_media)); + newSheet->setDisabled(m_alternate); +} + +#if ENABLE(XSLT) +void ProcessingInstruction::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet) +{ + ASSERT(m_isXSL); + m_sheet = XSLStyleSheet::create(this, href, baseURL); + parseStyleSheet(sheet); +} +#endif + +void ProcessingInstruction::parseStyleSheet(const String& sheet) +{ + m_sheet->parseString(sheet, true); + if (m_cachedSheet) + m_cachedSheet->removeClient(this); + m_cachedSheet = 0; + + m_loading = false; + m_sheet->checkLoaded(); +} + +void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet) +{ + ASSERT(!m_cachedSheet); + ASSERT(!m_loading); + m_sheet = sheet; + m_sheet->setTitle(m_title); + m_sheet->setDisabled(m_alternate); +} + +bool ProcessingInstruction::offsetInCharacters() const +{ + return true; +} + +int ProcessingInstruction::maxCharacterOffset() const +{ + return static_cast<int>(m_data.length()); +} + +void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + if (!sheet()) + return; + + addSubresourceURL(urls, sheet()->baseURL()); +} + +void ProcessingInstruction::insertedIntoDocument() +{ + ContainerNode::insertedIntoDocument(); + document()->addStyleSheetCandidateNode(this, m_createdByParser); + checkStyleSheet(); +} + +void ProcessingInstruction::removedFromDocument() +{ + ContainerNode::removedFromDocument(); + + document()->removeStyleSheetCandidateNode(this); + + if (m_sheet) { + ASSERT(m_sheet->ownerNode() == this); + m_sheet->clearOwnerNode(); + m_sheet = 0; + } + + if (m_cachedSheet) + document()->styleSelectorChanged(DeferRecalcStyle); +} + +void ProcessingInstruction::finishParsingChildren() +{ + m_createdByParser = false; + ContainerNode::finishParsingChildren(); +} + +#ifdef ANDROID_INSTRUMENT +void* ProcessingInstruction::operator new(size_t size) +{ + return Node::operator new(size); +} + +void* ProcessingInstruction::operator new[](size_t size) +{ + return Node::operator new[](size); +} + +void ProcessingInstruction::operator delete(void* p, size_t size) +{ + Node::operator delete(p, size); +} + +void ProcessingInstruction::operator delete[](void* p, size_t size) +{ + Node::operator delete[](p, size); +} +#endif + +} // namespace diff --git a/Source/WebCore/dom/ProcessingInstruction.h b/Source/WebCore/dom/ProcessingInstruction.h new file mode 100644 index 0000000..31f680d --- /dev/null +++ b/Source/WebCore/dom/ProcessingInstruction.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2000 Peter Kelly (pmk@post.com) + * Copyright (C) 2006 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 ProcessingInstruction_h +#define ProcessingInstruction_h + +#include "CachedResourceClient.h" +#include "CachedResourceHandle.h" +#include "ContainerNode.h" + +namespace WebCore { + +class StyleSheet; +class CSSStyleSheet; + +class ProcessingInstruction : public ContainerNode, private CachedResourceClient { +public: + static PassRefPtr<ProcessingInstruction> create(Document*, const String& target, const String& data); + virtual ~ProcessingInstruction(); + + const String& target() const { return m_target; } + const String& data() const { return m_data; } + void setData(const String&, ExceptionCode&); + + void setCreatedByParser(bool createdByParser) { m_createdByParser = createdByParser; } + + virtual void finishParsingChildren(); + + const String& localHref() const { return m_localHref; } + StyleSheet* sheet() const { return m_sheet.get(); } + void setCSSStyleSheet(PassRefPtr<CSSStyleSheet>); + +#if ENABLE(XSLT) + bool isXSL() const { return m_isXSL; } +#endif + +private: + ProcessingInstruction(Document*, const String& target, const String& data); + +#ifdef ANDROID_INSTRUMENT + // Overridden to resolve the ambiguous + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void* p, size_t size); + void operator delete[](void* p, size_t size); +#endif + + 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; + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + void checkStyleSheet(); + virtual void setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CachedCSSStyleSheet*); +#if ENABLE(XSLT) + virtual void setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet); +#endif + + bool isLoading() const; + virtual bool sheetLoaded(); + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + void parseStyleSheet(const String& sheet); + + String m_target; + String m_data; + String m_localHref; + String m_title; + String m_media; + CachedResourceHandle<CachedResource> m_cachedSheet; + RefPtr<StyleSheet> m_sheet; + bool m_loading; + bool m_alternate; + bool m_createdByParser; +#if ENABLE(XSLT) + bool m_isXSL; +#endif +}; + +} //namespace + +#endif diff --git a/Source/WebCore/dom/ProcessingInstruction.idl b/Source/WebCore/dom/ProcessingInstruction.idl new file mode 100644 index 0000000..8c722d3 --- /dev/null +++ b/Source/WebCore/dom/ProcessingInstruction.idl @@ -0,0 +1,35 @@ +/* + * 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 [CustomMarkFunction] ProcessingInstruction : Node { + + // DOM Level 1 + + readonly attribute [ConvertNullStringTo=Null] DOMString target; + attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString data + setter raises(DOMException); + + // interface LinkStyle from DOM Level 2 Style Sheets + readonly attribute StyleSheet sheet; + }; + +} diff --git a/Source/WebCore/dom/ProgressEvent.cpp b/Source/WebCore/dom/ProgressEvent.cpp new file mode 100644 index 0000000..c85eeff --- /dev/null +++ b/Source/WebCore/dom/ProgressEvent.cpp @@ -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. + */ + +#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 long long loaded, unsigned long long 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 long long loadedArg, unsigned long long totalArg) +{ + if (dispatched()) + return; + + initEvent(typeArg, canBubbleArg, cancelableArg); + + m_lengthComputable = lengthComputableArg; + m_loaded = loadedArg; + m_total = totalArg; +} + +} diff --git a/Source/WebCore/dom/ProgressEvent.h b/Source/WebCore/dom/ProgressEvent.h new file mode 100644 index 0000000..3e295c6 --- /dev/null +++ b/Source/WebCore/dom/ProgressEvent.h @@ -0,0 +1,64 @@ +/* + * 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: + static PassRefPtr<ProgressEvent> create() + { + return adoptRef(new ProgressEvent); + } + static PassRefPtr<ProgressEvent> create(const AtomicString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total) + { + return adoptRef(new ProgressEvent(type, lengthComputable, loaded, total)); + } + + void initProgressEvent(const AtomicString& typeArg, bool canBubbleArg, bool cancelableArg, + bool lengthComputableArg, unsigned long long loadedArg, unsigned long long totalArg); + + bool lengthComputable() const { return m_lengthComputable; } + unsigned long long loaded() const { return m_loaded; } + unsigned long long total() const { return m_total; } + +protected: + ProgressEvent(); + ProgressEvent(const AtomicString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total); + +private: + virtual bool isProgressEvent() const { return true; } + + bool m_lengthComputable; + unsigned long long m_loaded; + unsigned long long m_total; +}; +} + +#endif // ProgressEvent_h diff --git a/Source/WebCore/dom/ProgressEvent.idl b/Source/WebCore/dom/ProgressEvent.idl new file mode 100644 index 0000000..e832def --- /dev/null +++ b/Source/WebCore/dom/ProgressEvent.idl @@ -0,0 +1,37 @@ +/* + * 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 ProgressEvent : Event { + readonly attribute boolean lengthComputable; + readonly attribute unsigned long long loaded; + readonly attribute unsigned long long total; + + void initProgressEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, + in boolean lengthComputableArg, in unsigned long long loadedArg, in unsigned long long totalArg); + }; + +} diff --git a/Source/WebCore/dom/QualifiedName.cpp b/Source/WebCore/dom/QualifiedName.cpp new file mode 100644 index 0000000..90c6d29 --- /dev/null +++ b/Source/WebCore/dom/QualifiedName.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2005, 2006, 2009 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" + +#ifdef SKIP_STATIC_CONSTRUCTORS_ON_GCC +#define WEBCORE_QUALIFIEDNAME_HIDE_GLOBALS 1 +#else +#define QNAME_DEFAULT_CONSTRUCTOR +#endif + +#include "QualifiedName.h" +#include <wtf/Assertions.h> +#include <wtf/HashSet.h> +#include <wtf/StaticConstructors.h> + +namespace WebCore { + +typedef HashSet<QualifiedName::QualifiedNameImpl*, QualifiedNameHash> 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) + { + location = QualifiedName::QualifiedNameImpl::create(components.m_prefix, components.m_localName, components.m_namespace).releaseRef(); + } +}; + +static QNameSet* gNameCache; + +void QualifiedName::init(const AtomicString& p, const AtomicString& l, const AtomicString& n) +{ + if (!gNameCache) + gNameCache = new QNameSet; + QualifiedNameComponents components = { p.impl(), l.impl(), n.isEmpty() ? nullAtom.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(const AtomicString& p, const AtomicString& l, const AtomicString& n) +{ + init(p, l, n); +} + +QualifiedName::QualifiedName(const AtomicString& p, const char* l, const AtomicString& n) +{ + init(p, AtomicString(l), n); +} + +QualifiedName::~QualifiedName() +{ + deref(); +} + +void QualifiedName::deref() +{ +#ifdef QNAME_DEFAULT_CONSTRUCTOR + if (!m_impl) + return; +#endif + ASSERT(!isHashTableDeletedValue()); + + if (m_impl->hasOneRef()) + gNameCache->remove(m_impl); + m_impl->deref(); +} + +String QualifiedName::toString() const +{ + String local = localName(); + if (hasPrefix()) { + String result = prefix().string(); + result.append(":"); + result.append(local); + return result; + } + 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; + } +} + +const AtomicString& QualifiedName::localNameUpper() const +{ + if (!m_impl->m_localNameUpper) + m_impl->m_localNameUpper = m_impl->m_localName.upper(); + return m_impl->m_localNameUpper; +} + +} diff --git a/Source/WebCore/dom/QualifiedName.h b/Source/WebCore/dom/QualifiedName.h new file mode 100644 index 0000000..34fc069 --- /dev/null +++ b/Source/WebCore/dom/QualifiedName.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2005, 2006, 2009 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 QualifiedName_h +#define QualifiedName_h + +#include <wtf/HashTraits.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +struct QualifiedNameComponents { + StringImpl* m_prefix; + StringImpl* m_localName; + StringImpl* m_namespace; +}; + +class QualifiedName : public FastAllocBase { +public: + class QualifiedNameImpl : public RefCounted<QualifiedNameImpl> { + public: + static PassRefPtr<QualifiedNameImpl> create(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) + { + return adoptRef(new QualifiedNameImpl(prefix, localName, namespaceURI)); + } + + const AtomicString m_prefix; + const AtomicString m_localName; + const AtomicString m_namespace; + mutable AtomicString m_localNameUpper; + + private: + QualifiedNameImpl(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) + : m_prefix(prefix) + , m_localName(localName) + , m_namespace(namespaceURI) + { + ASSERT(!namespaceURI.isEmpty() || namespaceURI.isNull()); + } + }; + + QualifiedName(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI); + QualifiedName(const AtomicString& prefix, const char* localName, const AtomicString& namespaceURI); + QualifiedName(WTF::HashTableDeletedValueType) : m_impl(hashTableDeletedValue()) { } + bool isHashTableDeletedValue() const { return m_impl == hashTableDeletedValue(); } + ~QualifiedName(); +#ifdef QNAME_DEFAULT_CONSTRUCTOR + QualifiedName() : m_impl(0) { } +#endif + + QualifiedName(const QualifiedName& other) : m_impl(other.m_impl) { ref(); } + const QualifiedName& operator=(const QualifiedName& other) { other.ref(); deref(); m_impl = other.m_impl; return *this; } + + 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) { *this = QualifiedName(prefix, localName(), namespaceURI()); } + + 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; } + + // Uppercased localName, cached for efficiency + const AtomicString& localNameUpper() const; + + String toString() const; + + QualifiedNameImpl* impl() const { return m_impl; } + + // Init routine for globals + static void init(); + +private: + void init(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI); + void ref() const { m_impl->ref(); } + void deref(); + + static QualifiedNameImpl* hashTableDeletedValue() { return RefPtr<QualifiedNameImpl>::hashTableDeletedValue(); } + + 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(); } + +inline unsigned hashComponents(const QualifiedNameComponents& buf) +{ + return WTF::StringHasher::createBlobHash<sizeof(QualifiedNameComponents)>(&buf); +} + +struct QualifiedNameHash { + static unsigned hash(const QualifiedName& name) { return hash(name.impl()); } + + 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& a, const QualifiedName& b) { return a == b; } + static bool equal(const QualifiedName::QualifiedNameImpl* a, const QualifiedName::QualifiedNameImpl* b) { return a == b; } + + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +} + +namespace WTF { + + template<typename T> struct DefaultHash; + + template<> struct DefaultHash<WebCore::QualifiedName> { + typedef WebCore::QualifiedNameHash Hash; + }; + + template<> struct HashTraits<WebCore::QualifiedName> : GenericHashTraits<WebCore::QualifiedName> { + static const bool emptyValueIsZero = false; + static WebCore::QualifiedName emptyValue() { return WebCore::QualifiedName(nullAtom, nullAtom, nullAtom); } + static void constructDeletedValue(WebCore::QualifiedName& slot) { new (&slot) WebCore::QualifiedName(WTF::HashTableDeletedValue); } + static bool isDeletedValue(const WebCore::QualifiedName& slot) { return slot.isHashTableDeletedValue(); } + }; +} + +#endif diff --git a/Source/WebCore/dom/Range.cpp b/Source/WebCore/dom/Range.cpp new file mode 100644 index 0000000..1f15f26 --- /dev/null +++ b/Source/WebCore/dom/Range.cpp @@ -0,0 +1,1997 @@ +/* + * (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, 2008, 2009 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 "RangeException.h" + +#include "ClientRect.h" +#include "ClientRectList.h" +#include "DocumentFragment.h" +#include "FrameView.h" +#include "HTMLElement.h" +#include "NodeWithIndex.h" +#include "ProcessingInstruction.h" +#include "Text.h" +#include "TextIterator.h" +#include "VisiblePosition.h" +#include "htmlediting.h" +#include "markup.h" +#include "visible_units.h" +#include <stdio.h> +#include <wtf/text/CString.h> +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/Vector.h> + +namespace WebCore { + +using namespace std; + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter rangeCounter("Range"); +#endif + +inline Range::Range(PassRefPtr<Document> ownerDocument) + : m_ownerDocument(ownerDocument) + , m_start(m_ownerDocument) + , m_end(m_ownerDocument) +{ +#ifndef NDEBUG + rangeCounter.increment(); +#endif + + m_ownerDocument->attachRange(this); +} + +PassRefPtr<Range> Range::create(PassRefPtr<Document> ownerDocument) +{ + return adoptRef(new Range(ownerDocument)); +} + +inline Range::Range(PassRefPtr<Document> ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset) + : m_ownerDocument(ownerDocument) + , m_start(m_ownerDocument) + , m_end(m_ownerDocument) +{ +#ifndef NDEBUG + rangeCounter.increment(); +#endif + + m_ownerDocument->attachRange(this); + + // Simply setting the containers and offsets directly would not do any of the checking + // that setStart and setEnd do, so we call those functions. + ExceptionCode ec = 0; + setStart(startContainer, startOffset, ec); + ASSERT(!ec); + setEnd(endContainer, endOffset, ec); + ASSERT(!ec); +} + +PassRefPtr<Range> Range::create(PassRefPtr<Document> ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset) +{ + return adoptRef(new Range(ownerDocument, startContainer, startOffset, endContainer, endOffset)); +} + +PassRefPtr<Range> Range::create(PassRefPtr<Document> ownerDocument, const Position& start, const Position& end) +{ + return adoptRef(new Range(ownerDocument, start.containerNode(), start.computeOffsetInContainerNode(), end.containerNode(), end.computeOffsetInContainerNode())); +} + +Range::~Range() +{ + // Always detach (even if we've already detached) to fix https://bugs.webkit.org/show_bug.cgi?id=26044 + m_ownerDocument->detachRange(this); + +#ifndef NDEBUG + rangeCounter.decrement(); +#endif +} + +void Range::setDocument(Document* document) +{ + ASSERT(m_ownerDocument != document); + if (m_ownerDocument) + m_ownerDocument->detachRange(this); + m_ownerDocument = document; + m_start.setToStartOfNode(document); + m_end.setToStartOfNode(document); + m_ownerDocument->attachRange(this); +} + +Node* Range::startContainer(ExceptionCode& ec) const +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + return m_start.container(); +} + +int Range::startOffset(ExceptionCode& ec) const +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + return m_start.offset(); +} + +Node* Range::endContainer(ExceptionCode& ec) const +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + return m_end.container(); +} + +int Range::endOffset(ExceptionCode& ec) const +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + return m_end.offset(); +} + +Node* Range::commonAncestorContainer(ExceptionCode& ec) const +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + return commonAncestorContainer(m_start.container(), m_end.container()); +} + +Node* Range::commonAncestorContainer(Node* containerA, Node* containerB) +{ + for (Node* parentA = containerA; parentA; parentA = parentA->parentNode()) { + for (Node* parentB = containerB; parentB; parentB = parentB->parentNode()) { + if (parentA == parentB) + return parentA; + } + } + return 0; +} + +bool Range::collapsed(ExceptionCode& ec) const +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + return m_start == m_end; +} + +void Range::setStart(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec) +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + ec = 0; + Node* childNode = checkNodeWOffset(refNode.get(), offset, ec); + if (ec) + return; + + m_start.set(refNode, offset, childNode); + + // check if different root container + Node* endRootContainer = m_end.container(); + while (endRootContainer->parentNode()) + endRootContainer = endRootContainer->parentNode(); + Node* startRootContainer = m_start.container(); + while (startRootContainer->parentNode()) + startRootContainer = startRootContainer->parentNode(); + if (startRootContainer != endRootContainer) + collapse(true, ec); + // check if new start after end + else if (compareBoundaryPoints(m_start, m_end) > 0) + collapse(true, ec); +} + +void Range::setEnd(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec) +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return; + } + + if (!refNode) { + ec = NOT_FOUND_ERR; + return; + } + + if (refNode->document() != m_ownerDocument) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + ec = 0; + Node* childNode = checkNodeWOffset(refNode.get(), offset, ec); + if (ec) + return; + + m_end.set(refNode, offset, childNode); + + // check if different root container + Node* endRootContainer = m_end.container(); + while (endRootContainer->parentNode()) + endRootContainer = endRootContainer->parentNode(); + Node* startRootContainer = m_start.container(); + while (startRootContainer->parentNode()) + startRootContainer = startRootContainer->parentNode(); + if (startRootContainer != endRootContainer) + collapse(false, ec); + // check if new end before start + if (compareBoundaryPoints(m_start, m_end) > 0) + collapse(false, ec); +} + +void Range::collapse(bool toStart, ExceptionCode& ec) +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return; + } + + if (toStart) + m_end = m_start; + else + m_start = m_end; +} + +bool Range::isPointInRange(Node* refNode, int offset, ExceptionCode& ec) +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return false; + } + + if (!refNode) { + ec = HIERARCHY_REQUEST_ERR; + return false; + } + + if (!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; + + return compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset()) >= 0 + && compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset()) <= 0; +} + +short Range::comparePoint(Node* refNode, int offset, ExceptionCode& ec) const +{ + // 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 (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + if (!refNode) { + ec = HIERARCHY_REQUEST_ERR; + return 0; + } + + if (!refNode->attached() || 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_start.container(), m_start.offset()) < 0) + return -1; + + // compare to end, and point comes after + if (compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset()) > 0) + 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) const +{ + // 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_start.container() && refNode->attached()) { + ec = INVALID_STATE_ERR; + return NODE_BEFORE; + } + + if (m_start.container() && !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; + } + + ContainerNode* parentNode = refNode->parentNode(); + int 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) < 0) { // starts before + if (comparePoint(parentNode, nodeIndex + 1, ec) > 0) // 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) > 0) // 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_start.container()) { + 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_start, sourceRange->m_start); + case START_TO_END: + return compareBoundaryPoints(m_end, sourceRange->m_start); + case END_TO_END: + return compareBoundaryPoints(m_end, sourceRange->m_end); + case END_TO_START: + return compareBoundaryPoints(m_start, sourceRange->m_end); + } + + ec = SYNTAX_ERR; + return 0; +} + +short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB) +{ + ASSERT(containerA); + ASSERT(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* commonAncestor = commonAncestorContainer(containerA, containerB); + if (!commonAncestor) + return 0; + Node* childA = containerA; + while (childA && childA->parentNode() != commonAncestor) + childA = childA->parentNode(); + if (!childA) + childA = commonAncestor; + Node* childB = containerB; + while (childB && childB->parentNode() != commonAncestor) + childB = childB->parentNode(); + if (!childB) + childB = commonAncestor; + + if (childA == childB) + return 0; // A is equal to B + + Node* n = commonAncestor->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_NOT_REACHED(); + return 0; +} + +short Range::compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB) +{ + return compareBoundaryPoints(boundaryA.container(), boundaryA.offset(), boundaryB.container(), boundaryB.offset()); +} + +bool Range::boundaryPointsValid() const +{ + return m_start.container() && compareBoundaryPoints(m_start, m_end) <= 0; +} + +void Range::deleteContents(ExceptionCode& ec) +{ + 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_start.container() && refNode->attached()) + || (m_start.container() && !refNode->attached()) + || refNode->document() != m_ownerDocument) { + // Firefox doesn't throw an exception for these cases; it returns false. + return false; + } + + ContainerNode* parentNode = refNode->parentNode(); + int 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) < 0 && // starts before start + comparePoint(parentNode, nodeIndex + 1, ec) < 0) { // ends before start + return false; + } else if (comparePoint(parentNode, nodeIndex, ec) > 0 && // starts after end + comparePoint(parentNode, nodeIndex + 1, ec) > 0) { // ends after end + return false; + } + + return true; // all other cases +} + +PassRefPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionCode& ec) +{ + typedef Vector<RefPtr<Node> > NodeVector; + + RefPtr<DocumentFragment> fragment; + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + fragment = DocumentFragment::create(m_ownerDocument.get()); + + ec = 0; + if (collapsed(ec)) + return fragment.release(); + if (ec) + return 0; + + Node* commonRoot = commonAncestorContainer(ec); + if (ec) + return 0; + ASSERT(commonRoot); + + // what is the highest node that partially selects the start of the range? + Node* partialStart = 0; + if (m_start.container() != commonRoot) { + partialStart = m_start.container(); + while (partialStart->parentNode() != commonRoot) + partialStart = partialStart->parentNode(); + } + + // what is the highest node that partially selects the end of the range? + Node* partialEnd = 0; + if (m_end.container() != commonRoot) { + partialEnd = m_end.container(); + while (partialEnd->parentNode() != commonRoot) + partialEnd = partialEnd->parentNode(); + } + + // Simple case: the start and end containers are the same. We just grab + // everything >= start offset and < end offset + if (m_start.container() == m_end.container()) { + Node::NodeType startNodeType = m_start.container()->nodeType(); + if (startNodeType == Node::TEXT_NODE || startNodeType == Node::CDATA_SECTION_NODE || startNodeType == Node::COMMENT_NODE) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_start.container()->cloneNode(true)); + c->deleteData(m_end.offset(), c->length() - m_end.offset(), ec); + c->deleteData(0, m_start.offset(), ec); + fragment->appendChild(c.release(), ec); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast<CharacterData*>(m_start.container())->deleteData(m_start.offset(), m_end.offset() - m_start.offset(), ec); + } else if (startNodeType == Node::PROCESSING_INSTRUCTION_NODE) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_start.container()->cloneNode(true)); + c->setData(c->data().substring(m_start.offset(), m_end.offset() - m_start.offset()), ec); + fragment->appendChild(c.release(), ec); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(m_start.container()); + String data(pi->data()); + data.remove(m_start.offset(), m_end.offset() - m_start.offset()); + pi->setData(data, ec); + } + } else { + RefPtr<Node> n = m_start.container()->firstChild(); + int i; + for (i = 0; n && i < m_start.offset(); i++) // skip until start offset + n = n->nextSibling(); + int endOffset = m_end.offset(); + RefPtr<Node> next; + for (; n && i < endOffset; n = next, i++) { // delete until end offset + next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + fragment->appendChild(n, ec); // will remove n from its parent + else if (action == CLONE_CONTENTS) + fragment->appendChild(n->cloneNode(true), ec); + else + toContainerNode(m_start.container())->removeChild(n.get(), ec); + } + } + return fragment.release(); + } + + // Complex case: Start and end containers are different. + // There are three possibilities here: + // 1. Start container == commonRoot (End container must be a descendant) + // 2. End container == commonRoot (Start container must be a descendant) + // 3. Neither is commonRoot, they are both descendants + // + // In case 3, we grab everything after the start (up until a direct child + // of commonRoot) into leftContents, and everything before the end (up until + // a direct child of commonRoot) into rightContents. Then we process all + // commonRoot 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 commonRoot. + // + // These are deleted, cloned, or extracted (i.e. both) depending on action. + + RefPtr<Node> leftContents; + if (m_start.container() != commonRoot) { + // process the left-hand side of the range, up until the last ancestor of + // start container before commonRoot + Node::NodeType startNodeType = m_start.container()->nodeType(); + if (startNodeType == Node::TEXT_NODE || startNodeType == Node::CDATA_SECTION_NODE || startNodeType == Node::COMMENT_NODE) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_start.container()->cloneNode(true)); + c->deleteData(0, m_start.offset(), ec); + leftContents = c.release(); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast<CharacterData*>(m_start.container())->deleteData( + m_start.offset(), static_cast<CharacterData*>(m_start.container())->length() - m_start.offset(), ec); + } else if (startNodeType == Node::PROCESSING_INSTRUCTION_NODE) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_start.container()->cloneNode(true)); + c->setData(c->data().substring(m_start.offset()), ec); + leftContents = c.release(); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(m_start.container()); + String data(pi->data()); + pi->setData(data.left(m_start.offset()), ec); + } + } else { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + leftContents = m_start.container()->cloneNode(false); + NodeVector nodes; + Node* n = m_start.container()->firstChild(); + for (int i = 0; n; n = n->nextSibling(), i++) { + if (i < m_start.offset()) + continue; // Skip until start offset. + nodes.append(n); + } + for (NodeVector::const_iterator it = nodes.begin(); it != nodes.end(); it++) { + Node* n = it->get(); + if (action == EXTRACT_CONTENTS) + leftContents->appendChild(n, ec); // Will remove n from start container. + else if (action == CLONE_CONTENTS) + leftContents->appendChild(n->cloneNode(true), ec); + else + toContainerNode(m_start.container())->removeChild(n, ec); + } + } + + NodeVector ancestorNodes; + for (ContainerNode* n = m_start.container()->parentNode(); n && n != commonRoot; n = n->parentNode()) + ancestorNodes.append(n); + RefPtr<Node> n = m_start.container()->nextSibling(); + for (NodeVector::const_iterator it = ancestorNodes.begin(); it != ancestorNodes.end(); it++) { + Node* leftParent = it->get(); + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<Node> leftContentsParent = leftParent->cloneNode(false); + if (leftContentsParent) { // Might have been removed already during mutation event. + leftContentsParent->appendChild(leftContents, ec); + leftContents = leftContentsParent; + } + } + + RefPtr<Node> next; + for (; n; n = next) { + next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + leftContents->appendChild(n.get(), ec); // will remove n from leftParent + else if (action == CLONE_CONTENTS) + leftContents->appendChild(n->cloneNode(true), ec); + else + leftParent->removeChild(n.get(), ec); + } + n = leftParent->nextSibling(); + } + } + + RefPtr<Node> rightContents; + if (m_end.container() != commonRoot) { + // delete the right-hand side of the range, up until the last ancestor of + // end container before commonRoot + Node::NodeType endNodeType = m_end.container()->nodeType(); + if (endNodeType == Node::TEXT_NODE || endNodeType == Node::CDATA_SECTION_NODE || endNodeType == Node::COMMENT_NODE) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_end.container()->cloneNode(true)); + c->deleteData(m_end.offset(), static_cast<CharacterData*>(m_end.container())->length() - m_end.offset(), ec); + rightContents = c; + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast<CharacterData*>(m_end.container())->deleteData(0, m_end.offset(), ec); + } else if (endNodeType == Node::PROCESSING_INSTRUCTION_NODE) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_end.container()->cloneNode(true)); + c->setData(c->data().left(m_end.offset()), ec); + rightContents = c.release(); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(m_end.container()); + pi->setData(pi->data().substring(m_end.offset()), ec); + } + } else { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + rightContents = m_end.container()->cloneNode(false); + Node* n = m_end.container()->firstChild(); + if (n && m_end.offset()) { + NodeVector nodes; + int i = 0; + do { + nodes.append(n); + if (!n->nextSibling()) + break; + n = n->nextSibling(); + } while (i + 1 < m_end.offset()); + for (int i = nodes.size() - 1; i >= 0; i--) { + n = nodes[i].get(); + if (action == EXTRACT_CONTENTS) + rightContents->insertBefore(n, rightContents->firstChild(), ec); // will remove n from its parent + else if (action == CLONE_CONTENTS) + rightContents->insertBefore(n->cloneNode(true), rightContents->firstChild(), ec); + else + toContainerNode(m_end.container())->removeChild(n, ec); + } + } + } + + ContainerNode* rightParent = m_end.container()->parentNode(); + Node* n = m_end.container()->previousSibling(); + for (; rightParent != commonRoot; 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 its 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 commonRoot between the start and end container + + Node* processStart; // child of commonRoot + if (m_start.container() == commonRoot) { + processStart = m_start.container()->firstChild(); + for (int i = 0; i < m_start.offset(); i++) + processStart = processStart->nextSibling(); + } else { + processStart = m_start.container(); + while (processStart->parentNode() != commonRoot) + processStart = processStart->parentNode(); + processStart = processStart->nextSibling(); + } + Node* processEnd; // child of commonRoot + if (m_end.container() == commonRoot) { + processEnd = m_end.container()->firstChild(); + for (int i = 0; i < m_end.offset(); i++) + processEnd = processEnd->nextSibling(); + } else { + processEnd = m_end.container(); + while (processEnd->parentNode() != commonRoot) + processEnd = processEnd->parentNode(); + } + + // Collapse the range, making sure that the result is not within a node that was partially selected. + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + if (partialStart) + setStart(partialStart->parentNode(), partialStart->nodeIndex() + 1, ec); + else if (partialEnd) + setStart(partialEnd->parentNode(), partialEnd->nodeIndex(), ec); + if (ec) + return 0; + m_end = m_start; + } + + // 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); + + if (processStart) { + NodeVector nodes; + for (Node* n = processStart; n && n != processEnd; n = n->nextSibling()) + nodes.append(n); + for (NodeVector::const_iterator it = nodes.begin(); it != nodes.end(); it++) { + Node* n = it->get(); + if (action == EXTRACT_CONTENTS) + fragment->appendChild(n, ec); // will remove from commonRoot + else if (action == CLONE_CONTENTS) + fragment->appendChild(n->cloneNode(true), ec); + else + commonRoot->removeChild(n, ec); + } + } + + if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents) + fragment->appendChild(rightContents, ec); + + return fragment.release(); +} + +PassRefPtr<DocumentFragment> Range::extractContents(ExceptionCode& ec) +{ + checkDeleteExtract(ec); + if (ec) + return 0; + + return processContents(EXTRACT_CONTENTS, ec); +} + +PassRefPtr<DocumentFragment> Range::cloneContents(ExceptionCode& ec) +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + return processContents(CLONE_CONTENTS, ec); +} + +void Range::insertNode(PassRefPtr<Node> prpNewNode, ExceptionCode& ec) +{ + RefPtr<Node> newNode = prpNewNode; + + ec = 0; + + if (!m_start.container()) { + 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_start.container()->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 + bool startIsText = m_start.container()->isTextNode(); + if (startIsText && !m_start.container()->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 (startIsText) + checkAgainst = m_start.container()->parentNode(); + else + checkAgainst = m_start.container(); + + Node::NodeType newNodeType = newNode->nodeType(); + int numNewChildren; + if (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) { + // check each child node, not the DocumentFragment itself + numNewChildren = 0; + for (Node* c = newNode->firstChild(); c; c = c->nextSibling()) { + if (!checkAgainst->childTypeAllowed(c->nodeType())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + ++numNewChildren; + } + } else { + numNewChildren = 1; + if (!checkAgainst->childTypeAllowed(newNodeType)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + + for (Node* n = m_start.container(); 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 (newNodeType == Node::ATTRIBUTE_NODE || newNodeType == Node::ENTITY_NODE + || newNodeType == Node::NOTATION_NODE || newNodeType == Node::DOCUMENT_NODE) { + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } + + bool collapsed = m_start == m_end; + if (startIsText) { + RefPtr<Text> newText = static_cast<Text*>(m_start.container())->splitText(m_start.offset(), ec); + if (ec) + return; + m_start.container()->parentNode()->insertBefore(newNode.release(), newText.get(), ec); + if (ec) + return; + + // This special case doesn't seem to match the DOM specification, but it's currently required + // to pass Acid3. We might later decide to remove this. + if (collapsed) + m_end.setToBeforeChild(newText.get()); + } else { + RefPtr<Node> lastChild; + if (collapsed) + lastChild = (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) ? newNode->lastChild() : newNode; + + int startOffset = m_start.offset(); + m_start.container()->insertBefore(newNode.release(), m_start.container()->childNode(startOffset), ec); + if (ec) + return; + + // This special case doesn't seem to match the DOM specification, but it's currently required + // to pass Acid3. We might later decide to remove this. + if (collapsed) + m_end.set(m_start.container(), startOffset + numNewChildren, lastChild.get()); + } +} + +String Range::toString(ExceptionCode& ec) const +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return String(); + } + + Vector<UChar> result; + + Node* pastLast = pastLastNode(); + for (Node* n = firstNode(); n != pastLast; n = n->traverseNextNode()) { + if (n->nodeType() == Node::TEXT_NODE || n->nodeType() == Node::CDATA_SECTION_NODE) { + String data = static_cast<CharacterData*>(n)->data(); + int length = data.length(); + int start = (n == m_start.container()) ? min(max(0, m_start.offset()), length) : 0; + int end = (n == m_end.container()) ? min(max(start, m_end.offset()), 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_start.container()) + 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_start.container()->document()->updateLayout(); + + return plainText(this); +} + +PassRefPtr<DocumentFragment> Range::createContextualFragment(const String& markup, ExceptionCode& ec) const +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + Node* element = m_start.container()->isElementNode() ? m_start.container() : m_start.container()->parentNode(); + if (!element || !element->isHTMLElement()) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + // Logic from deprecatedCreateContextualFragment should just be moved into + // this function. Range::createContextualFragment semantics do not make + // sense for the rest of the DOM implementation to use. + RefPtr<DocumentFragment> fragment = static_cast<HTMLElement*>(element)->deprecatedCreateContextualFragment(markup); + if (!fragment) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + return fragment.release(); +} + + +void Range::detach(ExceptionCode& ec) +{ + // Check first to see if we've already detached: + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return; + } + + m_ownerDocument->detachRange(this); + + m_start.clear(); + m_end.clear(); +} + +Node* 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 0; + 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 0; + case Node::PROCESSING_INSTRUCTION_NODE: + if (static_cast<unsigned>(offset) > static_cast<ProcessingInstruction*>(n)->data().length()) + ec = INDEX_SIZE_ERR; + return 0; + 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 (!offset) + return 0; + Node* childBefore = n->childNode(offset - 1); + if (!childBefore) + ec = INDEX_SIZE_ERR; + return childBefore; + } + } + ASSERT_NOT_REACHED(); + return 0; +} + +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. + + switch (n->nodeType()) { + case Node::ATTRIBUTE_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_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::DOCUMENT_TYPE_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; + } + + Node* root = n; + while (ContainerNode* parent = root->parentNode()) + root = parent; + + switch (root->nodeType()) { + case Node::ATTRIBUTE_NODE: + case Node::DOCUMENT_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + break; + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + case Node::DOCUMENT_TYPE_NODE: + case Node::ELEMENT_NODE: + case Node::ENTITY_NODE: + case Node::ENTITY_REFERENCE_NODE: + case Node::NOTATION_NODE: + case Node::PROCESSING_INSTRUCTION_NODE: + case Node::TEXT_NODE: + case Node::XPATH_NAMESPACE_NODE: + if (root->isShadowRoot()) + break; + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } +} + +PassRefPtr<Range> Range::cloneRange(ExceptionCode& ec) const +{ + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return 0; + } + + return Range::create(m_ownerDocument, m_start.container(), m_start.offset(), m_end.container(), m_end.offset()); +} + +void Range::setStartAfter(Node* refNode, ExceptionCode& ec) +{ + if (!m_start.container()) { + 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_start.container()) { + 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_start.container()) { + 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_start.container()) { + 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. + for (ContainerNode* anc = refNode->parentNode(); anc; anc = anc->parentNode()) { + switch (anc->nodeType()) { + case Node::ATTRIBUTE_NODE: + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_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; + case Node::DOCUMENT_TYPE_NODE: + case Node::ENTITY_NODE: + case Node::NOTATION_NODE: + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } + } + + switch (refNode->nodeType()) { + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + case Node::DOCUMENT_TYPE_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; + case Node::ATTRIBUTE_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_NODE: + case Node::ENTITY_NODE: + case Node::NOTATION_NODE: + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } + + if (m_ownerDocument != refNode->document()) + setDocument(refNode->document()); + + ec = 0; + setStartBefore(refNode, ec); + if (ec) + return; + setEndAfter(refNode, ec); +} + +void Range::selectNodeContents(Node* refNode, ExceptionCode& ec) +{ + if (!m_start.container()) { + 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. + for (Node* n = refNode; n; n = n->parentNode()) { + switch (n->nodeType()) { + case Node::ATTRIBUTE_NODE: + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_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; + case Node::DOCUMENT_TYPE_NODE: + case Node::ENTITY_NODE: + case Node::NOTATION_NODE: + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } + } + + if (m_ownerDocument != refNode->document()) + setDocument(refNode->document()); + + m_start.setToStartOfNode(refNode); + m_end.setToEndOfNode(refNode); +} + +void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) +{ + RefPtr<Node> newParent = passNewParent; + + if (!m_start.container()) { + 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_start.container()->document()) { + ec = WRONG_DOCUMENT_ERR; + return; + } + + // Raise a HIERARCHY_REQUEST_ERR if m_start.container() doesn't accept children like newParent. + Node* parentOfNewParent = m_start.container(); + + // If m_start.container() is a character data node, it will be split and it will be its parent that will + // need to accept newParent (or in the case of a comment, it logically "would" be inserted into the parent, + // although this will fail below for another reason). + if (parentOfNewParent->isCharacterDataNode()) + parentOfNewParent = parentOfNewParent->parentNode(); + if (!parentOfNewParent || !parentOfNewParent->childTypeAllowed(newParent->nodeType())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + if (m_start.container() == newParent || m_start.container()->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? + + // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-Text node. + Node* startNonTextContainer = m_start.container(); + if (startNonTextContainer->nodeType() == Node::TEXT_NODE) + startNonTextContainer = startNonTextContainer->parentNode(); + Node* endNonTextContainer = m_end.container(); + if (endNonTextContainer->nodeType() == Node::TEXT_NODE) + endNonTextContainer = endNonTextContainer->parentNode(); + if (startNonTextContainer != endNonTextContainer) { + ec = RangeException::BAD_BOUNDARYPOINTS_ERR; + return; + } + + ec = 0; + while (Node* n = newParent->firstChild()) { + toContainerNode(newParent.get())->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_start.container()) { + 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 (!m_start.container()) { + ec = INVALID_STATE_ERR; + return; + } + + ec = 0; + if (!commonAncestorContainer(ec) || ec) + return; + + Node* pastLast = pastLastNode(); + for (Node* n = firstNode(); n != pastLast; n = n->traverseNextNode()) { + if (n->isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + + if (containedByReadOnly()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } +} + +bool Range::containedByReadOnly() const +{ + for (Node* n = m_start.container(); n; n = n->parentNode()) { + if (n->isReadOnlyNode()) + return true; + } + for (Node* n = m_end.container(); n; n = n->parentNode()) { + if (n->isReadOnlyNode()) + return true; + } + return false; +} + +Node* Range::firstNode() const +{ + if (!m_start.container()) + return 0; + if (m_start.container()->offsetInCharacters()) + return m_start.container(); + if (Node* child = m_start.container()->childNode(m_start.offset())) + return child; + if (!m_start.offset()) + return m_start.container(); + return m_start.container()->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_start.container(), m_start.offset(), 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::shadowTreeRootNode() const +{ + return startContainer() ? startContainer()->shadowTreeRootNode() : 0; +} + +Node* Range::pastLastNode() const +{ + if (!m_start.container() || !m_end.container()) + return 0; + if (m_end.container()->offsetInCharacters()) + return m_end.container()->traverseNextSibling(); + if (Node* child = m_end.container()->childNode(m_end.offset())) + return child; + return m_end.container()->traverseNextSibling(); +} + +IntRect Range::boundingBox() +{ + IntRect result; + Vector<IntRect> rects; + textRects(rects); + const size_t n = rects.size(); + for (size_t i = 0; i < n; ++i) + result.unite(rects[i]); + return result; +} + +void Range::textRects(Vector<IntRect>& rects, bool useSelectionHeight) +{ + Node* startContainer = m_start.container(); + Node* endContainer = m_end.container(); + + if (!startContainer || !endContainer) + return; + + Node* stopNode = pastLastNode(); + for (Node* node = firstNode(); node != stopNode; node = node->traverseNextNode()) { + RenderObject* r = node->renderer(); + if (!r || !r->isText()) + continue; + RenderText* renderText = toRenderText(r); + int startOffset = node == startContainer ? m_start.offset() : 0; + int endOffset = node == endContainer ? m_end.offset() : numeric_limits<int>::max(); + renderText->absoluteRectsForRange(rects, startOffset, endOffset, useSelectionHeight); + } +} + +void Range::textQuads(Vector<FloatQuad>& quads, bool useSelectionHeight) +{ + Node* startContainer = m_start.container(); + Node* endContainer = m_end.container(); + + if (!startContainer || !endContainer) + return; + + Node* stopNode = pastLastNode(); + for (Node* node = firstNode(); node != stopNode; node = node->traverseNextNode()) { + RenderObject* r = node->renderer(); + if (!r || !r->isText()) + continue; + RenderText* renderText = toRenderText(r); + int startOffset = node == startContainer ? m_start.offset() : 0; + int endOffset = node == endContainer ? m_end.offset() : numeric_limits<int>::max(); + renderText->absoluteQuadsForRange(quads, startOffset, endOffset, useSelectionHeight); + } +} + +#ifndef NDEBUG +#define FormatBufferSize 1024 +void Range::formatForDebugger(char* buffer, unsigned length) const +{ + String result; + String s; + + if (!m_start.container() || !m_end.container()) + result = "<empty>"; + else { + char s[FormatBufferSize]; + result += "from offset "; + result += String::number(m_start.offset()); + result += " of "; + m_start.container()->formatForDebugger(s, FormatBufferSize); + result += s; + result += " to offset "; + result += String::number(m_end.offset()); + result += " of "; + m_end.container()->formatForDebugger(s, FormatBufferSize); + result += s; + } + + strncpy(buffer, result.utf8().data(), length - 1); +} +#undef FormatBufferSize +#endif + +bool areRangesEqual(const Range* a, const Range* b) +{ + if (a == b) + return true; + if (!a || !b) + return false; + return a->startPosition() == b->startPosition() && a->endPosition() == b->endPosition(); +} + +PassRefPtr<Range> rangeOfContents(Node* node) +{ + ASSERT(node); + RefPtr<Range> range = Range::create(node->document()); + int exception = 0; + range->selectNodeContents(node, exception); + return range.release(); +} + +int Range::maxStartOffset() const +{ + if (!m_start.container()) + return 0; + if (!m_start.container()->offsetInCharacters()) + return m_start.container()->childNodeCount(); + return m_start.container()->maxCharacterOffset(); +} + +int Range::maxEndOffset() const +{ + if (!m_end.container()) + return 0; + if (!m_end.container()->offsetInCharacters()) + return m_end.container()->childNodeCount(); + return m_end.container()->maxCharacterOffset(); +} + +static inline void boundaryNodeChildrenChanged(RangeBoundaryPoint& boundary, ContainerNode* container) +{ + if (!boundary.childBefore()) + return; + if (boundary.container() != container) + return; + boundary.invalidateOffset(); +} + +void Range::nodeChildrenChanged(ContainerNode* container) +{ + ASSERT(container); + ASSERT(container->document() == m_ownerDocument); + boundaryNodeChildrenChanged(m_start, container); + boundaryNodeChildrenChanged(m_end, container); +} + +static inline void boundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint& boundary, ContainerNode* container) +{ + for (Node* nodeToBeRemoved = container->firstChild(); nodeToBeRemoved; nodeToBeRemoved = nodeToBeRemoved->nextSibling()) { + if (boundary.childBefore() == nodeToBeRemoved) { + boundary.setToStartOfNode(container); + return; + } + + for (Node* n = boundary.container(); n; n = n->parentNode()) { + if (n == nodeToBeRemoved) { + boundary.setToStartOfNode(container); + return; + } + } + } +} + +void Range::nodeChildrenWillBeRemoved(ContainerNode* container) +{ + ASSERT(container); + ASSERT(container->document() == m_ownerDocument); + boundaryNodeChildrenWillBeRemoved(m_start, container); + boundaryNodeChildrenWillBeRemoved(m_end, container); +} + +static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node* nodeToBeRemoved) +{ + if (boundary.childBefore() == nodeToBeRemoved) { + boundary.childBeforeWillBeRemoved(); + return; + } + + for (Node* n = boundary.container(); n; n = n->parentNode()) { + if (n == nodeToBeRemoved) { + boundary.setToBeforeChild(nodeToBeRemoved); + return; + } + } +} + +void Range::nodeWillBeRemoved(Node* node) +{ + ASSERT(node); + ASSERT(node->document() == m_ownerDocument); + ASSERT(node != m_ownerDocument); + ASSERT(node->parentNode()); + boundaryNodeWillBeRemoved(m_start, node); + boundaryNodeWillBeRemoved(m_end, node); +} + +static inline void boundaryTextInserted(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length) +{ + if (boundary.container() != text) + return; + unsigned boundaryOffset = boundary.offset(); + if (offset >= boundaryOffset) + return; + boundary.setOffset(boundaryOffset + length); +} + +void Range::textInserted(Node* text, unsigned offset, unsigned length) +{ + ASSERT(text); + ASSERT(text->document() == m_ownerDocument); + boundaryTextInserted(m_start, text, offset, length); + boundaryTextInserted(m_end, text, offset, length); +} + +static inline void boundaryTextRemoved(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length) +{ + if (boundary.container() != text) + return; + unsigned boundaryOffset = boundary.offset(); + if (offset >= boundaryOffset) + return; + if (offset + length >= boundaryOffset) + boundary.setOffset(offset); + else + boundary.setOffset(boundaryOffset - length); +} + +void Range::textRemoved(Node* text, unsigned offset, unsigned length) +{ + ASSERT(text); + ASSERT(text->document() == m_ownerDocument); + boundaryTextRemoved(m_start, text, offset, length); + boundaryTextRemoved(m_end, text, offset, length); +} + +static inline void boundaryTextNodesMerged(RangeBoundaryPoint& boundary, NodeWithIndex& oldNode, unsigned offset) +{ + if (boundary.container() == oldNode.node()) + boundary.set(oldNode.node()->previousSibling(), boundary.offset() + offset, 0); + else if (boundary.container() == oldNode.node()->parentNode() && boundary.offset() == oldNode.index()) + boundary.set(oldNode.node()->previousSibling(), offset, 0); +} + +void Range::textNodesMerged(NodeWithIndex& oldNode, unsigned offset) +{ + ASSERT(oldNode.node()); + ASSERT(oldNode.node()->document() == m_ownerDocument); + ASSERT(oldNode.node()->parentNode()); + ASSERT(oldNode.node()->isTextNode()); + ASSERT(oldNode.node()->previousSibling()); + ASSERT(oldNode.node()->previousSibling()->isTextNode()); + boundaryTextNodesMerged(m_start, oldNode, offset); + boundaryTextNodesMerged(m_end, oldNode, offset); +} + +static inline void boundaryTextNodesSplit(RangeBoundaryPoint& boundary, Text* oldNode) +{ + if (boundary.container() != oldNode) + return; + unsigned boundaryOffset = boundary.offset(); + if (boundaryOffset <= oldNode->length()) + return; + boundary.set(oldNode->nextSibling(), boundaryOffset - oldNode->length(), 0); +} + +void Range::textNodeSplit(Text* oldNode) +{ + ASSERT(oldNode); + ASSERT(oldNode->document() == m_ownerDocument); + ASSERT(oldNode->parentNode()); + ASSERT(oldNode->isTextNode()); + ASSERT(oldNode->nextSibling()); + ASSERT(oldNode->nextSibling()->isTextNode()); + boundaryTextNodesSplit(m_start, oldNode); + boundaryTextNodesSplit(m_end, oldNode); +} + +void Range::expand(const String& unit, ExceptionCode& ec) +{ + VisiblePosition start(startPosition()); + VisiblePosition end(endPosition()); + if (unit == "word") { + start = startOfWord(start); + end = endOfWord(end); + } else if (unit == "sentence") { + start = startOfSentence(start); + end = endOfSentence(end); + } else if (unit == "block") { + start = startOfParagraph(start); + end = endOfParagraph(end); + } else if (unit == "document") { + start = startOfDocument(start); + end = endOfDocument(end); + } else + return; + setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), ec); + setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), ec); +} + +PassRefPtr<ClientRectList> Range::getClientRects() const +{ + if (!m_start.container()) + return 0; + + m_ownerDocument->updateLayoutIgnorePendingStylesheets(); + + Vector<FloatQuad> quads; + getBorderAndTextQuads(quads); + + return ClientRectList::create(quads); +} + +PassRefPtr<ClientRect> Range::getBoundingClientRect() const +{ + if (!m_start.container()) + return 0; + + m_ownerDocument->updateLayoutIgnorePendingStylesheets(); + + Vector<FloatQuad> quads; + getBorderAndTextQuads(quads); + + if (quads.isEmpty()) + return ClientRect::create(); + + FloatRect result; + for (size_t i = 0; i < quads.size(); ++i) + result.unite(quads[i].boundingBox()); + + return ClientRect::create(result); +} + +static void adjustFloatQuadsForScrollAndAbsoluteZoom(Vector<FloatQuad>& quads, Document* document, RenderObject* renderer) +{ + FrameView* view = document->view(); + if (!view) + return; + + IntRect visibleContentRect = view->visibleContentRect(); + for (size_t i = 0; i < quads.size(); ++i) { + quads[i].move(-visibleContentRect.x(), -visibleContentRect.y()); + adjustFloatQuadForAbsoluteZoom(quads[i], renderer); + } +} + +void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const +{ + Node* startContainer = m_start.container(); + Node* endContainer = m_end.container(); + Node* stopNode = pastLastNode(); + + HashSet<Node*> nodeSet; + for (Node* node = firstNode(); node != stopNode; node = node->traverseNextNode()) { + if (node->isElementNode()) + nodeSet.add(node); + } + + for (Node* node = firstNode(); node != stopNode; node = node->traverseNextNode()) { + if (node->isElementNode()) { + if (!nodeSet.contains(node->parentNode())) { + if (RenderBoxModelObject* renderBoxModelObject = static_cast<Element*>(node)->renderBoxModelObject()) { + Vector<FloatQuad> elementQuads; + renderBoxModelObject->absoluteQuads(elementQuads); + adjustFloatQuadsForScrollAndAbsoluteZoom(elementQuads, m_ownerDocument.get(), renderBoxModelObject); + + quads.append(elementQuads); + } + } + } else if (node->isTextNode()) { + if (RenderObject* renderer = static_cast<Text*>(node)->renderer()) { + RenderText* renderText = toRenderText(renderer); + int startOffset = (node == startContainer) ? m_start.offset() : 0; + int endOffset = (node == endContainer) ? m_end.offset() : INT_MAX; + + Vector<FloatQuad> textQuads; + renderText->absoluteQuadsForRange(textQuads, startOffset, endOffset); + adjustFloatQuadsForScrollAndAbsoluteZoom(textQuads, m_ownerDocument.get(), renderText); + + quads.append(textQuads); + } + } + } +} + +} // namespace WebCore + +#ifndef NDEBUG + +void showTree(const WebCore::Range* range) +{ + if (range && range->boundaryPointsValid()) { + WebCore::Position start = range->startPosition(); + WebCore::Position end = range->endPosition(); + start.node()->showTreeAndMark(start.node(), "S", end.node(), "E"); + fprintf(stderr, "start offset: %d, end offset: %d\n", start.deprecatedEditingOffset(), end.deprecatedEditingOffset()); + } +} + +#endif diff --git a/Source/WebCore/dom/Range.h b/Source/WebCore/dom/Range.h new file mode 100644 index 0000000..aea76e1 --- /dev/null +++ b/Source/WebCore/dom/Range.h @@ -0,0 +1,167 @@ +/* + * (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, 2008, 2009 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 Range_h +#define Range_h + +#include "FloatQuad.h" +#include "RangeBoundaryPoint.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class ClientRect; +class ClientRectList; +class DocumentFragment; +class NodeWithIndex; +class Text; + +class Range : public RefCounted<Range> { +public: + static PassRefPtr<Range> create(PassRefPtr<Document>); + static PassRefPtr<Range> create(PassRefPtr<Document>, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset); + static PassRefPtr<Range> create(PassRefPtr<Document>, const Position&, const Position&); + ~Range(); + + Document* ownerDocument() const { return m_ownerDocument.get(); } + Node* startContainer() const { return m_start.container(); } + int startOffset() const { return m_start.offset(); } + Node* endContainer() const { return m_end.container(); } + int endOffset() const { return m_end.offset(); } + + 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(PassRefPtr<Node> container, int offset, ExceptionCode&); + void setEnd(PassRefPtr<Node> container, int offset, ExceptionCode&); + void collapse(bool toStart, ExceptionCode&); + bool isPointInRange(Node* refNode, int offset, ExceptionCode&); + short comparePoint(Node* refNode, int offset, ExceptionCode&) const; + enum CompareResults { NODE_BEFORE, NODE_AFTER, NODE_BEFORE_AND_AFTER, NODE_INSIDE }; + CompareResults compareNode(Node* refNode, ExceptionCode&) const; + 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 RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB); + 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&); + 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&); + + const Position startPosition() const { return m_start.toPosition(); } + const Position endPosition() const { return m_end.toPosition(); } + + Node* firstNode() const; + Node* pastLastNode() const; + + Position editingStartPosition() const; + + Node* shadowTreeRootNode() const; + + IntRect boundingBox(); + // Not transform-friendly + void textRects(Vector<IntRect>&, bool useSelectionHeight = false); + // Transform-friendly + void textQuads(Vector<FloatQuad>&, bool useSelectionHeight = false); + void getBorderAndTextQuads(Vector<FloatQuad>&) const; + + void nodeChildrenChanged(ContainerNode*); + void nodeChildrenWillBeRemoved(ContainerNode*); + void nodeWillBeRemoved(Node*); + + void textInserted(Node*, unsigned offset, unsigned length); + void textRemoved(Node*, unsigned offset, unsigned length); + void textNodesMerged(NodeWithIndex& oldNode, unsigned offset); + void textNodeSplit(Text* oldNode); + + // Expand range to a unit (word or sentence or block or document) boundary. + // Please refer to https://bugs.webkit.org/show_bug.cgi?id=27632 comment #5 + // for details. + void expand(const String&, ExceptionCode&); + + PassRefPtr<ClientRectList> getClientRects() const; + PassRefPtr<ClientRect> getBoundingClientRect() const; + +#ifndef NDEBUG + void formatForDebugger(char* buffer, unsigned length) const; +#endif + +private: + Range(PassRefPtr<Document>); + Range(PassRefPtr<Document>, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset); + + void setDocument(Document*); + + Node* checkNodeWOffset(Node*, int offset, ExceptionCode&) const; + void checkNodeBA(Node*, ExceptionCode&) const; + void checkDeleteExtract(ExceptionCode&); + bool containedByReadOnly() const; + int maxStartOffset() const; + int maxEndOffset() const; + + enum ActionType { DELETE_CONTENTS, EXTRACT_CONTENTS, CLONE_CONTENTS }; + PassRefPtr<DocumentFragment> processContents(ActionType, ExceptionCode&); + + RefPtr<Document> m_ownerDocument; + RangeBoundaryPoint m_start; + RangeBoundaryPoint m_end; +}; + +PassRefPtr<Range> rangeOfContents(Node*); + +bool areRangesEqual(const Range*, const Range*); + +} // namespace + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::Range*); +#endif + +#endif diff --git a/Source/WebCore/dom/Range.idl b/Source/WebCore/dom/Range.idl new file mode 100644 index 0000000..cd01d61 --- /dev/null +++ b/Source/WebCore/dom/Range.idl @@ -0,0 +1,130 @@ +/* + * 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 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); + +#if defined(LANGUAGE_JAVASCRIPT) || LANGUAGE_JAVASCRIPT + // CSSOM View Module API extensions + + ClientRectList getClientRects(); + ClientRect getBoundingClientRect(); +#endif + + // 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); + + void expand(in DOMString unit) + raises(RangeException, DOMException); + +#if !defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT + readonly attribute DOMString text; +#endif + }; + +} diff --git a/Source/WebCore/dom/RangeBoundaryPoint.h b/Source/WebCore/dom/RangeBoundaryPoint.h new file mode 100644 index 0000000..1bbbe1a --- /dev/null +++ b/Source/WebCore/dom/RangeBoundaryPoint.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) 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 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 RangeBoundaryPoint_h +#define RangeBoundaryPoint_h + +#include "Node.h" +#include "Position.h" + +namespace WebCore { + +class RangeBoundaryPoint { +public: + explicit RangeBoundaryPoint(PassRefPtr<Node> container); + + const Position toPosition() const; + + Node* container() const; + int offset() const; + Node* childBefore() const; + + void clear(); + + void set(PassRefPtr<Node> container, int offset, Node* childBefore); + void setOffset(int offset); + + void setToBeforeChild(Node*); + void setToStartOfNode(PassRefPtr<Node>); + void setToEndOfNode(PassRefPtr<Node>); + + void childBeforeWillBeRemoved(); + void invalidateOffset() const; + void ensureOffsetIsValid() const; + +private: + static const int invalidOffset = -1; + + RefPtr<Node> m_containerNode; + mutable int m_offsetInContainer; + Node* m_childBeforeBoundary; +}; + +inline RangeBoundaryPoint::RangeBoundaryPoint(PassRefPtr<Node> container) + : m_containerNode(container) + , m_offsetInContainer(0) + , m_childBeforeBoundary(0) +{ + ASSERT(m_containerNode); +} + +inline Node* RangeBoundaryPoint::container() const +{ + return m_containerNode.get(); +} + +inline Node* RangeBoundaryPoint::childBefore() const +{ + return m_childBeforeBoundary; +} + +inline void RangeBoundaryPoint::ensureOffsetIsValid() const +{ + if (m_offsetInContainer >= 0) + return; + + ASSERT(m_childBeforeBoundary); + m_offsetInContainer = m_childBeforeBoundary->nodeIndex() + 1; +} + +inline const Position RangeBoundaryPoint::toPosition() const +{ + ensureOffsetIsValid(); + return Position(m_containerNode.get(), m_offsetInContainer); +} + +inline int RangeBoundaryPoint::offset() const +{ + ensureOffsetIsValid(); + return m_offsetInContainer; +} + +inline void RangeBoundaryPoint::clear() +{ + m_containerNode.clear(); + m_offsetInContainer = 0; + m_childBeforeBoundary = 0; +} + +inline void RangeBoundaryPoint::set(PassRefPtr<Node> container, int offset, Node* childBefore) +{ + ASSERT(container); + ASSERT(offset >= 0); + ASSERT(childBefore == (offset ? container->childNode(offset - 1) : 0)); + m_containerNode = container; + m_offsetInContainer = offset; + m_childBeforeBoundary = childBefore; +} + +inline void RangeBoundaryPoint::setOffset(int offset) +{ + ASSERT(m_containerNode); + ASSERT(m_containerNode->offsetInCharacters()); + ASSERT(m_offsetInContainer >= 0); + ASSERT(!m_childBeforeBoundary); + m_offsetInContainer = offset; +} + +inline void RangeBoundaryPoint::setToBeforeChild(Node* child) +{ + ASSERT(child); + ASSERT(child->parentNode()); + m_childBeforeBoundary = child->previousSibling(); + m_containerNode = child->parentNode(); + m_offsetInContainer = m_childBeforeBoundary ? invalidOffset : 0; +} + +inline void RangeBoundaryPoint::setToStartOfNode(PassRefPtr<Node> container) +{ + ASSERT(container); + m_containerNode = container; + m_offsetInContainer = 0; + m_childBeforeBoundary = 0; +} + +inline void RangeBoundaryPoint::setToEndOfNode(PassRefPtr<Node> container) +{ + ASSERT(container); + m_containerNode = container; + if (m_containerNode->offsetInCharacters()) { + m_offsetInContainer = m_containerNode->maxCharacterOffset(); + m_childBeforeBoundary = 0; + } else { + m_childBeforeBoundary = m_containerNode->lastChild(); + m_offsetInContainer = m_childBeforeBoundary ? invalidOffset : 0; + } +} + +inline void RangeBoundaryPoint::childBeforeWillBeRemoved() +{ + ASSERT(m_offsetInContainer); + m_childBeforeBoundary = m_childBeforeBoundary->previousSibling(); + if (!m_childBeforeBoundary) + m_offsetInContainer = 0; + else if (m_offsetInContainer > 0) + --m_offsetInContainer; +} + +inline void RangeBoundaryPoint::invalidateOffset() const +{ + m_offsetInContainer = invalidOffset; +} + +inline bool operator==(const RangeBoundaryPoint& a, const RangeBoundaryPoint& b) +{ + if (a.container() != b.container()) + return false; + if (a.childBefore() || b.childBefore()) { + if (a.childBefore() != b.childBefore()) + return false; + } else { + if (a.offset() != b.offset()) + return false; + } + return true; +} + +} + +#endif diff --git a/Source/WebCore/dom/RangeException.h b/Source/WebCore/dom/RangeException.h new file mode 100644 index 0000000..c9f4f5c --- /dev/null +++ b/Source/WebCore/dom/RangeException.h @@ -0,0 +1,56 @@ +/* + * (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: + static PassRefPtr<RangeException> create(const ExceptionCodeDescription& description) + { + return adoptRef(new RangeException(description)); + } + + static const int RangeExceptionOffset = 200; + static const int RangeExceptionMax = 299; + + enum RangeExceptionCode { + BAD_BOUNDARYPOINTS_ERR = RangeExceptionOffset + 1, + INVALID_NODE_TYPE_ERR + }; + + private: + RangeException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + }; + +} // namespace WebCore + +#endif // RangeException_h diff --git a/Source/WebCore/dom/RangeException.idl b/Source/WebCore/dom/RangeException.idl new file mode 100644 index 0000000..c7c8558 --- /dev/null +++ b/Source/WebCore/dom/RangeException.idl @@ -0,0 +1,38 @@ +/* + * 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 [DontCheckEnums] RangeException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(LANGUAGE_JAVASCRIPT) && 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/Source/WebCore/dom/RawDataDocumentParser.h b/Source/WebCore/dom/RawDataDocumentParser.h new file mode 100644 index 0000000..9ca36f4 --- /dev/null +++ b/Source/WebCore/dom/RawDataDocumentParser.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Google, 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 RawDataDocumentParser_h +#define RawDataDocumentParser_h + +#include "DocumentParser.h" + +namespace WebCore { + +class RawDataDocumentParser : public DocumentParser { +protected: + RawDataDocumentParser(Document* document) + : DocumentParser(document) + { + } + + virtual void finish() + { + if (!isStopped()) + document()->finishedParsing(); + } + +private: + virtual void insert(const SegmentedString&) + { + // <https://bugs.webkit.org/show_bug.cgi?id=25397>: JS code can always call document.write, we need to handle it. + ASSERT_NOT_REACHED(); + } + + virtual void append(const SegmentedString&) + { + ASSERT_NOT_REACHED(); + } + + virtual bool finishWasCalled() + { + // finish() always calls document()->finishedParsing() so we will be + // deleted after finish(). + return false; + } +}; + +}; + +#endif // RawDataDocumentParser_h diff --git a/Source/WebCore/dom/RegisteredEventListener.cpp b/Source/WebCore/dom/RegisteredEventListener.cpp new file mode 100644 index 0000000..e8bc594 --- /dev/null +++ b/Source/WebCore/dom/RegisteredEventListener.cpp @@ -0,0 +1,30 @@ +/* + * 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, 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 "RegisteredEventListener.h" + +#include "EventListener.h" + +namespace WebCore { + +} // namespace WebCore diff --git a/Source/WebCore/dom/RegisteredEventListener.h b/Source/WebCore/dom/RegisteredEventListener.h new file mode 100644 index 0000000..12483fa --- /dev/null +++ b/Source/WebCore/dom/RegisteredEventListener.h @@ -0,0 +1,51 @@ +/* + * 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, 2008, 2009 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 RegisteredEventListener_h +#define RegisteredEventListener_h + +#include "EventListener.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + + class RegisteredEventListener { + public: + RegisteredEventListener(PassRefPtr<EventListener> listener, bool useCapture) + : listener(listener) + , useCapture(useCapture) + { + } + + RefPtr<EventListener> listener; + bool useCapture; + }; + + inline bool operator==(const RegisteredEventListener& a, const RegisteredEventListener& b) + { + return *a.listener == *b.listener && a.useCapture == b.useCapture; + } + +} // namespace WebCore + +#endif // RegisteredEventListener_h diff --git a/Source/WebCore/dom/ScopedEventQueue.cpp b/Source/WebCore/dom/ScopedEventQueue.cpp new file mode 100644 index 0000000..680d82d --- /dev/null +++ b/Source/WebCore/dom/ScopedEventQueue.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "ScopedEventQueue.h" + +#include "Event.h" +#include "EventTarget.h" + +namespace WebCore { + +ScopedEventQueue* ScopedEventQueue::s_instance = 0; + +ScopedEventQueue::ScopedEventQueue() + : m_scopingLevel(0) +{ +} + +ScopedEventQueue::~ScopedEventQueue() +{ + ASSERT(!m_scopingLevel); + ASSERT(!m_queuedEvents.size()); +} + +void ScopedEventQueue::initialize() +{ + ASSERT(!s_instance); + OwnPtr<ScopedEventQueue> instance = adoptPtr(new ScopedEventQueue); + s_instance = instance.leakPtr(); +} + +void ScopedEventQueue::enqueueEvent(PassRefPtr<Event> event) +{ + if (m_scopingLevel) + m_queuedEvents.append(event); + else + dispatchEvent(event); +} + +void ScopedEventQueue::dispatchAllEvents() +{ + Vector<RefPtr<Event> > queuedEvents; + queuedEvents.swap(m_queuedEvents); + + for (size_t i = 0; i < queuedEvents.size(); i++) + dispatchEvent(queuedEvents[i].release()); +} + +void ScopedEventQueue::dispatchEvent(PassRefPtr<Event> event) const +{ + RefPtr<EventTarget> eventTarget = event->target(); + eventTarget->dispatchEvent(event); +} + +ScopedEventQueue* ScopedEventQueue::instance() +{ + if (!s_instance) + initialize(); + + return s_instance; +} + +void ScopedEventQueue::incrementScopingLevel() +{ + m_scopingLevel++; +} + +void ScopedEventQueue::decrementScopingLevel() +{ + ASSERT(m_scopingLevel); + m_scopingLevel--; + if (!m_scopingLevel) + dispatchAllEvents(); +} + +} diff --git a/Source/WebCore/dom/ScopedEventQueue.h b/Source/WebCore/dom/ScopedEventQueue.h new file mode 100644 index 0000000..52415d4 --- /dev/null +++ b/Source/WebCore/dom/ScopedEventQueue.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 ScopedEventQueue_h +#define ScopedEventQueue_h + +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Event; + +class ScopedEventQueue { + WTF_MAKE_NONCOPYABLE(ScopedEventQueue); + +public: + ~ScopedEventQueue(); + + void enqueueEvent(PassRefPtr<Event>); + void dispatchAllEvents(); + static ScopedEventQueue* instance(); + + void incrementScopingLevel(); + void decrementScopingLevel(); + +private: + ScopedEventQueue(); + static void initialize(); + void dispatchEvent(PassRefPtr<Event>) const; + + Vector<RefPtr<Event> > m_queuedEvents; + unsigned m_scopingLevel; + + static ScopedEventQueue* s_instance; +}; + +class EventQueueScope { + WTF_MAKE_NONCOPYABLE(EventQueueScope); + +public: + EventQueueScope() { ScopedEventQueue::instance()->incrementScopingLevel(); } + ~EventQueueScope() { ScopedEventQueue::instance()->decrementScopingLevel(); } +}; + +} + +#endif // ScopedEventQueue_h diff --git a/Source/WebCore/dom/ScriptElement.cpp b/Source/WebCore/dom/ScriptElement.cpp new file mode 100644 index 0000000..747f7a1 --- /dev/null +++ b/Source/WebCore/dom/ScriptElement.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 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 "ScriptElement.h" + +#include "AsyncScriptRunner.h" +#include "CachedScript.h" +#include "CachedResourceLoader.h" +#include "Document.h" +#include "DocumentParser.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "HTMLNames.h" +#include "HTMLScriptElement.h" +#include "IgnoreDestructiveWriteCountIncrementer.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "ScriptController.h" +#include "ScriptSourceCode.h" +#include "ScriptValue.h" +#include "Settings.h" +#include "Text.h" +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringHash.h> + +#if ENABLE(SVG) +#include "SVGNames.h" +#include "SVGScriptElement.h" +#endif + +namespace WebCore { + +ScriptElement::ScriptElement(Element* element, bool wasInsertedByParser, bool wasAlreadyStarted) + : m_element(element) + , m_cachedScript(0) + , m_wasInsertedByParser(wasInsertedByParser) + , m_isExternalScript(false) + , m_wasAlreadyStarted(wasAlreadyStarted) + , m_haveFiredLoad(false) +{ + ASSERT(m_element); +} + +ScriptElement::~ScriptElement() +{ + stopLoadRequest(); +} + +void ScriptElement::insertedIntoDocument(const String& sourceUrl) +{ + if (wasInsertedByParser() && !isAsynchronous()) + return; + + // http://www.whatwg.org/specs/web-apps/current-work/#script + + if (!sourceUrl.isEmpty()) { + requestScript(sourceUrl); + return; + } + + // If there's an empty script node, we shouldn't evaluate the script + // because if a script is inserted afterwards (by setting text or innerText) + // it should be evaluated, and evaluateScript only evaluates a script once. + evaluateScript(ScriptSourceCode(scriptContent(), element()->document()->url())); // FIXME: Provide a real starting line number here. +} + +void ScriptElement::removedFromDocument() +{ + // Eventually stop loading any not-yet-finished content + stopLoadRequest(); +} + +void ScriptElement::childrenChanged() +{ + if (wasInsertedByParser()) + return; + + // If a node is inserted as a child of the script element + // and the script element has been inserted in the document + // we evaluate the script. + if (m_element->inDocument() && m_element->firstChild()) + evaluateScript(ScriptSourceCode(scriptContent(), m_element->document()->url())); // FIXME: Provide a real starting line number here +} + +void ScriptElement::finishParsingChildren(const String& sourceUrl) +{ + // The parser just reached </script>. If we have no src and no text, + // allow dynamic loading later. + if (sourceUrl.isEmpty() && scriptContent().isEmpty()) + m_wasInsertedByParser = false; +} + +void ScriptElement::handleSourceAttribute(const String& sourceUrl) +{ + if (ignoresLoadRequest() || sourceUrl.isEmpty()) + return; + + requestScript(sourceUrl); +} + +// Helper function +static bool isSupportedJavaScriptLanguage(const String& language) +{ + typedef HashSet<String, CaseFoldingHash> LanguageSet; + DEFINE_STATIC_LOCAL(LanguageSet, languages, ()); + if (languages.isEmpty()) { + languages.add("javascript"); + languages.add("javascript"); + languages.add("javascript1.0"); + languages.add("javascript1.1"); + languages.add("javascript1.2"); + languages.add("javascript1.3"); + languages.add("javascript1.4"); + languages.add("javascript1.5"); + languages.add("javascript1.6"); + languages.add("javascript1.7"); + languages.add("livescript"); + languages.add("ecmascript"); + languages.add("jscript"); + } + + return languages.contains(language); +} + +void ScriptElement::requestScript(const String& sourceUrl) +{ + // FIXME: Eventually we'd like to evaluate scripts which are inserted into a + // viewless document but this'll do for now. + // See http://bugs.webkit.org/show_bug.cgi?id=5727 + if (!m_element->document()->frame()) + return; + + RefPtr<Document> originalDocument = m_element->document(); + if (!m_element->dispatchBeforeLoadEvent(sourceUrl)) + return; + if (!m_element->inDocument() || m_element->document() != originalDocument) + return; + + ASSERT(!m_cachedScript); + m_cachedScript = m_element->document()->cachedResourceLoader()->requestScript(sourceUrl, scriptCharset()); + m_isExternalScript = true; + + // m_wasInsertedByParser is never reset - always resied at the initial value set while parsing. + // m_wasAlreadyStarted is left untouched as well to avoid script reexecution, if a <script> element + // is removed and reappended to the document. + m_haveFiredLoad = false; + + if (m_cachedScript) { + m_cachedScript->addClient(this); + return; + } + + dispatchErrorEvent(); +} + +void ScriptElement::evaluateScript(const ScriptSourceCode& sourceCode) +{ + if (wasAlreadyStarted() || sourceCode.isEmpty() || !shouldExecuteAsJavaScript()) + return; + + RefPtr<Document> document = m_element->document(); + ASSERT(document); + if (Frame* frame = document->frame()) { + if (!frame->script()->canExecuteScripts(AboutToExecuteScript)) + return; + + m_wasAlreadyStarted = true; + + // http://www.whatwg.org/specs/web-apps/current-work/#script + + { + IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(m_isExternalScript ? document.get() : 0); + // Create a script from the script element node, using the script + // block's source and the script block's type. + // Note: This is where the script is compiled and actually executed. + frame->script()->evaluate(sourceCode); + } + + Document::updateStyleForAllDocuments(); + } +} + +void ScriptElement::executeScript(const ScriptSourceCode& sourceCode) +{ + if (wasAlreadyStarted() || sourceCode.isEmpty()) + return; + RefPtr<Document> document = m_element->document(); + ASSERT(document); + Frame* frame = document->frame(); + if (!frame) + return; + + m_wasAlreadyStarted = true; + + frame->script()->executeScript(sourceCode); +} + +void ScriptElement::stopLoadRequest() +{ + if (m_cachedScript) { + m_cachedScript->removeClient(this); + m_cachedScript = 0; + } +} + +void ScriptElement::execute(CachedScript* cachedScript) +{ + ASSERT(cachedScript); + if (cachedScript->errorOccurred()) + dispatchErrorEvent(); + else { + evaluateScript(ScriptSourceCode(cachedScript)); + dispatchLoadEvent(); + } + cachedScript->removeClient(this); +} + +void ScriptElement::notifyFinished(CachedResource* o) +{ + ASSERT_UNUSED(o, o == m_cachedScript); + m_element->document()->asyncScriptRunner()->executeScriptSoon(this, m_cachedScript); + m_cachedScript = 0; +} + +bool ScriptElement::ignoresLoadRequest() const +{ + return wasAlreadyStarted() || m_isExternalScript || wasInsertedByParser() || !m_element->inDocument(); +} + +bool ScriptElement::shouldExecuteAsJavaScript() const +{ + /* + Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only javascript1.1 - javascript1.3. + Mozilla 1.8 and WinIE 7 both accept javascript and livescript. + WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't. + Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace. + We want to accept all the values that either of these browsers accept, but not other values. + + FIXME: Is this HTML5 compliant? + */ + String type = typeAttributeValue(); + if (!type.isEmpty()) { + if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace().lower())) + return false; + } else { + String language = languageAttributeValue(); + if (!language.isEmpty() && !isSupportedJavaScriptLanguage(language)) + return false; + } + + // No type or language is specified, so we assume the script to be JavaScript. + + String forAttribute = forAttributeValue(); + String eventAttribute = eventAttributeValue(); + if (!forAttribute.isEmpty() && !eventAttribute.isEmpty()) { + forAttribute = forAttribute.stripWhiteSpace(); + if (!equalIgnoringCase(forAttribute, "window")) + return false; + + eventAttribute = eventAttribute.stripWhiteSpace(); + if (!equalIgnoringCase(eventAttribute, "onload") && !equalIgnoringCase(eventAttribute, "onload()")) + return false; + } + + return true; +} + +String ScriptElement::scriptCharset() const +{ + // First we try to get encoding from charset attribute. + String charset = charsetAttributeValue().stripWhiteSpace(); + + // If charset has not been declared in script tag, fall back to frame encoding. + if (charset.isEmpty()) { + if (Frame* frame = m_element->document()->frame()) + charset = frame->loader()->writer()->encoding(); + } + + return charset; +} + +String ScriptElement::scriptContent() const +{ + Vector<UChar> val; + Text* firstTextNode = 0; + bool foundMultipleTextNodes = false; + + for (Node* n = m_element->firstChild(); n; n = n->nextSibling()) { + if (!n->isTextNode()) + continue; + + Text* t = static_cast<Text*>(n); + if (foundMultipleTextNodes) + append(val, t->data()); + else if (firstTextNode) { + append(val, firstTextNode->data()); + append(val, t->data()); + foundMultipleTextNodes = true; + } else + firstTextNode = t; + } + + if (firstTextNode && !foundMultipleTextNodes) + return firstTextNode->data(); + + return String::adopt(val); +} + +bool ScriptElement::isAsynchronous() const +{ + // Only external scripts may be asynchronous. + // See: http://dev.w3.org/html5/spec/Overview.html#attr-script-async + return !sourceAttributeValue().isEmpty() && asyncAttributeValue(); +} + +bool ScriptElement::isDeferred() const +{ + // Only external scripts may be deferred and async trumps defer to allow for backward compatibility. + // See: http://dev.w3.org/html5/spec/Overview.html#attr-script-defer + return !sourceAttributeValue().isEmpty() && !asyncAttributeValue() && deferAttributeValue(); +} + +ScriptElement* toScriptElement(Element* element) +{ + if (element->isHTMLElement() && element->hasTagName(HTMLNames::scriptTag)) + return static_cast<HTMLScriptElement*>(element); + +#if ENABLE(SVG) + if (element->isSVGElement() && element->hasTagName(SVGNames::scriptTag)) + return static_cast<SVGScriptElement*>(element); +#endif + + return 0; +} + +} diff --git a/Source/WebCore/dom/ScriptElement.h b/Source/WebCore/dom/ScriptElement.h new file mode 100644 index 0000000..07c59db --- /dev/null +++ b/Source/WebCore/dom/ScriptElement.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008 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 ScriptElement_h +#define ScriptElement_h + +#include "CachedResourceClient.h" +#include "CachedResourceHandle.h" + +namespace WebCore { + +class CachedScript; +class Element; +class ScriptElement; +class ScriptSourceCode; + +class ScriptElement : private CachedResourceClient { +public: + ScriptElement(Element*, bool createdByParser, bool isEvaluated); + virtual ~ScriptElement(); + + Element* element() const { return m_element; } + + // A charset for loading the script (may be overridden by HTTP headers or a BOM). + String scriptCharset() const; + + String scriptContent() const; + bool shouldExecuteAsJavaScript() const; + void executeScript(const ScriptSourceCode&); + void execute(CachedScript*); + + // XML parser calls these + virtual String sourceAttributeValue() const = 0; + virtual void dispatchLoadEvent() = 0; + virtual void dispatchErrorEvent() = 0; + + bool haveFiredLoadEvent() const { return m_haveFiredLoad; } + +protected: + void setHaveFiredLoadEvent(bool haveFiredLoad) { m_haveFiredLoad = haveFiredLoad; } + bool wasInsertedByParser() const { return m_wasInsertedByParser; } + bool wasAlreadyStarted() const { return m_wasAlreadyStarted; } + + // Helper functions used by our parent classes. + void insertedIntoDocument(const String& sourceUrl); + void removedFromDocument(); + void childrenChanged(); + void finishParsingChildren(const String& sourceUrl); + void handleSourceAttribute(const String& sourceUrl); + +private: + bool ignoresLoadRequest() const; + bool isAsynchronous() const; + bool isDeferred() const; + + void requestScript(const String& sourceUrl); + void evaluateScript(const ScriptSourceCode&); + void stopLoadRequest(); + + virtual void notifyFinished(CachedResource*); + + virtual String charsetAttributeValue() const = 0; + virtual String typeAttributeValue() const = 0; + virtual String languageAttributeValue() const = 0; + virtual String forAttributeValue() const = 0; + virtual String eventAttributeValue() const = 0; + virtual bool asyncAttributeValue() const = 0; + virtual bool deferAttributeValue() const = 0; + + Element* m_element; + CachedResourceHandle<CachedScript> m_cachedScript; + bool m_wasInsertedByParser; + bool m_isExternalScript; + bool m_wasAlreadyStarted; + bool m_haveFiredLoad; +}; + +ScriptElement* toScriptElement(Element*); + +} + +#endif diff --git a/Source/WebCore/dom/ScriptExecutionContext.cpp b/Source/WebCore/dom/ScriptExecutionContext.cpp new file mode 100644 index 0000000..bfda17b --- /dev/null +++ b/Source/WebCore/dom/ScriptExecutionContext.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (C) 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 "ScriptExecutionContext.h" + +#include "ActiveDOMObject.h" +#include "Blob.h" +#include "BlobURL.h" +#include "Database.h" +#include "DatabaseTask.h" +#include "DatabaseThread.h" +#include "FileThread.h" +#include "MessagePort.h" +#include "SecurityOrigin.h" +#include "ThreadableBlobRegistry.h" +#include "WorkerContext.h" +#include "WorkerThread.h" +#include <wtf/MainThread.h> +#include <wtf/PassRefPtr.h> + +#if USE(JSC) +#include "JSDOMWindow.h" +#endif + +namespace WebCore { + +class ProcessMessagesSoonTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<ProcessMessagesSoonTask> create() + { + return new ProcessMessagesSoonTask; + } + + virtual void performTask(ScriptExecutionContext* context) + { + context->dispatchMessagePortEvents(); + } +}; + +ScriptExecutionContext::ScriptExecutionContext() +#if ENABLE(DATABASE) + : m_hasOpenDatabases(false) +#endif +{ +} + +ScriptExecutionContext::~ScriptExecutionContext() +{ + HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end(); + for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { + ASSERT(iter->first->scriptExecutionContext() == this); + iter->first->contextDestroyed(); + } + + HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); + for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { + ASSERT((*iter)->scriptExecutionContext() == this); + (*iter)->contextDestroyed(); + } +#if ENABLE(DATABASE) + if (m_databaseThread) { + ASSERT(m_databaseThread->terminationRequested()); + m_databaseThread = 0; + } +#endif +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) + if (m_fileThread) { + m_fileThread->stop(); + m_fileThread = 0; + } +#endif + +#if ENABLE(BLOB) + HashSet<String>::iterator publicBlobURLsEnd = m_publicBlobURLs.end(); + for (HashSet<String>::iterator iter = m_publicBlobURLs.begin(); iter != publicBlobURLsEnd; ++iter) + ThreadableBlobRegistry::unregisterBlobURL(KURL(ParsedURLString, *iter)); +#endif +} + +#if ENABLE(DATABASE) + +DatabaseThread* ScriptExecutionContext::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 = DatabaseThread::create(); + if (!m_databaseThread->start()) + m_databaseThread = 0; + } + + return m_databaseThread.get(); +} + +void ScriptExecutionContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync) +{ + ASSERT(isContextThread()); + if (m_databaseThread) + m_databaseThread->requestTermination(cleanupSync); + else if (cleanupSync) + cleanupSync->taskCompleted(); +} + +#endif + +void ScriptExecutionContext::processMessagePortMessagesSoon() +{ + postTask(ProcessMessagesSoonTask::create()); +} + +void ScriptExecutionContext::dispatchMessagePortEvents() +{ + RefPtr<ScriptExecutionContext> protect(this); + + // Make a frozen copy. + Vector<MessagePort*> ports; + copyToVector(m_messagePorts, ports); + + unsigned portCount = ports.size(); + for (unsigned i = 0; i < portCount; ++i) { + MessagePort* port = ports[i]; + // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen + // as a result is that dispatchMessages() will be called needlessly. + if (m_messagePorts.contains(port) && port->started()) + port->dispatchMessages(); + } +} + +void ScriptExecutionContext::createdMessagePort(MessagePort* port) +{ + ASSERT(port); +#if ENABLE(WORKERS) + ASSERT((isDocument() && isMainThread()) + || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID())); +#endif + + m_messagePorts.add(port); +} + +void ScriptExecutionContext::destroyedMessagePort(MessagePort* port) +{ + ASSERT(port); +#if ENABLE(WORKERS) + ASSERT((isDocument() && isMainThread()) + || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID())); +#endif + + m_messagePorts.remove(port); +} + +bool ScriptExecutionContext::canSuspendActiveDOMObjects() +{ + // No protection against m_activeDOMObjects changing during iteration: canSuspend() shouldn't execute arbitrary JS. + HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end(); + for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { + ASSERT(iter->first->scriptExecutionContext() == this); + if (!iter->first->canSuspend()) + return false; + } + return true; +} + +void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why) +{ + // No protection against m_activeDOMObjects changing during iteration: suspend() shouldn't execute arbitrary JS. + HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end(); + for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { + ASSERT(iter->first->scriptExecutionContext() == this); + iter->first->suspend(why); + } +} + +void ScriptExecutionContext::resumeActiveDOMObjects() +{ + // No protection against m_activeDOMObjects changing during iteration: resume() shouldn't execute arbitrary JS. + HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end(); + for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { + ASSERT(iter->first->scriptExecutionContext() == this); + iter->first->resume(); + } +} + +void ScriptExecutionContext::stopActiveDOMObjects() +{ + // No protection against m_activeDOMObjects changing during iteration: stop() shouldn't execute arbitrary JS. + HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end(); + for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) { + ASSERT(iter->first->scriptExecutionContext() == this); + iter->first->stop(); + } + + // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead. + closeMessagePorts(); +} + +void ScriptExecutionContext::createdActiveDOMObject(ActiveDOMObject* object, void* upcastPointer) +{ + ASSERT(object); + ASSERT(upcastPointer); + m_activeDOMObjects.add(object, upcastPointer); +} + +void ScriptExecutionContext::destroyedActiveDOMObject(ActiveDOMObject* object) +{ + ASSERT(object); + m_activeDOMObjects.remove(object); +} + +void ScriptExecutionContext::closeMessagePorts() { + HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); + for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { + ASSERT((*iter)->scriptExecutionContext() == this); + (*iter)->close(); + } +} + +void ScriptExecutionContext::setSecurityOrigin(PassRefPtr<SecurityOrigin> securityOrigin) +{ + m_securityOrigin = securityOrigin; +} + +void ScriptExecutionContext::addTimeout(int timeoutId, DOMTimer* timer) +{ + ASSERT(!m_timeouts.contains(timeoutId)); + m_timeouts.set(timeoutId, timer); +} + +void ScriptExecutionContext::removeTimeout(int timeoutId) +{ + m_timeouts.remove(timeoutId); +} + +DOMTimer* ScriptExecutionContext::findTimeout(int timeoutId) +{ + return m_timeouts.get(timeoutId); +} + +#if ENABLE(BLOB) +KURL ScriptExecutionContext::createPublicBlobURL(Blob* blob) +{ + if (!blob) + return KURL(); + KURL publicURL = BlobURL::createPublicURL(securityOrigin()); + ThreadableBlobRegistry::registerBlobURL(publicURL, blob->url()); + m_publicBlobURLs.add(publicURL.string()); + return publicURL; +} + +void ScriptExecutionContext::revokePublicBlobURL(const KURL& url) +{ + if (m_publicBlobURLs.contains(url.string())) { + ThreadableBlobRegistry::unregisterBlobURL(url); + m_publicBlobURLs.remove(url.string()); + } +} +#endif + +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) +FileThread* ScriptExecutionContext::fileThread() +{ + if (!m_fileThread) { + m_fileThread = FileThread::create(); + if (!m_fileThread->start()) + m_fileThread = 0; + } + return m_fileThread.get(); +} +#endif + +ScriptExecutionContext::Task::~Task() +{ +} + +#if USE(JSC) +JSC::JSGlobalData* ScriptExecutionContext::globalData() +{ + if (isDocument()) + return JSDOMWindow::commonJSGlobalData(); + +#if ENABLE(WORKERS) + if (isWorkerContext()) + return static_cast<WorkerContext*>(this)->script()->globalData(); +#endif + + ASSERT_NOT_REACHED(); + return 0; +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/dom/ScriptExecutionContext.h b/Source/WebCore/dom/ScriptExecutionContext.h new file mode 100644 index 0000000..12eb776 --- /dev/null +++ b/Source/WebCore/dom/ScriptExecutionContext.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 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 ScriptExecutionContext_h +#define ScriptExecutionContext_h + +#include "ActiveDOMObject.h" +#include "Console.h" +#include "KURL.h" +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Threading.h> +#include <wtf/text/StringHash.h> + +#if USE(JSC) +#include <runtime/JSGlobalData.h> +#endif + +namespace WebCore { + + class Blob; +#if ENABLE(DATABASE) + class Database; + class DatabaseTaskSynchronizer; + class DatabaseThread; +#endif + class DOMTimer; +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) + class FileThread; +#endif + class MessagePort; + class SecurityOrigin; +#if ENABLE(INSPECTOR) + class InspectorController; +#endif + + class ScriptExecutionContext { + public: + ScriptExecutionContext(); + virtual ~ScriptExecutionContext(); + + virtual bool isDocument() const { return false; } + virtual bool isWorkerContext() const { return false; } + +#if ENABLE(DATABASE) + virtual bool allowDatabaseAccess() const = 0; + virtual void databaseExceededQuota(const String& name) = 0; + DatabaseThread* databaseThread(); + void setHasOpenDatabases() { m_hasOpenDatabases = true; } + bool hasOpenDatabases() const { return m_hasOpenDatabases; } + // When the database cleanup is done, cleanupSync will be signalled. + void stopDatabases(DatabaseTaskSynchronizer*); +#endif + virtual bool isContextThread() const = 0; + virtual bool isJSExecutionTerminated() const = 0; + + const KURL& url() const { return virtualURL(); } + KURL completeURL(const String& url) const { return virtualCompleteURL(url); } + + virtual String userAgent(const KURL&) const = 0; + + SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); } +#if ENABLE(INSPECTOR) + virtual InspectorController* inspectorController() const { return 0; } +#endif + + virtual void reportException(const String& errorMessage, int lineNumber, const String& sourceURL) = 0; + virtual void addMessage(MessageSource, MessageType, MessageLevel, const String& message, unsigned lineNumber, const String& sourceURL) = 0; + + // Active objects are not garbage collected even if inaccessible, e.g. because their activity may result in callbacks being invoked. + bool canSuspendActiveDOMObjects(); + // Active objects can be asked to suspend even if canSuspendActiveDOMObjects() returns 'false' - + // step-by-step JS debugging is one example. + void suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension); + void resumeActiveDOMObjects(); + void stopActiveDOMObjects(); + void createdActiveDOMObject(ActiveDOMObject*, void* upcastPointer); + void destroyedActiveDOMObject(ActiveDOMObject*); + typedef const HashMap<ActiveDOMObject*, void*> ActiveDOMObjectsMap; + ActiveDOMObjectsMap& activeDOMObjects() const { return m_activeDOMObjects; } + + // MessagePort is conceptually a kind of ActiveDOMObject, but it needs to be tracked separately for message dispatch. + void processMessagePortMessagesSoon(); + void dispatchMessagePortEvents(); + void createdMessagePort(MessagePort*); + void destroyedMessagePort(MessagePort*); + const HashSet<MessagePort*>& messagePorts() const { return m_messagePorts; } + + void ref() { refScriptExecutionContext(); } + void deref() { derefScriptExecutionContext(); } + + class Task : public Noncopyable { + public: + virtual ~Task(); + virtual void performTask(ScriptExecutionContext*) = 0; + // Certain tasks get marked specially so that they aren't discarded, and are executed, when the context is shutting down its message queue. + virtual bool isCleanupTask() const { return false; } + }; + + virtual void postTask(PassOwnPtr<Task>) = 0; // Executes the task on context's thread asynchronously. + + void addTimeout(int timeoutId, DOMTimer*); + void removeTimeout(int timeoutId); + DOMTimer* findTimeout(int timeoutId); + +#if ENABLE(BLOB) + KURL createPublicBlobURL(Blob*); + void revokePublicBlobURL(const KURL&); +#endif + +#if USE(JSC) + JSC::JSGlobalData* globalData(); +#endif + +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) + FileThread* fileThread(); + void stopFileThread(); +#endif + + protected: + // Explicitly override the security origin for this script context. + // Note: It is dangerous to change the security origin of a script context + // that already contains content. + void setSecurityOrigin(PassRefPtr<SecurityOrigin>); + + private: + virtual const KURL& virtualURL() const = 0; + virtual KURL virtualCompleteURL(const String&) const = 0; + + void closeMessagePorts(); + + RefPtr<SecurityOrigin> m_securityOrigin; + + HashSet<MessagePort*> m_messagePorts; + + HashMap<ActiveDOMObject*, void*> m_activeDOMObjects; + + HashMap<int, DOMTimer*> m_timeouts; + +#if ENABLE(BLOB) + HashSet<String> m_publicBlobURLs; +#endif + + virtual void refScriptExecutionContext() = 0; + virtual void derefScriptExecutionContext() = 0; + +#if ENABLE(DATABASE) + RefPtr<DatabaseThread> m_databaseThread; + bool m_hasOpenDatabases; // This never changes back to false, even after the database thread is closed. +#endif + +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) + RefPtr<FileThread> m_fileThread; +#endif + }; + +} // namespace WebCore + + +#endif // ScriptExecutionContext_h diff --git a/Source/WebCore/dom/ScriptableDocumentParser.cpp b/Source/WebCore/dom/ScriptableDocumentParser.cpp new file mode 100644 index 0000000..0712a15 --- /dev/null +++ b/Source/WebCore/dom/ScriptableDocumentParser.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 Google, 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 "ScriptableDocumentParser.h" + +namespace WebCore { + +ScriptableDocumentParser::ScriptableDocumentParser(Document* document) + : DecodedDataDocumentParser(document) + , m_xssAuditor(0) +{ +} + +}; diff --git a/Source/WebCore/dom/ScriptableDocumentParser.h b/Source/WebCore/dom/ScriptableDocumentParser.h new file mode 100644 index 0000000..d9bf85c --- /dev/null +++ b/Source/WebCore/dom/ScriptableDocumentParser.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 Google, 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 ScriptableDocumentParser_h +#define ScriptableDocumentParser_h + +#include "DecodedDataDocumentParser.h" +#include <wtf/text/TextPosition.h> + +namespace WebCore { + +class XSSAuditor; + +class ScriptableDocumentParser : public DecodedDataDocumentParser { +public: + // Only used by Document::open for deciding if its safe to act on a + // JavaScript document.open() call right now, or it should be ignored. + virtual bool isExecutingScript() const { return false; } + + // FIXME: Only the HTMLDocumentParser ever blocks script execution on + // stylesheet load, which is likely a bug in the XMLDocumentParser. + virtual void executeScriptsWaitingForStylesheets() { } + + virtual bool isWaitingForScripts() const = 0; + + // These are used to expose the current line/column to the scripting system. + virtual int lineNumber() const = 0; + virtual TextPosition0 textPosition() const = 0; + + XSSAuditor* xssAuditor() const { return m_xssAuditor; } + void setXSSAuditor(XSSAuditor* auditor) { m_xssAuditor = auditor; } + +protected: + explicit ScriptableDocumentParser(Document*); + +private: + virtual ScriptableDocumentParser* asScriptableDocumentParser() { return this; } + + // The XSSAuditor associated with this document parser. + XSSAuditor* m_xssAuditor; +}; + +} + +#endif // ScriptableDocumentParser_h diff --git a/Source/WebCore/dom/SelectElement.cpp b/Source/WebCore/dom/SelectElement.cpp new file mode 100644 index 0000000..661ba88 --- /dev/null +++ b/Source/WebCore/dom/SelectElement.cpp @@ -0,0 +1,1042 @@ +/* + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "SelectElement.h" + +#include "Attribute.h" +#include "CharacterNames.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "Element.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "FormDataList.h" +#include "Frame.h" +#include "HTMLFormElement.h" +#include "HTMLKeygenElement.h" +#include "HTMLNames.h" +#include "HTMLSelectElement.h" +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "OptionElement.h" +#include "OptionGroupElement.h" +#include "Page.h" +#include "RenderListBox.h" +#include "RenderMenuList.h" +#include "SpatialNavigation.h" +#include <wtf/Assertions.h> + +#if ENABLE(WML) +#include "WMLNames.h" +#include "WMLSelectElement.h" +#endif + +// Configure platform-specific behavior when focused pop-up receives arrow/space/return keystroke. +// (PLATFORM(MAC) and PLATFORM(GTK) are always false in Chromium, hence the extra tests.) +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) +#define ARROW_KEYS_POP_MENU 1 +#define SPACE_OR_RETURN_POP_MENU 0 +#elif PLATFORM(GTK) || (PLATFORM(CHROMIUM) && (OS(LINUX) || OS(FREEBSD))) +#define ARROW_KEYS_POP_MENU 0 +#define SPACE_OR_RETURN_POP_MENU 1 +#else +#define ARROW_KEYS_POP_MENU 0 +#define SPACE_OR_RETURN_POP_MENU 0 +#endif + +using std::min; +using std::max; +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +static const DOMTimeStamp typeAheadTimeout = 1000; + +void SelectElement::selectAll(SelectElementData& data, Element* element) +{ + ASSERT(!data.usesMenuList()); + if (!element->renderer() || !data.multiple()) + return; + + // Save the selection so it can be compared to the new selectAll selection when dispatching change events + saveLastSelection(data, element); + + data.setActiveSelectionState(true); + setActiveSelectionAnchorIndex(data, element, nextSelectableListIndex(data, element, -1)); + setActiveSelectionEndIndex(data, previousSelectableListIndex(data, element, -1)); + + updateListBoxSelection(data, element, false); + listBoxOnChange(data, element); +} + +void SelectElement::saveLastSelection(SelectElementData& data, Element* element) +{ + if (data.usesMenuList()) { + data.setLastOnChangeIndex(selectedIndex(data, element)); + return; + } + + Vector<bool>& lastOnChangeSelection = data.lastOnChangeSelection(); + lastOnChangeSelection.clear(); + + const Vector<Element*>& items = data.listItems(element); + for (unsigned i = 0; i < items.size(); ++i) { + OptionElement* optionElement = toOptionElement(items[i]); + lastOnChangeSelection.append(optionElement && optionElement->selected()); + } +} + +int SelectElement::nextSelectableListIndex(SelectElementData& data, Element* element, int startIndex) +{ + const Vector<Element*>& items = data.listItems(element); + int index = startIndex + 1; + while (index >= 0 && (unsigned) index < items.size() && (!isOptionElement(items[index]) || items[index]->disabled())) + ++index; + if ((unsigned) index == items.size()) + return startIndex; + return index; +} + +int SelectElement::previousSelectableListIndex(SelectElementData& data, Element* element, int startIndex) +{ + const Vector<Element*>& items = data.listItems(element); + if (startIndex == -1) + startIndex = items.size(); + int index = startIndex - 1; + while (index >= 0 && (unsigned) index < items.size() && (!isOptionElement(items[index]) || items[index]->disabled())) + --index; + if (index == -1) + return startIndex; + return index; +} + +void SelectElement::setActiveSelectionAnchorIndex(SelectElementData& data, Element* element, int index) +{ + data.setActiveSelectionAnchorIndex(index); + + // Cache the selection state so we can restore the old selection as the new selection pivots around this anchor index + Vector<bool>& cachedStateForActiveSelection = data.cachedStateForActiveSelection(); + cachedStateForActiveSelection.clear(); + + const Vector<Element*>& items = data.listItems(element); + for (unsigned i = 0; i < items.size(); ++i) { + OptionElement* optionElement = toOptionElement(items[i]); + cachedStateForActiveSelection.append(optionElement && optionElement->selected()); + } +} + +void SelectElement::setActiveSelectionEndIndex(SelectElementData& data, int index) +{ + data.setActiveSelectionEndIndex(index); +} + +void SelectElement::updateListBoxSelection(SelectElementData& data, Element* element, bool deselectOtherOptions) +{ + ASSERT(element->renderer() && (element->renderer()->isListBox() || data.multiple())); + ASSERT(!data.listItems(element).size() || data.activeSelectionAnchorIndex() >= 0); + + unsigned start = min(data.activeSelectionAnchorIndex(), data.activeSelectionEndIndex()); + unsigned end = max(data.activeSelectionAnchorIndex(), data.activeSelectionEndIndex()); + Vector<bool>& cachedStateForActiveSelection = data.cachedStateForActiveSelection(); + + const Vector<Element*>& items = data.listItems(element); + for (unsigned i = 0; i < items.size(); ++i) { + OptionElement* optionElement = toOptionElement(items[i]); + if (!optionElement || items[i]->disabled()) + continue; + + if (i >= start && i <= end) + optionElement->setSelectedState(data.activeSelectionState()); + else if (deselectOtherOptions || i >= cachedStateForActiveSelection.size()) + optionElement->setSelectedState(false); + else + optionElement->setSelectedState(cachedStateForActiveSelection[i]); + } + + toSelectElement(element)->updateValidity(); + scrollToSelection(data, element); +} + +void SelectElement::listBoxOnChange(SelectElementData& data, Element* element) +{ + ASSERT(!data.usesMenuList() || data.multiple()); + + Vector<bool>& lastOnChangeSelection = data.lastOnChangeSelection(); + const Vector<Element*>& items = data.listItems(element); + + // If the cached selection list is empty, or the size has changed, then fire dispatchFormControlChangeEvent, and return early. + if (lastOnChangeSelection.isEmpty() || lastOnChangeSelection.size() != items.size()) { + element->dispatchFormControlChangeEvent(); + return; + } + + // Update lastOnChangeSelection and fire dispatchFormControlChangeEvent + bool fireOnChange = false; + for (unsigned i = 0; i < items.size(); ++i) { + OptionElement* optionElement = toOptionElement(items[i]); + bool selected = optionElement && optionElement->selected(); + if (selected != lastOnChangeSelection[i]) + fireOnChange = true; + lastOnChangeSelection[i] = selected; + } + + if (fireOnChange) + element->dispatchFormControlChangeEvent(); +} + +void SelectElement::menuListOnChange(SelectElementData& data, Element* element) +{ + ASSERT(data.usesMenuList()); + + int selected = selectedIndex(data, element); + if (data.lastOnChangeIndex() != selected && data.userDrivenChange()) { + data.setLastOnChangeIndex(selected); + data.setUserDrivenChange(false); + element->dispatchFormControlChangeEvent(); + } +} + +void SelectElement::scrollToSelection(SelectElementData& data, Element* element) +{ + if (data.usesMenuList()) + return; + + if (RenderObject* renderer = element->renderer()) + toRenderListBox(renderer)->selectionChanged(); +} + +void SelectElement::setOptionsChangedOnRenderer(SelectElementData& data, Element* element) +{ + if (RenderObject* renderer = element->renderer()) { + if (data.usesMenuList()) + toRenderMenuList(renderer)->setOptionsChanged(true); + else + toRenderListBox(renderer)->setOptionsChanged(true); + } +} + +void SelectElement::setRecalcListItems(SelectElementData& data, Element* element) +{ + data.setShouldRecalcListItems(true); + data.setActiveSelectionAnchorIndex(-1); // Manual selection anchor is reset when manipulating the select programmatically. + setOptionsChangedOnRenderer(data, element); + element->setNeedsStyleRecalc(); +} + +void SelectElement::recalcListItems(SelectElementData& data, const Element* element, bool updateSelectedStates) +{ + Vector<Element*>& listItems = data.rawListItems(); + listItems.clear(); + + data.setShouldRecalcListItems(false); + + OptionElement* foundSelected = 0; + for (Node* currentNode = element->firstChild(); currentNode;) { + if (!currentNode->isElementNode()) { + currentNode = currentNode->traverseNextSibling(element); + continue; + } + + Element* current = static_cast<Element*>(currentNode); + + // optgroup tags may not nest. However, both FireFox and IE will + // flatten the tree automatically, so we follow suit. + // (http://www.w3.org/TR/html401/interact/forms.html#h-17.6) + if (isOptionGroupElement(current)) { + listItems.append(current); + if (current->firstChild()) { + currentNode = current->firstChild(); + continue; + } + } + + if (OptionElement* optionElement = toOptionElement(current)) { + listItems.append(current); + + if (updateSelectedStates && !data.multiple()) { + if (!foundSelected && (data.size() <= 1 || optionElement->selected())) { + foundSelected = optionElement; + foundSelected->setSelectedState(true); + } else if (foundSelected && optionElement->selected()) { + foundSelected->setSelectedState(false); + foundSelected = optionElement; + } + } + } + + if (current->hasTagName(HTMLNames::hrTag)) + listItems.append(current); + + // In conforming HTML code, only <optgroup> and <option> will be found + // within a <select>. We call traverseNextSibling so that we only step + // into those tags that we choose to. For web-compat, we should cope + // with the case where odd tags like a <div> have been added but we + // handle this because such tags have already been removed from the + // <select>'s subtree at this point. + currentNode = currentNode->traverseNextSibling(element); + } +} + +int SelectElement::selectedIndex(const SelectElementData& data, const Element* element) +{ + unsigned index = 0; + + // return the number of the first option selected + const Vector<Element*>& items = data.listItems(element); + for (size_t i = 0; i < items.size(); ++i) { + if (OptionElement* optionElement = toOptionElement(items[i])) { + if (optionElement->selected()) + return index; + ++index; + } + } + + return -1; +} + +void SelectElement::setSelectedIndex(SelectElementData& data, Element* element, int optionIndex, bool deselect, bool fireOnChangeNow, bool userDrivenChange) +{ + const Vector<Element*>& items = data.listItems(element); + int listIndex = optionToListIndex(data, element, optionIndex); + if (!data.multiple()) + deselect = true; + + Element* excludeElement = 0; + if (OptionElement* optionElement = (listIndex >= 0 ? toOptionElement(items[listIndex]) : 0)) { + excludeElement = items[listIndex]; + if (data.activeSelectionAnchorIndex() < 0 || deselect) + setActiveSelectionAnchorIndex(data, element, listIndex); + if (data.activeSelectionEndIndex() < 0 || deselect) + setActiveSelectionEndIndex(data, listIndex); + optionElement->setSelectedState(true); + } + + if (deselect) + deselectItems(data, element, excludeElement); + + // For the menu list case, this is what makes the selected element appear. + if (RenderObject* renderer = element->renderer()) + renderer->updateFromElement(); + + scrollToSelection(data, element); + + // This only gets called with fireOnChangeNow for menu lists. + if (data.usesMenuList()) { + data.setUserDrivenChange(userDrivenChange); + if (fireOnChangeNow) + menuListOnChange(data, element); + RenderObject* renderer = element->renderer(); + if (renderer) { + if (data.usesMenuList()) + toRenderMenuList(renderer)->didSetSelectedIndex(); + else if (renderer->isListBox()) + toRenderListBox(renderer)->selectionChanged(); + } + } + + if (Frame* frame = element->document()->frame()) + frame->page()->chrome()->client()->formStateDidChange(element); +} + +int SelectElement::optionToListIndex(const SelectElementData& data, const Element* element, int optionIndex) +{ + const Vector<Element*>& items = data.listItems(element); + int listSize = (int) items.size(); + if (optionIndex < 0 || optionIndex >= listSize) + return -1; + + int optionIndex2 = -1; + for (int listIndex = 0; listIndex < listSize; ++listIndex) { + if (isOptionElement(items[listIndex])) { + ++optionIndex2; + if (optionIndex2 == optionIndex) + return listIndex; + } + } + + return -1; +} + +int SelectElement::listToOptionIndex(const SelectElementData& data, const Element* element, int listIndex) +{ + const Vector<Element*>& items = data.listItems(element); + if (listIndex < 0 || listIndex >= int(items.size()) || + !isOptionElement(items[listIndex])) + return -1; + + int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list + for (int i = 0; i < listIndex; ++i) + if (isOptionElement(items[i])) + ++optionIndex; + + return optionIndex; +} + +void SelectElement::dispatchFocusEvent(SelectElementData& data, Element* element) +{ + // Save the selection so it can be compared to the new selection when dispatching change events during blur event dispatchal + if (data.usesMenuList()) + saveLastSelection(data, element); +} + +void SelectElement::dispatchBlurEvent(SelectElementData& data, Element* element) +{ + // We only need to fire change events here for menu lists, because we fire change events for list boxes whenever the selection change is actually made. + // This matches other browsers' behavior. + if (data.usesMenuList()) + menuListOnChange(data, element); +} + +void SelectElement::deselectItems(SelectElementData& data, Element* element, Element* excludeElement) +{ + const Vector<Element*>& items = data.listItems(element); + for (unsigned i = 0; i < items.size(); ++i) { + if (items[i] == excludeElement) + continue; + + if (OptionElement* optionElement = toOptionElement(items[i])) + optionElement->setSelectedState(false); + } +} + +bool SelectElement::saveFormControlState(const SelectElementData& data, const Element* element, String& value) +{ + const Vector<Element*>& items = data.listItems(element); + int length = items.size(); + + // FIXME: Change this code to use the new StringImpl::createUninitialized code path. + Vector<char, 1024> characters(length); + for (int i = 0; i < length; ++i) { + OptionElement* optionElement = toOptionElement(items[i]); + bool selected = optionElement && optionElement->selected(); + characters[i] = selected ? 'X' : '.'; + } + + value = String(characters.data(), length); + return true; +} + +void SelectElement::restoreFormControlState(SelectElementData& data, Element* element, const String& state) +{ + recalcListItems(data, element); + + const Vector<Element*>& items = data.listItems(element); + int length = items.size(); + + for (int i = 0; i < length; ++i) { + if (OptionElement* optionElement = toOptionElement(items[i])) + optionElement->setSelectedState(state[i] == 'X'); + } + + setOptionsChangedOnRenderer(data, element); +} + +void SelectElement::parseMultipleAttribute(SelectElementData& data, Element* element, Attribute* attribute) +{ + bool oldUsesMenuList = data.usesMenuList(); + data.setMultiple(!attribute->isNull()); + toSelectElement(element)->updateValidity(); + if (oldUsesMenuList != data.usesMenuList() && element->attached()) { + element->detach(); + element->attach(); + } +} + +bool SelectElement::appendFormData(SelectElementData& data, Element* element, FormDataList& list) +{ + const AtomicString& name = element->formControlName(); + if (name.isEmpty()) + return false; + + bool successful = false; + const Vector<Element*>& items = data.listItems(element); + + for (unsigned i = 0; i < items.size(); ++i) { + OptionElement* optionElement = toOptionElement(items[i]); + if (optionElement && optionElement->selected() && !optionElement->disabled()) { + list.appendData(name, optionElement->value()); + successful = true; + } + } + + // It's possible that this is a menulist with multiple options and nothing + // will be submitted (!successful). We won't send a unselected non-disabled + // option as fallback. This behavior matches to other browsers. + return successful; +} + +void SelectElement::reset(SelectElementData& data, Element* element) +{ + OptionElement* firstOption = 0; + OptionElement* selectedOption = 0; + + const Vector<Element*>& items = data.listItems(element); + for (unsigned i = 0; i < items.size(); ++i) { + OptionElement* optionElement = toOptionElement(items[i]); + if (!optionElement) + continue; + + if (items[i]->fastHasAttribute(HTMLNames::selectedAttr)) { + if (selectedOption && !data.multiple()) + selectedOption->setSelectedState(false); + optionElement->setSelectedState(true); + selectedOption = optionElement; + } else + optionElement->setSelectedState(false); + + if (!firstOption) + firstOption = optionElement; + } + + if (!selectedOption && firstOption && !data.multiple() && data.size() <= 1) + firstOption->setSelectedState(true); + + setOptionsChangedOnRenderer(data, element); + element->setNeedsStyleRecalc(); +} + +enum SkipDirection { + SkipBackwards = -1, + SkipForwards = 1 +}; + +// Returns the index of the next valid list item |skip| items past |listIndex| in direction |direction|. +static int nextValidIndex(const Vector<Element*>& listItems, int listIndex, SkipDirection direction, int skip) +{ + int lastGoodIndex = listIndex; + int size = listItems.size(); + for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) { + --skip; + if (!listItems[listIndex]->disabled() && isOptionElement(listItems[listIndex])) { + lastGoodIndex = listIndex; + if (skip <= 0) + break; + } + } + return lastGoodIndex; +} + +void SelectElement::menuListDefaultEventHandler(SelectElementData& data, Element* element, Event* event, HTMLFormElement* htmlForm) +{ + if (event->type() == eventNames().keydownEvent) { + if (!element->renderer() || !event->isKeyboardEvent()) + return; + + const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier(); + bool handled = false; + +#if ARROW_KEYS_POP_MENU + if (!isSpatialNavigationEnabled(element->document()->frame())) { + if (keyIdentifier == "Down" || keyIdentifier == "Up") { + element->focus(); + + if (!element->renderer()) // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event. + return; + + // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex, + // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. + saveLastSelection(data, element); + if (RenderMenuList* menuList = toRenderMenuList(element->renderer())) + menuList->showPopup(); + + event->setDefaultHandled(); + } + return; + } +#endif + // When using spatial navigation, we want to be able to navigate away from the select element + // when the user hits any of the arrow keys, instead of changing the selection. + if (isSpatialNavigationEnabled(element->document()->frame())) + if (!data.activeSelectionState()) + return; + + UNUSED_PARAM(htmlForm); + const Vector<Element*>& listItems = data.listItems(element); + + int listIndex = optionToListIndex(data, element, selectedIndex(data, element)); + if (keyIdentifier == "Down" || keyIdentifier == "Right") { + listIndex = nextValidIndex(listItems, listIndex, SkipForwards, 1); + handled = true; + } else if (keyIdentifier == "Up" || keyIdentifier == "Left") { + listIndex = nextValidIndex(listItems, listIndex, SkipBackwards, 1); + handled = true; + } else if (keyIdentifier == "PageDown") { + listIndex = nextValidIndex(listItems, listIndex, SkipForwards, 3); + handled = true; + } else if (keyIdentifier == "PageUp") { + listIndex = nextValidIndex(listItems, listIndex, SkipBackwards, 3); + handled = true; + } else if (keyIdentifier == "Home") { + listIndex = nextValidIndex(listItems, -1, SkipForwards, 1); + handled = true; + } else if (keyIdentifier == "End") { + listIndex = nextValidIndex(listItems, listItems.size(), SkipBackwards, 1); + handled = true; + } + + if (handled && listIndex >= 0 && (unsigned)listIndex < listItems.size()) + setSelectedIndex(data, element, listToOptionIndex(data, element, listIndex)); + + if (handled) + event->setDefaultHandled(); + } + + // Use key press event here since sending simulated mouse events + // on key down blocks the proper sending of the key press event. + if (event->type() == eventNames().keypressEvent) { + if (!element->renderer() || !event->isKeyboardEvent()) + return; + + int keyCode = static_cast<KeyboardEvent*>(event)->keyCode(); + bool handled = false; + + if (keyCode == ' ' && isSpatialNavigationEnabled(element->document()->frame())) { + // Use space to toggle arrow key handling for selection change or spatial navigation. + data.setActiveSelectionState(!data.activeSelectionState()); + event->setDefaultHandled(); + return; + } + +#if SPACE_OR_RETURN_POP_MENU + if (keyCode == ' ' || keyCode == '\r') { + element->focus(); + + if (!element->renderer()) // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event. + return; + + // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex, + // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. + saveLastSelection(data, element); + if (RenderMenuList* menuList = toRenderMenuList(element->renderer())) + menuList->showPopup(); + handled = true; + } +#elif ARROW_KEYS_POP_MENU + if (keyCode == ' ') { + element->focus(); + + if (!element->renderer()) // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event. + return; + + // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex, + // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. + saveLastSelection(data, element); + if (RenderMenuList* menuList = toRenderMenuList(element->renderer())) + menuList->showPopup(); + handled = true; + } else if (keyCode == '\r') { + if (htmlForm) + htmlForm->submitImplicitly(event, false); + menuListOnChange(data, element); + handled = true; + } +#else + int listIndex = optionToListIndex(data, element, selectedIndex(data, element)); + if (keyCode == '\r') { + // listIndex should already be selected, but this will fire the onchange handler. + setSelectedIndex(data, element, listToOptionIndex(data, element, listIndex), true, true); + handled = true; + } +#endif + if (handled) + event->setDefaultHandled(); + } + + if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + element->focus(); + if (element->renderer() && element->renderer()->isMenuList()) { + if (RenderMenuList* menuList = toRenderMenuList(element->renderer())) { + if (menuList->popupIsVisible()) + menuList->hidePopup(); + else { + // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex, + // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. + saveLastSelection(data, element); + menuList->showPopup(); + } + } + } + event->setDefaultHandled(); + } +} + +void SelectElement::updateSelectedState(SelectElementData& data, Element* element, int listIndex, + bool multi, bool shift) +{ + ASSERT(listIndex >= 0); + + // Save the selection so it can be compared to the new selection when dispatching change events during mouseup, or after autoscroll finishes. + saveLastSelection(data, element); + + data.setActiveSelectionState(true); + + bool shiftSelect = data.multiple() && shift; + bool multiSelect = data.multiple() && multi && !shift; + + Element* clickedElement = data.listItems(element)[listIndex]; + OptionElement* option = toOptionElement(clickedElement); + if (option) { + // Keep track of whether an active selection (like during drag selection), should select or deselect + if (option->selected() && multi) + data.setActiveSelectionState(false); + + if (!data.activeSelectionState()) + option->setSelectedState(false); + } + + // If we're not in any special multiple selection mode, then deselect all other items, excluding the clicked option. + // If no option was clicked, then this will deselect all items in the list. + if (!shiftSelect && !multiSelect) + deselectItems(data, element, clickedElement); + + // If the anchor hasn't been set, and we're doing a single selection or a shift selection, then initialize the anchor to the first selected index. + if (data.activeSelectionAnchorIndex() < 0 && !multiSelect) + setActiveSelectionAnchorIndex(data, element, selectedIndex(data, element)); + + // Set the selection state of the clicked option + if (option && !clickedElement->disabled()) + option->setSelectedState(true); + + // If there was no selectedIndex() for the previous initialization, or + // If we're doing a single selection, or a multiple selection (using cmd or ctrl), then initialize the anchor index to the listIndex that just got clicked. + if (data.activeSelectionAnchorIndex() < 0 || !shiftSelect) + setActiveSelectionAnchorIndex(data, element, listIndex); + + setActiveSelectionEndIndex(data, listIndex); + updateListBoxSelection(data, element, !multiSelect); +} + +void SelectElement::listBoxDefaultEventHandler(SelectElementData& data, Element* element, Event* event, HTMLFormElement* htmlForm) +{ + const Vector<Element*>& listItems = data.listItems(element); + + if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + element->focus(); + + if (!element->renderer()) // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event. + return; + + // Convert to coords relative to the list box if needed. + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + IntPoint localOffset = roundedIntPoint(element->renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), false, true)); + int listIndex = toRenderListBox(element->renderer())->listIndexAtOffset(localOffset.x(), localOffset.y()); + if (listIndex >= 0) { +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) + updateSelectedState(data, element, listIndex, mouseEvent->metaKey(), mouseEvent->shiftKey()); +#else + updateSelectedState(data, element, listIndex, mouseEvent->ctrlKey(), mouseEvent->shiftKey()); +#endif + if (Frame* frame = element->document()->frame()) + frame->eventHandler()->setMouseDownMayStartAutoscroll(); + + event->setDefaultHandled(); + } + } else if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton && element->document()->frame()->eventHandler()->autoscrollRenderer() != element->renderer()) { + // This makes sure we fire dispatchFormControlChangeEvent for a single click. For drag selection, onChange will fire when the autoscroll timer stops. + listBoxOnChange(data, element); + } else if (event->type() == eventNames().keydownEvent) { + if (!event->isKeyboardEvent()) + return; + const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier(); + + int endIndex = 0; + if (data.activeSelectionEndIndex() < 0) { + // Initialize the end index + if (keyIdentifier == "Down") + endIndex = nextSelectableListIndex(data, element, lastSelectedListIndex(data, element)); + else if (keyIdentifier == "Up") + endIndex = previousSelectableListIndex(data, element, optionToListIndex(data, element, selectedIndex(data, element))); + } else { + // Set the end index based on the current end index + if (keyIdentifier == "Down") + endIndex = nextSelectableListIndex(data, element, data.activeSelectionEndIndex()); + else if (keyIdentifier == "Up") + endIndex = previousSelectableListIndex(data, element, data.activeSelectionEndIndex()); + } + + if (isSpatialNavigationEnabled(element->document()->frame())) + // Check if the selection moves to the boundary. + if (keyIdentifier == "Left" || keyIdentifier == "Right" || ((keyIdentifier == "Down" || keyIdentifier == "Up") && endIndex == data.activeSelectionEndIndex())) + return; + + if (keyIdentifier == "Down" || keyIdentifier == "Up") { + // Save the selection so it can be compared to the new selection when dispatching change events immediately after making the new selection. + saveLastSelection(data, element); + + ASSERT_UNUSED(listItems, !listItems.size() || (endIndex >= 0 && (unsigned) endIndex < listItems.size())); + setActiveSelectionEndIndex(data, endIndex); + + // If the anchor is unitialized, or if we're going to deselect all other options, then set the anchor index equal to the end index. + bool deselectOthers = !data.multiple() || !static_cast<KeyboardEvent*>(event)->shiftKey(); + if (data.activeSelectionAnchorIndex() < 0 || deselectOthers) { + data.setActiveSelectionState(true); + if (deselectOthers) + deselectItems(data, element); + setActiveSelectionAnchorIndex(data, element, data.activeSelectionEndIndex()); + } + + toRenderListBox(element->renderer())->scrollToRevealElementAtListIndex(endIndex); + updateListBoxSelection(data, element, deselectOthers); + listBoxOnChange(data, element); + event->setDefaultHandled(); + } + } else if (event->type() == eventNames().keypressEvent) { + if (!event->isKeyboardEvent()) + return; + int keyCode = static_cast<KeyboardEvent*>(event)->keyCode(); + + if (keyCode == '\r') { + if (htmlForm) + htmlForm->submitImplicitly(event, false); + event->setDefaultHandled(); + return; + } + } +} + +void SelectElement::defaultEventHandler(SelectElementData& data, Element* element, Event* event, HTMLFormElement* htmlForm) +{ + if (!element->renderer()) + return; + + if (data.usesMenuList()) + menuListDefaultEventHandler(data, element, event, htmlForm); + else + listBoxDefaultEventHandler(data, element, event, htmlForm); + + if (event->defaultHandled()) + return; + + if (event->type() == eventNames().keypressEvent && event->isKeyboardEvent()) { + KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event); + if (!keyboardEvent->ctrlKey() && !keyboardEvent->altKey() && !keyboardEvent->metaKey() && isPrintableChar(keyboardEvent->charCode())) { + typeAheadFind(data, element, keyboardEvent); + event->setDefaultHandled(); + return; + } + } +} + +int SelectElement::lastSelectedListIndex(const SelectElementData& data, const Element* element) +{ + // return the number of the last option selected + unsigned index = 0; + bool found = false; + const Vector<Element*>& items = data.listItems(element); + for (size_t i = 0; i < items.size(); ++i) { + if (OptionElement* optionElement = toOptionElement(items[i])) { + if (optionElement->selected()) { + index = i; + found = true; + } + } + } + + return found ? (int) index : -1; +} + +static String stripLeadingWhiteSpace(const String& string) +{ + int length = string.length(); + + int i; + for (i = 0; i < length; ++i) { + if (string[i] != noBreakSpace && (string[i] <= 0x7F ? !isASCIISpace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral))) + break; + } + + return string.substring(i, length - i); +} + +void SelectElement::typeAheadFind(SelectElementData& data, Element* element, KeyboardEvent* event) +{ + if (event->timeStamp() < data.lastCharTime()) + return; + + DOMTimeStamp delta = event->timeStamp() - data.lastCharTime(); + data.setLastCharTime(event->timeStamp()); + + UChar c = event->charCode(); + + String prefix; + int searchStartOffset = 1; + if (delta > typeAheadTimeout) { + prefix = String(&c, 1); + data.setTypedString(prefix); + data.setRepeatingChar(c); + } else { + data.typedString().append(c); + + if (c == data.repeatingChar()) + // The user is likely trying to cycle through all the items starting with this character, so just search on the character + prefix = String(&c, 1); + else { + data.setRepeatingChar(0); + prefix = data.typedString(); + searchStartOffset = 0; + } + } + + const Vector<Element*>& items = data.listItems(element); + int itemCount = items.size(); + if (itemCount < 1) + return; + + int selected = selectedIndex(data, element); + int index = (optionToListIndex(data, element, selected >= 0 ? selected : 0) + searchStartOffset) % itemCount; + ASSERT(index >= 0); + + // Compute a case-folded copy of the prefix string before beginning the search for + // a matching element. This code uses foldCase to work around the fact that + // String::startWith does not fold non-ASCII characters. This code can be changed + // to use startWith once that is fixed. + String prefixWithCaseFolded(prefix.foldCase()); + for (int i = 0; i < itemCount; ++i, index = (index + 1) % itemCount) { + OptionElement* optionElement = toOptionElement(items[index]); + if (!optionElement || items[index]->disabled()) + continue; + + // Fold the option string and check if its prefix is equal to the folded prefix. + String text = optionElement->textIndentedToRespectGroupLabel(); + if (stripLeadingWhiteSpace(text).foldCase().startsWith(prefixWithCaseFolded)) { + setSelectedIndex(data, element, listToOptionIndex(data, element, index)); + if (!data.usesMenuList()) + listBoxOnChange(data, element); + + setOptionsChangedOnRenderer(data, element); + element->setNeedsStyleRecalc(); + return; + } + } +} + +void SelectElement::insertedIntoTree(SelectElementData& data, Element* element) +{ + // When the element is created during document parsing, it won't have any items yet - but for innerHTML + // and related methods, this method is called after the whole subtree is constructed. + recalcListItems(data, element, true); +} + +void SelectElement::accessKeySetSelectedIndex(SelectElementData& data, Element* element, int index) +{ + // first bring into focus the list box + if (!element->focused()) + element->accessKeyAction(false); + + // if this index is already selected, unselect. otherwise update the selected index + const Vector<Element*>& items = data.listItems(element); + int listIndex = optionToListIndex(data, element, index); + if (OptionElement* optionElement = (listIndex >= 0 ? toOptionElement(items[listIndex]) : 0)) { + if (optionElement->selected()) + optionElement->setSelectedState(false); + else + setSelectedIndex(data, element, index, false, true); + } + + if (data.usesMenuList()) + menuListOnChange(data, element); + else + listBoxOnChange(data, element); + + scrollToSelection(data, element); +} + +unsigned SelectElement::optionCount(const SelectElementData& data, const Element* element) +{ + unsigned options = 0; + + const Vector<Element*>& items = data.listItems(element); + for (unsigned i = 0; i < items.size(); ++i) { + if (isOptionElement(items[i])) + ++options; + } + + return options; +} + +// SelectElementData +SelectElementData::SelectElementData() + : m_multiple(false) + , m_size(0) + , m_lastOnChangeIndex(-1) + , m_activeSelectionState(false) + , m_activeSelectionAnchorIndex(-1) + , m_activeSelectionEndIndex(-1) + , m_recalcListItems(false) + , m_repeatingChar(0) + , m_lastCharTime(0) +{ +} + +SelectElementData::~SelectElementData() +{ +} + +void SelectElementData::checkListItems(const Element* element) const +{ +#if !ASSERT_DISABLED + Vector<Element*> items = m_listItems; + SelectElement::recalcListItems(*const_cast<SelectElementData*>(this), element, false); + ASSERT(items == m_listItems); +#else + UNUSED_PARAM(element); +#endif +} + +Vector<Element*>& SelectElementData::listItems(const Element* element) +{ + if (m_recalcListItems) + SelectElement::recalcListItems(*this, element); + else + checkListItems(element); + + return m_listItems; +} + +const Vector<Element*>& SelectElementData::listItems(const Element* element) const +{ + if (m_recalcListItems) + SelectElement::recalcListItems(*const_cast<SelectElementData*>(this), element); + else + checkListItems(element); + + return m_listItems; +} + +SelectElement* toSelectElement(Element* element) +{ + if (element->isHTMLElement()) { + if (element->hasTagName(HTMLNames::selectTag)) + return static_cast<HTMLSelectElement*>(element); + if (element->hasTagName(HTMLNames::keygenTag)) + return static_cast<HTMLKeygenElement*>(element); + } + +#if ENABLE(WML) + if (element->isWMLElement() && element->hasTagName(WMLNames::selectTag)) + return static_cast<WMLSelectElement*>(element); +#endif + + return 0; +} + +} diff --git a/Source/WebCore/dom/SelectElement.h b/Source/WebCore/dom/SelectElement.h new file mode 100644 index 0000000..222a1bb --- /dev/null +++ b/Source/WebCore/dom/SelectElement.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http//www.torchmobile.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. + * + */ + +#ifndef SelectElement_h +#define SelectElement_h + +#include "Event.h" +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Attribute; +class Element; +class Event; +class FormDataList; +class HTMLFormElement; +class KeyboardEvent; +class SelectElementData; + +class SelectElement { +public: + virtual bool multiple() const = 0; + + virtual int size() const = 0; + virtual const Vector<Element*>& listItems() const = 0; + + virtual void listBoxOnChange() = 0; + virtual void updateListBoxSelection(bool deselectOtherOptions) = 0; + + virtual void menuListOnChange() = 0; + + virtual int activeSelectionStartListIndex() const = 0; + virtual int activeSelectionEndListIndex() const = 0; + + virtual void setActiveSelectionAnchorIndex(int index) = 0; + virtual void setActiveSelectionEndIndex(int index) = 0; + + virtual int listToOptionIndex(int listIndex) const = 0; + virtual int optionToListIndex(int optionIndex) const = 0; + + virtual int selectedIndex() const = 0; + virtual void setSelectedIndex(int index, bool deselect = true) = 0; + virtual void setSelectedIndexByUser(int index, bool deselect = true, bool fireOnChangeNow = false, bool allowMultipleSelection = false) = 0; + + virtual void listBoxSelectItem(int listIndex, bool allowMultiplySelections, bool shift, bool fireOnChangeNow = true) = 0; + + virtual void updateValidity() = 0; + +protected: + virtual ~SelectElement() { } + + friend class SelectElementData; + + static void selectAll(SelectElementData&, Element*); + static void saveLastSelection(SelectElementData&, Element*); + static int nextSelectableListIndex(SelectElementData&, Element*, int startIndex); + static int previousSelectableListIndex(SelectElementData&, Element*, int startIndex); + static void setActiveSelectionAnchorIndex(SelectElementData&, Element*, int index); + static void setActiveSelectionEndIndex(SelectElementData&, int index); + static void updateListBoxSelection(SelectElementData&, Element*, bool deselectOtherOptions); + static void listBoxOnChange(SelectElementData&, Element*); + static void menuListOnChange(SelectElementData&, Element*); + static void scrollToSelection(SelectElementData&, Element*); + static void setRecalcListItems(SelectElementData&, Element*); + static void recalcListItems(SelectElementData&, const Element*, bool updateSelectedStates = true); + static int selectedIndex(const SelectElementData&, const Element*); + static void setSelectedIndex(SelectElementData&, Element*, int optionIndex, bool deselect = true, bool fireOnChangeNow = false, bool userDrivenChange = true); + static int optionToListIndex(const SelectElementData&, const Element*, int optionIndex); + static int listToOptionIndex(const SelectElementData&, const Element*, int listIndex); + static void dispatchFocusEvent(SelectElementData&, Element*); + static void dispatchBlurEvent(SelectElementData&, Element*); + static void deselectItems(SelectElementData&, Element*, Element* excludeElement = 0); + static bool saveFormControlState(const SelectElementData&, const Element*, String& state); + static void restoreFormControlState(SelectElementData&, Element*, const String& state); + static void parseMultipleAttribute(SelectElementData&, Element*, Attribute*); + static bool appendFormData(SelectElementData&, Element*, FormDataList&); + static void reset(SelectElementData&, Element*); + static void defaultEventHandler(SelectElementData&, Element*, Event*, HTMLFormElement*); + static int lastSelectedListIndex(const SelectElementData&, const Element*); + static void typeAheadFind(SelectElementData&, Element*, KeyboardEvent*); + static void insertedIntoTree(SelectElementData&, Element*); + static void accessKeySetSelectedIndex(SelectElementData&, Element*, int index); + static unsigned optionCount(const SelectElementData&, const Element*); + + static void updateSelectedState(SelectElementData& data, Element* element, int listIndex, + bool multi, bool shift); + +private: + static void menuListDefaultEventHandler(SelectElementData&, Element*, Event*, HTMLFormElement*); + static void listBoxDefaultEventHandler(SelectElementData&, Element*, Event*, HTMLFormElement*); + static void setOptionsChangedOnRenderer(SelectElementData&, Element*); +}; + +// HTML/WMLSelectElement hold this struct as member variable +// and pass it to the static helper functions in SelectElement +class SelectElementData { +public: + SelectElementData(); + ~SelectElementData(); + + bool multiple() const { return m_multiple; } + void setMultiple(bool value) { m_multiple = value; } + + int size() const { return m_size; } + void setSize(int value) { m_size = value; } + + bool usesMenuList() const + { +#if ENABLE(NO_LISTBOX_RENDERING) + return true; +#else + return !m_multiple && m_size <= 1; +#endif + } + + int lastOnChangeIndex() const { return m_lastOnChangeIndex; } + void setLastOnChangeIndex(int value) { m_lastOnChangeIndex = value; } + + bool userDrivenChange() const { return m_userDrivenChange; } + void setUserDrivenChange(bool value) { m_userDrivenChange = value; } + + Vector<bool>& lastOnChangeSelection() { return m_lastOnChangeSelection; } + + bool activeSelectionState() const { return m_activeSelectionState; } + void setActiveSelectionState(bool value) { m_activeSelectionState = value; } + + int activeSelectionAnchorIndex() const { return m_activeSelectionAnchorIndex; } + void setActiveSelectionAnchorIndex(int value) { m_activeSelectionAnchorIndex = value; } + + int activeSelectionEndIndex() const { return m_activeSelectionEndIndex; } + void setActiveSelectionEndIndex(int value) { m_activeSelectionEndIndex = value; } + + Vector<bool>& cachedStateForActiveSelection() { return m_cachedStateForActiveSelection; } + + bool shouldRecalcListItems() const { return m_recalcListItems; } + void setShouldRecalcListItems(bool value) { m_recalcListItems = value; } + + Vector<Element*>& rawListItems() { return m_listItems; } + Vector<Element*>& listItems(const Element*); + const Vector<Element*>& listItems(const Element*) const; + + UChar repeatingChar() const { return m_repeatingChar; } + void setRepeatingChar(const UChar& value) { m_repeatingChar = value; } + + DOMTimeStamp lastCharTime() const { return m_lastCharTime; } + void setLastCharTime(const DOMTimeStamp& value) { m_lastCharTime = value; } + + String& typedString() { return m_typedString; } + void setTypedString(const String& value) { m_typedString = value; } + +private: + void checkListItems(const Element*) const; + + bool m_multiple; + int m_size; + + int m_lastOnChangeIndex; + Vector<bool> m_lastOnChangeSelection; + bool m_userDrivenChange; + + bool m_activeSelectionState; + int m_activeSelectionAnchorIndex; + int m_activeSelectionEndIndex; + Vector<bool> m_cachedStateForActiveSelection; + + bool m_recalcListItems; + Vector<Element*> m_listItems; + + // Instance variables for type-ahead find + UChar m_repeatingChar; + DOMTimeStamp m_lastCharTime; + String m_typedString; +}; + +SelectElement* toSelectElement(Element*); + +} + +#endif diff --git a/Source/WebCore/dom/SelectorNodeList.cpp b/Source/WebCore/dom/SelectorNodeList.cpp new file mode 100644 index 0000000..039a29f --- /dev/null +++ b/Source/WebCore/dom/SelectorNodeList.cpp @@ -0,0 +1,74 @@ +/* + * 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. + * 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 "CSSSelectorList.h" +#include "CSSStyleSelector.h" +#include "Document.h" +#include "Element.h" +#include "HTMLNames.h" +#include "StaticNodeList.h" + +namespace WebCore { + +using namespace HTMLNames; + +PassRefPtr<StaticNodeList> createSelectorNodeList(Node* rootNode, const CSSSelectorList& querySelectorList) +{ + Vector<RefPtr<Node> > nodes; + Document* document = rootNode->document(); + CSSSelector* onlySelector = querySelectorList.hasOneSelector() ? querySelectorList.first() : 0; + bool strictParsing = !document->inQuirksMode(); + + CSSStyleSelector::SelectorChecker selectorChecker(document, strictParsing); + + if (strictParsing && rootNode->inDocument() && onlySelector && onlySelector->m_match == CSSSelector::Id && !document->containsMultipleElementsWithId(onlySelector->m_value)) { + Element* element = document->getElementById(onlySelector->m_value); + if (element && (rootNode->isDocumentNode() || element->isDescendantOf(rootNode)) && selectorChecker.checkSelector(onlySelector, element)) + nodes.append(element); + } else { + for (Node* n = rootNode->firstChild(); n; n = n->traverseNextNode(rootNode)) { + if (n->isElementNode()) { + Element* element = static_cast<Element*>(n); + for (CSSSelector* selector = querySelectorList.first(); selector; selector = CSSSelectorList::next(selector)) { + if (selectorChecker.checkSelector(selector, element)) { + nodes.append(n); + break; + } + } + } + } + } + + return StaticNodeList::adopt(nodes); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/SelectorNodeList.h b/Source/WebCore/dom/SelectorNodeList.h new file mode 100644 index 0000000..8357034 --- /dev/null +++ b/Source/WebCore/dom/SelectorNodeList.h @@ -0,0 +1,44 @@ +/* + * 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. + * 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 <wtf/PassRefPtr.h> + +namespace WebCore { + + class CSSSelectorList; + class Node; + class StaticNodeList; + + PassRefPtr<StaticNodeList> createSelectorNodeList(Node* rootNode, const CSSSelectorList&); + +} // namespace WebCore + +#endif // SelectorNodeList_h diff --git a/Source/WebCore/dom/SpaceSplitString.cpp b/Source/WebCore/dom/SpaceSplitString.cpp new file mode 100644 index 0000000..4bd5b1b --- /dev/null +++ b/Source/WebCore/dom/SpaceSplitString.cpp @@ -0,0 +1,127 @@ +/* + * 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 "SpaceSplitString.h" + +#include "HTMLParserIdioms.h" +#include <wtf/ASCIICType.h> +#include <wtf/text/StringBuilder.h> + +using namespace WTF; + +namespace WebCore { + +static bool hasNonASCIIOrUpper(const String& string) +{ + const UChar* characters = string.characters(); + unsigned length = string.length(); + bool hasUpper = false; + UChar ored = 0; + for (unsigned i = 0; i < length; i++) { + UChar c = characters[i]; + hasUpper |= isASCIIUpper(c); + ored |= c; + } + return hasUpper || (ored & ~0x7F); +} + +void SpaceSplitStringData::createVector() +{ + ASSERT(!m_createdVector); + ASSERT(m_vector.isEmpty()); + + if (m_shouldFoldCase && hasNonASCIIOrUpper(m_string)) + m_string = m_string.foldCase(); + + const UChar* characters = m_string.characters(); + unsigned length = m_string.length(); + unsigned start = 0; + while (true) { + while (start < length && isHTMLSpace(characters[start])) + ++start; + if (start >= length) + break; + unsigned end = start + 1; + while (end < length && isNotHTMLSpace(characters[end])) + ++end; + + m_vector.append(AtomicString(characters + start, end - start)); + + start = end + 1; + } + + m_string = String(); + m_createdVector = true; +} + +bool SpaceSplitStringData::containsAll(SpaceSplitStringData& other) +{ + ensureVector(); + other.ensureVector(); + size_t thisSize = m_vector.size(); + size_t otherSize = other.m_vector.size(); + for (size_t i = 0; i < otherSize; ++i) { + const AtomicString& name = other.m_vector[i]; + size_t j; + for (j = 0; j < thisSize; ++j) { + if (m_vector[j] == name) + break; + } + if (j == thisSize) + return false; + } + return true; +} + +void SpaceSplitStringData::add(const AtomicString& string) +{ + if (contains(string)) + return; + + m_vector.append(string); +} + +void SpaceSplitStringData::remove(const AtomicString& string) +{ + ensureVector(); + + size_t position = 0; + while (position < m_vector.size()) { + if (m_vector[position] == string) + m_vector.remove(position); + else + ++position; + } +} + +void SpaceSplitString::add(const AtomicString& string) +{ + if (m_data) + m_data->add(string); +} + +void SpaceSplitString::remove(const AtomicString& string) +{ + if (m_data) + m_data->remove(string); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/SpaceSplitString.h b/Source/WebCore/dom/SpaceSplitString.h new file mode 100644 index 0000000..826e6bd --- /dev/null +++ b/Source/WebCore/dom/SpaceSplitString.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2007, 2008, 2010 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 SpaceSplitString_h +#define SpaceSplitString_h + +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + + class SpaceSplitStringData : public Noncopyable { + public: + SpaceSplitStringData(const String& string, bool shouldFoldCase) + : m_string(string), m_shouldFoldCase(shouldFoldCase), m_createdVector(false) + { + } + + bool contains(const AtomicString& string) + { + ensureVector(); + size_t size = m_vector.size(); + for (size_t i = 0; i < size; ++i) { + if (m_vector[i] == string) + return true; + } + return false; + } + + bool containsAll(SpaceSplitStringData&); + + void add(const AtomicString&); + void remove(const AtomicString&); + + size_t size() { ensureVector(); return m_vector.size(); } + const AtomicString& operator[](size_t i) { ensureVector(); ASSERT(i < size()); return m_vector[i]; } + + private: + void ensureVector() { if (!m_createdVector) createVector(); } + void createVector(); + + typedef Vector<AtomicString, 8> StringVector; + String m_string; + StringVector m_vector; + bool m_shouldFoldCase; + bool m_createdVector; + }; + + class SpaceSplitString { + public: + SpaceSplitString() { } + SpaceSplitString(const String& string, bool shouldFoldCase) : m_data(adoptPtr(new SpaceSplitStringData(string, shouldFoldCase))) { } + + void set(const String& string, bool shouldFoldCase) { m_data = adoptPtr(new SpaceSplitStringData(string, shouldFoldCase)); } + void clear() { m_data.clear(); } + + bool contains(const AtomicString& string) const { return m_data && m_data->contains(string); } + bool containsAll(const SpaceSplitString& names) const { return !names.m_data || (m_data && m_data->containsAll(*names.m_data)); } + void add(const AtomicString&); + void remove(const AtomicString&); + + size_t size() const { return m_data ? m_data->size() : 0; } + bool isNull() const { return !m_data; } + const AtomicString& operator[](size_t i) const { ASSERT(i < size()); return (*m_data)[i]; } + + private: + OwnPtr<SpaceSplitStringData> m_data; + }; + +} // namespace WebCore + +#endif // SpaceSplitString_h diff --git a/Source/WebCore/dom/StaticHashSetNodeList.cpp b/Source/WebCore/dom/StaticHashSetNodeList.cpp new file mode 100644 index 0000000..6c8c2db --- /dev/null +++ b/Source/WebCore/dom/StaticHashSetNodeList.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2010 Antonio Gomes <tonikitoo@webkit.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. + * 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 "StaticHashSetNodeList.h" + +#include "Element.h" + +namespace WebCore { + +StaticHashSetNodeList::StaticHashSetNodeList(ListHashSet<RefPtr<Node> >& nodes) +{ + m_nodes.swap(nodes); +} + +StaticHashSetNodeList::StaticHashSetNodeList() +{ +} + +StaticHashSetNodeList::~StaticHashSetNodeList() +{ +} + +unsigned StaticHashSetNodeList::length() const +{ + return m_nodes.size(); +} + +Node* StaticHashSetNodeList::item(unsigned index) const +{ + if (index < (unsigned) m_nodes.size()) { + ListHashSet<RefPtr<Node> >::const_iterator it = m_nodes.begin(); + for (unsigned count = 0; count < index; ++it, ++count) { } + return (*it).get(); + } + + return 0; +} + +Node* StaticHashSetNodeList::itemWithName(const AtomicString& elementId) const +{ + ListHashSet<RefPtr<Node> >::const_iterator it = m_nodes.begin(); + ListHashSet<RefPtr<Node> >::const_iterator end = m_nodes.end(); + for ( ; it != end ; ++it) { + Node* node = (*it).get(); + if (node->hasID() && static_cast<Element*>(node)->getIdAttribute() == elementId) + return node; + } + + return 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/StaticHashSetNodeList.h b/Source/WebCore/dom/StaticHashSetNodeList.h new file mode 100644 index 0000000..e60bf44 --- /dev/null +++ b/Source/WebCore/dom/StaticHashSetNodeList.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2010 Antonio Gomes <tonikitoo@webkit.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. + * 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 StaticHashSetNodeList_h +#define StaticHashSetNodeList_h + +#include "NodeList.h" +#include <wtf/ListHashSet.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Node; + +class StaticHashSetNodeList : public NodeList { +public: + StaticHashSetNodeList(); + ~StaticHashSetNodeList(); + + // Adopts the contents of the nodes ListHashSet. + static PassRefPtr<StaticHashSetNodeList> adopt(const ListHashSet<RefPtr<Node> >& nodes) + { + return adopt(const_cast<ListHashSet<RefPtr<Node> >&>(nodes)); + } + + static PassRefPtr<StaticHashSetNodeList> adopt(ListHashSet<RefPtr<Node> >& nodes) + { + return adoptRef(new StaticHashSetNodeList(nodes)); + } + + virtual unsigned length() const; + virtual Node* item(unsigned index) const; + virtual Node* itemWithName(const AtomicString&) const; + +private: + StaticHashSetNodeList(ListHashSet<RefPtr<Node> >& nodes); + + ListHashSet<RefPtr<Node> > m_nodes; +}; + +} // namespace WebCore + +#endif // StaticHashSetNodeList_h diff --git a/Source/WebCore/dom/StaticNodeList.cpp b/Source/WebCore/dom/StaticNodeList.cpp new file mode 100644 index 0000000..0a19a05 --- /dev/null +++ b/Source/WebCore/dom/StaticNodeList.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007, 2010 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 "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(); + // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution. + if (node->hasID() && static_cast<Element*>(node)->getIdAttribute() == elementId) + return node; + } + + return 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/StaticNodeList.h b/Source/WebCore/dom/StaticNodeList.h new file mode 100644 index 0000000..60fca19 --- /dev/null +++ b/Source/WebCore/dom/StaticNodeList.h @@ -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. + * 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/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class Node; + + class StaticNodeList : public NodeList { + public: + // Adopts the contents of the nodes vector. + static PassRefPtr<StaticNodeList> adopt(Vector<RefPtr<Node> >& nodes) + { + return adoptRef(new StaticNodeList(nodes)); + } + + virtual unsigned length() const; + virtual Node* item(unsigned index) const; + virtual Node* itemWithName(const AtomicString&) const; + + private: + StaticNodeList(Vector<RefPtr<Node> >& nodes) + { + m_nodes.swap(nodes); + } + Vector<RefPtr<Node> > m_nodes; + }; + +} // namespace WebCore + +#endif // StaticNodeList_h diff --git a/Source/WebCore/dom/StyleElement.cpp b/Source/WebCore/dom/StyleElement.cpp new file mode 100644 index 0000000..e9878a6 --- /dev/null +++ b/Source/WebCore/dom/StyleElement.cpp @@ -0,0 +1,172 @@ +/* + * 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 "Attribute.h" +#include "Document.h" +#include "Element.h" +#include "MediaList.h" +#include "MediaQueryEvaluator.h" +#include "ScriptableDocumentParser.h" + +namespace WebCore { + +StyleElement::StyleElement(Document* document, bool createdByParser) + : m_createdByParser(createdByParser) + , m_loading(false) + , m_startLineNumber(0) +{ + if (createdByParser && document && document->scriptableDocumentParser()) + m_startLineNumber = document->scriptableDocumentParser()->lineNumber(); +} + +StyleElement::~StyleElement() +{ +} + +void StyleElement::insertedIntoDocument(Document* document, Element* element) +{ + ASSERT(document); + ASSERT(element); + document->addStyleSheetCandidateNode(element, m_createdByParser); + if (m_createdByParser) + return; + + process(element); +} + +void StyleElement::removedFromDocument(Document* document, Element* element) +{ + ASSERT(document); + ASSERT(element); + document->removeStyleSheetCandidateNode(element); + + if (m_sheet) { + ASSERT(m_sheet->ownerNode() == element); + m_sheet->clearOwnerNode(); + m_sheet = 0; + } + + // If we're in document teardown, then we don't need to do any notification of our sheet's removal. + if (document->renderer()) + document->styleSelectorChanged(DeferRecalcStyle); +} + +void StyleElement::childrenChanged(Element* element) +{ + ASSERT(element); + if (m_createdByParser) + return; + + process(element); +} + +void StyleElement::finishParsingChildren(Element* element) +{ + ASSERT(element); + process(element); + m_createdByParser = false; +} + +void StyleElement::process(Element* e) +{ + if (!e || !e->inDocument()) + return; + + unsigned resultLength = 0; + for (Node* c = e->firstChild(); c; c = c->nextSibling()) { + Node::NodeType nodeType = c->nodeType(); + if (nodeType == Node::TEXT_NODE || nodeType == Node::CDATA_SECTION_NODE || nodeType == Node::COMMENT_NODE) { + unsigned length = c->nodeValue().length(); + if (length > std::numeric_limits<unsigned>::max() - resultLength) + CRASH(); + resultLength += length; + } + } + UChar* text; + String sheetText = String::createUninitialized(resultLength, text); + + UChar* p = text; + for (Node* c = e->firstChild(); c; c = c->nextSibling()) { + Node::NodeType nodeType = c->nodeType(); + if (nodeType == Node::TEXT_NODE || nodeType == Node::CDATA_SECTION_NODE || nodeType == Node::COMMENT_NODE) { + String nodeValue = c->nodeValue(); + unsigned nodeLength = nodeValue.length(); + memcpy(p, nodeValue.characters(), nodeLength * sizeof(UChar)); + p += nodeLength; + } + } + ASSERT(p == text + resultLength); + + createSheet(e, m_startLineNumber, sheetText); +} + +void StyleElement::createSheet(Element* e, int startLineNumber, const String& text) +{ + ASSERT(e); + ASSERT(e->inDocument()); + Document* document = e->document(); + if (m_sheet) { + if (m_sheet->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 = MediaList::create(media(), e->isHTMLElement()); + MediaQueryEvaluator screenEval("screen", true); + MediaQueryEvaluator printEval("print", true); + if (screenEval.eval(mediaList.get()) || printEval.eval(mediaList.get())) { + document->addPendingSheet(); + m_loading = true; + m_sheet = CSSStyleSheet::create(e, String(), KURL(), document->inputEncoding()); + m_sheet->parseStringAtLine(text, !document->inQuirksMode(), startLineNumber); + m_sheet->setMedia(mediaList.get()); + m_sheet->setTitle(e->title()); + m_loading = false; + } + } + + if (m_sheet) + m_sheet->checkLoaded(); +} + +bool StyleElement::isLoading() const +{ + if (m_loading) + return true; + return m_sheet ? m_sheet->isLoading() : false; +} + +bool StyleElement::sheetLoaded(Document* document) +{ + ASSERT(document); + if (isLoading()) + return false; + + document->removePendingSheet(); + return true; +} + +} diff --git a/Source/WebCore/dom/StyleElement.h b/Source/WebCore/dom/StyleElement.h new file mode 100644 index 0000000..05c07c5 --- /dev/null +++ b/Source/WebCore/dom/StyleElement.h @@ -0,0 +1,63 @@ +/* + * 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 Document; +class Element; + +class StyleElement { +public: + StyleElement(Document*, bool createdByParser); + virtual ~StyleElement(); + +protected: + virtual const AtomicString& type() const = 0; + virtual const AtomicString& media() const = 0; + + StyleSheet* sheet() { return m_sheet.get(); } + + bool isLoading() const; + bool sheetLoaded(Document*); + + void insertedIntoDocument(Document*, Element*); + void removedFromDocument(Document*, Element*); + void childrenChanged(Element*); + void finishParsingChildren(Element*); + + RefPtr<CSSStyleSheet> m_sheet; + +private: + void createSheet(Element*, int startLineNumber, const String& text = String()); + void process(Element*); + + bool m_createdByParser; + bool m_loading; + int m_startLineNumber; +}; + +} + +#endif diff --git a/Source/WebCore/dom/StyledElement.cpp b/Source/WebCore/dom/StyledElement.cpp new file mode 100644 index 0000000..9a7c9c8 --- /dev/null +++ b/Source/WebCore/dom/StyledElement.cpp @@ -0,0 +1,458 @@ +/* + * 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, 2008, 2010 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 "StyledElement.h" + +#include "Attribute.h" +#include "ClassList.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "CSSValueKeywords.h" +#include "DOMTokenList.h" +#include "Document.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include <wtf/HashFunctions.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 void constructDeletedValue(MappedAttributeKey& slot) { slot.type = eLastEntry; } + static bool isDeletedValue(const MappedAttributeKey& value) { return value.type == 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())); +} + +CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry type, const QualifiedName& name, const AtomicString& value) +{ + if (!mappedAttributeDecls) + return 0; + return mappedAttributeDecls->get(MappedAttributeKey(type, name.localName().impl(), 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::setMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& name, const AtomicString& value, CSSMappedAttributeDeclaration* decl) +{ + if (!mappedAttributeDecls) + mappedAttributeDecls = new MappedAttributeDecls; + mappedAttributeDecls->set(MappedAttributeKey(entryType, name.localName().impl(), 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::updateStyleAttribute() const +{ + ASSERT(!isStyleAttributeValid()); + setIsStyleAttributeValid(); + setIsSynchronizingStyleAttribute(); + if (m_inlineStyleDecl) + const_cast<StyledElement*>(this)->setAttribute(styleAttr, m_inlineStyleDecl->cssText()); + clearIsSynchronizingStyleAttribute(); +} + +StyledElement::~StyledElement() +{ + destroyInlineStyleDecl(); +} + +PassRefPtr<Attribute> StyledElement::createAttribute(const QualifiedName& name, const AtomicString& value) +{ + return Attribute::createMapped(name, value); +} + +void StyledElement::createInlineStyleDecl() +{ + m_inlineStyleDecl = CSSMutableStyleDeclaration::create(); + m_inlineStyleDecl->setParent(document()->elementSheet()); + m_inlineStyleDecl->setNode(this); + m_inlineStyleDecl->setStrictParsing(isHTMLElement() && !document()->inQuirksMode()); +} + +void StyledElement::destroyInlineStyleDecl() +{ + if (m_inlineStyleDecl) { + m_inlineStyleDecl->setNode(0); + m_inlineStyleDecl->setParent(0); + m_inlineStyleDecl = 0; + } +} + +void StyledElement::attributeChanged(Attribute* attr, bool preserveDecls) +{ + if (!attr->isMappedAttribute()) { + Element::attributeChanged(attr, preserveDecls); + return; + } + + if (attr->decl() && !preserveDecls) { + attr->setDecl(0); + setNeedsStyleRecalc(); + if (attributeMap()) + attributeMap()->declRemoved(); + } + + bool checkDecl = true; + MappedAttributeEntry entry; + bool needToParse = mapToEntry(attr->name(), entry); + if (preserveDecls) { + if (attr->decl()) { + setNeedsStyleRecalc(); + if (attributeMap()) + attributeMap()->declAdded(); + checkDecl = false; + } + } else if (!attr->isNull() && entry != eNone) { + CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr); + if (decl) { + attr->setDecl(decl); + setNeedsStyleRecalc(); + if (attributeMap()) + attributeMap()->declAdded(); + checkDecl = false; + } else + needToParse = true; + } + + // parseMappedAttribute() might create a CSSMappedAttributeDeclaration on the attribute. + // Normally we would be concerned about reseting the parent of those declarations in StyledElement::didMoveToNewOwnerDocument(). + // But currently we always clear its parent and node below when adding it to the decl table. + // If that changes for some reason moving between documents will be buggy. + // webarchive/adopt-attribute-styled-node-webarchive.html should catch any resulting crashes. + if (needToParse) + parseMappedAttribute(attr); + + if (entry == eNone) + recalcStyleIfNeededAfterAttributeChanged(attr); + + if (checkDecl && attr->decl()) { + // Add the decl to the table in the appropriate spot. + setMappedAttributeDecl(entry, attr, attr->decl()); + attr->decl()->setMappedState(entry, attr->name(), attr->value()); + attr->decl()->setParent(0); + attr->decl()->setNode(0); + if (attributeMap()) + attributeMap()->declAdded(); + } + + updateAfterAttributeChanged(attr); +} + +bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + result = eNone; + if (attrName == styleAttr) + return !isSynchronizingStyleAttribute(); + return true; +} + +void StyledElement::classAttributeChanged(const AtomicString& newClassString) +{ + const UChar* characters = newClassString.characters(); + unsigned length = newClassString.length(); + unsigned i; + for (i = 0; i < length; ++i) { + if (isNotHTMLSpace(characters[i])) + break; + } + bool hasClass = i < length; + setHasClass(hasClass); + if (hasClass) { + attributes()->setClass(newClassString); + if (DOMTokenList* classList = optionalClassList()) + static_cast<ClassList*>(classList)->reset(newClassString); + } else if (attributeMap()) + attributeMap()->clearClass(); + setNeedsStyleRecalc(); + dispatchSubtreeModifiedEvent(); +} + +void StyledElement::parseMappedAttribute(Attribute* attr) +{ + if (isIdAttributeName(attr->name())) { + setHasID(!attr->isNull()); + if (attributeMap()) { + if (attr->isNull()) + attributeMap()->setIdForStyleResolution(nullAtom); + else if (document()->inQuirksMode()) + attributeMap()->setIdForStyleResolution(attr->value().lower()); + else + attributeMap()->setIdForStyleResolution(attr->value()); + } + setNeedsStyleRecalc(); + } else if (attr->name() == classAttr) + classAttributeChanged(attr->value()); + else if (attr->name() == styleAttr) { + if (attr->isNull()) + destroyInlineStyleDecl(); + else + getInlineStyleDecl()->parseDeclaration(attr->value()); + setIsStyleAttributeValid(); + setNeedsStyleRecalc(); + } +} + +CSSMutableStyleDeclaration* StyledElement::getInlineStyleDecl() +{ + if (!m_inlineStyleDecl) + createInlineStyleDecl(); + return m_inlineStyleDecl.get(); +} + +CSSStyleDeclaration* StyledElement::style() +{ + return getInlineStyleDecl(); +} + +void StyledElement::addCSSProperty(Attribute* attribute, int id, const String &value) +{ + if (!attribute->decl()) + createMappedDecl(attribute); + attribute->decl()->setProperty(id, value, false); +} + +void StyledElement::addCSSProperty(Attribute* attribute, int id, int value) +{ + if (!attribute->decl()) + createMappedDecl(attribute); + attribute->decl()->setProperty(id, value, false); +} + +void StyledElement::addCSSImageProperty(Attribute* attribute, int id, const String& url) +{ + if (!attribute->decl()) + createMappedDecl(attribute); + attribute->decl()->setImageProperty(id, url, false); +} + +void StyledElement::addCSSLength(Attribute* 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(Attribute* 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 everything 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 (!equalIgnoringCase(color, "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) { + colors[component] <<= 4; + if (isASCIIHexDigit(color[pos])) { + colors[component] += toASCIIHexValue(color[pos]); + 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; + + color = String::format("#%02x%02x%02x", colors[0], colors[1], colors[2]); + if (attr->decl()->setProperty(id, color, false)) + return; + } + } + attr->decl()->setProperty(id, CSSValueBlack, false); +} + +void StyledElement::createMappedDecl(Attribute* attr) +{ + RefPtr<CSSMappedAttributeDeclaration> decl = CSSMappedAttributeDeclaration::create(); + attr->setDecl(decl); + decl->setParent(document()->elementSheet()); + decl->setNode(this); + decl->setStrictParsing(false); // Mapped attributes are just always quirky. +} + +unsigned MappedAttributeHash::hash(const MappedAttributeKey& key) +{ + COMPILE_ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8, key_name_size); + COMPILE_ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8, key_value_size); + + WTF::StringHasher hasher; + const UChar* data; + + data = reinterpret_cast<const UChar*>(&key.name); + hasher.addCharacters(data[0], data[1]); + if (sizeof(key.name) == 8) + hasher.addCharacters(data[2], data[3]); + + data = reinterpret_cast<const UChar*>(&key.value); + hasher.addCharacters(data[0], data[1]); + if (sizeof(key.value) == 8) + hasher.addCharacters(data[2], data[3]); + + return hasher.hash(); +} + +void StyledElement::copyNonAttributeProperties(const Element *sourceElement) +{ + const StyledElement* source = static_cast<const StyledElement*>(sourceElement); + if (!source->m_inlineStyleDecl) + return; + + *getInlineStyleDecl() = *source->m_inlineStyleDecl; + setIsStyleAttributeValid(source->isStyleAttributeValid()); + setIsSynchronizingStyleAttribute(source->isSynchronizingStyleAttribute()); + + Element::copyNonAttributeProperties(sourceElement); +} + +void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + if (CSSMutableStyleDeclaration* style = inlineStyleDecl()) + style->addSubresourceStyleURLs(urls); +} + + +void StyledElement::didMoveToNewOwnerDocument() +{ + if (m_inlineStyleDecl) + m_inlineStyleDecl->setParent(document()->elementSheet()); + + Element::didMoveToNewOwnerDocument(); +} + +} diff --git a/Source/WebCore/dom/StyledElement.h b/Source/WebCore/dom/StyledElement.h new file mode 100644 index 0000000..8040dbf --- /dev/null +++ b/Source/WebCore/dom/StyledElement.h @@ -0,0 +1,113 @@ +/* + * 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, 2009, 2010 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 "CSSMutableStyleDeclaration.h" +#include "Element.h" +#include "MappedAttributeEntry.h" + +namespace WebCore { + +class Attribute; +class CSSMappedAttributeDeclaration; + +class StyledElement : public Element { +public: + virtual ~StyledElement(); + + bool hasMappedAttributes() const { return attributeMap() && attributeMap()->hasMappedAttributes(); } + bool isMappedAttribute(const QualifiedName& name) const { MappedAttributeEntry res = eNone; mapToEntry(name, res); return res != eNone; } + + void addCSSLength(Attribute*, int id, const String& value); + void addCSSProperty(Attribute*, int id, const String& value); + void addCSSProperty(Attribute*, int id, int value); + void addCSSImageProperty(Attribute*, int propertyID, const String& url); + void addCSSColor(Attribute*, int id, const String& color); + + static CSSMappedAttributeDeclaration* getMappedAttributeDecl(MappedAttributeEntry, const QualifiedName& name, const AtomicString& value); + static void setMappedAttributeDecl(MappedAttributeEntry, const QualifiedName& name, const AtomicString& value, CSSMappedAttributeDeclaration*); + static void removeMappedAttributeDecl(MappedAttributeEntry, const QualifiedName& name, const AtomicString& value); + + static CSSMappedAttributeDeclaration* getMappedAttributeDecl(MappedAttributeEntry, Attribute*); + static void setMappedAttributeDecl(MappedAttributeEntry, Attribute*, CSSMappedAttributeDeclaration*); + + CSSMutableStyleDeclaration* inlineStyleDecl() const { return m_inlineStyleDecl.get(); } + virtual bool canHaveAdditionalAttributeStyleDecls() const { return false; } + virtual void additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>&) { } + CSSMutableStyleDeclaration* getInlineStyleDecl(); + CSSStyleDeclaration* style(); + void invalidateStyleAttribute(); + + const SpaceSplitString& classNames() const; + + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; + + virtual PassRefPtr<Attribute> createAttribute(const QualifiedName&, const AtomicString& value); + +protected: + StyledElement(const QualifiedName& name, Document* document, ConstructionType type) + : Element(name, document, type) + { + } + + virtual void attributeChanged(Attribute*, bool preserveDecls = false); + virtual void parseMappedAttribute(Attribute*); + virtual void copyNonAttributeProperties(const Element*); + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + // classAttributeChanged() exists to share code between + // parseMappedAttribute (called via setAttribute()) and + // svgAttributeChanged (called when element.className.baseValue is set) + void classAttributeChanged(const AtomicString& newClassString); + + virtual void didMoveToNewOwnerDocument(); + +private: + void createMappedDecl(Attribute*); + + void createInlineStyleDecl(); + void destroyInlineStyleDecl(); + virtual void updateStyleAttribute() const; + + RefPtr<CSSMutableStyleDeclaration> m_inlineStyleDecl; +}; + +inline const SpaceSplitString& StyledElement::classNames() const +{ + ASSERT(hasClass()); + ASSERT(attributeMap()); + return attributeMap()->classNames(); +} + +inline void StyledElement::invalidateStyleAttribute() +{ + clearIsStyleAttributeValid(); +} + +} //namespace + +#endif diff --git a/Source/WebCore/dom/TagNodeList.cpp b/Source/WebCore/dom/TagNodeList.cpp new file mode 100644 index 0000000..4914e09 --- /dev/null +++ b/Source/WebCore/dom/TagNodeList.cpp @@ -0,0 +1,53 @@ +/* + * 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) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * 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) + , m_namespaceURI(namespaceURI) + , m_localName(localName) +{ + ASSERT(m_namespaceURI.isNull() || !m_namespaceURI.isEmpty()); +} + +TagNodeList::~TagNodeList() +{ + m_rootNode->removeCachedTagNodeList(this, QualifiedName(nullAtom, m_localName, m_namespaceURI)); +} + +bool TagNodeList::nodeMatches(Element* testNode) const +{ + if (m_namespaceURI != starAtom && m_namespaceURI != testNode->namespaceURI()) + return false; + + return m_localName == starAtom || m_localName == testNode->localName(); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/TagNodeList.h b/Source/WebCore/dom/TagNodeList.h new file mode 100644 index 0000000..9053b53 --- /dev/null +++ b/Source/WebCore/dom/TagNodeList.h @@ -0,0 +1,53 @@ +/* + * 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) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "DynamicNodeList.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + + // NodeList that limits to a particular tag. + class TagNodeList : public DynamicNodeList { + public: + static PassRefPtr<TagNodeList> create(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName) + { + return adoptRef(new TagNodeList(rootNode, namespaceURI, localName)); + } + + virtual ~TagNodeList(); + + private: + TagNodeList(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName); + + virtual bool nodeMatches(Element*) const; + + AtomicString m_namespaceURI; + AtomicString m_localName; + }; + +} // namespace WebCore + +#endif // TagNodeList_h diff --git a/Source/WebCore/dom/Text.cpp b/Source/WebCore/dom/Text.cpp new file mode 100644 index 0000000..47c532e --- /dev/null +++ b/Source/WebCore/dom/Text.cpp @@ -0,0 +1,359 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 "ExceptionCode.h" +#include "RenderText.h" +#include "TextBreakIterator.h" +#include <wtf/text/CString.h> + +#if ENABLE(SVG) +#include "RenderSVGInlineText.h" +#include "SVGNames.h" +#endif + +#if ENABLE(WML) +#include "WMLDocument.h" +#include "WMLVariables.h" +#endif + +using namespace std; + +namespace WebCore { + +PassRefPtr<Text> Text::create(Document* document, const String& data) +{ + return adoptRef(new Text(document, data)); +} + +PassRefPtr<Text> Text::splitText(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 > length()) { + ec = INDEX_SIZE_ERR; + return 0; + } + + RefPtr<StringImpl> oldStr = dataImpl(); + RefPtr<Text> newText = virtualCreate(oldStr->substring(offset)); + setDataImpl(oldStr->substring(0, offset)); + + dispatchModifiedEvent(oldStr.get()); + + if (parentNode()) + parentNode()->insertBefore(newText.get(), nextSibling(), ec); + if (ec) + return 0; + + if (parentNode()) + document()->textNodeSplit(this); + + if (renderer()) + toRenderText(renderer())->setTextWithOffset(dataImpl(), 0, oldStr->length()); + + 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); + + Node* onePastEndText = endText->nextSibling(); + unsigned resultLength = 0; + 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(); + if (std::numeric_limits<unsigned>::max() - data.length() < resultLength) + CRASH(); + resultLength += data.length(); + } + UChar* resultData; + String result = String::createUninitialized(resultLength, resultData); + UChar* p = resultData; + 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(); + unsigned dataLength = data.length(); + memcpy(p, data.characters(), dataLength * sizeof(UChar)); + p += dataLength; + } + ASSERT(p == resultData + resultLength); + + return result; +} + +PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&) +{ + // Remove all adjacent text nodes, and replace the contents of this one. + + // 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 + ContainerNode* 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 create(document(), 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->isRenderInline()) { + // <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*) +{ +#if ENABLE(SVG) + Node* parentOrHost = parentOrHostNode(); + if (parentOrHost->isSVGElement() +#if ENABLE(SVG_FOREIGN_OBJECT) + && !parentOrHost->hasTagName(SVGNames::foreignObjectTag) +#endif + ) + return new (arena) RenderSVGInlineText(this, dataImpl()); +#endif + + return new (arena) RenderText(this, dataImpl()); +} + +void Text::attach() +{ +#if ENABLE(WML) + if (document()->isWMLDocument() && !containsOnlyWhitespace()) { + String text = data(); + ASSERT(!text.isEmpty()); + + text = substituteVariableReferences(text, document()); + + ExceptionCode code = 0; + setData(text, code); + ASSERT(!code); + } +#endif + + createRendererIfNeeded(); + CharacterData::attach(); +} + +void Text::recalcStyle(StyleChange change) +{ + if (change != NoChange && parentNode()) { + if (renderer()) + renderer()->setStyle(parentNode()->renderer()->style()); + } + if (needsStyleRecalc()) { + if (renderer()) { + if (renderer()->isText()) + toRenderText(renderer())->setText(dataImpl()); + } else { + if (attached()) + detach(); + attach(); + } + } + clearNeedsStyleRecalc(); +} + +bool Text::childTypeAllowed(NodeType) +{ + return false; +} + +PassRefPtr<Text> Text::virtualCreate(const String& data) +{ + return create(document(), data); +} + +PassRefPtr<Text> Text::createWithLengthLimit(Document* document, const String& data, unsigned& charsLeft, unsigned maxChars) +{ + unsigned dataLength = data.length(); + + if (charsLeft == dataLength && charsLeft <= maxChars) { + charsLeft = 0; + return create(document, data); + } + + unsigned start = dataLength - charsLeft; + unsigned end = start + min(charsLeft, maxChars); + + // Check we are not on an unbreakable boundary. + // Some text break iterator implementations work best if the passed buffer is as small as possible, + // see <https://bugs.webkit.org/show_bug.cgi?id=29092>. + // We need at least two characters look-ahead to account for UTF-16 surrogates. + if (end < dataLength) { + TextBreakIterator* it = characterBreakIterator(data.characters() + start, (end + 2 > dataLength) ? dataLength - start : end - start + 2); + if (!isTextBreak(it, end - start)) + end = textBreakPreceding(it, end - start) + start; + } + + // If we have maxChars of unbreakable characters the above could lead to + // an infinite loop. + // FIXME: It would be better to just have the old value of end before calling + // textBreakPreceding rather than this, because this exceeds the length limit. + if (end <= start) + end = dataLength; + + charsLeft = dataLength - end; + return create(document, data.substring(start, end - start)); +} + +#ifndef NDEBUG +void Text::formatForDebugger(char *buffer, unsigned length) const +{ + String result; + String s; + + s = nodeName(); + if (s.length() > 0) { + result += s; + } + + s = data(); + 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/Source/WebCore/dom/Text.h b/Source/WebCore/dom/Text.h new file mode 100644 index 0000000..45678ef --- /dev/null +++ b/Source/WebCore/dom/Text.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 { + +class Text : public CharacterData { +public: + static const unsigned defaultLengthLimit = 1 << 16; + + static PassRefPtr<Text> create(Document*, const String&); + static PassRefPtr<Text> createWithLengthLimit(Document*, const String&, unsigned& charsLeft, unsigned lengthLimit = defaultLengthLimit); + + 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&); + + virtual void attach(); + +protected: + Text(Document* document, const String& data) + : CharacterData(document, data, CreateText) + { + } + +private: + virtual String nodeName() const; + virtual NodeType nodeType() const; + virtual PassRefPtr<Node> cloneNode(bool deep); + virtual bool rendererIsNeeded(RenderStyle*); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void recalcStyle(StyleChange = NoChange); + virtual bool childTypeAllowed(NodeType); + + virtual PassRefPtr<Text> virtualCreate(const String&); + +#ifndef NDEBUG + virtual void formatForDebugger(char* buffer, unsigned length) const; +#endif +}; + +} // namespace WebCore + +#endif // Text_h diff --git a/Source/WebCore/dom/Text.idl b/Source/WebCore/dom/Text.idl new file mode 100644 index 0000000..116eab7 --- /dev/null +++ b/Source/WebCore/dom/Text.idl @@ -0,0 +1,35 @@ +/* + * 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 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/Source/WebCore/dom/TextEvent.cpp b/Source/WebCore/dom/TextEvent.cpp new file mode 100644 index 0000000..68ed4b2 --- /dev/null +++ b/Source/WebCore/dom/TextEvent.cpp @@ -0,0 +1,115 @@ +/* + * 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 { + +TextEvent::InputType TextEvent::selectInputType(bool isLineBreak, bool isBackTab) +{ + if (isLineBreak) + return TextEvent::InputTypeLineBreak; + if (isBackTab) + return TextEvent::InputTypeBackTab; + return TextEvent::InputTypeKeyboard; +} + +PassRefPtr<TextEvent> TextEvent::create() +{ + return adoptRef(new TextEvent); +} + +PassRefPtr<TextEvent> TextEvent::create(PassRefPtr<AbstractView> view, const String& data, TextEvent::InputType inputType) +{ + return adoptRef(new TextEvent(view, data, inputType)); +} + +PassRefPtr<TextEvent> TextEvent::createForPlainTextPaste(PassRefPtr<AbstractView> view, const String& data, bool shouldSmartReplace) +{ + return adoptRef(new TextEvent(view, data, 0, shouldSmartReplace, false)); +} + +PassRefPtr<TextEvent> TextEvent::createForFragmentPaste(PassRefPtr<AbstractView> view, PassRefPtr<DocumentFragment> data, bool shouldSmartReplace, bool shouldMatchStyle) +{ + return adoptRef(new TextEvent(view, "", data, shouldSmartReplace, shouldMatchStyle)); +} + +PassRefPtr<TextEvent> TextEvent::createForDrop(PassRefPtr<AbstractView> view, const String& data) +{ + return adoptRef(new TextEvent(view, data, TextEvent::InputTypeDrop)); +} + +TextEvent::TextEvent() + : m_inputType(TextEvent::InputTypeKeyboard) + , m_shouldSmartReplace(false) + , m_shouldMatchStyle(false) +{ +} + +TextEvent::TextEvent(PassRefPtr<AbstractView> view, const String& data, InputType inputType) + : UIEvent(eventNames().textInputEvent, true, true, view, 0) + , m_inputType(inputType) + , m_data(data) + , m_pastingFragment(0) + , m_shouldSmartReplace(false) + , m_shouldMatchStyle(false) +{ +} + +TextEvent::TextEvent(PassRefPtr<AbstractView> view, const String& data, PassRefPtr<DocumentFragment> pastingFragment, + bool shouldSmartReplace, bool shouldMatchStyle) + : UIEvent(eventNames().textInputEvent, true, true, view, 0) + , m_inputType(TextEvent::InputTypePaste) + , m_data(data) + , m_pastingFragment(pastingFragment) + , m_shouldSmartReplace(shouldSmartReplace) + , m_shouldMatchStyle(shouldMatchStyle) +{ +} + +TextEvent::~TextEvent() +{ +} + +void TextEvent::initTextEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<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/Source/WebCore/dom/TextEvent.h b/Source/WebCore/dom/TextEvent.h new file mode 100644 index 0000000..f1d24e9 --- /dev/null +++ b/Source/WebCore/dom/TextEvent.h @@ -0,0 +1,86 @@ +/* + * 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 TextEvent_h +#define TextEvent_h + +#include "DocumentFragment.h" +#include "UIEvent.h" + +namespace WebCore { + + class TextEvent : public UIEvent { + public: + enum InputType { + InputTypeKeyboard, // any newline characters in the text are line breaks only, not paragraph separators. + InputTypeLineBreak, // any tab characters in the text are backtabs. + InputTypeBackTab, + InputTypePaste, + InputTypeDrop, + }; + + static InputType selectInputType(bool isLineBreak, bool isBackTab); + static PassRefPtr<TextEvent> create(); + static PassRefPtr<TextEvent> create(PassRefPtr<AbstractView> view, const String& data, InputType = InputTypeKeyboard); + static PassRefPtr<TextEvent> createForPlainTextPaste(PassRefPtr<AbstractView> view, const String& data, bool shouldSmartReplace); + static PassRefPtr<TextEvent> createForFragmentPaste(PassRefPtr<AbstractView> view, PassRefPtr<DocumentFragment> data, bool shouldSmartReplace, bool shouldMatchStyle); + static PassRefPtr<TextEvent> createForDrop(PassRefPtr<AbstractView> view, const String& data); + + virtual ~TextEvent(); + + void initTextEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, const String& data); + + String data() const { return m_data; } + + virtual bool isTextEvent() const; + + bool isLineBreak() const { return m_inputType == InputTypeLineBreak; } + bool isBackTab() const { return m_inputType == InputTypeBackTab; } + bool isPaste() const { return m_inputType == InputTypePaste; } + bool isDrop() const { return m_inputType == InputTypeDrop; } + + bool shouldSmartReplace() const { return m_shouldSmartReplace; } + bool shouldMatchStyle() const { return m_shouldMatchStyle; } + DocumentFragment* pastingFragment() const { return m_pastingFragment.get(); } + + private: + TextEvent(); + + TextEvent(PassRefPtr<AbstractView>, const String& data, InputType = InputTypeKeyboard); + TextEvent(PassRefPtr<AbstractView>, const String& data, PassRefPtr<DocumentFragment>, + bool shouldSmartReplace, bool shouldMatchStyle); + + InputType m_inputType; + String m_data; + + RefPtr<DocumentFragment> m_pastingFragment; + bool m_shouldSmartReplace; + bool m_shouldMatchStyle; + }; + +} // namespace WebCore + +#endif // TextEvent_h diff --git a/Source/WebCore/dom/TextEvent.idl b/Source/WebCore/dom/TextEvent.idl new file mode 100644 index 0000000..83b0e3b --- /dev/null +++ b/Source/WebCore/dom/TextEvent.idl @@ -0,0 +1,41 @@ +/* + * 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 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/Source/WebCore/dom/Touch.cpp b/Source/WebCore/dom/Touch.cpp new file mode 100644 index 0000000..fbb3fed --- /dev/null +++ b/Source/WebCore/dom/Touch.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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(TOUCH_EVENTS) + +#include "Touch.h" + +#include "FrameView.h" + +namespace WebCore { + +static int contentsX(Frame* frame) +{ + if (!frame) + return 0; + FrameView* frameView = frame->view(); + if (!frameView) + return 0; + return frameView->scrollX() / frame->pageZoomFactor(); +} + +static int contentsY(Frame* frame) +{ + if (!frame) + return 0; + FrameView* frameView = frame->view(); + if (!frameView) + return 0; + return frameView->scrollY() / frame->pageZoomFactor(); +} + +Touch::Touch(Frame* frame, EventTarget* target, unsigned identifier, int screenX, int screenY, int pageX, int pageY) + : m_target(target) + , m_identifier(identifier) + , m_clientX(pageX - contentsX(frame)) + , m_clientY(pageY - contentsY(frame)) + , m_screenX(screenX) + , m_screenY(screenY) + , m_pageX(pageX) + , m_pageY(pageY) +{ +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/dom/Touch.h b/Source/WebCore/dom/Touch.h new file mode 100644 index 0000000..54e85a5 --- /dev/null +++ b/Source/WebCore/dom/Touch.h @@ -0,0 +1,75 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 Touch_h +#define Touch_h + +#if ENABLE(TOUCH_EVENTS) + +#include "EventTarget.h" +#include "Frame.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Touch : public RefCounted<Touch> { +public: + static PassRefPtr<Touch> create(Frame* frame, EventTarget* target, + unsigned identifier, int screenX, int screenY, int pageX, int pageY) + { + return adoptRef(new Touch(frame, target, identifier, screenX, + screenY, pageX, pageY)); + } + + EventTarget* target() const { return m_target.get(); } + unsigned identifier() const { return m_identifier; } + int clientX() const { return m_clientX; } + int clientY() const { return m_clientY; } + int screenX() const { return m_screenX; } + int screenY() const { return m_screenY; } + int pageX() const { return m_pageX; } + int pageY() const { return m_pageY; } + +private: + Touch(Frame* frame, EventTarget* target, unsigned identifier, + int screenX, int screenY, int pageX, int pageY); + + RefPtr<EventTarget> m_target; + unsigned m_identifier; + int m_clientX; + int m_clientY; + int m_screenX; + int m_screenY; + int m_pageX; + int m_pageY; +}; + +} // namespace WebCore + +#endif // ENABLE(TOUCH_EVENTS) + +#endif /* Touch_h */ diff --git a/Source/WebCore/dom/Touch.idl b/Source/WebCore/dom/Touch.idl new file mode 100644 index 0000000..1afc63a --- /dev/null +++ b/Source/WebCore/dom/Touch.idl @@ -0,0 +1,40 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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=TOUCH_EVENTS + ] Touch { + readonly attribute long clientX; + readonly attribute long clientY; + readonly attribute long screenX; + readonly attribute long screenY; + readonly attribute long pageX; + readonly attribute long pageY; + readonly attribute EventTarget target; + readonly attribute unsigned long identifier; + }; +} diff --git a/Source/WebCore/dom/TouchEvent.cpp b/Source/WebCore/dom/TouchEvent.cpp new file mode 100644 index 0000000..88d3e6f --- /dev/null +++ b/Source/WebCore/dom/TouchEvent.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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(TOUCH_EVENTS) + +#include "TouchEvent.h" + +namespace WebCore { + +TouchEvent::TouchEvent() +{ +} + +TouchEvent::TouchEvent(TouchList* touches, TouchList* targetTouches, + TouchList* changedTouches, const AtomicString& type, + PassRefPtr<AbstractView> view, int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) + : MouseRelatedEvent(type, true, true, view, 0, screenX, screenY, pageX, pageY, + ctrlKey, altKey, shiftKey, metaKey) + , m_touches(touches) + , m_targetTouches(targetTouches) + , m_changedTouches(changedTouches) +{ +} + +TouchEvent::~TouchEvent() +{ +} + +void TouchEvent::initTouchEvent(TouchList* touches, TouchList* targetTouches, + TouchList* changedTouches, const AtomicString& type, + PassRefPtr<AbstractView> view, int screenX, int screenY, int clientX, int clientY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) +{ + if (dispatched()) + return; + + initUIEvent(type, true, true, view, 0); + + m_screenX = screenX; + m_screenY = screenY; + m_ctrlKey = ctrlKey; + m_altKey = altKey; + m_shiftKey = shiftKey; + m_metaKey = metaKey; + initCoordinates(clientX, clientY); +} + +} // namespace WebCore + +#endif // ENABLE(TOUCH_EVENTS) diff --git a/Source/WebCore/dom/TouchEvent.h b/Source/WebCore/dom/TouchEvent.h new file mode 100644 index 0000000..1514cf8 --- /dev/null +++ b/Source/WebCore/dom/TouchEvent.h @@ -0,0 +1,84 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 TouchEvent_h +#define TouchEvent_h + +#if ENABLE(TOUCH_EVENTS) + +#include "MouseRelatedEvent.h" +#include "TouchList.h" + +namespace WebCore { + +class TouchEvent : public MouseRelatedEvent { +public: + virtual ~TouchEvent(); + + static PassRefPtr<TouchEvent> create() + { + return adoptRef(new TouchEvent); + } + static PassRefPtr<TouchEvent> create(TouchList* touches, + TouchList* targetTouches, TouchList* changedTouches, + const AtomicString& type, PassRefPtr<AbstractView> view, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) + { + return adoptRef(new TouchEvent(touches, targetTouches, changedTouches, + type, view, screenX, screenY, pageX, pageY, + ctrlKey, altKey, shiftKey, metaKey)); + } + + void initTouchEvent(TouchList* touches, TouchList* targetTouches, + TouchList* changedTouches, const AtomicString& type, + PassRefPtr<AbstractView> view, int screenX, int screenY, + int clientX, int clientY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + + TouchList* touches() const { return m_touches.get(); } + TouchList* targetTouches() const { return m_targetTouches.get(); } + TouchList* changedTouches() const { return m_changedTouches.get(); } + +private: + TouchEvent(); + TouchEvent(TouchList* touches, TouchList* targetTouches, + TouchList* changedTouches, const AtomicString& type, + PassRefPtr<AbstractView>, int screenX, int screenY, int pageX, + int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + + virtual bool isTouchEvent() const { return true; } + + RefPtr<TouchList> m_touches; + RefPtr<TouchList> m_targetTouches; + RefPtr<TouchList> m_changedTouches; +}; + +} // namespace WebCore + +#endif // ENABLE(TOUCH_EVENTS) + +#endif // TouchEvent_h diff --git a/Source/WebCore/dom/TouchEvent.idl b/Source/WebCore/dom/TouchEvent.idl new file mode 100644 index 0000000..0e0d917 --- /dev/null +++ b/Source/WebCore/dom/TouchEvent.idl @@ -0,0 +1,53 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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=TOUCH_EVENTS + ] TouchEvent : UIEvent { + readonly attribute TouchList touches; + readonly attribute TouchList targetTouches; + readonly attribute TouchList changedTouches; + readonly attribute boolean ctrlKey; + readonly attribute boolean shiftKey; + readonly attribute boolean altKey; + readonly attribute boolean metaKey; + + void initTouchEvent(in TouchList touches, + in TouchList targetTouches, + in TouchList changedTouches, + in DOMString type, + 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); + }; +} diff --git a/Source/WebCore/dom/TouchList.cpp b/Source/WebCore/dom/TouchList.cpp new file mode 100644 index 0000000..4167e42 --- /dev/null +++ b/Source/WebCore/dom/TouchList.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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(TOUCH_EVENTS) + +#include "TouchList.h" + +namespace WebCore { + +Touch* TouchList::item(unsigned index) +{ + if (index >= m_values.size()) + return 0; + return m_values[index].get(); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/dom/TouchList.h b/Source/WebCore/dom/TouchList.h new file mode 100644 index 0000000..2546955 --- /dev/null +++ b/Source/WebCore/dom/TouchList.h @@ -0,0 +1,60 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 TouchList_h +#define TouchList_h + +#if ENABLE(TOUCH_EVENTS) + +#include "Touch.h" +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class TouchList : public RefCounted<TouchList> { +public: + static PassRefPtr<TouchList> create() + { + return adoptRef(new TouchList); + } + + unsigned length() const { return m_values.size(); } + + Touch* item(unsigned); + + void append(const PassRefPtr<Touch> touch) { m_values.append(touch); } + +private: + TouchList() {} + + Vector<RefPtr<Touch> > m_values; +}; + +} // namespace WebCore + +#endif // ENABLE(TOUCH_EVENTS) + +#endif /* TouchList_h */ diff --git a/Source/WebCore/dom/TouchList.idl b/Source/WebCore/dom/TouchList.idl new file mode 100644 index 0000000..ede7bf2 --- /dev/null +++ b/Source/WebCore/dom/TouchList.idl @@ -0,0 +1,36 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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=TOUCH_EVENTS, + HasIndexGetter + ] TouchList { + readonly attribute unsigned long length; + + Touch item(in unsigned long index); + }; +} diff --git a/Source/WebCore/dom/TransformSource.h b/Source/WebCore/dom/TransformSource.h new file mode 100644 index 0000000..f97afcf --- /dev/null +++ b/Source/WebCore/dom/TransformSource.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009 Jakub Wieczorek <faw217@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. + */ + +#ifndef TransformSource_h +#define TransformSource_h + +#if ENABLE(XSLT) + +#include "PlatformString.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +#if USE(QXMLQUERY) + typedef String PlatformTransformSource; +#else + typedef void* PlatformTransformSource; +#endif + + class TransformSource : public Noncopyable { + public: + TransformSource(const PlatformTransformSource& source); + ~TransformSource(); + + PlatformTransformSource platformSource() const { return m_source; } + + private: + PlatformTransformSource m_source; + }; + +} // namespace WebCore + +#endif + +#endif // TransformSource_h diff --git a/Source/WebCore/dom/TransformSourceLibxslt.cpp b/Source/WebCore/dom/TransformSourceLibxslt.cpp new file mode 100644 index 0000000..e058438 --- /dev/null +++ b/Source/WebCore/dom/TransformSourceLibxslt.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009 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" + +#if ENABLE(XSLT) +#include "TransformSource.h" + +#include <libxml/tree.h> + +namespace WebCore { + +TransformSource::TransformSource(const PlatformTransformSource& source) + : m_source(source) +{ +} + +TransformSource::~TransformSource() +{ + xmlFreeDoc((xmlDocPtr)m_source); +} + +} + +#endif diff --git a/Source/WebCore/dom/TransformSourceQt.cpp b/Source/WebCore/dom/TransformSourceQt.cpp new file mode 100644 index 0000000..074f2cb --- /dev/null +++ b/Source/WebCore/dom/TransformSourceQt.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009 Jakub Wieczorek <faw217@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. + */ + +#include "config.h" +#include "TransformSource.h" + +namespace WebCore { + +TransformSource::TransformSource(const PlatformTransformSource& source) + : m_source(source) +{ +} + +TransformSource::~TransformSource() +{ +} + +} diff --git a/Source/WebCore/dom/Traversal.cpp b/Source/WebCore/dom/Traversal.cpp new file mode 100644 index 0000000..5d56deb --- /dev/null +++ b/Source/WebCore/dom/Traversal.cpp @@ -0,0 +1,54 @@ +/* + * 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" + +namespace WebCore { + +Traversal::Traversal(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> nodeFilter, bool expandEntityReferences) + : m_root(rootNode) + , m_whatToShow(whatToShow) + , m_filter(nodeFilter) + , m_expandEntityReferences(expandEntityReferences) +{ +} + +short Traversal::acceptNode(ScriptState* state, Node* node) const +{ + // FIXME: To handle XML properly we would have to check m_expandEntityReferences. + + // The bit 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(state, node); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/Traversal.h b/Source/WebCore/dom/Traversal.h new file mode 100644 index 0000000..8b4f764 --- /dev/null +++ b/Source/WebCore/dom/Traversal.h @@ -0,0 +1,56 @@ +/* + * 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 "ScriptState.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + + class Node; + class NodeFilter; + + class Traversal { + public: + 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: + Traversal(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); + short acceptNode(ScriptState*, Node*) const; + + private: + RefPtr<Node> m_root; + unsigned m_whatToShow; + RefPtr<NodeFilter> m_filter; + bool m_expandEntityReferences; + }; + +} // namespace WebCore + +#endif // Traversal_h diff --git a/Source/WebCore/dom/TreeDepthLimit.h b/Source/WebCore/dom/TreeDepthLimit.h new file mode 100644 index 0000000..e78e093 --- /dev/null +++ b/Source/WebCore/dom/TreeDepthLimit.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 TreeDepthLimit_h +#define TreeDepthLimit_h + +#ifndef MAX_DOM_TREE_DEPTH +#define MAX_DOM_TREE_DEPTH 5000 +#endif +const unsigned maxDOMTreeDepth = MAX_DOM_TREE_DEPTH; + +#endif // TreeDepthLimit.h + diff --git a/Source/WebCore/dom/TreeWalker.cpp b/Source/WebCore/dom/TreeWalker.cpp new file mode 100644 index 0000000..d28603f --- /dev/null +++ b/Source/WebCore/dom/TreeWalker.cpp @@ -0,0 +1,279 @@ +/* + * 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 "ContainerNode.h" +#include "NodeFilter.h" +#include "ScriptState.h" +#include <wtf/PassRefPtr.h> + +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(ScriptState* state) +{ + RefPtr<Node> node = m_current; + while (node != root()) { + node = node->parentNode(); + if (!node) + return 0; + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return setCurrent(node.release()); + } + return 0; +} + +Node* TreeWalker::firstChild(ScriptState* state) +{ + for (RefPtr<Node> node = m_current->firstChild(); node; ) { + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) + 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; + } + ContainerNode* parent = node->parentNode(); + if (!parent || parent == root() || parent == m_current) + return 0; + node = parent; + } while (node); + } + return 0; +} + +Node* TreeWalker::lastChild(ScriptState* state) +{ + for (RefPtr<Node> node = m_current->lastChild(); node; ) { + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) + 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; + } + ContainerNode* parent = node->parentNode(); + if (!parent || parent == root() || parent == m_current) + return 0; + node = parent; + } while (node); + } + return 0; +} + +Node* TreeWalker::previousSibling(ScriptState* state) +{ + RefPtr<Node> node = m_current; + if (node == root()) + return 0; + while (1) { + for (RefPtr<Node> sibling = node->previousSibling(); sibling; ) { + short acceptNodeResult = acceptNode(state, sibling.get()); + if (state && state->hadException()) + return 0; + switch (acceptNodeResult) { + case NodeFilter::FILTER_ACCEPT: + m_current = sibling.release(); + return m_current.get(); + case NodeFilter::FILTER_SKIP: + if (sibling->lastChild()) { + sibling = sibling->lastChild(); + node = sibling; + continue; + } + break; + case NodeFilter::FILTER_REJECT: + break; + } + sibling = sibling->previousSibling(); + } + node = node->parentNode(); + if (!node || node == root()) + return 0; + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return 0; + } +} + +Node* TreeWalker::nextSibling(ScriptState* state) +{ + RefPtr<Node> node = m_current; + if (node == root()) + return 0; + while (1) { + for (RefPtr<Node> sibling = node->nextSibling(); sibling; ) { + short acceptNodeResult = acceptNode(state, sibling.get()); + if (state && state->hadException()) + 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(); + node = sibling; + continue; + } + break; + case NodeFilter::FILTER_REJECT: + break; + } + sibling = sibling->nextSibling(); + } + node = node->parentNode(); + if (!node || node == root()) + return 0; + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return 0; + } +} + +Node* TreeWalker::previousNode(ScriptState* state) +{ + RefPtr<Node> node = m_current; + while (node != root()) { + while (Node* previousSibling = node->previousSibling()) { + node = previousSibling; + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_REJECT) + continue; + while (Node* lastChild = node->lastChild()) { + node = lastChild; + acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_REJECT) + break; + } + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) { + m_current = node.release(); + return m_current.get(); + } + } + if (node == root()) + return 0; + ContainerNode* parent = node->parentNode(); + if (!parent) + return 0; + node = parent; + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) + return 0; + if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) + return setCurrent(node.release()); + } + return 0; +} + +Node* TreeWalker::nextNode(ScriptState* state) +{ + RefPtr<Node> node = m_current; +Children: + while (Node* firstChild = node->firstChild()) { + node = firstChild; + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) + 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(state, node.get()); + if (state && state->hadException()) + 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/Source/WebCore/dom/TreeWalker.h b/Source/WebCore/dom/TreeWalker.h new file mode 100644 index 0000000..88e59da --- /dev/null +++ b/Source/WebCore/dom/TreeWalker.h @@ -0,0 +1,75 @@ +/* + * 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 "NodeFilter.h" +#include "Traversal.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + typedef int ExceptionCode; + + class TreeWalker : public RefCounted<TreeWalker>, public Traversal { + public: + static PassRefPtr<TreeWalker> create(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) + { + return adoptRef(new TreeWalker(rootNode, whatToShow, filter, expandEntityReferences)); + } + + Node* currentNode() const { return m_current.get(); } + void setCurrentNode(PassRefPtr<Node>, ExceptionCode&); + + Node* parentNode(ScriptState*); + Node* firstChild(ScriptState*); + Node* lastChild(ScriptState*); + Node* previousSibling(ScriptState*); + Node* nextSibling(ScriptState*); + Node* previousNode(ScriptState*); + Node* nextNode(ScriptState*); + + // Do not call these functions. They are just scaffolding to support the Objective-C bindings. + // They operate in the main thread normal world, and they swallow JS exceptions. + Node* parentNode() { return parentNode(scriptStateFromNode(mainThreadNormalWorld(), m_current.get())); } + Node* firstChild() { return firstChild(scriptStateFromNode(mainThreadNormalWorld(), m_current.get())); } + Node* lastChild() { return lastChild(scriptStateFromNode(mainThreadNormalWorld(), m_current.get())); } + Node* previousSibling() { return previousSibling(scriptStateFromNode(mainThreadNormalWorld(), m_current.get())); } + Node* nextSibling() { return nextSibling(scriptStateFromNode(mainThreadNormalWorld(), m_current.get())); } + Node* previousNode() { return previousNode(scriptStateFromNode(mainThreadNormalWorld(), m_current.get())); } + Node* nextNode() { return nextNode(scriptStateFromNode(mainThreadNormalWorld(), m_current.get())); } + + private: + TreeWalker(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); + + Node* setCurrent(PassRefPtr<Node>); + + RefPtr<Node> m_current; + }; + +} // namespace WebCore + +#endif // TreeWalker_h diff --git a/Source/WebCore/dom/TreeWalker.idl b/Source/WebCore/dom/TreeWalker.idl new file mode 100644 index 0000000..890e315 --- /dev/null +++ b/Source/WebCore/dom/TreeWalker.idl @@ -0,0 +1,43 @@ +/* + * 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 NodeFilter filter; + readonly attribute boolean expandEntityReferences; + attribute Node currentNode + setter raises(DOMException); + + [CallWith=ScriptState] Node parentNode(); + [CallWith=ScriptState] Node firstChild(); + [CallWith=ScriptState] Node lastChild(); + [CallWith=ScriptState] Node previousSibling(); + [CallWith=ScriptState] Node nextSibling(); + [CallWith=ScriptState] Node previousNode(); + [CallWith=ScriptState] Node nextNode(); + }; + +} diff --git a/Source/WebCore/dom/UIEvent.cpp b/Source/WebCore/dom/UIEvent.cpp new file mode 100644 index 0000000..401015b --- /dev/null +++ b/Source/WebCore/dom/UIEvent.cpp @@ -0,0 +1,97 @@ +/* + * 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, 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 "UIEvent.h" + +#include "DOMWindow.h" + +namespace WebCore { + +UIEvent::UIEvent() + : m_detail(0) +{ +} + +UIEvent::UIEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg, PassRefPtr<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, PassRefPtr<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/Source/WebCore/dom/UIEvent.h b/Source/WebCore/dom/UIEvent.h new file mode 100644 index 0000000..8e330a5 --- /dev/null +++ b/Source/WebCore/dom/UIEvent.h @@ -0,0 +1,75 @@ +/* + * 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, 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 UIEvent_h +#define UIEvent_h + +#include "DOMWindow.h" +#include "Event.h" + +namespace WebCore { + + typedef DOMWindow AbstractView; + + class UIEvent : public Event { + public: + static PassRefPtr<UIEvent> create() + { + return adoptRef(new UIEvent); + } + static PassRefPtr<UIEvent> create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView> view, int detail) + { + return adoptRef(new UIEvent(type, canBubble, cancelable, view, detail)); + } + virtual ~UIEvent(); + + void initUIEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, 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; + + protected: + UIEvent(); + UIEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, int detail); + + private: + RefPtr<AbstractView> m_view; + int m_detail; + }; + +} // namespace WebCore + +#endif // UIEvent_h diff --git a/Source/WebCore/dom/UIEvent.idl b/Source/WebCore/dom/UIEvent.idl new file mode 100644 index 0000000..88dc108 --- /dev/null +++ b/Source/WebCore/dom/UIEvent.idl @@ -0,0 +1,43 @@ +/* + * 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 UIEvent : Event { + readonly attribute DOMWindow view; + readonly attribute long detail; + + [OldStyleObjC] void initUIEvent(in DOMString type, + in boolean canBubble, + in boolean cancelable, + in DOMWindow view, + in long detail); + + // extensions + 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/Source/WebCore/dom/UIEventWithKeyState.cpp b/Source/WebCore/dom/UIEventWithKeyState.cpp new file mode 100644 index 0000000..d757b60 --- /dev/null +++ b/Source/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/Source/WebCore/dom/UIEventWithKeyState.h b/Source/WebCore/dom/UIEventWithKeyState.h new file mode 100644 index 0000000..414161a --- /dev/null +++ b/Source/WebCore/dom/UIEventWithKeyState.h @@ -0,0 +1,68 @@ +/* + * 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, 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 UIEventWithKeyState_h +#define UIEventWithKeyState_h + +#include "UIEvent.h" + +namespace WebCore { + + class UIEventWithKeyState : public UIEvent { + public: + 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: + UIEventWithKeyState() + : m_ctrlKey(false) + , m_altKey(false) + , m_shiftKey(false) + , m_metaKey(false) + { + } + + UIEventWithKeyState(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<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) + { + } + + // 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/Source/WebCore/dom/UserGestureIndicator.cpp b/Source/WebCore/dom/UserGestureIndicator.cpp new file mode 100644 index 0000000..06a3831 --- /dev/null +++ b/Source/WebCore/dom/UserGestureIndicator.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 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. 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 INC. 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 "UserGestureIndicator.h" + +namespace WebCore { + +ProcessingUserGestureState UserGestureIndicator::s_processingUserGesture = PossiblyProcessingUserGesture; + +UserGestureIndicator::UserGestureIndicator(ProcessingUserGestureState state) + : m_previousValue(s_processingUserGesture) +{ + s_processingUserGesture = state; +} + +UserGestureIndicator::~UserGestureIndicator() +{ + s_processingUserGesture = m_previousValue; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/UserGestureIndicator.h b/Source/WebCore/dom/UserGestureIndicator.h new file mode 100644 index 0000000..17ea319 --- /dev/null +++ b/Source/WebCore/dom/UserGestureIndicator.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 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. 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 INC. 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 UserGestureIndicator_h +#define UserGestureIndicator_h + +#include <wtf/Noncopyable.h> + +namespace WebCore { + +enum ProcessingUserGestureState { + DefinitelyProcessingUserGesture, + PossiblyProcessingUserGesture, + DefinitelyNotProcessingUserGesture +}; + +class UserGestureIndicator : public Noncopyable { +public: + static bool processingUserGesture() { return s_processingUserGesture == DefinitelyProcessingUserGesture; } + static ProcessingUserGestureState getUserGestureState() { return s_processingUserGesture; } + + explicit UserGestureIndicator(ProcessingUserGestureState); + ~UserGestureIndicator(); + +private: + static ProcessingUserGestureState s_processingUserGesture; + ProcessingUserGestureState m_previousValue; +}; + +} // namespace WebCore + +#endif // UserGestureIndicator_h diff --git a/Source/WebCore/dom/UserTypingGestureIndicator.cpp b/Source/WebCore/dom/UserTypingGestureIndicator.cpp new file mode 100644 index 0000000..5120843 --- /dev/null +++ b/Source/WebCore/dom/UserTypingGestureIndicator.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 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. 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 INC. 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 "UserTypingGestureIndicator.h" + +#include "Frame.h" +#include "Node.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +static bool s_processingUserTypingGesture; +bool UserTypingGestureIndicator::processingUserTypingGesture() +{ + return s_processingUserTypingGesture; +} + +static RefPtr<Node>& focusedNode() +{ + DEFINE_STATIC_LOCAL(RefPtr<Node>, node, ()); + return node; +} + +Node* UserTypingGestureIndicator::focusedElementAtGestureStart() +{ + return focusedNode().get(); +} + +UserTypingGestureIndicator::UserTypingGestureIndicator(Frame* frame) + : m_previousProcessingUserTypingGesture(s_processingUserTypingGesture) + , m_previousFocusedNode(focusedNode()) +{ + s_processingUserTypingGesture = true; + focusedNode() = frame->document() ? frame->document()->focusedNode() : 0; +} + +UserTypingGestureIndicator::~UserTypingGestureIndicator() +{ + s_processingUserTypingGesture = m_previousProcessingUserTypingGesture; + focusedNode() = m_previousFocusedNode; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/UserTypingGestureIndicator.h b/Source/WebCore/dom/UserTypingGestureIndicator.h new file mode 100644 index 0000000..6c71079 --- /dev/null +++ b/Source/WebCore/dom/UserTypingGestureIndicator.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 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. 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 INC. 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 UserTypingGestureIndicator_h +#define UserTypingGestureIndicator_h + +#include <wtf/Noncopyable.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Frame; +class Node; + +class UserTypingGestureIndicator : public Noncopyable { +public: + static bool processingUserTypingGesture(); + static Node* focusedElementAtGestureStart(); + + explicit UserTypingGestureIndicator(Frame*); + ~UserTypingGestureIndicator(); + +private: + bool m_previousProcessingUserTypingGesture; + RefPtr<Node> m_previousFocusedNode; +}; + +} // namespace WebCore + +#endif // UserTypingGestureIndicator_h diff --git a/Source/WebCore/dom/ViewportArguments.cpp b/Source/WebCore/dom/ViewportArguments.cpp new file mode 100644 index 0000000..d3026e7 --- /dev/null +++ b/Source/WebCore/dom/ViewportArguments.cpp @@ -0,0 +1,371 @@ +/* + * 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. + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "ViewportArguments.h" + +#include "Chrome.h" +#include "Console.h" +#include "DOMWindow.h" +#include "Document.h" +#include "Frame.h" +#include "IntSize.h" +#include "Page.h" +#include "PlatformString.h" +#include "ScriptableDocumentParser.h" + +using namespace std; + +namespace WebCore { + +ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktopWidth, int deviceWidth, int deviceHeight, int deviceDPI, IntSize visibleViewport) +{ + ViewportAttributes result; + + float availableWidth = visibleViewport.width(); + float availableHeight = visibleViewport.height(); + + ASSERT(availableWidth > 0 && availableHeight > 0); + + switch (int(args.width)) { + case ViewportArguments::ValueDesktopWidth: + args.width = desktopWidth; + break; + case ViewportArguments::ValueDeviceWidth: + args.width = deviceWidth; + break; + case ViewportArguments::ValueDeviceHeight: + args.width = deviceHeight; + break; + } + + switch (int(args.height)) { + case ViewportArguments::ValueDesktopWidth: + args.height = desktopWidth; + break; + case ViewportArguments::ValueDeviceWidth: + args.height = deviceWidth; + break; + case ViewportArguments::ValueDeviceHeight: + args.height = deviceHeight; + break; + } + + switch (int(args.targetDensityDpi)) { + case ViewportArguments::ValueDeviceDPI: + args.targetDensityDpi = deviceDPI; + break; + case ViewportArguments::ValueLowDPI: + args.targetDensityDpi = 120; + break; + case ViewportArguments::ValueAuto: + case ViewportArguments::ValueMediumDPI: + args.targetDensityDpi = 160; + break; + case ViewportArguments::ValueHighDPI: + args.targetDensityDpi = 240; + break; + } + + result.devicePixelRatio = float(deviceDPI / args.targetDensityDpi); + + // Resolve non-'auto' width and height to pixel values. + if (result.devicePixelRatio != 1.0) { + availableWidth /= result.devicePixelRatio; + availableHeight /= result.devicePixelRatio; + deviceWidth /= result.devicePixelRatio; + deviceHeight /= result.devicePixelRatio; + + if (args.width != ViewportArguments::ValueAuto) + args.width /= result.devicePixelRatio; + if (args.height != ViewportArguments::ValueAuto) + args.height /= result.devicePixelRatio; + } + + // Clamp values to range defined by spec and resolve minimum-scale and maximum-scale values + if (args.width != ViewportArguments::ValueAuto) + args.width = min(float(10000), max(args.width, float(1))); + if (args.height != ViewportArguments::ValueAuto) + args.height = min(float(10000), max(args.height, float(1))); + + if (args.initialScale != ViewportArguments::ValueAuto) + args.initialScale = min(float(10), max(args.initialScale, float(0.1))); + if (args.minimumScale != ViewportArguments::ValueAuto) + args.minimumScale = min(float(10), max(args.minimumScale, float(0.1))); + if (args.maximumScale != ViewportArguments::ValueAuto) + args.maximumScale = min(float(10), max(args.maximumScale, float(0.1))); + + // Resolve minimum-scale and maximum-scale values according to spec. + if (args.minimumScale == ViewportArguments::ValueAuto) + result.minimumScale = float(0.25); + else + result.minimumScale = args.minimumScale; + + if (args.maximumScale == ViewportArguments::ValueAuto) { + result.maximumScale = float(5.0); + result.minimumScale = min(float(5.0), result.minimumScale); + } else + result.maximumScale = args.maximumScale; + result.maximumScale = max(result.minimumScale, result.maximumScale); + + // Resolve initial-scale value. + result.initialScale = args.initialScale; + if (result.initialScale == ViewportArguments::ValueAuto) { + result.initialScale = availableWidth / desktopWidth; + if (args.width != ViewportArguments::ValueAuto) + result.initialScale = availableWidth / args.width; + if (args.height != ViewportArguments::ValueAuto) { + // if 'auto', the initial-scale will be negative here and thus ignored. + result.initialScale = max(result.initialScale, availableHeight / args.height); + } + } + + // Constrain initial-scale value to minimum-scale/maximum-scale range. + result.initialScale = min(result.maximumScale, max(result.minimumScale, result.initialScale)); + + // Resolve width value. + float width; + if (args.width != ViewportArguments::ValueAuto) + width = args.width; + else { + if (args.initialScale == ViewportArguments::ValueAuto) + width = desktopWidth; + else if (args.height != ViewportArguments::ValueAuto) + width = args.height * (availableWidth / availableHeight); + else + width = availableWidth / result.initialScale; + } + + // Resolve height value. + float height; + if (args.height != ViewportArguments::ValueAuto) + height = args.height; + else + height = width * availableHeight / availableWidth; + + // Extend width and height to fill the visual viewport for the resolved initial-scale. + width = max(width, availableWidth / result.initialScale); + height = max(height, availableHeight / result.initialScale); + result.layoutSize.setWidth(width); + result.layoutSize.setHeight(height); + + // Update minimum scale factor, to never allow zooming out more than viewport + result.minimumScale = max(result.minimumScale, max(availableWidth / width, availableHeight / height)); + + result.userScalable = args.userScalable; + // Make maximum and minimum scale equal to the initial scale if user is not allowed to zoom in/out. + if (!args.userScalable) + result.maximumScale = result.minimumScale = result.initialScale; + + return result; +} + +static float findSizeValue(const String& keyString, const String& valueString, Document* document) +{ + // 1) Non-negative number values are translated to px lengths. + // 2) Negative number values are translated to auto. + // 3) device-width and device-height are used as keywords. + // 4) Other keywords and unknown values translate to 0.0. + + if (equalIgnoringCase(valueString, "desktop-width")) + return ViewportArguments::ValueDesktopWidth; + if (equalIgnoringCase(valueString, "device-width")) + return ViewportArguments::ValueDeviceWidth; + if (equalIgnoringCase(valueString, "device-height")) + return ViewportArguments::ValueDeviceHeight; + + bool ok; + float value = valueString.toFloat(&ok); + if (!ok) { + reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString); + return float(0.0); + } + + if (value < 0) + return ViewportArguments::ValueAuto; + + if (keyString == "width") + reportViewportWarning(document, DeviceWidthShouldBeUsedWarning, keyString); + else if (keyString == "height") + reportViewportWarning(document, DeviceHeightShouldBeUsedWarning, keyString); + + return value; +} + +static float findScaleValue(const String& keyString, const String& valueString, Document* document) +{ + // 1) Non-negative number values are translated to <number> values. + // 2) Negative number values are translated to auto. + // 3) yes is translated to 1.0. + // 4) device-width and device-height are translated to 10.0. + // 5) no and unknown values are translated to 0.0 + + if (equalIgnoringCase(valueString, "yes")) + return float(1.0); + if (equalIgnoringCase(valueString, "no")) + return float(0.0); + if (equalIgnoringCase(valueString, "desktop-width")) + return float(10.0); + if (equalIgnoringCase(valueString, "device-width")) + return float(10.0); + if (equalIgnoringCase(valueString, "device-height")) + return float(10.0); + + bool ok; + float value = valueString.toFloat(&ok); + if (!ok) { + reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString); + return float(0.0); + } + + if (value < 0) + return ViewportArguments::ValueAuto; + + if (value > 10.0) + reportViewportWarning(document, MaximumScaleTooLargeError, keyString); + + return value; +} + +static bool findUserScalableValue(const String& keyString, const String& valueString, Document* document) +{ + // yes and no are used as keywords. + // Numbers >= 1, numbers <= -1, device-width and device-height are mapped to yes. + // Numbers in the range <-1, 1>, and unknown values, are mapped to no. + + if (equalIgnoringCase(valueString, "yes")) + return true; + if (equalIgnoringCase(valueString, "no")) + return false; + if (equalIgnoringCase(valueString, "desktop-width")) + return true; + if (equalIgnoringCase(valueString, "device-width")) + return true; + if (equalIgnoringCase(valueString, "device-height")) + return true; + + bool ok; + float value = valueString.toFloat(&ok); + if (!ok) { + reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString); + return false; + } + + if (fabs(value) < 1) + return false; + + return true; +} + +static float findTargetDensityDPIValue(const String& keyString, const String& valueString, Document* document) +{ + if (equalIgnoringCase(valueString, "device-dpi")) + return ViewportArguments::ValueDeviceDPI; + if (equalIgnoringCase(valueString, "low-dpi")) + return ViewportArguments::ValueLowDPI; + if (equalIgnoringCase(valueString, "medium-dpi")) + return ViewportArguments::ValueMediumDPI; + if (equalIgnoringCase(valueString, "high-dpi")) + return ViewportArguments::ValueHighDPI; + + bool ok; + float value = valueString.toFloat(&ok); + if (!ok) { + reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString); + return ViewportArguments::ValueAuto; + } + + if (value < 70 || value > 400) { + reportViewportWarning(document, TargetDensityDpiTooSmallOrLargeError, keyString); + return ViewportArguments::ValueAuto; + } + + return value; +} + +void setViewportFeature(const String& keyString, const String& valueString, Document* document, void* data) +{ + ViewportArguments* arguments = static_cast<ViewportArguments*>(data); + + if (keyString == "width") + arguments->width = findSizeValue(keyString, valueString, document); + else if (keyString == "height") + arguments->height = findSizeValue(keyString, valueString, document); + else if (keyString == "initial-scale") + arguments->initialScale = findScaleValue(keyString, valueString, document); + else if (keyString == "minimum-scale") + arguments->minimumScale = findScaleValue(keyString, valueString, document); + else if (keyString == "maximum-scale") + arguments->maximumScale = findScaleValue(keyString, valueString, document); + else if (keyString == "user-scalable") + arguments->userScalable = findUserScalableValue(keyString, valueString, document); + else if (keyString == "target-densitydpi") + arguments->targetDensityDpi = findTargetDensityDPIValue(keyString, valueString, document); +} + +static const char* viewportErrorMessageTemplate(ViewportErrorCode errorCode) +{ + static const char* const errors[] = { + "Viewport width or height set to physical device width, try using \"device-width\" constant instead for future compatibility.", + "Viewport height or height set to physical device height, try using \"device-height\" constant instead for future compatibility.", + "Viewport argument \"%replacement\" not recognized. Content ignored.", + "Viewport maximum-scale cannot be larger than 10.0. The maximum-scale will be set to 10.0.", + "Viewport target-densitydpi has to take a number between 70 and 400 as a valid target dpi, try using \"device-dpi\", \"low-dpi\", \"medium-dpi\" or \"high-dpi\" instead for future compatibility." + }; + + return errors[errorCode]; +} + +static MessageLevel viewportErrorMessageLevel(ViewportErrorCode errorCode) +{ + return errorCode == UnrecognizedViewportArgumentError || errorCode == MaximumScaleTooLargeError ? ErrorMessageLevel : TipMessageLevel; +} + +// FIXME: Why is this different from SVGDocumentExtensions parserLineNumber? +// FIXME: Callers should probably use ScriptController::eventHandlerLineNumber() +static int parserLineNumber(Document* document) +{ + if (!document) + return 0; + ScriptableDocumentParser* parser = document->scriptableDocumentParser(); + if (!parser) + return 0; + return parser->lineNumber() + 1; +} + +void reportViewportWarning(Document* document, ViewportErrorCode errorCode, const String& replacement) +{ + Frame* frame = document->frame(); + if (!frame) + return; + + String message = viewportErrorMessageTemplate(errorCode); + message.replace("%replacement", replacement); + + frame->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, viewportErrorMessageLevel(errorCode), message, parserLineNumber(document), document->url().string()); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/ViewportArguments.h b/Source/WebCore/dom/ViewportArguments.h new file mode 100644 index 0000000..4f678f3 --- /dev/null +++ b/Source/WebCore/dom/ViewportArguments.h @@ -0,0 +1,109 @@ +/* + * 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. + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 ViewportArguments_h +#define ViewportArguments_h + +#include "IntSize.h" +#include <wtf/Forward.h> + +namespace WebCore { + +class Document; + +enum ViewportErrorCode { + DeviceWidthShouldBeUsedWarning, + DeviceHeightShouldBeUsedWarning, + UnrecognizedViewportArgumentError, + MaximumScaleTooLargeError, + TargetDensityDpiTooSmallOrLargeError +}; + +struct ViewportAttributes { + IntSize layoutSize; + + float devicePixelRatio; + + float initialScale; + float minimumScale; + float maximumScale; + + bool userScalable; +}; + +struct ViewportArguments { + + enum { + ValueAuto = -1, + ValueDesktopWidth = -2, + ValueDeviceWidth = -3, + ValueDeviceHeight = -4, + ValueDeviceDPI = -5, + ValueLowDPI = -6, + ValueMediumDPI = -7, + ValueHighDPI = -8 + }; + + ViewportArguments() + : initialScale(ValueAuto) + , minimumScale(ValueAuto) + , maximumScale(ValueAuto) + , width(ValueAuto) + , height(ValueAuto) + , targetDensityDpi(ValueAuto) + , userScalable(true) + { + } + + float initialScale; + float minimumScale; + float maximumScale; + float width; + float height; + float targetDensityDpi; + + bool userScalable; + + bool operator==(const ViewportArguments& other) const + { + return initialScale == other.initialScale + && minimumScale == other.minimumScale + && maximumScale == other.maximumScale + && width == other.width + && height == other.height + && targetDensityDpi == other.targetDensityDpi + && userScalable == other.userScalable; + } +}; + +ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktopWidth, int deviceWidth, int deviceHeight, int deviceDPI, IntSize visibleViewport); + +void setViewportFeature(const String& keyString, const String& valueString, Document*, void* data); +void reportViewportWarning(Document*, ViewportErrorCode, const String& replacement); + +} // namespace WebCore + +#endif // ViewportArguments_h diff --git a/Source/WebCore/dom/WebKitAnimationEvent.cpp b/Source/WebCore/dom/WebKitAnimationEvent.cpp new file mode 100644 index 0000000..371ea69 --- /dev/null +++ b/Source/WebCore/dom/WebKitAnimationEvent.cpp @@ -0,0 +1,74 @@ +/* + * 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 "WebKitAnimationEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +WebKitAnimationEvent::WebKitAnimationEvent() + : m_elapsedTime(0.0) +{ +} + +WebKitAnimationEvent::WebKitAnimationEvent(const AtomicString& type, const String& animationName, double elapsedTime) + : Event(type, true, true) + , m_animationName(animationName) + , m_elapsedTime(elapsedTime) +{ +} + +WebKitAnimationEvent::~WebKitAnimationEvent() +{ +} + +void WebKitAnimationEvent::initWebKitAnimationEvent(const AtomicString& type, + bool canBubbleArg, + bool cancelableArg, + const String& animationName, + double elapsedTime) +{ + if (dispatched()) + return; + + initEvent(type, canBubbleArg, cancelableArg); + + m_animationName = animationName; + m_elapsedTime = elapsedTime; +} + +const String& WebKitAnimationEvent::animationName() const +{ + return m_animationName; +} + +double WebKitAnimationEvent::elapsedTime() const +{ + return m_elapsedTime; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/WebKitAnimationEvent.h b/Source/WebCore/dom/WebKitAnimationEvent.h new file mode 100644 index 0000000..bc7e57c --- /dev/null +++ b/Source/WebCore/dom/WebKitAnimationEvent.h @@ -0,0 +1,67 @@ +/* + * 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 WebKitAnimationEvent_h +#define WebKitAnimationEvent_h + +#include "Event.h" + +namespace WebCore { + + class WebKitAnimationEvent : public Event { + public: + static PassRefPtr<WebKitAnimationEvent> create() + { + return adoptRef(new WebKitAnimationEvent); + } + static PassRefPtr<WebKitAnimationEvent> create(const AtomicString& type, const String& animationName, double elapsedTime) + { + return adoptRef(new WebKitAnimationEvent(type, animationName, elapsedTime)); + } + + virtual ~WebKitAnimationEvent(); + + void initWebKitAnimationEvent(const AtomicString& type, + bool canBubbleArg, + bool cancelableArg, + const String& animationName, + double elapsedTime); + + const String& animationName() const; + double elapsedTime() const; + + virtual bool isWebKitAnimationEvent() const { return true; } + + private: + WebKitAnimationEvent(); + WebKitAnimationEvent(const AtomicString& type, const String& animationName, double elapsedTime); + + String m_animationName; + double m_elapsedTime; + }; + +} // namespace WebCore + +#endif // WebKitAnimationEvent_h diff --git a/Source/WebCore/dom/WebKitAnimationEvent.idl b/Source/WebCore/dom/WebKitAnimationEvent.idl new file mode 100644 index 0000000..86ffcc5 --- /dev/null +++ b/Source/WebCore/dom/WebKitAnimationEvent.idl @@ -0,0 +1,38 @@ +/* + * 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. + */ + +module events { + + interface WebKitAnimationEvent : Event { + readonly attribute DOMString animationName; + readonly attribute double elapsedTime; + void initWebKitAnimationEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in DOMString animationNameArg, + in double elapsedTimeArg); +}; + +} diff --git a/Source/WebCore/dom/WebKitTransitionEvent.cpp b/Source/WebCore/dom/WebKitTransitionEvent.cpp new file mode 100644 index 0000000..f0e86de --- /dev/null +++ b/Source/WebCore/dom/WebKitTransitionEvent.cpp @@ -0,0 +1,75 @@ +/* + * 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 "WebKitTransitionEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +WebKitTransitionEvent::WebKitTransitionEvent() + : m_propertyName() + , m_elapsedTime(0.0) +{ +} + +WebKitTransitionEvent::WebKitTransitionEvent(const AtomicString& type, const String& propertyName, double elapsedTime) + : Event(type, true, true) + , m_propertyName(propertyName) + , m_elapsedTime(elapsedTime) +{ +} + +WebKitTransitionEvent::~WebKitTransitionEvent() +{ +} + +void WebKitTransitionEvent::initWebKitTransitionEvent(const AtomicString& type, + bool canBubbleArg, + bool cancelableArg, + const String& propertyName, + double elapsedTime) +{ + if (dispatched()) + return; + + initEvent(type, canBubbleArg, cancelableArg); + + m_propertyName = propertyName; + m_elapsedTime = elapsedTime; +} + +const String& WebKitTransitionEvent::propertyName() const +{ + return m_propertyName; +} + +double WebKitTransitionEvent::elapsedTime() const +{ + return m_elapsedTime; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/WebKitTransitionEvent.h b/Source/WebCore/dom/WebKitTransitionEvent.h new file mode 100644 index 0000000..ccbf252 --- /dev/null +++ b/Source/WebCore/dom/WebKitTransitionEvent.h @@ -0,0 +1,67 @@ +/* + * 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 WebKitTransitionEvent_h +#define WebKitTransitionEvent_h + +#include "Event.h" + +namespace WebCore { + + class WebKitTransitionEvent : public Event { + public: + static PassRefPtr<WebKitTransitionEvent> create() + { + return adoptRef(new WebKitTransitionEvent); + } + static PassRefPtr<WebKitTransitionEvent> create(const AtomicString& type, const String& animationName, double elapsedTime) + { + return adoptRef(new WebKitTransitionEvent(type, animationName, elapsedTime)); + } + + virtual ~WebKitTransitionEvent(); + + void initWebKitTransitionEvent(const AtomicString& type, + bool canBubbleArg, + bool cancelableArg, + const String& propertyName, + double elapsedTime); + + const String& propertyName() const; + double elapsedTime() const; + + virtual bool isWebKitTransitionEvent() const { return true; } + + private: + WebKitTransitionEvent(); + WebKitTransitionEvent(const AtomicString& type, const String& propertyName, double elapsedTime); + + String m_propertyName; + double m_elapsedTime; + }; + +} // namespace WebCore + +#endif // WebKitTransitionEvent_h diff --git a/Source/WebCore/dom/WebKitTransitionEvent.idl b/Source/WebCore/dom/WebKitTransitionEvent.idl new file mode 100644 index 0000000..fa619fa --- /dev/null +++ b/Source/WebCore/dom/WebKitTransitionEvent.idl @@ -0,0 +1,38 @@ +/* + * 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. + */ + +module events { + + interface WebKitTransitionEvent : Event { + readonly attribute DOMString propertyName; + readonly attribute double elapsedTime; + void initWebKitTransitionEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in DOMString propertyNameArg, + in double elapsedTimeArg); +}; + +} diff --git a/Source/WebCore/dom/WheelEvent.cpp b/Source/WebCore/dom/WheelEvent.cpp new file mode 100644 index 0000000..0981a57 --- /dev/null +++ b/Source/WebCore/dom/WheelEvent.cpp @@ -0,0 +1,95 @@ +/* + * 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, 2008, 2010 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 "WheelEvent.h" + +#include "EventNames.h" +#include <wtf/MathExtras.h> + +namespace WebCore { + +WheelEvent::WheelEvent() + : m_wheelDeltaX(0) + , m_wheelDeltaY(0) + , m_rawDeltaX(0) + , m_rawDeltaY(0) + , m_granularity(Pixel) +{ +} + +WheelEvent::WheelEvent(float wheelTicksX, float wheelTicksY, float rawDeltaX, float rawDeltaY, + Granularity granularity, PassRefPtr<AbstractView> view, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) + : MouseRelatedEvent(eventNames().mousewheelEvent, + true, true, view, 0, screenX, screenY, pageX, pageY, + ctrlKey, altKey, shiftKey, metaKey) + , m_wheelDeltaX(lroundf(wheelTicksX * 120)) + , m_wheelDeltaY(lroundf(wheelTicksY * 120)) // Normalize to the Windows 120 multiple + , m_rawDeltaX(rawDeltaX) + , m_rawDeltaY(rawDeltaY) + , m_granularity(granularity) +{ +} + +void WheelEvent::initWheelEvent(int rawDeltaX, int rawDeltaY, PassRefPtr<AbstractView> view, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) +{ + if (dispatched()) + return; + + initUIEvent(eventNames().mousewheelEvent, true, true, view, 0); + + m_screenX = screenX; + m_screenY = screenY; + m_ctrlKey = ctrlKey; + m_altKey = altKey; + m_shiftKey = shiftKey; + m_metaKey = metaKey; + + // Normalize to the Windows 120 multiple + m_wheelDeltaX = rawDeltaX * 120; + m_wheelDeltaY = rawDeltaY * 120; + + m_rawDeltaX = rawDeltaX; + m_rawDeltaY = rawDeltaY; + m_granularity = Pixel; + + initCoordinates(pageX, pageY); +} + +void WheelEvent::initWebKitWheelEvent(int rawDeltaX, int rawDeltaY, PassRefPtr<AbstractView> view, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) +{ + initWheelEvent(rawDeltaX, rawDeltaY, view, screenX, screenY, pageX, pageY, + ctrlKey, altKey, shiftKey, metaKey); +} + +bool WheelEvent::isWheelEvent() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/WheelEvent.h b/Source/WebCore/dom/WheelEvent.h new file mode 100644 index 0000000..b085e86 --- /dev/null +++ b/Source/WebCore/dom/WheelEvent.h @@ -0,0 +1,87 @@ +/* + * 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, 2008, 2010 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 WheelEvent_h +#define WheelEvent_h + +#include "MouseRelatedEvent.h" + +namespace WebCore { + + // extension: mouse wheel event + class WheelEvent : public MouseRelatedEvent { + public: + enum Granularity { Pixel, Line, Page }; + + static PassRefPtr<WheelEvent> create() + { + return adoptRef(new WheelEvent); + } + static PassRefPtr<WheelEvent> create(float wheelTicksX, float wheelTicksY, + float rawDeltaX, float rawDeltaY, Granularity granularity, PassRefPtr<AbstractView> view, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey) + { + return adoptRef(new WheelEvent(wheelTicksX, wheelTicksY, rawDeltaX, rawDeltaY, + granularity, view, screenX, screenY, pageX, pageY, + ctrlKey, altKey, shiftKey, metaKey)); + } + + void initWheelEvent(int rawDeltaX, int rawDeltaY, PassRefPtr<AbstractView>, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + + void initWebKitWheelEvent(int rawDeltaX, int rawDeltaY, PassRefPtr<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; } + int rawDeltaX() const { return m_rawDeltaX; } + int rawDeltaY() const { return m_rawDeltaY; } + Granularity granularity() const { return m_granularity; } + + // Needed for Objective-C legacy support + bool isHorizontal() const { return m_wheelDeltaX; } + + private: + WheelEvent(); + WheelEvent(float wheelTicksX, float wheelTicksY, float rawDeltaX, float rawDeltaY, + Granularity granularity, PassRefPtr<AbstractView>, + int screenX, int screenY, int pageX, int pageY, + bool ctrlKey, bool altKey, bool shiftKey, bool metaKey); + + virtual bool isWheelEvent() const; + + int m_wheelDeltaX; + int m_wheelDeltaY; + + int m_rawDeltaX; + int m_rawDeltaY; + Granularity m_granularity; + }; + +} // namespace WebCore + +#endif // WheelEvent_h diff --git a/Source/WebCore/dom/WheelEvent.idl b/Source/WebCore/dom/WheelEvent.idl new file mode 100644 index 0000000..4c709ce --- /dev/null +++ b/Source/WebCore/dom/WheelEvent.idl @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2006, 2007, 2010 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 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) && LANGUAGE_OBJECTIVE_C + readonly attribute boolean isHorizontal; +#endif /* defined(LANGUAGE_OBJECTIVE_C) */ + +#if !defined(LANGUAGE_JAVASCRIPT) || !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) */ + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + void initWebKitWheelEvent(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/Source/WebCore/dom/WindowEventContext.cpp b/Source/WebCore/dom/WindowEventContext.cpp new file mode 100644 index 0000000..0709330 --- /dev/null +++ b/Source/WebCore/dom/WindowEventContext.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google 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 "WindowEventContext.h" + +#include "DOMWindow.h" +#include "Document.h" +#include "Event.h" +#include "EventContext.h" +#include "Node.h" + +namespace WebCore { + +WindowEventContext::WindowEventContext(Event* event, PassRefPtr<Node> node, const EventContext* topEventContext) +{ + // We don't dispatch load events to the window. This quirk was originally + // added because Mozilla doesn't propagate load events to the window object. + if (event->type() == eventNames().loadEvent) + return; + + Node* topLevelContainer = topEventContext ? topEventContext->node() : node.get(); + if (!topLevelContainer->isDocumentNode()) + return; + + m_window = static_cast<Document*>(topLevelContainer)->domWindow(); + m_target = topEventContext ? topEventContext->target() : node.get(); +} + +bool WindowEventContext::handleLocalEvents(Event* event) +{ + if (!m_window) + return false; + + event->setTarget(target()); + event->setCurrentTarget(window()); + m_window->fireEventListeners(event); + return true; +} + +} diff --git a/Source/WebCore/dom/WindowEventContext.h b/Source/WebCore/dom/WindowEventContext.h new file mode 100644 index 0000000..a19e04d --- /dev/null +++ b/Source/WebCore/dom/WindowEventContext.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Google 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 WindowEventContext_h +#define WindowEventContext_h + +#include <wtf/RefPtr.h> + +namespace WebCore { + +class DOMWindow; +class EventTarget; +class EventContext; +class Event; +class Node; + +class WindowEventContext { +public: + WindowEventContext(Event*, PassRefPtr<Node>, const EventContext*); + + DOMWindow* window() const; + EventTarget* target() const; + bool handleLocalEvents(Event* event); + +private: + RefPtr<DOMWindow> m_window; + RefPtr<EventTarget> m_target; +}; + +inline DOMWindow* WindowEventContext::window() const +{ + return m_window.get(); +} + +inline EventTarget* WindowEventContext::target() const +{ + return m_target.get(); +} + +} + +#endif // WindowEventContext_h diff --git a/Source/WebCore/dom/XMLDocumentParser.cpp b/Source/WebCore/dom/XMLDocumentParser.cpp new file mode 100644 index 0000000..68c8dd9 --- /dev/null +++ b/Source/WebCore/dom/XMLDocumentParser.cpp @@ -0,0 +1,410 @@ +/* + * 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) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Holger Hans Peter Freyther + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "XMLDocumentParser.h" + +#include "CDATASection.h" +#include "CachedScript.h" +#include "Comment.h" +#include "CachedResourceLoader.h" +#include "Document.h" +#include "DocumentFragment.h" +#include "DocumentType.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HTMLLinkElement.h" +#include "HTMLNames.h" +#include "HTMLStyleElement.h" +#include "ImageLoader.h" +#include "ProcessingInstruction.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "ScriptController.h" +#include "ScriptElement.h" +#include "ScriptSourceCode.h" +#include "ScriptValue.h" +#include "TextResourceDecoder.h" +#include "TreeDepthLimit.h" +#include <wtf/text/StringConcatenate.h> +#include <wtf/StringExtras.h> +#include <wtf/Threading.h> +#include <wtf/Vector.h> + +#if ENABLE(SVG) +#include "SVGNames.h" +#include "SVGStyleElement.h" +#endif + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +const int maxErrors = 25; + +#if ENABLE(WML) +bool XMLDocumentParser::isWMLDocument() const +{ + return document()->isWMLDocument(); +} +#endif + +void XMLDocumentParser::pushCurrentNode(Node* n) +{ + ASSERT(n); + ASSERT(m_currentNode); + if (n != document()) + n->ref(); + m_currentNodeStack.append(m_currentNode); + m_currentNode = n; + if (m_currentNodeStack.size() > maxDOMTreeDepth) + handleError(fatal, "Excessive node nesting.", lineNumber(), columnNumber()); +} + +void XMLDocumentParser::popCurrentNode() +{ + if (!m_currentNode) + return; + ASSERT(m_currentNodeStack.size()); + + if (m_currentNode != document()) + m_currentNode->deref(); + + m_currentNode = m_currentNodeStack.last(); + m_currentNodeStack.removeLast(); +} + +void XMLDocumentParser::clearCurrentNodeStack() +{ + if (m_currentNode && m_currentNode != document()) + m_currentNode->deref(); + m_currentNode = 0; + + if (m_currentNodeStack.size()) { // Aborted parsing. + for (size_t i = m_currentNodeStack.size() - 1; i != 0; --i) + m_currentNodeStack[i]->deref(); + if (m_currentNodeStack[0] && m_currentNodeStack[0] != document()) + m_currentNodeStack[0]->deref(); + m_currentNodeStack.clear(); + } +} + +void XMLDocumentParser::insert(const SegmentedString& source) +{ + // FIXME: This is a hack to work around the fact that XMLHttpRequest + // responseXML() calls Document::write() which in turn calls insert(). In + // HTML, that's correct, as insert() implies a synchronous parse. For XML, + // all parsing is synchronous but document.write shouldn't be supported. + append(source); +} + +void XMLDocumentParser::append(const SegmentedString& s) +{ + String parseString = s.toString(); + + if (m_sawXSLTransform || !m_sawFirstElement) + m_originalSourceForTransform += parseString; + + if (isStopped() || m_sawXSLTransform) + return; + + if (m_parserPaused) { + m_pendingSrc.append(s); + return; + } + + doWrite(s.toString()); + + // After parsing, go ahead and dispatch image beforeload events. + ImageLoader::dispatchPendingBeforeLoadEvents(); +} + +void XMLDocumentParser::handleError(ErrorType type, const char* m, int lineNumber, int columnNumber) +{ + handleError(type, m, TextPosition1(WTF::OneBasedNumber::fromOneBasedInt(lineNumber), WTF::OneBasedNumber::fromOneBasedInt(columnNumber))); +} + +void XMLDocumentParser::handleError(ErrorType type, const char* m, TextPosition1 position) +{ + if (type == fatal || (m_errorCount < maxErrors && m_lastErrorPosition.m_line != position.m_line && m_lastErrorPosition.m_column != position.m_column)) { + switch (type) { + case warning: + m_errorMessages += makeString("warning on line ", String::number(position.m_line.oneBasedInt()), " at column ", String::number(position.m_column.oneBasedInt()), ": ", m); + break; + case fatal: + case nonFatal: + m_errorMessages += makeString("error on line ", String::number(position.m_line.oneBasedInt()), " at column ", String::number(position.m_column.oneBasedInt()), ": ", m); + } + + m_lastErrorPosition = position; + ++m_errorCount; + } + + if (type != warning) + m_sawError = true; + + if (type == fatal) + stopParsing(); +} + +void XMLDocumentParser::enterText() +{ +#if !USE(QXMLSTREAM) + ASSERT(m_bufferedText.size() == 0); +#endif + RefPtr<Node> newNode = Text::create(document(), ""); + m_currentNode->deprecatedParserAddChild(newNode.get()); + pushCurrentNode(newNode.get()); +} + +#if !USE(QXMLSTREAM) +static inline String toString(const xmlChar* string, size_t size) +{ + return String::fromUTF8(reinterpret_cast<const char*>(string), size); +} +#endif + + +void XMLDocumentParser::exitText() +{ + if (isStopped()) + return; + + if (!m_currentNode || !m_currentNode->isTextNode()) + return; + +#if !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(); + + popCurrentNode(); +} + +void XMLDocumentParser::detach() +{ + clearCurrentNodeStack(); + ScriptableDocumentParser::detach(); +} + +void XMLDocumentParser::end() +{ + // XMLDocumentParserLibxml2 will do bad things to the document if doEnd() is called. + // I don't believe XMLDocumentParserQt needs doEnd called in the fragment case. + ASSERT(!m_parsingFragment); + + doEnd(); + + // doEnd() could process a script tag, thus pausing parsing. + if (m_parserPaused) + return; + + if (m_sawError) + insertErrorMessageBlock(); + else { + exitText(); + document()->styleSelectorChanged(RecalcStyleImmediately); + } + + if (isParsing()) + prepareToStopParsing(); + document()->setReadyState(Document::Interactive); + clearCurrentNodeStack(); + document()->finishedParsing(); +} + +void XMLDocumentParser::finish() +{ + // FIXME: We should ASSERT(!m_parserStopped) here, since it does not + // makes sense to call any methods on DocumentParser once it's been stopped. + // However, FrameLoader::stop calls Document::finishParsing unconditionally + // which in turn calls m_parser->finish(). + + if (m_parserPaused) + m_finishCalled = true; + else + end(); +} + +bool XMLDocumentParser::finishWasCalled() +{ + return m_finishCalled; +} + +static inline RefPtr<Element> createXHTMLParserErrorHeader(Document* doc, const String& errorMessages) +{ + RefPtr<Element> reportElement = doc->createElement(QualifiedName(nullAtom, "parsererror", xhtmlNamespaceURI), false); + reportElement->setAttribute(styleAttr, "display: block; white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black"); + + ExceptionCode ec = 0; + RefPtr<Element> h3 = doc->createElement(h3Tag, false); + reportElement->appendChild(h3.get(), ec); + h3->appendChild(doc->createTextNode("This page contains the following errors:"), ec); + + RefPtr<Element> fixed = doc->createElement(divTag, false); + reportElement->appendChild(fixed.get(), ec); + fixed->setAttribute(styleAttr, "font-family:monospace;font-size:12px"); + fixed->appendChild(doc->createTextNode(errorMessages), ec); + + h3 = doc->createElement(h3Tag, false); + 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 XMLDocumentParser::insertErrorMessageBlock() +{ +#if 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* document = this->document(); + Element* documentElement = document->documentElement(); + if (!documentElement) { + RefPtr<Element> rootElement = document->createElement(htmlTag, false); + document->appendChild(rootElement, ec); + RefPtr<Element> body = document->createElement(bodyTag, false); + rootElement->appendChild(body, ec); + documentElement = body.get(); + } +#if ENABLE(SVG) + else if (documentElement->namespaceURI() == SVGNames::svgNamespaceURI) { + RefPtr<Element> rootElement = document->createElement(htmlTag, false); + RefPtr<Element> body = document->createElement(bodyTag, false); + rootElement->appendChild(body, ec); + body->appendChild(documentElement, ec); + document->appendChild(rootElement.get(), ec); + documentElement = body.get(); + } +#endif +#if ENABLE(WML) + else if (isWMLDocument()) { + RefPtr<Element> rootElement = document->createElement(htmlTag, false); + RefPtr<Element> body = document->createElement(bodyTag, false); + rootElement->appendChild(body, ec); + body->appendChild(documentElement, ec); + document->appendChild(rootElement.get(), ec); + documentElement = body.get(); + } +#endif + + RefPtr<Element> reportElement = createXHTMLParserErrorHeader(document, m_errorMessages); + documentElement->insertBefore(reportElement, documentElement->firstChild(), ec); +#if ENABLE(XSLT) + if (document->transformSourceDocument()) { + RefPtr<Element> paragraph = document->createElement(pTag, false); + paragraph->setAttribute(styleAttr, "white-space: normal"); + paragraph->appendChild(document->createTextNode("This document was created as the result of an XSL transformation. The line and column numbers given are from the transformed result."), ec); + reportElement->appendChild(paragraph.release(), ec); + } +#endif + document->updateStyleIfNeeded(); +} + +void XMLDocumentParser::notifyFinished(CachedResource* unusedResource) +{ + ASSERT_UNUSED(unusedResource, unusedResource == m_pendingScript); + ASSERT(m_pendingScript->accessCount() > 0); + + ScriptSourceCode sourceCode(m_pendingScript.get()); + bool errorOccurred = m_pendingScript->errorOccurred(); + + m_pendingScript->removeClient(this); + m_pendingScript = 0; + + RefPtr<Element> e = m_scriptElement; + m_scriptElement = 0; + + ScriptElement* scriptElement = toScriptElement(e.get()); + ASSERT(scriptElement); + + // JavaScript can detach this parser, make sure it's kept alive even if detached. + RefPtr<XMLDocumentParser> protect(this); + + if (errorOccurred) + scriptElement->dispatchErrorEvent(); + else { + scriptElement->executeScript(sourceCode); + scriptElement->dispatchLoadEvent(); + } + + m_scriptElement = 0; + + if (!isDetached() && !m_requestingScript) + resumeParsing(); +} + +bool XMLDocumentParser::isWaitingForScripts() const +{ + return m_pendingScript; +} + +void XMLDocumentParser::pauseParsing() +{ + if (m_parsingFragment) + return; + + m_parserPaused = true; +} + +bool XMLDocumentParser::parseDocumentFragment(const String& chunk, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission) +{ + if (!chunk.length()) + return true; + + // FIXME: We need to implement the HTML5 XML Fragment parsing algorithm: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-xhtml-syntax.html#xml-fragment-parsing-algorithm + // For now we have a hack for script/style innerHTML support: + if (contextElement && (contextElement->hasLocalName(HTMLNames::scriptTag) || contextElement->hasLocalName(HTMLNames::styleTag))) { + fragment->parserAddChild(fragment->document()->createTextNode(chunk)); + return true; + } + + RefPtr<XMLDocumentParser> parser = XMLDocumentParser::create(fragment, contextElement, scriptingPermission); + bool wellFormed = parser->appendFragmentSource(chunk); + // Do not call finish(). Current finish() and doEnd() implementations touch the main Document/loader + // and can cause crashes in the fragment case. + parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction. + return wellFormed; // appendFragmentSource()'s wellFormed is more permissive than wellFormed(). +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/XMLDocumentParser.h b/Source/WebCore/dom/XMLDocumentParser.h new file mode 100644 index 0000000..70628ed --- /dev/null +++ b/Source/WebCore/dom/XMLDocumentParser.h @@ -0,0 +1,344 @@ +/* + * 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) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.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. + * + */ + +#ifndef XMLDocumentParser_h +#define XMLDocumentParser_h + +#if USE(EXPAT) + +#include "CachedResourceClient.h" +#include "SegmentedString.h" +#include "StringHash.h" +#include "Tokenizer.h" +#include <libexpat/expat.h> +#include <wtf/HashMap.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + + class Node; + class CachedScript; + 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 &str, bool); + virtual void finish(); + virtual bool isWaitingForScripts() const; + virtual void stopParsing(); + virtual bool wellFormed() const { return !m_sawError; } + virtual int lineNumber() const; + virtual int columnNumber() const; + + // from CachedObjectClient + virtual void notifyFinished(CachedResource *finishedObj); + + // callbacks from parser expat + void startElementNs(const XML_Char *name, const XML_Char **atts); + void endElementNs(); + void characters(const XML_Char *s, int len); + void processingInstruction(const XML_Char *target, const XML_Char *data); + void comment(const XML_Char *s); + void startCdata(); + void endCdata(); + + void error(ErrorType type, const char* m, int lineNumber, int columnNumber); + + // utilities + XML_Parser getXMLParser() const { return m_parser; } + void setXMLParser(XML_Parser parser) { m_parser = parser; } + + private: + void setCurrentNode(Node*); + + void end(); + + void pauseParsing(); + void resumeParsing(); + + void reportError(); + void insertErrorMessageBlock(); + + bool enterText(); + void exitText(); + + Document *m_doc; + FrameView *m_view; + + XML_Parser m_parser; + + Node *m_currentNode; + bool m_currentNodeIsReferenced; + + bool m_sawError; + bool m_sawXSLTransform; + bool m_sawFirstElement; + + bool m_parserPaused; + bool m_requestingScript; + bool m_finishCalled; + + int m_errorCount; + 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; + + OwnPtr<PendingCallbacks> m_pendingCallbacks; + SegmentedString m_pendingSrc; + }; + +HashMap<String, String> parseAttributes(const String&, bool& attrsOK); +bool parseXMLDocumentFragment(const String&, DocumentFragment*, Element* parent = 0); + +} // namespace WebCore + +#else // USE(EXPAT) + +#include "CachedResourceClient.h" +#include "CachedResourceHandle.h" +#include "FragmentScriptingPermission.h" +#include "ScriptableDocumentParser.h" +#include "SegmentedString.h" +#include <wtf/HashMap.h> +#include <wtf/OwnPtr.h> +#include <wtf/text/StringHash.h> + +#if USE(QXMLSTREAM) +#include <qxmlstream.h> +#else +#include <libxml/tree.h> +#include <libxml/xmlstring.h> +#endif + +namespace WebCore { + + class Node; + class CachedScript; + class CachedResourceLoader; + class DocumentFragment; + class Document; + class Element; + class FrameView; + class PendingCallbacks; + class ScriptElement; + +#if !USE(QXMLSTREAM) + class XMLParserContext : public RefCounted<XMLParserContext> { + public: + static PassRefPtr<XMLParserContext> createMemoryParser(xmlSAXHandlerPtr, void*, const char*); + static PassRefPtr<XMLParserContext> createStringParser(xmlSAXHandlerPtr, void*); + ~XMLParserContext(); + xmlParserCtxtPtr context() const { return m_context; } + + private: + XMLParserContext(xmlParserCtxtPtr context) + : m_context(context) + { + } + xmlParserCtxtPtr m_context; + }; +#endif + + class XMLDocumentParser : public ScriptableDocumentParser, public CachedResourceClient { + public: + static PassRefPtr<XMLDocumentParser> create(Document* document, FrameView* view) + { + return adoptRef(new XMLDocumentParser(document, view)); + } + static PassRefPtr<XMLDocumentParser> create(DocumentFragment* fragment, Element* element, FragmentScriptingPermission permission) + { + return adoptRef(new XMLDocumentParser(fragment, element, permission)); + } + + ~XMLDocumentParser(); + + // Exposed for callbacks: + enum ErrorType { warning, nonFatal, fatal }; + void handleError(ErrorType, const char* message, int lineNumber, int columnNumber); + void handleError(ErrorType, const char* message, TextPosition1); + + void setIsXHTMLDocument(bool isXHTML) { m_isXHTMLDocument = isXHTML; } + bool isXHTMLDocument() const { return m_isXHTMLDocument; } +#if ENABLE(XHTMLMP) + void setIsXHTMLMPDocument(bool isXHTML) { m_isXHTMLMPDocument = isXHTML; } + bool isXHTMLMPDocument() const { return m_isXHTMLMPDocument; } +#endif +#if ENABLE(WML) + bool isWMLDocument() const; +#endif + + static bool parseDocumentFragment(const String&, DocumentFragment*, Element* parent = 0, FragmentScriptingPermission = FragmentScriptingAllowed); + + // WMLErrorHandling uses these functions. + virtual bool wellFormed() const { return !m_sawError; } + + TextPosition0 textPosition() const; + TextPosition1 textPositionOneBased() const; + + static bool supportsXMLVersion(const String&); + + private: + XMLDocumentParser(Document*, FrameView* = 0); + XMLDocumentParser(DocumentFragment*, Element*, FragmentScriptingPermission); + + // From DocumentParser + virtual void insert(const SegmentedString&); + virtual void append(const SegmentedString&); + virtual void finish(); + virtual bool finishWasCalled(); + virtual bool isWaitingForScripts() const; + virtual void stopParsing(); + virtual void detach(); + virtual int lineNumber() const; + int columnNumber() const; + + // from CachedResourceClient + virtual void notifyFinished(CachedResource*); + + void end(); + + void pauseParsing(); + void resumeParsing(); + + bool appendFragmentSource(const String&); + +#if USE(QXMLSTREAM) +private: + void parse(); + void startDocument(); + void parseStartElement(); + void parseEndElement(); + void parseCharacters(); + void parseProcessingInstruction(); + void parseCdata(); + void parseComment(); + void endDocument(); + void parseDtd(); + bool hasError() const; +#else +public: + // 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(); +#endif + private: + void initializeParserContext(const char* chunk = 0); + + void pushCurrentNode(Node*); + void popCurrentNode(); + void clearCurrentNodeStack(); + + void insertErrorMessageBlock(); + + void enterText(); + void exitText(); + + void doWrite(const String&); + void doEnd(); + + FrameView* m_view; + + String m_originalSourceForTransform; + +#if USE(QXMLSTREAM) + QXmlStreamReader m_stream; + bool m_wroteText; +#else + xmlParserCtxtPtr context() const { return m_context ? m_context->context() : 0; }; + RefPtr<XMLParserContext> m_context; + OwnPtr<PendingCallbacks> m_pendingCallbacks; + Vector<xmlChar> m_bufferedText; +#endif + Node* m_currentNode; + Vector<Node*> m_currentNodeStack; + + bool m_sawError; + bool m_sawXSLTransform; + bool m_sawFirstElement; + bool m_isXHTMLDocument; +#if ENABLE(XHTMLMP) + bool m_isXHTMLMPDocument; + bool m_hasDocTypeDeclaration; +#endif + + bool m_parserPaused; + bool m_requestingScript; + bool m_finishCalled; + + int m_errorCount; + TextPosition1 m_lastErrorPosition; + String m_errorMessages; + + CachedResourceHandle<CachedScript> m_pendingScript; + RefPtr<Element> m_scriptElement; + TextPosition1 m_scriptStartPosition; + + bool m_parsingFragment; + AtomicString m_defaultNamespaceURI; + + typedef HashMap<AtomicString, AtomicString> PrefixForNamespaceMap; + PrefixForNamespaceMap m_prefixToNamespaceMap; + SegmentedString m_pendingSrc; + FragmentScriptingPermission m_scriptingPermission; + }; + +#if ENABLE(XSLT) +void* xmlDocPtrForString(CachedResourceLoader*, const String& source, const String& url); +#endif + +HashMap<String, String> parseAttributes(const String&, bool& attrsOK); + +} // namespace WebCore + +#endif // USE(EXPAT) + +#endif // XMLDocumentParser_h diff --git a/Source/WebCore/dom/XMLDocumentParserLibxml2.cpp b/Source/WebCore/dom/XMLDocumentParserLibxml2.cpp new file mode 100644 index 0000000..0f6b4b4 --- /dev/null +++ b/Source/WebCore/dom/XMLDocumentParserLibxml2.cpp @@ -0,0 +1,1514 @@ +/* + * 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) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Holger Hans Peter Freyther + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "XMLDocumentParser.h" + +#include "CDATASection.h" +#include "CachedScript.h" +#include "Comment.h" +#include "CachedResourceLoader.h" +#include "Document.h" +#include "DocumentFragment.h" +#include "DocumentType.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HTMLEntityParser.h" +#include "HTMLHtmlElement.h" +#include "HTMLLinkElement.h" +#include "HTMLNames.h" +#include "HTMLStyleElement.h" +#include "ProcessingInstruction.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "ScriptElement.h" +#include "ScriptSourceCode.h" +#include "ScriptValue.h" +#include "TextResourceDecoder.h" +#include "TransformSource.h" +#include "XMLNSNames.h" +#include "XMLDocumentParserScope.h" +#include <libxml/parser.h> +#include <libxml/parserInternals.h> +#include <wtf/text/CString.h> +#include <wtf/StringExtras.h> +#include <wtf/Threading.h> +#include <wtf/UnusedParam.h> +#include <wtf/Vector.h> + +#if ENABLE(XSLT) +#include <libxslt/xslt.h> +#endif + +#if ENABLE(XHTMLMP) +#include "HTMLNames.h" +#include "HTMLScriptElement.h" +#endif + +using namespace std; + +namespace WebCore { + +class PendingCallbacks : public Noncopyable { +public: + ~PendingCallbacks() + { + deleteAllValues(m_callbacks); + } + + 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 = static_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 = static_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(XMLDocumentParser::ErrorType type, const xmlChar* message, int lineNumber, int columnNumber) + { + PendingErrorCallback* callback = new PendingErrorCallback; + + callback->message = xmlStrdup(message); + callback->type = type; + callback->lineNumber = lineNumber; + callback->columnNumber = columnNumber; + + m_callbacks.append(callback); + } + + void callAndRemoveFirstCallback(XMLDocumentParser* parser) + { + OwnPtr<PendingCallback> callback(m_callbacks.takeFirst()); + callback->call(parser); + } + + bool isEmpty() const { return m_callbacks.isEmpty(); } + +private: + struct PendingCallback { + virtual ~PendingCallback() { } + virtual void call(XMLDocumentParser* parser) = 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(XMLDocumentParser* parser) + { + parser->startElementNs(xmlLocalName, xmlPrefix, xmlURI, + nb_namespaces, const_cast<const xmlChar**>(namespaces), + nb_attributes, nb_defaulted, const_cast<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(XMLDocumentParser* parser) + { + parser->endElementNs(); + } + }; + + struct PendingCharactersCallback : public PendingCallback { + virtual ~PendingCharactersCallback() + { + xmlFree(s); + } + + virtual void call(XMLDocumentParser* parser) + { + parser->characters(s, len); + } + + xmlChar* s; + int len; + }; + + struct PendingProcessingInstructionCallback : public PendingCallback { + virtual ~PendingProcessingInstructionCallback() + { + xmlFree(target); + xmlFree(data); + } + + virtual void call(XMLDocumentParser* parser) + { + parser->processingInstruction(target, data); + } + + xmlChar* target; + xmlChar* data; + }; + + struct PendingCDATABlockCallback : public PendingCallback { + virtual ~PendingCDATABlockCallback() + { + xmlFree(s); + } + + virtual void call(XMLDocumentParser* parser) + { + parser->cdataBlock(s, len); + } + + xmlChar* s; + int len; + }; + + struct PendingCommentCallback : public PendingCallback { + virtual ~PendingCommentCallback() + { + xmlFree(s); + } + + virtual void call(XMLDocumentParser* parser) + { + parser->comment(s); + } + + xmlChar* s; + }; + + struct PendingInternalSubsetCallback : public PendingCallback { + virtual ~PendingInternalSubsetCallback() + { + xmlFree(name); + xmlFree(externalID); + xmlFree(systemID); + } + + virtual void call(XMLDocumentParser* parser) + { + parser->internalSubset(name, externalID, systemID); + } + + xmlChar* name; + xmlChar* externalID; + xmlChar* systemID; + }; + + struct PendingErrorCallback: public PendingCallback { + virtual ~PendingErrorCallback() + { + xmlFree(message); + } + + virtual void call(XMLDocumentParser* parser) + { + parser->handleError(type, reinterpret_cast<char*>(message), lineNumber, columnNumber); + } + + XMLDocumentParser::ErrorType type; + xmlChar* message; + int lineNumber; + int columnNumber; + }; + + Deque<PendingCallback*> m_callbacks; +}; +// -------------------------------- + +static int globalDescriptor = 0; +static ThreadIdentifier libxmlLoaderThread = 0; + +static int matchFunc(const char*) +{ + // Only match loads initiated due to uses of libxml2 from within XMLDocumentParser to avoid + // interfering with client applications that also use libxml2. http://bugs.webkit.org/show_bug.cgi?id=17353 + return XMLDocumentParserScope::currentCachedResourceLoader && 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 void switchToUTF16(xmlParserCtxtPtr ctxt) +{ + // 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(ctxt, BOMHighByte == 0xFF ? XML_CHAR_ENCODING_UTF16LE : XML_CHAR_ENCODING_UTF16BE); +} + +static bool shouldAllowExternalLoad(const KURL& url) +{ + String urlString = url.string(); + + // On non-Windows platforms libxml asks for this URL, the + // "XML_XML_DEFAULT_CATALOG", on initialization. + if (urlString == "file:///etc/xml/catalog") + return false; + + // On Windows, libxml computes a URL relative to where its DLL resides. + if (urlString.startsWith("file:///", false) && urlString.endsWith("/etc/catalog", false)) + return false; + + // The most common DTD. There isn't much point in hammering www.w3c.org + // by requesting this URL for every XHTML document. + if (urlString.startsWith("http://www.w3.org/TR/xhtml", false)) + return false; + + // Similarly, there isn't much point in requesting the SVG DTD. + if (urlString.startsWith("http://www.w3.org/Graphics/SVG", false)) + return false; + + // The libxml doesn't give us a lot of context for deciding whether to + // allow this request. In the worst case, this load could be for an + // external entity and the resulting document could simply read the + // retrieved content. If we had more context, we could potentially allow + // the parser to load a DTD. As things stand, we take the conservative + // route and allow same-origin requests only. + if (!XMLDocumentParserScope::currentCachedResourceLoader->document()->securityOrigin()->canRequest(url)) { + XMLDocumentParserScope::currentCachedResourceLoader->printAccessDeniedMessage(url); + return false; + } + + return true; +} + +static void* openFunc(const char* uri) +{ + ASSERT(XMLDocumentParserScope::currentCachedResourceLoader); + ASSERT(currentThread() == libxmlLoaderThread); + + KURL url(KURL(), uri); + + if (!shouldAllowExternalLoad(url)) + return &globalDescriptor; + + ResourceError error; + ResourceResponse response; + Vector<char> data; + + + { + CachedResourceLoader* cachedResourceLoader = XMLDocumentParserScope::currentCachedResourceLoader; + XMLDocumentParserScope scope(0); + // FIXME: We should restore the original global error handler as well. + + if (cachedResourceLoader->frame()) + cachedResourceLoader->frame()->loader()->loadResourceSynchronously(url, AllowStoredCredentials, error, response, data); + } + + // We have to check the URL again after the load to catch redirects. + // See <https://bugs.webkit.org/show_bug.cgi?id=21963>. + if (!shouldAllowExternalLoad(response.url())) + return &globalDescriptor; + + 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*, const char*, int) +{ + // 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; +} + +#if ENABLE(XSLT) +static void errorFunc(void*, const char*, ...) +{ + // FIXME: It would be nice to display error messages somewhere. +} +#endif + +static bool didInit = false; + +PassRefPtr<XMLParserContext> XMLParserContext::createStringParser(xmlSAXHandlerPtr handlers, void* userData) +{ + 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; + switchToUTF16(parser); + + return adoptRef(new XMLParserContext(parser)); +} + + +// Chunk should be encoded in UTF-8 +PassRefPtr<XMLParserContext> XMLParserContext::createMemoryParser(xmlSAXHandlerPtr handlers, void* userData, const char* chunk) +{ + if (!didInit) { + xmlInitParser(); + xmlRegisterInputCallbacks(matchFunc, openFunc, readFunc, closeFunc); + xmlRegisterOutputCallbacks(matchFunc, openFunc, writeFunc, closeFunc); + libxmlLoaderThread = currentThread(); + didInit = true; + } + + xmlParserCtxtPtr parser = xmlCreateMemoryParserCtxt(chunk, xmlStrlen((const xmlChar*)chunk)); + + if (!parser) + return 0; + + // Copy the sax handler + memcpy(parser->sax, handlers, sizeof(xmlSAXHandler)); + + // Set parser options. + // XML_PARSE_NODICT: default dictionary option. + // XML_PARSE_NOENT: force entities substitutions. + xmlCtxtUseOptions(parser, XML_PARSE_NODICT | XML_PARSE_NOENT); + + // Internal initialization + parser->sax2 = 1; + parser->instate = XML_PARSER_CONTENT; // We are parsing a CONTENT + parser->depth = 0; + parser->str_xml = xmlDictLookup(parser->dict, BAD_CAST "xml", 3); + parser->str_xmlns = xmlDictLookup(parser->dict, BAD_CAST "xmlns", 5); + parser->str_xml_ns = xmlDictLookup(parser->dict, XML_XML_NAMESPACE, 36); + parser->_private = userData; + + return adoptRef(new XMLParserContext(parser)); +} + +// -------------------------------- + +bool XMLDocumentParser::supportsXMLVersion(const String& version) +{ + return version == "1.0"; +} + +XMLDocumentParser::XMLDocumentParser(Document* document, FrameView* frameView) + : ScriptableDocumentParser(document) + , m_view(frameView) + , m_context(0) + , m_pendingCallbacks(new PendingCallbacks) + , m_currentNode(document) + , m_sawError(false) + , m_sawXSLTransform(false) + , m_sawFirstElement(false) + , m_isXHTMLDocument(false) +#if ENABLE(XHTMLMP) + , m_isXHTMLMPDocument(false) + , m_hasDocTypeDeclaration(false) +#endif + , m_parserPaused(false) + , m_requestingScript(false) + , m_finishCalled(false) + , m_errorCount(0) + , m_lastErrorPosition(TextPosition1::belowRangePosition()) + , m_pendingScript(0) + , m_scriptStartPosition(TextPosition1::belowRangePosition()) + , m_parsingFragment(false) + , m_scriptingPermission(FragmentScriptingAllowed) +{ +} + +XMLDocumentParser::XMLDocumentParser(DocumentFragment* fragment, Element* parentElement, FragmentScriptingPermission scriptingPermission) + : ScriptableDocumentParser(fragment->document()) + , m_view(0) + , m_context(0) + , m_pendingCallbacks(new PendingCallbacks) + , m_currentNode(fragment) + , m_sawError(false) + , m_sawXSLTransform(false) + , m_sawFirstElement(false) + , m_isXHTMLDocument(false) +#if ENABLE(XHTMLMP) + , m_isXHTMLMPDocument(false) + , m_hasDocTypeDeclaration(false) +#endif + , m_parserPaused(false) + , m_requestingScript(false) + , m_finishCalled(false) + , m_errorCount(0) + , m_lastErrorPosition(TextPosition1::belowRangePosition()) + , m_pendingScript(0) + , m_scriptStartPosition(TextPosition1::belowRangePosition()) + , m_parsingFragment(true) + , m_scriptingPermission(scriptingPermission) +{ + fragment->ref(); + + // Add namespaces based on the parent node + Vector<Element*> elemStack; + while (parentElement) { + elemStack.append(parentElement); + + ContainerNode* n = parentElement->parentNode(); + if (!n || !n->isElementNode()) + break; + parentElement = static_cast<Element*>(n); + } + + if (elemStack.isEmpty()) + return; + + for (Element* element = elemStack.last(); !elemStack.isEmpty(); elemStack.removeLast()) { + if (NamedNodeMap* attrs = element->attributes()) { + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute* attr = attrs->attributeItem(i); + if (attr->localName() == xmlnsAtom) + m_defaultNamespaceURI = attr->value(); + else if (attr->prefix() == xmlnsAtom) + m_prefixToNamespaceMap.set(attr->localName(), attr->value()); + } + } + } + + // 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(); +} + +XMLParserContext::~XMLParserContext() +{ + if (m_context->myDoc) + xmlFreeDoc(m_context->myDoc); + xmlFreeParserCtxt(m_context); +} + +XMLDocumentParser::~XMLDocumentParser() +{ + // The XMLDocumentParser will always be detached before being destroyed. + ASSERT(m_currentNodeStack.isEmpty()); + ASSERT(!m_currentNode); + + // FIXME: m_pendingScript handling should be moved into XMLDocumentParser.cpp! + if (m_pendingScript) + m_pendingScript->removeClient(this); +} + +void XMLDocumentParser::doWrite(const String& parseString) +{ + ASSERT(!isDetached()); + if (!m_context) + initializeParserContext(); + + // Protect the libxml context from deletion during a callback + RefPtr<XMLParserContext> context = m_context; + + // libXML throws an error if you try to switch the encoding for an empty string. + if (parseString.length()) { + // JavaScript may cause the parser to detach during xmlParseChunk + // keep this alive until this function is done. + RefPtr<XMLDocumentParser> protect(this); + + switchToUTF16(context->context()); + XMLDocumentParserScope scope(document()->cachedResourceLoader()); + xmlParseChunk(context->context(), reinterpret_cast<const char*>(parseString.characters()), sizeof(UChar) * parseString.length(), 0); + + // JavaScript (which may be run under the xmlParseChunk callstack) may + // cause the parser to be stopped or detached. + if (isStopped()) + return; + } + + // FIXME: Why is this here? And why is it after we process the passed source? + if (document()->decoder() && document()->decoder()->sawError()) { + // If the decoder saw an error, report it as fatal (stops parsing) + handleError(fatal, "Encoding error", context->context()->input->line, context->context()->input->col); + } +} + +static inline String toString(const xmlChar* string, size_t size) +{ + return String::fromUTF8(reinterpret_cast<const char*>(string), size); +} + +static inline String toString(const xmlChar* string) +{ + return String::fromUTF8(reinterpret_cast<const char*>(string)); +} + +static inline AtomicString toAtomicString(const xmlChar* string, size_t size) +{ + return AtomicString::fromUTF8(reinterpret_cast<const char*>(string), size); +} + +static inline AtomicString toAtomicString(const xmlChar* string) +{ + return AtomicString::fromUTF8(reinterpret_cast<const char*>(string)); +} + +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, FragmentScriptingPermission scriptingPermission) +{ + xmlSAX2Namespace* namespaces = reinterpret_cast<xmlSAX2Namespace*>(libxmlNamespaces); + for (int i = 0; i < nb_namespaces; i++) { + AtomicString namespaceQName = xmlnsAtom; + AtomicString namespaceURI = toAtomicString(namespaces[i].uri); + if (namespaces[i].prefix) + namespaceQName = "xmlns:" + toString(namespaces[i].prefix); + newElement->setAttributeNS(XMLNSNames::xmlnsNamespaceURI, namespaceQName, namespaceURI, ec, scriptingPermission); + 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, FragmentScriptingPermission scriptingPermission) +{ + xmlSAX2Attributes* attributes = reinterpret_cast<xmlSAX2Attributes*>(libxmlAttributes); + for (int i = 0; i < nb_attributes; i++) { + int valueLength = static_cast<int>(attributes[i].end - attributes[i].value); + AtomicString attrValue = toAtomicString(attributes[i].value, valueLength); + String attrPrefix = toString(attributes[i].prefix); + AtomicString attrURI = attrPrefix.isEmpty() ? AtomicString() : toAtomicString(attributes[i].uri); + AtomicString attrQName = attrPrefix.isEmpty() ? toAtomicString(attributes[i].localname) : AtomicString(attrPrefix + ":" + toString(attributes[i].localname)); + + newElement->setAttributeNS(attrURI, attrQName, attrValue, ec, scriptingPermission); + if (ec) // exception setting attributes + return; + } +} + +void XMLDocumentParser::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 (isStopped()) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendStartElementNSCallback(xmlLocalName, xmlPrefix, xmlURI, nb_namespaces, libxmlNamespaces, + nb_attributes, nb_defaulted, libxmlAttributes); + return; + } + +#if ENABLE(XHTMLMP) + // check if the DOCTYPE Declaration of XHTMLMP document exists + if (!m_hasDocTypeDeclaration && document()->isXHTMLMPDocument()) { + handleError(fatal, "DOCTYPE declaration lost.", lineNumber(), columnNumber()); + return; + } +#endif + + exitText(); + + AtomicString localName = toAtomicString(xmlLocalName); + AtomicString uri = toAtomicString(xmlURI); + AtomicString prefix = toAtomicString(xmlPrefix); + + if (m_parsingFragment && uri.isNull()) { + if (!prefix.isNull()) + uri = m_prefixToNamespaceMap.get(prefix); + else + uri = m_defaultNamespaceURI; + } + +#if ENABLE(XHTMLMP) + if (!m_sawFirstElement && isXHTMLMPDocument()) { + // As per the section 7.1 of OMA-WAP-XHTMLMP-V1_1-20061020-A.pdf, + // we should make sure that the root element MUST be 'html' and + // ensure the name of the default namespace on the root elment 'html' + // MUST be 'http://www.w3.org/1999/xhtml' + if (localName != HTMLNames::htmlTag.localName()) { + handleError(fatal, "XHTMLMP document expects 'html' as root element.", lineNumber(), columnNumber()); + return; + } + + if (uri.isNull()) { + m_defaultNamespaceURI = HTMLNames::xhtmlNamespaceURI; + uri = m_defaultNamespaceURI; + } + } +#endif + + bool isFirstElement = !m_sawFirstElement; + m_sawFirstElement = true; + + QualifiedName qName(prefix, localName, uri); + RefPtr<Element> newElement = document()->createElement(qName, true); + if (!newElement) { + stopParsing(); + return; + } + + ExceptionCode ec = 0; + handleElementNamespaces(newElement.get(), libxmlNamespaces, nb_namespaces, ec, m_scriptingPermission); + if (ec) { + stopParsing(); + return; + } + + handleElementAttributes(newElement.get(), libxmlAttributes, nb_attributes, ec, m_scriptingPermission); + if (ec) { + stopParsing(); + return; + } + + newElement->beginParsingChildren(); + + ScriptElement* scriptElement = toScriptElement(newElement.get()); + if (scriptElement) + m_scriptStartPosition = textPositionOneBased(); + + m_currentNode->deprecatedParserAddChild(newElement.get()); + + pushCurrentNode(newElement.get()); + if (m_view && !newElement->attached()) + newElement->attach(); + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (newElement->hasTagName(HTMLNames::htmlTag)) + static_cast<HTMLHtmlElement*>(newElement.get())->insertedByParser(); +#endif + + if (!m_parsingFragment && isFirstElement && document()->frame()) + document()->frame()->loader()->dispatchDocumentElementAvailable(); +} + +void XMLDocumentParser::endElementNs() +{ + if (isStopped()) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendEndElementNSCallback(); + return; + } + + exitText(); + + Node* n = m_currentNode; + n->finishParsingChildren(); + + if (m_scriptingPermission == FragmentScriptingNotAllowed && n->isElementNode() && toScriptElement(static_cast<Element*>(n))) { + popCurrentNode(); + ExceptionCode ec; + n->remove(ec); + return; + } + + if (!n->isElementNode() || !m_view) { + popCurrentNode(); + return; + } + + Element* element = static_cast<Element*>(n); + + // The element's parent may have already been removed from document. + // Parsing continues in this case, but scripts aren't executed. + if (!element->inDocument()) { + popCurrentNode(); + return; + } + + ScriptElement* scriptElement = toScriptElement(element); + if (!scriptElement) { + popCurrentNode(); + return; + } + + // Don't load external scripts for standalone documents (for now). + ASSERT(!m_pendingScript); + m_requestingScript = true; + +#if ENABLE(XHTMLMP) + if (!scriptElement->shouldExecuteAsJavaScript()) + document()->setShouldProcessNoscriptElement(true); + else +#endif + { + // FIXME: Script execution should be shared should be shared between + // the libxml2 and Qt XMLDocumentParser implementations. + + // JavaScript can detach the parser. Make sure this is not released + // before the end of this method. + RefPtr<XMLDocumentParser> protect(this); + + String scriptHref = scriptElement->sourceAttributeValue(); + if (!scriptHref.isEmpty()) { + // we have a src attribute + String scriptCharset = scriptElement->scriptCharset(); + if (element->dispatchBeforeLoadEvent(scriptHref) && + (m_pendingScript = document()->cachedResourceLoader()->requestScript(scriptHref, scriptCharset))) { + m_scriptElement = element; + m_pendingScript->addClient(this); + + // m_pendingScript will be 0 if script was already loaded and ref() executed it + if (m_pendingScript) + pauseParsing(); + } else + m_scriptElement = 0; + } else + scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition)); + + // JavaScript may have detached the parser + if (isDetached()) + return; + } + m_requestingScript = false; + popCurrentNode(); +} + +void XMLDocumentParser::characters(const xmlChar* s, int len) +{ + if (isStopped()) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendCharactersCallback(s, len); + return; + } + + if (!m_currentNode->isTextNode()) + enterText(); + m_bufferedText.append(s, len); +} + +void XMLDocumentParser::error(ErrorType type, const char* message, va_list args) +{ + if (isStopped()) + return; + +#if COMPILER(MSVC) || COMPILER(RVCT) + char m[1024]; + vsnprintf(m, sizeof(m) - 1, message, args); +#else + char* m; + if (vasprintf(&m, message, args) == -1) + return; +#endif + + if (m_parserPaused) + m_pendingCallbacks->appendErrorCallback(type, reinterpret_cast<const xmlChar*>(m), lineNumber(), columnNumber()); + else + handleError(type, m, lineNumber(), columnNumber()); + +#if !COMPILER(MSVC) && !COMPILER(RVCT) + free(m); +#endif +} + +void XMLDocumentParser::processingInstruction(const xmlChar* target, const xmlChar* data) +{ + if (isStopped()) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendProcessingInstructionCallback(target, data); + return; + } + + exitText(); + + // ### handle exceptions + int exception = 0; + RefPtr<ProcessingInstruction> pi = document()->createProcessingInstruction( + toString(target), toString(data), exception); + if (exception) + return; + + pi->setCreatedByParser(true); + + m_currentNode->deprecatedParserAddChild(pi.get()); + if (m_view && !pi->attached()) + pi->attach(); + + pi->finishParsingChildren(); + +#if ENABLE(XSLT) + m_sawXSLTransform = !m_sawFirstElement && pi->isXSL(); + if (m_sawXSLTransform && !document()->transformSourceDocument()) + stopParsing(); +#endif +} + +void XMLDocumentParser::cdataBlock(const xmlChar* s, int len) +{ + if (isStopped()) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendCDATABlockCallback(s, len); + return; + } + + exitText(); + + RefPtr<Node> newNode = CDATASection::create(document(), toString(s, len)); + m_currentNode->deprecatedParserAddChild(newNode.get()); + if (m_view && !newNode->attached()) + newNode->attach(); +} + +void XMLDocumentParser::comment(const xmlChar* s) +{ + if (isStopped()) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendCommentCallback(s); + return; + } + + exitText(); + + RefPtr<Node> newNode = Comment::create(document(), toString(s)); + m_currentNode->deprecatedParserAddChild(newNode.get()); + if (m_view && !newNode->attached()) + newNode->attach(); +} + +void XMLDocumentParser::startDocument(const xmlChar* version, const xmlChar* encoding, int standalone) +{ + ExceptionCode ec = 0; + + if (version) + document()->setXMLVersion(toString(version), ec); + document()->setXMLStandalone(standalone == 1, ec); // possible values are 0, 1, and -1 + if (encoding) + document()->setXMLEncoding(toString(encoding)); +} + +void XMLDocumentParser::endDocument() +{ + exitText(); +#if ENABLE(XHTMLMP) + m_hasDocTypeDeclaration = false; +#endif +} + +void XMLDocumentParser::internalSubset(const xmlChar* name, const xmlChar* externalID, const xmlChar* systemID) +{ + if (isStopped()) + return; + + if (m_parserPaused) { + m_pendingCallbacks->appendInternalSubsetCallback(name, externalID, systemID); + return; + } + + if (document()) { +#if ENABLE(WML) || ENABLE(XHTMLMP) + String extId = toString(externalID); +#endif +#if ENABLE(WML) + if (isWMLDocument() + && extId != "-//WAPFORUM//DTD WML 1.3//EN" + && extId != "-//WAPFORUM//DTD WML 1.2//EN" + && extId != "-//WAPFORUM//DTD WML 1.1//EN" + && extId != "-//WAPFORUM//DTD WML 1.0//EN") + handleError(fatal, "Invalid DTD Public ID", lineNumber(), columnNumber()); +#endif +#if ENABLE(XHTMLMP) + String dtdName = toString(name); + if (extId == "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" + || extId == "-//WAPFORUM//DTD XHTML Mobile 1.1//EN") { + if (dtdName != HTMLNames::htmlTag.localName()) { + handleError(fatal, "Invalid DOCTYPE declaration, expected 'html' as root element.", lineNumber(), columnNumber()); + return; + } + + if (document()->isXHTMLMPDocument()) + setIsXHTMLMPDocument(true); + else + setIsXHTMLDocument(true); + + m_hasDocTypeDeclaration = true; + } +#endif + + document()->parserAddChild(DocumentType::create(document(), toString(name), toString(externalID), toString(systemID))); + } +} + +static inline XMLDocumentParser* getParser(void* closure) +{ + xmlParserCtxtPtr ctxt = static_cast<xmlParserCtxtPtr>(closure); + return static_cast<XMLDocumentParser*>(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 + UNUSED_PARAM(closure); + + // 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; + + getParser(closure)->startElementNs(localname, prefix, uri, nb_namespaces, namespaces, nb_attributes, nb_defaulted, libxmlAttributes); +} + +static void endElementNsHandler(void* closure, const xmlChar*, const xmlChar*, const xmlChar*) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getParser(closure)->endElementNs(); +} + +static void charactersHandler(void* closure, const xmlChar* s, int len) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getParser(closure)->characters(s, len); +} + +static void processingInstructionHandler(void* closure, const xmlChar* target, const xmlChar* data) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getParser(closure)->processingInstruction(target, data); +} + +static void cdataBlockHandler(void* closure, const xmlChar* s, int len) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getParser(closure)->cdataBlock(s, len); +} + +static void commentHandler(void* closure, const xmlChar* comment) +{ + if (hackAroundLibXMLEntityBug(closure)) + return; + + getParser(closure)->comment(comment); +} + +WTF_ATTRIBUTE_PRINTF(2, 3) +static void warningHandler(void* closure, const char* message, ...) +{ + va_list args; + va_start(args, message); + getParser(closure)->error(XMLDocumentParser::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); + getParser(closure)->error(XMLDocumentParser::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); + getParser(closure)->error(XMLDocumentParser::nonFatal, message, args); + va_end(args); +} + +// Using a static 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 xmlEntityPtr sharedXHTMLEntity() +{ + static xmlEntity entity; + if (!entity.type) { + entity.type = XML_ENTITY_DECL; + entity.orig = sharedXHTMLEntityResult; + entity.content = sharedXHTMLEntityResult; + entity.etype = XML_INTERNAL_PREDEFINED_ENTITY; + } + return &entity; +} + +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); + xmlEntityPtr entity = sharedXHTMLEntity(); + entity->length = value.length(); + entity->name = name; + memcpy(sharedXHTMLEntityResult, value.data(), entity->length + 1); + + return entity; +} + +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 && (getParser(closure)->isXHTMLDocument() +#if ENABLE(XHTMLMP) + || getParser(closure)->isXHTMLMPDocument() +#endif +#if ENABLE(WML) + || getParser(closure)->isWMLDocument() +#endif + )) { + ent = getXHTMLEntity(name); + if (ent) + ent->etype = XML_INTERNAL_GENERAL_ENTITY; + } + + return ent; +} + +static void startDocumentHandler(void* closure) +{ + xmlParserCtxt* ctxt = static_cast<xmlParserCtxt*>(closure); + switchToUTF16(ctxt); + getParser(closure)->startDocument(ctxt->version, ctxt->encoding, ctxt->standalone); + xmlSAX2StartDocument(closure); +} + +static void endDocumentHandler(void* closure) +{ + getParser(closure)->endDocument(); + xmlSAX2EndDocument(closure); +} + +static void internalSubsetHandler(void* closure, const xmlChar* name, const xmlChar* externalID, const xmlChar* systemID) +{ + getParser(closure)->internalSubset(name, externalID, systemID); + xmlSAX2InternalSubset(closure, name, externalID, systemID); +} + +static void externalSubsetHandler(void* closure, const xmlChar*, const xmlChar* externalId, const xmlChar*) +{ + 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") + ) + getParser(closure)->setIsXHTMLDocument(true); // controls if we replace entities or not. +} + +static void ignorableWhitespaceHandler(void*, const xmlChar*, int) +{ + // 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 +} + +void XMLDocumentParser::initializeParserContext(const char* chunk) +{ + 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; + DocumentParser::startParsing(); + m_sawError = false; + m_sawXSLTransform = false; + m_sawFirstElement = false; + + XMLDocumentParserScope scope(document()->cachedResourceLoader()); + if (m_parsingFragment) + m_context = XMLParserContext::createMemoryParser(&sax, this, chunk); + else { + ASSERT(!chunk); + m_context = XMLParserContext::createStringParser(&sax, this); + } +} + +void XMLDocumentParser::doEnd() +{ +#if ENABLE(XSLT) + if (m_sawXSLTransform) { + void* doc = xmlDocPtrForString(document()->cachedResourceLoader(), m_originalSourceForTransform, document()->url().string()); + document()->setTransformSource(new TransformSource(doc)); + + document()->setParsing(false); // Make the doc think it's done, so it will apply xsl sheets. + document()->styleSelectorChanged(RecalcStyleImmediately); + document()->setParsing(true); + DocumentParser::stopParsing(); + } +#endif + + if (isStopped()) + return; + + if (m_context) { + // Tell libxml we're done. + { + XMLDocumentParserScope scope(document()->cachedResourceLoader()); + xmlParseChunk(context(), 0, 0, 1); + } + + m_context = 0; + } +} + +#if ENABLE(XSLT) +void* xmlDocPtrForString(CachedResourceLoader* cachedResourceLoader, 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); + + XMLDocumentParserScope scope(cachedResourceLoader, errorFunc, 0); + 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); + return sourceDoc; +} +#endif + +int XMLDocumentParser::lineNumber() const +{ + // FIXME: The implementation probably returns 1-based int, but method should return 0-based. + return context() ? context()->input->line : 1; +} + +int XMLDocumentParser::columnNumber() const +{ + // FIXME: The implementation probably returns 1-based int, but method should return 0-based. + return context() ? context()->input->col : 1; +} + +TextPosition0 XMLDocumentParser::textPosition() const +{ + xmlParserCtxtPtr context = this->context(); + if (!context) + return TextPosition0::minimumPosition(); + // FIXME: The context probably contains 1-based numbers, but we treat them as 0-based, + // to be consistent with fixme's in lineNumber() and columnNumber + // methods. + return TextPosition0(WTF::ZeroBasedNumber::fromZeroBasedInt(context->input->line), + WTF::ZeroBasedNumber::fromZeroBasedInt(context->input->col)); +} + +// This method has a correct implementation, in contrast to textPosition() method. +// It should replace textPosition(). +TextPosition1 XMLDocumentParser::textPositionOneBased() const +{ + xmlParserCtxtPtr context = this->context(); + if (!context) + return TextPosition1::minimumPosition(); + return TextPosition1(WTF::OneBasedNumber::fromOneBasedInt(context->input->line), + WTF::OneBasedNumber::fromOneBasedInt(context->input->col)); +} + +void XMLDocumentParser::stopParsing() +{ + DocumentParser::stopParsing(); + if (context()) + xmlStopParser(context()); +} + +void XMLDocumentParser::resumeParsing() +{ + ASSERT(!isDetached()); + ASSERT(m_parserPaused); + + m_parserPaused = false; + + // First, execute any pending callbacks + while (!m_pendingCallbacks->isEmpty()) { + m_pendingCallbacks->callAndRemoveFirstCallback(this); + + // A callback paused the parser + if (m_parserPaused) + return; + } + + // Then, write any pending data + SegmentedString rest = m_pendingSrc; + m_pendingSrc.clear(); + append(rest); + + // Finally, if finish() has been called and write() didn't result + // in any further callbacks being queued, call end() + if (m_finishCalled && m_pendingCallbacks->isEmpty()) + end(); +} + +bool XMLDocumentParser::appendFragmentSource(const String& chunk) +{ + ASSERT(!m_context); + ASSERT(m_parsingFragment); + + CString chunkAsUtf8 = chunk.utf8(); + initializeParserContext(chunkAsUtf8.data()); + xmlParseContent(context()); + endDocument(); // Close any open text nodes. + + // FIXME: If this code is actually needed, it should probably move to finish() + // XMLDocumentParserQt has a similar check (m_stream.error() == QXmlStreamReader::PrematureEndOfDocumentError) in doEnd(). + // Check if all the chunk has been processed. + long bytesProcessed = xmlByteConsumed(context()); + if (bytesProcessed == -1 || ((unsigned long)bytesProcessed) != chunkAsUtf8.length()) { + // FIXME: I don't believe we can hit this case without also having seen an error. + // If we hit this ASSERT, we've found a test case which demonstrates the need for this code. + ASSERT(m_sawError); + return false; + } + + // No error if the chunk is well formed or it is not but we have no error. + return context()->wellFormed || !xmlCtxtGetLastError(context()); +} + +// -------------------------------- + +struct AttributeParseState { + HashMap<String, String> attributes; + bool gotAttributes; +}; + +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); + } +} + +HashMap<String, String> parseAttributes(const String& string, bool& attrsOK) +{ + AttributeParseState state; + state.gotAttributes = false; + + xmlSAXHandler sax; + memset(&sax, 0, sizeof(sax)); + sax.startElementNs = attributesStartElementNsHandler; + sax.initialized = XML_SAX2_MAGIC; + RefPtr<XMLParserContext> parser = XMLParserContext::createStringParser(&sax, &state); + String parseString = "<?xml version=\"1.0\"?><attrs " + string + " />"; + xmlParseChunk(parser->context(), reinterpret_cast<const char*>(parseString.characters()), parseString.length() * sizeof(UChar), 1); + attrsOK = state.gotAttributes; + return state.attributes; +} + +} diff --git a/Source/WebCore/dom/XMLDocumentParserQt.cpp b/Source/WebCore/dom/XMLDocumentParserQt.cpp new file mode 100644 index 0000000..d0bf88d --- /dev/null +++ b/Source/WebCore/dom/XMLDocumentParserQt.cpp @@ -0,0 +1,732 @@ +/* + * 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) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Holger Hans Peter Freyther + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "XMLDocumentParser.h" + +#include "CDATASection.h" +#include "CachedScript.h" +#include "Comment.h" +#include "CachedResourceLoader.h" +#include "Document.h" +#include "DocumentFragment.h" +#include "DocumentType.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HTMLEntityParser.h" +#include "HTMLHtmlElement.h" +#include "HTMLLinkElement.h" +#include "HTMLNames.h" +#include "HTMLStyleElement.h" +#include "ProcessingInstruction.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "ScriptableDocumentParser.h" +#include "ScriptController.h" +#include "ScriptElement.h" +#include "ScriptSourceCode.h" +#include "ScriptValue.h" +#include "TextResourceDecoder.h" +#include "TransformSource.h" +#include <QDebug> +#include <wtf/StringExtras.h> +#include <wtf/Threading.h> +#include <wtf/Vector.h> +#include <wtf/text/CString.h> + +#if ENABLE(XHTMLMP) +#include "HTMLNames.h" +#include "HTMLScriptElement.h" +#endif + +using namespace std; + +namespace WebCore { + +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); +} + +// -------------------------------- + +bool XMLDocumentParser::supportsXMLVersion(const String& version) +{ + return version == "1.0"; +} + +XMLDocumentParser::XMLDocumentParser(Document* document, FrameView* frameView) + : ScriptableDocumentParser(document) + , m_view(frameView) + , m_wroteText(false) + , m_currentNode(document) + , m_sawError(false) + , m_sawXSLTransform(false) + , m_sawFirstElement(false) + , m_isXHTMLDocument(false) +#if ENABLE(XHTMLMP) + , m_isXHTMLMPDocument(false) + , m_hasDocTypeDeclaration(false) +#endif + , m_parserPaused(false) + , m_requestingScript(false) + , m_finishCalled(false) + , m_errorCount(0) + , m_lastErrorPosition(TextPosition1::belowRangePosition()) + , m_pendingScript(0) + , m_scriptStartPosition(TextPosition1::belowRangePosition()) + , m_parsingFragment(false) + , m_scriptingPermission(FragmentScriptingAllowed) +{ + m_stream.setEntityResolver(new EntityResolver); +} + +XMLDocumentParser::XMLDocumentParser(DocumentFragment* fragment, Element* parentElement, FragmentScriptingPermission permission) + : ScriptableDocumentParser(fragment->document()) + , m_view(0) + , m_wroteText(false) + , m_currentNode(fragment) + , m_sawError(false) + , m_sawXSLTransform(false) + , m_sawFirstElement(false) + , m_isXHTMLDocument(false) +#if ENABLE(XHTMLMP) + , m_isXHTMLMPDocument(false) + , m_hasDocTypeDeclaration(false) +#endif + , m_parserPaused(false) + , m_requestingScript(false) + , m_finishCalled(false) + , m_errorCount(0) + , m_lastErrorPosition(TextPosition1::belowRangePosition()) + , m_pendingScript(0) + , m_scriptStartPosition(TextPosition1::belowRangePosition()) + , m_parsingFragment(true) + , m_scriptingPermission(permission) +{ + fragment->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; + + QXmlStreamNamespaceDeclarations namespaces; + for (Element* element = elemStack.last(); !elemStack.isEmpty(); elemStack.removeLast()) { + if (NamedNodeMap* 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); + + // 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(); +} + +XMLDocumentParser::~XMLDocumentParser() +{ + clearCurrentNodeStack(); + if (m_pendingScript) + m_pendingScript->removeClient(this); + delete m_stream.entityResolver(); +} + +void XMLDocumentParser::doWrite(const String& parseString) +{ + m_wroteText = true; + + if (document()->decoder() && document()->decoder()->sawError()) { + // If the decoder saw an error, report it as fatal (stops parsing) + handleError(fatal, "Encoding error", lineNumber(), columnNumber()); + return; + } + + QString data(parseString); + if (!data.isEmpty()) { + // JavaScript may cause the parser to detach, + // keep this alive until this function is done. + RefPtr<XMLDocumentParser> protect(this); + + m_stream.addData(data); + parse(); + } + + return; +} + +void XMLDocumentParser::initializeParserContext(const char*) +{ + DocumentParser::startParsing(); + m_sawError = false; + m_sawXSLTransform = false; + m_sawFirstElement = false; +} + +void XMLDocumentParser::doEnd() +{ +#if ENABLE(XSLT) + if (m_sawXSLTransform) { + document()->setTransformSource(new TransformSource(m_originalSourceForTransform)); + document()->setParsing(false); // Make the doc think it's done, so it will apply xsl sheets. + document()->styleSelectorChanged(RecalcStyleImmediately); + document()->setParsing(true); + DocumentParser::stopParsing(); + } +#endif + + if (m_stream.error() == QXmlStreamReader::PrematureEndOfDocumentError + || (m_wroteText && !m_sawFirstElement && !m_sawXSLTransform && !m_sawError)) + handleError(fatal, qPrintable(m_stream.errorString()), lineNumber(), columnNumber()); +} + +int XMLDocumentParser::lineNumber() const +{ + return m_stream.lineNumber(); +} + +int XMLDocumentParser::columnNumber() const +{ + return m_stream.columnNumber(); +} + +TextPosition0 XMLDocumentParser::textPosition() const +{ + return TextPosition0(WTF::ZeroBasedNumber::fromZeroBasedInt(lineNumber()), WTF::ZeroBasedNumber::fromZeroBasedInt(columnNumber())); +} + +// This method incorrectly reinterprets zero-base lineNumber method as one-based number. +// FIXME: This error is kept for compatibility. We should fix it eventually. +TextPosition1 XMLDocumentParser::textPositionOneBased() const +{ + return TextPosition1(WTF::OneBasedNumber::fromOneBasedInt(lineNumber()), WTF::OneBasedNumber::fromOneBasedInt(columnNumber())); +} + +void XMLDocumentParser::stopParsing() +{ + ScriptableDocumentParser::stopParsing(); +} + +void XMLDocumentParser::resumeParsing() +{ + ASSERT(m_parserPaused); + + m_parserPaused = false; + + // First, execute any pending callbacks + parse(); + if (m_parserPaused) + return; + + // Then, write any pending data + SegmentedString rest = m_pendingSrc; + m_pendingSrc.clear(); + append(rest); + + // Finally, if finish() has been called and append() didn't result + // in any further callbacks being queued, call end() + if (m_finishCalled && !m_parserPaused && !m_pendingScript) + end(); +} + +bool XMLDocumentParser::appendFragmentSource(const String& source) +{ + ASSERT(!m_sawFirstElement); + append(String("<qxmlstreamdummyelement>")); + append(source); + append(String("</qxmlstreamdummyelement>")); + return !hasError(); +} + +// -------------------------------- + +struct AttributeParseState { + HashMap<String, String> attributes; + bool gotAttributes; +}; + +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); + } +} + +HashMap<String, String> parseAttributes(const String& string, bool& attrsOK) +{ + AttributeParseState state; + state.gotAttributes = false; + + QXmlStreamReader stream; + QString dummy = QString(QLatin1String("<?xml version=\"1.0\"?><attrs %1 />")).arg(string); + stream.addData(dummy); + while (!stream.atEnd()) { + stream.readNext(); + if (stream.isStartElement()) { + attributesStartElementNsHandler(&state, stream.attributes()); + } + } + attrsOK = state.gotAttributes; + return state.attributes; +} + +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, FragmentScriptingPermission scriptingPermission) +{ + 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:"); + namespaceQName.append(decl.prefix()); + newElement->setAttributeNS("http://www.w3.org/2000/xmlns/", namespaceQName, namespaceURI, ec, scriptingPermission); + if (ec) // exception setting attributes + return; + } +} + +static inline void handleElementAttributes(Element* newElement, const QXmlStreamAttributes &attrs, ExceptionCode& ec, + FragmentScriptingPermission scriptingPermission) +{ + 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, scriptingPermission); + if (ec) // exception setting attributes + return; + } +} + +void XMLDocumentParser::parse() +{ + while (!isStopped() && !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: { +#if ENABLE(XHTMLMP) + if (document()->isXHTMLMPDocument() && !m_hasDocTypeDeclaration) { + handleError(fatal, "DOCTYPE declaration lost.", lineNumber(), columnNumber()); + break; + } +#endif + 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(); +#if ENABLE(XHTMLMP) + m_hasDocTypeDeclaration = true; +#endif + } + break; + case QXmlStreamReader::EntityReference: { + //qDebug()<<"---------- ENTITY = "<<m_stream.name().toString() + // <<", t = "<<m_stream.text().toString(); + if (isXHTMLDocument() +#if ENABLE(XHTMLMP) + || isXHTMLMPDocument() +#endif +#if ENABLE(WML) + || isWMLDocument() +#endif + ) { + 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 XMLDocumentParser::startDocument() +{ + initializeParserContext(); + ExceptionCode ec = 0; + + if (!m_parsingFragment) { + document()->setXMLStandalone(m_stream.isStandaloneDocument(), ec); + + QStringRef version = m_stream.documentVersion(); + if (!version.isEmpty()) + document()->setXMLVersion(version, ec); + QStringRef encoding = m_stream.documentEncoding(); + if (!encoding.isEmpty()) + document()->setXMLEncoding(encoding); + } +} + +void XMLDocumentParser::parseStartElement() +{ + if (!m_sawFirstElement && m_parsingFragment) { + // skip dummy element for fragments + m_sawFirstElement = true; + return; + } + + 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; + } + + QualifiedName qName(prefix, localName, uri); + RefPtr<Element> newElement = document()->createElement(qName, true); + if (!newElement) { + stopParsing(); + return; + } + +#if ENABLE(XHTMLMP) + if (!m_sawFirstElement && isXHTMLMPDocument()) { + // As per 7.1 section of OMA-WAP-XHTMLMP-V1_1-20061020-A.pdf, + // we should make sure that the root element MUST be 'html' and + // ensure the name of the default namespace on the root elment 'html' + // MUST be 'http://www.w3.org/1999/xhtml' + if (localName != HTMLNames::htmlTag.localName()) { + handleError(fatal, "XHTMLMP document expects 'html' as root element.", lineNumber(), columnNumber()); + return; + } + + if (uri.isNull()) { + m_defaultNamespaceURI = HTMLNames::xhtmlNamespaceURI; + uri = m_defaultNamespaceURI; + m_stream.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration(prefix, HTMLNames::xhtmlNamespaceURI)); + } + } +#endif + + bool isFirstElement = !m_sawFirstElement; + m_sawFirstElement = true; + + ExceptionCode ec = 0; + handleElementNamespaces(newElement.get(), m_stream.namespaceDeclarations(), ec, m_scriptingPermission); + if (ec) { + stopParsing(); + return; + } + + handleElementAttributes(newElement.get(), m_stream.attributes(), ec, m_scriptingPermission); + if (ec) { + stopParsing(); + return; + } + + ScriptElement* scriptElement = toScriptElement(newElement.get()); + if (scriptElement) + m_scriptStartPosition = textPositionOneBased(); + + m_currentNode->deprecatedParserAddChild(newElement.get()); + + pushCurrentNode(newElement.get()); + if (m_view && !newElement->attached()) + newElement->attach(); + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (newElement->hasTagName(HTMLNames::htmlTag)) + static_cast<HTMLHtmlElement*>(newElement.get())->insertedByParser(); +#endif + + if (isFirstElement && document()->frame()) + document()->frame()->loader()->dispatchDocumentElementAvailable(); +} + +void XMLDocumentParser::parseEndElement() +{ + exitText(); + + Node* n = m_currentNode; + n->finishParsingChildren(); + + if (m_scriptingPermission == FragmentScriptingNotAllowed && n->isElementNode() && toScriptElement(static_cast<Element*>(n))) { + popCurrentNode(); + ExceptionCode ec; + n->remove(ec); + return; + } + + if (!n->isElementNode() || !m_view) { + if (!m_currentNodeStack.isEmpty()) + popCurrentNode(); + return; + } + + Element* element = static_cast<Element*>(n); + + // The element's parent may have already been removed from document. + // Parsing continues in this case, but scripts aren't executed. + if (!element->inDocument()) { + popCurrentNode(); + return; + } + + ScriptElement* scriptElement = toScriptElement(element); + if (!scriptElement) { + popCurrentNode(); + return; + } + + // don't load external scripts for standalone documents (for now) + ASSERT(!m_pendingScript); + m_requestingScript = true; + +#if ENABLE(XHTMLMP) + if (!scriptElement->shouldExecuteAsJavaScript()) + document()->setShouldProcessNoscriptElement(true); + else +#endif + { + String scriptHref = scriptElement->sourceAttributeValue(); + if (!scriptHref.isEmpty()) { + // we have a src attribute + String scriptCharset = scriptElement->scriptCharset(); + if (element->dispatchBeforeLoadEvent(scriptHref) && + (m_pendingScript = document()->cachedResourceLoader()->requestScript(scriptHref, scriptCharset))) { + m_scriptElement = element; + m_pendingScript->addClient(this); + + // m_pendingScript will be 0 if script was already loaded and ref() executed it + if (m_pendingScript) + pauseParsing(); + } else + m_scriptElement = 0; + } else + scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition)); + } + m_requestingScript = false; + popCurrentNode(); +} + +void XMLDocumentParser::parseCharacters() +{ + if (!m_currentNode->isTextNode()) + enterText(); + ExceptionCode ec = 0; + static_cast<Text*>(m_currentNode)->appendData(m_stream.text(), ec); +} + +void XMLDocumentParser::parseProcessingInstruction() +{ + exitText(); + + // ### handle exceptions + int exception = 0; + RefPtr<ProcessingInstruction> pi = document()->createProcessingInstruction( + m_stream.processingInstructionTarget(), + m_stream.processingInstructionData(), exception); + if (exception) + return; + + pi->setCreatedByParser(true); + + m_currentNode->deprecatedParserAddChild(pi.get()); + if (m_view && !pi->attached()) + pi->attach(); + + pi->finishParsingChildren(); + +#if ENABLE(XSLT) + m_sawXSLTransform = !m_sawFirstElement && pi->isXSL(); + if (m_sawXSLTransform && !document()->transformSourceDocument()) + stopParsing(); +#endif +} + +void XMLDocumentParser::parseCdata() +{ + exitText(); + + RefPtr<Node> newNode = CDATASection::create(document(), m_stream.text()); + + m_currentNode->deprecatedParserAddChild(newNode.get()); + if (m_view && !newNode->attached()) + newNode->attach(); +} + +void XMLDocumentParser::parseComment() +{ + exitText(); + + RefPtr<Node> newNode = Comment::create(document(), m_stream.text()); + + m_currentNode->deprecatedParserAddChild(newNode.get()); + if (m_view && !newNode->attached()) + newNode->attach(); +} + +void XMLDocumentParser::endDocument() +{ +#if ENABLE(XHTMLMP) + m_hasDocTypeDeclaration = false; +#endif +} + +bool XMLDocumentParser::hasError() const +{ + return m_stream.hasError(); +} + +void XMLDocumentParser::parseDtd() +{ + QStringRef name = m_stream.dtdName(); + QStringRef publicId = m_stream.dtdPublicId(); + QStringRef systemId = m_stream.dtdSystemId(); + + //qDebug() << dtd << name << publicId << systemId; + if ((publicId == QLatin1String("-//W3C//DTD XHTML 1.0 Transitional//EN")) + || (publicId == QLatin1String("-//W3C//DTD XHTML 1.1//EN")) + || (publicId == QLatin1String("-//W3C//DTD XHTML 1.0 Strict//EN")) + || (publicId == QLatin1String("-//W3C//DTD XHTML 1.0 Frameset//EN")) + || (publicId == QLatin1String("-//W3C//DTD XHTML Basic 1.0//EN")) + || (publicId == QLatin1String("-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN")) + || (publicId == QLatin1String("-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN")) +#if !ENABLE(XHTMLMP) + || (publicId == QLatin1String("-//WAPFORUM//DTD XHTML Mobile 1.0//EN")) +#endif + ) + setIsXHTMLDocument(true); // controls if we replace entities or not. +#if ENABLE(XHTMLMP) + else if ((publicId == QLatin1String("-//WAPFORUM//DTD XHTML Mobile 1.1//EN")) + || (publicId == QLatin1String("-//WAPFORUM//DTD XHTML Mobile 1.0//EN"))) { + if (AtomicString(name) != HTMLNames::htmlTag.localName()) { + handleError(fatal, "Invalid DOCTYPE declaration, expected 'html' as root element.", lineNumber(), columnNumber()); + return; + } + + if (document()->isXHTMLMPDocument()) // check if the MIME type is correct with this method + setIsXHTMLMPDocument(true); + else + setIsXHTMLDocument(true); + } +#endif +#if ENABLE(WML) + else if (document()->isWMLDocument() + && publicId != QLatin1String("-//WAPFORUM//DTD WML 1.3//EN") + && publicId != QLatin1String("-//WAPFORUM//DTD WML 1.2//EN") + && publicId != QLatin1String("-//WAPFORUM//DTD WML 1.1//EN") + && publicId != QLatin1String("-//WAPFORUM//DTD WML 1.0//EN")) + handleError(fatal, "Invalid DTD Public ID", lineNumber(), columnNumber()); +#endif + if (!m_parsingFragment) + document()->parserAddChild(DocumentType::create(document(), name, publicId, systemId)); + +} +} diff --git a/Source/WebCore/dom/XMLDocumentParserScope.cpp b/Source/WebCore/dom/XMLDocumentParserScope.cpp new file mode 100644 index 0000000..0a473ed --- /dev/null +++ b/Source/WebCore/dom/XMLDocumentParserScope.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 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 "XMLDocumentParserScope.h" + +namespace WebCore { + +CachedResourceLoader* XMLDocumentParserScope::currentCachedResourceLoader = 0; + +XMLDocumentParserScope::XMLDocumentParserScope(CachedResourceLoader* cachedResourceLoader) + : m_oldCachedResourceLoader(currentCachedResourceLoader) +#if ENABLE(XSLT) + , m_oldGenericErrorFunc(xmlGenericError) + , m_oldStructuredErrorFunc(xmlStructuredError) + , m_oldErrorContext(xmlGenericErrorContext) +#endif +{ + currentCachedResourceLoader = cachedResourceLoader; +} + +#if ENABLE(XSLT) +XMLDocumentParserScope::XMLDocumentParserScope(CachedResourceLoader* cachedResourceLoader, xmlGenericErrorFunc genericErrorFunc, xmlStructuredErrorFunc structuredErrorFunc, void* errorContext) + : m_oldCachedResourceLoader(currentCachedResourceLoader) + , m_oldGenericErrorFunc(xmlGenericError) + , m_oldStructuredErrorFunc(xmlStructuredError) + , m_oldErrorContext(xmlGenericErrorContext) +{ + currentCachedResourceLoader = cachedResourceLoader; + if (genericErrorFunc) + xmlSetGenericErrorFunc(errorContext, genericErrorFunc); + if (structuredErrorFunc) + xmlSetStructuredErrorFunc(errorContext, structuredErrorFunc); +} +#endif + +XMLDocumentParserScope::~XMLDocumentParserScope() +{ + currentCachedResourceLoader = m_oldCachedResourceLoader; +#if ENABLE(XSLT) + xmlSetGenericErrorFunc(m_oldErrorContext, m_oldGenericErrorFunc); + xmlSetStructuredErrorFunc(m_oldErrorContext, m_oldStructuredErrorFunc); +#endif +} + +} diff --git a/Source/WebCore/dom/XMLDocumentParserScope.h b/Source/WebCore/dom/XMLDocumentParserScope.h new file mode 100644 index 0000000..58e8a6b --- /dev/null +++ b/Source/WebCore/dom/XMLDocumentParserScope.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 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 XMLDocumentParserScope_h +#define XMLDocumentParserScope_h + +#include <wtf/Noncopyable.h> + +#if ENABLE(XSLT) +#include <libxml/tree.h> +#endif + +namespace WebCore { + + class CachedResourceLoader; + + class XMLDocumentParserScope : public Noncopyable { + public: + XMLDocumentParserScope(CachedResourceLoader* cachedResourceLoader); + ~XMLDocumentParserScope(); + + static CachedResourceLoader* currentCachedResourceLoader; + +#if ENABLE(XSLT) + XMLDocumentParserScope(CachedResourceLoader* cachedResourceLoader, xmlGenericErrorFunc genericErrorFunc, xmlStructuredErrorFunc structuredErrorFunc = 0, void* errorContext = 0); +#endif + + private: + CachedResourceLoader* m_oldCachedResourceLoader; + +#if ENABLE(XSLT) + xmlGenericErrorFunc m_oldGenericErrorFunc; + xmlStructuredErrorFunc m_oldStructuredErrorFunc; + void* m_oldErrorContext; +#endif + }; + +} // namespace WebCore + +#endif // XMLDocumentParserScope_h diff --git a/Source/WebCore/dom/default/PlatformMessagePortChannel.cpp b/Source/WebCore/dom/default/PlatformMessagePortChannel.cpp new file mode 100644 index 0000000..9e0e7dc --- /dev/null +++ b/Source/WebCore/dom/default/PlatformMessagePortChannel.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2009 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "PlatformMessagePortChannel.h" + +#include "MessagePort.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +// MessagePortChannel implementations - just delegate to the PlatformMessagePortChannel. +void MessagePortChannel::createChannel(PassRefPtr<MessagePort> port1, PassRefPtr<MessagePort> port2) +{ + PlatformMessagePortChannel::createChannel(port1, port2); +} + +PassOwnPtr<MessagePortChannel> MessagePortChannel::create(PassRefPtr<PlatformMessagePortChannel> channel) +{ + return new MessagePortChannel(channel); +} + +MessagePortChannel::MessagePortChannel(PassRefPtr<PlatformMessagePortChannel> channel) + : m_channel(channel) +{ +} + +MessagePortChannel::~MessagePortChannel() +{ + // Make sure we close our platform channel when the base is freed, to keep the channel objects from leaking. + m_channel->close(); +} + +bool MessagePortChannel::entangleIfOpen(MessagePort* port) +{ + return m_channel->entangleIfOpen(port); +} + +void MessagePortChannel::disentangle() +{ + m_channel->disentangle(); +} + +void MessagePortChannel::postMessageToRemote(PassOwnPtr<MessagePortChannel::EventData> message) +{ + m_channel->postMessageToRemote(message); +} + +bool MessagePortChannel::tryGetMessageFromRemote(OwnPtr<MessagePortChannel::EventData>& result) +{ + return m_channel->tryGetMessageFromRemote(result); +} + +void MessagePortChannel::close() +{ + m_channel->close(); +} + +bool MessagePortChannel::isConnectedTo(MessagePort* port) +{ + return m_channel->isConnectedTo(port); +} + +bool MessagePortChannel::hasPendingActivity() +{ + return m_channel->hasPendingActivity(); +} + +MessagePort* MessagePortChannel::locallyEntangledPort(const ScriptExecutionContext* context) +{ + return m_channel->locallyEntangledPort(context); +} + +PassRefPtr<PlatformMessagePortChannel> PlatformMessagePortChannel::create(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing) +{ + return adoptRef(new PlatformMessagePortChannel(incoming, outgoing)); +} + +PlatformMessagePortChannel::PlatformMessagePortChannel(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing) + : m_entangledChannel(0) + , m_incomingQueue(incoming) + , m_outgoingQueue(outgoing) + , m_remotePort(0) +{ +} + +PlatformMessagePortChannel::~PlatformMessagePortChannel() +{ +} + +void PlatformMessagePortChannel::createChannel(PassRefPtr<MessagePort> port1, PassRefPtr<MessagePort> port2) +{ + // Create incoming/outgoing queues. + RefPtr<PlatformMessagePortChannel::MessagePortQueue> queue1 = PlatformMessagePortChannel::MessagePortQueue::create(); + RefPtr<PlatformMessagePortChannel::MessagePortQueue> queue2 = PlatformMessagePortChannel::MessagePortQueue::create(); + + // Create proxies for each endpoint. + RefPtr<PlatformMessagePortChannel> channel1 = PlatformMessagePortChannel::create(queue1, queue2); + RefPtr<PlatformMessagePortChannel> channel2 = PlatformMessagePortChannel::create(queue2, queue1); + + // Entangle the two endpoints. + channel1->setEntangledChannel(channel2); + channel2->setEntangledChannel(channel1); + + // Now entangle the proxies with the appropriate local ports. + port1->entangle(MessagePortChannel::create(channel2)); + port2->entangle(MessagePortChannel::create(channel1)); +} + +bool PlatformMessagePortChannel::entangleIfOpen(MessagePort* port) +{ + // We can't call member functions on our remote pair while holding our mutex or we'll deadlock, but we need to guard against the remote port getting closed/freed, so create a standalone reference. + RefPtr<PlatformMessagePortChannel> remote = entangledChannel(); + if (!remote) + return false; + remote->setRemotePort(port); + return true; +} + +void PlatformMessagePortChannel::disentangle() +{ + RefPtr<PlatformMessagePortChannel> remote = entangledChannel(); + if (remote) + remote->setRemotePort(0); +} + +void PlatformMessagePortChannel::setRemotePort(MessagePort* port) +{ + MutexLocker lock(m_mutex); + // Should never set port if it is already set. + ASSERT(!port || !m_remotePort); + m_remotePort = port; +} + +MessagePort* PlatformMessagePortChannel::remotePort() +{ + MutexLocker lock(m_mutex); + return m_remotePort; +} + +PassRefPtr<PlatformMessagePortChannel> PlatformMessagePortChannel::entangledChannel() +{ + MutexLocker lock(m_mutex); + return m_entangledChannel; +} + +void PlatformMessagePortChannel::setEntangledChannel(PassRefPtr<PlatformMessagePortChannel> remote) +{ + MutexLocker lock(m_mutex); + // Should only be set as part of initial creation/entanglement. + if (remote) + ASSERT(!m_entangledChannel.get()); + m_entangledChannel = remote; +} + +void PlatformMessagePortChannel::postMessageToRemote(PassOwnPtr<MessagePortChannel::EventData> message) +{ + MutexLocker lock(m_mutex); + if (!m_outgoingQueue) + return; + bool wasEmpty = m_outgoingQueue->appendAndCheckEmpty(message); + if (wasEmpty && m_remotePort) + m_remotePort->messageAvailable(); +} + +bool PlatformMessagePortChannel::tryGetMessageFromRemote(OwnPtr<MessagePortChannel::EventData>& result) +{ + MutexLocker lock(m_mutex); + result = m_incomingQueue->tryGetMessage(); + return result; +} + +bool PlatformMessagePortChannel::isConnectedTo(MessagePort* port) +{ + MutexLocker lock(m_mutex); + return m_remotePort == port; +} + +// Closes the port so no further messages can be sent from either end. +void PlatformMessagePortChannel::close() +{ + RefPtr<PlatformMessagePortChannel> remote = entangledChannel(); + if (!remote) + return; + closeInternal(); + remote->closeInternal(); +} + +void PlatformMessagePortChannel::closeInternal() +{ + MutexLocker lock(m_mutex); + // Disentangle ourselves from the other end. We still maintain a reference to our incoming queue, since previously-existing messages should still be delivered. + m_remotePort = 0; + m_entangledChannel = 0; + m_outgoingQueue = 0; +} + +bool PlatformMessagePortChannel::hasPendingActivity() +{ + MutexLocker lock(m_mutex); + return !m_incomingQueue->isEmpty(); +} + +MessagePort* PlatformMessagePortChannel::locallyEntangledPort(const ScriptExecutionContext* context) +{ + MutexLocker lock(m_mutex); + // See if both contexts are run by the same thread (are the same context, or are both documents). + if (m_remotePort) { + // The remote port's ScriptExecutionContext is guaranteed not to change here - MessagePort::contextDestroyed() will close the port before the context goes away, and close() will block because we are holding the mutex. + ScriptExecutionContext* remoteContext = m_remotePort->scriptExecutionContext(); + if (remoteContext == context || (remoteContext && remoteContext->isDocument() && context->isDocument())) + return m_remotePort; + } + return 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/default/PlatformMessagePortChannel.h b/Source/WebCore/dom/default/PlatformMessagePortChannel.h new file mode 100644 index 0000000..2aad952 --- /dev/null +++ b/Source/WebCore/dom/default/PlatformMessagePortChannel.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2009 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 PlatformMessagePortChannel_h +#define PlatformMessagePortChannel_h + +#include "MessagePortChannel.h" + +#include <wtf/MessageQueue.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + + class MessagePort; + + // PlatformMessagePortChannel is a platform-dependent interface to the remote side of a message channel. + // This default implementation supports multiple threads running within a single process. Implementations for multi-process platforms should define these public APIs in their own platform-specific PlatformMessagePortChannel file. + // The goal of this implementation is to eliminate contention except when cloning or closing the port, so each side of the channel has its own separate mutex. + class PlatformMessagePortChannel : public ThreadSafeShared<PlatformMessagePortChannel> { + public: + static void createChannel(PassRefPtr<MessagePort>, PassRefPtr<MessagePort>); + + // APIs delegated from MessagePortChannel.h + bool entangleIfOpen(MessagePort*); + void disentangle(); + void postMessageToRemote(PassOwnPtr<MessagePortChannel::EventData>); + bool tryGetMessageFromRemote(OwnPtr<MessagePortChannel::EventData>&); + void close(); + bool isConnectedTo(MessagePort*); + bool hasPendingActivity(); + MessagePort* locallyEntangledPort(const ScriptExecutionContext*); + + // Wrapper for MessageQueue that allows us to do thread safe sharing by two proxies. + class MessagePortQueue : public ThreadSafeShared<MessagePortQueue> { + public: + static PassRefPtr<MessagePortQueue> create() { return adoptRef(new MessagePortQueue()); } + + PassOwnPtr<MessagePortChannel::EventData> tryGetMessage() + { + return m_queue.tryGetMessage(); + } + + bool appendAndCheckEmpty(PassOwnPtr<MessagePortChannel::EventData> message) + { + return m_queue.appendAndCheckEmpty(message); + } + + bool isEmpty() + { + return m_queue.isEmpty(); + } + + private: + MessagePortQueue() { } + + MessageQueue<MessagePortChannel::EventData> m_queue; + }; + + ~PlatformMessagePortChannel(); + + private: + static PassRefPtr<PlatformMessagePortChannel> create(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing); + PlatformMessagePortChannel(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing); + + PassRefPtr<PlatformMessagePortChannel> entangledChannel(); + void setEntangledChannel(PassRefPtr<PlatformMessagePortChannel>); + + void setRemotePort(MessagePort*); + MessagePort* remotePort(); + void closeInternal(); + + // Mutex used to ensure exclusive access to the object internals. + Mutex m_mutex; + + // Pointer to our entangled pair - cleared when close() is called. + RefPtr<PlatformMessagePortChannel> m_entangledChannel; + + // Reference to the message queue for the (local) entangled port. + RefPtr<MessagePortQueue> m_incomingQueue; + RefPtr<MessagePortQueue> m_outgoingQueue; + + // The port we are connected to (the remote port) - this is the port that is notified when new messages arrive. + MessagePort* m_remotePort; + }; + +} // namespace WebCore + +#endif // PlatformMessagePortChannel_h diff --git a/Source/WebCore/dom/make_names.pl b/Source/WebCore/dom/make_names.pl new file mode 100755 index 0000000..836137e --- /dev/null +++ b/Source/WebCore/dom/make_names.pl @@ -0,0 +1,1098 @@ +#!/usr/bin/perl -w + +# Copyright (C) 2005, 2006, 2007, 2009 Apple Inc. All rights reserved. +# Copyright (C) 2009, Julien Chaffraix <jchaffraix@webkit.org> +# Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.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. + +use strict; + +use Config; +use Getopt::Long; +use File::Path; +use IO::File; +use InFilesParser; + +sub readTags($$); +sub readAttrs($$); + +my $printFactory = 0; +my $printWrapperFactory = 0; +my $printWrapperFactoryV8 = 0; +my $tagsFile = ""; +my $attrsFile = ""; +my $outputDir = "."; +my %parsedTags = (); +my %parsedAttrs = (); +my %enabledTags = (); +my %enabledAttrs = (); +my %allTags = (); +my %allAttrs = (); +my %parameters = (); +my $extraDefines = 0; +my %extensionAttrs = (); + +require Config; + +my $gccLocation = ""; +if ($ENV{CC}) { + $gccLocation = $ENV{CC}; +} elsif (($Config::Config{'osname'}) =~ /solaris/i) { + $gccLocation = "/usr/sfw/bin/gcc"; +} else { + $gccLocation = "/usr/bin/gcc"; +} +my $preprocessor = $gccLocation . " -E -P -x c++"; + +GetOptions( + 'tags=s' => \$tagsFile, + 'attrs=s' => \$attrsFile, + 'factory' => \$printFactory, + 'outputDir=s' => \$outputDir, + 'extraDefines=s' => \$extraDefines, + 'preprocessor=s' => \$preprocessor, + 'wrapperFactory' => \$printWrapperFactory, + 'wrapperFactoryV8' => \$printWrapperFactoryV8 +); + +die "You must specify at least one of --tags <file> or --attrs <file>" unless (length($tagsFile) || length($attrsFile)); + +if (length($tagsFile)) { + %allTags = %{readTags($tagsFile, 0)}; + %enabledTags = %{readTags($tagsFile, 1)}; +} + +if (length($attrsFile)) { + %allAttrs = %{readAttrs($attrsFile, 0)}; + %enabledAttrs = %{readAttrs($attrsFile, 1)}; +} + +die "You must specify a namespace (e.g. SVG) for <namespace>Names.h" unless $parameters{namespace}; +die "You must specify a namespaceURI (e.g. http://www.w3.org/2000/svg)" unless $parameters{namespaceURI}; + +$parameters{namespacePrefix} = $parameters{namespace} unless $parameters{namespacePrefix}; + +mkpath($outputDir); +my $namesBasePath = "$outputDir/$parameters{namespace}Names"; +my $factoryBasePath = "$outputDir/$parameters{namespace}ElementFactory"; +my $wrapperFactoryFileName = "$parameters{namespace}ElementWrapperFactory"; + +printNamesHeaderFile("$namesBasePath.h"); +printNamesCppFile("$namesBasePath.cpp"); + +if ($printFactory) { + printFactoryCppFile("$factoryBasePath.cpp"); + printFactoryHeaderFile("$factoryBasePath.h"); +} + +die "You cannot specify both --wrapperFactory and --wrapperFactoryV8" if $printWrapperFactory && $printWrapperFactoryV8; +my $wrapperFactoryType = ""; +if ($printWrapperFactory) { + $wrapperFactoryType = "JS"; +} elsif ($printWrapperFactoryV8) { + $wrapperFactoryType = "V8"; +} + +if ($wrapperFactoryType) { + printWrapperFactoryCppFile($outputDir, $wrapperFactoryType, $wrapperFactoryFileName); + printWrapperFactoryHeaderFile($outputDir, $wrapperFactoryType, $wrapperFactoryFileName); +} + +### Hash initialization + +sub defaultTagPropertyHash +{ + return ( + 'constructorNeedsCreatedByParser' => 0, + 'constructorNeedsFormElement' => 0, + 'interfaceName' => defaultInterfaceName($_[0]), + # By default, the JSInterfaceName is the same as the interfaceName. + 'JSInterfaceName' => defaultInterfaceName($_[0]), + 'mapToTagName' => '', + 'wrapperOnlyIfMediaIsAvailable' => 0, + 'conditional' => 0 + ); +} + +sub defaultParametersHash +{ + return ( + 'namespace' => '', + 'namespacePrefix' => '', + 'namespaceURI' => '', + 'guardFactoryWith' => '', + 'tagsNullNamespace' => 0, + 'attrsNullNamespace' => 0 + ); +} + +sub defaultInterfaceName +{ + die "No namespace found" if !$parameters{namespace}; + return $parameters{namespace} . upperCaseName($_[0]) . "Element" +} + +### Parsing handlers + +sub tagsHandler +{ + my ($tag, $property, $value) = @_; + + $tag =~ s/-/_/g; + + # Initialize default property values. + $parsedTags{$tag} = { defaultTagPropertyHash($tag) } if !defined($parsedTags{$tag}); + + if ($property) { + die "Unknown property $property for tag $tag\n" if !defined($parsedTags{$tag}{$property}); + + # The code relies on JSInterfaceName deriving from interfaceName to check for custom JSInterfaceName. + # So override JSInterfaceName if it was not already set. + $parsedTags{$tag}{JSInterfaceName} = $value if $property eq "interfaceName" && $parsedTags{$tag}{JSInterfaceName} eq $parsedTags{$tag}{interfaceName}; + + $parsedTags{$tag}{$property} = $value; + } +} + +sub attrsHandler +{ + my ($attr, $property, $value) = @_; + # Translate HTML5 extension attributes of the form 'x-webkit-feature' to 'webkitfeature'. + # We don't just check for the 'x-' prefix because there are attributes such as x-height + # which should follow the default path below. + if ($attr =~ m/^x-webkit-(.*)/) { + my $newAttr = "webkit$1"; + $extensionAttrs{$newAttr} = $attr; + $attr = $newAttr; + } + $attr =~ s/-/_/g; + + # Initialize default properties' values. + $parsedAttrs{$attr} = {} if !defined($parsedAttrs{$attr}); + + if ($property) { + die "Unknown property $property for attribute $attr\n" if !defined($parsedAttrs{$attr}{$property}); + $parsedAttrs{$attr}{$property} = $value; + } +} + +sub parametersHandler +{ + my ($parameter, $value) = @_; + + # Initialize default properties' values. + %parameters = defaultParametersHash() if !(keys %parameters); + + die "Unknown parameter $parameter for tags/attrs\n" if !defined($parameters{$parameter}); + $parameters{$parameter} = $value; +} + +## Support routines + +sub preprocessorCommand() +{ + return $preprocessor if $extraDefines eq 0; + return $preprocessor . " -D" . join(" -D", split(" ", $extraDefines)); +} + +sub readNames($$$$) +{ + my ($namesFile, $hashToFillRef, $handler, $usePreprocessor) = @_; + + my $names = new IO::File; + if ($usePreprocessor) { + open($names, preprocessorCommand() . " " . $namesFile . "|") or die "Failed to open file: $namesFile"; + } else { + open($names, $namesFile) or die "Failed to open file: $namesFile"; + } + + my $InParser = InFilesParser->new(); + $InParser->parse($names, \¶metersHandler, $handler); + + close($names); + die "Failed to read names from file: $namesFile" if (keys %{$hashToFillRef} == 0); + return $hashToFillRef; +} + +sub readAttrs($$) +{ + my ($namesFile, $usePreprocessor) = @_; + %parsedAttrs = (); + return readNames($namesFile, \%parsedAttrs, \&attrsHandler, $usePreprocessor); +} + +sub readTags($$) +{ + my ($namesFile, $usePreprocessor) = @_; + %parsedTags = (); + return readNames($namesFile, \%parsedTags, \&tagsHandler, $usePreprocessor); +} + +sub printMacros +{ + my ($F, $macro, $suffix, $namesRef) = @_; + my %names = %$namesRef; + + for my $name (sort keys %$namesRef) { + print F "$macro $name","$suffix;\n"; + } +} + +sub usesDefaultWrapper +{ + my $tagName = shift; + return $tagName eq $parameters{namespace} . "Element"; +} + +# Build a direct mapping from the tags to the Element to create, excluding +# Element that have not constructor. +sub buildConstructorMap +{ + my %tagConstructorMap = (); + for my $tagName (keys %enabledTags) { + my $interfaceName = $enabledTags{$tagName}{interfaceName}; + next if (usesDefaultWrapper($interfaceName)); + + if ($enabledTags{$tagName}{mapToTagName}) { + die "Cannot handle multiple mapToTagName for $tagName\n" if $enabledTags{$enabledTags{$tagName}{mapToTagName}}{mapToTagName}; + $interfaceName = $enabledTags{ $enabledTags{$tagName}{mapToTagName} }{interfaceName}; + } + + # Chop the string to keep the interesting part. + $interfaceName =~ s/$parameters{namespace}(.*)Element/$1/; + $tagConstructorMap{$tagName} = lc($interfaceName); + } + + return %tagConstructorMap; +} + +# Helper method that print the constructor's signature avoiding +# unneeded arguments. +sub printConstructorSignature +{ + my ($F, $tagName, $constructorName, $constructorTagName) = @_; + + print F "static PassRefPtr<$parameters{namespace}Element> ${constructorName}Constructor(const QualifiedName& $constructorTagName, Document* document"; + if ($parameters{namespace} eq "HTML") { + print F ", HTMLFormElement*"; + print F " formElement" if $enabledTags{$tagName}{constructorNeedsFormElement}; + } + print F ", bool"; + print F " createdByParser" if $enabledTags{$tagName}{constructorNeedsCreatedByParser}; + print F ")\n{\n"; +} + +# Helper method to dump the constructor interior and call the +# Element constructor with the right arguments. +# The variable names should be kept in sync with the previous method. +sub printConstructorInterior +{ + my ($F, $tagName, $interfaceName, $constructorTagName) = @_; + + # Handle media elements. + if ($enabledTags{$tagName}{wrapperOnlyIfMediaIsAvailable}) { + print F <<END + Settings* settings = document->settings(); + if (!MediaPlayer::isAvailable() || (settings && !settings->isMediaEnabled())) + return HTMLElement::create($constructorTagName, document); + +END +; + } + + # Call the constructor with the right parameters. + print F " return ${interfaceName}::create($constructorTagName, document"; + print F ", formElement" if $enabledTags{$tagName}{constructorNeedsFormElement}; + print F ", createdByParser" if $enabledTags{$tagName}{constructorNeedsCreatedByParser}; + print F ");\n}\n\n"; +} + +sub printConstructors +{ + my ($F, $tagConstructorMapRef) = @_; + my %tagConstructorMap = %$tagConstructorMapRef; + + # This is to avoid generating the same constructor several times. + my %uniqueTags = (); + for my $tagName (sort keys %tagConstructorMap) { + my $interfaceName = $enabledTags{$tagName}{interfaceName}; + + # Ignore the mapped tag + # FIXME: It could be moved inside this loop but was split for readibility. + next if (defined($uniqueTags{$interfaceName}) || $enabledTags{$tagName}{mapToTagName}); + + $uniqueTags{$interfaceName} = '1'; + + my $conditional = $enabledTags{$tagName}{conditional}; + if ($conditional) { + my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; + print F "#if ${conditionalString}\n\n"; + } + + printConstructorSignature($F, $tagName, $tagConstructorMap{$tagName}, "tagName"); + printConstructorInterior($F, $tagName, $interfaceName, "tagName"); + + if ($conditional) { + print F "#endif\n"; + } + } + + # Mapped tag name uses a special wrapper to keep their prefix and namespaceURI while using the mapped localname. + for my $tagName (sort keys %tagConstructorMap) { + if ($enabledTags{$tagName}{mapToTagName}) { + my $mappedName = $enabledTags{$tagName}{mapToTagName}; + printConstructorSignature($F, $mappedName, $mappedName . "To" . $tagName, "tagName"); + printConstructorInterior($F, $mappedName, $enabledTags{$mappedName}{interfaceName}, "QualifiedName(tagName.prefix(), ${mappedName}Tag.localName(), tagName.namespaceURI())"); + } + } +} + +sub printFunctionInits +{ + my ($F, $tagConstructorMap) = @_; + my %tagConstructorMap = %$tagConstructorMap; + + for my $tagName (sort keys %tagConstructorMap) { + + my $conditional = $enabledTags{$tagName}{conditional}; + if ($conditional) { + my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; + print F "#if ${conditionalString}\n"; + } + + if ($enabledTags{$tagName}{mapToTagName}) { + print F " addTag(${tagName}Tag, $enabledTags{$tagName}{mapToTagName}To${tagName}Constructor);\n"; + } else { + print F " addTag(${tagName}Tag, $tagConstructorMap{$tagName}Constructor);\n"; + } + + if ($conditional) { + print F "#endif\n\n"; + } + } +} + +sub svgCapitalizationHacks +{ + my $name = shift; + + $name = "FE" . ucfirst $1 if $name =~ /^fe(.+)$/; + + return $name; +} + +sub upperCaseName +{ + my $name = shift; + + $name = svgCapitalizationHacks($name) if ($parameters{namespace} eq "SVG"); + + while ($name =~ /^(.*?)_(.*)/) { + $name = $1 . ucfirst $2; + } + + return ucfirst $name; +} + +sub printLicenseHeader +{ + my $F = shift; + print F "/* + * THIS FILE WAS AUTOMATICALLY GENERATED, DO NOT EDIT. + * + * This file was generated by the dom/make_names.pl script. + * + * Copyright (C) 2005, 2006, 2007, 2008, 2009 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. + */ + +"; +} + +sub printNamesHeaderFile +{ + my ($headerPath) = shift; + my $F; + open F, ">$headerPath"; + + printLicenseHeader($F); + print F "#ifndef DOM_$parameters{namespace}NAMES_H\n"; + print F "#define DOM_$parameters{namespace}NAMES_H\n\n"; + print F "#include \"QualifiedName.h\"\n\n"; + + print F "namespace WebCore {\n\n namespace $parameters{namespace}Names {\n\n"; + + my $lowerNamespace = lc($parameters{namespacePrefix}); + print F "#ifndef DOM_$parameters{namespace}NAMES_HIDE_GLOBALS\n"; + print F "// Namespace\n"; + print F "extern const WTF::AtomicString ${lowerNamespace}NamespaceURI;\n\n"; + + if (keys %allTags) { + print F "// Tags\n"; + printMacros($F, "extern const WebCore::QualifiedName", "Tag", \%allTags); + } + + if (keys %allAttrs) { + print F "// Attributes\n"; + printMacros($F, "extern const WebCore::QualifiedName", "Attr", \%allAttrs); + } + print F "#endif\n\n"; + + if (keys %allTags) { + print F "WebCore::QualifiedName** get$parameters{namespace}Tags(size_t* size);\n"; + } + + if (keys %allAttrs) { + print F "WebCore::QualifiedName** get$parameters{namespace}Attrs(size_t* size);\n"; + } + + print F "\nvoid 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($parameters{namespacePrefix}); + +print F "#include \"config.h\"\n"; + +print F "#ifdef SKIP_STATIC_CONSTRUCTORS_ON_GCC\n"; +print F "#define DOM_$parameters{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 \"$parameters{namespace}Names.h\"\n\n"; +print F "#include <wtf/StaticConstructors.h>\n"; + +print F "namespace WebCore {\n\n namespace $parameters{namespace}Names { + +using namespace WebCore; + +DEFINE_GLOBAL(AtomicString, ${lowerNamespace}NamespaceURI, \"$parameters{namespaceURI}\") +"; + + if (keys %allTags) { + print F "// Tags\n"; + for my $name (sort keys %allTags) { + print F "DEFINE_GLOBAL(QualifiedName, ", $name, "Tag, nullAtom, \"$name\", ${lowerNamespace}NamespaceURI);\n"; + } + + print F "\n\nWebCore::QualifiedName** get$parameters{namespace}Tags(size_t* size)\n"; + print F "{\n static WebCore::QualifiedName* $parameters{namespace}Tags[] = {\n"; + for my $name (sort keys %allTags) { + print F " (WebCore::QualifiedName*)&${name}Tag,\n"; + } + print F " };\n"; + print F " *size = ", scalar(keys %allTags), ";\n"; + print F " return $parameters{namespace}Tags;\n"; + print F "}\n"; + } + + if (keys %allAttrs) { + print F "\n// Attributes\n"; + for my $name (sort keys %allAttrs) { + print F "DEFINE_GLOBAL(QualifiedName, ", $name, "Attr, nullAtom, \"$name\", ${lowerNamespace}NamespaceURI);\n"; + } + print F "\n\nWebCore::QualifiedName** get$parameters{namespace}Attrs(size_t* size)\n"; + print F "{\n static WebCore::QualifiedName* $parameters{namespace}Attr[] = {\n"; + for my $name (sort keys %allAttrs) { + print F " (WebCore::QualifiedName*)&${name}Attr,\n"; + } + print F " };\n"; + print F " *size = ", scalar(keys %allAttrs), ";\n"; + print F " return $parameters{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(\"$parameters{namespaceURI}\");\n\n"); + + print(F " // Namespace\n"); + print(F " new ((void*)&${lowerNamespace}NamespaceURI) AtomicString(${lowerNamespace}NS);\n\n"); + if (keys %allTags) { + my $tagsNamespace = $parameters{tagsNullNamespace} ? "nullAtom" : "${lowerNamespace}NS"; + printDefinitions($F, \%allTags, "tags", $tagsNamespace); + } + if (keys %allAttrs) { + my $attrsNamespace = $parameters{attrsNullNamespace} ? "nullAtom" : "${lowerNamespace}NS"; + printDefinitions($F, \%allAttrs, "attributes", $attrsNamespace); + } + + print F "}\n\n} }\n\n"; + close F; +} + +sub printJSElementIncludes +{ + my $F = shift; + my $wrapperFactoryType = shift; + + my %tagsSeen; + for my $tagName (sort keys %enabledTags) { + my $JSInterfaceName = $enabledTags{$tagName}{JSInterfaceName}; + next if defined($tagsSeen{$JSInterfaceName}) || usesDefaultJSWrapper($tagName); + $tagsSeen{$JSInterfaceName} = 1; + + print F "#include \"${wrapperFactoryType}${JSInterfaceName}.h\"\n"; + } +} + +sub printElementIncludes +{ + my $F = shift; + + my %tagsSeen; + for my $tagName (sort keys %enabledTags) { + my $interfaceName = $enabledTags{$tagName}{interfaceName}; + next if defined($tagsSeen{$interfaceName}); + $tagsSeen{$interfaceName} = 1; + + print F "#include \"${interfaceName}.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 (sort keys %$namesRef) { + my $realName = $extensionAttrs{$name}; + if (!$realName) { + $realName = $name; + $realName =~ s/_/-/g; + } + print F " new ((void*)&$name","${shortCamelType}) QualifiedName(nullAtom, \"$realName\", $namespaceURI);\n"; + } +} + +## ElementFactory routines + +sub printFactoryCppFile +{ + my $cppPath = shift; + my $F; + open F, ">$cppPath"; + +printLicenseHeader($F); + +print F <<END +#include "config.h" +END +; + +print F "\n#if $parameters{guardFactoryWith}\n\n" if $parameters{guardFactoryWith}; + +print F <<END +#include "$parameters{namespace}ElementFactory.h" +#include "$parameters{namespace}Names.h" +END +; + +printElementIncludes($F); + +print F <<END +#include <wtf/HashMap.h> + +#if ENABLE(DASHBOARD_SUPPORT) || ENABLE(VIDEO) +#include "Document.h" +#include "Settings.h" +#endif + +namespace WebCore { + +using namespace $parameters{namespace}Names; + +END +; + +print F "typedef PassRefPtr<$parameters{namespace}Element> (*ConstructorFunction)(const QualifiedName&, Document*"; +print F ", HTMLFormElement*" if $parameters{namespace} eq "HTML"; +print F ", bool createdByParser);\n"; +print F <<END +typedef HashMap<AtomicStringImpl*, ConstructorFunction> FunctionMap; + +static FunctionMap* gFunctionMap = 0; + +END +; + +my %tagConstructorMap = buildConstructorMap(); + +printConstructors($F, \%tagConstructorMap); + +print F <<END +static void addTag(const QualifiedName& tag, ConstructorFunction func) +{ + gFunctionMap->set(tag.localName().impl(), func); +} + +static void createFunctionMap() +{ + ASSERT(!gFunctionMap); + + // Create the table. + gFunctionMap = new FunctionMap; + + // Populate it with constructor functions. +END +; + +printFunctionInits($F, \%tagConstructorMap); + +print F "}\n"; + +print F "\nPassRefPtr<$parameters{namespace}Element> $parameters{namespace}ElementFactory::create$parameters{namespace}Element(const QualifiedName& qName, Document* document"; +print F ", HTMLFormElement* formElement" if $parameters{namespace} eq "HTML"; +print F ", bool createdByParser)\n{\n"; + +print F <<END + if (!document) + return 0; + +END +; + +if ($parameters{namespace} ne "HTML") { +print F <<END +#if ENABLE(DASHBOARD_SUPPORT) + Settings* settings = document->settings(); + if (settings && settings->usesDashboardBackwardCompatibilityMode()) + return 0; +#endif +END +; + +} + +print F <<END + if (!gFunctionMap) + createFunctionMap(); + if (ConstructorFunction function = gFunctionMap->get(qName.localName().impl())) +END +; + +if ($parameters{namespace} eq "HTML") { + print F " return function(qName, document, formElement, createdByParser);\n"; +} else { + print F " return function(qName, document, createdByParser);\n"; +} + +print F " return $parameters{namespace}Element::create(qName, document);\n"; + +print F <<END +} + +} // namespace WebCore + +END +; + + print F "#endif\n" if $parameters{guardFactoryWith}; + + close F; +} + +sub printFactoryHeaderFile +{ + my $headerPath = shift; + my $F; + open F, ">$headerPath"; + + printLicenseHeader($F); + + print F<<END +#ifndef $parameters{namespace}ElementFactory_h +#define $parameters{namespace}ElementFactory_h + +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + class Element; + class Document; + class QualifiedName; +} + +namespace WebCore { + + class $parameters{namespace}Element; +END +; + +print F " class HTMLFormElement;\n" if $parameters{namespace} eq "HTML"; + +print F<<END + // 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 $parameters{namespace}ElementFactory { + public: + PassRefPtr<Element> createElement(const WebCore::QualifiedName&, WebCore::Document*, bool createdByParser = true); +END +; +print F " static PassRefPtr<$parameters{namespace}Element> create$parameters{namespace}Element(const WebCore::QualifiedName&, WebCore::Document*"; +print F ", HTMLFormElement* = 0" if $parameters{namespace} eq "HTML"; +print F ", bool createdByParser = true);\n"; + +printf F<<END + }; +} + +#endif // $parameters{namespace}ElementFactory_h + +END +; + + close F; +} + +## Wrapper Factory routines + +sub usesDefaultJSWrapper +{ + my $name = shift; + + # A tag reuses the default wrapper if its JSInterfaceName matches the default namespace Element. + return $enabledTags{$name}{JSInterfaceName} eq $parameters{namespace} . "Element" || $enabledTags{$name}{JSInterfaceName} eq "HTMLNoScriptElement"; +} + +sub printWrapperFunctions +{ + my $F = shift; + my $wrapperFactoryType = shift; + + my %tagsSeen; + for my $tagName (sort keys %enabledTags) { + # Avoid defining the same wrapper method twice. + my $JSInterfaceName = $enabledTags{$tagName}{JSInterfaceName}; + next if defined($tagsSeen{$JSInterfaceName}) || usesDefaultJSWrapper($tagName); + $tagsSeen{$JSInterfaceName} = 1; + + my $conditional = $enabledTags{$tagName}{conditional}; + if ($conditional) { + my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; + print F "#if ${conditionalString}\n\n"; + } + + if ($wrapperFactoryType eq "JS") { + # Hack for the media tags + # FIXME: This should have been done via a CustomWrapper attribute and a separate *Custom file. + if ($enabledTags{$tagName}{wrapperOnlyIfMediaIsAvailable}) { + print F <<END +static JSNode* create${JSInterfaceName}Wrapper(ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element) +{ + Settings* settings = element->document()->settings(); + if (!MediaPlayer::isAvailable() || (settings && !settings->isMediaEnabled())) + return CREATE_DOM_NODE_WRAPPER(exec, globalObject, $parameters{namespace}Element, element.get()); + return CREATE_DOM_NODE_WRAPPER(exec, globalObject, ${JSInterfaceName}, element.get()); +} + +END +; + } else { + print F <<END +static JSNode* create${JSInterfaceName}Wrapper(ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element) +{ + return CREATE_DOM_NODE_WRAPPER(exec, globalObject, ${JSInterfaceName}, element.get()); +} + +END +; + } + } elsif ($wrapperFactoryType eq "V8") { + if ($enabledTags{$tagName}{wrapperOnlyIfMediaIsAvailable}) { + print F <<END +static v8::Handle<v8::Value> create${JSInterfaceName}Wrapper($parameters{namespace}Element* element) +{ + Settings* settings = element->document()->settings(); + if (!MediaPlayer::isAvailable() || (settings && !settings->isMediaEnabled())) + return V8$parameters{namespace}Element::wrap(element); + return toV8(static_cast<${JSInterfaceName}*>(element)); +} + +END +; + } else { + print F <<END +static v8::Handle<v8::Value> create${JSInterfaceName}Wrapper($parameters{namespace}Element* element) +{ + return toV8(static_cast<${JSInterfaceName}*>(element)); +} + + +END +; + } + } + + if ($conditional) { + print F "#endif\n\n"; + } + } +} + +sub printWrapperFactoryCppFile +{ + my $outputDir = shift; + my $wrapperFactoryType = shift; + my $wrapperFactoryFileName = shift; + my $F; + open F, ">" . $outputDir . "/" . $wrapperFactoryType . $wrapperFactoryFileName . ".cpp"; + + printLicenseHeader($F); + + print F "#include \"config.h\"\n"; + print F "#include \"$wrapperFactoryType$parameters{namespace}ElementWrapperFactory.h\"\n"; + + print F "\n#if $parameters{guardFactoryWith}\n\n" if $parameters{guardFactoryWith}; + + printJSElementIncludes($F, $wrapperFactoryType); + + print F "\n#include \"$parameters{namespace}Names.h\"\n\n"; + + printElementIncludes($F); + + print F <<END +#include <wtf/StdLibExtras.h> + +#if ENABLE(VIDEO) +#include "Document.h" +#include "Settings.h" +#endif + +END +; + + if ($wrapperFactoryType eq "JS") { + print F <<END +using namespace JSC; +END +; + } elsif ($wrapperFactoryType eq "V8") { + print F <<END +#include "V8$parameters{namespace}Element.h" + +#include <v8.h> +END +; + } + + print F <<END + +namespace WebCore { + +using namespace $parameters{namespace}Names; + +END +; + if ($wrapperFactoryType eq "JS") { + print F <<END +typedef JSNode* (*Create$parameters{namespace}ElementWrapperFunction)(ExecState*, JSDOMGlobalObject*, PassRefPtr<$parameters{namespace}Element>); + +END +; + } elsif ($wrapperFactoryType eq "V8") { + print F <<END +typedef v8::Handle<v8::Value> (*Create$parameters{namespace}ElementWrapperFunction)($parameters{namespace}Element*); + +END +; + } + + printWrapperFunctions($F, $wrapperFactoryType); + + if ($wrapperFactoryType eq "JS") { + print F <<END +JSNode* createJS$parameters{namespace}Wrapper(ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<$parameters{namespace}Element> element) +{ + typedef HashMap<WTF::AtomicStringImpl*, Create$parameters{namespace}ElementWrapperFunction> FunctionMap; + DEFINE_STATIC_LOCAL(FunctionMap, map, ()); + if (map.isEmpty()) { +END +; + } elsif ($wrapperFactoryType eq "V8") { + print F <<END +v8::Handle<v8::Value> createV8$parameters{namespace}Wrapper($parameters{namespace}Element* element, bool forceNewObject) +{ + typedef HashMap<WTF::AtomicStringImpl*, Create$parameters{namespace}ElementWrapperFunction> FunctionMap; + DEFINE_STATIC_LOCAL(FunctionMap, map, ()); + if (map.isEmpty()) { +END +; + } + + for my $tag (sort keys %enabledTags) { + # Do not add the name to the map if it does not have a JS wrapper constructor or uses the default wrapper. + next if usesDefaultJSWrapper($tag, \%enabledTags); + + my $conditional = $enabledTags{$tag}{conditional}; + if ($conditional) { + my $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; + print F "#if ${conditionalString}\n"; + } + + my $ucTag = $enabledTags{$tag}{JSInterfaceName}; + print F " map.set(${tag}Tag.localName().impl(), create${ucTag}Wrapper);\n"; + + if ($conditional) { + print F "#endif\n"; + } + } + + print F <<END + } + Create$parameters{namespace}ElementWrapperFunction createWrapperFunction = map.get(element->localName().impl()); + if (createWrapperFunction) +END +; + if ($wrapperFactoryType eq "JS") { + print F <<END + return createWrapperFunction(exec, globalObject, element); + return CREATE_DOM_NODE_WRAPPER(exec, globalObject, $parameters{namespace}Element, element.get()); +END +; + } elsif ($wrapperFactoryType eq "V8") { + print F <<END + return createWrapperFunction(element); + return V8$parameters{namespace}Element::wrap(element, forceNewObject); +END +; + } + print F <<END +} + +} + +END +; + + print F "#endif\n" if $parameters{guardFactoryWith}; + + close F; +} + +sub printWrapperFactoryHeaderFile +{ + my $outputDir = shift; + my $wrapperFactoryType = shift; + my $wrapperFactoryFileName = shift; + my $F; + open F, ">" . $outputDir . "/" . $wrapperFactoryType . $wrapperFactoryFileName . ".h"; + + printLicenseHeader($F); + + print F "#ifndef $wrapperFactoryType$parameters{namespace}ElementWrapperFactory_h\n"; + print F "#define $wrapperFactoryType$parameters{namespace}ElementWrapperFactory_h\n\n"; + + print F "#if $parameters{guardFactoryWith}\n" if $parameters{guardFactoryWith}; + + if ($wrapperFactoryType eq "JS") { + print F <<END +#include <wtf/Forward.h> + +namespace JSC { + class ExecState; +} + +namespace WebCore { + + class JSNode; + class JSDOMGlobalObject; + class $parameters{namespace}Element; + + JSNode* createJS$parameters{namespace}Wrapper(JSC::ExecState*, JSDOMGlobalObject*, PassRefPtr<$parameters{namespace}Element>); + +} + +END +; + } elsif ($wrapperFactoryType eq "V8") { + print F <<END +#include <v8.h> + +namespace WebCore { + + class $parameters{namespace}Element; + + v8::Handle<v8::Value> createV8$parameters{namespace}Wrapper($parameters{namespace}Element*, bool); +} +END +; + } + + print F "#endif // $parameters{guardFactoryWith}\n\n" if $parameters{guardFactoryWith}; + + print F "#endif // $wrapperFactoryType$parameters{namespace}ElementWrapperFactory_h\n"; + + close F; +} |