diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
commit | 635860845790a19bf50bbc51ba8fb66a96dde068 (patch) | |
tree | ef6ad9ff73a5b57f65249d4232a202fa77e6a140 /WebCore/dom | |
parent | 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (diff) | |
download | external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.zip external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.gz external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.bz2 |
auto import from //depot/cupcake/@136594
Diffstat (limited to 'WebCore/dom')
107 files changed, 5244 insertions, 1673 deletions
diff --git a/WebCore/dom/ActiveDOMObject.cpp b/WebCore/dom/ActiveDOMObject.cpp index 5e46953..9d36065 100644 --- a/WebCore/dom/ActiveDOMObject.cpp +++ b/WebCore/dom/ActiveDOMObject.cpp @@ -28,6 +28,8 @@ #include "ActiveDOMObject.h" #include "ScriptExecutionContext.h" +#include "WorkerContext.h" +#include "WorkerThread.h" namespace WebCore { @@ -35,13 +37,29 @@ ActiveDOMObject::ActiveDOMObject(ScriptExecutionContext* scriptExecutionContext, : m_scriptExecutionContext(scriptExecutionContext) , m_pendingActivityCount(0) { +#if ENABLE(WORKERS) + ASSERT((m_scriptExecutionContext->isDocument() && isMainThread()) + || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext)->thread()->threadID())); +#endif + m_scriptExecutionContext->createdActiveDOMObject(this, upcastPointer); } ActiveDOMObject::~ActiveDOMObject() { - if (m_scriptExecutionContext) + if (m_scriptExecutionContext) { +#if ENABLE(WORKERS) + ASSERT((m_scriptExecutionContext->isDocument() && isMainThread()) + || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext)->thread()->threadID())); +#endif + m_scriptExecutionContext->destroyedActiveDOMObject(this); + } +} + +bool ActiveDOMObject::hasPendingActivity() const +{ + return m_pendingActivityCount; } void ActiveDOMObject::contextDestroyed() @@ -49,6 +67,19 @@ void ActiveDOMObject::contextDestroyed() m_scriptExecutionContext = 0; } +bool ActiveDOMObject::canSuspend() const +{ + return false; +} + +void ActiveDOMObject::suspend() +{ +} + +void ActiveDOMObject::resume() +{ +} + void ActiveDOMObject::stop() { } diff --git a/WebCore/dom/ActiveDOMObject.h b/WebCore/dom/ActiveDOMObject.h index 601219c..e58d3f9 100644 --- a/WebCore/dom/ActiveDOMObject.h +++ b/WebCore/dom/ActiveDOMObject.h @@ -38,9 +38,19 @@ namespace WebCore { ActiveDOMObject(ScriptExecutionContext*, void* upcastPointer); ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; } - bool hasPendingActivity() { return m_pendingActivityCount; } + 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. + virtual bool canSuspend() const; + virtual void suspend(); + virtual void resume(); virtual void stop(); protected: diff --git a/WebCore/dom/CharacterData.cpp b/WebCore/dom/CharacterData.cpp index 247a5b6..0ce4170 100644 --- a/WebCore/dom/CharacterData.cpp +++ b/WebCore/dom/CharacterData.cpp @@ -23,7 +23,6 @@ #include "CharacterData.h" #include "CString.h" -#include "Document.h" #include "EventNames.h" #include "ExceptionCode.h" #include "MutationEvent.h" @@ -31,14 +30,14 @@ namespace WebCore { -CharacterData::CharacterData(Document *doc) - : EventTargetNode(doc) +CharacterData::CharacterData(Document *doc, bool isText) + : EventTargetNode(doc, false, false, isText) , m_data(StringImpl::empty()) { } -CharacterData::CharacterData(Document* document, const String& text) - : EventTargetNode(document) +CharacterData::CharacterData(Document* document, const String& text, bool isText) + : EventTargetNode(document, false, false, isText) { m_data = text.impl() ? text.impl() : StringImpl::empty(); } @@ -61,7 +60,7 @@ void CharacterData::setData(const String& data, ExceptionCode&) detach(); attach(); } else if (renderer()) - static_cast<RenderText*>(renderer())->setText(m_data); + toRenderText(renderer())->setText(m_data); dispatchModifiedEvent(oldStr.get()); @@ -89,7 +88,7 @@ void CharacterData::appendData(const String& arg, ExceptionCode&) detach(); attach(); } else if (renderer()) - static_cast<RenderText*>(renderer())->setTextWithOffset(m_data, oldStr->length(), 0); + toRenderText(renderer())->setTextWithOffset(m_data, oldStr->length(), 0); dispatchModifiedEvent(oldStr.get()); } @@ -110,7 +109,7 @@ void CharacterData::insertData(unsigned offset, const String& arg, ExceptionCode detach(); attach(); } else if (renderer()) - static_cast<RenderText*>(renderer())->setTextWithOffset(m_data, offset, 0); + toRenderText(renderer())->setTextWithOffset(m_data, offset, 0); dispatchModifiedEvent(oldStr.get()); @@ -139,7 +138,7 @@ void CharacterData::deleteData(unsigned offset, unsigned count, ExceptionCode& e detach(); attach(); } else if (renderer()) - static_cast<RenderText*>(renderer())->setTextWithOffset(m_data, offset, count); + toRenderText(renderer())->setTextWithOffset(m_data, offset, count); dispatchModifiedEvent(oldStr.get()); @@ -169,7 +168,7 @@ void CharacterData::replaceData(unsigned offset, unsigned count, const String& a detach(); attach(); } else if (renderer()) - static_cast<RenderText*>(renderer())->setTextWithOffset(m_data, offset, count); + toRenderText(renderer())->setTextWithOffset(m_data, offset, count); dispatchModifiedEvent(oldStr.get()); diff --git a/WebCore/dom/CharacterData.h b/WebCore/dom/CharacterData.h index 48ddc36..412650e 100644 --- a/WebCore/dom/CharacterData.h +++ b/WebCore/dom/CharacterData.h @@ -29,8 +29,8 @@ namespace WebCore { class CharacterData : public EventTargetNode { public: - CharacterData(Document*, const String& text); - CharacterData(Document*); + CharacterData(Document*, const String& text, bool isText = false); + CharacterData(Document*, bool isText = false); virtual ~CharacterData(); // DOM methods & attributes for CharacterData diff --git a/WebCore/dom/ContainerNode.cpp b/WebCore/dom/ContainerNode.cpp index bcd54c1..958fc4e 100644 --- a/WebCore/dom/ContainerNode.cpp +++ b/WebCore/dom/ContainerNode.cpp @@ -25,18 +25,16 @@ #include "ContainerNodeAlgorithms.h" #include "DeleteButtonController.h" -#include "Document.h" -#include "Editor.h" #include "EventNames.h" #include "ExceptionCode.h" +#include "FloatRect.h" #include "Frame.h" #include "FrameView.h" #include "InlineTextBox.h" #include "MutationEvent.h" #include "RenderTheme.h" #include "RootInlineBox.h" -#include "SystemTime.h" -#include <wtf/Vector.h> +#include <wtf/CurrentTime.h> namespace WebCore { @@ -647,25 +645,26 @@ void ContainerNode::cloneChildNodes(ContainerNode *clone) document()->frame()->editor()->deleteButtonController()->enable(); } -bool ContainerNode::getUpperLeftCorner(int &xPos, int &yPos) const +// FIXME: This doesn't work correctly with transforms. +bool ContainerNode::getUpperLeftCorner(FloatPoint& point) const { if (!renderer()) return false; + // What is this code really trying to do? RenderObject *o = renderer(); RenderObject *p = o; - xPos = yPos = 0; if (!o->isInline() || o->isReplaced()) { - o->absolutePosition(xPos, yPos); + point = o->localToAbsolute(); return true; } // find the next text/image child, to get a position - while(o) { + while (o) { p = o; if (o->firstChild()) o = o->firstChild(); - else if(o->nextSibling()) + else if (o->nextSibling()) o = o->nextSibling(); else { RenderObject *next = 0; @@ -680,70 +679,75 @@ bool ContainerNode::getUpperLeftCorner(int &xPos, int &yPos) const } if (!o->isInline() || o->isReplaced()) { - o->absolutePosition(xPos, yPos); + point = o->localToAbsolute(); return true; } - if (p->element() && p->element() == this && o->isText() && !o->isBR() && !static_cast<RenderText*>(o)->firstTextBox()) { + if (p->element() && p->element() == 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()) { - o->container()->absolutePosition(xPos, yPos); - if (o->isText() && static_cast<RenderText *>(o)->firstTextBox()) { - xPos += static_cast<RenderText *>(o)->minXPos(); - yPos += static_cast<RenderText *>(o)->firstTextBox()->root()->topOverflow(); - } else { - xPos += o->xPos(); - yPos += o->yPos(); + point = o->container()->localToAbsolute(); + if (o->isText() && toRenderText(o)->firstTextBox()) { + point.move(toRenderText(o)->linesBoundingBox().x(), + toRenderText(o)->firstTextBox()->root()->topOverflow()); + } else if (o->isBox()) { + RenderBox* box = toRenderBox(o); + point.move(box->x(), box->y()); } 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. + // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling? if (!o && document()->view()) { - yPos += document()->view()->contentsHeight(); + point = FloatPoint(0, document()->view()->contentsHeight()); return true; } return false; } -bool ContainerNode::getLowerRightCorner(int &xPos, int &yPos) const +// FIXME: This doesn't work correctly with transforms. +bool ContainerNode::getLowerRightCorner(FloatPoint& point) const { if (!renderer()) return false; RenderObject *o = renderer(); - xPos = yPos = 0; if (!o->isInline() || o->isReplaced()) { - o->absolutePosition(xPos, yPos); - xPos += o->width(); - yPos += o->height() + o->borderTopExtra() + o->borderBottomExtra(); + RenderBox* box = toRenderBox(o); + point = o->localToAbsolute(); + point.move(box->width(), box->height()); return true; } + // find the last text/image child, to get a position - while(o) { - if(o->lastChild()) + while (o) { + if (o->lastChild()) o = o->lastChild(); - else if(o->previousSibling()) + else if (o->previousSibling()) o = o->previousSibling(); else { RenderObject *prev = 0; while(!prev) { o = o->parent(); - if(!o) return false; + if (!o) + return false; prev = o->previousSibling(); } o = prev; } if (o->isText() || o->isReplaced()) { - o->container()->absolutePosition(xPos, yPos); - if (o->isText()) - xPos += static_cast<RenderText *>(o)->minXPos() + o->width(); - else - xPos += o->xPos()+o->width(); - yPos += o->yPos()+o->height(); + point = o->container()->localToAbsolute(); + if (o->isText()) { + RenderText* text = toRenderText(o); + IntRect linesBox = text->linesBoundingBox(); + point.move(linesBox.x() + linesBox.width(), linesBox.height()); + } else { + RenderBox* box = toRenderBox(o); + point.move(box->x() + box->width(), box->y() + box->height()); + } return true; } } @@ -752,29 +756,24 @@ bool ContainerNode::getLowerRightCorner(int &xPos, int &yPos) const IntRect ContainerNode::getRect() const { - int xPos = 0, yPos = 0, xEnd = 0, yEnd = 0; - bool foundUpperLeft = getUpperLeftCorner(xPos,yPos); - bool foundLowerRight = getLowerRightCorner(xEnd,yEnd); + 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) { - xEnd = xPos; - yEnd = yPos; - } else { - xPos = xEnd; - yPos = yEnd; - } + if (foundUpperLeft) + lowerRight = upperLeft; + else + upperLeft = lowerRight; } - if (xEnd < xPos) - xEnd = xPos; - if (yEnd < yPos) - yEnd = yPos; - - return IntRect(xPos, yPos, xEnd - xPos, yEnd - yPos); + 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) diff --git a/WebCore/dom/ContainerNode.h b/WebCore/dom/ContainerNode.h index c36c941..91ca49a 100644 --- a/WebCore/dom/ContainerNode.h +++ b/WebCore/dom/ContainerNode.h @@ -25,6 +25,7 @@ #define ContainerNode_h #include "EventTargetNode.h" +#include "FloatPoint.h" namespace WebCore { @@ -89,8 +90,8 @@ protected: private: static void dispatchPostAttachCallbacks(); - bool getUpperLeftCorner(int& x, int& y) const; - bool getLowerRightCorner(int& x, int& y) const; + bool getUpperLeftCorner(FloatPoint&) const; + bool getLowerRightCorner(FloatPoint&) const; Node* m_firstChild; Node* m_lastChild; diff --git a/WebCore/dom/DOMImplementation.cpp b/WebCore/dom/DOMImplementation.cpp index d7a8903..738575f 100644 --- a/WebCore/dom/DOMImplementation.cpp +++ b/WebCore/dom/DOMImplementation.cpp @@ -4,6 +4,7 @@ * (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 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 @@ -46,17 +47,25 @@ #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) -static void addString(HashSet<String, CaseFoldingHash>& set, const char* string) +typedef HashSet<String, CaseFoldingHash> FeatureSet; + +static void addString(FeatureSet& set, const char* string) { set.add(string); } @@ -64,7 +73,7 @@ static void addString(HashSet<String, CaseFoldingHash>& set, const char* string) static bool isSVG10Feature(const String &feature) { static bool initialized = false; - static HashSet<String, CaseFoldingHash> svgFeatures; + DEFINE_STATIC_LOCAL(FeatureSet, svgFeatures, ()); if (!initialized) { #if ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT) && ENABLE(SVG_FILTER) && ENABLE(SVG_FONTS) addString(svgFeatures, "svg"); @@ -89,7 +98,7 @@ static bool isSVG10Feature(const String &feature) static bool isSVG11Feature(const String &feature) { static bool initialized = false; - static HashSet<String, CaseFoldingHash> svgFeatures; + 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. @@ -225,6 +234,11 @@ PassRefPtr<Document> DOMImplementation::createDocument(const String& namespaceUR doc = SVGDocument::create(0); else #endif +#if ENABLE(WML) + if (namespaceURI == WMLNames::wmlNamespaceURI) + doc = WMLDocument::create(0); + else +#endif if (namespaceURI == HTMLNames::xhtmlNamespaceURI) doc = Document::createXHTML(0); else @@ -273,8 +287,8 @@ bool DOMImplementation::isXMLMIMEType(const String& mimeType) { if (mimeType == "text/xml" || mimeType == "application/xml" || mimeType == "text/xsl") return true; - static const char* validChars = "[0-9a-zA-Z_\\-+~!$\\^{}|.%'`#&*]"; // per RFCs: 3023, 2045 - static RegularExpression xmlTypeRegExp(String("^") + validChars + "+/" + validChars + "+\\+xml$"); + 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; } @@ -308,7 +322,12 @@ PassRefPtr<Document> DOMImplementation::createDocument(const String& type, Frame return HTMLDocument::create(frame); if (type == "application/xhtml+xml") return Document::createXHTML(frame); - + +#if ENABLE(WML) + if (type == "text/vnd.wap.wml" || type == "application/vnd.wap.wmlc") + return WMLDocument::create(frame); +#endif + #if ENABLE(FTPDIR) // Plugins cannot take FTP from us either if (type == "application/x-ftp-directory") diff --git a/WebCore/dom/DOMStringList.cpp b/WebCore/dom/DOMStringList.cpp new file mode 100644 index 0000000..f89702b --- /dev/null +++ b/WebCore/dom/DOMStringList.cpp @@ -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 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 "DOMStringList.h" + +namespace WebCore { + +DOMStringList::~DOMStringList() +{ +} + +} // namespace WebCore diff --git a/WebCore/dom/DOMStringList.h b/WebCore/dom/DOMStringList.h new file mode 100644 index 0000000..630e33e --- /dev/null +++ b/WebCore/dom/DOMStringList.h @@ -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. + * + */ + +#ifndef DOMStringList_h +#define DOMStringList_h + +#include "PlatformString.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + + class DOMStringList : public RefCounted<DOMStringList> { + public: + virtual ~DOMStringList(); + + virtual unsigned length() const = 0; + virtual String item(unsigned) const = 0; + virtual bool contains(const String&) const = 0; + }; + +} // namespace WebCore + +#endif // DOMStringList_h diff --git a/WebCore/dom/DOMStringList.idl b/WebCore/dom/DOMStringList.idl new file mode 100644 index 0000000..16d4e12 --- /dev/null +++ b/WebCore/dom/DOMStringList.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 core { + + interface [ + GenerateConstructor, + HasCustomIndexGetter + ] DOMStringList { + + // Unlike the index getter, item() never raises exceptions, and returns null for out of bounds indices. + [Custom] DOMString item(in unsigned long index); + + readonly attribute unsigned long length; + boolean contains(in DOMString str); + }; + +} diff --git a/WebCore/dom/DedicatedWorker.cpp b/WebCore/dom/DedicatedWorker.cpp deleted file mode 100644 index 162fa9d..0000000 --- a/WebCore/dom/DedicatedWorker.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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" - -#if ENABLE(WORKERS) - -#include "DedicatedWorker.h" - -#include "CachedScript.h" -#include "DOMWindow.h" -#include "DocLoader.h" -#include "Document.h" -#include "Event.h" -#include "EventException.h" -#include "EventListener.h" -#include "EventNames.h" -#include "ExceptionCode.h" -#include "FrameLoader.h" -#include "MessagePort.h" -#include "SecurityOrigin.h" -#include "ScriptExecutionContext.h" -#include "Timer.h" -#include <wtf/MainThread.h> - -namespace WebCore { - -class WorkerThreadScriptLoadTimer : public TimerBase { -public: - WorkerThreadScriptLoadTimer(PassRefPtr<DedicatedWorker> worker) - : m_worker(worker) - { - ASSERT(m_worker); - } - -private: - virtual void fired() - { - m_worker->startLoad(); - delete this; - } - - RefPtr<DedicatedWorker> m_worker; -}; - -DedicatedWorker::DedicatedWorker(const String& url, Document* document, ExceptionCode& ec) - : ActiveDOMObject(document, this) -{ - m_scriptURL = document->completeURL(url); - if (!m_scriptURL.isValid()) { - ec = SYNTAX_ERR; - return; - } - - if (!document->securityOrigin()->canAccess(SecurityOrigin::create(m_scriptURL).get())) { - ec = SECURITY_ERR; - return; - } - - WorkerThreadScriptLoadTimer* timer = new WorkerThreadScriptLoadTimer(this); - timer->startOneShot(0); -} - -DedicatedWorker::~DedicatedWorker() -{ - ASSERT(isMainThread()); -} - -Document* DedicatedWorker::document() const -{ - ASSERT(scriptExecutionContext()->isDocument()); - return static_cast<Document*>(scriptExecutionContext()); -} - -PassRefPtr<MessagePort> DedicatedWorker::startConversation(ScriptExecutionContext* /*scriptExecutionContext*/, const String& /*message*/) -{ - // Not implemented. - return 0; -} - -void DedicatedWorker::close() -{ - // Not implemented. -} - -void DedicatedWorker::postMessage(const String& /*message*/, MessagePort* /*port*/) -{ - // Not implemented. -} - -void DedicatedWorker::startLoad() -{ - m_cachedScript = document()->docLoader()->requestScript(m_scriptURL, document()->charset()); - - if (m_cachedScript) { - setPendingActivity(this); // The worker context does not exist while loading, so we much ensure that the worker object is not collected, as well as its event listeners. - m_cachedScript->addClient(this); - return; - } - - dispatchErrorEvent(); -} - -void DedicatedWorker::notifyFinished(CachedResource* resource) -{ - ASSERT(resource == m_cachedScript.get()); - if (m_cachedScript->errorOccurred()) { - dispatchErrorEvent(); - unsetPendingActivity(this); - } else { - // Start the worker thread. - } - - m_cachedScript->removeClient(this); - m_cachedScript = 0; -} - -void DedicatedWorker::dispatchErrorEvent() -{ - if (m_onErrorListener) { - RefPtr<Event> evt = Event::create(eventNames().errorEvent, false, true); - // DedicatedWorker is not an EventTarget, so target and currentTarget remain null. - m_onErrorListener->handleEvent(evt.release().get(), true); - } -} - -} // namespace WebCore - -#endif // ENABLE(WORKERS) diff --git a/WebCore/dom/Document.cpp b/WebCore/dom/Document.cpp index f5a72c2..a687df8 100644 --- a/WebCore/dom/Document.cpp +++ b/WebCore/dom/Document.cpp @@ -4,6 +4,8 @@ * (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) 2008 David Levin (levin@chromium.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -33,8 +35,10 @@ #include "CString.h" #include "CachedCSSStyleSheet.h" #include "Comment.h" +#include "Console.h" #include "CookieJar.h" #include "DOMImplementation.h" +#include "DOMTimer.h" #include "DOMWindow.h" #include "DocLoader.h" #include "DocumentFragment.h" @@ -53,6 +57,7 @@ #include "FrameLoader.h" #include "FrameTree.h" #include "FrameView.h" +#include "HTMLAnchorElement.h" #include "HTMLBodyElement.h" #include "HTMLCanvasElement.h" #include "HTMLDocument.h" @@ -71,6 +76,7 @@ #include "HitTestRequest.h" #include "HitTestResult.h" #include "ImageLoader.h" +#include "InspectorController.h" #include "KeyboardEvent.h" #include "Logging.h" #include "MessageEvent.h" @@ -87,21 +93,20 @@ #include "ProcessingInstruction.h" #include "ProgressEvent.h" #include "RegisteredEventListener.h" -#include "RegularExpression.h" #include "RenderArena.h" #include "RenderView.h" #include "RenderWidget.h" +#include "ScriptController.h" #include "SecurityOrigin.h" #include "SegmentedString.h" #include "SelectionController.h" #include "Settings.h" -#include "StringHash.h" #include "StyleSheetList.h" -#include "SystemTime.h" #include "TextEvent.h" #include "TextIterator.h" #include "TextResourceDecoder.h" #include "TreeWalker.h" +#include "Timer.h" #include "UIEvent.h" #include "WebKitAnimationEvent.h" #include "WebKitTransitionEvent.h" @@ -111,13 +116,21 @@ #include "XMLTokenizer.h" #include "JSDOMBinding.h" #include "ScriptController.h" -#include <runtime/JSLock.h> +#include <wtf/CurrentTime.h> +#include <wtf/HashFunctions.h> +#include <wtf/MainThread.h> +#include <wtf/StdLibExtras.h> +#include <wtf/PassRefPtr.h> #if ENABLE(DATABASE) #include "Database.h" #include "DatabaseThread.h" #endif +#if ENABLE(DOM_STORAGE) +#include "StorageEvent.h" +#endif + #if ENABLE(XPATH) #include "XPathEvaluator.h" #include "XPathExpression.h" @@ -160,6 +173,13 @@ #include "TimeCounter.h" #endif +#if ENABLE(WML) +#include "WMLDocument.h" +#include "WMLElement.h" +#include "WMLElementFactory.h" +#include "WMLNames.h" +#endif + using namespace std; using namespace WTF; using namespace Unicode; @@ -178,9 +198,6 @@ static const int cLayoutScheduleThreshold = 250; // Use 1 to represent the document's default form. static HTMLFormElement* const defaultForm = reinterpret_cast<HTMLFormElement*>(1); -// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's -static const unsigned PHI = 0x9e3779b9U; - // DOM Level 2 says (letters added): // // a) Name start characters must have one of the categories Ll, Lu, Lo, Lt, Nl. @@ -283,6 +300,7 @@ Document::Document(Frame* frame, bool isXHTML) : ContainerNode(0) , m_domtree_version(0) , m_styleSheets(StyleSheetList::create(this)) + , m_frameElementsShouldIgnoreScrolling(false) , m_title("") , m_titleSetExplicitly(false) , m_imageLoadEventTimer(this, &Document::imageLoadEventTimerFired) @@ -353,6 +371,7 @@ Document::Document(Frame* frame, bool isXHTML) m_usesSiblingRules = false; m_usesFirstLineRules = false; m_usesFirstLetterRules = false; + m_usesBeforeAfterRules = false; m_gotoAnchorNeededAfterStylesheetsLoad = false; m_styleSelector = 0; @@ -517,16 +536,13 @@ void Document::childrenChanged(bool changedByParser, Node* beforeChange, Node* a m_documentElement = 0; } -Element* Document::documentElement() const +void Document::cacheDocumentElement() const { - if (!m_documentElement) { - Node* n = firstChild(); - while (n && !n->isElementNode()) - n = n->nextSibling(); - m_documentElement = static_cast<Element*>(n); - } - - return m_documentElement.get(); + 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) @@ -537,7 +553,7 @@ PassRefPtr<Element> Document::createElement(const AtomicString& name, ExceptionC } if (m_isXHTML) - return HTMLElementFactory::createHTMLElement(name, this, 0, false); + return HTMLElementFactory::createHTMLElement(QualifiedName(nullAtom, name, xhtmlNamespaceURI), this, 0, false); return createElement(QualifiedName(nullAtom, name, nullAtom), false, ec); } @@ -735,9 +751,9 @@ PassRefPtr<Node> Document::adoptNode(PassRefPtr<Node> source, ExceptionCode& ec) bool Document::hasPrefixNamespaceMismatch(const QualifiedName& qName) { - static const AtomicString xmlnsNamespaceURI("http://www.w3.org/2000/xmlns/"); - static const AtomicString xmlns("xmlns"); - static const AtomicString xml("xml"); + DEFINE_STATIC_LOCAL(const AtomicString, xmlnsNamespaceURI, ("http://www.w3.org/2000/xmlns/")); + DEFINE_STATIC_LOCAL(const AtomicString, xmlns, ("xmlns")); + DEFINE_STATIC_LOCAL(const AtomicString, xml, ("xml")); // These checks are from DOM Core Level 2, createElementNS // http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrElNS @@ -762,11 +778,15 @@ PassRefPtr<Element> Document::createElement(const QualifiedName& qName, bool cre // FIXME: Use registered namespaces and look up in a hash to find the right factory. if (qName.namespaceURI() == xhtmlNamespaceURI) - e = HTMLElementFactory::createHTMLElement(qName.localName(), this, 0, createdByParser); + 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 || isWMLDocument()) + e = WMLElementFactory::createWMLElement(qName, this, createdByParser); +#endif if (!e) e = new Element(qName, document()); @@ -902,7 +922,7 @@ Element* Document::elementFromPoint(int x, int y) const HitTestRequest request(true, true); HitTestResult result(IntPoint(x, y)); - renderer()->layer()->hitTest(request, result); + renderView()->layer()->hitTest(request, result); Node* n = result.innerNode(); while (n && !n->isElementNode()) @@ -1107,7 +1127,7 @@ void Document::recalcStyle(StyleChange change) if (m_inStyleRecalc) return; // Guard against re-entrancy. -dwh - + m_inStyleRecalc = true; suspendPostAttachCallbacks(); @@ -1184,12 +1204,17 @@ bail_out: void Document::updateRendering() { - if (hasChangedChild() && !inPageCache()) - recalcStyle(NoChange); + if (!hasChangedChild() || inPageCache()) + return; + + if (m_frame) + m_frame->animation()->beginAnimationUpdate(); + + recalcStyle(NoChange); - // Tell the animation controller that the style is available and it can start animations + // Tell the animation controller that updateRendering is finished and it can do any post-processing if (m_frame) - m_frame->animation()->styleAvailable(); + m_frame->animation()->endAnimationUpdate(); } void Document::updateDocumentsRendering() @@ -1289,6 +1314,7 @@ void Document::detach() ASSERT(!m_inPageCache); clearAXObjectCache(); + stopActiveDOMObjects(); RenderObject* render = renderer(); @@ -1329,6 +1355,9 @@ void Document::clearFramePointer() void Document::removeAllEventListenersFromAllNodes() { + size_t size = m_windowEventListeners.size(); + for (size_t i = 0; i < size; ++i) + m_windowEventListeners[i]->setRemoved(true); m_windowEventListeners.clear(); removeAllDisconnectedNodeEventListeners(); for (Node *n = this; n; n = n->traverseNextNode()) { @@ -1356,6 +1385,11 @@ void Document::removeAllDisconnectedNodeEventListeners() m_disconnectedNodesWithEventListeners.clear(); } +RenderView* Document::renderView() const +{ + return static_cast<RenderView*>(renderer()); +} + void Document::clearAXObjectCache() { // clear cache in top document @@ -1426,7 +1460,7 @@ void Document::open(Document* ownerDocument) if (ownerDocument) { setURL(ownerDocument->url()); m_cookieURL = ownerDocument->cookieURL(); - m_securityOrigin = ownerDocument->securityOrigin(); + ScriptExecutionContext::setSecurityOrigin(ownerDocument->securityOrigin()); } if (m_frame) { @@ -1556,7 +1590,7 @@ void Document::implicitClose() if (!this->body() && isHTMLDocument()) { if (Node* documentElement = this->documentElement()) { ExceptionCode ec = 0; - documentElement->appendChild(new HTMLBodyElement(this), ec); + documentElement->appendChild(new HTMLBodyElement(bodyTag, this), ec); ASSERT(!ec); } } @@ -1652,13 +1686,14 @@ void Document::setParsing(bool b) bool Document::shouldScheduleLayout() { - // We can update layout if: - // (a) we actually need a layout - // (b) our stylesheets are all loaded - // (c) we have a <body> - return (renderer() && renderer()->needsLayout() && haveStylesheetsLoaded() && - documentElement() && documentElement()->renderer() && - (!documentElement()->hasTagName(htmlTag) || body())); + // 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() @@ -1729,6 +1764,9 @@ void Document::clear() removeChildren(); + size_t size = m_windowEventListeners.size(); + for (size_t i = 0; i < size; ++i) + m_windowEventListeners[i]->setRemoved(true); m_windowEventListeners.clear(); } @@ -1737,6 +1775,11 @@ 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; @@ -1756,7 +1799,15 @@ void Document::setBaseElementURL(const KURL& baseElementURL) void Document::updateBaseURL() { - m_baseURL = m_baseElementURL.isEmpty() ? KURL(documentURI()) : m_baseElementURL; + // 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(); @@ -2044,7 +2095,7 @@ MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& r return MouseEventWithHitTestResults(event, HitTestResult(IntPoint())); HitTestResult result(documentPoint); - renderer()->layer()->hitTest(request, result); + renderView()->layer()->hitTest(request, result); if (!request.readonly) updateRendering(); @@ -2695,22 +2746,32 @@ DOMWindow* Document::domWindow() const PassRefPtr<Event> Document::createEvent(const String& eventType, ExceptionCode& ec) { - if (eventType == "UIEvents" || eventType == "UIEvent") - return UIEvent::create(); - if (eventType == "MouseEvents" || eventType == "MouseEvent") + if (eventType == "Event" || eventType == "Events" || eventType == "HTMLEvents") + return Event::create(); + if (eventType == "KeyboardEvent" || eventType == "KeyboardEvents") + return KeyboardEvent::create(); + if (eventType == "MessageEvent") + return MessageEvent::create(); + if (eventType == "MouseEvent" || eventType == "MouseEvents") return MouseEvent::create(); - if (eventType == "MutationEvents" || eventType == "MutationEvent") + if (eventType == "MutationEvent" || eventType == "MutationEvents") return MutationEvent::create(); - if (eventType == "KeyboardEvents" || eventType == "KeyboardEvent") - return KeyboardEvent::create(); - if (eventType == "HTMLEvents" || eventType == "Event" || eventType == "Events") - return Event::create(); + if (eventType == "OverflowEvent") + return OverflowEvent::create(); if (eventType == "ProgressEvent") return ProgressEvent::create(); +#if ENABLE(DOM_STORAGE) + if (eventType == "StorageEvent") + return StorageEvent::create(); +#endif if (eventType == "TextEvent") return TextEvent::create(); - if (eventType == "OverflowEvent") - return OverflowEvent::create(); + if (eventType == "UIEvent" || eventType == "UIEvents") + return UIEvent::create(); + if (eventType == "WebKitAnimationEvent") + return WebKitAnimationEvent::create(); + if (eventType == "WebKitTransitionEvent") + return WebKitTransitionEvent::create(); if (eventType == "WheelEvent") return WheelEvent::create(); #if ENABLE(SVG) @@ -2719,12 +2780,6 @@ PassRefPtr<Event> Document::createEvent(const String& eventType, ExceptionCode& if (eventType == "SVGZoomEvents") return SVGZoomEvent::create(); #endif - if (eventType == "MessageEvent") - return MessageEvent::create(); - if (eventType == "WebKitAnimationEvent") - return WebKitAnimationEvent::create(); - if (eventType == "WebKitTransitionEvent") - return WebKitTransitionEvent::create(); #if ENABLE(TOUCH_EVENTS) // Android if (eventType == "TouchEvent") return TouchEvent::create(); @@ -2766,18 +2821,19 @@ CSSStyleDeclaration* Document::getOverrideStyle(Element*, const String&) return 0; } -void Document::handleWindowEvent(Event* evt, bool useCapture) +void Document::handleWindowEvent(Event* event, bool useCapture) { if (m_windowEventListeners.isEmpty()) return; - // if any html event listeners are registered on the window, then dispatch them here - RegisteredEventListenerList listenersCopy = m_windowEventListeners; - RegisteredEventListenerList::iterator it = listenersCopy.begin(); - - for (; it != listenersCopy.end(); ++it) - if ((*it)->eventType() == evt->type() && (*it)->useCapture() == useCapture && !(*it)->removed()) - (*it)->listener()->handleEvent(evt, true); + // If any HTML event listeners are registered on the window, dispatch them here. + RegisteredEventListenerVector listenersCopy = m_windowEventListeners; + size_t size = listenersCopy.size(); + for (size_t i = 0; i < size; ++i) { + RegisteredEventListener& r = *listenersCopy[i]; + if (r.eventType() == event->type() && r.useCapture() == useCapture && !r.removed()) + r.listener()->handleEvent(event, true); + } } void Document::setWindowInlineEventListenerForType(const AtomicString& eventType, PassRefPtr<EventListener> listener) @@ -2790,24 +2846,27 @@ void Document::setWindowInlineEventListenerForType(const AtomicString& eventType EventListener* Document::windowInlineEventListenerForType(const AtomicString& eventType) { - RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); - for (; it != m_windowEventListeners.end(); ++it) { - if ((*it)->eventType() == eventType && (*it)->listener()->isInline()) - return (*it)->listener(); + size_t size = m_windowEventListeners.size(); + for (size_t i = 0; i < size; ++i) { + RegisteredEventListener& r = *m_windowEventListeners[i]; + if (r.eventType() == eventType && r.listener()->isInline()) + return r.listener(); } return 0; } void Document::removeWindowInlineEventListenerForType(const AtomicString& eventType) { - RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); - for (; it != m_windowEventListeners.end(); ++it) { - if ((*it)->eventType() == eventType && (*it)->listener()->isInline()) { + size_t size = m_windowEventListeners.size(); + for (size_t i = 0; i < size; ++i) { + RegisteredEventListener& r = *m_windowEventListeners[i]; + if (r.eventType() == eventType && r.listener()->isInline()) { if (eventType == eventNames().unloadEvent) removePendingFrameUnloadEventCount(); else if (eventType == eventNames().beforeunloadEvent) removePendingFrameBeforeUnloadEventCount(); - m_windowEventListeners.remove(it); + r.setRemoved(true); + m_windowEventListeners.remove(i); return; } } @@ -2822,20 +2881,22 @@ void Document::addWindowEventListener(const AtomicString& eventType, PassRefPtr< // Remove existing identical listener set with identical arguments. // The DOM 2 spec says that "duplicate instances are discarded" in this case. removeWindowEventListener(eventType, listener.get(), useCapture); + addListenerTypeIfNeeded(eventType); m_windowEventListeners.append(RegisteredEventListener::create(eventType, listener, useCapture)); } void Document::removeWindowEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) { - RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); - for (; it != m_windowEventListeners.end(); ++it) { - RegisteredEventListener& r = **it; + size_t size = m_windowEventListeners.size(); + for (size_t i = 0; i < size; ++i) { + RegisteredEventListener& r = *m_windowEventListeners[i]; if (r.eventType() == eventType && r.listener() == listener && r.useCapture() == useCapture) { if (eventType == eventNames().unloadEvent) removePendingFrameUnloadEventCount(); else if (eventType == eventNames().beforeunloadEvent) removePendingFrameBeforeUnloadEventCount(); - m_windowEventListeners.remove(it); + r.setRemoved(true); + m_windowEventListeners.remove(i); return; } } @@ -2843,10 +2904,11 @@ void Document::removeWindowEventListener(const AtomicString& eventType, EventLis bool Document::hasWindowEventListener(const AtomicString& eventType) { - RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); - for (; it != m_windowEventListeners.end(); ++it) - if ((*it)->eventType() == eventType) + size_t size = m_windowEventListeners.size(); + for (size_t i = 0; i < size; ++i) { + if (m_windowEventListeners[i]->eventType() == eventType) return true; + } return false; } @@ -2905,34 +2967,48 @@ void Document::removeImage(ImageLoader* image) { // Remove instances of this image from both lists. // Use loops because we allow multiple instances to get into the lists. - while (m_imageLoadEventDispatchSoonList.removeRef(image)) { } - while (m_imageLoadEventDispatchingList.removeRef(image)) { } + size_t size = m_imageLoadEventDispatchSoonList.size(); + for (size_t i = 0; i < size; ++i) { + if (m_imageLoadEventDispatchSoonList[i] == image) + m_imageLoadEventDispatchSoonList[i] = 0; + } + size = m_imageLoadEventDispatchingList.size(); + for (size_t i = 0; i < size; ++i) { + if (m_imageLoadEventDispatchingList[i] == image) + m_imageLoadEventDispatchingList[i] = 0; + } if (m_imageLoadEventDispatchSoonList.isEmpty()) m_imageLoadEventTimer.stop(); } void Document::dispatchImageLoadEventsNow() { - // need to avoid re-entering this function; if new dispatches are + // Need to avoid re-entering this function; if new dispatches are // scheduled before the parent finishes processing the list, they - // will set a timer and eventually be processed + // will set a timer and eventually be processed. if (!m_imageLoadEventDispatchingList.isEmpty()) return; - +#ifdef BUILDING_ON_LEOPARD + bool shouldReenableMemoryCacheClientCalls = false; + if (settings() && settings()->needsIChatMemoryCacheCallsQuirk() && page()->areMemoryCacheClientCallsEnabled()) { + shouldReenableMemoryCacheClientCalls = true; + page()->setMemoryCacheClientCallsEnabled(false); + } +#endif m_imageLoadEventTimer.stop(); - + m_imageLoadEventDispatchingList = m_imageLoadEventDispatchSoonList; m_imageLoadEventDispatchSoonList.clear(); - for (DeprecatedPtrListIterator<ImageLoader> it(m_imageLoadEventDispatchingList); it.current();) { - ImageLoader* image = it.current(); - // Must advance iterator *before* dispatching call. - // Otherwise, it might be advanced automatically if dispatching the call had a side effect - // of destroying the current ImageLoader, and then we would advance past the *next* item, - // missing one altogether. - ++it; - image->dispatchLoadEvent(); + size_t size = m_imageLoadEventDispatchingList.size(); + for (size_t i = 0; i < size; ++i) { + if (ImageLoader* image = m_imageLoadEventDispatchingList[i]) + image->dispatchLoadEvent(); } m_imageLoadEventDispatchingList.clear(); +#ifdef BUILDING_ON_LEOPARD + if (shouldReenableMemoryCacheClientCalls && page()) + page()->setMemoryCacheClientCallsEnabled(true); +#endif } void Document::imageLoadEventTimerFired(Timer<Document>*) @@ -2952,7 +3028,11 @@ String Document::cookie() const if (page() && !page()->cookieEnabled()) return String(); - return cookies(this, cookieURL()); + KURL cookieURL = this->cookieURL(); + if (cookieURL.isEmpty()) + return String(); + + return cookies(this, cookieURL); } void Document::setCookie(const String& value) @@ -2960,7 +3040,11 @@ void Document::setCookie(const String& value) if (page() && !page()->cookieEnabled()) return; - setCookies(this, cookieURL(), policyBaseURL(), value); + KURL cookieURL = this->cookieURL(); + if (cookieURL.isEmpty()) + return; + + setCookies(this, cookieURL, policyBaseURL(), value); } String Document::referrer() const @@ -2972,7 +3056,7 @@ String Document::referrer() const String Document::domain() const { - return m_securityOrigin->domain(); + return securityOrigin()->domain(); } void Document::setDomain(const String& newDomain) @@ -2983,13 +3067,15 @@ void Document::setDomain(const String& newDomain) // FIXME: We should add logging indicating why a domain was not allowed. // If the new domain is the same as the old domain, still call - // m_securityOrigin.setDomainForDOM. This will change the + // securityOrigin()->setDomainForDOM. This will change the // security check behavior. For example, if a page loaded on port 8000 // assigns its current domain using document.domain, the page will // allow other pages loaded on different ports in the same domain that // have also assigned to access this page. if (equalIgnoringCase(domain(), newDomain)) { - m_securityOrigin->setDomainFromDOM(newDomain); + securityOrigin()->setDomainFromDOM(newDomain); + if (m_frame) + m_frame->script()->updateSecurityOrigin(); return; } @@ -3010,7 +3096,9 @@ void Document::setDomain(const String& newDomain) if (test != newDomain) return; - m_securityOrigin->setDomainFromDOM(newDomain); + securityOrigin()->setDomainFromDOM(newDomain); + if (m_frame) + m_frame->script()->updateSecurityOrigin(); } String Document::lastModified() const @@ -3167,17 +3255,11 @@ void Document::setDecoder(PassRefPtr<TextResourceDecoder> decoder) m_decoder = decoder; } -UChar Document::backslashAsCurrencySymbol() const -{ - if (!m_decoder) - return '\\'; - return m_decoder->encoding().backslashAsCurrencySymbol(); -} - 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(); if (!m_decoder) @@ -3185,11 +3267,6 @@ KURL Document::completeURL(const String& url) const return KURL(m_baseURL, url, m_decoder->encoding()); } -bool Document::inPageCache() -{ - return m_inPageCache; -} - void Document::setInPageCache(bool flag) { if (m_inPageCache == flag) @@ -3233,6 +3310,23 @@ 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; @@ -3912,14 +4006,17 @@ Vector<String> Document::formElementsState() const { Vector<String> stateVector; stateVector.reserveCapacity(m_formElementsWithState.size() * 3); - typedef ListHashSet<HTMLFormControlElementWithState*>::const_iterator Iterator; + typedef ListHashSet<FormControlElementWithState*>::const_iterator Iterator; Iterator end = m_formElementsWithState.end(); for (Iterator it = m_formElementsWithState.begin(); it != end; ++it) { - HTMLFormControlElementWithState* e = *it; + FormControlElementWithState* e = *it; String value; if (e->saveState(value)) { - stateVector.append(e->name().string()); - stateVector.append(e->type().string()); + FormControlElement* formControlElement = e->toFormControlElement(); + ASSERT(formControlElement); + + stateVector.append(formControlElement->name().string()); + stateVector.append(formControlElement->type().string()); stateVector.append(value); } } @@ -4049,7 +4146,7 @@ unsigned FormElementKeyHash::hash(const FormElementKey& k) unsigned l = sizeof(k) / (sizeof(uint16_t) * 2); const uint16_t* s = reinterpret_cast<const uint16_t*>(&k); - uint32_t hash = PHI; + uint32_t hash = WTF::stringHashingStartValue; // Main loop for (; l > 0; l--) { @@ -4101,14 +4198,14 @@ bool Document::useSecureKeyboardEntryWhenActive() const void Document::initSecurityContext() { - if (m_securityOrigin && !m_securityOrigin->isEmpty()) + 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(""); - m_securityOrigin = SecurityOrigin::createEmpty(); + ScriptExecutionContext::setSecurityOrigin(SecurityOrigin::createEmpty()); return; } @@ -4116,7 +4213,7 @@ void Document::initSecurityContext() // loading URL. const KURL& url = m_frame->loader()->url(); m_cookieURL = url; - m_securityOrigin = SecurityOrigin::create(url); + ScriptExecutionContext::setSecurityOrigin(SecurityOrigin::create(url)); if (FrameLoader::allowSubstituteDataAccessToLocal()) { // If this document was loaded with substituteData, then the document can @@ -4125,10 +4222,10 @@ void Document::initSecurityContext() // discussion. DocumentLoader* documentLoader = m_frame->loader()->documentLoader(); if (documentLoader && documentLoader->substituteData().isValid()) - m_securityOrigin->grantLoadLocalResources(); + securityOrigin()->grantLoadLocalResources(); } - if (!m_securityOrigin->isEmpty()) + if (!securityOrigin()->isEmpty()) return; // If we do not obtain a meaningful origin from the URL, then we try to @@ -4142,13 +4239,15 @@ void Document::initSecurityContext() m_cookieURL = ownerFrame->document()->cookieURL(); // We alias the SecurityOrigins to match Firefox, see Bug 15313 // https://bugs.webkit.org/show_bug.cgi?id=15313 - m_securityOrigin = ownerFrame->document()->securityOrigin(); + ScriptExecutionContext::setSecurityOrigin(ownerFrame->document()->securityOrigin()); } } void Document::setSecurityOrigin(SecurityOrigin* securityOrigin) { - m_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(); } @@ -4184,176 +4283,6 @@ DOMSelection* Document::getSelection() const return frame() ? frame()->domWindow()->getSelection() : 0; } -static inline int findSlashDotDotSlash(const UChar* characters, size_t length) -{ - if (length < 4) - return -1; - unsigned loopLimit = length - 3; - for (unsigned i = 0; i < loopLimit; ++i) { - if (characters[i] == '/' && characters[i + 1] == '.' && characters[i + 2] == '.' && characters[i + 3] == '/') - return i; - } - return -1; -} - -static inline int findSlashSlash(const UChar* characters, size_t length, int position) -{ - if (length < 2) - return -1; - unsigned loopLimit = length - 1; - for (unsigned i = position; i < loopLimit; ++i) { - if (characters[i] == '/' && characters[i + 1] == '/') - return i; - } - return -1; -} - -static inline int findSlashDotSlash(const UChar* characters, size_t length) -{ - if (length < 3) - return -1; - unsigned loopLimit = length - 2; - for (unsigned i = 0; i < loopLimit; ++i) { - if (characters[i] == '/' && characters[i + 1] == '.' && characters[i + 2] == '/') - return i; - } - return -1; -} - -static inline bool containsColonSlashSlash(const UChar* characters, unsigned length) -{ - if (length < 3) - return false; - unsigned loopLimit = length - 2; - for (unsigned i = 0; i < loopLimit; ++i) { - if (characters[i] == ':' && characters[i + 1] == '/' && characters[i + 2] == '/') - return true; - } - return false; -} - -static inline void cleanPath(Vector<UChar, 512>& path) -{ - // FIXME: Shold not do this in the query or anchor part. - int pos; - while ((pos = findSlashDotDotSlash(path.data(), path.size())) != -1) { - int prev = reverseFind(path.data(), path.size(), '/', pos - 1); - // don't remove the host, i.e. http://foo.org/../foo.html - if (prev < 0 || (prev > 3 && path[prev - 2] == ':' && path[prev - 1] == '/')) - path.remove(pos, 3); - else - path.remove(prev, pos - prev + 3); - } - - // FIXME: Shold not do this in the query part. - // Set refPos to -2 to mean "I haven't looked for the anchor yet". - // We don't want to waste a function call on the search for the the anchor - // in the vast majority of cases where there is no "//" in the path. - pos = 0; - int refPos = -2; - while ((pos = findSlashSlash(path.data(), path.size(), pos)) != -1) { - if (refPos == -2) - refPos = find(path.data(), path.size(), '#'); - if (refPos > 0 && pos >= refPos) - break; - - if (pos == 0 || path[pos - 1] != ':') - path.remove(pos); - else - pos += 2; - } - - // FIXME: Shold not do this in the query or anchor part. - while ((pos = findSlashDotSlash(path.data(), path.size())) != -1) - path.remove(pos, 2); -} - -static inline bool matchLetter(UChar c, UChar lowercaseLetter) -{ - return (c | 0x20) == lowercaseLetter; -} - -static inline bool needsTrailingSlash(const UChar* characters, unsigned length) -{ - if (length < 6) - return false; - if (!matchLetter(characters[0], 'h') - || !matchLetter(characters[1], 't') - || !matchLetter(characters[2], 't') - || !matchLetter(characters[3], 'p')) - return false; - if (!(characters[4] == ':' - || (matchLetter(characters[4], 's') && characters[5] == ':'))) - return false; - - unsigned pos = characters[4] == ':' ? 5 : 6; - - // Skip initial two slashes if present. - if (pos + 1 < length && characters[pos] == '/' && characters[pos + 1] == '/') - pos += 2; - - // Find next slash. - while (pos < length && characters[pos] != '/') - ++pos; - - return pos == length; -} - -unsigned Document::visitedLinkHash(const AtomicString& attributeURL) const -{ - const UChar* characters = attributeURL.characters(); - unsigned length = attributeURL.length(); - if (!length) - return 0; - - // This is a poor man's completeURL. Faster with less memory allocation. - // FIXME: It's missing a lot of what completeURL does and a lot of what KURL does. - // For example, it does not handle international domain names properly. - - // FIXME: It is wrong that we do not do further processing on strings that have "://" in them: - // 1) The "://" could be in the query or anchor. - // 2) The URL's path could have a "/./" or a "/../" or a "//" sequence in it. - - // FIXME: needsTrailingSlash does not properly return true for a URL that has no path, but does - // have a query or anchor. - - bool hasColonSlashSlash = containsColonSlashSlash(characters, length); - - if (hasColonSlashSlash && !needsTrailingSlash(characters, length)) - return AlreadyHashed::avoidDeletedValue(attributeURL.string().impl()->hash()); - - Vector<UChar, 512> buffer; - - if (hasColonSlashSlash) { - // FIXME: This is incorrect for URLs that have a query or anchor; the "/" needs to go at the - // end of the path, *before* the query or anchor. - buffer.append(characters, length); - buffer.append('/'); - return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(buffer.data(), buffer.size())); - } - - switch (characters[0]) { - case '/': - buffer.append(m_baseURL.string().characters(), m_baseURL.pathStart()); - break; - case '#': - buffer.append(m_baseURL.string().characters(), m_baseURL.pathEnd()); - break; - default: - buffer.append(m_baseURL.string().characters(), m_baseURL.pathAfterLastSlash()); - break; - } - buffer.append(characters, length); - cleanPath(buffer); - if (needsTrailingSlash(buffer.data(), buffer.size())) { - // FIXME: This is incorrect for URLs that have a query or anchor; the "/" needs to go at the - // end of the path, *before* the query or anchor. - buffer.append('/'); - } - - return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(buffer.data(), buffer.size())); -} - #if ENABLE(DATABASE) void Document::addOpenDatabase(Database* database) @@ -4379,7 +4308,7 @@ DatabaseThread* Document::databaseThread() if (!m_databaseThread && !m_hasOpenDatabases) { // Create the database thread on first request - but not if at least one database was already opened, // because in that case we already had a database thread and terminated it and should not create another. - m_databaseThread = DatabaseThread::create(this); + m_databaseThread = DatabaseThread::create(); if (!m_databaseThread->start()) m_databaseThread = 0; } @@ -4405,6 +4334,14 @@ void Document::stopDatabases() #endif +#if ENABLE(WML) +void Document::resetWMLPageState() +{ + if (WMLPageState* pageState = wmlPageStateForDocument(this)) + pageState->reset(); +} +#endif + void Document::attachRange(Range* range) { ASSERT(!m_ranges.contains(range)); @@ -4430,7 +4367,7 @@ HTMLCanvasElement* Document::getCSSCanvasElement(const String& name) { RefPtr<HTMLCanvasElement> result = m_cssCanvasElements.get(name).get(); if (!result) { - result = new HTMLCanvasElement(this); + result = new HTMLCanvasElement(canvasTag, this); m_cssCanvasElements.set(name, result); } return result.get(); @@ -4490,4 +4427,118 @@ void Document::removeTouchEventListener(Node* node) } #endif + +void Document::addTimeout(int timeoutId, DOMTimer* timer) +{ + ASSERT(!m_timeouts.contains(timeoutId)); + m_timeouts.set(timeoutId, timer); +} + +void Document::removeTimeout(int timeoutId) +{ + m_timeouts.remove(timeoutId); +} + +DOMTimer* Document::findTimeout(int timeoutId) +{ + return m_timeouts.get(timeoutId); +} + +void Document::reportException(const String& errorMessage, int lineNumber, const String& sourceURL) +{ + if (DOMWindow* window = domWindow()) + window->console()->addMessage(JSMessageSource, ErrorMessageLevel, errorMessage, lineNumber, sourceURL); +} + +void Document::addMessage(MessageDestination destination, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL) +{ + switch (destination) { + case InspectorControllerDestination: + if (page()) + page()->inspectorController()->addMessageToConsole(source, level, message, lineNumber, sourceURL); + return; + case ConsoleDestination: + if (DOMWindow* window = domWindow()) + window->console()->addMessage(source, level, message, lineNumber, sourceURL); + return; + } + ASSERT_NOT_REACHED(); +} + +void Document::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString) +{ + if (page()) + page()->inspectorController()->resourceRetrievedByXMLHttpRequest(identifier, sourceString); +} + +class ScriptExecutionContextTaskTimer : public TimerBase { +public: + ScriptExecutionContextTaskTimer(PassRefPtr<Document> context, PassRefPtr<ScriptExecutionContext::Task> task) + : m_context(context) + , m_task(task) + { + } + +private: + virtual void fired() + { + m_task->performTask(m_context.get()); + delete this; + } + + RefPtr<Document> m_context; + RefPtr<ScriptExecutionContext::Task> m_task; +}; + +struct PerformTaskContext { + PerformTaskContext(ScriptExecutionContext* scriptExecutionContext, PassRefPtr<ScriptExecutionContext::Task> task) + : scriptExecutionContext(scriptExecutionContext) + , task(task) + { + } + + ScriptExecutionContext* scriptExecutionContext; // The context should exist until task execution. + RefPtr<ScriptExecutionContext::Task> task; +}; + +static void performTask(void* ctx) +{ + PerformTaskContext* ptctx = reinterpret_cast<PerformTaskContext*>(ctx); + ptctx->task->performTask(ptctx->scriptExecutionContext); + delete ptctx; +} + +void Document::postTask(PassRefPtr<Task> task) +{ + if (isMainThread()) { + ScriptExecutionContextTaskTimer* timer = new ScriptExecutionContextTaskTimer(static_cast<Document*>(this), task); + timer->startOneShot(0); + } else { + callOnMainThread(performTask, new PerformTaskContext(this, 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 (inCompatMode()) { + // 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; +} + } // namespace WebCore diff --git a/WebCore/dom/Document.h b/WebCore/dom/Document.h index f674731..e1135bd 100644 --- a/WebCore/dom/Document.h +++ b/WebCore/dom/Document.h @@ -4,6 +4,7 @@ * (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/) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,13 +28,12 @@ #include "Attr.h" #include "Color.h" -#include "DeprecatedPtrList.h" #include "DocumentMarker.h" #include "HTMLCollection.h" #include "HTMLFormElement.h" -#include "KURL.h" #include "ScriptExecutionContext.h" #include "StringHash.h" +#include "TextResourceDecoder.h" #include "Timer.h" #include <wtf/HashCountedSet.h> #include <wtf/ListHashSet.h> @@ -63,6 +63,7 @@ namespace WebCore { class Database; class DOMImplementation; class DOMSelection; + class DOMTimer; class DOMWindow; class DatabaseThread; class DocLoader; @@ -73,12 +74,12 @@ namespace WebCore { class EntityReference; class Event; class EventListener; + class FormControlElementWithState; class Frame; class FrameView; class HTMLCanvasElement; class HTMLDocument; class HTMLElement; - class HTMLFormControlElementWithState; class HTMLFormElement; class HTMLHeadElement; class HTMLInputElement; @@ -95,6 +96,7 @@ namespace WebCore { class Range; class RegisteredEventListener; class RenderArena; + class RenderView; class SecurityOrigin; class Settings; class StyleSheet; @@ -214,7 +216,14 @@ public: DOMImplementation* implementation() const; virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); - Element* documentElement() 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); @@ -272,6 +281,13 @@ public: PassRefPtr<HTMLCollection> windowNamedItems(const String& name); PassRefPtr<HTMLCollection> documentNamedItems(const String& name); + // 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); + HTMLCollection::CollectionInfo* collectionInfo(HTMLCollection::Type type) { ASSERT(type >= HTMLCollection::FirstUnnamedDocumentCachedType); @@ -295,6 +311,9 @@ public: #endif virtual bool isPluginDocument() const { return false; } virtual bool isMediaDocument() const { return false; } +#if ENABLE(WML) + virtual bool isWMLDocument() const { return false; } +#endif CSSStyleSelector* styleSelector() const { return m_styleSelector; } @@ -351,10 +370,12 @@ public: 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; } // Machinery for saving and restoring state when you leave and then go back to a page. - void registerFormElementWithState(HTMLFormControlElementWithState* e) { m_formElementsWithState.add(e); } - void unregisterFormElementWithState(HTMLFormControlElementWithState* e) { m_formElementsWithState.remove(e); } + void registerFormElementWithState(FormControlElementWithState* e) { m_formElementsWithState.add(e); } + void unregisterFormElementWithState(FormControlElementWithState* e) { m_formElementsWithState.remove(e); } Vector<String> formElementsState() const; void setStateForNewFormElements(const Vector<String>&); bool hasStateForNewFormElements() const; @@ -391,6 +412,8 @@ public: RenderArena* renderArena() { return m_renderArena; } + RenderView* renderView() const; + void clearAXObjectCache(); AXObjectCache* axObjectCache() const; @@ -423,8 +446,6 @@ public: KURL completeURL(const String&) const; - unsigned visitedLinkHash(const AtomicString& attributeURL) const; - // from cachedObjectClient virtual void setCSSStyleSheet(const String& url, const String& charset, const CachedCSSStyleSheet*); @@ -728,7 +749,7 @@ public: void setDocType(PassRefPtr<DocumentType>); - void finishedParsing(); + virtual void finishedParsing(); #if ENABLE(XPATH) // XPathEvaluator methods @@ -785,6 +806,18 @@ public: void initDNSPrefetch(); void parseDNSPrefetchControlHeader(const String&); + virtual void reportException(const String& errorMessage, int lineNumber, const String& sourceURL); + virtual void addMessage(MessageDestination, MessageSource, MessageLevel, const String& message, unsigned lineNumber, const String& sourceURL); + virtual void resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString); + virtual void postTask(PassRefPtr<Task>); // Executes the task on context's thread asynchronously. + + void addTimeout(int timeoutId, DOMTimer*); + void removeTimeout(int timeoutId); + DOMTimer* findTimeout(int timeoutId); + +protected: + Document(Frame*, bool isXHTML); + #if ENABLE(TOUCH_EVENTS) // Android public: typedef HashMap< RefPtr<Node>, unsigned > TouchListenerMap; @@ -797,14 +830,12 @@ private: TouchListenerMap m_touchEventListeners; #endif -protected: - Document(Frame*, bool isXHTML); - private: 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. CSSStyleSelector* m_styleSelector; bool m_didCalculateStyleSelector; @@ -881,10 +912,10 @@ private: RefPtr<StyleSheetList> m_styleSheets; // All of the stylesheets that are currently in effect for our media type and stylesheet set. ListHashSet<Node*> m_styleSheetCandidateNodes; // All of the nodes that could potentially provide stylesheets to the document (<link>, <style>, <?xml-stylesheet>) - RegisteredEventListenerList m_windowEventListeners; + RegisteredEventListenerVector m_windowEventListeners; typedef HashMap<FormElementKey, Vector<String>, FormElementKeyHash, FormElementKeyHashTraits> FormElementStateMap; - ListHashSet<HTMLFormControlElementWithState*> m_formElementsWithState; + ListHashSet<FormControlElementWithState*> m_formElementsWithState; FormElementStateMap m_stateForNewFormElements; Color m_linkColor; @@ -904,10 +935,11 @@ private: bool m_usesSiblingRules; bool m_usesFirstLineRules; bool m_usesFirstLetterRules; + bool m_usesBeforeAfterRules; bool m_gotoAnchorNeededAfterStylesheetsLoad; - bool m_isDNSPrefetchEnabled; bool m_haveExplicitlyDisabledDNSPrefetch; + bool m_frameElementsShouldIgnoreScrolling; String m_title; bool m_titleSetExplicitly; @@ -921,8 +953,8 @@ private: mutable AXObjectCache* m_axObjectCache; - DeprecatedPtrList<ImageLoader> m_imageLoadEventDispatchSoonList; - DeprecatedPtrList<ImageLoader> m_imageLoadEventDispatchingList; + Vector<ImageLoader*> m_imageLoadEventDispatchSoonList; + Vector<ImageLoader*> m_imageLoadEventDispatchingList; Timer<Document> m_imageLoadEventTimer; Timer<Document> m_updateFocusAppearanceTimer; @@ -956,7 +988,7 @@ private: String m_contentLanguage; public: - bool inPageCache(); + bool inPageCache() const { return m_inPageCache; } void setInPageCache(bool flag); // Elements can register themselves for the "documentWillBecomeInactive()" and @@ -966,13 +998,34 @@ public: 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(); } - UChar backslashAsCurrencySymbol() const; + String displayStringModifiedByEncoding(const String& str) const { + if (m_decoder) + return m_decoder->encoding().displayString(str.impl()); + return str; + } + PassRefPtr<StringImpl> displayStringModifiedByEncoding(PassRefPtr<StringImpl> str) const { + if (m_decoder) + return m_decoder->encoding().displayString(str); + return str; + } + void displayBufferModifiedByEncoding(UChar* buffer, unsigned len) const { + if (m_decoder) + m_decoder->encoding().displayBuffer(buffer, len); + } + + // 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; } @@ -996,7 +1049,6 @@ public: #endif void initSecurityContext(); - SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); } // Explicitly override the security origin for this document. // Note: It is dangerous to change the security origin of a document @@ -1016,7 +1068,11 @@ public: void setUsingGeolocation(bool f) { m_usingGeolocation = f; } bool usingGeolocation() const { return m_usingGeolocation; }; - + +#if ENABLE(WML) + void resetWMLPageState(); +#endif + protected: void clearXMLVersion() { m_xmlVersion = String(); } @@ -1027,7 +1083,7 @@ private: void updateFocusAppearanceTimerFired(Timer<Document>*); void updateBaseURL(); - RefPtr<SecurityOrigin> m_securityOrigin; + void cacheDocumentElement() const; RenderObject* m_savedRenderer; int m_secureForms; @@ -1074,6 +1130,7 @@ private: String m_iconURL; HashSet<Element*> m_documentActivationCallbackElements; + HashSet<Element*> m_mediaVolumeCallbackElements; bool m_useSecureKeyboardEntryWhenActive; @@ -1102,6 +1159,9 @@ private: #ifdef ANDROID_MOBILE int mExtraLayoutDelay; #endif + + typedef HashMap<int, DOMTimer*> TimeoutsMap; + TimeoutsMap m_timeouts; }; inline bool Document::hasElementWithId(AtomicStringImpl* id) const diff --git a/WebCore/dom/Document.idl b/WebCore/dom/Document.idl index 968d52a..dfdfe46 100644 --- a/WebCore/dom/Document.idl +++ b/WebCore/dom/Document.idl @@ -238,6 +238,11 @@ module core { raises(DOMException); NodeList querySelectorAll(in [ConvertUndefinedOrNullToNullString] DOMString selectors) raises(DOMException); + +#if ENABLE_WML + // Only used from within WML layout tests, WML doesn't have JS support at all. + void resetWMLPageState(); +#endif }; } diff --git a/WebCore/dom/EditingText.cpp b/WebCore/dom/EditingText.cpp index 59661e9..40cf97d 100644 --- a/WebCore/dom/EditingText.cpp +++ b/WebCore/dom/EditingText.cpp @@ -44,7 +44,7 @@ EditingText::~EditingText() { } -bool EditingText::rendererIsNeeded(RenderStyle *style) +bool EditingText::rendererIsNeeded(RenderStyle*) { return true; } diff --git a/WebCore/dom/Element.cpp b/WebCore/dom/Element.cpp index f228d53..781179d 100644 --- a/WebCore/dom/Element.cpp +++ b/WebCore/dom/Element.cpp @@ -4,7 +4,7 @@ * (C) 2001 Peter Kelly (pmk@post.com) * (C) 2001 Dirk Mueller (mueller@kde.org) * (C) 2007 David Smith (catfish.man@gmail.com) - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * 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 @@ -56,13 +56,6 @@ using namespace XMLNames; Element::Element(const QualifiedName& tagName, Document* doc) : ContainerNode(doc, true) , m_tagName(tagName) - , m_isStyleAttributeValid(true) - , m_synchronizingStyleAttribute(false) -#if ENABLE(SVG) - , m_areSVGAttributesValid(true) - , m_synchronizingSVGAttributes(false) -#endif - , m_parsingChildrenFinished(true) { } @@ -85,7 +78,7 @@ inline ElementRareData* Element::ensureRareData() NodeRareData* Element::createRareData() { - return new ElementRareData(this); + return new ElementRareData; } PassRefPtr<Node> Element::cloneNode(bool deep) @@ -106,6 +99,11 @@ PassRefPtr<Node> Element::cloneNode(bool deep) return clone.release(); } +PassRefPtr<Element> Element::cloneElement() +{ + return static_pointer_cast<Element>(cloneNode(false)); +} + void Element::removeAttribute(const QualifiedName& name, ExceptionCode& ec) { if (namedAttrMap) { @@ -219,7 +217,7 @@ void Element::scrollByUnits(int units, ScrollGranularity granularity) direction = ScrollUp; units = -units; } - rend->layer()->scroll(direction, granularity, units); + toRenderBox(rend)->layer()->scroll(direction, granularity, units); } } } @@ -276,7 +274,7 @@ static int adjustForAbsoluteZoom(int value, RenderObject* renderer) int Element::offsetLeft() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) return adjustForLocalZoom(rend->offsetLeft(), rend); return 0; } @@ -284,7 +282,7 @@ int Element::offsetLeft() int Element::offsetTop() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) return adjustForLocalZoom(rend->offsetTop(), rend); return 0; } @@ -292,7 +290,7 @@ int Element::offsetTop() int Element::offsetWidth() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) return adjustForAbsoluteZoom(rend->offsetWidth(), rend); return 0; } @@ -300,7 +298,7 @@ int Element::offsetWidth() int Element::offsetHeight() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) return adjustForAbsoluteZoom(rend->offsetHeight(), rend); return 0; } @@ -308,7 +306,7 @@ int Element::offsetHeight() Element* Element::offsetParent() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) if (RenderObject* offsetParent = rend->offsetParent()) return static_cast<Element*>(offsetParent->element()); return 0; @@ -318,7 +316,7 @@ int Element::clientLeft() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) return adjustForAbsoluteZoom(rend->clientLeft(), rend); return 0; } @@ -327,7 +325,7 @@ int Element::clientTop() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) return adjustForAbsoluteZoom(rend->clientTop(), rend); return 0; } @@ -342,12 +340,14 @@ int Element::clientWidth() if ((!inCompatMode && document()->documentElement() == this) || (inCompatMode && isHTMLElement() && document()->body() == this)) { if (FrameView* view = document()->view()) - return view->visibleWidth(); + return view->layoutWidth(); } - if (RenderObject* rend = renderer()) - return adjustForAbsoluteZoom(rend->clientWidth(), rend); + if (RenderBox* rend = renderBox()) { + if (!rend->isRenderInline()) + return adjustForAbsoluteZoom(rend->clientWidth(), rend); + } return 0; } @@ -362,18 +362,20 @@ int Element::clientHeight() if ((!inCompatMode && document()->documentElement() == this) || (inCompatMode && isHTMLElement() && document()->body() == this)) { if (FrameView* view = document()->view()) - return view->visibleHeight(); + return view->layoutHeight(); } - if (RenderObject* rend = renderer()) - return adjustForAbsoluteZoom(rend->clientHeight(), rend); + if (RenderBox* rend = renderBox()) { + if (!rend->isRenderInline()) + return adjustForAbsoluteZoom(rend->clientHeight(), rend); + } return 0; } int Element::scrollLeft() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) return adjustForAbsoluteZoom(rend->scrollLeft(), rend); return 0; } @@ -381,7 +383,7 @@ int Element::scrollLeft() int Element::scrollTop() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) return adjustForAbsoluteZoom(rend->scrollTop(), rend); return 0; } @@ -389,30 +391,34 @@ int Element::scrollTop() void Element::setScrollLeft(int newLeft) { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) rend->setScrollLeft(static_cast<int>(newLeft * rend->style()->effectiveZoom())); } void Element::setScrollTop(int newTop) { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) + if (RenderBox* rend = renderBox()) rend->setScrollTop(static_cast<int>(newTop * rend->style()->effectiveZoom())); } int Element::scrollWidth() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) - return adjustForAbsoluteZoom(rend->scrollWidth(), rend); + if (RenderBox* rend = renderBox()) { + if (rend->hasOverflowClip() || !rend->isRenderInline()) + return adjustForAbsoluteZoom(rend->scrollWidth(), rend); + } return 0; } int Element::scrollHeight() { document()->updateLayoutIgnorePendingStylesheets(); - if (RenderObject* rend = renderer()) - return adjustForAbsoluteZoom(rend->scrollHeight(), rend); + if (RenderBox* rend = renderBox()) { + if (rend->hasOverflowClip() || !rend->isRenderInline()) + return adjustForAbsoluteZoom(rend->scrollHeight(), rend); + } return 0; } @@ -496,18 +502,18 @@ PassRefPtr<Attribute> Element::createAttribute(const QualifiedName& name, const return Attribute::create(name, value); } -void Element::attributeChanged(Attribute* attr, bool preserveDecls) +void Element::attributeChanged(Attribute* attr, bool) { + if (!document()->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 - if (document()->axObjectCache()->accessibilityEnabled()) - document()->axObjectCache()->handleActiveDescendantChanged(renderer()); - } - if (attrName == roleAttr) { + document()->axObjectCache()->handleActiveDescendantChanged(renderer()); + } else if (attrName == roleAttr) { // the role attribute can change at any time, and the AccessibilityObject must pick up these changes - if (document()->axObjectCache()->accessibilityEnabled()) - document()->axObjectCache()->handleAriaRoleChanged(renderer()); + document()->axObjectCache()->handleAriaRoleChanged(renderer()); } } @@ -587,19 +593,12 @@ KURL Element::baseURI() const return KURL(parentBase, base.string()); } -bool Element::contains(const Node* node) const -{ - if (!node) - return false; - return this == node || node->isDescendantOf(this); -} - void Element::createAttributeMap() const { namedAttrMap = NamedAttrMap::create(const_cast<Element*>(this)); } -bool Element::isURLAttribute(Attribute *attr) const +bool Element::isURLAttribute(Attribute*) const { return false; } @@ -656,10 +655,10 @@ void Element::attach() ContainerNode::attach(); if (hasRareData()) { ElementRareData* data = rareData(); - if (data->m_needsFocusAppearanceUpdateSoonAfterAttach) { + if (data->needsFocusAppearanceUpdateSoonAfterAttach()) { if (isFocusable() && document()->focusedNode() == this) document()->updateFocusAppearanceSoon(); - data->m_needsFocusAppearanceUpdateSoonAfterAttach = false; + data->setNeedsFocusAppearanceUpdateSoonAfterAttach(false); } } } @@ -668,7 +667,7 @@ void Element::detach() { cancelFocusAppearanceUpdate(); if (hasRareData()) - rareData()->resetComputedStyle(this); + rareData()->resetComputedStyle(); ContainerNode::detach(); } @@ -686,7 +685,7 @@ void Element::recalcStyle(StyleChange change) if ((change > NoChange || changed())) { if (hasRareData()) - rareData()->resetComputedStyle(this); + rareData()->resetComputedStyle(); } if (hasParentStyle && (change >= Inherit || changed())) { RefPtr<RenderStyle> newStyle = document()->styleSelector()->styleForElement(this); @@ -884,7 +883,7 @@ void Element::dispatchAttrRemovalEvent(Attribute*) #endif } -void Element::dispatchAttrAdditionEvent(Attribute *attr) +void Element::dispatchAttrAdditionEvent(Attribute*) { ASSERT(!eventDispatchForbidden()); #if 0 @@ -1090,7 +1089,7 @@ void Element::focus(bool restorePreviousSelection) page->focusController()->setFocusedNode(this, doc->frame()); if (!isFocusable()) { - ensureRareData()->m_needsFocusAppearanceUpdateSoonAfterAttach = true; + ensureRareData()->setNeedsFocusAppearanceUpdateSoonAfterAttach(true); return; } @@ -1098,7 +1097,7 @@ void Element::focus(bool restorePreviousSelection) updateFocusAppearance(restorePreviousSelection); } -void Element::updateFocusAppearance(bool restorePreviousSelection) +void Element::updateFocusAppearance(bool /*restorePreviousSelection*/) { if (this == rootEditableElement()) { Frame* frame = document()->frame(); @@ -1199,7 +1198,7 @@ RenderStyle* Element::computedStyle() void Element::cancelFocusAppearanceUpdate() { if (hasRareData()) - rareData()->m_needsFocusAppearanceUpdateSoonAfterAttach = false; + rareData()->setNeedsFocusAppearanceUpdateSoonAfterAttach(false); if (document()->focusedNode() == this) document()->cancelFocusAppearanceUpdate(); } diff --git a/WebCore/dom/Element.h b/WebCore/dom/Element.h index b9dc0fd..e9bab28 100644 --- a/WebCore/dom/Element.h +++ b/WebCore/dom/Element.h @@ -116,8 +116,12 @@ public: virtual void removedFromDocument(); virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + PassRefPtr<Element> cloneElement(); + void normalizeAttributes(); + virtual bool isFormControlElement() const { return false; } + virtual bool isFormControlElementWithState() const { return false; } virtual bool isInputTypeHidden() const { return false; } virtual bool isPasswordField() const { return false; } @@ -136,7 +140,7 @@ public: // not part of the DOM void setAttributeMap(PassRefPtr<NamedAttrMap>); - virtual void copyNonAttributeProperties(const Element* source) {} + virtual void copyNonAttributeProperties(const Element* /*source*/) { } virtual void attach(); virtual void detach(); @@ -152,7 +156,7 @@ public: void dispatchAttrRemovalEvent(Attribute*); void dispatchAttrAdditionEvent(Attribute*); - virtual void accessKeyAction(bool sendToAnyEvent) { } + virtual void accessKeyAction(bool /*sendToAnyEvent*/) { } virtual bool isURLAttribute(Attribute*) const; virtual const QualifiedName& imageSourceAttributeName() const; @@ -165,8 +169,6 @@ public: virtual void formatForDebugger(char* buffer, unsigned length) const; #endif - bool contains(const Node*) const; - String innerText() const; String outerText() const; @@ -179,10 +181,13 @@ public: IntSize minimumSizeForResizing() const; void setMinimumSizeForResizing(const IntSize&); - // Use Document::registerForDocumentActivationCallbacks() to subscribe these + // 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 m_parsingChildrenFinished; } virtual void finishParsingChildren(); virtual void beginParsingChildren() { m_parsingChildrenFinished = false; } @@ -218,20 +223,6 @@ protected: ElementRareData* ensureRareData(); mutable RefPtr<NamedAttrMap> namedAttrMap; - - // These two bits are really used by the StyledElement subclass, but they are pulled up here in order to be shared with other - // Element bits. - mutable bool m_isStyleAttributeValid : 1; - mutable bool m_synchronizingStyleAttribute : 1; - -#if ENABLE(SVG) - // These bit is are used by SVGElement subclasses, and it lives here for the same reason as above. - mutable bool m_areSVGAttributesValid : 1; - mutable bool m_synchronizingSVGAttributes : 1; -#endif - -private: - bool m_parsingChildrenFinished : 1; }; inline bool Node::hasTagName(const QualifiedName& name) const @@ -249,6 +240,12 @@ inline NamedAttrMap* Node::attributes() const return isElementNode() ? static_cast<const Element*>(this)->attributes() : 0; } +inline Element* Node::parentElement() const +{ + Node* parent = parentNode(); + return parent && parent->isElementNode() ? static_cast<Element*>(parent) : 0; +} + } //namespace #endif diff --git a/WebCore/dom/ElementRareData.h b/WebCore/dom/ElementRareData.h index 8250757..94e0499 100644 --- a/WebCore/dom/ElementRareData.h +++ b/WebCore/dom/ElementRareData.h @@ -1,6 +1,5 @@ -/** - * - * Copyright (C) 2008 Apple Computer, Inc. +/* + * Copyright (C) 2008, 2009 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 @@ -30,8 +29,12 @@ namespace WebCore { class ElementRareData : public NodeRareData { public: - ElementRareData(Element*); - void resetComputedStyle(Element*); + ElementRareData(); + + void resetComputedStyle(); + + using NodeRareData::needsFocusAppearanceUpdateSoonAfterAttach; + using NodeRareData::setNeedsFocusAppearanceUpdateSoonAfterAttach; IntSize m_minimumSizeForResizing; RefPtr<RenderStyle> m_computedStyle; @@ -42,12 +45,12 @@ inline IntSize defaultMinimumSizeForResizing() return IntSize(INT_MAX, INT_MAX); } -inline ElementRareData::ElementRareData(Element* e) +inline ElementRareData::ElementRareData() : m_minimumSizeForResizing(defaultMinimumSizeForResizing()) { } -inline void ElementRareData::resetComputedStyle(Element* element) +inline void ElementRareData::resetComputedStyle() { m_computedStyle.clear(); } diff --git a/WebCore/dom/Event.cpp b/WebCore/dom/Event.cpp index 728afef..918f463 100644 --- a/WebCore/dom/Event.cpp +++ b/WebCore/dom/Event.cpp @@ -24,7 +24,7 @@ #include "Event.h" #include "AtomicString.h" -#include "SystemTime.h" +#include <wtf/CurrentTime.h> namespace WebCore { diff --git a/WebCore/dom/Event.h b/WebCore/dom/Event.h index 474ec6a..df92de1 100644 --- a/WebCore/dom/Event.h +++ b/WebCore/dom/Event.h @@ -87,7 +87,7 @@ namespace WebCore { bool bubbles() const { return m_canBubble; } bool cancelable() const { return m_cancelable; } - DOMTimeStamp timeStamp() { return m_createTime; } + DOMTimeStamp timeStamp() const { return m_createTime; } void stopPropagation() { m_propagationStopped = true; } // IE Extensions diff --git a/WebCore/dom/Event.idl b/WebCore/dom/Event.idl index ae394b9..99b0bd1 100644 --- a/WebCore/dom/Event.idl +++ b/WebCore/dom/Event.idl @@ -23,6 +23,7 @@ module events { // Introduced in DOM Level 2: interface [ GenerateConstructor, + NoStaticTables, ObjCCustomInternalImpl, InterfaceUUID=D17495FA-ACAD-4d27-9362-E19E057B189D, ImplementationUUID=CFDCDDB2-5B3F-412d-BDA4-80B23C721549 diff --git a/WebCore/dom/EventNames.cpp b/WebCore/dom/EventNames.cpp index 22a716d..00191ab 100644 --- a/WebCore/dom/EventNames.cpp +++ b/WebCore/dom/EventNames.cpp @@ -23,21 +23,8 @@ #include "config.h" #include "EventNames.h" -#include <wtf/Threading.h> - -#if ENABLE(WORKERS) -#include <wtf/ThreadSpecific.h> -using namespace WTF; -#endif - namespace WebCore { -#if ENABLE(WORKERS) -static ThreadSpecific<EventNames>* staticEventNames; -#else -static EventNames* staticEventNames; -#endif - #define INITIALIZE_EVENT_NAME(name) \ , name##Event(#name) EventNames::EventNames() @@ -46,29 +33,4 @@ DOM_EVENT_NAMES_FOR_EACH(INITIALIZE_EVENT_NAME) { } -EventNames& eventNames() -{ -#if ENABLE(WORKERS) - return **staticEventNames; -#else - return *staticEventNames; -#endif -} - -void EventNames::init() -{ - if (!staticEventNames) { - // Initialization is not thread safe, so this function must be called from the main thread first. - ASSERT(isMainThread()); - - AtomicString::init(); - -#if ENABLE(WORKERS) - staticEventNames = new ThreadSpecific<EventNames>; -#else - staticEventNames = new EventNames; -#endif - } -} - } diff --git a/WebCore/dom/EventNames.h b/WebCore/dom/EventNames.h index fc24797..d6f2314 100644 --- a/WebCore/dom/EventNames.h +++ b/WebCore/dom/EventNames.h @@ -23,10 +23,7 @@ #define EventNames_h #include "AtomicString.h" - -namespace WTF { - template<typename> class ThreadSpecific; -} +#include "ThreadGlobalData.h" namespace WebCore { @@ -71,6 +68,7 @@ namespace WebCore { macro(mouseup) \ macro(mousewheel) \ macro(noupdate) \ + macro(obsolete) \ macro(offline) \ macro(online) \ macro(overflowchanged) \ @@ -138,20 +136,20 @@ namespace WebCore { // end of DOM_EVENT_NAMES_FOR_EACH class EventNames { - friend class WTF::ThreadSpecific<EventNames>; - - EventNames(); int dummy; // Needed to make initialization macro work. public: - static void init(); + EventNames(); #define DOM_EVENT_NAMES_DECLARE(name) AtomicString name##Event; DOM_EVENT_NAMES_FOR_EACH(DOM_EVENT_NAMES_DECLARE) #undef DOM_EVENT_NAMES_DECLARE }; - EventNames& eventNames(); + inline EventNames& eventNames() + { + return threadGlobalData().eventNames(); + } } diff --git a/WebCore/dom/EventTarget.cpp b/WebCore/dom/EventTarget.cpp index 12b6e8c..dcebd64 100644 --- a/WebCore/dom/EventTarget.cpp +++ b/WebCore/dom/EventTarget.cpp @@ -78,6 +78,18 @@ MessagePort* EventTarget::toMessagePort() return 0; } +#if ENABLE(WORKERS) +Worker* EventTarget::toWorker() +{ + return 0; +} + +WorkerContext* EventTarget::toWorkerContext() +{ + return 0; +} +#endif + #ifndef NDEBUG void forbidEventDispatch() { diff --git a/WebCore/dom/EventTarget.h b/WebCore/dom/EventTarget.h index da2130c..3a3ec84 100644 --- a/WebCore/dom/EventTarget.h +++ b/WebCore/dom/EventTarget.h @@ -42,17 +42,15 @@ namespace WebCore { class EventListener; class EventTargetNode; class MessagePort; - class RegisteredEventListener; class ScriptExecutionContext; class SVGElementInstance; + class Worker; + class WorkerContext; class XMLHttpRequest; class XMLHttpRequestUpload; typedef int ExceptionCode; - template<typename T> class DeprecatedValueList; - typedef DeprecatedValueList<RefPtr<RegisteredEventListener> > RegisteredEventListenerList; - class EventTarget { public: virtual MessagePort* toMessagePort(); @@ -65,6 +63,10 @@ namespace WebCore { #if ENABLE(SVG) virtual SVGElementInstance* toSVGElementInstance(); #endif +#if ENABLE(WORKERS) + virtual Worker* toWorker(); + virtual WorkerContext* toWorkerContext(); +#endif virtual ScriptExecutionContext* scriptExecutionContext() const = 0; @@ -78,7 +80,7 @@ namespace WebCore { // 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) { } + virtual void postDispatchEventHandler(Event*, void* /*dataFromPreDispatch*/) { } protected: virtual ~EventTarget(); @@ -88,14 +90,16 @@ namespace WebCore { virtual void derefEventTarget() = 0; }; + void forbidEventDispatch(); + void allowEventDispatch(); + #ifndef NDEBUG -void forbidEventDispatch(); -void allowEventDispatch(); -bool eventDispatchForbidden(); + bool eventDispatchForbidden(); #else -inline void forbidEventDispatch() { } -inline void allowEventDispatch() { } -#endif // NDEBUG + inline void forbidEventDispatch() { } + inline void allowEventDispatch() { } +#endif } + #endif diff --git a/WebCore/dom/EventTargetNode.cpp b/WebCore/dom/EventTargetNode.cpp index eb61d41..d3d9a32 100644 --- a/WebCore/dom/EventTargetNode.cpp +++ b/WebCore/dom/EventTargetNode.cpp @@ -35,6 +35,7 @@ #include "KeyboardEvent.h" #include "MouseEvent.h" #include "MutationEvent.h" +#include "NodeRareData.h" #include "Page.h" #include "PlatformMouseEvent.h" #include "PlatformWheelEvent.h" @@ -60,19 +61,15 @@ namespace WebCore { static HashSet<EventTargetNode*>* gNodesDispatchingSimulatedClicks = 0; -EventTargetNode::EventTargetNode(Document* doc, bool isElement, bool isContainer) - : Node(doc, isElement, isContainer) - , m_regdListeners(0) +EventTargetNode::EventTargetNode(Document* doc, bool isElement, bool isContainer, bool isText) + : Node(doc, isElement, isContainer, isText) { } EventTargetNode::~EventTargetNode() { - if (m_regdListeners && !m_regdListeners->isEmpty() && !inDocument()) + if (!eventListeners().isEmpty() && !inDocument()) document()->unregisterDisconnectedNodeWithEventListeners(this); - - delete m_regdListeners; - m_regdListeners = 0; } ScriptExecutionContext* EventTargetNode::scriptExecutionContext() const @@ -80,9 +77,19 @@ ScriptExecutionContext* EventTargetNode::scriptExecutionContext() const return document(); } +const RegisteredEventListenerVector& EventTargetNode::eventListeners() const +{ + if (hasRareData()) { + if (RegisteredEventListenerVector* listeners = rareData()->listeners()) + return *listeners; + } + static const RegisteredEventListenerVector* emptyListenersVector = new RegisteredEventListenerVector; + return *emptyListenersVector; +} + void EventTargetNode::insertedIntoDocument() { - if (m_regdListeners && !m_regdListeners->isEmpty()) + if (!eventListeners().isEmpty()) document()->unregisterDisconnectedNodeWithEventListeners(this); Node::insertedIntoDocument(); @@ -90,7 +97,7 @@ void EventTargetNode::insertedIntoDocument() void EventTargetNode::removedFromDocument() { - if (m_regdListeners && !m_regdListeners->isEmpty()) + if (!eventListeners().isEmpty()) document()->registerDisconnectedNodeWithEventListeners(this); Node::removedFromDocument(); @@ -98,7 +105,7 @@ void EventTargetNode::removedFromDocument() void EventTargetNode::willMoveToNewOwnerDocument() { - if (m_regdListeners && !m_regdListeners->isEmpty()) + if (!eventListeners().isEmpty()) document()->unregisterDisconnectedNodeWithEventListeners(this); Node::willMoveToNewOwnerDocument(); @@ -106,7 +113,7 @@ void EventTargetNode::willMoveToNewOwnerDocument() void EventTargetNode::didMoveToNewOwnerDocument() { - if (m_regdListeners && !m_regdListeners->isEmpty()) + if (!eventListeners().isEmpty()) document()->registerDisconnectedNodeWithEventListeners(this); Node::didMoveToNewOwnerDocument(); @@ -138,24 +145,23 @@ static inline void updateSVGElementInstancesAfterEventListenerChange(EventTarget void EventTargetNode::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) { - Document* doc = document(); - if (!doc->attached()) + Document* document = this->document(); + if (!document->attached()) return; - doc->addListenerTypeIfNeeded(eventType); + document->addListenerTypeIfNeeded(eventType); - if (!m_regdListeners) - m_regdListeners = new RegisteredEventListenerList; + RegisteredEventListenerVector& listeners = ensureRareData()->ensureListeners(); // Remove existing identical listener set with identical arguments. // The DOM2 spec says that "duplicate instances are discarded" in this case. removeEventListener(eventType, listener.get(), useCapture); // adding the first one - if (m_regdListeners->isEmpty() && !inDocument()) - doc->registerDisconnectedNodeWithEventListeners(this); + if (listeners.isEmpty() && !inDocument()) + document->registerDisconnectedNodeWithEventListeners(this); - m_regdListeners->append(RegisteredEventListener::create(eventType, listener, useCapture)); + listeners.append(RegisteredEventListener::create(eventType, listener, useCapture)); updateSVGElementInstancesAfterEventListenerChange(this); #if ENABLE(TOUCH_EVENTS) // Android @@ -163,24 +169,28 @@ void EventTargetNode::addEventListener(const AtomicString& eventType, PassRefPtr eventType == eventNames().touchendEvent || eventType == eventNames().touchmoveEvent || eventType == eventNames().touchcancelEvent) - doc->addTouchEventListener(this); + document->addTouchEventListener(this); #endif } void EventTargetNode::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) { - if (!m_regdListeners) + if (!hasRareData()) + return; + + RegisteredEventListenerVector* listeners = rareData()->listeners(); + if (!listeners) return; - RegisteredEventListenerList::Iterator end = m_regdListeners->end(); - for (RegisteredEventListenerList::Iterator it = m_regdListeners->begin(); it != end; ++it) { - RegisteredEventListener& r = **it; + size_t size = listeners->size(); + for (size_t i = 0; i < size; ++i) { + RegisteredEventListener& r = *listeners->at(i); if (r.eventType() == eventType && r.listener() == listener && r.useCapture() == useCapture) { - (*it)->setRemoved(true); - it = m_regdListeners->remove(it); + r.setRemoved(true); + listeners->remove(i); // removed last - if (m_regdListeners->isEmpty() && !inDocument()) + if (listeners->isEmpty() && !inDocument()) document()->unregisterDisconnectedNodeWithEventListeners(this); updateSVGElementInstancesAfterEventListenerChange(this); @@ -199,27 +209,33 @@ void EventTargetNode::removeEventListener(const AtomicString& eventType, EventLi void EventTargetNode::removeAllEventListeners() { + if (!hasRareData()) + return; + + RegisteredEventListenerVector* listeners = rareData()->listeners(); + if (!listeners) + return; + #if ENABLE(TOUCH_EVENTS) // Android document()->removeTouchEventListener(this); #endif - delete m_regdListeners; - m_regdListeners = 0; + size_t size = listeners->size(); + for (size_t i = 0; i < size; ++i) + listeners->at(i)->setRemoved(true); + listeners->clear(); } -void EventTargetNode::handleLocalEvents(Event *evt, bool useCapture) +void EventTargetNode::handleLocalEvents(Event* event, bool useCapture) { - if (disabled() && evt->isMouseEvent()) - return; - - if (!m_regdListeners || m_regdListeners->isEmpty()) + if (disabled() && event->isMouseEvent()) return; - RegisteredEventListenerList listenersCopy = *m_regdListeners; - RegisteredEventListenerList::Iterator end = listenersCopy.end(); - - for (RegisteredEventListenerList::Iterator it = listenersCopy.begin(); it != end; ++it) { - if ((*it)->eventType() == evt->type() && (*it)->useCapture() == useCapture && !(*it)->removed()) - (*it)->listener()->handleEvent(evt, false); + RegisteredEventListenerVector listenersCopy = eventListeners(); + size_t size = listenersCopy.size(); + for (size_t i = 0; i < size; ++i) { + const RegisteredEventListener& r = *listenersCopy[i]; + if (r.eventType() == event->type() && r.useCapture() == useCapture && !r.removed()) + r.listener()->handleEvent(event, false); } } @@ -273,135 +289,131 @@ bool EventTargetNode::dispatchEvent(PassRefPtr<Event> e, ExceptionCode& ec) evt->setTarget(eventTargetRespectingSVGTargetRules(this)); RefPtr<FrameView> view = document()->view(); - return dispatchGenericEvent(evt.release(), ec); + return dispatchGenericEvent(evt.release()); } -bool EventTargetNode::dispatchGenericEvent(PassRefPtr<Event> e, ExceptionCode& ec) +bool EventTargetNode::dispatchGenericEvent(PassRefPtr<Event> prpEvent) { - RefPtr<Event> evt(e); + RefPtr<Event> event(prpEvent); ASSERT(!eventDispatchForbidden()); - ASSERT(evt->target()); - ASSERT(!evt->type().isNull()); // JavaScript code could create an event with an empty name - - // work out what nodes to send event to - DeprecatedPtrList<Node> nodeChain; - + 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<EventTargetNode> thisNode(this); + Vector<RefPtr<ContainerNode> > ancestors; if (inDocument()) { - for (Node* n = this; n; n = n->eventParentNode()) { + for (ContainerNode* ancestor = eventParentNode(); ancestor; ancestor = ancestor->eventParentNode()) { #if ENABLE(SVG) - // Skip <use> shadow tree elements - if (n->isSVGElement() && n->isShadowNode()) + // Skip <use> shadow tree elements. + if (ancestor->isSVGElement() && ancestor->isShadowNode()) continue; #endif - - n->ref(); - nodeChain.prepend(n); + ancestors.append(ancestor); } - } else { - // if node is not in the document just send event to itself - ref(); - nodeChain.prepend(this); } - DeprecatedPtrListIterator<Node> it(nodeChain); - - // Before we begin dispatching events, give the target node a chance to do some work prior - // to the DOM event handlers getting a crack. - void* data = preDispatchEventHandler(evt.get()); - - // trigger any capturing event handlers on our way down - evt->setEventPhase(Event::CAPTURING_PHASE); - it.toFirst(); - - // Handle window events for capture phase, except load events, this quirk is needed - // because Mozilla used to never propagate load events to the window object - if (evt->type() != eventNames().loadEvent && it.current()->isDocumentNode() && !evt->propagationStopped()) - static_cast<Document*>(it.current())->handleWindowEvent(evt.get(), true); - - EventTargetNode* eventTargetNode = 0; - for (; it.current() && it.current() != this && !evt->propagationStopped(); ++it) { - eventTargetNode = EventTargetNodeCast(it.current()); - evt->setCurrentTarget(eventTargetRespectingSVGTargetRules(eventTargetNode)); - - eventTargetNode->handleLocalEvents(evt.get(), true); + // Set up a pointer to indicate whether to dispatch window events. + // We don't dispatch load events to the window. That quirk was originally + // added because Mozilla doesn't propagate load events to the window object. + Document* documentForWindowEvents = 0; + if (event->type() != eventNames().loadEvent) { + EventTargetNode* topLevelContainer = ancestors.isEmpty() ? this : ancestors.last().get(); + if (topLevelContainer->isDocumentNode()) + documentForWindowEvents = static_cast<Document*>(topLevelContainer); } - // dispatch to the actual target node - it.toLast(); - - if (!evt->propagationStopped()) { - evt->setEventPhase(Event::AT_TARGET); - - eventTargetNode = EventTargetNodeCast(it.current()); - evt->setCurrentTarget(eventTargetRespectingSVGTargetRules(eventTargetNode)); + // 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; - // We do want capturing event listeners to be invoked here, even though - // that violates the specification since Mozilla does it. - eventTargetNode->handleLocalEvents(evt.get(), true); + // Trigger capturing event handlers, starting at the top and working our way down. + event->setEventPhase(Event::CAPTURING_PHASE); - eventTargetNode->handleLocalEvents(evt.get(), false); + if (documentForWindowEvents) { + event->setCurrentTarget(documentForWindowEvents); + documentForWindowEvents->handleWindowEvent(event.get(), true); + if (event->propagationStopped()) + goto doneDispatching; + } + for (size_t i = ancestors.size(); i; --i) { + ContainerNode* ancestor = ancestors[i - 1].get(); + event->setCurrentTarget(eventTargetRespectingSVGTargetRules(ancestor)); + ancestor->handleLocalEvents(event.get(), true); + if (event->propagationStopped()) + goto doneDispatching; } - --it; - - // ok, now bubble up again (only non-capturing event handlers will be called) - // ### recalculate the node chain here? (e.g. if target node moved in document by previous event handlers) - // no. the DOM specs says: - // The chain of EventTargets from the event target to the top of the tree - // is determined before the initial dispatch of the event. - // If modifications occur to the tree during event processing, - // event flow will proceed based on the initial state of the tree. - // - // since the initial dispatch is before the capturing phase, - // there's no need to recalculate the node chain. - // (tobias) - - if (evt->bubbles()) { - evt->setEventPhase(Event::BUBBLING_PHASE); - - for (; it.current() && !evt->propagationStopped() && !evt->cancelBubble(); --it) { - eventTargetNode = EventTargetNodeCast(it.current()); - evt->setCurrentTarget(eventTargetRespectingSVGTargetRules(eventTargetNode)); - - eventTargetNode->handleLocalEvents(evt.get(), false); + event->setEventPhase(Event::AT_TARGET); + + // We do want capturing event listeners to be invoked here, even though + // that violates some versions of the DOM specification; Mozilla does it. + event->setCurrentTarget(eventTargetRespectingSVGTargetRules(this)); + handleLocalEvents(event.get(), true); + if (event->propagationStopped()) + goto doneDispatching; + handleLocalEvents(event.get(), false); + 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) { + ContainerNode* ancestor = ancestors[i].get(); + event->setCurrentTarget(eventTargetRespectingSVGTargetRules(ancestor)); + ancestor->handleLocalEvents(event.get(), false); + if (event->propagationStopped() || event->cancelBubble()) + goto doneDispatching; + } + if (documentForWindowEvents) { + event->setCurrentTarget(documentForWindowEvents); + documentForWindowEvents->handleWindowEvent(event.get(), false); + if (event->propagationStopped() || event->cancelBubble()) + goto doneDispatching; } - - it.toFirst(); - - // Handle window events for bubbling phase, except load events, this quirk is needed - // because Mozilla used to never propagate load events at all - if (evt->type() != eventNames().loadEvent && it.current()->isDocumentNode() && !evt->propagationStopped() && !evt->cancelBubble()) { - evt->setCurrentTarget(EventTargetNodeCast(it.current())); - static_cast<Document*>(it.current())->handleWindowEvent(evt.get(), false); - } } - evt->setCurrentTarget(0); - evt->setEventPhase(0); // I guess this is correct, the spec does not seem to say - // anything about the default event handler phase. - - - // Now call the post dispatch. - postDispatchEventHandler(evt.get(), data); - - // now we call all default event handlers (this is not part of DOM - it is internal to WebCore) - it.toLast(); - - if (evt->bubbles()) - for (; it.current() && !evt->defaultPrevented() && !evt->defaultHandled(); --it) - EventTargetNodeCast(it.current())->defaultEventHandler(evt.get()); - else if (!evt->defaultPrevented() && !evt->defaultHandled()) - EventTargetNodeCast(it.current())->defaultEventHandler(evt.get()); - - // deref all nodes in chain - it.toFirst(); - for (; it.current(); ++it) - it.current()->deref(); // this may delete us +doneDispatching: + 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) { + ContainerNode* ancestor = ancestors[i].get(); + ancestor->defaultEventHandler(event.get()); + ASSERT(!event->defaultPrevented()); + if (event->defaultHandled()) + goto doneWithDefault; + } + } + } +doneWithDefault: Document::updateDocumentsRendering(); - return !evt->defaultPrevented(); // ### what if defaultPrevented was called before dispatchEvent? + return !event->defaultPrevented(); } bool EventTargetNode::dispatchSubtreeModifiedEvent() @@ -442,8 +454,7 @@ void EventTargetNode::dispatchWindowEvent(const AtomicString& eventType, bool ca if (ownerElement) { RefPtr<Event> ownerEvent = Event::create(eventType, false, cancelableArg); ownerEvent->setTarget(ownerElement); - ExceptionCode ec = 0; - ownerElement->dispatchGenericEvent(ownerEvent.release(), ec); + ownerElement->dispatchGenericEvent(ownerEvent.release()); } } } @@ -692,19 +703,24 @@ void EventTargetNode::dispatchStorageEvent(const AtomicString &eventType, const void EventTargetNode::removeInlineEventListenerForType(const AtomicString& eventType) { - if (!m_regdListeners) // nothing to remove + if (!hasRareData()) return; - - RegisteredEventListenerList::Iterator end = m_regdListeners->end(); - for (RegisteredEventListenerList::Iterator it = m_regdListeners->begin(); it != end; ++it) { - EventListener* listener = (*it)->listener(); - if ((*it)->eventType() != eventType || !listener->isInline()) + + RegisteredEventListenerVector* listeners = rareData()->listeners(); + if (!listeners) + return; + + size_t size = listeners->size(); + for (size_t i = 0; i < size; ++i) { + RegisteredEventListener& r = *listeners->at(i); + if (r.eventType() != eventType || !r.listener()->isInline()) continue; - it = m_regdListeners->remove(it); + r.setRemoved(true); + listeners->remove(i); // removed last - if (m_regdListeners->isEmpty() && !inDocument()) + if (listeners->isEmpty() && !inDocument()) document()->unregisterDisconnectedNodeWithEventListeners(this); updateSVGElementInstancesAfterEventListenerChange(this); @@ -727,26 +743,26 @@ void EventTargetNode::setInlineEventListenerForTypeAndAttribute(const AtomicStri EventListener* EventTargetNode::inlineEventListenerForType(const AtomicString& eventType) const { - if (!m_regdListeners) - return 0; - - RegisteredEventListenerList::Iterator end = m_regdListeners->end(); - for (RegisteredEventListenerList::Iterator it = m_regdListeners->begin(); it != end; ++it) - if ((*it)->eventType() == eventType && (*it)->listener()->isInline()) - return (*it)->listener(); + const RegisteredEventListenerVector& listeners = eventListeners(); + size_t size = listeners.size(); + for (size_t i = 0; i < size; ++i) { + const RegisteredEventListener& r = *listeners[i]; + if (r.eventType() == eventType && r.listener()->isInline()) + return r.listener(); + } return 0; } #ifdef ANDROID EventListener *EventTargetNode::getEventListener(const AtomicString &eventType) { - if (!m_regdListeners) - return 0; - - RegisteredEventListenerList::Iterator end = m_regdListeners->end(); - for (RegisteredEventListenerList::Iterator it = m_regdListeners->begin(); it != end; ++it) - if ((*it)->eventType() == eventType) - return (*it)->listener(); + const RegisteredEventListenerVector& listeners = eventListeners(); + size_t size = listeners.size(); + for (size_t i = 0; i < size; ++i) { + const RegisteredEventListener& r = *listeners[i]; + if (r.eventType() == eventType) + return r.listener(); + } return 0; } #endif diff --git a/WebCore/dom/EventTargetNode.h b/WebCore/dom/EventTargetNode.h index cb9a293..1814b7b 100644 --- a/WebCore/dom/EventTargetNode.h +++ b/WebCore/dom/EventTargetNode.h @@ -2,7 +2,7 @@ * 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) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> * * This library is free software; you can redistribute it and/or @@ -32,10 +32,13 @@ namespace WebCore { class Attribute; class Frame; +class RegisteredEventListener; + +typedef Vector<RefPtr<RegisteredEventListener> > RegisteredEventListenerVector; class EventTargetNode : public Node, public EventTarget { public: - EventTargetNode(Document*, bool isElement = false, bool isContainer = false); + EventTargetNode(Document*, bool isElement = false, bool isContainer = false, bool isText = false); virtual ~EventTargetNode(); virtual bool isEventTargetNode() const { return true; } @@ -75,7 +78,7 @@ public: void dispatchStorageEvent(const AtomicString &eventType, const String& key, const String& oldValue, const String& newValue, Frame* source); bool dispatchWebKitAnimationEvent(const AtomicString& eventType, const String& animationName, double elapsedTime); bool dispatchWebKitTransitionEvent(const AtomicString& eventType, const String& propertyName, double elapsedTime); - bool dispatchGenericEvent(PassRefPtr<Event>, ExceptionCode&); + bool dispatchGenericEvent(PassRefPtr<Event>); virtual void handleLocalEvents(Event*, bool useCapture); @@ -98,7 +101,7 @@ public: */ virtual bool disabled() const; - RegisteredEventListenerList* localEventListeners() const { return m_regdListeners; } + const RegisteredEventListenerVector& eventListeners() const; EventListener* onabort() const; void setOnabort(PassRefPtr<EventListener>); @@ -194,10 +197,6 @@ public: using Node::ref; using Node::deref; -protected: - friend class EventTarget; - RegisteredEventListenerList* m_regdListeners; - private: virtual void refEventTarget() { ref(); } virtual void derefEventTarget() { deref(); } diff --git a/WebCore/dom/FormControlElement.cpp b/WebCore/dom/FormControlElement.cpp new file mode 100644 index 0000000..2d883c6 --- /dev/null +++ b/WebCore/dom/FormControlElement.cpp @@ -0,0 +1,50 @@ +/* + * 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 "FormControlElement.h" + +#include "Element.h" +#include "HTMLFormControlElement.h" +#include <wtf/Assertions.h> + +#if ENABLE(WML) +#include "WMLFormControlElement.h" +#endif + +namespace WebCore { + +FormControlElement* toFormControlElement(Element* element) +{ + if (!element->isFormControlElement()) + return 0; + + if (element->isHTMLElement()) + return static_cast<HTMLFormControlElement*>(element); + +#if ENABLE(WML) + if (element->isWMLElement()) + return static_cast<WMLFormControlElement*>(element); +#endif + + return 0; +} + +} diff --git a/WebCore/dom/FormControlElement.h b/WebCore/dom/FormControlElement.h new file mode 100644 index 0000000..881c9fd --- /dev/null +++ b/WebCore/dom/FormControlElement.h @@ -0,0 +1,47 @@ +/* + * 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 FormControlElement_h +#define FormControlElement_h + +namespace WebCore { + +class AtomicString; +class Element; + +class FormControlElement { +public: + virtual ~FormControlElement() { } + + virtual bool valueMatchesRenderer() const = 0; + virtual void setValueMatchesRenderer(bool value = true) = 0; + + virtual const AtomicString& name() const = 0; + virtual const AtomicString& type() const = 0; + +protected: + FormControlElement() { } +}; + +FormControlElement* toFormControlElement(Element*); + +} + +#endif diff --git a/WebCore/dom/FormControlElementWithState.cpp b/WebCore/dom/FormControlElementWithState.cpp new file mode 100644 index 0000000..34a719d --- /dev/null +++ b/WebCore/dom/FormControlElementWithState.cpp @@ -0,0 +1,74 @@ +/* + * 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 "FormControlElementWithState.h" + +#include "Document.h" +#include "Element.h" +#include "HTMLFormControlElement.h" +#include <wtf/Assertions.h> + +#if ENABLE(WML) +#include "WMLFormControlElement.h" +#endif + +namespace WebCore { + +void FormControlElementWithState::registerFormControlElementWithState(FormControlElementWithState* element, Document* document) +{ + document->registerFormElementWithState(element); +} + +void FormControlElementWithState::unregisterFormControlElementWithState(FormControlElementWithState* element, Document* document) +{ + document->unregisterFormElementWithState(element); +} + +void FormControlElementWithState::finishParsingChildren(FormControlElementWithState* element, Document* document) +{ + if (!document->hasStateForNewFormElements()) + return; + + FormControlElement* formControlElement = element->toFormControlElement(); + ASSERT(formControlElement); + + String state; + if (document->takeStateForFormElement(formControlElement->name().impl(), formControlElement->type().impl(), state)) + element->restoreState(state); +} + +FormControlElementWithState* toFormControlElementWithState(Element* element) +{ + if (!element->isFormControlElementWithState()) + return 0; + + if (element->isHTMLElement()) + return static_cast<HTMLFormControlElementWithState*>(element); + +#if ENABLE(WML) + if (element->isWMLElement()) + return static_cast<WMLFormControlElementWithState*>(element); +#endif + + return 0; +} + +} diff --git a/WebCore/dom/FormControlElementWithState.h b/WebCore/dom/FormControlElementWithState.h new file mode 100644 index 0000000..4c2b15a --- /dev/null +++ b/WebCore/dom/FormControlElementWithState.h @@ -0,0 +1,53 @@ +/* + * 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 FormControlElementWithState_h +#define FormControlElementWithState_h + +namespace WebCore { + +class Document; +class Element; +class FormControlElement; +class String; + +class FormControlElementWithState { +public: + virtual ~FormControlElementWithState() { } + + virtual bool saveState(String& value) const = 0; + virtual void restoreState(const String& value) = 0; + + // Every FormControlElementWithState class, is also a FormControlElement class by definition. + virtual FormControlElement* toFormControlElement() = 0; + +protected: + FormControlElementWithState() { } + + static void registerFormControlElementWithState(FormControlElementWithState*, Document*); + static void unregisterFormControlElementWithState(FormControlElementWithState*, Document*); + static void finishParsingChildren(FormControlElementWithState*, Document*); +}; + +FormControlElementWithState* toFormControlElementWithState(Element*); + +} + +#endif diff --git a/WebCore/dom/GenericWorkerTask.h b/WebCore/dom/GenericWorkerTask.h new file mode 100644 index 0000000..79fd148 --- /dev/null +++ b/WebCore/dom/GenericWorkerTask.h @@ -0,0 +1,109 @@ +/* + * 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 GenericWorkerTask_h +#define GenericWorkerTask_h + +#if ENABLE(WORKERS) + +#include "WorkerMessagingProxy.h" +#include "ScriptExecutionContext.h" +#include <wtf/PassRefPtr.h> + +namespace WebCore { + class GenericWorkerTaskBase : public ScriptExecutionContext::Task { + protected: + GenericWorkerTaskBase(WorkerMessagingProxy* messagingProxy) : m_messagingProxy(messagingProxy) + { + } + + bool canPerformTask() + { + return !m_messagingProxy->askedToTerminate(); + } + + WorkerMessagingProxy* m_messagingProxy; + }; + + 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 GenericWorkerTask6 : public GenericWorkerTaskBase { + public: + typedef void (*Method)(ScriptExecutionContext*, MP1, MP2, MP3, MP4, MP5, MP6); + typedef GenericWorkerTask6<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5, P6, MP6> GenericWorkerTask; + + static PassRefPtr<GenericWorkerTask> create(WorkerMessagingProxy* messagingProxy, Method method, const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5, const P6& parameter6) + { + return adoptRef(new GenericWorkerTask(messagingProxy, method, parameter1, parameter2, parameter3, parameter4, parameter5, parameter6)); + } + + private: + GenericWorkerTask6(WorkerMessagingProxy* messagingProxy, Method method, const P1& parameter1, const P2& parameter2, const P3& parameter3, const P4& parameter4, const P5& parameter5, const P6& parameter6) + : GenericWorkerTaskBase(messagingProxy) + , 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) + { + if (!canPerformTask()) + return; + (*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> + PassRefPtr<GenericWorkerTask6<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5, P6, MP6> > createCallbackTask( + WorkerMessagingProxy* messagingProxy, + 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 GenericWorkerTask6<P1, MP1, P2, MP2, P3, MP3, P4, MP4, P5, MP5, P6, MP6>::create(messagingProxy, method, parameter1, parameter2, parameter3, parameter4, parameter5, parameter6); + } + +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // GenericWorkerTask_h diff --git a/WebCore/dom/InputElement.cpp b/WebCore/dom/InputElement.cpp new file mode 100644 index 0000000..47c1a28 --- /dev/null +++ b/WebCore/dom/InputElement.cpp @@ -0,0 +1,307 @@ +/* + * 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" +#include "ChromeClient.h" +#include "Document.h" +#include "Event.h" +#include "EventNames.h" +#include "FormControlElement.h" +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "Page.h" +#include "RenderTextControlSingleLine.h" +#include "SelectionController.h" +#include "TextIterator.h" +#include "TextBreakIterator.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 http://bugs.webkit.org/show_bugs.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(InputElementData& data, Document* document) +{ + if (!data.inputElement()->isTextField()) + return; + + updatePlaceholderVisibility(data, document); + + if (data.inputElement()->isPasswordField() && document->frame()) + document->setUseSecureKeyboardEntryWhenActive(true); +} + +void InputElement::dispatchBlurEvent(InputElementData& data, Document* document) +{ + if (!data.inputElement()->isTextField()) + return; + + Frame* frame = document->frame(); + if (!frame) + return; + + updatePlaceholderVisibility(data, document); + + if (data.inputElement()->isPasswordField()) + document->setUseSecureKeyboardEntryWhenActive(false); + + frame->textFieldDidEndEditing(data.element()); +} + +void InputElement::updatePlaceholderVisibility(InputElementData& data, Document* document, bool placeholderValueChanged) +{ + ASSERT(data.inputElement()->isTextField()); + + bool oldPlaceholderShouldBeVisible = data.placeholderShouldBeVisible(); + Element* element = data.element(); + + data.setPlaceholderShouldBeVisible(data.inputElement()->value().isEmpty() + && document->focusedNode() != element + && !data.inputElement()->placeholderValue().isEmpty()); + + if ((oldPlaceholderShouldBeVisible != data.placeholderShouldBeVisible() || placeholderValueChanged) && element->renderer()) + static_cast<RenderTextControlSingleLine*>(element->renderer())->updatePlaceholderVisibility(); +} + +void InputElement::updateFocusAppearance(InputElementData& data, Document* document, bool restorePreviousSelection) +{ + ASSERT(data.inputElement()->isTextField()); + + if (!restorePreviousSelection || data.cachedSelectionStart() == -1) + data.inputElement()->select(); + else + // Restore the cached selection. + updateSelectionRange(data, data.cachedSelectionStart(), data.cachedSelectionEnd()); + + if (document && document->frame()) + document->frame()->revealSelection(); +} + +void InputElement::updateSelectionRange(InputElementData& data, int start, int end) +{ + if (!data.inputElement()->isTextField()) + return; + + if (RenderTextControl* renderer = static_cast<RenderTextControl*>(data.element()->renderer())) + renderer->setSelectionRange(start, end); +} + +void InputElement::aboutToUnload(InputElementData& data, Document* document) +{ + if (!data.inputElement()->isTextField() || !data.element()->focused() || !document->frame()) + return; + + document->frame()->textFieldDidEndEditing(data.element()); +} + +void InputElement::setValueFromRenderer(InputElementData& data, Document* document, const String& value) +{ + // Renderer and our event handler are responsible for constraining values. + ASSERT(value == data.inputElement()->constrainValue(value) || data.inputElement()->constrainValue(value).isEmpty()); + + if (data.inputElement()->isTextField()) + updatePlaceholderVisibility(data, document); + + // 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* element = data.element(); + FormControlElement* formControlElement = toFormControlElement(element); + ASSERT(formControlElement); + formControlElement->setValueMatchesRenderer(); + + // Fire the "input" DOM event + element->dispatchEventForType(eventNames().inputEvent, true, false); + notifyFormStateChanged(data, document); +} + +static int numCharactersInGraphemeClusters(StringImpl* s, int numGraphemeClusters) +{ + if (!s) + return 0; + + TextBreakIterator* it = characterBreakIterator(s->characters(), s->length()); + if (!it) + return 0; + + for (int i = 0; i < numGraphemeClusters; ++i) { + if (textBreakNext(it) == TextBreakDone) + return s->length(); + } + + return textBreakCurrent(it); +} + +String InputElement::constrainValue(const InputElementData& data, const String& proposedValue, int maxLength) +{ + String string = proposedValue; + if (!data.inputElement()->isTextField()) + return string; + + string.replace("\r\n", " "); + string.replace('\r', ' '); + string.replace('\n', ' '); + + StringImpl* s = string.impl(); + int newLength = numCharactersInGraphemeClusters(s, maxLength); + for (int i = 0; i < newLength; ++i) { + const UChar& current = (*s)[i]; + if (current < ' ' && current != '\t') { + newLength = i; + break; + } + } + + if (newLength < static_cast<int>(string.length())) + return string.left(newLength); + + return string; +} + +static int numGraphemeClusters(StringImpl* s) +{ + if (!s) + return 0; + + TextBreakIterator* it = characterBreakIterator(s->characters(), s->length()); + if (!it) + return 0; + + int num = 0; + while (textBreakNext(it) != TextBreakDone) + ++num; + + return num; +} + +void InputElement::handleBeforeTextInsertedEvent(InputElementData& data, Document* document, Event* event) +{ + ASSERT(event->isBeforeTextInsertedEvent()); + + // Make sure that the text to be inserted will not violate the maxLength. + int oldLength = numGraphemeClusters(data.inputElement()->value().impl()); + ASSERT(oldLength <= data.maxLength()); + int selectionLength = numGraphemeClusters(plainText(document->frame()->selection()->selection().toRange().get()).impl()); + ASSERT(oldLength >= selectionLength); + int maxNewLength = data.maxLength() - (oldLength - selectionLength); + + // Truncate the inserted text to avoid violating the maxLength and other constraints. + BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(event); + textEvent->setText(constrainValue(data, textEvent->text(), maxNewLength)); +} + +void InputElement::parseSizeAttribute(InputElementData& data, MappedAttribute* attribute) +{ + data.setSize(attribute->isNull() ? InputElement::s_defaultSize : attribute->value().toInt()); + + if (RenderObject* renderer = data.element()->renderer()) + renderer->setNeedsLayoutAndPrefWidthsRecalc(); +} + +void InputElement::parseMaxLengthAttribute(InputElementData& data, MappedAttribute* 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); + + data.element()->setChanged(); +} + +void InputElement::updateValueIfNeeded(InputElementData& data) +{ + String oldValue = data.value(); + String newValue = data.inputElement()->constrainValue(oldValue); + if (newValue != oldValue) + data.inputElement()->setValue(newValue); +} + +void InputElement::notifyFormStateChanged(InputElementData& data, Document* document) +{ + Frame* frame = document->frame(); + if (!frame) + return; + + if (Page* page = frame->page()) + page->chrome()->client()->formStateDidChange(data.element()); +} + +// InputElementData +InputElementData::InputElementData(InputElement* inputElement, Element* element) + : m_inputElement(inputElement) + , m_element(element) + , m_placeholderShouldBeVisible(false) + , m_size(InputElement::s_defaultSize) + , m_maxLength(InputElement::s_maximumLength) + , m_cachedSelectionStart(-1) + , m_cachedSelectionEnd(-1) +{ + ASSERT(m_inputElement); + ASSERT(m_element); +} + +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; +} + +} diff --git a/WebCore/dom/InputElement.h b/WebCore/dom/InputElement.h new file mode 100644 index 0000000..c64f131 --- /dev/null +++ b/WebCore/dom/InputElement.h @@ -0,0 +1,125 @@ +/* + * 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 "AtomicString.h" +#include "PlatformString.h" + +namespace WebCore { + +class Document; +class Element; +class Event; +class InputElementData; +class MappedAttribute; + +class InputElement { +public: + virtual ~InputElement() { } + + virtual bool isPasswordField() const = 0; + virtual bool isSearchField() const = 0; + virtual bool isTextField() const = 0; + + virtual bool placeholderShouldBeVisible() const = 0; + virtual bool searchEventsShouldBeDispatched() const = 0; + + virtual int size() const = 0; + virtual String value() const = 0; + virtual void setValue(const String&) = 0; + virtual String placeholderValue() const = 0; + + virtual String constrainValue(const String&) const = 0; + virtual void setValueFromRenderer(const String&) = 0; + + virtual void cacheSelection(int start, int end) = 0; + virtual void select() = 0; + + static const int s_maximumLength; + static const int s_defaultSize; + +protected: + InputElement() { } + + static void dispatchFocusEvent(InputElementData&, Document*); + static void dispatchBlurEvent(InputElementData&, Document*); + static void updatePlaceholderVisibility(InputElementData&, Document*, bool placeholderValueChanged = false); + static void updateFocusAppearance(InputElementData&, Document*, bool restorePreviousSelection); + static void updateSelectionRange(InputElementData&, int start, int end); + static void aboutToUnload(InputElementData&, Document*); + static void setValueFromRenderer(InputElementData&, Document*, const String&); + static String constrainValue(const InputElementData&, const String& proposedValue, int maxLength); + static void handleBeforeTextInsertedEvent(InputElementData&, Document*, Event*); + static void parseSizeAttribute(InputElementData&, MappedAttribute*); + static void parseMaxLengthAttribute(InputElementData&, MappedAttribute*); + static void updateValueIfNeeded(InputElementData&); + static void notifyFormStateChanged(InputElementData&, Document*); +}; + +// HTML/WMLInputElement hold this struct as member variable +// and pass it to the static helper functions in InputElement +class InputElementData { +public: + InputElementData(InputElement*, Element*); + ~InputElementData(); + + InputElement* inputElement() const { return m_inputElement; } + Element* element() const { return m_element; } + + bool placeholderShouldBeVisible() const { return m_placeholderShouldBeVisible; } + void setPlaceholderShouldBeVisible(bool visible) { m_placeholderShouldBeVisible = visible; } + + 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; } + + 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; } + +private: + InputElement* m_inputElement; + Element* m_element; + bool m_placeholderShouldBeVisible; + AtomicString m_name; + String m_value; + int m_size; + int m_maxLength; + int m_cachedSelectionStart; + int m_cachedSelectionEnd; +}; + +InputElement* toInputElement(Element*); + +} + +#endif diff --git a/WebCore/dom/MessageEvent.idl b/WebCore/dom/MessageEvent.idl index 5390c43..8e8f271 100644 --- a/WebCore/dom/MessageEvent.idl +++ b/WebCore/dom/MessageEvent.idl @@ -27,7 +27,8 @@ module events { interface [ - GenerateConstructor + GenerateConstructor, + NoStaticTables ] MessageEvent : Event { readonly attribute DOMString data; diff --git a/WebCore/dom/MessagePort.cpp b/WebCore/dom/MessagePort.cpp index 58467af..0607bea 100644 --- a/WebCore/dom/MessagePort.cpp +++ b/WebCore/dom/MessagePort.cpp @@ -38,17 +38,23 @@ namespace WebCore { -class CloseMessagePortTimer : public TimerBase { +class MessagePortCloseEventTask : public ScriptExecutionContext::Task { public: - CloseMessagePortTimer(PassRefPtr<MessagePort> port) + static PassRefPtr<MessagePortCloseEventTask> create(PassRefPtr<MessagePort> port) + { + return adoptRef(new MessagePortCloseEventTask(port)); + } + +private: + MessagePortCloseEventTask(PassRefPtr<MessagePort> port) : m_port(port) { ASSERT(m_port); } -private: - virtual void fired() + virtual void performTask(ScriptExecutionContext* unusedContext) { + ASSERT_UNUSED(unusedContext, unusedContext == m_port->scriptExecutionContext()); ASSERT(!m_port->active()); // Closing may destroy the port, dispatch any remaining messages now. @@ -56,20 +62,34 @@ private: m_port->dispatchMessages(); m_port->dispatchCloseEvent(); - delete this; } RefPtr<MessagePort> m_port; }; +PassRefPtr<MessagePort::EventData> MessagePort::EventData::create(const String& message, PassRefPtr<MessagePort> port) +{ + return adoptRef(new EventData(message, port)); +} + +MessagePort::EventData::EventData(const String& message, PassRefPtr<MessagePort> messagePort) + : message(message.copy()) + , messagePort(messagePort) +{ +} + +MessagePort::EventData::~EventData() +{ +} + MessagePort::MessagePort(ScriptExecutionContext* scriptExecutionContext) : m_entangledPort(0) , m_queueIsOpen(false) , m_scriptExecutionContext(scriptExecutionContext) , m_pendingCloseEvent(false) - , m_jsWrapperIsInaccessible(false) { - scriptExecutionContext->createdMessagePort(this); + if (scriptExecutionContext) + scriptExecutionContext->createdMessagePort(this); } MessagePort::~MessagePort() @@ -81,7 +101,7 @@ MessagePort::~MessagePort() m_scriptExecutionContext->destroyedMessagePort(this); } -PassRefPtr<MessagePort> MessagePort::clone(ScriptExecutionContext* newOwner, ExceptionCode& ec) +PassRefPtr<MessagePort> MessagePort::clone(ExceptionCode& ec) { if (!m_entangledPort) { ec = INVALID_STATE_ERR; @@ -89,13 +109,13 @@ PassRefPtr<MessagePort> MessagePort::clone(ScriptExecutionContext* newOwner, Exc } RefPtr<MessagePort> remotePort = m_entangledPort; - RefPtr<MessagePort> newPort = MessagePort::create(newOwner); + RefPtr<MessagePort> newPort = MessagePort::create(0); // Move all the events in the port message queue of original port to the port message queue of new port, if any, leaving the new port's port message queue in its initial closed state. // If events are posted (e.g. from a worker thread) while this code is executing, there is no guarantee whether they end up in the original or new port's message queue. - RefPtr<Event> event; - while (m_messageQueue.tryGetMessage(event)) - newPort->m_messageQueue.append(event); + RefPtr<EventData> eventData; + while (m_messageQueue.tryGetMessage(eventData)) + newPort->m_messageQueue.append(eventData); entangle(remotePort.get(), newPort.get()); // The port object will be unentangled. return newPort; @@ -108,7 +128,7 @@ void MessagePort::postMessage(const String& message, ExceptionCode& ec) void MessagePort::postMessage(const String& message, MessagePort* dataPort, ExceptionCode& ec) { - if (!m_entangledPort || !m_scriptExecutionContext || !m_entangledPort->m_scriptExecutionContext) + if (!m_entangledPort || !m_scriptExecutionContext) return; RefPtr<MessagePort> newMessagePort; @@ -117,31 +137,27 @@ void MessagePort::postMessage(const String& message, MessagePort* dataPort, Exce ec = INVALID_ACCESS_ERR; return; } - newMessagePort = dataPort->clone(m_entangledPort->m_scriptExecutionContext, ec); + newMessagePort = dataPort->clone(ec); if (ec) return; } - DOMWindow* window = (m_scriptExecutionContext->isDocument() && m_entangledPort->m_scriptExecutionContext->isDocument()) ? - static_cast<Document*>(m_scriptExecutionContext)->domWindow() : 0; - m_entangledPort->m_messageQueue.append(MessageEvent::create(message, "", "", window, newMessagePort.get())); - if (m_entangledPort->m_queueIsOpen) + m_entangledPort->m_messageQueue.append(EventData::create(message, newMessagePort)); + if (m_entangledPort->m_queueIsOpen && m_entangledPort->m_scriptExecutionContext) m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon(); } PassRefPtr<MessagePort> MessagePort::startConversation(ScriptExecutionContext* scriptExecutionContext, const String& message) { RefPtr<MessagePort> port1 = MessagePort::create(scriptExecutionContext); - if (!m_entangledPort || !m_scriptExecutionContext || !m_entangledPort->m_scriptExecutionContext) + if (!m_entangledPort || !m_scriptExecutionContext) return port1; - RefPtr<MessagePort> port2 = MessagePort::create(m_entangledPort->m_scriptExecutionContext); + RefPtr<MessagePort> port2 = MessagePort::create(0); entangle(port1.get(), port2.get()); - DOMWindow* window = (m_scriptExecutionContext->isDocument() && m_entangledPort->m_scriptExecutionContext->isDocument()) ? - static_cast<Document*>(m_scriptExecutionContext)->domWindow() : 0; - m_entangledPort->m_messageQueue.append(MessageEvent::create(message, "", "", window, port2.get())); - if (m_entangledPort->m_queueIsOpen) + m_entangledPort->m_messageQueue.append(EventData::create(message, port2)); + if (m_entangledPort->m_queueIsOpen && m_entangledPort->m_scriptExecutionContext) m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon(); return port1; } @@ -193,24 +209,44 @@ void MessagePort::unentangle() void MessagePort::contextDestroyed() { - if (m_entangledPort) { - RefPtr<MessagePort> survivingPort = m_entangledPort; + ASSERT(m_scriptExecutionContext); + + if (m_entangledPort) unentangle(); - if (survivingPort->m_scriptExecutionContext != m_scriptExecutionContext) // Otherwise, survivingPort won't really survive. - survivingPort->queueCloseEvent(); - } + m_scriptExecutionContext = 0; } +void MessagePort::attachToContext(ScriptExecutionContext* scriptExecutionContext) +{ + ASSERT(!m_scriptExecutionContext); + ASSERT(!m_queueIsOpen); + + m_scriptExecutionContext = scriptExecutionContext; + m_scriptExecutionContext->createdMessagePort(this); + + // FIXME: Need to call processMessagePortMessagesSoon()? +} + +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. + // FIXME: Such messages should be dispatched if the document returns from page cache. They are only allowed to be lost if the document is discarded. ASSERT(queueIsOpen()); - RefPtr<Event> evt; - while (m_messageQueue.tryGetMessage(evt)) { + RefPtr<EventData> eventData; + while (m_messageQueue.tryGetMessage(eventData)) { + + ASSERT(!eventData->messagePort || !eventData->messagePort->m_scriptExecutionContext); + if (eventData->messagePort) + eventData->messagePort->attachToContext(m_scriptExecutionContext); - ASSERT(evt->type() == eventNames().messageEvent); + RefPtr<Event> evt = MessageEvent::create(eventData->message, "", "", 0, eventData->messagePort); if (m_onMessageListener) { evt->setTarget(this); @@ -229,8 +265,7 @@ void MessagePort::queueCloseEvent() ASSERT(!m_pendingCloseEvent); m_pendingCloseEvent = true; - CloseMessagePortTimer* timer = new CloseMessagePortTimer(this); - timer->startOneShot(0); + m_scriptExecutionContext->postTask(MessagePortCloseEventTask::create(this)); } void MessagePort::dispatchCloseEvent() @@ -269,7 +304,7 @@ void MessagePort::addEventListener(const AtomicString& eventType, PassRefPtr<Eve } } -void MessagePort::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool useCapture) +void MessagePort::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) { EventListenersMap::iterator iter = m_eventListeners.find(eventType); if (iter == m_eventListeners.end()) @@ -286,7 +321,7 @@ void MessagePort::removeEventListener(const AtomicString& eventType, EventListen bool MessagePort::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) { - if (event->type().isEmpty()) { + if (!event || event->type().isEmpty()) { ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; return true; } @@ -303,8 +338,6 @@ bool MessagePort::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) bool MessagePort::hasPendingActivity() { - // We only care about the result of this function when there is no entangled port, or it is inaccessible, so no more messages can be added to the queue. - // Thus, using MessageQueue::isEmpty() does not cause a race condition here. return m_pendingCloseEvent || (m_queueIsOpen && !m_messageQueue.isEmpty()); } diff --git a/WebCore/dom/MessagePort.h b/WebCore/dom/MessagePort.h index dc11d23..19252a7 100644 --- a/WebCore/dom/MessagePort.h +++ b/WebCore/dom/MessagePort.h @@ -35,7 +35,6 @@ #include <wtf/MessageQueue.h> #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> -#include <wtf/Threading.h> #include <wtf/Vector.h> namespace WebCore { @@ -47,12 +46,12 @@ namespace WebCore { class String; class WorkerContext; - class MessagePort : public ThreadSafeShared<MessagePort>, public EventTarget { + class MessagePort : public RefCounted<MessagePort>, public EventTarget { public: static PassRefPtr<MessagePort> create(ScriptExecutionContext* scriptExecutionContext) { return adoptRef(new MessagePort(scriptExecutionContext)); } ~MessagePort(); - PassRefPtr<MessagePort> clone(ScriptExecutionContext*, ExceptionCode&); + PassRefPtr<MessagePort> clone(ExceptionCode&); // Returns a port that isn't attached to any context. bool active() const { return m_entangledPort; } void postMessage(const String& message, ExceptionCode&); @@ -68,7 +67,8 @@ namespace WebCore { void unentangle(); void contextDestroyed(); - virtual ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; } + void attachToContext(ScriptExecutionContext*); + virtual ScriptExecutionContext* scriptExecutionContext() const; virtual MessagePort* toMessagePort() { return this; } @@ -83,22 +83,20 @@ namespace WebCore { typedef HashMap<AtomicString, ListenerVector> EventListenersMap; EventListenersMap& eventListeners() { return m_eventListeners; } - using ThreadSafeShared<MessagePort>::ref; - using ThreadSafeShared<MessagePort>::deref; + using RefCounted<MessagePort>::ref; + using RefCounted<MessagePort>::deref; bool hasPendingActivity(); + // FIXME: Per current spec, setting onmessage should automagically start the port (unlike addEventListener("message", ...)). void setOnmessage(PassRefPtr<EventListener> eventListener) { m_onMessageListener = eventListener; } EventListener* onmessage() const { return m_onMessageListener.get(); } void setOnclose(PassRefPtr<EventListener> eventListener) { m_onCloseListener = eventListener; } EventListener* onclose() const { return m_onCloseListener.get(); } - void setJSWrapperIsInaccessible() { m_jsWrapperIsInaccessible = true; } - bool jsWrapperIsInaccessible() const { return m_jsWrapperIsInaccessible; } - private: - friend class CloseMessagePortTimer; + friend class MessagePortCloseEventTask; MessagePort(ScriptExecutionContext*); @@ -108,7 +106,19 @@ namespace WebCore { void dispatchCloseEvent(); MessagePort* m_entangledPort; - MessageQueue<RefPtr<Event> > m_messageQueue; + + // FIXME: EventData is necessary to pass messages to other threads. In single threaded case, we can just queue a created event. + struct EventData : public RefCounted<EventData> { + static PassRefPtr<EventData> create(const String& message, PassRefPtr<MessagePort>); + ~EventData(); + + String message; + RefPtr<MessagePort> messagePort; + + private: + EventData(const String& message, PassRefPtr<MessagePort>); + }; + MessageQueue<RefPtr<EventData> > m_messageQueue; // FIXME: No need to use MessageQueue in single threaded case. bool m_queueIsOpen; ScriptExecutionContext* m_scriptExecutionContext; @@ -118,8 +128,7 @@ namespace WebCore { EventListenersMap m_eventListeners; - bool m_pendingCloseEvent; - bool m_jsWrapperIsInaccessible; + bool m_pendingCloseEvent; // The port is GC protected while waiting for a close event to be dispatched. }; } // namespace WebCore diff --git a/WebCore/dom/MouseRelatedEvent.cpp b/WebCore/dom/MouseRelatedEvent.cpp index 5a6c1f7..a69c8a7 100644 --- a/WebCore/dom/MouseRelatedEvent.cpp +++ b/WebCore/dom/MouseRelatedEvent.cpp @@ -133,11 +133,9 @@ void MouseRelatedEvent::receivedTarget() // Adjust offsetX/Y to be relative to the target's position. if (!isSimulated()) { if (RenderObject* r = targ->renderer()) { - int rx, ry; - if (r->absolutePosition(rx, ry)) { - m_offsetX -= rx; - m_offsetY -= ry; - } + FloatPoint absPos = r->absoluteToLocal(FloatPoint(m_pageX, m_pageY), false, true); + m_offsetX = absPos.x(); + m_offsetY = absPos.y(); } } diff --git a/WebCore/dom/Node.cpp b/WebCore/dom/Node.cpp index 214a19e..eb591a7 100644 --- a/WebCore/dom/Node.cpp +++ b/WebCore/dom/Node.cpp @@ -33,6 +33,7 @@ #include "CSSRule.h" #include "CSSRuleList.h" #include "CSSSelector.h" +#include "CSSSelectorList.h" #include "CSSStyleRule.h" #include "CSSStyleSelector.h" #include "CSSStyleSheet.h" @@ -60,21 +61,172 @@ #include "Text.h" #include "XMLNames.h" #include "htmlediting.h" -#include <runtime/ExecState.h> -#include <runtime/JSLock.h> #include <wtf/RefCountedLeakCounter.h> +#define DUMP_NODE_STATISTICS 0 + +using namespace std; + namespace WebCore { using namespace HTMLNames; -// -------- - 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; + size_t mappedAttrMaps = 0; + + for (HashSet<Node*>::const_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 (NamedAttrMap* attrMap = element->attributes(true)) { + attributes += attrMap->length(); + ++attrMaps; + if (attrMap->isMappedAttributeMap()) + ++mappedAttrMaps; + 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>::const_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 MappedAttributes: %zu [%zu]\n", mappedAttributes, sizeof(MappedAttribute)); + printf(" Number of MappedAttributes with a StyleDeclaration: %zu\n", mappedAttributesWithStyleDecl); + printf(" Number of Attributes with an Attr: %zu\n", attributesWithAttr); + printf(" Number of NamedAttrMaps: %zu\n", attrMaps); + printf(" Number of NamedMappedAttrMap: %zu\n", mappedAttrMaps); +#endif +} + #ifndef NDEBUG static WTF::RefCountedLeakCounter nodeCounter("WebCoreNode"); @@ -140,7 +292,7 @@ Node::StyleChange Node::diff( RenderStyle *s1, RenderStyle *s2 ) return ch; } -Node::Node(Document* doc, bool isElement, bool isContainer) +Node::Node(Document* doc, bool isElement, bool isContainer, bool isText) : m_document(doc) , m_previous(0) , m_next(0) @@ -160,6 +312,16 @@ Node::Node(Document* doc, bool isElement, bool isContainer) , m_hasRareData(false) , m_isElement(isElement) , m_isContainer(isContainer) + , m_isText(isText) + , m_parsingChildrenFinished(true) +#if ENABLE(SVG) + , m_areSVGAttributesValid(true) +#endif + , m_isStyleAttributeValid(true) + , m_synchronizingStyleAttribute(false) +#if ENABLE(SVG) + , m_synchronizingSVGAttributes(false) +#endif { #ifndef NDEBUG if (shouldIgnoreLeaks) @@ -167,20 +329,9 @@ Node::Node(Document* doc, bool isElement, bool isContainer) else nodeCounter.increment(); #endif -} - -void Node::setDocument(Document* doc) -{ - if (inDocument() || m_document == doc) - return; - - willMoveToNewOwnerDocument(); - - updateDOMNodeDocument(this, m_document.get(), doc); - - m_document = doc; - - didMoveToNewOwnerDocument(); +#if DUMP_NODE_STATISTICS + liveNodeSet.add(this); +#endif } Node::~Node() @@ -192,7 +343,11 @@ Node::~Node() else nodeCounter.decrement(); #endif - + +#if DUMP_NODE_STATISTICS + liveNodeSet.remove(this); +#endif + if (!hasRareData()) ASSERT(!NodeRareData::rareDataMap().contains(this)); else { @@ -215,7 +370,21 @@ Node::~Node() m_next->setPreviousSibling(0); } -inline NodeRareData* Node::rareData() const +void Node::setDocument(Document* doc) +{ + if (inDocument() || m_document == doc) + return; + + willMoveToNewOwnerDocument(); + + updateDOMNodeDocument(this, m_document.get(), doc); + + m_document = doc; + + didMoveToNewOwnerDocument(); +} + +NodeRareData* Node::rareData() const { ASSERT(hasRareData()); return NodeRareData::rareDataFromMap(this); @@ -268,7 +437,7 @@ PassRefPtr<NodeList> Node::childNodes() { NodeRareData* data = ensureRareData(); if (!data->nodeLists()) { - data->setNodeLists(std::auto_ptr<NodeListsNodeData>(new NodeListsNodeData)); + data->setNodeLists(auto_ptr<NodeListsNodeData>(new NodeListsNodeData)); document()->addNodeListCache(); } @@ -412,12 +581,16 @@ bool Node::shouldUseInputMethod() const return isContentEditable(); } -IntRect Node::getRect() const +RenderBox* Node::renderBox() const { - int _x, _y; - if (renderer() && renderer()->absolutePosition(_x, _y)) - return IntRect( _x, _y, renderer()->width(), renderer()->height() + renderer()->borderTopExtra() + renderer()->borderBottomExtra()); + return m_renderer && m_renderer->isBox() ? static_cast<RenderBox*>(m_renderer) : 0; +} +IntRect Node::getRect() const +{ + // FIXME: broken with transforms + if (renderer()) + return renderer()->absoluteBoundingBoxRect(); return IntRect(); } @@ -479,13 +652,13 @@ bool Node::canLazyAttach() void Node::setFocus(bool b) { if (b || hasRareData()) - ensureRareData()->m_focused = b; + ensureRareData()->setFocused(b); } bool Node::rareDataFocused() const { ASSERT(hasRareData()); - return rareData()->m_focused; + return rareData()->isFocused(); } bool Node::isFocusable() const @@ -516,7 +689,7 @@ void Node::registerDynamicNodeList(DynamicNodeList* list) { NodeRareData* data = ensureRareData(); if (!data->nodeLists()) { - data->setNodeLists(std::auto_ptr<NodeListsNodeData>(new NodeListsNodeData)); + data->setNodeLists(auto_ptr<NodeListsNodeData>(new NodeListsNodeData)); document()->addNodeListCache(); } else if (!m_document->hasNodeListCaches()) { // We haven't been receiving notifications while there were no registered lists, so the cache is invalid now. @@ -672,7 +845,7 @@ Node* Node::traversePreviousSiblingPostOrder(const Node* stayWithin) const return 0; } -void Node::checkSetPrefix(const AtomicString &_prefix, ExceptionCode& ec) +void Node::checkSetPrefix(const AtomicString&, ExceptionCode& ec) { // Perform error checking as required by spec for setting Node.prefix. Used by // Element::setPrefix() and Attr::setPrefix() @@ -702,19 +875,17 @@ void Node::checkSetPrefix(const AtomicString &_prefix, ExceptionCode& ec) }*/ } -bool Node::canReplaceChild(Node* newChild, Node* oldChild) +bool Node::canReplaceChild(Node* newChild, Node*) { if (newChild->nodeType() != DOCUMENT_FRAGMENT_NODE) { if (!childTypeAllowed(newChild->nodeType())) return false; - } - else { + } else { for (Node *n = newChild->firstChild(); n; n = n->nextSibling()) { if (!childTypeAllowed(n->nodeType())) return false; } } - return true; } @@ -852,6 +1023,13 @@ bool Node::isDescendantOf(const Node *other) const return false; } +bool Node::contains(const Node* node) const +{ + if (!node) + return false; + return this == node || node->isDescendantOf(this); +} + bool Node::childAllowed(Node* newChild) { return childTypeAllowed(newChild->nodeType()); @@ -1059,7 +1237,7 @@ bool Node::rendererIsNeeded(RenderStyle *style) return (document()->documentElement() == this) || (style->display() != NONE); } -RenderObject *Node::createRenderer(RenderArena *arena, RenderStyle *style) +RenderObject* Node::createRenderer(RenderArena*, RenderStyle*) { ASSERT(false); return 0; @@ -1204,25 +1382,38 @@ bool Node::inSameContainingBlockFlowElement(Node *n) PassRefPtr<NodeList> Node::getElementsByTagName(const String& name) { - return getElementsByTagNameNS("*", name); + return getElementsByTagNameNS(starAtom, name); } -PassRefPtr<NodeList> Node::getElementsByTagNameNS(const String& namespaceURI, const String& localName) +PassRefPtr<NodeList> Node::getElementsByTagNameNS(const AtomicString& namespaceURI, const String& localName) { if (localName.isNull()) return 0; + + NodeRareData* data = ensureRareData(); + if (!data->nodeLists()) { + data->setNodeLists(auto_ptr<NodeListsNodeData>(new NodeListsNodeData)); + document()->addNodeListCache(); + } String name = localName; if (document()->isHTMLDocument()) name = localName.lower(); - return TagNodeList::create(this, namespaceURI.isEmpty() ? nullAtom : AtomicString(namespaceURI), name); + + AtomicString localNameAtom = name; + + pair<NodeListsNodeData::TagCacheMap::iterator, bool> result = data->nodeLists()->m_tagNodeListCaches.add(QualifiedName(nullAtom, localNameAtom, namespaceURI), 0); + if (result.second) + result.first->second = new DynamicNodeList::Caches; + + return TagNodeList::create(this, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localNameAtom, result.first->second); } PassRefPtr<NodeList> Node::getElementsByName(const String& elementName) { NodeRareData* data = ensureRareData(); if (!data->nodeLists()) { - data->setNodeLists(std::auto_ptr<NodeListsNodeData>(new NodeListsNodeData)); + data->setNodeLists(auto_ptr<NodeListsNodeData>(new NodeListsNodeData)); document()->addNodeListCache(); } @@ -1237,7 +1428,7 @@ PassRefPtr<NodeList> Node::getElementsByClassName(const String& classNames) { NodeRareData* data = ensureRareData(); if (!data->nodeLists()) { - data->setNodeLists(std::auto_ptr<NodeListsNodeData>(new NodeListsNodeData)); + data->setNodeLists(auto_ptr<NodeListsNodeData>(new NodeListsNodeData)); document()->addNodeListCache(); } @@ -1256,19 +1447,19 @@ static bool forEachTagSelector(Functor& functor, CSSSelector* selector) do { if (functor(selector)) return true; - if (CSSSelector* simpleSelector = selector->m_simpleSelector) { + if (CSSSelector* simpleSelector = selector->simpleSelector()) { if (forEachTagSelector(functor, simpleSelector)) return true; } - } while ((selector = selector->m_tagHistory)); + } while ((selector = selector->tagHistory())); return false; } template <typename Functor> -static bool forEachSelector(Functor& functor, CSSSelector* selector) +static bool forEachSelector(Functor& functor, const CSSSelectorList& selectorList) { - for (; selector; selector = selector->next()) { + for (CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) { if (forEachTagSelector(functor, selector)) return true; } @@ -1282,16 +1473,16 @@ public: { if (selector->hasTag() && selector->m_tag.prefix() != nullAtom && selector->m_tag.prefix() != starAtom) return true; - if (selector->hasAttribute() && selector->m_attr.prefix() != nullAtom && selector->m_attr.prefix() != starAtom) + if (selector->hasAttribute() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom) return true; return false; } }; -static bool selectorNeedsNamespaceResolution(CSSSelector* currentSelector) +static bool selectorNeedsNamespaceResolution(const CSSSelectorList& selectorList) { SelectorNeedsNamespaceResolutionFunctor functor; - return forEachSelector(functor, currentSelector); + return forEachSelector(functor, selectorList); } PassRefPtr<Element> Node::querySelector(const String& selectors, ExceptionCode& ec) @@ -1303,14 +1494,16 @@ PassRefPtr<Element> Node::querySelector(const String& selectors, ExceptionCode& bool strictParsing = !document()->inCompatMode(); CSSParser p(strictParsing); - std::auto_ptr<CSSSelector> querySelector = p.parseSelector(selectors, document()); - if (!querySelector.get()) { + 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 (selectorNeedsNamespaceResolution(querySelector.get())) { + if (selectorNeedsNamespaceResolution(querySelectorList)) { ec = NAMESPACE_ERR; return 0; } @@ -1318,10 +1511,10 @@ PassRefPtr<Element> Node::querySelector(const String& selectors, ExceptionCode& CSSStyleSelector::SelectorChecker selectorChecker(document(), strictParsing); // FIXME: we could also optimize for the the [id="foo"] case - if (strictParsing && querySelector->m_match == CSSSelector::Id && inDocument() && !querySelector->next()) { - ASSERT(querySelector->m_attr == idAttr); - Element* element = document()->getElementById(querySelector->m_value); - if (element && (isDocumentNode() || element->isDescendantOf(this)) && selectorChecker.checkSelector(querySelector.get(), element)) + if (strictParsing && inDocument() && querySelectorList.hasOneSelector() && querySelectorList.first()->m_match == CSSSelector::Id) { + ASSERT(querySelectorList.first()->attribute() == idAttr); + Element* element = document()->getElementById(querySelectorList.first()->m_value); + if (element && (isDocumentNode() || element->isDescendantOf(this)) && selectorChecker.checkSelector(querySelectorList.first(), element)) return element; return 0; } @@ -1330,7 +1523,7 @@ PassRefPtr<Element> Node::querySelector(const String& selectors, ExceptionCode& for (Node* n = firstChild(); n; n = n->traverseNextNode(this)) { if (n->isElementNode()) { Element* element = static_cast<Element*>(n); - for (CSSSelector* selector = querySelector.get(); selector; selector = selector->next()) { + for (CSSSelector* selector = querySelectorList.first(); selector; selector = CSSSelectorList::next(selector)) { if (selectorChecker.checkSelector(selector, element)) return element; } @@ -1349,20 +1542,21 @@ PassRefPtr<NodeList> Node::querySelectorAll(const String& selectors, ExceptionCo bool strictParsing = !document()->inCompatMode(); CSSParser p(strictParsing); - std::auto_ptr<CSSSelector> querySelector = p.parseSelector(selectors, document()); + CSSSelectorList querySelectorList; + p.parseSelector(selectors, document(), querySelectorList); - if (!querySelector.get()) { + if (!querySelectorList.first()) { ec = SYNTAX_ERR; return 0; } // Throw a NAMESPACE_ERR if the selector includes any namespace prefixes. - if (selectorNeedsNamespaceResolution(querySelector.get())) { + if (selectorNeedsNamespaceResolution(querySelectorList)) { ec = NAMESPACE_ERR; return 0; } - return createSelectorNodeList(this, querySelector.get()); + return createSelectorNodeList(this, querySelectorList); } Document *Node::ownerDocument() const @@ -1428,7 +1622,7 @@ bool Node::isEqualNode(Node *other) const return true; } -bool Node::isDefaultNamespace(const String &namespaceURI) const +bool Node::isDefaultNamespace(const AtomicString &namespaceURI) const { // Implemented according to // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#isDefaultNamespaceAlgo @@ -1478,7 +1672,7 @@ bool Node::isDefaultNamespace(const String &namespaceURI) const } } -String Node::lookupPrefix(const String &namespaceURI) const +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 @@ -1573,7 +1767,7 @@ String Node::lookupNamespaceURI(const String &prefix) const } } -String Node::lookupNamespacePrefix(const String &_namespaceURI, const Element *originalElement) const +String Node::lookupNamespacePrefix(const AtomicString &_namespaceURI, const Element *originalElement) const { if (_namespaceURI.isNull()) return String(); @@ -1759,7 +1953,7 @@ unsigned short Node::compareDocumentPosition(Node* otherNode) // Walk the two chains backwards and look for the first difference. unsigned index1 = chain1.size(); unsigned index2 = chain2.size(); - for (unsigned i = std::min(index1, index2); i; --i) { + for (unsigned i = min(index1, index2); i; --i) { Node* child1 = chain1[--index1]; Node* child2 = chain2[--index2]; if (child1 != child2) { @@ -1902,6 +2096,9 @@ void Node::formatForDebugger(char* buffer, unsigned length) const void NodeListsNodeData::invalidateCaches() { m_childNodeListCaches.reset(); + TagCacheMap::const_iterator tagCachesEnd = m_tagNodeListCaches.end(); + for (TagCacheMap::const_iterator it = m_tagNodeListCaches.begin(); it != tagCachesEnd; ++it) + it->second->reset(); invalidateCachesThatDependOnAttributes(); } @@ -1923,6 +2120,12 @@ bool NodeListsNodeData::isEmpty() const if (m_childNodeListCaches.refCount) return false; + + TagCacheMap::const_iterator tagCachesEnd = m_tagNodeListCaches.end(); + for (TagCacheMap::const_iterator it = m_tagNodeListCaches.begin(); it != tagCachesEnd; ++it) { + if (it->second->refCount) + return false; + } CacheMap::const_iterator classCachesEnd = m_classNodeListCaches.end(); for (CacheMap::const_iterator it = m_classNodeListCaches.begin(); it != classCachesEnd; ++it) { @@ -1939,20 +2142,39 @@ bool NodeListsNodeData::isEmpty() const return true; } -void Node::getSubresourceURLs(Vector<KURL>& urls) const +void Node::getSubresourceURLs(ListHashSet<KURL>& urls) const { - Vector<String> subresourceStrings; - getSubresourceAttributeStrings(subresourceStrings); - - for (unsigned i = 0; i < subresourceStrings.size(); ++i) { - String& subresourceString(subresourceStrings[i]); - - // FIXME: Is parseURL appropriate here? - if (subresourceString.length()) - urls.append(document()->completeURL(parseURL(subresourceString))); - } + addSubresourceAttributeURLs(urls); +} + +ContainerNode* Node::eventParentNode() +{ + Node* parent = parentNode(); + ASSERT(!parent || parent->isContainerNode()); + return static_cast<ContainerNode*>(parent); +} + +#ifdef ANDROID_INSTRUMENT +static size_t nodeSize = 0; + +void* Node::operator new(size_t s) throw() +{ + nodeSize += s; + return ::operator new(s); } +void Node::operator delete(void* ptr, size_t s) +{ + nodeSize -= s; + ::operator delete(ptr); +} + +size_t Node::reportDOMNodesSize() +{ + return nodeSize; +} +#endif + // -------- } // namespace WebCore diff --git a/WebCore/dom/Node.h b/WebCore/dom/Node.h index a471a47..fa9995d 100644 --- a/WebCore/dom/Node.h +++ b/WebCore/dom/Node.h @@ -25,10 +25,11 @@ #define Node_h #include "DocPtr.h" +#include "KURLHash.h" #include "PlatformString.h" #include "TreeShared.h" #include <wtf/Assertions.h> -#include <wtf/HashSet.h> +#include <wtf/ListHashSet.h> #include <wtf/OwnPtr.h> #include <wtf/PassRefPtr.h> @@ -42,7 +43,6 @@ class Element; class Event; class EventListener; class IntRect; -class KURL; class KeyboardEvent; class NSResolver; class NamedAttrMap; @@ -51,8 +51,8 @@ class PlatformKeyboardEvent; class PlatformMouseEvent; class PlatformWheelEvent; class QualifiedName; -class RegisteredEventListener; class RenderArena; +class RenderBox; class RenderObject; class RenderStyle; class StringBuilder; @@ -95,10 +95,12 @@ public: static void startIgnoringLeaks(); static void stopIgnoringLeaks(); + static void dumpStatistics(); + enum StyleChange { NoChange, NoInherit, Inherit, Detach, Force }; static StyleChange diff(RenderStyle*, RenderStyle*); - Node(Document*, bool isElement = false, bool isContainer = false); + Node(Document*, bool isElement = false, bool isContainer = false, bool isText = false); virtual ~Node(); // DOM methods & attributes for Node @@ -109,10 +111,10 @@ public: virtual void setNodeValue(const String&, ExceptionCode&); virtual NodeType nodeType() const = 0; Node* parentNode() const { return parent(); } - Node* parentElement() const { return parent(); } // IE extension + Element* parentElement() const; Node* previousSibling() const { return m_previous; } Node* nextSibling() const { return m_next; } - virtual PassRefPtr<NodeList> childNodes(); + PassRefPtr<NodeList> childNodes(); Node* firstChild() const { return isContainerNode() ? containerFirstChild() : 0; } Node* lastChild() const { return isContainerNode() ? containerLastChild() : 0; } bool hasAttributes() const; @@ -120,7 +122,7 @@ public: virtual KURL baseURI() const; - void getSubresourceURLs(Vector<KURL>&) 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 @@ -141,10 +143,10 @@ public: bool isSameNode(Node* other) const { return this == other; } bool isEqualNode(Node*) const; - bool isDefaultNamespace(const String& namespaceURI) const; - String lookupPrefix(const String& namespaceURI) const; + bool isDefaultNamespace(const AtomicString& namespaceURI) const; + String lookupPrefix(const AtomicString& namespaceURI) const; String lookupNamespaceURI(const String& prefix) const; - String lookupNamespacePrefix(const String& namespaceURI, const Element* originalElement) const; + String lookupNamespacePrefix(const AtomicString& namespaceURI, const Element* originalElement) const; String textContent(bool convertBRsToNewlines = false) const; void setTextContent(const String&, ExceptionCode&); @@ -156,6 +158,8 @@ public: bool isElementNode() const { return m_isElement; } bool isContainerNode() const { return m_isContainer; } + bool isTextNode() const { return m_isText; } + virtual bool isHTMLElement() const { return false; } #if ENABLE(SVG) @@ -164,10 +168,15 @@ public: static bool isSVGElement() { return false; } #endif +#if ENABLE(WML) + virtual bool isWMLElement() const { return false; } +#else + static bool isWMLElement() { return false; } +#endif + virtual bool isStyledElement() const { return false; } virtual bool isFrameOwnerElement() const { return false; } virtual bool isAttributeNode() const { return false; } - virtual bool isTextNode() const { return false; } virtual bool isCommentNode() const { return false; } virtual bool isCharacterDataNode() const { return false; } bool isDocumentNode() const; @@ -179,7 +188,7 @@ public: bool isInShadowTree(); // The node's parent for the purpose of event capture and bubbling. - virtual Node* eventParentNode() { return parentNode(); } + virtual ContainerNode* eventParentNode(); bool isBlockFlow() const; bool isBlockFlowOrBlockTable() const; @@ -281,6 +290,7 @@ public: virtual bool isKeyboardFocusable(KeyboardEvent*) const; virtual bool isMouseFocusable() const; + virtual bool isAutofilled() const { return false; } virtual bool isControl() const { return false; } // Eventually the notion of what is a control will be extensible. virtual bool isEnabled() const { return true; } virtual bool isChecked() const { return false; } @@ -365,8 +375,13 @@ public: RenderObject* previousRenderer(); void setRenderer(RenderObject* renderer) { m_renderer = renderer; } + // Use with caution. Does no type checking. Mostly a convenience method for shadow nodes of form controls, where we know exactly + // what kind of renderer we made. + RenderBox* renderBox() const; + void checkSetPrefix(const AtomicString& prefix, ExceptionCode&); bool isDescendantOf(const Node*) const; + bool contains(const Node*) const; // These two methods are mutually exclusive. The former is used to do strict error-checking // when adding children via the public DOM API (e.g., appendChild()). The latter is called only when parsing, @@ -447,14 +462,14 @@ public: // 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) { } + virtual void insertedIntoTree(bool /*deep*/) { } + virtual void removedFromTree(bool /*deep*/) { } /** * Notifies the node that it's list of children have changed (either by adding or removing child nodes), or a child * node that is of the type CDATA_SECTION_NODE, TEXT_NODE or COMMENT_NODE has changed its value. */ - virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0) {}; + virtual 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; @@ -472,7 +487,7 @@ public: void notifyLocalNodeListsAttributeChanged(); PassRefPtr<NodeList> getElementsByTagName(const String&); - PassRefPtr<NodeList> getElementsByTagNameNS(const String& namespaceURI, const String& localName); + PassRefPtr<NodeList> getElementsByTagNameNS(const AtomicString& namespaceURI, const String& localName); PassRefPtr<NodeList> getElementsByName(const String& elementName); PassRefPtr<NodeList> getElementsByClassName(const String& classNames); @@ -481,11 +496,21 @@ public: unsigned short compareDocumentPosition(Node*); +#ifdef ANDROID_INSTRUMENT + // Overridden to prevent the normal new from being called. + void* operator new(size_t) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + + static size_t reportDOMNodesSize(); +#endif + protected: virtual void willMoveToNewOwnerDocument() { } virtual void didMoveToNewOwnerDocument() { } - virtual void getSubresourceAttributeStrings(Vector<String>&) const { } + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const { } void setTabIndexExplicitly(short); bool hasRareData() const { return m_hasRareData; } @@ -506,14 +531,16 @@ private: virtual const AtomicString& virtualPrefix() const; virtual const AtomicString& virtualLocalName() const; virtual const AtomicString& virtualNamespaceURI() const; - + + Element* ancestorElement() const; + + void appendTextContent(bool convertBRsToNewlines, StringBuilder&) const; + DocPtr<Document> m_document; Node* m_previous; Node* m_next; RenderObject* m_renderer; - // make sure we don't use more than 16 bits here -- adding more would increase the size of all Nodes - unsigned m_styleChange : 2; bool m_hasId : 1; bool m_hasClass : 1; @@ -529,12 +556,37 @@ private: bool m_hasRareData : 1; const bool m_isElement : 1; const bool m_isContainer : 1; - // no bits left + const bool m_isText : 1; - Element* ancestorElement() const; +protected: + // These bits are used by the Element derived class, pulled up here so they can + // be stored in the same memory word as the Node bits above. + bool m_parsingChildrenFinished : 1; +#if ENABLE(SVG) + mutable bool m_areSVGAttributesValid : 1; +#endif - void appendTextContent(bool convertBRsToNewlines, StringBuilder&) const; + // These bits are used by the StyledElement derived class, and live here for the + // same reason as above. + mutable bool m_isStyleAttributeValid : 1; + mutable bool m_synchronizingStyleAttribute : 1; + +#if ENABLE(SVG) + // This bit is used by the SVGElement derived class, and lives here for the same + // reason as above. + mutable bool m_synchronizingSVGAttributes : 1; +#endif + + // 11 bits remaining }; + +// Used in Node::addSubresourceAttributeURLs() and in addSubresourceStyleURLs() +inline void addSubresourceURL(ListHashSet<KURL>& urls, const KURL& url) +{ + if (!url.isNull()) + urls.add(url); +} + } //namespace #ifndef NDEBUG diff --git a/WebCore/dom/NodeFilter.cpp b/WebCore/dom/NodeFilter.cpp index 9e0c43a..c5d855a 100644 --- a/WebCore/dom/NodeFilter.cpp +++ b/WebCore/dom/NodeFilter.cpp @@ -26,16 +26,13 @@ #include "NodeFilter.h" #include "Node.h" -#include <runtime/ExecState.h> - -using namespace JSC; namespace WebCore { -short NodeFilter::acceptNode(ExecState* exec, Node* node) const +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(exec, node) : static_cast<short>(FILTER_ACCEPT); + return m_condition ? m_condition->acceptNode(state, node) : static_cast<short>(FILTER_ACCEPT); } } // namespace WebCore diff --git a/WebCore/dom/NodeFilter.h b/WebCore/dom/NodeFilter.h index 47d0a4a..4356357 100644 --- a/WebCore/dom/NodeFilter.h +++ b/WebCore/dom/NodeFilter.h @@ -71,11 +71,11 @@ namespace WebCore { return adoptRef(new NodeFilter(condition)); } - short acceptNode(JSC::ExecState*, Node*) const; + short acceptNode(ScriptState*, Node*) const; void mark() { m_condition->mark(); }; // For non-JS bindings. Silently ignores the JavaScript exception if any. - short acceptNode(Node* node) const { return acceptNode(execStateFromNode(node), node); } + short acceptNode(Node* node) const { return acceptNode(scriptStateFromNode(node), node); } private: NodeFilter(PassRefPtr<NodeFilterCondition> condition) : m_condition(condition) { } diff --git a/WebCore/dom/NodeFilterCondition.cpp b/WebCore/dom/NodeFilterCondition.cpp index 74677b7..1d2e1e1 100644 --- a/WebCore/dom/NodeFilterCondition.cpp +++ b/WebCore/dom/NodeFilterCondition.cpp @@ -27,11 +27,9 @@ #include "NodeFilter.h" -using namespace JSC; - namespace WebCore { -short NodeFilterCondition::acceptNode(ExecState*, Node*) const +short NodeFilterCondition::acceptNode(ScriptState*, Node*) const { return NodeFilter::FILTER_ACCEPT; } diff --git a/WebCore/dom/NodeFilterCondition.h b/WebCore/dom/NodeFilterCondition.h index 6fcdfa8..c94cc9a 100644 --- a/WebCore/dom/NodeFilterCondition.h +++ b/WebCore/dom/NodeFilterCondition.h @@ -25,12 +25,9 @@ #ifndef NodeFilterCondition_h #define NodeFilterCondition_h +#include "ScriptState.h" #include <wtf/RefCounted.h> -namespace JSC { - class ExecState; -} - namespace WebCore { class Node; @@ -38,7 +35,7 @@ namespace WebCore { class NodeFilterCondition : public RefCounted<NodeFilterCondition> { public: virtual ~NodeFilterCondition() { } - virtual short acceptNode(JSC::ExecState*, Node*) const = 0; + virtual short acceptNode(ScriptState*, Node*) const = 0; virtual void mark() { } }; diff --git a/WebCore/dom/NodeIterator.cpp b/WebCore/dom/NodeIterator.cpp index 4707e9f..af07f42 100644 --- a/WebCore/dom/NodeIterator.cpp +++ b/WebCore/dom/NodeIterator.cpp @@ -25,12 +25,10 @@ #include "config.h" #include "NodeIterator.h" -#include <runtime/ExecState.h> #include "Document.h" #include "ExceptionCode.h" #include "NodeFilter.h" - -using namespace JSC; +#include "ScriptState.h" namespace WebCore { @@ -86,7 +84,7 @@ NodeIterator::~NodeIterator() root()->document()->detachNodeIterator(this); } -PassRefPtr<Node> NodeIterator::nextNode(ExecState* exec, ExceptionCode& ec) +PassRefPtr<Node> NodeIterator::nextNode(ScriptState* state, ExceptionCode& ec) { if (m_detached) { ec = INVALID_STATE_ERR; @@ -101,8 +99,8 @@ PassRefPtr<Node> NodeIterator::nextNode(ExecState* exec, ExceptionCode& ec) // 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(exec, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; - if (exec && exec->hadException()) + bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; + if (state && state->hadException()) break; if (nodeWasAccepted) { m_referenceNode = m_candidateNode; @@ -115,7 +113,7 @@ PassRefPtr<Node> NodeIterator::nextNode(ExecState* exec, ExceptionCode& ec) return result.release(); } -PassRefPtr<Node> NodeIterator::previousNode(ExecState* exec, ExceptionCode& ec) +PassRefPtr<Node> NodeIterator::previousNode(ScriptState* state, ExceptionCode& ec) { if (m_detached) { ec = INVALID_STATE_ERR; @@ -130,8 +128,8 @@ PassRefPtr<Node> NodeIterator::previousNode(ExecState* exec, ExceptionCode& ec) // 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(exec, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; - if (exec && exec->hadException()) + bool nodeWasAccepted = acceptNode(state, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; + if (state && state->hadException()) break; if (nodeWasAccepted) { m_referenceNode = m_candidateNode; diff --git a/WebCore/dom/NodeIterator.h b/WebCore/dom/NodeIterator.h index 842e71d..d7eee36 100644 --- a/WebCore/dom/NodeIterator.h +++ b/WebCore/dom/NodeIterator.h @@ -43,8 +43,8 @@ namespace WebCore { } ~NodeIterator(); - PassRefPtr<Node> nextNode(JSC::ExecState*, ExceptionCode&); - PassRefPtr<Node> previousNode(JSC::ExecState*, ExceptionCode&); + PassRefPtr<Node> nextNode(ScriptState*, ExceptionCode&); + PassRefPtr<Node> previousNode(ScriptState*, ExceptionCode&); void detach(); Node* referenceNode() const { return m_referenceNode.node.get(); } @@ -54,8 +54,8 @@ namespace WebCore { void nodeWillBeRemoved(Node*); // For non-JS bindings. Silently ignores the JavaScript exception if any. - PassRefPtr<Node> nextNode(ExceptionCode& ec) { return nextNode(execStateFromNode(referenceNode()), ec); } - PassRefPtr<Node> previousNode(ExceptionCode& ec) { return previousNode(execStateFromNode(referenceNode()), ec); } + PassRefPtr<Node> nextNode(ExceptionCode& ec) { return nextNode(scriptStateFromNode(referenceNode()), ec); } + PassRefPtr<Node> previousNode(ExceptionCode& ec) { return previousNode(scriptStateFromNode(referenceNode()), ec); } private: NodeIterator(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); diff --git a/WebCore/dom/NodeRareData.h b/WebCore/dom/NodeRareData.h index acad86c..bbcfca8 100644 --- a/WebCore/dom/NodeRareData.h +++ b/WebCore/dom/NodeRareData.h @@ -1,6 +1,5 @@ -/** - * - * Copyright (C) 2008 Apple Computer, Inc. +/* + * Copyright (C) 2008 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 @@ -24,7 +23,10 @@ #define NodeRareData_h #include "DynamicNodeList.h" +#include "EventListener.h" +#include "RegisteredEventListener.h" #include "StringHash.h" +#include "QualifiedName.h" #include <wtf/HashSet.h> #include <wtf/OwnPtr.h> @@ -40,10 +42,14 @@ struct NodeListsNodeData { CacheMap m_classNodeListCaches; CacheMap m_nameNodeListCaches; + typedef HashMap<QualifiedName, DynamicNodeList::Caches*> TagCacheMap; + TagCacheMap m_tagNodeListCaches; + ~NodeListsNodeData() { deleteAllValues(m_classNodeListCaches); deleteAllValues(m_nameNodeListCaches); + deleteAllValues(m_tagNodeListCaches); } void invalidateCaches(); @@ -54,9 +60,9 @@ struct NodeListsNodeData { class NodeRareData { public: NodeRareData() - : m_focused(false) - , m_tabIndex(0) - , m_tabIndexSetExplicitly(false) + : m_tabIndex(0) + , m_tabIndexWasSetExplicitly(false) + , m_isFocused(false) , m_needsFocusAppearanceUpdateSoonAfterAttach(false) { } @@ -79,18 +85,34 @@ public: NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); } short tabIndex() const { return m_tabIndex; } - void setTabIndexExplicitly(short index) { m_tabIndex = index; m_tabIndexSetExplicitly = true; } - bool tabIndexSetExplicitly() const { return m_tabIndexSetExplicitly; } - - bool m_focused : 1; + void setTabIndexExplicitly(short index) { m_tabIndex = index; m_tabIndexWasSetExplicitly = true; } + bool tabIndexSetExplicitly() const { return m_tabIndexWasSetExplicitly; } + + RegisteredEventListenerVector* listeners() { return m_eventListeners.get(); } + RegisteredEventListenerVector& ensureListeners() + { + if (!m_eventListeners) + m_eventListeners.set(new RegisteredEventListenerVector); + return *m_eventListeners; + } + + 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<RegisteredEventListenerVector > m_eventListeners; short m_tabIndex; - bool m_tabIndexSetExplicitly : 1; -public: - bool m_needsFocusAppearanceUpdateSoonAfterAttach : 1; //for ElementRareData + bool m_tabIndexWasSetExplicitly : 1; + bool m_isFocused : 1; + bool m_needsFocusAppearanceUpdateSoonAfterAttach : 1; }; + } //namespace #endif diff --git a/WebCore/dom/OptionElement.cpp b/WebCore/dom/OptionElement.cpp new file mode 100644 index 0000000..ea8f826 --- /dev/null +++ b/WebCore/dom/OptionElement.cpp @@ -0,0 +1,124 @@ +/* + * 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 <wtf/Assertions.h> + +#if ENABLE(WML) +#include "WMLOptionElement.h" +#include "WMLNames.h" +#endif + +namespace WebCore { + +void OptionElement::setSelectedState(OptionElementData& data, bool selected) +{ + if (data.selected() == selected) + return; + + data.setSelected(selected); + data.element()->setChanged(); +} + +String OptionElement::collectOptionText(const OptionElementData& data, Document* document) +{ + String text; + + // WinIE does not use the label attribute, so as a quirk, we ignore it. + if (!document->inCompatMode()) + text = data.label(); + + if (text.isEmpty()) { + Node* n = data.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(data.element()); + else + n = n->traverseNextNode(data.element()); + } + } + + text = document->displayStringModifiedByEncoding(text); + + // 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, Document* document) +{ + Element* parentElement = static_cast<Element*>(data.element()->parentNode()); + if (parentElement && toOptionGroupElement(parentElement)) + return " " + collectOptionText(data, document); + + return collectOptionText(data, document); +} + +String OptionElement::collectOptionValue(const OptionElementData& data, Document* document) +{ + String value = data.value(); + if (!value.isNull()) + return value; + + // Use the text if the value wasn't set. + return collectOptionText(data, document).stripWhiteSpace(); +} + +// OptionElementData +OptionElementData::OptionElementData(Element* element) + : m_element(element) + , m_selected(false) +{ + ASSERT(m_element); +} + +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; +} + +} diff --git a/WebCore/dom/OptionElement.h b/WebCore/dom/OptionElement.h new file mode 100644 index 0000000..18fc4ea --- /dev/null +++ b/WebCore/dom/OptionElement.h @@ -0,0 +1,80 @@ +/* + * 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 OptionElement { +public: + virtual ~OptionElement() { } + + virtual bool selected() const = 0; + virtual void setSelectedState(bool) = 0; + + virtual String textIndentedToRespectGroupLabel() const = 0; + virtual String value() const = 0; + +protected: + OptionElement() { } + + static void setSelectedState(OptionElementData&, bool selected); + static String collectOptionText(const OptionElementData&, Document*); + static String collectOptionTextRespectingGroupLabel(const OptionElementData&, Document*); + static String collectOptionValue(const OptionElementData&, Document*); +}; + +// HTML/WMLOptionElement hold this struct as member variable +// and pass it to the static helper functions in OptionElement +class OptionElementData { +public: + OptionElementData(Element*); + ~OptionElementData(); + + Element* element() const { return m_element; } + + 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: + Element* m_element; + String m_value; + String m_label; + bool m_selected; +}; + +OptionElement* toOptionElement(Element*); + +} + +#endif diff --git a/WebCore/dom/OptionGroupElement.cpp b/WebCore/dom/OptionGroupElement.cpp new file mode 100644 index 0000000..548fb9a --- /dev/null +++ b/WebCore/dom/OptionGroupElement.cpp @@ -0,0 +1,49 @@ +/* + * 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; +} + +} diff --git a/WebCore/dom/OptionGroupElement.h b/WebCore/dom/OptionGroupElement.h new file mode 100644 index 0000000..eee3930 --- /dev/null +++ b/WebCore/dom/OptionGroupElement.h @@ -0,0 +1,43 @@ +/* + * 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 + +namespace WebCore { + +class Element; +class String; + +class OptionGroupElement { +public: + virtual ~OptionGroupElement() { } + + virtual String groupLabelText() const = 0; + +protected: + OptionGroupElement() { } +}; + +OptionGroupElement* toOptionGroupElement(Element*); + +} + +#endif diff --git a/WebCore/dom/Position.cpp b/WebCore/dom/Position.cpp index 359cc32..faa4a13 100644 --- a/WebCore/dom/Position.cpp +++ b/WebCore/dom/Position.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * 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 @@ -29,13 +29,12 @@ #include "CSSComputedStyleDeclaration.h" #include "CString.h" #include "CharacterNames.h" -#include "Document.h" -#include "Element.h" -#include "HTMLNames.h" #include "Logging.h" #include "PositionIterator.h" +#include "RenderBlock.h" #include "Text.h" #include "TextIterator.h" +#include "VisiblePosition.h" #include "htmlediting.h" #include "visible_units.h" #include <stdio.h> @@ -53,7 +52,7 @@ static Node *nextRenderedEditable(Node *node) RenderObject* renderer = node->renderer(); if (!renderer) continue; - if (renderer->inlineBoxWrapper() || renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox()) + if (renderer->inlineBoxWrapper() || renderer->isText() && toRenderText(renderer)->firstTextBox()) return node; } return 0; @@ -68,7 +67,7 @@ static Node *previousRenderedEditable(Node *node) RenderObject* renderer = node->renderer(); if (!renderer) continue; - if (renderer->inlineBoxWrapper() || renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox()) + if (renderer->inlineBoxWrapper() || renderer->isText() && toRenderText(renderer)->firstTextBox()) return node; } return 0; @@ -195,17 +194,17 @@ int Position::renderedOffset() const return offset(); int result = 0; - RenderText *textRenderer = static_cast<RenderText *>(node()->renderer()); + RenderText *textRenderer = toRenderText(node()->renderer()); for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { - int start = box->m_start; - int end = box->m_start + box->m_len; + int start = box->start(); + int end = box->start() + box->len(); if (offset() < start) return result; if (offset() <= end) { result += offset() - start; return result; } - result += box->m_len; + result += box->len(); } return result; } @@ -283,7 +282,7 @@ static bool endsOfNodeAreVisuallyDistinctPositions(Node* node) return false; // There is a VisiblePosition inside an empty inline-block container. - return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && node->renderer()->height() != 0 && !node->firstChild(); + return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && toRenderBox(node->renderer())->height() != 0 && !node->firstChild(); } static Node* enclosingVisualBoundary(Node* node) @@ -365,7 +364,7 @@ Position Position::upstream() const } // return current position if it is in rendered text - if (renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox()) { + 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 @@ -376,7 +375,7 @@ Position Position::upstream() const } unsigned textOffset = currentPos.offsetInLeafNode(); - RenderText* textRenderer = static_cast<RenderText*>(renderer); + RenderText* textRenderer = toRenderText(renderer); InlineTextBox* lastTextBox = textRenderer->lastTextBox(); for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (textOffset <= box->start() + box->len()) { @@ -481,14 +480,14 @@ Position Position::downstream() const } // return current position if it is in rendered text - if (renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox()) { + if (renderer->isText() && toRenderText(renderer)->firstTextBox()) { if (currentNode != startNode) { ASSERT(currentPos.atStartOfNode()); return Position(currentNode, renderer->caretMinOffset()); } unsigned textOffset = currentPos.offsetInLeafNode(); - RenderText* textRenderer = static_cast<RenderText*>(renderer); + RenderText* textRenderer = toRenderText(renderer); InlineTextBox* lastTextBox = textRenderer->lastTextBox(); for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (textOffset <= box->end()) { @@ -535,9 +534,11 @@ bool Position::hasRenderedNonAnonymousDescendantsWithHeight(RenderObject* render { RenderObject* stop = renderer->nextInPreOrderAfterChildren(); for (RenderObject *o = renderer->firstChild(); o && o != stop; o = o->nextInPreOrder()) - if (o->element() && o->height()) - return true; - + if (o->element()) { + if ((o->isText() && toRenderText(o)->linesBoundingBox().height()) || + (o->isBox() && toRenderBox(o)->borderBoundingBox().height())) + return true; + } return false; } @@ -568,7 +569,7 @@ bool Position::isCandidate() const return (offset() == 0 || offset() == maxDeepOffset(node())) && !nodeIsUserSelectNone(node()->parent()); if (!node()->hasTagName(htmlTag) && renderer->isBlockFlow() && !hasRenderedNonAnonymousDescendantsWithHeight(renderer) && - (renderer->height() || node()->hasTagName(bodyTag))) + (toRenderBox(renderer)->height() || node()->hasTagName(bodyTag))) return offset() == 0 && !nodeIsUserSelectNone(node()); return false; @@ -583,9 +584,9 @@ bool Position::inRenderedText() const if (!renderer) return false; - RenderText *textRenderer = static_cast<RenderText *>(renderer); + RenderText *textRenderer = toRenderText(renderer); for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { - if (offset() < box->m_start && !textRenderer->containsReversedText()) { + if (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. @@ -615,19 +616,19 @@ bool Position::isRenderedCharacter() const if (isNull() || !node()->isTextNode()) return false; - RenderObject *renderer = node()->renderer(); + RenderObject* renderer = node()->renderer(); if (!renderer) return false; - RenderText *textRenderer = static_cast<RenderText *>(renderer); - for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { - if (offset() < box->m_start && !textRenderer->containsReversedText()) { + RenderText* textRenderer = toRenderText(renderer); + for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { + if (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 (offset() >= box->m_start && offset() < box->m_start + box->m_len) + if (offset() >= static_cast<int>(box->start()) && offset() < static_cast<int>(box->start() + box->len())) return true; } @@ -743,7 +744,7 @@ Position Position::leadingWhitespacePosition(EAffinity affinity, bool considerNo } // This assumes that it starts in editable content. -Position Position::trailingWhitespacePosition(EAffinity affinity, bool considerNonCollapsibleWhitespace) const +Position Position::trailingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace) const { ASSERT(isEditablePosition(*this)); if (isNull()) @@ -771,6 +772,41 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox, 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) +{ + InlineTextBox* match = 0; + int minOffset = INT_MAX; + RenderBlock* container = renderer->containingBlock(); + RenderObject* next = renderer; + while ((next = next->nextInPreOrder(container))) { + if (next->isRenderBlock()) + break; + if (next->isBR()) + break; + if (isNonTextLeafChild(next)) + break; + if (next->isText()) { + for (InlineTextBox* box = toRenderText(next)->firstTextBox(); box; box = box->nextTextBox()) { + int caretMinOffset = box->caretMinOffset(); + if (caretMinOffset < minOffset) { + match = box; + minOffset = caretMinOffset; + } + } + } + } + return match; +} + void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const { caretOffset = offset(); @@ -780,7 +816,7 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi if (!inlineBox || caretOffset > inlineBox->caretMinOffset() && caretOffset < inlineBox->caretMaxOffset()) return; } else { - RenderText* textRenderer = static_cast<RenderText*>(renderer); + RenderText* textRenderer = toRenderText(renderer); InlineTextBox* box; InlineTextBox* candidate = 0; @@ -802,6 +838,11 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi candidate = box; } + if (candidate && !box && affinity == DOWNSTREAM) { + box = searchAheadForBetterMatch(textRenderer); + if (box) + caretOffset = box->caretMinOffset(); + } inlineBox = box ? box : candidate; } diff --git a/WebCore/dom/PositionIterator.cpp b/WebCore/dom/PositionIterator.cpp index 218ace1..06e1e32 100644 --- a/WebCore/dom/PositionIterator.cpp +++ b/WebCore/dom/PositionIterator.cpp @@ -27,7 +27,7 @@ #include "PositionIterator.h" #include "Node.h" -#include "RenderObject.h" +#include "RenderBlock.h" #include "htmlediting.h" namespace WebCore { @@ -151,7 +151,7 @@ bool PositionIterator::isCandidate() const return (atStartOfNode() || atEndOfNode()) && !Position::nodeIsUserSelectNone(m_parent->parent()); if (!m_parent->hasTagName(htmlTag) && renderer->isBlockFlow() && !Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer) && - (renderer->height() || m_parent->hasTagName(bodyTag))) + (static_cast<RenderBlock*>(renderer)->height() || m_parent->hasTagName(bodyTag))) return atStartOfNode() && !Position::nodeIsUserSelectNone(m_parent); return false; diff --git a/WebCore/dom/ProcessingInstruction.cpp b/WebCore/dom/ProcessingInstruction.cpp index 3d1a608..906902a 100644 --- a/WebCore/dom/ProcessingInstruction.cpp +++ b/WebCore/dom/ProcessingInstruction.cpp @@ -136,39 +136,35 @@ void ProcessingInstruction::checkStyleSheet() m_title = attrs.get("title"); m_media = attrs.get("media"); - if (href.length() > 1) { - if (href[0] == '#') { - m_localHref = href.substring(1); + 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) { - m_sheet = XSLStyleSheet::createEmbedded(this, m_localHref); - m_loading = false; - } -#endif + // We need to make a synthetic XSLStyleSheet that is embedded. It needs to be able + // to kick off import/include loads that can hang off some parent sheet. + if (m_isXSL) { + m_sheet = XSLStyleSheet::createEmbedded(this, m_localHref); + m_loading = false; } - else - { - m_loading = true; - document()->addPendingSheet(); - if (m_cachedSheet) - m_cachedSheet->removeClient(this); +#endif + } else { + m_loading = true; + document()->addPendingSheet(); + if (m_cachedSheet) + m_cachedSheet->removeClient(this); #if ENABLE(XSLT) - if (m_isXSL) - m_cachedSheet = document()->docLoader()->requestXSLStyleSheet(document()->completeURL(href).string()); - else + if (m_isXSL) + m_cachedSheet = document()->docLoader()->requestXSLStyleSheet(document()->completeURL(href).string()); + else #endif - { - String charset = attrs.get("charset"); - if (charset.isEmpty()) - charset = document()->frame()->loader()->encoding(); - - m_cachedSheet = document()->docLoader()->requestCSSStyleSheet(document()->completeURL(href).string(), charset); - } - if (m_cachedSheet) - m_cachedSheet->addClient(this); + { + String charset = attrs.get("charset"); + if (charset.isEmpty()) + charset = document()->frame()->loader()->encoding(); + + m_cachedSheet = document()->docLoader()->requestCSSStyleSheet(document()->completeURL(href).string(), charset); } + if (m_cachedSheet) + m_cachedSheet->addClient(this); } } } @@ -243,12 +239,12 @@ int ProcessingInstruction::maxCharacterOffset() const return static_cast<int>(m_data.length()); } -void ProcessingInstruction::getSubresourceAttributeStrings(Vector<String>& urls) const +void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const { if (!sheet()) return; - - urls.append(sheet()->href()); + + addSubresourceURL(urls, sheet()->baseURL()); } void ProcessingInstruction::insertedIntoDocument() diff --git a/WebCore/dom/ProcessingInstruction.h b/WebCore/dom/ProcessingInstruction.h index e16f700..3f42ed4 100644 --- a/WebCore/dom/ProcessingInstruction.h +++ b/WebCore/dom/ProcessingInstruction.h @@ -75,7 +75,7 @@ public: bool isXSL() const { return m_isXSL; } #endif - virtual void getSubresourceAttributeStrings(Vector<String>&) const; + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; private: void parseStyleSheet(const String& sheet); diff --git a/WebCore/dom/QualifiedName.cpp b/WebCore/dom/QualifiedName.cpp index a65b6ae..f40f398 100644 --- a/WebCore/dom/QualifiedName.cpp +++ b/WebCore/dom/QualifiedName.cpp @@ -34,69 +34,19 @@ namespace WebCore { -struct QualifiedNameComponents { - StringImpl* m_prefix; - StringImpl* m_localName; - StringImpl* m_namespace; -}; - -// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's -static const unsigned PHI = 0x9e3779b9U; - -static inline unsigned hashComponents(const QualifiedNameComponents& buf) -{ - ASSERT(sizeof(QualifiedNameComponents) % (sizeof(uint16_t) * 2) == 0); - - unsigned l = sizeof(QualifiedNameComponents) / (sizeof(uint16_t) * 2); - const uint16_t* s = reinterpret_cast<const uint16_t*>(&buf); - uint32_t hash = PHI; - - // Main loop - for (; l > 0; l--) { - hash += s[0]; - uint32_t tmp = (s[1] << 11) ^ hash; - hash = (hash << 16) ^ tmp; - s += 2; - hash += hash >> 11; - } - - // Force "avalanching" of final 127 bits - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 2; - hash += hash >> 15; - hash ^= hash << 10; - - // this avoids ever returning a hash code of 0, since that is used to - // signal "hash not computed yet", using a value that is likely to be - // effectively the same as 0 when the low bits are masked - if (hash == 0) - hash = 0x80000000; - - return hash; -} - -struct QNameHash { - static unsigned hash(const QualifiedName::QualifiedNameImpl* name) { - QualifiedNameComponents c = { name->m_prefix.impl(), name->m_localName.impl(), name->m_namespace.impl() }; - return hashComponents(c); - } - - static bool equal(const QualifiedName::QualifiedNameImpl* a, const QualifiedName::QualifiedNameImpl* b) { return a == b; } - - static const bool safeToCompareToEmptyOrDeleted = false; -}; - -typedef HashSet<QualifiedName::QualifiedNameImpl*, QNameHash> QNameSet; +typedef HashSet<QualifiedName::QualifiedNameImpl*, QualifiedNameHash> QNameSet; struct QNameComponentsTranslator { - static unsigned hash(const QualifiedNameComponents& components) { + static unsigned hash(const QualifiedNameComponents& components) + { return hashComponents(components); } - static bool equal(QualifiedName::QualifiedNameImpl* name, const QualifiedNameComponents& c) { + static bool equal(QualifiedName::QualifiedNameImpl* name, const QualifiedNameComponents& c) + { return c.m_prefix == name->m_prefix.impl() && c.m_localName == name->m_localName.impl() && c.m_namespace == name->m_namespace.impl(); } - static void translate(QualifiedName::QualifiedNameImpl*& location, const QualifiedNameComponents& components, unsigned hash) { + static void translate(QualifiedName::QualifiedNameImpl*& location, const QualifiedNameComponents& components, unsigned) + { location = QualifiedName::QualifiedNameImpl::create(components.m_prefix, components.m_localName, components.m_namespace).releaseRef(); } }; diff --git a/WebCore/dom/QualifiedName.h b/WebCore/dom/QualifiedName.h index 5aa8abf..d09cdea 100644 --- a/WebCore/dom/QualifiedName.h +++ b/WebCore/dom/QualifiedName.h @@ -23,8 +23,15 @@ #define QualifiedName_h #include "AtomicString.h" +#include <wtf/HashFunctions.h> namespace WebCore { + +struct QualifiedNameComponents { + StringImpl* m_prefix; + StringImpl* m_localName; + StringImpl* m_namespace; +}; class QualifiedName { public: @@ -75,7 +82,7 @@ public: // Init routine for globals static void init(); - + private: void ref() { m_impl->ref(); } void deref(); @@ -93,5 +100,70 @@ inline bool operator!=(const AtomicString& a, const QualifiedName& q) { return a 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) +{ + ASSERT(sizeof(QualifiedNameComponents) % (sizeof(uint16_t) * 2) == 0); + + unsigned l = sizeof(QualifiedNameComponents) / (sizeof(uint16_t) * 2); + const uint16_t* s = reinterpret_cast<const uint16_t*>(&buf); + uint32_t hash = WTF::stringHashingStartValue; + + // Main loop + for (; l > 0; l--) { + hash += s[0]; + uint32_t tmp = (s[1] << 11) ^ hash; + hash = (hash << 16) ^ tmp; + s += 2; + hash += hash >> 11; + } + + // Force "avalanching" of final 127 bits + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 2; + hash += hash >> 15; + hash ^= hash << 10; + + // this avoids ever returning a hash code of 0, since that is used to + // signal "hash not computed yet", using a value that is likely to be + // effectively the same as 0 when the low bits are masked + if (hash == 0) + hash = 0x80000000; + + return hash; +} + +struct 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(WebCore::nullAtom, WebCore::nullAtom, WebCore::nullAtom); } + static void constructDeletedValue(WebCore::QualifiedName& slot) { new (&slot) WebCore::QualifiedName(WebCore::nullAtom, WebCore::AtomicString(HashTableDeletedValue), WebCore::nullAtom); } + static bool isDeletedValue(const WebCore::QualifiedName& slot) { return slot.localName().isHashTableDeletedValue(); } + }; } + #endif diff --git a/WebCore/dom/Range.cpp b/WebCore/dom/Range.cpp index afd563e..b5afdd1 100644 --- a/WebCore/dom/Range.cpp +++ b/WebCore/dom/Range.cpp @@ -26,16 +26,12 @@ #include "RangeException.h" #include "CString.h" -#include "Document.h" #include "DocumentFragment.h" -#include "ExceptionCode.h" -#include "HTMLElement.h" -#include "HTMLNames.h" #include "NodeWithIndex.h" #include "ProcessingInstruction.h" -#include "RenderBlock.h" #include "Text.h" #include "TextIterator.h" +#include "VisiblePosition.h" #include "markup.h" #include "visible_units.h" #include <stdio.h> @@ -44,7 +40,6 @@ namespace WebCore { using namespace std; -using namespace HTMLNames; #ifndef NDEBUG static WTF::RefCountedLeakCounter rangeCounter("Range"); @@ -1425,18 +1420,16 @@ void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) // allowed by the type of node? // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-Text node. - if (m_start.container()->nodeType() != Node::TEXT_NODE) { - if (m_start.offset() > 0 && m_start.offset() < maxStartOffset()) { - ec = RangeException::BAD_BOUNDARYPOINTS_ERR; - return; - } + 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; } - if (m_end.container()->nodeType() != Node::TEXT_NODE) { - if (m_end.offset() > 0 && m_end.offset() < maxEndOffset()) { - ec = RangeException::BAD_BOUNDARYPOINTS_ERR; - return; - } - } ec = 0; while (Node* n = newParent->firstChild()) { diff --git a/WebCore/dom/RegisteredEventListener.cpp b/WebCore/dom/RegisteredEventListener.cpp index 7312785..f257e56 100644 --- a/WebCore/dom/RegisteredEventListener.cpp +++ b/WebCore/dom/RegisteredEventListener.cpp @@ -35,9 +35,4 @@ RegisteredEventListener::RegisteredEventListener(const AtomicString& eventType, { } -bool operator==(const RegisteredEventListener& a, const RegisteredEventListener& b) -{ - return a.eventType() == b.eventType() && a.listener() == b.listener() && a.useCapture() == b.useCapture(); -} - } // namespace WebCore diff --git a/WebCore/dom/RegisteredEventListener.h b/WebCore/dom/RegisteredEventListener.h index 58cd931..29b061d 100644 --- a/WebCore/dom/RegisteredEventListener.h +++ b/WebCore/dom/RegisteredEventListener.h @@ -53,10 +53,6 @@ namespace WebCore { bool m_removed; }; - - bool operator==(const RegisteredEventListener&, const RegisteredEventListener&); - inline bool operator!=(const RegisteredEventListener& a, const RegisteredEventListener& b) { return !(a == b); } - } // namespace WebCore #endif // RegisteredEventListener_h diff --git a/WebCore/dom/ScriptElement.cpp b/WebCore/dom/ScriptElement.cpp index 5071afc..1cd8696 100644 --- a/WebCore/dom/ScriptElement.cpp +++ b/WebCore/dom/ScriptElement.cpp @@ -29,10 +29,20 @@ #include "Document.h" #include "Frame.h" #include "FrameLoader.h" +#include "HTMLNames.h" +#include "HTMLScriptElement.h" #include "MIMETypeRegistry.h" #include "ScriptController.h" +#include "ScriptSourceCode.h" +#include "ScriptValue.h" #include "StringHash.h" #include "Text.h" +#include <wtf/StdLibExtras.h> + +#if ENABLE(SVG) +#include "SVGNames.h" +#include "SVGScriptElement.h" +#endif namespace WebCore { @@ -49,7 +59,7 @@ void ScriptElement::insertedIntoDocument(ScriptElementData& data, const String& // 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. - data.evaluateScript(data.element()->document()->url().string(), data.scriptContent()); + data.evaluateScript(ScriptSourceCode(data.scriptContent(), data.element()->document()->url())); // FIXME: Provide a real starting line number here. } void ScriptElement::removedFromDocument(ScriptElementData& data) @@ -69,7 +79,7 @@ void ScriptElement::childrenChanged(ScriptElementData& data) // and the script element has been inserted in the document // we evaluate the script. if (element->inDocument() && element->firstChild()) - data.evaluateScript(element->document()->url().string(), data.scriptContent()); + data.evaluateScript(ScriptSourceCode(data.scriptContent(), element->document()->url())); // FIXME: Provide a real starting line number here } void ScriptElement::finishParsingChildren(ScriptElementData& data, const String& sourceUrl) @@ -91,7 +101,8 @@ void ScriptElement::handleSourceAttribute(ScriptElementData& data, const String& // Helper function static bool isSupportedJavaScriptLanguage(const String& language) { - static HashSet<String, CaseFoldingHash> languages; + typedef HashSet<String, CaseFoldingHash> LanguageSet; + DEFINE_STATIC_LOCAL(LanguageSet, languages, ()); if (languages.isEmpty()) { languages.add("javascript"); languages.add("javascript"); @@ -155,9 +166,9 @@ void ScriptElementData::requestScript(const String& sourceUrl) m_scriptElement->dispatchErrorEvent(); } -void ScriptElementData::evaluateScript(const String& sourceUrl, const String& content) +void ScriptElementData::evaluateScript(const ScriptSourceCode& sourceCode) { - if (m_evaluated || content.isEmpty() || !shouldExecuteAsJavaScript()) + if (m_evaluated || sourceCode.isEmpty() || !shouldExecuteAsJavaScript()) return; if (Frame* frame = m_element->document()->frame()) { @@ -166,9 +177,7 @@ void ScriptElementData::evaluateScript(const String& sourceUrl, const String& co m_evaluated = true; - // FIXME: This starting line number will be incorrect for evaluation triggered - // from insertedIntoDocument or childrenChanged. - frame->script()->evaluate(sourceUrl, 1, content); + frame->script()->evaluate(sourceCode); Document::updateDocumentsRendering(); } } @@ -193,7 +202,7 @@ void ScriptElementData::notifyFinished(CachedResource* o) if (cs->errorOccurred()) m_scriptElement->dispatchErrorEvent(); else { - evaluateScript(cs->url(), cs->script()); + evaluateScript(ScriptSourceCode(cs)); m_scriptElement->dispatchLoadEvent(); } @@ -267,4 +276,17 @@ String ScriptElementData::scriptContent() const return String::adopt(val); } +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/WebCore/dom/ScriptElement.h b/WebCore/dom/ScriptElement.h index 65706ea..d57fbde 100644 --- a/WebCore/dom/ScriptElement.h +++ b/WebCore/dom/ScriptElement.h @@ -29,6 +29,7 @@ namespace WebCore { class CachedScript; class Element; class ScriptElementData; +class ScriptSourceCode; class ScriptElement { public: @@ -77,7 +78,7 @@ public: void setHaveFiredLoadEvent(bool firedLoad) { m_firedLoad = firedLoad; } void requestScript(const String& sourceUrl); - void evaluateScript(const String& sourceUrl, const String& content); + void evaluateScript(const ScriptSourceCode&); void stopLoadRequest(); private: @@ -92,6 +93,8 @@ private: bool m_firedLoad; }; +ScriptElement* toScriptElement(Element*); + } #endif diff --git a/WebCore/dom/ScriptExecutionContext.cpp b/WebCore/dom/ScriptExecutionContext.cpp index 5bb076a..1d1aaec 100644 --- a/WebCore/dom/ScriptExecutionContext.cpp +++ b/WebCore/dom/ScriptExecutionContext.cpp @@ -28,31 +28,30 @@ #include "ScriptExecutionContext.h" #include "ActiveDOMObject.h" +#include "Document.h" #include "MessagePort.h" -#include "Timer.h" +#include "SecurityOrigin.h" +#include "WorkerContext.h" +#include "WorkerThread.h" +#include <wtf/MainThread.h> #include <wtf/PassRefPtr.h> namespace WebCore { -class MessagePortTimer : public TimerBase { +class ProcessMessagesSoonTask : public ScriptExecutionContext::Task { public: - MessagePortTimer(PassRefPtr<ScriptExecutionContext> context) - : m_context(context) + static PassRefPtr<ProcessMessagesSoonTask> create() { + return adoptRef(new ProcessMessagesSoonTask); } -private: - virtual void fired() + virtual void performTask(ScriptExecutionContext* context) { - m_context->dispatchMessagePortEvents(); - delete this; + context->dispatchMessagePortEvents(); } - - RefPtr<ScriptExecutionContext> m_context; }; ScriptExecutionContext::ScriptExecutionContext() - : m_firedMessagePortTimer(false) { } @@ -73,13 +72,7 @@ ScriptExecutionContext::~ScriptExecutionContext() void ScriptExecutionContext::processMessagePortMessagesSoon() { - if (m_firedMessagePortTimer) - return; - - MessagePortTimer* timer = new MessagePortTimer(this); - timer->startOneShot(0); - - m_firedMessagePortTimer = true; + postTask(ProcessMessagesSoonTask::create()); } void ScriptExecutionContext::dispatchMessagePortEvents() @@ -90,11 +83,11 @@ void ScriptExecutionContext::dispatchMessagePortEvents() Vector<MessagePort*> ports; copyToVector(m_messagePorts, ports); - m_firedMessagePortTimer = false; - 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->queueIsOpen()) port->dispatchMessages(); } @@ -103,17 +96,60 @@ void ScriptExecutionContext::dispatchMessagePortEvents() 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() +{ + // 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(); + } +} + +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); @@ -134,5 +170,13 @@ void ScriptExecutionContext::destroyedActiveDOMObject(ActiveDOMObject* object) m_activeDOMObjects.remove(object); } +void ScriptExecutionContext::setSecurityOrigin(PassRefPtr<SecurityOrigin> securityOrigin) +{ + m_securityOrigin = securityOrigin; +} + +ScriptExecutionContext::Task::~Task() +{ +} } // namespace WebCore diff --git a/WebCore/dom/ScriptExecutionContext.h b/WebCore/dom/ScriptExecutionContext.h index 565dbe0..6f09e1a 100644 --- a/WebCore/dom/ScriptExecutionContext.h +++ b/WebCore/dom/ScriptExecutionContext.h @@ -27,17 +27,26 @@ #ifndef ScriptExecutionContext_h #define ScriptExecutionContext_h +#include "Console.h" +#include "KURL.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> namespace WebCore { class ActiveDOMObject; class MessagePort; - class KURL; class SecurityOrigin; + class ScriptString; class String; + enum MessageDestination { + InspectorControllerDestination, + ConsoleDestination, + }; + class ScriptExecutionContext { public: ScriptExecutionContext(); @@ -47,14 +56,27 @@ namespace WebCore { virtual bool isWorkerContext() const { return false; } const KURL& url() const { return virtualURL(); } + KURL completeURL(const String& url) const { return virtualCompleteURL(url); } + + SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); } + + virtual void reportException(const String& errorMessage, int lineNumber, const String& sourceURL) = 0; + virtual void addMessage(MessageDestination, MessageSource, MessageLevel, const String& message, unsigned lineNumber, const String& sourceURL) = 0; + virtual void resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString) = 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(); + void resumeActiveDOMObjects(); void stopActiveDOMObjects(); void createdActiveDOMObject(ActiveDOMObject*, void* upcastPointer); void destroyedActiveDOMObject(ActiveDOMObject*); - const HashMap<ActiveDOMObject*, void*>& activeDOMObjects() const { return m_activeDOMObjects; } + 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, and for cross-heap GC support. + // MessagePort is conceptually a kind of ActiveDOMObject, but it needs to be tracked separately for message dispatch. void processMessagePortMessagesSoon(); void dispatchMessagePortEvents(); void createdMessagePort(MessagePort*); @@ -64,10 +86,26 @@ namespace WebCore { void ref() { refScriptExecutionContext(); } void deref() { derefScriptExecutionContext(); } + class Task : public ThreadSafeShared<Task> { + public: + virtual ~Task(); + virtual void performTask(ScriptExecutionContext*) = 0; + }; + + virtual void postTask(PassRefPtr<Task>) = 0; // Executes the task on context's thread asynchronously. + + 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; + + RefPtr<SecurityOrigin> m_securityOrigin; - bool m_firedMessagePortTimer; HashSet<MessagePort*> m_messagePorts; HashMap<ActiveDOMObject*, void*> m_activeDOMObjects; diff --git a/WebCore/dom/SelectorNodeList.cpp b/WebCore/dom/SelectorNodeList.cpp index 6a4284d..85b1238 100644 --- a/WebCore/dom/SelectorNodeList.cpp +++ b/WebCore/dom/SelectorNodeList.cpp @@ -30,6 +30,7 @@ #include "SelectorNodeList.h" #include "CSSSelector.h" +#include "CSSSelectorList.h" #include "CSSStyleSelector.h" #include "Document.h" #include "Element.h" @@ -39,25 +40,25 @@ namespace WebCore { using namespace HTMLNames; -PassRefPtr<StaticNodeList> createSelectorNodeList(Node* rootNode, CSSSelector* querySelector) +PassRefPtr<StaticNodeList> createSelectorNodeList(Node* rootNode, const CSSSelectorList& querySelectorList) { Vector<RefPtr<Node> > nodes; Document* document = rootNode->document(); - AtomicString selectorValue = querySelector->m_value; + CSSSelector* onlySelector = querySelectorList.hasOneSelector() ? querySelectorList.first() : 0; bool strictParsing = !document->inCompatMode(); CSSStyleSelector::SelectorChecker selectorChecker(document, strictParsing); - if (strictParsing && querySelector->m_match == CSSSelector::Id && rootNode->inDocument() && !querySelector->next() && !document->containsMultipleElementsWithId(selectorValue)) { - ASSERT(querySelector->m_attr == idAttr); - Element* element = document->getElementById(selectorValue); - if (element && (rootNode->isDocumentNode() || element->isDescendantOf(rootNode)) && selectorChecker.checkSelector(querySelector, element)) + if (strictParsing && rootNode->inDocument() && onlySelector && onlySelector->m_match == CSSSelector::Id && !document->containsMultipleElementsWithId(onlySelector->m_value)) { + ASSERT(querySelectorList.first()->attribute() == idAttr); + 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 = querySelector; selector; selector = selector->next()) { + for (CSSSelector* selector = querySelectorList.first(); selector; selector = CSSSelectorList::next(selector)) { if (selectorChecker.checkSelector(selector, element)) { nodes.append(n); break; diff --git a/WebCore/dom/SelectorNodeList.h b/WebCore/dom/SelectorNodeList.h index 58a00b1..8240189 100644 --- a/WebCore/dom/SelectorNodeList.h +++ b/WebCore/dom/SelectorNodeList.h @@ -33,9 +33,9 @@ namespace WebCore { - class CSSSelector; + class CSSSelectorList; - PassRefPtr<StaticNodeList> createSelectorNodeList(Node* rootNode, CSSSelector*); + PassRefPtr<StaticNodeList> createSelectorNodeList(Node* rootNode, const CSSSelectorList&); } // namespace WebCore diff --git a/WebCore/dom/StaticStringList.cpp b/WebCore/dom/StaticStringList.cpp new file mode 100644 index 0000000..a6de92a --- /dev/null +++ b/WebCore/dom/StaticStringList.cpp @@ -0,0 +1,67 @@ +/* + * 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 "StaticStringList.h" + +namespace WebCore { + +StaticStringList::StaticStringList(const Vector<String>& strings) + : m_strings(strings) +{ +} + +StaticStringList::StaticStringList() +{ +} + +StaticStringList::~StaticStringList() +{ +} + +unsigned StaticStringList::length() const +{ + return m_strings.size(); +} + +String StaticStringList::item(unsigned index) const +{ + if (index >= m_strings.size()) + return ""; + return m_strings[index]; +} + +bool StaticStringList::contains(const String& str) const +{ + size_t count = m_strings.size(); + for (size_t i = 0; i < count; ++i) { + if (m_strings[i] == str) + return true; + } + return false; +} + +} // namespace WebCore diff --git a/WebCore/dom/StaticStringList.h b/WebCore/dom/StaticStringList.h new file mode 100644 index 0000000..5b13e87 --- /dev/null +++ b/WebCore/dom/StaticStringList.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 StaticStringList_h +#define StaticStringList_h + +#include "DOMStringList.h" + +namespace WebCore { + + class StaticStringList : public DOMStringList { + public: + static PassRefPtr<StaticStringList> create(const Vector<String>& strings) + { + return adoptRef(new StaticStringList(strings)); + } + static PassRefPtr<StaticStringList> adopt(Vector<String>& strings) + { + StaticStringList* newList = new StaticStringList; + newList->m_strings.swap(strings); + return adoptRef(newList); + } + virtual ~StaticStringList(); + + virtual unsigned length() const; + virtual String item(unsigned) const; + virtual bool contains(const String&) const; + + private: + StaticStringList(const Vector<String>&); + StaticStringList(); + + Vector<String> m_strings; + }; + +} // namespace WebCore + +#endif // StaticStringList diff --git a/WebCore/dom/StyleElement.cpp b/WebCore/dom/StyleElement.cpp index e8882e5..68c3ec7 100644 --- a/WebCore/dom/StyleElement.cpp +++ b/WebCore/dom/StyleElement.cpp @@ -40,7 +40,7 @@ StyleSheet* StyleElement::sheet(Element* e) return m_sheet.get(); } -void StyleElement::insertedIntoDocument(Document* document, Element* element) +void StyleElement::insertedIntoDocument(Document*, Element* element) { process(element); } diff --git a/WebCore/dom/StyledElement.cpp b/WebCore/dom/StyledElement.cpp index 351c5eb..d5af1b7 100644 --- a/WebCore/dom/StyledElement.cpp +++ b/WebCore/dom/StyledElement.cpp @@ -29,6 +29,7 @@ #include "CSSValueKeywords.h" #include "Document.h" #include "HTMLNames.h" +#include <wtf/HashFunctions.h> using namespace std; @@ -424,15 +425,11 @@ void StyledElement::createMappedDecl(MappedAttribute* attr) decl->setStrictParsing(false); // Mapped attributes are just always quirky. } -// Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's -// or anything like that. -const unsigned PHI = 0x9e3779b9U; - // Paul Hsieh's SuperFastHash // http://www.azillionmonkeys.com/qed/hash.html unsigned MappedAttributeHash::hash(const MappedAttributeKey& key) { - uint32_t hash = PHI; + uint32_t hash = WTF::stringHashingStartValue; uint32_t tmp; const uint16_t* p; @@ -499,4 +496,10 @@ void StyledElement::copyNonAttributeProperties(const Element *sourceElement) Element::copyNonAttributeProperties(sourceElement); } +void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + if (CSSMutableStyleDeclaration* style = inlineStyleDecl()) + style->addSubresourceStyleURLs(urls); +} + } diff --git a/WebCore/dom/StyledElement.h b/WebCore/dom/StyledElement.h index f22a5e8..4ca48e9 100644 --- a/WebCore/dom/StyledElement.h +++ b/WebCore/dom/StyledElement.h @@ -80,6 +80,8 @@ public: virtual void copyNonAttributeProperties(const Element*); + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + protected: // classAttributeChanged() exists to share code between // parseMappedAttribute (called via setAttribute()) and diff --git a/WebCore/dom/TagNodeList.cpp b/WebCore/dom/TagNodeList.cpp index ac5acdf..bec9b8e 100644 --- a/WebCore/dom/TagNodeList.cpp +++ b/WebCore/dom/TagNodeList.cpp @@ -29,8 +29,8 @@ namespace WebCore { -TagNodeList::TagNodeList(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName) - : DynamicNodeList(rootNode) +TagNodeList::TagNodeList(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName, DynamicNodeList::Caches* caches) + : DynamicNodeList(rootNode, caches) , m_namespaceURI(namespaceURI) , m_localName(localName) { diff --git a/WebCore/dom/TagNodeList.h b/WebCore/dom/TagNodeList.h index d2a3096..0031bad 100644 --- a/WebCore/dom/TagNodeList.h +++ b/WebCore/dom/TagNodeList.h @@ -32,13 +32,13 @@ 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) + static PassRefPtr<TagNodeList> create(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName, DynamicNodeList::Caches* caches) { - return adoptRef(new TagNodeList(rootNode, namespaceURI, localName)); + return adoptRef(new TagNodeList(rootNode, namespaceURI, localName, caches)); } private: - TagNodeList(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName); + TagNodeList(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName, DynamicNodeList::Caches* caches); virtual bool nodeMatches(Element*) const; diff --git a/WebCore/dom/Text.cpp b/WebCore/dom/Text.cpp index 723997e..6277ff6 100644 --- a/WebCore/dom/Text.cpp +++ b/WebCore/dom/Text.cpp @@ -23,26 +23,30 @@ #include "Text.h" #include "CString.h" -#include "Document.h" #include "ExceptionCode.h" #include "RenderText.h" #include "TextBreakIterator.h" #if ENABLE(SVG) #include "RenderSVGInlineText.h" -#endif // ENABLE(SVG) +#endif + +#if ENABLE(WML) +#include "WMLDocument.h" +#include "WMLVariables.h" +#endif namespace WebCore { // DOM Section 1.1.1 Text::Text(Document* document, const String& text) - : CharacterData(document, text) + : CharacterData(document, text, true) { } Text::Text(Document* document) - : CharacterData(document) + : CharacterData(document, true) { } @@ -76,7 +80,7 @@ PassRefPtr<Text> Text::splitText(unsigned offset, ExceptionCode& ec) document()->textNodeSplit(this); if (renderer()) - static_cast<RenderText*>(renderer())->setText(m_data); + toRenderText(renderer())->setText(m_data); return newText.release(); } @@ -205,7 +209,7 @@ bool Text::rendererIsNeeded(RenderStyle *style) if (prev && prev->isBR()) // <span><br/> <br/></span> return false; - if (par->isInlineFlow()) { + if (par->isRenderInline()) { // <span><div/> <div/></span> if (prev && !prev->isInline()) return false; @@ -226,12 +230,12 @@ bool Text::rendererIsNeeded(RenderStyle *style) return true; } -RenderObject *Text::createRenderer(RenderArena *arena, RenderStyle *style) +RenderObject *Text::createRenderer(RenderArena* arena, RenderStyle*) { #if ENABLE(SVG) if (parentNode()->isSVGElement()) return new (arena) RenderSVGInlineText(this, m_data); -#endif // ENABLE(SVG) +#endif return new (arena) RenderText(this, m_data); } @@ -251,7 +255,7 @@ void Text::recalcStyle(StyleChange change) if (changed()) { if (renderer()) { if (renderer()->isText()) - static_cast<RenderText*>(renderer())->setText(m_data); + toRenderText(renderer())->setText(m_data); } else { if (attached()) detach(); @@ -297,6 +301,29 @@ PassRefPtr<Text> Text::createWithLengthLimit(Document* doc, const String& text, return new Text(doc, nodeText); } +#if ENABLE(WML) +void Text::insertedIntoDocument() +{ + CharacterData::insertedIntoDocument(); + + if (!parentNode()->isWMLElement() || !length()) + return; + + WMLPageState* pageState = wmlPageStateForDocument(document()); + if (!pageState->hasVariables()) + return; + + String text = data(); + if (!text.impl() || text.impl()->containsOnlyWhitespace()) + return; + + text = substituteVariableReferences(text, document()); + + ExceptionCode ec; + setData(text, ec); +} +#endif + #ifndef NDEBUG void Text::formatForDebugger(char *buffer, unsigned length) const { diff --git a/WebCore/dom/Text.h b/WebCore/dom/Text.h index 70fdd15..5e711d0 100644 --- a/WebCore/dom/Text.h +++ b/WebCore/dom/Text.h @@ -51,7 +51,6 @@ public: // Other methods (not part of DOM) - virtual bool isTextNode() const { return true; } virtual void attach(); virtual bool rendererIsNeeded(RenderStyle*); virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); @@ -60,6 +59,10 @@ public: static PassRefPtr<Text> createWithLengthLimit(Document*, const String&, unsigned& charsLeft, unsigned maxChars = cTextNodeLengthLimit); +#if ENABLE(WML) + virtual void insertedIntoDocument(); +#endif + #ifndef NDEBUG virtual void formatForDebugger(char* buffer, unsigned length) const; #endif diff --git a/WebCore/dom/Tokenizer.h b/WebCore/dom/Tokenizer.h index 1dfaca2..1ed9484 100644 --- a/WebCore/dom/Tokenizer.h +++ b/WebCore/dom/Tokenizer.h @@ -31,12 +31,6 @@ namespace WebCore { class Tokenizer { public: - Tokenizer(bool viewSourceMode = false) - : m_parserStopped(false) - , m_inViewSourceMode(viewSourceMode) - { - } - virtual ~Tokenizer() { } // Script output must be prepended, while new data @@ -51,7 +45,7 @@ namespace WebCore { virtual int executingScript() const { return 0; } virtual bool wantsRawData() const { return false; } - virtual bool writeRawData(const char* data, int len) { return false; } + virtual bool writeRawData(const char* /*data*/, int /*length*/) { return false; } bool inViewSourceMode() const { return m_inViewSourceMode; } void setInViewSourceMode(bool mode) { m_inViewSourceMode = mode; } @@ -66,6 +60,12 @@ namespace WebCore { virtual bool isHTMLTokenizer() const { return false; } protected: + Tokenizer(bool viewSourceMode = false) + : m_parserStopped(false) + , m_inViewSourceMode(viewSourceMode) + { + } + // The tokenizer has buffers, so parsing may continue even after // it stops receiving data. We use m_parserStopped to stop the tokenizer // even when it has buffered data. diff --git a/WebCore/dom/Traversal.cpp b/WebCore/dom/Traversal.cpp index c2c91d4..5d56deb 100644 --- a/WebCore/dom/Traversal.cpp +++ b/WebCore/dom/Traversal.cpp @@ -28,8 +28,6 @@ #include "Node.h" #include "NodeFilter.h" -using namespace JSC; - namespace WebCore { Traversal::Traversal(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> nodeFilter, bool expandEntityReferences) @@ -40,7 +38,7 @@ Traversal::Traversal(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr< { } -short Traversal::acceptNode(ExecState* exec, Node* node) const +short Traversal::acceptNode(ScriptState* state, Node* node) const { // FIXME: To handle XML properly we would have to check m_expandEntityReferences. @@ -50,7 +48,7 @@ short Traversal::acceptNode(ExecState* exec, Node* node) const return NodeFilter::FILTER_SKIP; if (!m_filter) return NodeFilter::FILTER_ACCEPT; - return m_filter->acceptNode(exec, node); + return m_filter->acceptNode(state, node); } } // namespace WebCore diff --git a/WebCore/dom/Traversal.h b/WebCore/dom/Traversal.h index 6e87103..fc4403f 100644 --- a/WebCore/dom/Traversal.h +++ b/WebCore/dom/Traversal.h @@ -25,13 +25,10 @@ #ifndef Traversal_h #define Traversal_h +#include "ScriptState.h" #include <wtf/Forward.h> #include <wtf/RefPtr.h> -namespace JSC { - class ExecState; -} - namespace WebCore { class Node; @@ -46,7 +43,7 @@ namespace WebCore { protected: Traversal(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); - short acceptNode(JSC::ExecState*, Node*) const; + short acceptNode(ScriptState*, Node*) const; private: RefPtr<Node> m_root; diff --git a/WebCore/dom/TreeWalker.cpp b/WebCore/dom/TreeWalker.cpp index 7960fae..de06363 100644 --- a/WebCore/dom/TreeWalker.cpp +++ b/WebCore/dom/TreeWalker.cpp @@ -25,14 +25,12 @@ #include "config.h" #include "TreeWalker.h" -#include <runtime/ExecState.h> #include "ExceptionCode.h" #include "ContainerNode.h" #include "NodeFilter.h" +#include "ScriptState.h" #include <wtf/PassRefPtr.h> -using namespace JSC; - namespace WebCore { TreeWalker::TreeWalker(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) @@ -56,15 +54,15 @@ inline Node* TreeWalker::setCurrent(PassRefPtr<Node> node) return m_current.get(); } -Node* TreeWalker::parentNode(ExecState* exec) +Node* TreeWalker::parentNode(ScriptState* state) { RefPtr<Node> node = m_current; while (node != root()) { node = node->parentNode(); if (!node) return 0; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) return 0; if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) return setCurrent(node.release()); @@ -72,11 +70,11 @@ Node* TreeWalker::parentNode(ExecState* exec) return 0; } -Node* TreeWalker::firstChild(ExecState* exec) +Node* TreeWalker::firstChild(ScriptState* state) { for (RefPtr<Node> node = m_current->firstChild(); node; ) { - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) return 0; switch (acceptNodeResult) { case NodeFilter::FILTER_ACCEPT: @@ -105,11 +103,11 @@ Node* TreeWalker::firstChild(ExecState* exec) return 0; } -Node* TreeWalker::lastChild(ExecState* exec) +Node* TreeWalker::lastChild(ScriptState* state) { for (RefPtr<Node> node = m_current->lastChild(); node; ) { - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) return 0; switch (acceptNodeResult) { case NodeFilter::FILTER_ACCEPT: @@ -138,15 +136,15 @@ Node* TreeWalker::lastChild(ExecState* exec) return 0; } -Node* TreeWalker::previousSibling(ExecState* exec) +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(exec, sibling.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, sibling.get()); + if (state && state->hadException()) return 0; switch (acceptNodeResult) { case NodeFilter::FILTER_ACCEPT: @@ -166,23 +164,23 @@ Node* TreeWalker::previousSibling(ExecState* exec) node = node->parentNode(); if (!node || node == root()) return 0; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) return 0; if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) return 0; } } -Node* TreeWalker::nextSibling(ExecState* exec) +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(exec, sibling.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, sibling.get()); + if (state && state->hadException()) return 0; switch (acceptNodeResult) { case NodeFilter::FILTER_ACCEPT: @@ -202,29 +200,29 @@ Node* TreeWalker::nextSibling(ExecState* exec) node = node->parentNode(); if (!node || node == root()) return 0; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) return 0; if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) return 0; } } -Node* TreeWalker::previousNode(ExecState* exec) +Node* TreeWalker::previousNode(ScriptState* state) { RefPtr<Node> node = m_current; while (node != root()) { while (Node* previousSibling = node->previousSibling()) { node = previousSibling; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) + 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(exec, node.get()); - if (exec && exec->hadException()) + acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) return 0; if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) continue; @@ -240,8 +238,8 @@ Node* TreeWalker::previousNode(ExecState* exec) if (!parent) return 0; node = parent; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) return 0; if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) return setCurrent(node.release()); @@ -249,14 +247,14 @@ Node* TreeWalker::previousNode(ExecState* exec) return 0; } -Node* TreeWalker::nextNode(ExecState* exec) +Node* TreeWalker::nextNode(ScriptState* state) { RefPtr<Node> node = m_current; Children: while (Node* firstChild = node->firstChild()) { node = firstChild; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) return 0; if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) return setCurrent(node.release()); @@ -265,8 +263,8 @@ Children: } while (Node* nextSibling = node->traverseNextSibling(root())) { node = nextSibling; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) + short acceptNodeResult = acceptNode(state, node.get()); + if (state && state->hadException()) return 0; if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) return setCurrent(node.release()); diff --git a/WebCore/dom/TreeWalker.h b/WebCore/dom/TreeWalker.h index 348ff94..d06acb9 100644 --- a/WebCore/dom/TreeWalker.h +++ b/WebCore/dom/TreeWalker.h @@ -45,22 +45,22 @@ namespace WebCore { Node* currentNode() const { return m_current.get(); } void setCurrentNode(PassRefPtr<Node>, ExceptionCode&); - Node* parentNode(JSC::ExecState*); - Node* firstChild(JSC::ExecState*); - Node* lastChild(JSC::ExecState*); - Node* previousSibling(JSC::ExecState*); - Node* nextSibling(JSC::ExecState*); - Node* previousNode(JSC::ExecState*); - Node* nextNode(JSC::ExecState*); + Node* parentNode(ScriptState*); + Node* firstChild(ScriptState*); + Node* lastChild(ScriptState*); + Node* previousSibling(ScriptState*); + Node* nextSibling(ScriptState*); + Node* previousNode(ScriptState*); + Node* nextNode(ScriptState*); // For non-JS bindings. Silently ignores the JavaScript exception if any. - Node* parentNode() { return parentNode(execStateFromNode(m_current.get())); } - Node* firstChild() { return firstChild(execStateFromNode(m_current.get())); } - Node* lastChild() { return lastChild(execStateFromNode(m_current.get())); } - Node* previousSibling() { return previousSibling(execStateFromNode(m_current.get())); } - Node* nextSibling() { return nextSibling(execStateFromNode(m_current.get())); } - Node* previousNode() { return previousNode(execStateFromNode(m_current.get())); } - Node* nextNode() { return nextNode(execStateFromNode(m_current.get())); } + Node* parentNode() { return parentNode(scriptStateFromNode(m_current.get())); } + Node* firstChild() { return firstChild(scriptStateFromNode(m_current.get())); } + Node* lastChild() { return lastChild(scriptStateFromNode(m_current.get())); } + Node* previousSibling() { return previousSibling(scriptStateFromNode(m_current.get())); } + Node* nextSibling() { return nextSibling(scriptStateFromNode(m_current.get())); } + Node* previousNode() { return previousNode(scriptStateFromNode(m_current.get())); } + Node* nextNode() { return nextNode(scriptStateFromNode(m_current.get())); } private: TreeWalker(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); diff --git a/WebCore/dom/UIEvent.idl b/WebCore/dom/UIEvent.idl index de83a2a..36d0988 100644 --- a/WebCore/dom/UIEvent.idl +++ b/WebCore/dom/UIEvent.idl @@ -32,7 +32,7 @@ module events { in DOMWindow view, in long detail); - // extentsions + // extensions readonly attribute long keyCode; readonly attribute long charCode; readonly attribute long layerX; diff --git a/WebCore/dom/WebKitAnimationEvent.cpp b/WebCore/dom/WebKitAnimationEvent.cpp index ddaaf64..371ea69 100644 --- a/WebCore/dom/WebKitAnimationEvent.cpp +++ b/WebCore/dom/WebKitAnimationEvent.cpp @@ -47,17 +47,18 @@ WebKitAnimationEvent::~WebKitAnimationEvent() } void WebKitAnimationEvent::initWebKitAnimationEvent(const AtomicString& type, - bool /*canBubbleArg*/, - bool /*cancelableArg*/, + bool canBubbleArg, + bool cancelableArg, const String& animationName, double elapsedTime) { if (dispatched()) return; - initEvent(type, false, true); + initEvent(type, canBubbleArg, cancelableArg); m_animationName = animationName; + m_elapsedTime = elapsedTime; } const String& WebKitAnimationEvent::animationName() const diff --git a/WebCore/dom/WebKitAnimationEvent.idl b/WebCore/dom/WebKitAnimationEvent.idl index a19a874..f874995 100644 --- a/WebCore/dom/WebKitAnimationEvent.idl +++ b/WebCore/dom/WebKitAnimationEvent.idl @@ -25,7 +25,9 @@ module events { -interface WebKitAnimationEvent : Event { + interface [ + GenerateConstructor + ] WebKitAnimationEvent : Event { readonly attribute DOMString animationName; readonly attribute double elapsedTime; void initWebKitAnimationEvent(in DOMString typeArg, diff --git a/WebCore/dom/WebKitTransitionEvent.cpp b/WebCore/dom/WebKitTransitionEvent.cpp index f4bdbf5..f0e86de 100644 --- a/WebCore/dom/WebKitTransitionEvent.cpp +++ b/WebCore/dom/WebKitTransitionEvent.cpp @@ -48,17 +48,18 @@ WebKitTransitionEvent::~WebKitTransitionEvent() } void WebKitTransitionEvent::initWebKitTransitionEvent(const AtomicString& type, - bool /*canBubbleArg*/, - bool /*cancelableArg*/, + bool canBubbleArg, + bool cancelableArg, const String& propertyName, double elapsedTime) { if (dispatched()) return; - initEvent(type, false, true); + initEvent(type, canBubbleArg, cancelableArg); m_propertyName = propertyName; + m_elapsedTime = elapsedTime; } const String& WebKitTransitionEvent::propertyName() const diff --git a/WebCore/dom/WebKitTransitionEvent.idl b/WebCore/dom/WebKitTransitionEvent.idl index 9e80dae..8c645cb 100644 --- a/WebCore/dom/WebKitTransitionEvent.idl +++ b/WebCore/dom/WebKitTransitionEvent.idl @@ -25,7 +25,9 @@ module events { -interface WebKitTransitionEvent : Event { + interface [ + GenerateConstructor + ] WebKitTransitionEvent : Event { readonly attribute DOMString propertyName; readonly attribute double elapsedTime; void initWebKitTransitionEvent(in DOMString typeArg, diff --git a/WebCore/dom/Worker.cpp b/WebCore/dom/Worker.cpp new file mode 100644 index 0000000..6889a42 --- /dev/null +++ b/WebCore/dom/Worker.cpp @@ -0,0 +1,203 @@ +/* + * 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" + +#if ENABLE(WORKERS) + +#include "Worker.h" + +#include "CachedScript.h" +#include "DOMWindow.h" +#include "DocLoader.h" +#include "Document.h" +#include "EventException.h" +#include "EventListener.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "MessageEvent.h" +#include "SecurityOrigin.h" +#include "WorkerContext.h" +#include "WorkerMessagingProxy.h" +#include "WorkerTask.h" +#include "WorkerThread.h" +#include <wtf/MainThread.h> + +namespace WebCore { + +Worker::Worker(const String& url, Document* doc, ExceptionCode& ec) + : ActiveDOMObject(doc, this) + , m_messagingProxy(new WorkerMessagingProxy(doc, this)) +{ + m_scriptURL = doc->completeURL(url); + if (url.isEmpty() || !m_scriptURL.isValid()) { + ec = SYNTAX_ERR; + return; + } + + if (!doc->securityOrigin()->canAccess(SecurityOrigin::create(m_scriptURL).get())) { + ec = SECURITY_ERR; + return; + } + + m_cachedScript = doc->docLoader()->requestScript(m_scriptURL, document()->charset()); + if (!m_cachedScript) { + dispatchErrorEvent(); + return; + } + + setPendingActivity(this); // The worker context does not exist while loading, so we much ensure that the worker object is not collected, as well as its event listeners. + m_cachedScript->addClient(this); +} + +Worker::~Worker() +{ + ASSERT(isMainThread()); + ASSERT(scriptExecutionContext()); // The context is protected by messaging proxy, so it cannot be destroyed while a Worker exists. + m_messagingProxy->workerObjectDestroyed(); +} + +Document* Worker::document() const +{ + ASSERT(scriptExecutionContext()->isDocument()); + return static_cast<Document*>(scriptExecutionContext()); +} + +void Worker::postMessage(const String& message) +{ + m_messagingProxy->postMessageToWorkerContext(message); +} + +void Worker::terminate() +{ + m_messagingProxy->terminate(); +} + +bool Worker::canSuspend() const +{ + // FIXME: It is not currently possible to suspend a worker, so pages with workers can not go into page cache. + return false; +} + +void Worker::stop() +{ + terminate(); +} + +bool Worker::hasPendingActivity() const +{ + return m_messagingProxy->workerThreadHasPendingActivity() || ActiveDOMObject::hasPendingActivity(); +} + +void Worker::notifyFinished(CachedResource* unusedResource) +{ + ASSERT_UNUSED(unusedResource, unusedResource == m_cachedScript); + + if (m_cachedScript->errorOccurred()) + dispatchErrorEvent(); + else { + String userAgent = document()->frame() ? document()->frame()->loader()->userAgent(m_scriptURL) : String(); + RefPtr<WorkerThread> thread = WorkerThread::create(m_scriptURL, userAgent, m_cachedScript->script(), m_messagingProxy); + m_messagingProxy->workerThreadCreated(thread); + thread->start(); + } + + m_cachedScript->removeClient(this); + m_cachedScript = 0; + + unsetPendingActivity(this); +} + +void Worker::dispatchErrorEvent() +{ + RefPtr<Event> evt = Event::create(eventNames().errorEvent, false, true); + if (m_onErrorListener) { + evt->setTarget(this); + evt->setCurrentTarget(this); + m_onErrorListener->handleEvent(evt.get(), true); + } + + ExceptionCode ec = 0; + dispatchEvent(evt.release(), ec); + ASSERT(!ec); +} + +void Worker::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) { + ListenerVector listeners; + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } else { + ListenerVector& listeners = iter->second; + for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) + return; + } + + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } +} + +void Worker::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) + return; + + ListenerVector& listeners = iter->second; + for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) { + listeners.remove(listenerIter - listeners.begin()); + return; + } + } +} + +bool Worker::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) +{ + if (!event || event->type().isEmpty()) { + ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + return true; + } + + ListenerVector listenersCopy = m_eventListeners.get(event->type()); + for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) { + event->setTarget(this); + event->setCurrentTarget(this); + listenerIter->get()->handleEvent(event.get(), false); + } + + return !event->defaultPrevented(); +} + +} // namespace WebCore + +#endif // ENABLE(WORKERS) diff --git a/WebCore/dom/DedicatedWorker.h b/WebCore/dom/Worker.h index ed24112..6a47bac 100644 --- a/WebCore/dom/DedicatedWorker.h +++ b/WebCore/dom/Worker.h @@ -24,15 +24,17 @@ * */ -#ifndef DedicatedWorker_h -#define DedicatedWorker_h +#ifndef Worker_h +#define Worker_h #if ENABLE(WORKERS) +#include "AtomicStringHash.h" #include "ActiveDOMObject.h" #include "CachedResourceClient.h" #include "CachedResourceHandle.h" #include "EventListener.h" +#include "EventTarget.h" #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -43,51 +45,69 @@ namespace WebCore { class CachedScript; class Document; class ScriptExecutionContext; - class MessagePort; class String; + class WorkerMessagingProxy; + class WorkerTask; typedef int ExceptionCode; - class DedicatedWorker : public RefCounted<DedicatedWorker>, public ActiveDOMObject, private CachedResourceClient { + class Worker : public RefCounted<Worker>, public ActiveDOMObject, private CachedResourceClient, public EventTarget { public: - static PassRefPtr<DedicatedWorker> create(const String& url, Document* document, ExceptionCode& ec) { return adoptRef(new DedicatedWorker(url, document, ec)); } - ~DedicatedWorker(); + static PassRefPtr<Worker> create(const String& url, Document* document, ExceptionCode& ec) { return adoptRef(new Worker(url, document, ec)); } + ~Worker(); + virtual ScriptExecutionContext* scriptExecutionContext() const { return ActiveDOMObject::scriptExecutionContext(); } Document* document() const; - PassRefPtr<MessagePort> startConversation(ScriptExecutionContext*, const String& message); - void close(); - void postMessage(const String& message, MessagePort* port = 0); + virtual Worker* toWorker() { return this; } - void setOnMessageListener(PassRefPtr<EventListener> eventListener) { m_onMessageListener = eventListener; } - EventListener* onMessageListener() const { return m_onMessageListener.get(); } + void postMessage(const String& message); - void setOnCloseListener(PassRefPtr<EventListener> eventListener) { m_onCloseListener = eventListener; } - EventListener* onCloseListener() const { return m_onCloseListener.get(); } + void terminate(); - void setOnErrorListener(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; } - EventListener* onErrorListener() const { return m_onErrorListener.get(); } + virtual bool canSuspend() const; + virtual void stop(); + virtual bool hasPendingActivity() const; - private: - friend class WorkerThreadScriptLoadTimer; + virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); + + typedef Vector<RefPtr<EventListener> > ListenerVector; + typedef HashMap<AtomicString, ListenerVector> EventListenersMap; + EventListenersMap& eventListeners() { return m_eventListeners; } + + using RefCounted<Worker>::ref; + using RefCounted<Worker>::deref; + + void setOnmessage(PassRefPtr<EventListener> eventListener) { m_onMessageListener = eventListener; } + EventListener* onmessage() const { return m_onMessageListener.get(); } - DedicatedWorker(const String&, Document*, ExceptionCode&); + void setOnerror(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; } + EventListener* onerror() const { return m_onErrorListener.get(); } + + private: + Worker(const String&, Document*, ExceptionCode&); - void startLoad(); virtual void notifyFinished(CachedResource*); void dispatchErrorEvent(); + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + KURL m_scriptURL; CachedResourceHandle<CachedScript> m_cachedScript; + WorkerMessagingProxy* m_messagingProxy; // The proxy outlives the worker to perform thread shutdown. + RefPtr<EventListener> m_onMessageListener; - RefPtr<EventListener> m_onCloseListener; RefPtr<EventListener> m_onErrorListener; + EventListenersMap m_eventListeners; }; } // namespace WebCore #endif // ENABLE(WORKERS) -#endif // DedicatedWorker_h +#endif // Worker_h diff --git a/WebCore/dom/Worker.idl b/WebCore/dom/Worker.idl new file mode 100644 index 0000000..2ef9b62 --- /dev/null +++ b/WebCore/dom/Worker.idl @@ -0,0 +1,48 @@ +/* + * 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 threads { + + interface [CustomMarkFunction, Conditional=WORKERS] Worker { + + attribute EventListener onerror; + attribute EventListener onmessage; + void postMessage(in DOMString message); + + void terminate(); + + // EventTarget interface + [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 evt) + raises(EventException); + }; + +} diff --git a/WebCore/dom/WorkerContext.cpp b/WebCore/dom/WorkerContext.cpp new file mode 100644 index 0000000..fe4643e --- /dev/null +++ b/WebCore/dom/WorkerContext.cpp @@ -0,0 +1,223 @@ +/* + * 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" + +#if ENABLE(WORKERS) + +#include "WorkerContext.h" + +#include "ActiveDOMObject.h" +#include "DOMWindow.h" +#include "Event.h" +#include "EventException.h" +#include "GenericWorkerTask.h" +#include "NotImplemented.h" +#include "SecurityOrigin.h" +#include "WorkerLocation.h" +#include "WorkerMessagingProxy.h" +#include "WorkerNavigator.h" +#include "WorkerTask.h" +#include "WorkerThread.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +WorkerContext::WorkerContext(const KURL& url, const String& userAgent, WorkerThread* thread) + : m_url(url) + , m_userAgent(userAgent) + , m_location(WorkerLocation::create(url)) + , m_script(new WorkerScriptController(this)) + , m_thread(thread) +{ + setSecurityOrigin(SecurityOrigin::create(url)); +} + +WorkerContext::~WorkerContext() +{ + ASSERT(currentThread() == m_thread->threadID()); + + m_thread->messagingProxy()->workerContextDestroyed(); +} + +ScriptExecutionContext* WorkerContext::scriptExecutionContext() const +{ + return const_cast<WorkerContext*>(this); +} + +const KURL& WorkerContext::virtualURL() const +{ + return m_url; +} + +KURL WorkerContext::virtualCompleteURL(const String& url) const +{ + return completeURL(url); +} + +KURL WorkerContext::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? + if (url.isNull()) + return KURL(); + // FIXME: does this need to provide a charset, like Document::completeURL does? + return KURL(m_location->url(), url); +} + +WorkerNavigator* WorkerContext::navigator() const +{ + if (!m_navigator) + m_navigator = WorkerNavigator::create(m_userAgent); + return m_navigator.get(); +} + +bool WorkerContext::hasPendingActivity() const +{ + ActiveDOMObjectsMap& activeObjects = activeDOMObjects(); + ActiveDOMObjectsMap::const_iterator activeObjectsEnd = activeObjects.end(); + for (ActiveDOMObjectsMap::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) { + if (iter->first->hasPendingActivity()) + return true; + } + return false; +} + +void WorkerContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL) +{ + m_thread->messagingProxy()->postWorkerException(errorMessage, lineNumber, sourceURL); +} + +static void addMessageTask(ScriptExecutionContext* context, MessageDestination destination, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL) +{ + context->addMessage(destination, source, level, message, lineNumber, sourceURL); +} + +void WorkerContext::addMessage(MessageDestination destination, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL) +{ + // createCallbackTask has to be a separate statement from postTaskToParentContext to make the destructor + // for message.copy() get called before postTaskToParentContext. (If they are one statement, the destructor + // gets called after postTaskToParentContext which causes a race condition.) + RefPtr<Task> task = createCallbackTask(m_thread->messagingProxy(), &addMessageTask, destination, source, level, message.copy(), lineNumber, sourceURL.copy()); + postTaskToParentContext(task.release()); +} + +void WorkerContext::resourceRetrievedByXMLHttpRequest(unsigned long, const ScriptString&) +{ + // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175 + notImplemented(); +} + +void WorkerContext::postMessage(const String& message) +{ + m_thread->messagingProxy()->postMessageToWorkerObject(message); +} + +void WorkerContext::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) { + ListenerVector listeners; + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } else { + ListenerVector& listeners = iter->second; + for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) + return; + } + + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } +} + +void WorkerContext::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) + return; + + ListenerVector& listeners = iter->second; + for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) { + listeners.remove(listenerIter - listeners.begin()); + return; + } + } +} + +bool WorkerContext::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) +{ + if (!event || event->type().isEmpty()) { + ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + return true; + } + + ListenerVector listenersCopy = m_eventListeners.get(event->type()); + for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) { + event->setTarget(this); + event->setCurrentTarget(this); + listenerIter->get()->handleEvent(event.get(), false); + } + + return !event->defaultPrevented(); +} + +class ScriptExecutionContextTaskWorkerTask : public WorkerTask { +public: + static PassRefPtr<ScriptExecutionContextTaskWorkerTask> create(PassRefPtr<ScriptExecutionContext::Task> task) + { + return adoptRef(new ScriptExecutionContextTaskWorkerTask(task)); + } + +private: + ScriptExecutionContextTaskWorkerTask(PassRefPtr<ScriptExecutionContext::Task> task) + : m_task(task) + { + } + + virtual void performTask(WorkerContext* context) + { + m_task->performTask(context); + } + + RefPtr<ScriptExecutionContext::Task> m_task; +}; + +void WorkerContext::postTask(PassRefPtr<Task> task) +{ + thread()->runLoop().postTask(ScriptExecutionContextTaskWorkerTask::create(task)); +} + +void WorkerContext::postTaskToParentContext(PassRefPtr<Task> task) +{ + thread()->messagingProxy()->postTaskToParentContext(task); +} + +} // namespace WebCore + +#endif // ENABLE(WORKERS) diff --git a/WebCore/dom/WorkerContext.h b/WebCore/dom/WorkerContext.h new file mode 100644 index 0000000..2e443d9 --- /dev/null +++ b/WebCore/dom/WorkerContext.h @@ -0,0 +1,124 @@ +/* + * 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 WorkerContext_h +#define WorkerContext_h + +#if ENABLE(WORKERS) + +#include "AtomicStringHash.h" +#include "EventListener.h" +#include "EventTarget.h" +#include "ScriptExecutionContext.h" +#include "WorkerScriptController.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class WorkerLocation; + class WorkerNavigator; + class WorkerThread; + + class WorkerContext : public RefCounted<WorkerContext>, public ScriptExecutionContext, public EventTarget { + public: + static PassRefPtr<WorkerContext> create(const KURL& url, const String& userAgent, WorkerThread* thread) + { + return adoptRef(new WorkerContext(url, userAgent, thread)); + } + + virtual ~WorkerContext(); + + virtual bool isWorkerContext() const { return true; } + + virtual ScriptExecutionContext* scriptExecutionContext() const; + + const KURL& url() const { return m_url; } + KURL completeURL(const String&) const; + + WorkerLocation* location() const { return m_location.get(); } + WorkerNavigator* navigator() const; + + WorkerScriptController* script() { return m_script.get(); } + void clearScript() { return m_script.clear(); } + WorkerThread* thread() { return m_thread; } + + bool hasPendingActivity() const; + + virtual void reportException(const String& errorMessage, int lineNumber, const String& sourceURL); + virtual void addMessage(MessageDestination, MessageSource, MessageLevel, const String& message, unsigned lineNumber, const String& sourceURL); + virtual void resourceRetrievedByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString); + + virtual WorkerContext* toWorkerContext() { return this; } + + void postMessage(const String& message); + virtual void postTask(PassRefPtr<Task>); // Executes the task on context's thread asynchronously. + void postTaskToParentContext(PassRefPtr<Task>); // Executes the task in the parent's context (and thread) asynchronously. + + virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); + + void setOnmessage(PassRefPtr<EventListener> eventListener) { m_onmessageListener = eventListener; } + EventListener* onmessage() const { return m_onmessageListener.get(); } + + typedef Vector<RefPtr<EventListener> > ListenerVector; + typedef HashMap<AtomicString, ListenerVector> EventListenersMap; + EventListenersMap& eventListeners() { return m_eventListeners; } + + using RefCounted<WorkerContext>::ref; + using RefCounted<WorkerContext>::deref; + + private: + virtual void refScriptExecutionContext() { ref(); } + virtual void derefScriptExecutionContext() { deref(); } + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + + WorkerContext(const KURL&, const String&, WorkerThread*); + + virtual const KURL& virtualURL() const; + virtual KURL virtualCompleteURL(const String&) const; + + KURL m_url; + String m_userAgent; + RefPtr<WorkerLocation> m_location; + mutable RefPtr<WorkerNavigator> m_navigator; + + OwnPtr<WorkerScriptController> m_script; + WorkerThread* m_thread; + + RefPtr<EventListener> m_onmessageListener; + EventListenersMap m_eventListeners; + }; + +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // WorkerContext_h diff --git a/WebCore/dom/WorkerContext.idl b/WebCore/dom/WorkerContext.idl new file mode 100644 index 0000000..6e7223c --- /dev/null +++ b/WebCore/dom/WorkerContext.idl @@ -0,0 +1,61 @@ +/* + * 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 threads { + + interface [ + Conditional=WORKERS, + CustomGetOwnPropertySlot, + CustomMarkFunction, + ExtendsDOMGlobalObject, + LegacyParent=JSWorkerContextBase, + NoStaticTables + ] WorkerContext { +#if defined(LANGUAGE_JAVASCRIPT) + attribute [Custom] WorkerContext self; +#endif + + attribute EventListener onmessage; + void postMessage(in DOMString message); + + attribute [Replaceable] WorkerLocation location; + attribute [Replaceable] WorkerNavigator navigator; + + attribute MessageEventConstructor MessageEvent; + attribute WorkerLocationConstructor WorkerLocation; + + // EventTarget interface + [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 evt) + raises(EventException); + }; + +} diff --git a/WebCore/dom/WorkerLocation.cpp b/WebCore/dom/WorkerLocation.cpp new file mode 100644 index 0000000..115a9ad --- /dev/null +++ b/WebCore/dom/WorkerLocation.cpp @@ -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. + * + */ + +#include "config.h" + +#if ENABLE(WORKERS) + +#include "WorkerLocation.h" + +#include "PlatformString.h" + +namespace WebCore { + +String WorkerLocation::href() const +{ + return m_url.hasPath() ? m_url.prettyURL() : m_url.prettyURL() + "/"; +} + +String WorkerLocation::protocol() const +{ + return m_url.protocol() + ":"; +} + +String WorkerLocation::host() const +{ + return m_url.port() ? m_url.host() + ":" + String::number((static_cast<int>(m_url.port()))) : m_url.host(); +} + +String WorkerLocation::hostname() const +{ + return m_url.host(); +} + +String WorkerLocation::port() const +{ + return m_url.port() ? String::number(static_cast<int>(m_url.port())) : ""; +} + +String WorkerLocation::pathname() const +{ + return m_url.path().isEmpty() ? "/" : m_url.path(); +} + +String WorkerLocation::search() const +{ + return m_url.query(); +} + +String WorkerLocation::hash() const +{ + return m_url.ref().isNull() ? "" : "#" + m_url.ref(); +} + +String WorkerLocation::toString() const +{ + return m_url.hasPath() ? m_url.prettyURL() : m_url.prettyURL() + "/"; +} + + +} // namespace WebCore + +#endif // ENABLE(WORKERS) diff --git a/WebCore/dom/WorkerLocation.h b/WebCore/dom/WorkerLocation.h new file mode 100644 index 0000000..52c31ad --- /dev/null +++ b/WebCore/dom/WorkerLocation.h @@ -0,0 +1,73 @@ +/* + * 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 WorkerLocation_h +#define WorkerLocation_h + +#if ENABLE(WORKERS) + +#include "KURL.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class String; + + class WorkerLocation : public RefCounted<WorkerLocation> { + public: + static PassRefPtr<WorkerLocation> create(const KURL& url) + { + return adoptRef(new WorkerLocation(url)); + } + + const KURL& url() const { return m_url; } + + String href() const; + + // URI decomposition attributes + String protocol() const; + String host() const; + String hostname() const; + String port() const; + String pathname() const; + String search() const; + String hash() const; + + String toString() const; + + private: + WorkerLocation(const KURL& url) : m_url(url) { } + + KURL m_url; + }; + +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // WorkerLocation_h diff --git a/WebCore/dom/WorkerLocation.idl b/WebCore/dom/WorkerLocation.idl new file mode 100644 index 0000000..5551f18 --- /dev/null +++ b/WebCore/dom/WorkerLocation.idl @@ -0,0 +1,48 @@ +/* + * 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 threads { + + interface [ + Conditional=WORKERS, + GenerateConstructor, + NoStaticTables + ] WorkerLocation { + readonly attribute DOMString href; + readonly attribute DOMString protocol; + readonly attribute DOMString host; + readonly attribute DOMString hostname; + readonly attribute DOMString port; + readonly attribute DOMString pathname; + readonly attribute DOMString search; + readonly attribute DOMString hash; + + [DontEnum] DOMString toString(); + }; + +} diff --git a/WebCore/dom/WorkerMessagingProxy.cpp b/WebCore/dom/WorkerMessagingProxy.cpp new file mode 100644 index 0000000..b0d145e --- /dev/null +++ b/WebCore/dom/WorkerMessagingProxy.cpp @@ -0,0 +1,316 @@ +/* + * 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" + +#if ENABLE(WORKERS) + +#include "WorkerMessagingProxy.h" + +#include "DOMWindow.h" +#include "Document.h" +#include "MessageEvent.h" +#include "Worker.h" +#include "WorkerContext.h" +#include "WorkerTask.h" +#include "WorkerThread.h" + +namespace WebCore { + +class MessageWorkerContextTask : public WorkerTask { +public: + static PassRefPtr<MessageWorkerContextTask> create(const String& message) + { + return adoptRef(new MessageWorkerContextTask(message)); + } + +private: + MessageWorkerContextTask(const String& message) + : m_message(message.copy()) + { + } + + virtual void performTask(WorkerContext* context) + { + RefPtr<Event> evt = MessageEvent::create(m_message, "", "", 0, 0); + + if (context->onmessage()) { + evt->setTarget(context); + evt->setCurrentTarget(context); + context->onmessage()->handleEvent(evt.get(), false); + } + + ExceptionCode ec = 0; + context->dispatchEvent(evt.release(), ec); + ASSERT(!ec); + + context->thread()->messagingProxy()->confirmWorkerThreadMessage(context->hasPendingActivity()); + } + +private: + String m_message; +}; + +class MessageWorkerTask : public ScriptExecutionContext::Task { +public: + static PassRefPtr<MessageWorkerTask> create(const String& message, WorkerMessagingProxy* messagingProxy) + { + return adoptRef(new MessageWorkerTask(message, messagingProxy)); + } + +private: + MessageWorkerTask(const String& message, WorkerMessagingProxy* messagingProxy) + : m_message(message.copy()) + , m_messagingProxy(messagingProxy) + { + } + + virtual void performTask(ScriptExecutionContext*) + { + Worker* workerObject = m_messagingProxy->workerObject(); + if (!workerObject || m_messagingProxy->askedToTerminate()) + return; + + RefPtr<Event> evt = MessageEvent::create(m_message, "", "", 0, 0); + + if (workerObject->onmessage()) { + evt->setTarget(workerObject); + evt->setCurrentTarget(workerObject); + workerObject->onmessage()->handleEvent(evt.get(), false); + } + + ExceptionCode ec = 0; + workerObject->dispatchEvent(evt.release(), ec); + ASSERT(!ec); + } + +private: + String m_message; + WorkerMessagingProxy* m_messagingProxy; +}; + +class WorkerExceptionTask : public ScriptExecutionContext::Task { +public: + static PassRefPtr<WorkerExceptionTask> create(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy) + { + return adoptRef(new WorkerExceptionTask(errorMessage, lineNumber, sourceURL, messagingProxy)); + } + +private: + WorkerExceptionTask(const String& errorMessage, int lineNumber, const String& sourceURL, WorkerMessagingProxy* messagingProxy) + : m_errorMessage(errorMessage.copy()) + , m_lineNumber(lineNumber) + , m_sourceURL(sourceURL.copy()) + , m_messagingProxy(messagingProxy) + { + } + + virtual void performTask(ScriptExecutionContext* context) + { + if (!m_messagingProxy->askedToTerminate()) + context->reportException(m_errorMessage, m_lineNumber, m_sourceURL); + } + + String m_errorMessage; + int m_lineNumber; + String m_sourceURL; + WorkerMessagingProxy* m_messagingProxy; +}; + +class WorkerContextDestroyedTask : public ScriptExecutionContext::Task { +public: + static PassRefPtr<WorkerContextDestroyedTask> create(WorkerMessagingProxy* messagingProxy) + { + return adoptRef(new WorkerContextDestroyedTask(messagingProxy)); + } + +private: + WorkerContextDestroyedTask(WorkerMessagingProxy* messagingProxy) + : m_messagingProxy(messagingProxy) + { + } + + virtual void performTask(ScriptExecutionContext*) + { + m_messagingProxy->workerContextDestroyedInternal(); + } + + WorkerMessagingProxy* m_messagingProxy; +}; + +class WorkerThreadActivityReportTask : public ScriptExecutionContext::Task { +public: + static PassRefPtr<WorkerThreadActivityReportTask> create(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity) + { + return adoptRef(new WorkerThreadActivityReportTask(messagingProxy, confirmingMessage, hasPendingActivity)); + } + +private: + WorkerThreadActivityReportTask(WorkerMessagingProxy* messagingProxy, bool confirmingMessage, bool hasPendingActivity) + : m_messagingProxy(messagingProxy) + , m_confirmingMessage(confirmingMessage) + , m_hasPendingActivity(hasPendingActivity) + { + } + + virtual void performTask(ScriptExecutionContext*) + { + m_messagingProxy->reportWorkerThreadActivityInternal(m_confirmingMessage, m_hasPendingActivity); + } + + WorkerMessagingProxy* m_messagingProxy; + bool m_confirmingMessage; + bool m_hasPendingActivity; +}; + + +WorkerMessagingProxy::WorkerMessagingProxy(PassRefPtr<ScriptExecutionContext> scriptExecutionContext, Worker* workerObject) + : m_scriptExecutionContext(scriptExecutionContext) + , m_workerObject(workerObject) + , m_unconfirmedMessageCount(0) + , m_workerThreadHadPendingActivity(false) + , m_askedToTerminate(false) +{ + ASSERT(m_workerObject); + ASSERT((m_scriptExecutionContext->isDocument() && isMainThread()) + || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID())); +} + +WorkerMessagingProxy::~WorkerMessagingProxy() +{ + ASSERT(!m_workerObject); + ASSERT((m_scriptExecutionContext->isDocument() && isMainThread()) + || (m_scriptExecutionContext->isWorkerContext() && currentThread() == static_cast<WorkerContext*>(m_scriptExecutionContext.get())->thread()->threadID())); +} + +void WorkerMessagingProxy::postMessageToWorkerObject(const String& message) +{ + m_scriptExecutionContext->postTask(MessageWorkerTask::create(message, this)); +} + +void WorkerMessagingProxy::postMessageToWorkerContext(const String& message) +{ + if (m_askedToTerminate) + return; + + if (m_workerThread) { + ++m_unconfirmedMessageCount; + m_workerThread->runLoop().postTask(MessageWorkerContextTask::create(message)); + } else + m_queuedEarlyTasks.append(MessageWorkerContextTask::create(message)); +} + +void WorkerMessagingProxy::postTaskToParentContext(PassRefPtr<ScriptExecutionContext::Task> task) +{ + m_scriptExecutionContext->postTask(task); +} + +void WorkerMessagingProxy::postWorkerException(const String& errorMessage, int lineNumber, const String& sourceURL) +{ + m_scriptExecutionContext->postTask(WorkerExceptionTask::create(errorMessage, lineNumber, sourceURL, this)); +} + +void WorkerMessagingProxy::workerThreadCreated(PassRefPtr<WorkerThread> workerThread) +{ + m_workerThread = workerThread; + + if (m_askedToTerminate) { + // Worker.terminate() could be called from JS before the thread was created. + m_workerThread->stop(); + } else { + unsigned taskCount = m_queuedEarlyTasks.size(); + ASSERT(!m_unconfirmedMessageCount); + m_unconfirmedMessageCount = taskCount + 1; // Worker initialization counts as a pending message. + + for (unsigned i = 0; i < taskCount; ++i) + m_workerThread->runLoop().postTask(m_queuedEarlyTasks[i]); + m_queuedEarlyTasks.clear(); + } +} + +void WorkerMessagingProxy::workerObjectDestroyed() +{ + m_workerObject = 0; + if (m_workerThread) + terminate(); + else + workerContextDestroyedInternal(); // It never existed, just do our cleanup. +} + +void WorkerMessagingProxy::workerContextDestroyed() +{ + m_scriptExecutionContext->postTask(WorkerContextDestroyedTask::create(this)); + // Will execute workerContextDestroyedInternal() on context's thread. +} + +void WorkerMessagingProxy::workerContextDestroyedInternal() +{ + // WorkerContextDestroyedTask is always the last to be performed, so the proxy is not needed for communication + // in either side any more. However, the Worker object may still exist, and it assumes that the proxy exists, too. + if (!m_workerObject) + delete this; +} + +void WorkerMessagingProxy::terminate() +{ + if (m_askedToTerminate) + return; + m_askedToTerminate = true; + + if (m_workerThread) + m_workerThread->stop(); +} + +void WorkerMessagingProxy::confirmWorkerThreadMessage(bool hasPendingActivity) +{ + m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, true, hasPendingActivity)); + // Will execute reportWorkerThreadActivityInternal() on context's thread. +} + +void WorkerMessagingProxy::reportWorkerThreadActivity(bool hasPendingActivity) +{ + m_scriptExecutionContext->postTask(WorkerThreadActivityReportTask::create(this, false, hasPendingActivity)); + // Will execute reportWorkerThreadActivityInternal() on context's thread. +} + +void WorkerMessagingProxy::reportWorkerThreadActivityInternal(bool confirmingMessage, bool hasPendingActivity) +{ + if (confirmingMessage && !m_askedToTerminate) { + ASSERT(m_unconfirmedMessageCount); + --m_unconfirmedMessageCount; + } + + m_workerThreadHadPendingActivity = hasPendingActivity; +} + +bool WorkerMessagingProxy::workerThreadHasPendingActivity() const +{ + return (m_unconfirmedMessageCount || m_workerThreadHadPendingActivity) && !m_askedToTerminate; +} + +} // namespace WebCore + +#endif // ENABLE(WORKERS) diff --git a/WebCore/dom/WorkerMessagingProxy.h b/WebCore/dom/WorkerMessagingProxy.h new file mode 100644 index 0000000..e7ba8a9 --- /dev/null +++ b/WebCore/dom/WorkerMessagingProxy.h @@ -0,0 +1,96 @@ +/* + * 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 WorkerMessagingProxy_h +#define WorkerMessagingProxy_h + +#if ENABLE(WORKERS) + +#include "ScriptExecutionContext.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class ScriptExecutionContext; + class String; + class Worker; + class WorkerTask; + class WorkerThread; + + class WorkerMessagingProxy : Noncopyable { + public: + WorkerMessagingProxy(PassRefPtr<ScriptExecutionContext>, Worker*); + + void postMessageToWorkerObject(const String& message); + void postMessageToWorkerContext(const String& message); + void postTaskToParentContext(PassRefPtr<ScriptExecutionContext::Task>); + + void postWorkerException(const String& errorMessage, int lineNumber, const String& sourceURL); + + void workerThreadCreated(PassRefPtr<WorkerThread>); + void workerObjectDestroyed(); + void workerContextDestroyed(); + + void terminate(); + + void confirmWorkerThreadMessage(bool hasPendingActivity); + void reportWorkerThreadActivity(bool hasPendingActivity); + bool workerThreadHasPendingActivity() const; + + private: + friend class GenericWorkerTaskBase; + friend class MessageWorkerTask; + friend class WorkerContextDestroyedTask; + friend class WorkerExceptionTask; + friend class WorkerThreadActivityReportTask; + + ~WorkerMessagingProxy(); + + void workerContextDestroyedInternal(); + void reportWorkerThreadActivityInternal(bool confirmingMessage, bool hasPendingActivity); + Worker* workerObject() const { return m_workerObject; } + bool askedToTerminate() { return m_askedToTerminate; } + + RefPtr<ScriptExecutionContext> m_scriptExecutionContext; + Worker* m_workerObject; + RefPtr<WorkerThread> m_workerThread; + + unsigned m_unconfirmedMessageCount; // Unconfirmed messages from worker object to worker thread. + bool m_workerThreadHadPendingActivity; // The latest confirmation from worker thread reported that it was still active. + + bool m_askedToTerminate; + + Vector<RefPtr<WorkerTask> > m_queuedEarlyTasks; // Tasks are queued here until there's a thread object created. + }; + +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // WorkerMessagingProxy_h diff --git a/WebCore/dom/WorkerRunLoop.cpp b/WebCore/dom/WorkerRunLoop.cpp new file mode 100644 index 0000000..98c2e8d --- /dev/null +++ b/WebCore/dom/WorkerRunLoop.cpp @@ -0,0 +1,69 @@ +/* + * 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 "WorkerRunLoop.h" +#include "WorkerContext.h" +#include "WorkerTask.h" +#include "WorkerThread.h" + +namespace WebCore { + +void WorkerRunLoop::run(WorkerContext* context) +{ + ASSERT(context); + ASSERT(context->thread()); + ASSERT(context->thread()->threadID() == currentThread()); + + while (true) { + RefPtr<WorkerTask> task; + if (!m_messageQueue.waitForMessage(task)) + break; + + task->performTask(context); + } +} + +void WorkerRunLoop::terminate() +{ + m_messageQueue.kill(); +} + +void WorkerRunLoop::postTask(PassRefPtr<WorkerTask> task) +{ + m_messageQueue.append(task); +} + +} // namespace WebCore + +#endif // ENABLE(WORKERS) diff --git a/WebCore/dom/WorkerRunLoop.h b/WebCore/dom/WorkerRunLoop.h new file mode 100644 index 0000000..03a746b --- /dev/null +++ b/WebCore/dom/WorkerRunLoop.h @@ -0,0 +1,64 @@ +/* + * 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 WorkerRunLoop_h +#define WorkerRunLoop_h + +#if ENABLE(WORKERS) + +#include "WorkerTask.h" +#include <wtf/MessageQueue.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + + class WorkerContext; + + class WorkerRunLoop { + public: + WorkerRunLoop() {} + + // Blocking call. Waits for tasks and timers, invokes the callbacks. + void run(WorkerContext*); + + void terminate(); + bool terminated() { return m_messageQueue.killed(); } + + void postTask(PassRefPtr<WorkerTask>); + + private: + MessageQueue<RefPtr<WorkerTask> > m_messageQueue; + }; + +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // WorkerRunLoop_h diff --git a/WebCore/dom/WorkerTask.cpp b/WebCore/dom/WorkerTask.cpp new file mode 100644 index 0000000..4a22c59 --- /dev/null +++ b/WebCore/dom/WorkerTask.cpp @@ -0,0 +1,41 @@ +/* + * 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" + +#if ENABLE(WORKERS) + +#include "WorkerTask.h" + +namespace WebCore { + +WorkerTask::~WorkerTask() +{ +} + +} // namespace WebCore + +#endif // ENABLE(WORKERS) diff --git a/WebCore/dom/DedicatedWorker.idl b/WebCore/dom/WorkerTask.h index 5e74683..a842ce2 100644 --- a/WebCore/dom/DedicatedWorker.idl +++ b/WebCore/dom/WorkerTask.h @@ -24,20 +24,25 @@ * */ -module threads { +#ifndef WorkerTask_h +#define WorkerTask_h - interface [CustomMarkFunction, Conditional=WORKERS] DedicatedWorker { +#if ENABLE(WORKERS) - // These also used for shared workers. - attribute [Custom] EventListener onclose; - attribute [Custom] EventListener onerror; +#include <wtf/Threading.h> - // These are only for dedicated workers. - attribute [Custom] EventListener onmessage; - [Custom] MessagePort startConversation(in DOMString message); - void close(); - void postMessage(in DOMString message, in [Optional] MessagePort port); +namespace WebCore { + class WorkerContext; + + class WorkerTask : public ThreadSafeShared<WorkerTask> { + public: + virtual ~WorkerTask(); + virtual void performTask(WorkerContext*) = 0; }; -} +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // WorkerTask_h diff --git a/WebCore/dom/WorkerThread.cpp b/WebCore/dom/WorkerThread.cpp new file mode 100644 index 0000000..a762e0b --- /dev/null +++ b/WebCore/dom/WorkerThread.cpp @@ -0,0 +1,154 @@ +/* + * 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" + +#if ENABLE(WORKERS) + +#include "WorkerThread.h" + +#include "KURL.h" +#include "PlatformString.h" +#include "ScriptSourceCode.h" +#include "ScriptValue.h" +#include "Worker.h" +#include "WorkerContext.h" +#include "WorkerMessagingProxy.h" +#include "WorkerTask.h" + +#include <utility> +#include <wtf/Noncopyable.h> + +namespace WebCore { +struct WorkerThreadStartupData : Noncopyable { +public: + static std::auto_ptr<WorkerThreadStartupData> create(const KURL& scriptURL, const String& userAgent, const String& sourceCode) + { + return std::auto_ptr<WorkerThreadStartupData>(new WorkerThreadStartupData(scriptURL, userAgent, sourceCode)); + } + + KURL m_scriptURL; + String m_userAgent; + String m_sourceCode; +private: + WorkerThreadStartupData(const KURL& scriptURL, const String& userAgent, const String& sourceCode); +}; + +WorkerThreadStartupData::WorkerThreadStartupData(const KURL& scriptURL, const String& userAgent, const String& sourceCode) + : m_scriptURL(scriptURL.copy()) + , m_userAgent(userAgent.copy()) + , m_sourceCode(sourceCode.copy()) +{ +} + +PassRefPtr<WorkerThread> WorkerThread::create(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerMessagingProxy* messagingProxy) +{ + return adoptRef(new WorkerThread(scriptURL, userAgent, sourceCode, messagingProxy)); +} + +WorkerThread::WorkerThread(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerMessagingProxy* messagingProxy) + : m_threadID(0) + , m_messagingProxy(messagingProxy) + , m_startupData(WorkerThreadStartupData::create(scriptURL, userAgent, sourceCode)) +{ +} + +WorkerThread::~WorkerThread() +{ +} + +bool WorkerThread::start() +{ + // Mutex protection is necessary to ensure that m_threadID is initialized when the thread starts. + MutexLocker lock(m_threadCreationMutex); + + if (m_threadID) + return true; + + m_threadID = createThread(WorkerThread::workerThreadStart, this, "WebCore::Worker"); + + return m_threadID; +} + +void* WorkerThread::workerThreadStart(void* thread) +{ + return static_cast<WorkerThread*>(thread)->workerThread(); +} + +void* WorkerThread::workerThread() +{ + { + MutexLocker lock(m_threadCreationMutex); + m_workerContext = WorkerContext::create(m_startupData->m_scriptURL, m_startupData->m_userAgent, this); + if (m_runLoop.terminated()) { + // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet, + // forbidExecution() couldn't be called from stop(). + m_workerContext->script()->forbidExecution(); + } + } + + WorkerScriptController* script = m_workerContext->script(); + script->evaluate(ScriptSourceCode(m_startupData->m_sourceCode, m_startupData->m_scriptURL)); + // Free the startup data to cause its member variable deref's happen on the worker's thread (since + // all ref/derefs of these objects are happening on the thread at this point). Note that + // WorkerThread::~WorkerThread happens on a different thread where it was created. + m_startupData.clear(); + + m_messagingProxy->confirmWorkerThreadMessage(m_workerContext->hasPendingActivity()); // This wasn't really a message, but it counts as one for GC. + + // Blocks until terminated. + m_runLoop.run(m_workerContext.get()); + + ThreadIdentifier threadID = m_threadID; + + m_workerContext->clearScript(); + ASSERT(m_workerContext->hasOneRef()); + // The below assignment will destroy the context, which will in turn notify messaging proxy. + // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them. + m_workerContext = 0; + + // The thread object may be already destroyed from notification now, don't try to access "this". + detachThread(threadID); + + return 0; +} + +void WorkerThread::stop() +{ + // Mutex protection is necessary because stop() can be called before the context is fully created. + MutexLocker lock(m_threadCreationMutex); + + // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever. + if (m_workerContext) + m_workerContext->script()->forbidExecution(); + + // FIXME: Rudely killing the thread won't work when we allow nested workers, because they will try to post notifications of their destruction. + m_runLoop.terminate(); +} + +} // namespace WebCore + +#endif // ENABLE(WORKERS) diff --git a/WebCore/dom/WorkerThread.h b/WebCore/dom/WorkerThread.h new file mode 100644 index 0000000..504db81 --- /dev/null +++ b/WebCore/dom/WorkerThread.h @@ -0,0 +1,78 @@ +/* + * 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 WorkerThread_h +#define WorkerThread_h + +#if ENABLE(WORKERS) + +#include <WorkerRunLoop.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class KURL; + class String; + class WorkerContext; + class WorkerMessagingProxy; + class WorkerTask; + struct WorkerThreadStartupData; + + class WorkerThread : public RefCounted<WorkerThread> { + public: + static PassRefPtr<WorkerThread> create(const KURL& scriptURL, const String& userAgent, const String& sourceCode, WorkerMessagingProxy*); + ~WorkerThread(); + + bool start(); + void stop(); + + ThreadIdentifier threadID() const { return m_threadID; } + WorkerRunLoop& runLoop() { return m_runLoop; } + WorkerMessagingProxy* messagingProxy() const { return m_messagingProxy; } + + private: + WorkerThread(const KURL&, const String& userAgent, const String& sourceCode, WorkerMessagingProxy*); + + static void* workerThreadStart(void*); + void* workerThread(); + + ThreadIdentifier m_threadID; + WorkerRunLoop m_runLoop; + WorkerMessagingProxy* m_messagingProxy; + + RefPtr<WorkerContext> m_workerContext; + Mutex m_threadCreationMutex; + + OwnPtr<WorkerThreadStartupData> m_startupData; + }; + +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // WorkerThread_h diff --git a/WebCore/dom/XMLTokenizer.cpp b/WebCore/dom/XMLTokenizer.cpp index b6e6f52..6f57a46 100644 --- a/WebCore/dom/XMLTokenizer.cpp +++ b/WebCore/dom/XMLTokenizer.cpp @@ -5,6 +5,7 @@ * 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 @@ -38,15 +39,16 @@ #include "FrameView.h" #include "HTMLLinkElement.h" #include "HTMLNames.h" -#include "HTMLScriptElement.h" #include "HTMLStyleElement.h" -#include "HTMLTokenizer.h" -#include "ScriptController.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 <wtf/Platform.h> #include <wtf/StringExtras.h> @@ -55,7 +57,6 @@ #if ENABLE(SVG) #include "SVGNames.h" -#include "SVGScriptElement.h" #include "SVGStyleElement.h" #endif @@ -65,30 +66,15 @@ namespace WebCore { const int maxErrors = 25; -bool isScriptElement(Element* element) -{ - return element->hasTagName(HTMLNames::scriptTag) -#if ENABLE(SVG) - || element->hasTagName(SVGNames::scriptTag) -#endif - ; -} - -ScriptElement* castToScriptElement(Element* element) +#if ENABLE(WML) +bool XMLTokenizer::isWMLDocument() const { - ASSERT(isScriptElement(element)); - - if (element->hasTagName(HTMLNames::scriptTag)) - return static_cast<HTMLScriptElement*>(element); + if (m_doc) + return m_doc->isWMLDocument(); -#if ENABLE(SVG) - if (element->hasTagName(SVGNames::scriptTag)) - return static_cast<SVGScriptElement*>(element); -#endif - - ASSERT_NOT_REACHED(); - return 0; + return false; } +#endif void XMLTokenizer::setCurrentNode(Node* n) { @@ -120,25 +106,6 @@ bool XMLTokenizer::write(const SegmentedString& s, bool /*appendData*/) return false; } -void XMLTokenizer::eventuallyMarkAsParserCreated(Element* element) -{ - if (element->hasTagName(HTMLNames::scriptTag)) - static_cast<HTMLScriptElement*>(element)->setCreatedByParser(true); -#if ENABLE(SVG) - else if (element->hasTagName(SVGNames::scriptTag)) - static_cast<SVGScriptElement*>(element)->setCreatedByParser(true); -#endif - else if (element->hasTagName(HTMLNames::styleTag)) - static_cast<HTMLStyleElement*>(element)->setCreatedByParser(true); -#if ENABLE(SVG) - else if (element->hasTagName(SVGNames::styleTag)) - static_cast<SVGStyleElement*>(element)->setCreatedByParser(true); -#endif - else if (element->hasTagName(HTMLNames::linkTag)) - static_cast<HTMLLinkElement*>(element)->setCreatedByParser(true); -} - - void XMLTokenizer::handleError(ErrorType type, const char* m, int lineNumber, int columnNumber) { if (type == fatal || (m_errorCount < maxErrors && m_lastErrorLine != lineNumber && m_lastErrorColumn != columnNumber)) { @@ -276,9 +243,16 @@ void XMLTokenizer::insertErrorMessageBlock() } #if ENABLE(SVG) else if (documentElement->namespaceURI() == SVGNames::svgNamespaceURI) { - // Until our SVG implementation has text support, it is best if we - // wrap the erroneous SVG document in an xhtml document and render - // the combined document with error messages. + RefPtr<Node> rootElement = doc->createElementNS(HTMLNames::xhtmlNamespaceURI, "html", ec); + RefPtr<Node> body = doc->createElementNS(HTMLNames::xhtmlNamespaceURI, "body", ec); + rootElement->appendChild(body, ec); + body->appendChild(documentElement, ec); + doc->appendChild(rootElement.get(), ec); + documentElement = body.get(); + } +#endif +#if ENABLE(WML) + else if (isWMLDocument()) { RefPtr<Node> rootElement = doc->createElementNS(HTMLNames::xhtmlNamespaceURI, "html", ec); RefPtr<Node> body = doc->createElementNS(HTMLNames::xhtmlNamespaceURI, "body", ec); rootElement->appendChild(body, ec); @@ -301,30 +275,30 @@ void XMLTokenizer::insertErrorMessageBlock() doc->updateRendering(); } -void XMLTokenizer::notifyFinished(CachedResource* finishedObj) +void XMLTokenizer::notifyFinished(CachedResource* unusedResource) { - ASSERT(m_pendingScript == finishedObj); + ASSERT_UNUSED(unusedResource, unusedResource == m_pendingScript); ASSERT(m_pendingScript->accessCount() > 0); - String cachedScriptUrl = m_pendingScript->url(); - String scriptSource = m_pendingScript->script(); + 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 = castToScriptElement(e.get()); + ScriptElement* scriptElement = toScriptElement(e.get()); ASSERT(scriptElement); if (errorOccurred) scriptElement->dispatchErrorEvent(); else { - m_view->frame()->loader()->executeScript(cachedScriptUrl, 1, scriptSource); + m_view->frame()->loader()->executeScript(sourceCode); scriptElement->dispatchLoadEvent(); } - + m_scriptElement = 0; if (!m_requestingScript) diff --git a/WebCore/dom/XMLTokenizer.h b/WebCore/dom/XMLTokenizer.h index 9b416b9..c5c0847 100644 --- a/WebCore/dom/XMLTokenizer.h +++ b/WebCore/dom/XMLTokenizer.h @@ -181,6 +181,9 @@ namespace WebCore { void setIsXHTMLDocument(bool isXHTML) { m_isXHTMLDocument = isXHTML; } bool isXHTMLDocument() const { return m_isXHTMLDocument; } +#if ENABLE(WML) + bool isWMLDocument() const; +#endif // from CachedResourceClient virtual void notifyFinished(CachedResource* finishedObj); @@ -224,7 +227,6 @@ public: private: friend bool parseXMLDocumentFragment(const String& chunk, DocumentFragment* fragment, Element* parent); - static void eventuallyMarkAsParserCreated(Element* element); void initializeParserContext(const char* chunk = 0); void setCurrentNode(Node*); @@ -286,9 +288,6 @@ void setLoaderForLibXMLCallbacks(DocLoader*); HashMap<String, String> parseAttributes(const String&, bool& attrsOK); bool parseXMLDocumentFragment(const String&, DocumentFragment*, Element* parent = 0); -bool isScriptElement(Element*); -ScriptElement* castToScriptElement(Element*); - } // namespace WebCore #endif // USE(EXPAT) diff --git a/WebCore/dom/XMLTokenizerLibxml2.cpp b/WebCore/dom/XMLTokenizerLibxml2.cpp index 8dfb682..24ea6e8 100644 --- a/WebCore/dom/XMLTokenizerLibxml2.cpp +++ b/WebCore/dom/XMLTokenizerLibxml2.cpp @@ -5,6 +5,7 @@ * 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 @@ -38,36 +39,38 @@ #include "FrameView.h" #include "HTMLLinkElement.h" #include "HTMLStyleElement.h" -#include "HTMLTokenizer.h" -#include "ScriptController.h" +#include "HTMLTokenizer.h" // for decodeNamedEntity #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 <libxml/parser.h> #include <libxml/parserInternals.h> #include <wtf/Platform.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 - using namespace std; namespace WebCore { -class PendingCallbacks { +class PendingCallbacks : Noncopyable { public: - PendingCallbacks() + ~PendingCallbacks() { - m_callbacks.setAutoDelete(true); + deleteAllValues(m_callbacks); } void appendStartElementNSCallback(const xmlChar* xmlLocalName, const xmlChar* xmlPrefix, const xmlChar* xmlURI, int nb_namespaces, @@ -79,12 +82,12 @@ public: callback->xmlPrefix = xmlStrdup(xmlPrefix); callback->xmlURI = xmlStrdup(xmlURI); callback->nb_namespaces = nb_namespaces; - callback->namespaces = reinterpret_cast<xmlChar**>(xmlMalloc(sizeof(xmlChar*) * nb_namespaces * 2)); + 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 = reinterpret_cast<xmlChar**>(xmlMalloc(sizeof(xmlChar*) * nb_attributes * 5)); + 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. @@ -115,7 +118,7 @@ public: callback->s = xmlStrndup(s, len); callback->len = len; - m_callbacks.append(callback); + m_callbacks.append(callback); } void appendProcessingInstructionCallback(const xmlChar* target, const xmlChar* data) @@ -172,23 +175,20 @@ public: void callAndRemoveFirstCallback(XMLTokenizer* tokenizer) { - PendingCallback* cb = m_callbacks.getFirst(); - - cb->call(tokenizer); + OwnPtr<PendingCallback> callback(m_callbacks.first()); m_callbacks.removeFirst(); + callback->call(tokenizer); } bool isEmpty() const { return m_callbacks.isEmpty(); } private: - struct PendingCallback { - - virtual ~PendingCallback() { } - + struct PendingCallback { + virtual ~PendingCallback() { } virtual void call(XMLTokenizer* tokenizer) = 0; }; - struct PendingStartElementNSCallback : public PendingCallback { + struct PendingStartElementNSCallback : public PendingCallback { virtual ~PendingStartElementNSCallback() { xmlFree(xmlLocalName); xmlFree(xmlPrefix); @@ -204,8 +204,8 @@ private: virtual void call(XMLTokenizer* tokenizer) { tokenizer->startElementNs(xmlLocalName, xmlPrefix, xmlURI, - nb_namespaces, (const xmlChar**)namespaces, - nb_attributes, nb_defaulted, (const xmlChar**)(attributes)); + nb_namespaces, const_cast<const xmlChar**>(namespaces), + nb_attributes, nb_defaulted, const_cast<const xmlChar**>(attributes)); } xmlChar* xmlLocalName; @@ -320,8 +320,7 @@ private: int columnNumber; }; -public: - DeprecatedPtrList<PendingCallback> m_callbacks; + Deque<PendingCallback*> m_callbacks; }; // -------------------------------- @@ -329,7 +328,7 @@ static int globalDescriptor = 0; static DocLoader* globalDocLoader = 0; static ThreadIdentifier libxmlLoaderThread = 0; -static int matchFunc(const char* uri) +static int matchFunc(const char*) { // Only match loads initiated due to uses of libxml2 from within XMLTokenizer to avoid // interfering with client applications that also use libxml2. http://bugs.webkit.org/show_bug.cgi?id=17353 @@ -432,7 +431,7 @@ static int readFunc(void* context, char* buffer, int len) return data->readOutBytes(buffer, len); } -static int writeFunc(void* context, const char* buffer, int len) +static int writeFunc(void*, const char*, int) { // Always just do 0-byte writes return 0; @@ -751,9 +750,9 @@ void XMLTokenizer::startElementNs(const xmlChar* xmlLocalName, const xmlChar* xm jsProxy->setEventHandlerLineno(0); newElement->beginParsingChildren(); - eventuallyMarkAsParserCreated(newElement.get()); - if (isScriptElement(newElement.get())) + ScriptElement* scriptElement = toScriptElement(newElement.get()); + if (scriptElement) m_scriptStartLine = lineNumber(); if (!m_currentNode->addChild(newElement.get())) { @@ -781,37 +780,40 @@ void XMLTokenizer::endElementNs() Node* n = m_currentNode; RefPtr<Node> parent = n->parentNode(); n->finishParsingChildren(); - - // don't load external scripts for standalone documents (for now) - if (n->isElementNode() && m_view && isScriptElement(static_cast<Element*>(n))) { - ASSERT(!m_pendingScript); - m_requestingScript = true; - - Element* element = static_cast<Element*>(n); - ScriptElement* scriptElement = castToScriptElement(element); - - String scriptHref = scriptElement->sourceAttributeValue(); - if (!scriptHref.isEmpty()) { - // we have a src attribute - String scriptCharset = scriptElement->scriptCharset(); - if ((m_pendingScript = m_doc->docLoader()->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 { - String scriptCode = scriptElement->scriptContent(); - m_view->frame()->loader()->executeScript(m_doc->url().string(), m_scriptStartLine, scriptCode); - } - m_requestingScript = false; + if (!n->isElementNode() || !m_view) { + setCurrentNode(parent.get()); + return; } + Element* element = static_cast<Element*>(n); + ScriptElement* scriptElement = toScriptElement(element); + if (!scriptElement) { + setCurrentNode(parent.get()); + return; + } + + // don't load external scripts for standalone documents (for now) + ASSERT(!m_pendingScript); + m_requestingScript = true; + + String scriptHref = scriptElement->sourceAttributeValue(); + if (!scriptHref.isEmpty()) { + // we have a src attribute + String scriptCharset = scriptElement->scriptCharset(); + if ((m_pendingScript = m_doc->docLoader()->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 + m_view->frame()->loader()->executeScript(ScriptSourceCode(scriptElement->scriptContent(), m_doc->url(), m_scriptStartLine)); + + m_requestingScript = false; setCurrentNode(parent.get()); } @@ -950,8 +952,19 @@ void XMLTokenizer::internalSubset(const xmlChar* name, const xmlChar* externalID return; } - if (m_doc) + if (m_doc) { +#if ENABLE(WML) + String extId = toString(externalID); + 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 + m_doc->addChild(DocumentType::create(m_doc, toString(name), toString(externalID), toString(systemID))); + } } static inline XMLTokenizer* getTokenizer(void* closure) @@ -965,6 +978,8 @@ static inline XMLTokenizer* getTokenizer(void* closure) 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 @@ -980,7 +995,7 @@ static void startElementNsHandler(void* closure, const xmlChar* localname, const getTokenizer(closure)->startElementNs(localname, prefix, uri, nb_namespaces, namespaces, nb_attributes, nb_defaulted, libxmlAttributes); } -static void endElementNsHandler(void* closure, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri) +static void endElementNsHandler(void* closure, const xmlChar*, const xmlChar*, const xmlChar*) { if (hackAroundLibXMLEntityBug(closure)) return; @@ -1086,7 +1101,11 @@ static xmlEntityPtr getEntityHandler(void* closure, const xmlChar* name) } ent = xmlGetDocEntity(ctxt->myDoc, name); - if (!ent && getTokenizer(closure)->isXHTMLDocument()) { + if (!ent && (getTokenizer(closure)->isXHTMLDocument() +#if ENABLE(WML) + || getTokenizer(closure)->isWMLDocument() +#endif + )) { ent = getXHTMLEntity(name); if (ent) ent->etype = XML_INTERNAL_GENERAL_ENTITY; @@ -1114,7 +1133,7 @@ static void internalSubsetHandler(void* closure, const xmlChar* name, const xmlC xmlSAX2InternalSubset(closure, name, externalID, systemID); } -static void externalSubsetHandler(void* closure, const xmlChar* name, const xmlChar* externalId, const xmlChar* 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") @@ -1128,7 +1147,7 @@ static void externalSubsetHandler(void* closure, const xmlChar* name, const xmlC getTokenizer(closure)->setIsXHTMLDocument(true); // controls if we replace entities or not. } -static void ignorableWhitespaceHandler(void* ctx, const xmlChar* ch, int len) +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 @@ -1294,9 +1313,9 @@ struct AttributeParseState { 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) +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; @@ -1338,5 +1357,3 @@ HashMap<String, String> parseAttributes(const String& string, bool& attrsOK) } } - - diff --git a/WebCore/dom/XMLTokenizerQt.cpp b/WebCore/dom/XMLTokenizerQt.cpp index 55cae00..ce50126 100644 --- a/WebCore/dom/XMLTokenizerQt.cpp +++ b/WebCore/dom/XMLTokenizerQt.cpp @@ -5,6 +5,7 @@ * 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 @@ -39,13 +40,15 @@ #include "HTMLLinkElement.h" #include "HTMLStyleElement.h" #include "HTMLTokenizer.h" -#include "ScriptController.h" -#include "ScriptElement.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 <QDebug> #include <wtf/Platform.h> @@ -445,7 +448,11 @@ void XMLTokenizer::parse() case QXmlStreamReader::EntityReference: { //qDebug()<<"---------- ENTITY = "<<m_stream.name().toString() // <<", t = "<<m_stream.text().toString(); - if (isXHTMLDocument()) { + if (isXHTMLDocument() +#if ENABLE(WML) + || isWMLDocument() +#endif + ) { QString entity = m_stream.name().toString(); UChar c = decodeNamedEntity(entity.toUtf8().constData()); if (m_currentNode->isTextNode() || enterText()) { @@ -533,9 +540,8 @@ void XMLTokenizer::parseStartElement() return; } - eventuallyMarkAsParserCreated(newElement.get()); - - if (isScriptElement(newElement.get())) + ScriptElement* scriptElement = toScriptElement(newElement.get()); + if (scriptElement) m_scriptStartLine = lineNumber(); if (!m_currentNode->addChild(newElement.get())) { @@ -556,36 +562,39 @@ void XMLTokenizer::parseEndElement() RefPtr<Node> parent = n->parentNode(); n->finishParsingChildren(); - // don't load external scripts for standalone documents (for now) - if (n->isElementNode() && m_view && isScriptElement(static_cast<Element*>(n))) { - ASSERT(!m_pendingScript); - m_requestingScript = true; - - Element* element = static_cast<Element*>(n); - ScriptElement* scriptElement = castToScriptElement(element); - - String scriptHref = scriptElement->sourceAttributeValue(); - if (!scriptHref.isEmpty()) { - // we have a src attribute - String scriptCharset = scriptElement->scriptCharset(); - if ((m_pendingScript = m_doc->docLoader()->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 { - String scriptCode = scriptElement->scriptContent(); - m_view->frame()->loader()->executeScript(m_doc->url().string(), m_scriptStartLine, scriptCode); - } + if (!n->isElementNode() || !m_view) { + setCurrentNode(parent.get()); + return; + } - m_requestingScript = false; + Element* element = static_cast<Element*>(n); + ScriptElement* scriptElement = toScriptElement(element); + if (!scriptElement) { + setCurrentNode(parent.get()); + return; } + // don't load external scripts for standalone documents (for now) + ASSERT(!m_pendingScript); + m_requestingScript = true; + + String scriptHref = scriptElement->sourceAttributeValue(); + if (!scriptHref.isEmpty()) { + // we have a src attribute + String scriptCharset = scriptElement->scriptCharset(); + if ((m_pendingScript = m_doc->docLoader()->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 + m_view->frame()->loader()->executeScript(ScriptSourceCode(scriptElement->scriptContent(), m_doc->url(), m_scriptStartLine)); + + m_requestingScript = false; setCurrentNode(parent.get()); } @@ -744,6 +753,14 @@ void XMLTokenizer::parseDtd() || (publicId == QLatin1String("-//WAPFORUM//DTD XHTML Mobile 1.0//EN"))) { setIsXHTMLDocument(true); // controls if we replace entities or not. } +#if ENABLE(WML) + else if (m_doc->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) m_doc->addChild(DocumentType::create(m_doc, name, publicId, systemId)); diff --git a/WebCore/dom/make_names.pl b/WebCore/dom/make_names.pl index c0d0d86..f9eba88 100755 --- a/WebCore/dom/make_names.pl +++ b/WebCore/dom/make_names.pl @@ -1,6 +1,7 @@ #!/usr/bin/perl -w -# Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. +# Copyright (C) 2005, 2006, 2007, 2009 Apple Inc. All rights reserved. +# Copyright (C) 2009, Julien Chaffraix <jchaffraix@webkit.org> # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -45,8 +46,6 @@ my %attrs = (); my %parameters = (); my $extraDefines = 0; my $preprocessor = "/usr/bin/gcc -E -P -x c++"; -my %svgCustomMappings = (); -my %htmlCustomMappings = (); GetOptions('tags=s' => \$tagsFile, 'attrs=s' => \$attrsFile, @@ -88,9 +87,13 @@ if ($printWrapperFactory) { sub initializeTagPropertyHash { - return ('interfaceName' => upperCaseName($_[0])."Element", - 'applyAudioHack' => 0, - 'exportString' => 0); + return ('constructorNeedsCreatedByParser' => 0, + 'constructorNeedsFormElement' => 0, + 'exportString' => 0, + 'interfaceName' => defaultInterfaceName($_[0]), + # By default, the JSInterfaceName is the same as the interfaceName. + 'JSInterfaceName' => defaultInterfaceName($_[0]), + 'wrapperOnlyIfMediaIsAvailable' => 0); } sub initializeAttrPropertyHash @@ -109,6 +112,12 @@ sub initializeParametersHash 'exportStrings' => 0); } +sub defaultInterfaceName +{ + die "No namespace found" if !$parameters{'namespace'}; + return $parameters{'namespace'} . upperCaseName($_[0]) . "Element" +} + ### Parsing handlers sub tagsHandler @@ -122,6 +131,11 @@ sub tagsHandler if ($property) { die "Unknown property $property for tag $tag\n" if !defined($tags{$tag}{$property}); + # The code rely on JSInterfaceName deriving from interfaceName to check for custom JSInterfaceName. + # So just override JSInterfaceName if it was not already set. + if ($property eq "interfaceName" && $tags{$tag}{'JSInterfaceName'} eq $tags{$tag}{'interfaceName'}) { + $tags{$tag}{'JSInterfaceName'} = $value; + } $tags{$tag}{$property} = $value; } } @@ -205,25 +219,44 @@ sub printMacros sub printConstructors { - my ($F, $namesRef) = @_; - my %names = %$namesRef; + my $F = shift; print F "#if $parameters{'guardFactoryWith'}\n" if $parameters{'guardFactoryWith'}; - for my $name (sort keys %names) { - my $ucName = $names{$name}{"interfaceName"}; + for my $name (sort keys %tags) { + my $ucName = $tags{$name}{'interfaceName'}; + + # Print the method signature avoiding unused arguments' name. + print F "static PassRefPtr<$parameters{'namespace'}Element> ${name}Constructor(Document* doc"; + if ($parameters{'namespace'} eq "HTML") { + print F ", HTMLFormElement* formElement"; + if ($tags{$name}{'constructorNeedsFormElement'}) { + print F " formElement"; + } + } + print F ", bool"; + if ($tags{$name}{'constructorNeedsCreatedByParser'}) { + print F " createdByParser"; + } + print F ")\n{\n"; - print F "$parameters{'namespace'}Element* ${name}Constructor(Document* doc, bool createdByParser)\n"; - print F "{\n"; - print F " return new $parameters{'namespace'}${ucName}($parameters{'namespace'}Names::${name}Tag, doc);\n"; - print F "}\n\n"; + # Now call the constructor with the right parameters. + print F " return new ${ucName}($parameters{'namespace'}Names::${name}Tag, doc"; + if ($tags{$name}{'constructorNeedsFormElement'}) { + print F ", formElement"; + } + if ($tags{$name}{'constructorNeedsCreatedByParser'}) { + print F ", createdByParser"; + } + print F ");\n}\n\n"; } print F "#endif\n" if $parameters{'guardFactoryWith'}; } sub printFunctionInits { - my ($F, $namesRef) = @_; - for my $name (sort keys %$namesRef) { + my $F = shift; + + for my $name (sort keys %tags) { print F " gFunctionMap->set($parameters{'namespace'}Names::${name}Tag.localName().impl(), ${name}Constructor);\n"; } } @@ -308,16 +341,23 @@ sub printNamesHeaderFile if (keys %tags) { print F "// Tags\n"; printMacros($F, "extern const WebCore::QualifiedName", "Tag", \%tags); - print F "\n\nWebCore::QualifiedName** get$parameters{'namespace'}Tags(size_t* size);\n"; } if (keys %attrs) { print F "// Attributes\n"; printMacros($F, "extern const WebCore::QualifiedName", "Attr", \%attrs); - print F "\n\nWebCore::QualifiedName** get$parameters{'namespace'}Attr(size_t* size);\n"; } print F "#endif\n\n"; - print F "void init();\n\n"; + + if (keys %tags) { + print F "WebCore::QualifiedName** get$parameters{'namespace'}Tags(size_t* size);\n"; + } + + if (keys %attrs) { + 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"; @@ -425,25 +465,29 @@ print F "\nvoid init() sub printJSElementIncludes { - my ($F, $namesRef) = @_; - my %names = %$namesRef; - for my $name (sort keys %names) { - next if (hasCustomMapping($name)); + my $F = shift; + + my %tagsSeen; + for my $tagName (sort keys %tags) { + my $JSInterfaceName = $tags{$tagName}{"JSInterfaceName"}; + next if defined($tagsSeen{$JSInterfaceName}) || usesDefaultJSWrapper($tagName); + $tagsSeen{$JSInterfaceName} = 1; - my $ucName = $names{$name}{"interfaceName"}; - print F "#include \"JS$parameters{'namespace'}${ucName}.h\"\n"; + print F "#include \"JS${JSInterfaceName}.h\"\n"; } } sub printElementIncludes { - my ($F, $namesRef, $shouldSkipCustomMappings) = @_; - my %names = %$namesRef; - for my $name (sort keys %names) { - next if ($shouldSkipCustomMappings && hasCustomMapping($name)); + my $F = shift; - my $ucName = $names{$name}{"interfaceName"}; - print F "#include \"$parameters{'namespace'}${ucName}.h\"\n"; + my %tagsSeen; + for my $tagName (sort keys %tags) { + my $interfaceName = $tags{$tagName}{"interfaceName"}; + next if defined($tagsSeen{$interfaceName}); + $tagsSeen{$interfaceName} = 1; + + print F "#include \"${interfaceName}.h\"\n"; } } @@ -515,15 +559,30 @@ print F <<END END ; -printElementIncludes($F, \%tags, 0); +if ($parameters{'namespace'} eq "HTML") { + print F "#include \"HTMLFormElement.h\"\n"; +} + +printElementIncludes($F); print F <<END #include <wtf/HashMap.h> using namespace WebCore; -typedef $parameters{'namespace'}Element* (*ConstructorFunc)(Document* doc, bool createdByParser); -typedef WTF::HashMap<AtomicStringImpl*, ConstructorFunc> FunctionMap; +END +; + +print F "typedef PassRefPtr<$parameters{'namespace'}Element> (*ConstructorFunction)(Document*"; + +if ($parameters{'namespace'} eq "HTML") { + print F ", HTMLFormElement* formElement"; +} + +print F ", bool createdByParser);\n"; + +print F <<END +typedef WTF::HashMap<AtomicStringImpl*, ConstructorFunction> FunctionMap; static FunctionMap* gFunctionMap = 0; @@ -532,7 +591,7 @@ namespace WebCore { END ; -printConstructors($F, \%tags); +printConstructors($F); print F "#if $parameters{'guardFactoryWith'}\n" if $parameters{'guardFactoryWith'}; @@ -548,16 +607,18 @@ static inline void createFunctionMapIfNecessary() END ; -printFunctionInits($F, \%tags); +printFunctionInits($F); print F "}\n"; -print F "#endif\n\n" if $parameters{'guardFactoryWith'}; +print F "#endif\n" if $parameters{'guardFactoryWith'}; -print F <<END -$parameters{'namespace'}Element* $parameters{'namespace'}ElementFactory::create$parameters{'namespace'}Element(const QualifiedName& qName, Document* doc, bool createdByParser) -{ -END -; +print F "\nPassRefPtr<$parameters{'namespace'}Element> $parameters{'namespace'}ElementFactory::create$parameters{'namespace'}Element(const QualifiedName& qName, Document* doc"; + +if ($parameters{"namespace"} eq "HTML") { + print F ", HTMLFormElement* formElement"; +} + +print F ", bool createdByParser)\n{\n"; print F "#if $parameters{'guardFactoryWith'}\n" if $parameters{'guardFactoryWith'}; @@ -573,14 +634,19 @@ print F <<END #endif createFunctionMapIfNecessary(); - ConstructorFunc func = gFunctionMap->get(qName.localName().impl()); + ConstructorFunction func = gFunctionMap->get(qName.localName().impl()); if (func) - return func(doc, createdByParser); - - return new $parameters{'namespace'}Element(qName, doc); END ; +if ($parameters{"namespace"} eq "HTML") { + print F " return func(doc, formElement, createdByParser);\n"; +} else { + print F " return func(doc, createdByParser);\n"; +} + +print F " return new $parameters{'namespace'}Element(qName, doc);\n"; + if ($parameters{'guardFactoryWith'}) { print F <<END @@ -595,7 +661,7 @@ END print F <<END } -} // namespace +} // namespace WebCore END ; @@ -611,10 +677,12 @@ sub printFactoryHeaderFile printLicenseHeader($F); -print F "#ifndef $parameters{'namespace'}ELEMENTFACTORY_H\n"; -print F "#define $parameters{'namespace'}ELEMENTFACTORY_H\n\n"; + print F<<END +#ifndef $parameters{'namespace'}ElementFactory_h +#define $parameters{'namespace'}ElementFactory_h + +#include <wtf/PassRefPtr.h> -print F " namespace WebCore { class Element; class Document; @@ -625,121 +693,79 @@ namespace WebCore { namespace WebCore { class $parameters{'namespace'}Element; +END +; + +if ($parameters{'namespace'} eq "HTML") { + print F " class HTMLFormElement;\n"; +} +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 - { + // elements. In a compound document world, the generic createElement function (will end up being virtual) will be called. + class $parameters{'namespace'}ElementFactory { public: - WebCore::Element* createElement(const WebCore::QualifiedName& qName, WebCore::Document* doc, bool createdByParser = true); - static $parameters{'namespace'}Element* create$parameters{'namespace'}Element(const WebCore::QualifiedName& qName, WebCore::Document* doc, bool createdByParser = true); + 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*"; + +if ($parameters{'namespace'} eq "HTML") { + print F ", HTMLFormElement* = 0"; +} + +print F ", bool /*createdByParser*/ = true);\n"; + +printf F<<END }; } -#endif +#endif // $parameters{'namespace'}ElementFactory_h -"; +END +; close F; } ## Wrapper Factory routines -sub initializeCustomMappings -{ - if (!keys %svgCustomMappings) { - # These are used to map a tag to another one in WrapperFactory - # (for example, "h2" is mapped to "h1" so that they use the same JS Wrapper ("h1" wrapper)) - # Mapping to an empty string will not generate a wrapper - %svgCustomMappings = ('animateMotion' => '', - 'hkern' => '', - 'mpath' => ''); - %htmlCustomMappings = ('abbr' => '', - 'acronym' => '', - 'address' => '', - 'b' => '', - 'bdo' => '', - 'big' => '', - 'center' => '', - 'cite' => '', - 'code' => '', - 'colgroup' => 'col', - 'dd' => '', - 'dfn' => '', - 'dt' => '', - 'em' => '', - 'h2' => 'h1', - 'h3' => 'h1', - 'h4' => 'h1', - 'h5' => 'h1', - 'h6' => 'h1', - 'i' => '', - 'image' => 'img', - 'ins' => 'del', - 'kbd' => '', - 'keygen' => 'select', - 'listing' => 'pre', - 'layer' => '', - 'nobr' => '', - 'noembed' => '', - 'noframes' => '', - 'nolayer' => '', - 'noscript' => '', - 'plaintext' => '', - 's' => '', - 'samp' => '', - 'small' => '', - 'span' => '', - 'strike' => '', - 'strong' => '', - 'sub' => '', - 'sup' => '', - 'tfoot' => 'tbody', - 'th' => 'td', - 'thead' => 'tbody', - 'tt' => '', - 'u' => '', - 'var' => '', - 'wbr' => '', - 'xmp' => 'pre'); - } -} - -sub hasCustomMapping +sub usesDefaultJSWrapper { my $name = shift; - initializeCustomMappings(); - return 1 if $parameters{'namespace'} eq "HTML" && exists($htmlCustomMappings{$name}); - return 1 if $parameters{'namespace'} eq "SVG" && exists($svgCustomMappings{$name}); - return 0; + + # A tag reuses the default wrapper if its JSInterfaceName matches the default namespace Element. + return $tags{$name}{'JSInterfaceName'} eq $parameters{"namespace"} . "Element"; } sub printWrapperFunctions { - my ($F, $namesRef) = @_; - my %names = %$namesRef; - for my $name (sort keys %names) { - # Custom mapping do not need a JS wrapper - next if (hasCustomMapping($name)); + my $F = shift; + + my %tagsSeen; + for my $tagName (sort keys %tags) { + # Avoid defining the same wrapper method twice. + my $JSInterfaceName = $tags{$tagName}{"JSInterfaceName"}; + next if defined($tagsSeen{$JSInterfaceName}) || usesDefaultJSWrapper($tagName); + $tagsSeen{$JSInterfaceName} = 1; - my $ucName = $names{$name}{"interfaceName"}; # Hack for the media tags - if ($names{$name}{"applyAudioHack"}) { + if ($tags{$tagName}{"wrapperOnlyIfMediaIsAvailable"}) { print F <<END -static JSNode* create${ucName}Wrapper(ExecState* exec, PassRefPtr<$parameters{'namespace'}Element> element) +static JSNode* create${JSInterfaceName}Wrapper(ExecState* exec, PassRefPtr<$parameters{'namespace'}Element> element) { if (!MediaPlayer::isAvailable()) return CREATE_DOM_NODE_WRAPPER(exec, $parameters{'namespace'}Element, element.get()); - return CREATE_DOM_NODE_WRAPPER(exec, $parameters{'namespace'}${ucName}, element.get()); + return CREATE_DOM_NODE_WRAPPER(exec, ${JSInterfaceName}, element.get()); } END ; } else { print F <<END -static JSNode* create${ucName}Wrapper(ExecState* exec, PassRefPtr<$parameters{'namespace'}Element> element) +static JSNode* create${JSInterfaceName}Wrapper(ExecState* exec, PassRefPtr<$parameters{'namespace'}Element> element) { - return CREATE_DOM_NODE_WRAPPER(exec, $parameters{'namespace'}${ucName}, element.get()); + return CREATE_DOM_NODE_WRAPPER(exec, ${JSInterfaceName}, element.get()); } END @@ -762,12 +788,14 @@ sub printWrapperFactoryCppFile print F "#include \"JS$parameters{'namespace'}ElementWrapperFactory.h\"\n"; - printJSElementIncludes($F, \%tags); + printJSElementIncludes($F); print F "\n#include \"$parameters{'namespace'}Names.h\"\n\n"; - printElementIncludes($F, \%tags, 1); + printElementIncludes($F); + print F "\n#include <wtf/StdLibExtras.h>\n\n"; + print F <<END using namespace JSC; @@ -780,34 +808,25 @@ typedef JSNode* (*Create$parameters{'namespace'}ElementWrapperFunction)(ExecStat END ; - printWrapperFunctions($F, \%tags); + printWrapperFunctions($F); print F <<END JSNode* createJS$parameters{'namespace'}Wrapper(ExecState* exec, PassRefPtr<$parameters{'namespace'}Element> element) { - static HashMap<WebCore::AtomicStringImpl*, Create$parameters{'namespace'}ElementWrapperFunction> map; + typedef HashMap<WebCore::AtomicStringImpl*, Create$parameters{'namespace'}ElementWrapperFunction> FunctionMap; + DEFINE_STATIC_LOCAL(FunctionMap, map, ()); if (map.isEmpty()) { END ; for my $tag (sort keys %tags) { - next if (hasCustomMapping($tag)); + # 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, \%tags); - my $ucTag = $tags{$tag}{"interfaceName"}; + my $ucTag = $tags{$tag}{"JSInterfaceName"}; print F " map.set(${tag}Tag.localName().impl(), create${ucTag}Wrapper);\n"; } - if ($parameters{'namespace'} eq "HTML") { - for my $tag (sort keys %htmlCustomMappings) { - next if !$htmlCustomMappings{$tag}; - - my $ucCustomTag = $tags{$htmlCustomMappings{$tag}}{"interfaceName"}; - print F " map.set(${tag}Tag.localName().impl(), create${ucCustomTag}Wrapper);\n"; - } - } - - # Currently SVG has no need to add custom map.set as it only has empty elements - print F <<END } Create$parameters{'namespace'}ElementWrapperFunction createWrapperFunction = map.get(element->localName().impl()); |