diff options
Diffstat (limited to 'WebCore/dom')
27 files changed, 1039 insertions, 768 deletions
diff --git a/WebCore/dom/ContainerNode.cpp b/WebCore/dom/ContainerNode.cpp index e559ba7..ef62b38 100644 --- a/WebCore/dom/ContainerNode.cpp +++ b/WebCore/dom/ContainerNode.cpp @@ -134,31 +134,12 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce if (child->parentNode()) break; - ASSERT(!child->nextSibling()); - ASSERT(!child->previousSibling()); - - // Add child before "next". - forbidEventDispatch(); - Node* prev = next->previousSibling(); - ASSERT(m_lastChild != prev); - next->setPreviousSibling(child); - if (prev) { - ASSERT(m_firstChild != next); - ASSERT(prev->nextSibling() == next); - prev->setNextSibling(child); - } else { - ASSERT(m_firstChild == next); - m_firstChild = child; - } - child->setParent(this); - child->setPreviousSibling(prev); - child->setNextSibling(next.get()); - allowEventDispatch(); + insertBeforeCommon(next.get(), child); // Send notification about the children change. childrenChanged(false, refChildPreviousSibling.get(), next.get(), 1); notifyChildInserted(child); - + // Add child to the rendering tree. if (attached() && !child->attached() && child->parent() == this) { if (shouldLazyAttach) @@ -176,6 +157,57 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce return true; } +void ContainerNode::insertBeforeCommon(Node* nextChild, Node* newChild) +{ + ASSERT(newChild); + ASSERT(!newChild->parent()); // Use insertBefore if you need to handle reparenting (and want DOM mutation events). + ASSERT(!newChild->nextSibling()); + ASSERT(!newChild->previousSibling()); + + forbidEventDispatch(); + Node* prev = nextChild->previousSibling(); + ASSERT(m_lastChild != prev); + nextChild->setPreviousSibling(newChild); + if (prev) { + ASSERT(m_firstChild != nextChild); + ASSERT(prev->nextSibling() == nextChild); + prev->setNextSibling(newChild); + } else { + ASSERT(m_firstChild == nextChild); + m_firstChild = newChild; + } + newChild->setParent(this); + newChild->setPreviousSibling(prev); + newChild->setNextSibling(nextChild); + allowEventDispatch(); +} + +void ContainerNode::parserInsertBefore(PassRefPtr<Node> newChild, Node* nextChild) +{ + ASSERT(newChild); + ASSERT(nextChild); + ASSERT(nextChild->parentNode() == this); + + NodeVector targets; + collectTargetNodes(newChild.get(), targets); + if (targets.isEmpty()) + return; + + if (nextChild->previousSibling() == newChild || nextChild == newChild) // nothing to do + return; + + RefPtr<Node> next = nextChild; + RefPtr<Node> nextChildPreviousSibling = nextChild->previousSibling(); + for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { + Node* child = it->get(); + + insertBeforeCommon(next.get(), child); + + childrenChanged(true, nextChildPreviousSibling.get(), nextChild, 1); + notifyChildInserted(child); + } +} + bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec, bool shouldLazyAttach) { // Check that this node is not "floating". @@ -367,31 +399,9 @@ bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec) // that no callers call with ref count == 0 and parent = 0 (as of this // writing, there are definitely callers who call that way). - forbidEventDispatch(); - - // Remove from rendering tree - if (child->attached()) - child->detach(); - - // Remove the child - Node *prev, *next; - prev = child->previousSibling(); - next = child->nextSibling(); - - if (next) - next->setPreviousSibling(prev); - if (prev) - prev->setNextSibling(next); - if (m_firstChild == child) - m_firstChild = next; - if (m_lastChild == child) - m_lastChild = prev; - - child->setPreviousSibling(0); - child->setNextSibling(0); - child->setParent(0); - - allowEventDispatch(); + Node* prev = child->previousSibling(); + Node* next = child->nextSibling(); + removeBetween(prev, next, child.get()); // Dispatch post-removal mutation events childrenChanged(false, prev, next, -1); @@ -405,6 +415,50 @@ bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec) return child; } +void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node* oldChild) +{ + ASSERT(oldChild); + ASSERT(oldChild->parentNode() == this); + + forbidEventDispatch(); + + // Remove from rendering tree + if (oldChild->attached()) + oldChild->detach(); + + if (nextChild) + nextChild->setPreviousSibling(previousChild); + if (previousChild) + previousChild->setNextSibling(nextChild); + if (m_firstChild == oldChild) + m_firstChild = nextChild; + if (m_lastChild == oldChild) + m_lastChild = previousChild; + + oldChild->setPreviousSibling(0); + oldChild->setNextSibling(0); + oldChild->setParent(0); + + allowEventDispatch(); +} + +void ContainerNode::parserRemoveChild(Node* oldChild) +{ + ASSERT(oldChild); + ASSERT(oldChild->parentNode() == this); + + Node* prev = oldChild->previousSibling(); + Node* next = oldChild->nextSibling(); + + removeBetween(prev, next, oldChild); + + childrenChanged(true, prev, next, -1); + if (oldChild->inDocument()) + oldChild->removedFromDocument(); + else + oldChild->removedFromTree(true); +} + // this differs from other remove functions because it forcibly removes all the children, // regardless of read-only status or event exceptions, e.g. bool ContainerNode::removeChildren() @@ -534,45 +588,27 @@ bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bo return true; } -void ContainerNode::addChildCommon(Node* newChild) +void ContainerNode::parserAddChild(PassRefPtr<Node> newChild) { - ASSERT(!newChild->parent()); // Use appendChild if you need to handle reparenting. + ASSERT(newChild); + ASSERT(!newChild->parent()); // Use appendChild if you need to handle reparenting (and want DOM mutation events). + forbidEventDispatch(); Node* last = m_lastChild; // FIXME: This method should take a PassRefPtr. - appendChildToContainer<Node, ContainerNode>(newChild, this); + appendChildToContainer<Node, ContainerNode>(newChild.get(), this); allowEventDispatch(); + // FIXME: Why doesn't this use notifyChildInserted(newChild) instead? document()->incDOMTreeVersion(); if (inDocument()) newChild->insertedIntoDocument(); childrenChanged(true, last, 0, 1); } -void ContainerNode::parserAddChild(PassRefPtr<Node> newChild) +void ContainerNode::deprecatedParserAddChild(PassRefPtr<Node> node) { - ASSERT(newChild); - // This function is only used during parsing. - // It does not send any DOM mutation events or handle reparenting. - - addChildCommon(newChild.get()); -} - -ContainerNode* ContainerNode::legacyParserAddChild(PassRefPtr<Node> newChild) -{ - ASSERT(newChild); - // This function is only used during parsing. - // It does not send any DOM mutation events. - - // Check for consistency with DTD, but only when parsing HTML. - if (document()->isHTMLDocument() && !childAllowed(newChild.get())) - return 0; - - addChildCommon(newChild.get()); - - if (newChild->isElementNode()) - return static_cast<ContainerNode*>(newChild.get()); - return this; + parserAddChild(node); } void ContainerNode::suspendPostAttachCallbacks() diff --git a/WebCore/dom/ContainerNode.h b/WebCore/dom/ContainerNode.h index a27d95b..ad0a54a 100644 --- a/WebCore/dom/ContainerNode.h +++ b/WebCore/dom/ContainerNode.h @@ -48,8 +48,12 @@ public: virtual bool removeChild(Node* child, ExceptionCode&); virtual bool appendChild(PassRefPtr<Node> newChild, ExceptionCode&, bool shouldLazyAttach = false); - virtual ContainerNode* legacyParserAddChild(PassRefPtr<Node>); - virtual void parserAddChild(PassRefPtr<Node>); + // These methods are only used during parsing. + // They don't send DOM mutation events or handle reparenting. + // However, arbitrary code may be run by beforeload handlers. + void parserAddChild(PassRefPtr<Node>); + void parserRemoveChild(Node*); + void parserInsertBefore(PassRefPtr<Node> newChild, Node* refChild); bool hasChildNodes() const { return m_firstChild; } virtual void attach(); @@ -95,8 +99,14 @@ protected: void setLastChild(Node* child) { m_lastChild = child; } private: - // FIXME: This should take a PassRefPtr. - void addChildCommon(Node*); + // Never call this function directly. If you're trying to call this + // function, your code is either wrong or you're supposed to call + // parserAddChild. Please do not call parserAddChild unless you are the + // parser! + virtual void deprecatedParserAddChild(PassRefPtr<Node>); + + void removeBetween(Node* previousChild, Node* nextChild, Node* oldChild); + void insertBeforeCommon(Node* nextChild, Node* oldChild); static void dispatchPostAttachCallbacks(); diff --git a/WebCore/dom/DOMImplementation.cpp b/WebCore/dom/DOMImplementation.cpp index 467f1fc..30e889f 100644 --- a/WebCore/dom/DOMImplementation.cpp +++ b/WebCore/dom/DOMImplementation.cpp @@ -258,9 +258,9 @@ PassRefPtr<Document> DOMImplementation::createDocument(const String& namespaceUR // FIXME: Shouldn't this call appendChild instead? if (doctype) - doc->legacyParserAddChild(doctype); + doc->parserAddChild(doctype); if (documentElement) - doc->legacyParserAddChild(documentElement.release()); + doc->parserAddChild(documentElement.release()); return doc.release(); } diff --git a/WebCore/dom/DeviceOrientationController.cpp b/WebCore/dom/DeviceOrientationController.cpp index 111577f..a744366 100644 --- a/WebCore/dom/DeviceOrientationController.cpp +++ b/WebCore/dom/DeviceOrientationController.cpp @@ -44,9 +44,9 @@ DeviceOrientationController::DeviceOrientationController(Page* page, DeviceOrien void DeviceOrientationController::timerFired(Timer<DeviceOrientationController>* timer) { ASSERT_UNUSED(timer, timer == &m_timer); - ASSERT(!m_client || m_client->lastOrientation()); + ASSERT(m_client->lastOrientation()); - RefPtr<DeviceOrientation> orientation = m_client ? m_client->lastOrientation() : DeviceOrientation::create(); + RefPtr<DeviceOrientation> orientation = m_client->lastOrientation(); RefPtr<DeviceOrientationEvent> event = DeviceOrientationEvent::create(eventNames().deviceorientationEvent, orientation.get()); Vector<DOMWindow*> listenersVector; @@ -58,11 +58,10 @@ void DeviceOrientationController::timerFired(Timer<DeviceOrientationController>* void DeviceOrientationController::addListener(DOMWindow* window) { - // If no client is present, we should fire an event with all parameters null. If - // the client already has an orientation, we should fire an event with that - // orientation. In both cases, the event is fired asynchronously, but without + // If the client already has an orientation, we should fire an event with that + // orientation. The event is fired asynchronously, but without // waiting for the client to get a new orientation. - if (!m_client || m_client->lastOrientation()) { + if (m_client->lastOrientation()) { m_newListeners.add(window); if (!m_timer.isActive()) m_timer.startOneShot(0); @@ -71,7 +70,7 @@ void DeviceOrientationController::addListener(DOMWindow* window) // The client must not call back synchronously. bool wasEmpty = m_listeners.isEmpty(); m_listeners.add(window); - if (wasEmpty && m_client) + if (wasEmpty) m_client->startUpdating(); } @@ -79,7 +78,7 @@ void DeviceOrientationController::removeListener(DOMWindow* window) { m_listeners.remove(window); m_newListeners.remove(window); - if (m_listeners.isEmpty() && m_client) + if (m_listeners.isEmpty()) m_client->stopUpdating(); } @@ -91,7 +90,7 @@ void DeviceOrientationController::removeAllListeners(DOMWindow* window) m_listeners.removeAll(window); m_newListeners.remove(window); - if (m_listeners.isEmpty() && m_client) + if (m_listeners.isEmpty()) m_client->stopUpdating(); } diff --git a/WebCore/dom/Document.cpp b/WebCore/dom/Document.cpp index 20c6b16..03ad8e7 100644 --- a/WebCore/dom/Document.cpp +++ b/WebCore/dom/Document.cpp @@ -89,7 +89,6 @@ #include "InspectorController.h" #include "InspectorTimelineAgent.h" #include "KeyboardEvent.h" -#include "LegacyHTMLTreeBuilder.h" #include "Logging.h" #include "MessageEvent.h" #include "MouseEvent.h" @@ -123,7 +122,6 @@ #include "StaticHashSetNodeList.h" #include "StyleSheetList.h" #include "TextEvent.h" -#include "TextIterator.h" #include "TextResourceDecoder.h" #include "Timer.h" #include "TransformSource.h" @@ -165,10 +163,6 @@ #include "XSLTProcessor.h" #endif -#if ENABLE(XBL) -#include "XBLBindingManager.h" -#endif - #if ENABLE(SVG) #include "SVGDocumentExtensions.h" #include "SVGElementFactory.h" @@ -394,9 +388,6 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML) , m_asyncScriptRunner(AsyncScriptRunner::create()) , m_xmlVersion("1.0") , m_xmlStandalone(false) -#if ENABLE(XBL) - , m_bindingManager(new XBLBindingManager(this)) -#endif , m_savedRenderer(0) , m_designMode(inherit) , m_selfOnlyRefCount(0) @@ -441,8 +432,14 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML) #if !PLATFORM(ANDROID) m_axObjectCache = 0; +<<<<<<< HEAD #endif +======= + + m_markers = new DocumentMarkerController(); + +>>>>>>> webkit.org at r66079 m_docLoader = new DocLoader(this); m_visuallyOrdered = false; @@ -515,10 +512,9 @@ void Document::removedLastRef() removeAllChildren(); - deleteAllValues(m_markers); - m_markers.clear(); + m_markers->detach(); - m_parser.clear(); + detachParser(); m_cssCanvasElements.clear(); @@ -551,18 +547,19 @@ Document::~Document() destroyAllWrapperCaches(); #endif - m_parser.clear(); + // Currently we believe that Document can never outlive the parser. + // Although the Document may be replaced synchronously, DocumentParsers + // generally keep at least one reference to an Element which would in turn + // has a reference to the Document. If you hit this ASSERT, then that + // assumption is wrong. DocumentParser::detach() should ensure that even + // if the DocumentParser outlives the Document it won't cause badness. + ASSERT(!m_parser || m_parser->refCount() == 1); + detachParser(); m_document = 0; m_docLoader.clear(); m_renderArena.clear(); -#if ENABLE(XBL) - m_bindingManager.clear(); -#endif - - deleteAllValues(m_markers); - clearAXObjectCache(); m_decoder = 0; @@ -1009,8 +1006,11 @@ void Document::setXMLVersion(const String& version, ExceptionCode& ec) ec = NOT_SUPPORTED_ERR; return; } - - // FIXME: Also raise NOT_SUPPORTED_ERR if the version is set to a value that is not supported by this Document. + + if (!XMLDocumentParser::supportsXMLVersion(version)) { + ec = NOT_SUPPORTED_ERR; + return; + } m_xmlVersion = version; } @@ -1514,6 +1514,8 @@ bail_out: void Document::updateStyleIfNeeded() { + ASSERT(!view() || (!view()->isInLayout() && !view()->isPainting())); + if (!childNeedsStyleRecalc() || inPageCache()) return; @@ -1822,10 +1824,10 @@ void Document::setVisuallyOrdered() renderer()->style()->setVisuallyOrdered(true); } -DocumentParser* Document::createParser() +PassRefPtr<DocumentParser> Document::createParser() { // FIXME: this should probably pass the frame instead - return new XMLDocumentParser(this, view()); + return XMLDocumentParser::create(this, view()); } ScriptableDocumentParser* Document::scriptableDocumentParser() const @@ -1859,6 +1861,14 @@ void Document::open(Document* ownerDocument) m_frame->loader()->didExplicitOpen(); } +void Document::detachParser() +{ + if (!m_parser) + return; + m_parser->detach(); + m_parser.clear(); +} + void Document::cancelParsing() { if (m_parser) { @@ -1866,7 +1876,7 @@ void Document::cancelParsing() // the onload handler when closing as a side effect of a cancel-style // change, such as opening a new document or closing the window while // still parsing - m_parser.clear(); + detachParser(); close(); } } @@ -1875,8 +1885,6 @@ void Document::implicitOpen() { cancelParsing(); - m_parser.clear(); - removeChildren(); m_parser = createParser(); @@ -1953,6 +1961,26 @@ void Document::close() } } +// FIXME: These settings probably don't work anymore. We should either remove +// them or make them work properly. +#ifdef BUILDING_ON_LEOPARD +static bool shouldCreateImplicitHead(Document* document) +{ + ASSERT(document); + Settings* settings = document->page() ? document->page()->settings() : 0; + return settings ? !settings->needsLeopardMailQuirks() : true; +} +#elif defined(BUILDING_ON_TIGER) +static bool shouldCreateImplicitHead(Document* document) +{ + ASSERT(document); + Settings* settings = document->page() ? document->page()->settings() : 0; + return settings ? !settings->needsTigerMailQuirks() : true; +} +#else +inline bool shouldCreateImplicitHead(Document*) { return true; } +#endif + void Document::implicitClose() { // If we're in the middle of recalcStyle, we need to defer the close until the style information is accurate and all elements are re-attached. @@ -1974,7 +2002,7 @@ void Document::implicitClose() // We have to clear the parser, in case someone document.write()s from the // onLoad event handler, as in Radar 3206524. - m_parser.clear(); + detachParser(); // Parser should have picked up all preloads by now m_docLoader->clearPreloads(); @@ -3233,7 +3261,7 @@ void Document::textInserted(Node* text, unsigned offset, unsigned length) } // Update the markers for spelling and grammar checking. - shiftMarkers(text, offset, length); + m_markers->shiftMarkers(text, offset, length); } void Document::textRemoved(Node* text, unsigned offset, unsigned length) @@ -3245,8 +3273,8 @@ void Document::textRemoved(Node* text, unsigned offset, unsigned length) } // Update the markers for spelling and grammar checking. - removeMarkers(text, offset, length); - shiftMarkers(text, offset + length, 0 - length); + m_markers->removeMarkers(text, offset, length); + m_markers->shiftMarkers(text, offset + length, 0 - length); } void Document::textNodesMerged(Text* oldNode, unsigned offset) @@ -3822,6 +3850,9 @@ static Editor::Command command(Document* document, const String& commandName, bo Frame* frame = document->frame(); if (!frame || frame->document() != document) return Editor::Command(); + + document->updateStyleIfNeeded(); + return frame->editor()->command(commandName, userInterface ? CommandFromDOMWithUserInterface : CommandFromDOM); } @@ -3856,489 +3887,6 @@ String Document::queryCommandValue(const String& commandName) return command(this, commandName).value(); } -static IntRect placeholderRectForMarker() -{ - return IntRect(-1, -1, -1, -1); -} - -void Document::addMarker(Range* range, DocumentMarker::MarkerType type, String description) -{ - // Use a TextIterator to visit the potentially multiple nodes the range covers. - for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { - RefPtr<Range> textPiece = markedText.range(); - int exception = 0; - DocumentMarker marker = {type, textPiece->startOffset(exception), textPiece->endOffset(exception), description, false}; - addMarker(textPiece->startContainer(exception), marker); - } -} - -void Document::removeMarkers(Range* range, DocumentMarker::MarkerType markerType) -{ - if (m_markers.isEmpty()) - return; - - ExceptionCode ec = 0; - Node* startContainer = range->startContainer(ec); - Node* endContainer = range->endContainer(ec); - - Node* pastLastNode = range->pastLastNode(); - for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { - int startOffset = node == startContainer ? range->startOffset(ec) : 0; - int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; - int length = endOffset - startOffset; - removeMarkers(node, startOffset, length, markerType); - } -} - -// Markers are stored in order sorted by their start offset. -// Markers of the same type do not overlap each other. - -void Document::addMarker(Node* node, DocumentMarker newMarker) -{ - ASSERT(newMarker.endOffset >= newMarker.startOffset); - if (newMarker.endOffset == newMarker.startOffset) - return; - - MarkerMapVectorPair* vectorPair = m_markers.get(node); - - if (!vectorPair) { - vectorPair = new MarkerMapVectorPair; - vectorPair->first.append(newMarker); - vectorPair->second.append(placeholderRectForMarker()); - m_markers.set(node, vectorPair); - } else { - Vector<DocumentMarker>& markers = vectorPair->first; - Vector<IntRect>& rects = vectorPair->second; - size_t numMarkers = markers.size(); - ASSERT(numMarkers == rects.size()); - size_t i; - // Iterate over all markers whose start offset is less than or equal to the new marker's. - // If one of them is of the same type as the new marker and touches it or intersects with it - // (there is at most one), remove it and adjust the new marker's start offset to encompass it. - for (i = 0; i < numMarkers; ++i) { - DocumentMarker marker = markers[i]; - if (marker.startOffset > newMarker.startOffset) - break; - if (marker.type == newMarker.type && marker.endOffset >= newMarker.startOffset) { - newMarker.startOffset = marker.startOffset; - markers.remove(i); - rects.remove(i); - numMarkers--; - break; - } - } - size_t j = i; - // Iterate over all markers whose end offset is less than or equal to the new marker's, - // removing markers of the same type as the new marker which touch it or intersect with it, - // adjusting the new marker's end offset to cover them if necessary. - while (j < numMarkers) { - DocumentMarker marker = markers[j]; - if (marker.startOffset > newMarker.endOffset) - break; - if (marker.type == newMarker.type) { - markers.remove(j); - rects.remove(j); - if (newMarker.endOffset <= marker.endOffset) { - newMarker.endOffset = marker.endOffset; - break; - } - numMarkers--; - } else - j++; - } - // At this point i points to the node before which we want to insert. - markers.insert(i, newMarker); - rects.insert(i, placeholderRectForMarker()); - } - - // repaint the affected node - if (node->renderer()) - node->renderer()->repaint(); -} - -// copies markers from srcNode to dstNode, applying the specified shift delta to the copies. The shift is -// useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode. -void Document::copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta, DocumentMarker::MarkerType markerType) -{ - if (length <= 0) - return; - - MarkerMapVectorPair* vectorPair = m_markers.get(srcNode); - if (!vectorPair) - return; - - ASSERT(vectorPair->first.size() == vectorPair->second.size()); - - bool docDirty = false; - unsigned endOffset = startOffset + length - 1; - Vector<DocumentMarker>& markers = vectorPair->first; - for (size_t i = 0; i != markers.size(); ++i) { - DocumentMarker marker = markers[i]; - - // stop if we are now past the specified range - if (marker.startOffset > endOffset) - break; - - // skip marker that is before the specified range or is the wrong type - if (marker.endOffset < startOffset || (marker.type != markerType && markerType != DocumentMarker::AllMarkers)) - continue; - - // pin the marker to the specified range and apply the shift delta - docDirty = true; - if (marker.startOffset < startOffset) - marker.startOffset = startOffset; - if (marker.endOffset > endOffset) - marker.endOffset = endOffset; - marker.startOffset += delta; - marker.endOffset += delta; - - addMarker(dstNode, marker); - } - - // repaint the affected node - if (docDirty && dstNode->renderer()) - dstNode->renderer()->repaint(); -} - -void Document::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerType markerType) -{ - if (length <= 0) - return; - - MarkerMapVectorPair* vectorPair = m_markers.get(node); - if (!vectorPair) - return; - - Vector<DocumentMarker>& markers = vectorPair->first; - Vector<IntRect>& rects = vectorPair->second; - ASSERT(markers.size() == rects.size()); - bool docDirty = false; - unsigned endOffset = startOffset + length; - for (size_t i = 0; i < markers.size();) { - DocumentMarker marker = markers[i]; - - // markers are returned in order, so stop if we are now past the specified range - if (marker.startOffset >= endOffset) - break; - - // skip marker that is wrong type or before target - if (marker.endOffset < startOffset || (marker.type != markerType && markerType != DocumentMarker::AllMarkers)) { - i++; - continue; - } - - // at this point we know that marker and target intersect in some way - docDirty = true; - - // pitch the old marker and any associated rect - markers.remove(i); - rects.remove(i); - - // add either of the resulting slices that are left after removing target - if (startOffset > marker.startOffset) { - DocumentMarker newLeft = marker; - newLeft.endOffset = startOffset; - markers.insert(i, newLeft); - rects.insert(i, placeholderRectForMarker()); - // i now points to the newly-inserted node, but we want to skip that one - i++; - } - if (marker.endOffset > endOffset) { - DocumentMarker newRight = marker; - newRight.startOffset = endOffset; - markers.insert(i, newRight); - rects.insert(i, placeholderRectForMarker()); - // i now points to the newly-inserted node, but we want to skip that one - i++; - } - } - - if (markers.isEmpty()) { - ASSERT(rects.isEmpty()); - m_markers.remove(node); - delete vectorPair; - } - - // repaint the affected node - if (docDirty && node->renderer()) - node->renderer()->repaint(); -} - -DocumentMarker* Document::markerContainingPoint(const IntPoint& point, DocumentMarker::MarkerType markerType) -{ - // outer loop: process each node that contains any markers - MarkerMap::iterator end = m_markers.end(); - for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { - // inner loop; process each marker in this node - MarkerMapVectorPair* vectorPair = nodeIterator->second; - Vector<DocumentMarker>& markers = vectorPair->first; - Vector<IntRect>& rects = vectorPair->second; - ASSERT(markers.size() == rects.size()); - unsigned markerCount = markers.size(); - for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { - DocumentMarker& marker = markers[markerIndex]; - - // skip marker that is wrong type - if (marker.type != markerType && markerType != DocumentMarker::AllMarkers) - continue; - - IntRect& r = rects[markerIndex]; - - // skip placeholder rects - if (r == placeholderRectForMarker()) - continue; - - if (r.contains(point)) - return ▮ - } - } - - return 0; -} - -Vector<DocumentMarker> Document::markersForNode(Node* node) -{ - MarkerMapVectorPair* vectorPair = m_markers.get(node); - if (vectorPair) - return vectorPair->first; - return Vector<DocumentMarker>(); -} - -Vector<IntRect> Document::renderedRectsForMarkers(DocumentMarker::MarkerType markerType) -{ - Vector<IntRect> result; - - // outer loop: process each node - MarkerMap::iterator end = m_markers.end(); - for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { - // inner loop; process each marker in this node - MarkerMapVectorPair* vectorPair = nodeIterator->second; - Vector<DocumentMarker>& markers = vectorPair->first; - Vector<IntRect>& rects = vectorPair->second; - ASSERT(markers.size() == rects.size()); - unsigned markerCount = markers.size(); - for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { - DocumentMarker marker = markers[markerIndex]; - - // skip marker that is wrong type - if (marker.type != markerType && markerType != DocumentMarker::AllMarkers) - continue; - - IntRect r = rects[markerIndex]; - // skip placeholder rects - if (r == placeholderRectForMarker()) - continue; - - result.append(r); - } - } - - return result; -} - -void Document::removeMarkers(Node* node) -{ - MarkerMap::iterator i = m_markers.find(node); - if (i != m_markers.end()) { - delete i->second; - m_markers.remove(i); - if (RenderObject* renderer = node->renderer()) - renderer->repaint(); - } -} - -void Document::removeMarkers(DocumentMarker::MarkerType markerType) -{ - // outer loop: process each markered node in the document - MarkerMap markerMapCopy = m_markers; - MarkerMap::iterator end = markerMapCopy.end(); - for (MarkerMap::iterator i = markerMapCopy.begin(); i != end; ++i) { - Node* node = i->first.get(); - bool nodeNeedsRepaint = false; - - // inner loop: process each marker in the current node - MarkerMapVectorPair* vectorPair = i->second; - Vector<DocumentMarker>& markers = vectorPair->first; - Vector<IntRect>& rects = vectorPair->second; - ASSERT(markers.size() == rects.size()); - for (size_t i = 0; i != markers.size();) { - DocumentMarker marker = markers[i]; - - // skip nodes that are not of the specified type - if (marker.type != markerType && markerType != DocumentMarker::AllMarkers) { - ++i; - continue; - } - - // pitch the old marker - markers.remove(i); - rects.remove(i); - nodeNeedsRepaint = true; - // markerIterator now points to the next node - } - - // Redraw the node if it changed. Do this before the node is removed from m_markers, since - // m_markers might contain the last reference to the node. - if (nodeNeedsRepaint) { - RenderObject* renderer = node->renderer(); - if (renderer) - renderer->repaint(); - } - - // delete the node's list if it is now empty - if (markers.isEmpty()) { - ASSERT(rects.isEmpty()); - m_markers.remove(node); - delete vectorPair; - } - } -} - -void Document::repaintMarkers(DocumentMarker::MarkerType markerType) -{ - // outer loop: process each markered node in the document - MarkerMap::iterator end = m_markers.end(); - for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { - Node* node = i->first.get(); - - // inner loop: process each marker in the current node - MarkerMapVectorPair* vectorPair = i->second; - Vector<DocumentMarker>& markers = vectorPair->first; - bool nodeNeedsRepaint = false; - for (size_t i = 0; i != markers.size(); ++i) { - DocumentMarker marker = markers[i]; - - // skip nodes that are not of the specified type - if (marker.type == markerType || markerType == DocumentMarker::AllMarkers) { - nodeNeedsRepaint = true; - break; - } - } - - if (!nodeNeedsRepaint) - continue; - - // cause the node to be redrawn - if (RenderObject* renderer = node->renderer()) - renderer->repaint(); - } -} - -void Document::setRenderedRectForMarker(Node* node, const DocumentMarker& marker, const IntRect& r) -{ - MarkerMapVectorPair* vectorPair = m_markers.get(node); - if (!vectorPair) { - ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about - return; - } - - Vector<DocumentMarker>& markers = vectorPair->first; - ASSERT(markers.size() == vectorPair->second.size()); - unsigned markerCount = markers.size(); - for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { - DocumentMarker m = markers[markerIndex]; - if (m == marker) { - vectorPair->second[markerIndex] = r; - return; - } - } - - ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about -} - -void Document::invalidateRenderedRectsForMarkersInRect(const IntRect& r) -{ - // outer loop: process each markered node in the document - MarkerMap::iterator end = m_markers.end(); - for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { - - // inner loop: process each rect in the current node - MarkerMapVectorPair* vectorPair = i->second; - Vector<IntRect>& rects = vectorPair->second; - - unsigned rectCount = rects.size(); - for (unsigned rectIndex = 0; rectIndex < rectCount; ++rectIndex) - if (rects[rectIndex].intersects(r)) - rects[rectIndex] = placeholderRectForMarker(); - } -} - -void Document::shiftMarkers(Node* node, unsigned startOffset, int delta, DocumentMarker::MarkerType markerType) -{ - MarkerMapVectorPair* vectorPair = m_markers.get(node); - if (!vectorPair) - return; - - Vector<DocumentMarker>& markers = vectorPair->first; - Vector<IntRect>& rects = vectorPair->second; - ASSERT(markers.size() == rects.size()); - - bool docDirty = false; - for (size_t i = 0; i != markers.size(); ++i) { - DocumentMarker& marker = markers[i]; - if (marker.startOffset >= startOffset && (markerType == DocumentMarker::AllMarkers || marker.type == markerType)) { - ASSERT((int)marker.startOffset + delta >= 0); - marker.startOffset += delta; - marker.endOffset += delta; - docDirty = true; - - // Marker moved, so previously-computed rendered rectangle is now invalid - rects[i] = placeholderRectForMarker(); - } - } - - // repaint the affected node - if (docDirty && node->renderer()) - node->renderer()->repaint(); -} - -void Document::setMarkersActive(Range* range, bool active) -{ - if (m_markers.isEmpty()) - return; - - ExceptionCode ec = 0; - Node* startContainer = range->startContainer(ec); - Node* endContainer = range->endContainer(ec); - - Node* pastLastNode = range->pastLastNode(); - for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { - int startOffset = node == startContainer ? range->startOffset(ec) : 0; - int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; - setMarkersActive(node, startOffset, endOffset, active); - } -} - -void Document::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active) -{ - MarkerMapVectorPair* vectorPair = m_markers.get(node); - if (!vectorPair) - return; - - Vector<DocumentMarker>& markers = vectorPair->first; - ASSERT(markers.size() == vectorPair->second.size()); - - bool docDirty = false; - for (size_t i = 0; i != markers.size(); ++i) { - DocumentMarker& marker = markers[i]; - - // Markers are returned in order, so stop if we are now past the specified range. - if (marker.startOffset >= endOffset) - break; - - // Skip marker that is wrong type or before target. - if (marker.endOffset < startOffset || marker.type != DocumentMarker::TextMatch) - continue; - - marker.activeMatch = active; - docDirty = true; - } - - // repaint the affected node - if (docDirty && node->renderer()) - node->renderer()->repaint(); -} - #if ENABLE(XSLT) void Document::applyXSLTransform(ProcessingInstruction* pi) diff --git a/WebCore/dom/Document.h b/WebCore/dom/Document.h index fc57ddb..ff7e646 100644 --- a/WebCore/dom/Document.h +++ b/WebCore/dom/Document.h @@ -32,7 +32,7 @@ #include "CollectionType.h" #include "Color.h" #include "ContainerNode.h" -#include "DocumentMarker.h" +#include "DocumentMarkerController.h" #include "QualifiedName.h" #include "ScriptExecutionContext.h" #include "Timer.h" @@ -125,10 +125,6 @@ class SVGDocumentExtensions; class TransformSource; #endif -#if ENABLE(XBL) -class XBLBindingManager; -#endif - #if ENABLE(XPATH) class XPathEvaluator; class XPathExpression; @@ -563,7 +559,7 @@ public: CSSStyleSheet* elementSheet(); CSSStyleSheet* mappedElementSheet(); - virtual DocumentParser* createParser(); + virtual PassRefPtr<DocumentParser> createParser(); DocumentParser* parser() const { return m_parser.get(); } ScriptableDocumentParser* scriptableDocumentParser() const; @@ -819,6 +815,8 @@ public: HTMLHeadElement* head(); + DocumentMarkerController* markers() const { return m_markers.get(); } + bool execCommand(const String& command, bool userInterface = false, const String& value = String()); bool queryCommandEnabled(const String& command); bool queryCommandIndeterm(const String& command); @@ -826,24 +824,6 @@ public: bool queryCommandSupported(const String& command); String queryCommandValue(const String& command); - void addMarker(Range*, DocumentMarker::MarkerType, String description = String()); - void addMarker(Node*, DocumentMarker); - void copyMarkers(Node *srcNode, unsigned startOffset, int length, Node *dstNode, int delta, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); - void removeMarkers(Range*, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); - void removeMarkers(Node*, unsigned startOffset, int length, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); - void removeMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); - void removeMarkers(Node*); - void repaintMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); - void setRenderedRectForMarker(Node*, const DocumentMarker&, const IntRect&); - void invalidateRenderedRectsForMarkersInRect(const IntRect&); - void shiftMarkers(Node*, unsigned startOffset, int delta, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); - void setMarkersActive(Range*, bool); - void setMarkersActive(Node*, unsigned startOffset, unsigned endOffset, bool); - - DocumentMarker* markerContainingPoint(const IntPoint&, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); - Vector<DocumentMarker> markersForNode(Node*); - Vector<IntRect> renderedRectsForMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); - // designMode support enum InheritedBool { off = false, on = true, inherit }; void setDesignMode(InheritedBool value); @@ -866,11 +846,6 @@ public: TransformSource* transformSource() const { return m_transformSource.get(); } #endif -#if ENABLE(XBL) - // XBL methods - XBLBindingManager* bindingManager() const { return m_bindingManager.get(); } -#endif - void incDOMTreeVersion() { ++m_domTreeVersion; } unsigned domTreeVersion() const { return m_domTreeVersion; } @@ -1035,6 +1010,7 @@ protected: private: + void detachParser(); typedef void (*ArgumentsCallback)(const String& keyString, const String& valueString, Document*, void* data); void processArguments(const String& features, void* data, ArgumentsCallback); @@ -1079,7 +1055,7 @@ private: Frame* m_frame; OwnPtr<DocLoader> m_docLoader; - OwnPtr<DocumentParser> m_parser; + RefPtr<DocumentParser> m_parser; bool m_wellFormed; // Document URLs. @@ -1193,6 +1169,7 @@ private: OwnPtr<RenderArena> m_renderArena; +<<<<<<< HEAD typedef std::pair<Vector<DocumentMarker>, Vector<IntRect> > MarkerMapVectorPair; typedef HashMap<RefPtr<Node>, MarkerMapVectorPair*> MarkerMap; MarkerMap m_markers; @@ -1200,6 +1177,10 @@ private: #if !PLATFORM(ANDROID) mutable AXObjectCache* m_axObjectCache; #endif +======= + mutable AXObjectCache* m_axObjectCache; + OwnPtr<DocumentMarkerController> m_markers; +>>>>>>> webkit.org at r66079 Timer<Document> m_updateFocusAppearanceTimer; @@ -1221,10 +1202,13 @@ private: RefPtr<Document> m_transformSourceDocument; #endif +<<<<<<< HEAD #if ENABLE(XBL) OwnPtr<XBLBindingManager> m_bindingManager; // The access point through which documents and elements communicate with XBL. #endif +======= +>>>>>>> webkit.org at r66079 typedef HashMap<AtomicStringImpl*, HTMLMapElement*> ImageMapsByName; ImageMapsByName m_imageMapsByName; @@ -1327,7 +1311,7 @@ inline bool Node::isDocumentNode() const // here because it uses a Document method but we really want to inline it inline Node::Node(Document* document, ConstructionType type) - : TreeShared<Node>(initialRefCount(type)) + : TreeShared<ContainerNode>(initialRefCount(type)) , m_document(document) , m_previous(0) , m_next(0) diff --git a/WebCore/dom/DocumentFragment.cpp b/WebCore/dom/DocumentFragment.cpp index 70e57b9..c9c3020 100644 --- a/WebCore/dom/DocumentFragment.cpp +++ b/WebCore/dom/DocumentFragment.cpp @@ -75,12 +75,6 @@ PassRefPtr<Node> DocumentFragment::cloneNode(bool deep) return clone.release(); } -bool DocumentFragment::shouldUseLegacyHTMLParser() const -{ - return document()->page() && document()->page()->settings() - && !document()->page()->settings()->html5ParserEnabled(); -} - void DocumentFragment::parseHTML(const String& source, Element* contextElement, FragmentScriptingPermission scriptingPermission) { HTMLDocumentParser::parseDocumentFragment(source, this, contextElement, scriptingPermission); diff --git a/WebCore/dom/DocumentFragment.h b/WebCore/dom/DocumentFragment.h index d3dadb8..d588b4e 100644 --- a/WebCore/dom/DocumentFragment.h +++ b/WebCore/dom/DocumentFragment.h @@ -39,8 +39,6 @@ public: private: DocumentFragment(Document*); - bool shouldUseLegacyHTMLParser() const; - virtual String nodeName() const; virtual NodeType nodeType() const; virtual PassRefPtr<Node> cloneNode(bool deep); diff --git a/WebCore/dom/DocumentMarkerController.cpp b/WebCore/dom/DocumentMarkerController.cpp new file mode 100644 index 0000000..2b7fd85 --- /dev/null +++ b/WebCore/dom/DocumentMarkerController.cpp @@ -0,0 +1,527 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "DocumentMarkerController.h" + +#include "Node.h" +#include "Range.h" +#include "TextIterator.h" + +namespace WebCore { + +static IntRect placeholderRectForMarker() +{ + return IntRect(-1, -1, -1, -1); +} + +void DocumentMarkerController::detach() +{ + if (m_markers.isEmpty()) + return; + deleteAllValues(m_markers); + m_markers.clear(); +} + +void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, String description) +{ + // Use a TextIterator to visit the potentially multiple nodes the range covers. + for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { + RefPtr<Range> textPiece = markedText.range(); + int exception = 0; + DocumentMarker marker = {type, textPiece->startOffset(exception), textPiece->endOffset(exception), description, false}; + addMarker(textPiece->startContainer(exception), marker); + } +} + +void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerType markerType) +{ + if (m_markers.isEmpty()) + return; + + ExceptionCode ec = 0; + Node* startContainer = range->startContainer(ec); + Node* endContainer = range->endContainer(ec); + + Node* pastLastNode = range->pastLastNode(); + for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { + int startOffset = node == startContainer ? range->startOffset(ec) : 0; + int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; + int length = endOffset - startOffset; + removeMarkers(node, startOffset, length, markerType); + } +} + +// Markers are stored in order sorted by their start offset. +// Markers of the same type do not overlap each other. + +void DocumentMarkerController::addMarker(Node* node, DocumentMarker newMarker) +{ + ASSERT(newMarker.endOffset >= newMarker.startOffset); + if (newMarker.endOffset == newMarker.startOffset) + return; + + MarkerMapVectorPair* vectorPair = m_markers.get(node); + + if (!vectorPair) { + vectorPair = new MarkerMapVectorPair; + vectorPair->first.append(newMarker); + vectorPair->second.append(placeholderRectForMarker()); + m_markers.set(node, vectorPair); + } else { + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + size_t numMarkers = markers.size(); + ASSERT(numMarkers == rects.size()); + size_t i; + // Iterate over all markers whose start offset is less than or equal to the new marker's. + // If one of them is of the same type as the new marker and touches it or intersects with it + // (there is at most one), remove it and adjust the new marker's start offset to encompass it. + for (i = 0; i < numMarkers; ++i) { + DocumentMarker marker = markers[i]; + if (marker.startOffset > newMarker.startOffset) + break; + if (marker.type == newMarker.type && marker.endOffset >= newMarker.startOffset) { + newMarker.startOffset = marker.startOffset; + markers.remove(i); + rects.remove(i); + numMarkers--; + break; + } + } + size_t j = i; + // Iterate over all markers whose end offset is less than or equal to the new marker's, + // removing markers of the same type as the new marker which touch it or intersect with it, + // adjusting the new marker's end offset to cover them if necessary. + while (j < numMarkers) { + DocumentMarker marker = markers[j]; + if (marker.startOffset > newMarker.endOffset) + break; + if (marker.type == newMarker.type) { + markers.remove(j); + rects.remove(j); + if (newMarker.endOffset <= marker.endOffset) { + newMarker.endOffset = marker.endOffset; + break; + } + numMarkers--; + } else + j++; + } + // At this point i points to the node before which we want to insert. + markers.insert(i, newMarker); + rects.insert(i, placeholderRectForMarker()); + } + + // repaint the affected node + if (node->renderer()) + node->renderer()->repaint(); +} + +// copies markers from srcNode to dstNode, applying the specified shift delta to the copies. The shift is +// useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode. +void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta, DocumentMarker::MarkerType markerType) +{ + if (length <= 0) + return; + + MarkerMapVectorPair* vectorPair = m_markers.get(srcNode); + if (!vectorPair) + return; + + ASSERT(vectorPair->first.size() == vectorPair->second.size()); + + bool docDirty = false; + unsigned endOffset = startOffset + length - 1; + Vector<DocumentMarker>& markers = vectorPair->first; + for (size_t i = 0; i != markers.size(); ++i) { + DocumentMarker marker = markers[i]; + + // stop if we are now past the specified range + if (marker.startOffset > endOffset) + break; + + // skip marker that is before the specified range or is the wrong type + if (marker.endOffset < startOffset || (marker.type != markerType && markerType != DocumentMarker::AllMarkers)) + continue; + + // pin the marker to the specified range and apply the shift delta + docDirty = true; + if (marker.startOffset < startOffset) + marker.startOffset = startOffset; + if (marker.endOffset > endOffset) + marker.endOffset = endOffset; + marker.startOffset += delta; + marker.endOffset += delta; + + addMarker(dstNode, marker); + } + + // repaint the affected node + if (docDirty && dstNode->renderer()) + dstNode->renderer()->repaint(); +} + +void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerType markerType) +{ + if (length <= 0) + return; + + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) + return; + + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + bool docDirty = false; + unsigned endOffset = startOffset + length; + for (size_t i = 0; i < markers.size();) { + DocumentMarker marker = markers[i]; + + // markers are returned in order, so stop if we are now past the specified range + if (marker.startOffset >= endOffset) + break; + + // skip marker that is wrong type or before target + if (marker.endOffset < startOffset || (marker.type != markerType && markerType != DocumentMarker::AllMarkers)) { + i++; + continue; + } + + // at this point we know that marker and target intersect in some way + docDirty = true; + + // pitch the old marker and any associated rect + markers.remove(i); + rects.remove(i); + + // add either of the resulting slices that are left after removing target + if (startOffset > marker.startOffset) { + DocumentMarker newLeft = marker; + newLeft.endOffset = startOffset; + markers.insert(i, newLeft); + rects.insert(i, placeholderRectForMarker()); + // i now points to the newly-inserted node, but we want to skip that one + i++; + } + if (marker.endOffset > endOffset) { + DocumentMarker newRight = marker; + newRight.startOffset = endOffset; + markers.insert(i, newRight); + rects.insert(i, placeholderRectForMarker()); + // i now points to the newly-inserted node, but we want to skip that one + i++; + } + } + + if (markers.isEmpty()) { + ASSERT(rects.isEmpty()); + m_markers.remove(node); + delete vectorPair; + } + + // repaint the affected node + if (docDirty && node->renderer()) + node->renderer()->repaint(); +} + +DocumentMarker* DocumentMarkerController::markerContainingPoint(const IntPoint& point, DocumentMarker::MarkerType markerType) +{ + // outer loop: process each node that contains any markers + MarkerMap::iterator end = m_markers.end(); + for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { + // inner loop; process each marker in this node + MarkerMapVectorPair* vectorPair = nodeIterator->second; + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + unsigned markerCount = markers.size(); + for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { + DocumentMarker& marker = markers[markerIndex]; + + // skip marker that is wrong type + if (marker.type != markerType && markerType != DocumentMarker::AllMarkers) + continue; + + IntRect& r = rects[markerIndex]; + + // skip placeholder rects + if (r == placeholderRectForMarker()) + continue; + + if (r.contains(point)) + return ▮ + } + } + + return 0; +} + +Vector<DocumentMarker> DocumentMarkerController::markersForNode(Node* node) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (vectorPair) + return vectorPair->first; + return Vector<DocumentMarker>(); +} + +Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType) +{ + Vector<IntRect> result; + + // outer loop: process each node + MarkerMap::iterator end = m_markers.end(); + for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { + // inner loop; process each marker in this node + MarkerMapVectorPair* vectorPair = nodeIterator->second; + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + unsigned markerCount = markers.size(); + for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { + DocumentMarker marker = markers[markerIndex]; + + // skip marker that is wrong type + if (marker.type != markerType && markerType != DocumentMarker::AllMarkers) + continue; + + IntRect r = rects[markerIndex]; + // skip placeholder rects + if (r == placeholderRectForMarker()) + continue; + + result.append(r); + } + } + + return result; +} + +void DocumentMarkerController::removeMarkers(Node* node) +{ + MarkerMap::iterator i = m_markers.find(node); + if (i != m_markers.end()) { + delete i->second; + m_markers.remove(i); + if (RenderObject* renderer = node->renderer()) + renderer->repaint(); + } +} + +void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerType markerType) +{ + // outer loop: process each markered node in the document + MarkerMap markerMapCopy = m_markers; + MarkerMap::iterator end = markerMapCopy.end(); + for (MarkerMap::iterator i = markerMapCopy.begin(); i != end; ++i) { + Node* node = i->first.get(); + bool nodeNeedsRepaint = false; + + // inner loop: process each marker in the current node + MarkerMapVectorPair* vectorPair = i->second; + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + for (size_t i = 0; i != markers.size();) { + DocumentMarker marker = markers[i]; + + // skip nodes that are not of the specified type + if (marker.type != markerType && markerType != DocumentMarker::AllMarkers) { + ++i; + continue; + } + + // pitch the old marker + markers.remove(i); + rects.remove(i); + nodeNeedsRepaint = true; + // markerIterator now points to the next node + } + + // Redraw the node if it changed. Do this before the node is removed from m_markers, since + // m_markers might contain the last reference to the node. + if (nodeNeedsRepaint) { + RenderObject* renderer = node->renderer(); + if (renderer) + renderer->repaint(); + } + + // delete the node's list if it is now empty + if (markers.isEmpty()) { + ASSERT(rects.isEmpty()); + m_markers.remove(node); + delete vectorPair; + } + } +} + +void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerType markerType) +{ + // outer loop: process each markered node in the document + MarkerMap::iterator end = m_markers.end(); + for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { + Node* node = i->first.get(); + + // inner loop: process each marker in the current node + MarkerMapVectorPair* vectorPair = i->second; + Vector<DocumentMarker>& markers = vectorPair->first; + bool nodeNeedsRepaint = false; + for (size_t i = 0; i != markers.size(); ++i) { + DocumentMarker marker = markers[i]; + + // skip nodes that are not of the specified type + if (marker.type == markerType || markerType == DocumentMarker::AllMarkers) { + nodeNeedsRepaint = true; + break; + } + } + + if (!nodeNeedsRepaint) + continue; + + // cause the node to be redrawn + if (RenderObject* renderer = node->renderer()) + renderer->repaint(); + } +} + +void DocumentMarkerController::setRenderedRectForMarker(Node* node, const DocumentMarker& marker, const IntRect& r) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) { + ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about + return; + } + + Vector<DocumentMarker>& markers = vectorPair->first; + ASSERT(markers.size() == vectorPair->second.size()); + unsigned markerCount = markers.size(); + for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { + DocumentMarker m = markers[markerIndex]; + if (m == marker) { + vectorPair->second[markerIndex] = r; + return; + } + } + + ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about +} + +void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const IntRect& r) +{ + // outer loop: process each markered node in the document + MarkerMap::iterator end = m_markers.end(); + for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { + + // inner loop: process each rect in the current node + MarkerMapVectorPair* vectorPair = i->second; + Vector<IntRect>& rects = vectorPair->second; + + unsigned rectCount = rects.size(); + for (unsigned rectIndex = 0; rectIndex < rectCount; ++rectIndex) + if (rects[rectIndex].intersects(r)) + rects[rectIndex] = placeholderRectForMarker(); + } +} + +void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta, DocumentMarker::MarkerType markerType) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) + return; + + Vector<DocumentMarker>& markers = vectorPair->first; + Vector<IntRect>& rects = vectorPair->second; + ASSERT(markers.size() == rects.size()); + + bool docDirty = false; + for (size_t i = 0; i != markers.size(); ++i) { + DocumentMarker& marker = markers[i]; + if (marker.startOffset >= startOffset && (markerType == DocumentMarker::AllMarkers || marker.type == markerType)) { + ASSERT((int)marker.startOffset + delta >= 0); + marker.startOffset += delta; + marker.endOffset += delta; + docDirty = true; + + // Marker moved, so previously-computed rendered rectangle is now invalid + rects[i] = placeholderRectForMarker(); + } + } + + // repaint the affected node + if (docDirty && node->renderer()) + node->renderer()->repaint(); +} + +void DocumentMarkerController::setMarkersActive(Range* range, bool active) +{ + if (m_markers.isEmpty()) + return; + + ExceptionCode ec = 0; + Node* startContainer = range->startContainer(ec); + Node* endContainer = range->endContainer(ec); + + Node* pastLastNode = range->pastLastNode(); + for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { + int startOffset = node == startContainer ? range->startOffset(ec) : 0; + int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; + setMarkersActive(node, startOffset, endOffset, active); + } +} + +void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) + return; + + Vector<DocumentMarker>& markers = vectorPair->first; + ASSERT(markers.size() == vectorPair->second.size()); + + bool docDirty = false; + for (size_t i = 0; i != markers.size(); ++i) { + DocumentMarker& marker = markers[i]; + + // Markers are returned in order, so stop if we are now past the specified range. + if (marker.startOffset >= endOffset) + break; + + // Skip marker that is wrong type or before target. + if (marker.endOffset < startOffset || marker.type != DocumentMarker::TextMatch) + continue; + + marker.activeMatch = active; + docDirty = true; + } + + // repaint the affected node + if (docDirty && node->renderer()) + node->renderer()->repaint(); +} + +} // namespace WebCore diff --git a/WebCore/dom/DocumentMarkerController.h b/WebCore/dom/DocumentMarkerController.h new file mode 100644 index 0000000..8921baa --- /dev/null +++ b/WebCore/dom/DocumentMarkerController.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef DocumentMarkerController_h +#define DocumentMarkerController_h + +#include "DocumentMarker.h" +#include <wtf/HashMap.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class IntPoint; +class IntRect; +class Node; +class Range; + +class DocumentMarkerController : public Noncopyable { +public: + ~DocumentMarkerController() { detach(); } + + void detach(); + void addMarker(Range*, DocumentMarker::MarkerType, String description = String()); + void addMarker(Node*, DocumentMarker); + void copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void removeMarkers(Range*, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void removeMarkers(Node*, unsigned startOffset, int length, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void removeMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void removeMarkers(Node*); + void repaintMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void setRenderedRectForMarker(Node*, const DocumentMarker&, const IntRect&); + void invalidateRenderedRectsForMarkersInRect(const IntRect&); + void shiftMarkers(Node*, unsigned startOffset, int delta, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void setMarkersActive(Range*, bool); + void setMarkersActive(Node*, unsigned startOffset, unsigned endOffset, bool); + + DocumentMarker* markerContainingPoint(const IntPoint&, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + Vector<DocumentMarker> markersForNode(Node*); + Vector<IntRect> renderedRectsForMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + +private: + typedef std::pair<Vector<DocumentMarker>, Vector<IntRect> > MarkerMapVectorPair; + typedef HashMap<RefPtr<Node>, MarkerMapVectorPair*> MarkerMap; + MarkerMap m_markers; +}; + +} // namespace WebCore + +#endif // DocumentMarkerController_h diff --git a/WebCore/dom/DocumentParser.cpp b/WebCore/dom/DocumentParser.cpp index b80927c..cc4c61b 100644 --- a/WebCore/dom/DocumentParser.cpp +++ b/WebCore/dom/DocumentParser.cpp @@ -37,5 +37,18 @@ DocumentParser::DocumentParser(Document* document) ASSERT(document); } +DocumentParser::~DocumentParser() +{ + // Document is expected to call detach() before releasing its ref. + // This ASSERT is slightly awkward for parsers with a fragment case + // as there is no Document to release the ref. + ASSERT(!m_document); +} + +void DocumentParser::detach() +{ + m_document = 0; +} + }; diff --git a/WebCore/dom/DocumentParser.h b/WebCore/dom/DocumentParser.h index e942d1f..3c28856 100644 --- a/WebCore/dom/DocumentParser.h +++ b/WebCore/dom/DocumentParser.h @@ -24,19 +24,18 @@ #ifndef DocumentParser_h #define DocumentParser_h -#include <wtf/Noncopyable.h> +#include <wtf/RefCounted.h> namespace WebCore { class Document; class DocumentWriter; -class LegacyHTMLTreeBuilder; class SegmentedString; class ScriptableDocumentParser; -class DocumentParser : public Noncopyable { +class DocumentParser : public RefCounted<DocumentParser> { public: - virtual ~DocumentParser() { } + virtual ~DocumentParser(); virtual ScriptableDocumentParser* asScriptableDocumentParser() { return 0; } @@ -56,17 +55,27 @@ public: virtual void finish() = 0; virtual bool finishWasCalled() = 0; - virtual void stopParsing() { m_parserStopped = true; } // FIXME: processingData() is only used by DocumentLoader::isLoadingInAPISense // and is very unclear as to what it actually means. The LegacyHTMLDocumentParser // used to implements it. virtual bool processingData() const { return false; } - // FIXME: Exposed for HTMLFormControlElement::removedFromTree. HTML DOM - // code should not need to reach into implementation details of the parser. - virtual LegacyHTMLTreeBuilder* htmlTreeBuilder() const { return 0; } - - Document* document() const { return m_document; } + // document() will return 0 after detach() is called. + Document* document() const { ASSERT(m_document); return m_document; } + bool isDetached() const { return !m_document; } + + // Document is expected to detach the parser before releasing its ref. + // After detach, m_document is cleared. The parser will unwind its + // callstacks, but not produce any more nodes. + // It is impossible for the parser to touch the rest of WebCore after + // detach is called. + virtual void detach(); + + // stopParsing() is used when a load is canceled/stopped. + // stopParsing() is currently different from detach(), but shouldn't be. + // It should NOT be ok to call any methods on DocumentParser after either + // detach() or stopParsing() but right now only detach() will ASSERT. + virtual void stopParsing() { m_parserStopped = true; } protected: DocumentParser(Document*); @@ -74,9 +83,12 @@ protected: // The parser has buffers, so parsing may continue even after // it stops receiving data. We use m_parserStopped to stop the parser // even when it has buffered data. + // FIXME: m_document = 0 could be changed to mean "parser stopped". bool m_parserStopped; +private: // Every DocumentParser needs a pointer back to the document. + // m_document will be 0 after the parser is stopped. Document* m_document; }; diff --git a/WebCore/dom/Element.cpp b/WebCore/dom/Element.cpp index 849b900..2d44f62 100644 --- a/WebCore/dom/Element.cpp +++ b/WebCore/dom/Element.cpp @@ -1213,6 +1213,11 @@ void Element::setAttributeNS(const AtomicString& namespaceURI, const AtomicStrin if (!Document::parseQualifiedName(qualifiedName, prefix, localName, ec)) return; + if (namespaceURI.isNull() && !prefix.isNull()) { + ec = NAMESPACE_ERR; + return; + } + QualifiedName qName(prefix, localName, namespaceURI); if (scriptingPermission == FragmentScriptingNotAllowed && (isEventHandlerAttribute(qName) || isAttributeToRemove(qName, value))) @@ -1293,9 +1298,6 @@ void Element::focus(bool restorePreviousSelection) if (doc->focusedNode() == this) return; - if (!supportsFocus()) - return; - // If the stylesheets have already been loaded we can reliably check isFocusable. // If not, we continue and set the focused node on the focus controller below so // that it can be updated soon after attach. @@ -1305,6 +1307,9 @@ void Element::focus(bool restorePreviousSelection) return; } + if (!supportsFocus()) + return; + RefPtr<Node> protect; if (Page* page = doc->page()) { // Focus and change event handlers can cause us to lose our last ref. diff --git a/WebCore/dom/EventNames.h b/WebCore/dom/EventNames.h index 4c5a08a..85fe69b 100644 --- a/WebCore/dom/EventNames.h +++ b/WebCore/dom/EventNames.h @@ -103,6 +103,9 @@ namespace WebCore { macro(textInput) \ macro(unload) \ macro(updateready) \ + macro(write) \ + macro(writeend) \ + macro(writestart) \ macro(zoom) \ \ macro(DOMActivate) \ diff --git a/WebCore/dom/EventTarget.cpp b/WebCore/dom/EventTarget.cpp index 42a153a..effd2a2 100644 --- a/WebCore/dom/EventTarget.cpp +++ b/WebCore/dom/EventTarget.cpp @@ -162,12 +162,22 @@ FileReader* EventTarget::toFileReader() return 0; } #endif +#if ENABLE(FILE_WRITER) +FileWriter* EventTarget::toFileWriter() +{ + return 0; +} +#endif #if ENABLE(INDEXED_DATABASE) IDBRequest* EventTarget::toIDBRequest() { return 0; } +IDBTransaction* EventTarget::toIDBTransaction() +{ + return 0; +} #endif bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) diff --git a/WebCore/dom/EventTarget.h b/WebCore/dom/EventTarget.h index b4aa542..b2985f7 100644 --- a/WebCore/dom/EventTarget.h +++ b/WebCore/dom/EventTarget.h @@ -48,7 +48,9 @@ namespace WebCore { class EventListener; class EventSource; class FileReader; + class FileWriter; class IDBRequest; + class IDBTransaction; class MessagePort; class Node; class Notification; @@ -122,9 +124,13 @@ namespace WebCore { #if ENABLE(BLOB) virtual FileReader* toFileReader(); #endif +#if ENABLE(FILE_WRITER) + virtual FileWriter* toFileWriter(); +#endif #if ENABLE(INDEXED_DATABASE) virtual IDBRequest* toIDBRequest(); + virtual IDBTransaction* toIDBTransaction(); #endif virtual ScriptExecutionContext* scriptExecutionContext() const = 0; diff --git a/WebCore/dom/Node.cpp b/WebCore/dom/Node.cpp index 98fb2e5..2c63b62 100644 --- a/WebCore/dom/Node.cpp +++ b/WebCore/dom/Node.cpp @@ -646,14 +646,8 @@ const AtomicString& Node::virtualNamespaceURI() const return nullAtom; } -ContainerNode* Node::legacyParserAddChild(PassRefPtr<Node>) +void Node::deprecatedParserAddChild(PassRefPtr<Node>) { - return 0; -} - -void Node::parserAddChild(PassRefPtr<Node>) -{ - ASSERT_NOT_REACHED(); } bool Node::isContentEditable() const @@ -1235,11 +1229,6 @@ bool Node::contains(const Node* node) const return this == node || node->isDescendantOf(this); } -bool Node::childAllowed(Node* newChild) -{ - return childTypeAllowed(newChild->nodeType()); -} - void Node::attach() { ASSERT(!attached()); @@ -2377,9 +2366,7 @@ void Node::getSubresourceURLs(ListHashSet<KURL>& urls) const ContainerNode* Node::eventParentNode() { - Node* parent = parentNode(); - ASSERT(!parent || parent->isContainerNode()); - return static_cast<ContainerNode*>(parent); + return parentNode(); } Node* Node::enclosingLinkEventParentOrSelf() @@ -2668,7 +2655,7 @@ static inline SVGElementInstance* eventTargetAsSVGElementInstance(Node* referenc if (!n->isShadowNode() || !n->isSVGElement()) continue; - Node* shadowTreeParentElement = n->shadowParentNode(); + ContainerNode* shadowTreeParentElement = n->shadowParentNode(); ASSERT(shadowTreeParentElement->hasTagName(SVGNames::useTag)); if (SVGElementInstance* instance = static_cast<SVGUseElement*>(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode)) diff --git a/WebCore/dom/Node.h b/WebCore/dom/Node.h index a1a8878..e4e2b4a 100644 --- a/WebCore/dom/Node.h +++ b/WebCore/dom/Node.h @@ -86,8 +86,7 @@ enum StyleChangeType { SyntheticStyleChange = 3 << nodeStyleChangeShift }; -// this class implements nodes, which can have a parent but no children: -class Node : public EventTarget, public TreeShared<Node>, public ScriptWrappable { +class Node : public EventTarget, public TreeShared<ContainerNode>, public ScriptWrappable { friend class Document; public: enum NodeType { @@ -134,7 +133,7 @@ public: virtual String nodeValue() const; virtual void setNodeValue(const String&, ExceptionCode&); virtual NodeType nodeType() const = 0; - Node* parentNode() const { return parent(); } + ContainerNode* parentNode() const { return parent(); } Element* parentElement() const; Node* previousSibling() const { return m_previous; } Node* nextSibling() const { return m_next; } @@ -209,7 +208,7 @@ public: virtual bool isCharacterDataNode() const { return false; } bool isDocumentNode() const; virtual bool isShadowNode() const { return false; } - virtual Node* shadowParentNode() { return 0; } + virtual ContainerNode* shadowParentNode() { return 0; } Node* shadowAncestorNode(); Node* shadowTreeRootNode(); bool isInShadowTree(); @@ -259,14 +258,9 @@ public: Element* rootEditableElement() const; bool inSameContainingBlockFlowElement(Node*); - - // Used by the parser. Checks against the DTD, unlike DOM operations like appendChild(). - // Also does not dispatch DOM mutation events. - // Returns the appropriate container node for future insertions as you parse, or 0 for failure. - virtual ContainerNode* legacyParserAddChild(PassRefPtr<Node>); - // addChild is tied into the logic of the LegacyHTMLTreeBuilder. We need - // a "clean" version to use for the HTML5 version of the HTMLTreeBuilder. - virtual void parserAddChild(PassRefPtr<Node>); + + // FIXME: All callers of this function are almost certainly wrong! + virtual void deprecatedParserAddChild(PassRefPtr<Node>); // Called by the parser when this element's close tag is reached, // signaling that all child tags have been parsed and added. @@ -427,11 +421,9 @@ public: 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, - // to sanity-check against the DTD for error recovery. + // This method is used to do strict error-checking when adding children via + // the public DOM API (e.g., appendChild()). void checkAddChild(Node* newChild, ExceptionCode&); // Error-checking when adding via the DOM API - virtual bool childAllowed(Node* newChild); // Error-checking during parsing that checks the DTD void checkReplaceChild(Node* newChild, Node* oldChild, ExceptionCode&); virtual bool canReplaceChild(Node* newChild, Node* oldChild); @@ -606,8 +598,8 @@ public: */ virtual bool disabled() const; - using TreeShared<Node>::ref; - using TreeShared<Node>::deref; + using TreeShared<ContainerNode>::ref; + using TreeShared<ContainerNode>::deref; virtual EventTargetData* eventTargetData(); virtual EventTargetData* ensureEventTargetData(); diff --git a/WebCore/dom/Position.h b/WebCore/dom/Position.h index 9f2ee24..552d675 100644 --- a/WebCore/dom/Position.h +++ b/WebCore/dom/Position.h @@ -26,9 +26,9 @@ #ifndef Position_h #define Position_h +#include "ContainerNode.h" #include "TextAffinity.h" #include "TextDirection.h" -#include "Node.h" // for position creation functions #include <wtf/Assertions.h> #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> diff --git a/WebCore/dom/RawDataDocumentParser.h b/WebCore/dom/RawDataDocumentParser.h index 2eb3d0a..093ddaf 100644 --- a/WebCore/dom/RawDataDocumentParser.h +++ b/WebCore/dom/RawDataDocumentParser.h @@ -31,17 +31,16 @@ namespace WebCore { class RawDataDocumentParser : public DocumentParser { -public: +protected: RawDataDocumentParser(Document* document) : DocumentParser(document) { } -protected: virtual void finish() { - if (!m_parserStopped) - m_document->finishedParsing(); + if (!m_parserStopped && !isDetached()) + document()->finishedParsing(); } private: diff --git a/WebCore/dom/ScriptableDocumentParser.h b/WebCore/dom/ScriptableDocumentParser.h index f5f2e42..e2b3f09 100644 --- a/WebCore/dom/ScriptableDocumentParser.h +++ b/WebCore/dom/ScriptableDocumentParser.h @@ -52,9 +52,6 @@ public: XSSAuditor* xssAuditor() const { return m_xssAuditor; } void setXSSAuditor(XSSAuditor* auditor) { m_xssAuditor = auditor; } - // Exposed for LegacyHTMLTreeBuilder::reportErrorToConsole - virtual bool processingContentWrittenByScript() const { return false; } - protected: ScriptableDocumentParser(Document*, bool viewSourceMode = false); diff --git a/WebCore/dom/SelectElement.cpp b/WebCore/dom/SelectElement.cpp index e9958a2..0ca6cb3 100644 --- a/WebCore/dom/SelectElement.cpp +++ b/WebCore/dom/SelectElement.cpp @@ -554,6 +554,7 @@ void SelectElement::menuListDefaultEventHandler(SelectElementData& data, Element handled = true; } #else + UNUSED_PARAM(htmlForm); const Vector<Element*>& listItems = data.listItems(element); int listIndex = optionToListIndex(data, element, selectedIndex(data, element)); diff --git a/WebCore/dom/TreeWalker.cpp b/WebCore/dom/TreeWalker.cpp index 9c46fb3..6a8ca87 100644 --- a/WebCore/dom/TreeWalker.cpp +++ b/WebCore/dom/TreeWalker.cpp @@ -153,6 +153,7 @@ Node* TreeWalker::previousSibling(ScriptState* state) case NodeFilter::FILTER_SKIP: if (sibling->lastChild()) { sibling = sibling->lastChild(); + node = sibling; continue; } break; @@ -189,6 +190,7 @@ Node* TreeWalker::nextSibling(ScriptState* state) case NodeFilter::FILTER_SKIP: if (sibling->firstChild()) { sibling = sibling->firstChild(); + node = sibling; continue; } break; diff --git a/WebCore/dom/XMLDocumentParser.cpp b/WebCore/dom/XMLDocumentParser.cpp index 3d2f324..c6d9f89 100644 --- a/WebCore/dom/XMLDocumentParser.cpp +++ b/WebCore/dom/XMLDocumentParser.cpp @@ -132,7 +132,7 @@ void XMLDocumentParser::append(const SegmentedString& s) if (m_sawXSLTransform || !m_sawFirstElement) m_originalSourceForTransform += parseString; - if (m_parserStopped || m_sawXSLTransform) + if (isDetached() || m_parserStopped || m_sawXSLTransform) return; if (m_parserPaused) { @@ -170,16 +170,14 @@ void XMLDocumentParser::handleError(ErrorType type, const char* m, int lineNumbe stopParsing(); } -bool XMLDocumentParser::enterText() +void XMLDocumentParser::enterText() { #if !USE(QXMLSTREAM) ASSERT(m_bufferedText.size() == 0); #endif RefPtr<Node> newNode = Text::create(document(), ""); - if (!m_currentNode->legacyParserAddChild(newNode.get())) - return false; + m_currentNode->deprecatedParserAddChild(newNode.get()); pushCurrentNode(newNode.get()); - return true; } #if !USE(QXMLSTREAM) @@ -211,8 +209,18 @@ void XMLDocumentParser::exitText() popCurrentNode(); } +void XMLDocumentParser::detach() +{ + clearCurrentNodeStack(); + ScriptableDocumentParser::detach(); +} + void XMLDocumentParser::end() { + // XMLDocumentParserLibxml2 will do bad things to the document if doEnd() is called. + // I don't believe XMLDocumentParserQt needs doEnd called in the fragment case. + ASSERT(!m_parsingFragment); + doEnd(); // doEnd() could process a script tag, thus pausing parsing. @@ -227,12 +235,16 @@ void XMLDocumentParser::end() } clearCurrentNodeStack(); - if (!m_parsingFragment) - document()->finishedParsing(); + document()->finishedParsing(); } void XMLDocumentParser::finish() { + // FIXME: We should ASSERT(!m_parserStopped) here, since it does not + // makes sense to call any methods on DocumentParser once it's been stopped. + // However, FrameLoader::stop calls Document::finishParsing unconditionally + // which in turn calls m_parser->finish(). + if (m_parserPaused) m_finishCalled = true; else @@ -338,6 +350,9 @@ void XMLDocumentParser::notifyFinished(CachedResource* unusedResource) ScriptElement* scriptElement = toScriptElement(e.get()); ASSERT(scriptElement); + // JavaScript can detach this parser, make sure it's kept alive even if detached. + RefPtr<XMLDocumentParser> protect(this); + if (errorOccurred) scriptElement->dispatchErrorEvent(); else { @@ -347,7 +362,7 @@ void XMLDocumentParser::notifyFinished(CachedResource* unusedResource) m_scriptElement = 0; - if (!m_requestingScript) + if (!isDetached() && !m_requestingScript) resumeParsing(); } @@ -364,4 +379,25 @@ void XMLDocumentParser::pauseParsing() m_parserPaused = true; } +bool XMLDocumentParser::parseDocumentFragment(const String& chunk, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission) +{ + if (!chunk.length()) + return true; + + // FIXME: We need to implement the HTML5 XML Fragment parsing algorithm: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-xhtml-syntax.html#xml-fragment-parsing-algorithm + // For now we have a hack for script/style innerHTML support: + if (contextElement && (contextElement->hasLocalName(HTMLNames::scriptTag) || contextElement->hasLocalName(HTMLNames::styleTag))) { + fragment->parserAddChild(fragment->document()->createTextNode(chunk)); + return true; + } + + RefPtr<XMLDocumentParser> parser = XMLDocumentParser::create(fragment, contextElement, scriptingPermission); + bool wellFormed = parser->appendFragmentSource(chunk); + // Do not call finish(). Current finish() and doEnd() implementations touch the main Document/loader + // and can cause crashes in the fragment case. + parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction. + return wellFormed; // appendFragmentSource()'s wellFormed is more permissive than wellFormed(). } + +} // namespace WebCore diff --git a/WebCore/dom/XMLDocumentParser.h b/WebCore/dom/XMLDocumentParser.h index 141adf7..4211e4e 100644 --- a/WebCore/dom/XMLDocumentParser.h +++ b/WebCore/dom/XMLDocumentParser.h @@ -181,8 +181,15 @@ namespace WebCore { class XMLDocumentParser : public ScriptableDocumentParser, public CachedResourceClient { public: - XMLDocumentParser(Document*, FrameView* = 0); - XMLDocumentParser(DocumentFragment*, Element*, FragmentScriptingPermission); + static PassRefPtr<XMLDocumentParser> create(Document* document, FrameView* view) + { + return adoptRef(new XMLDocumentParser(document, view)); + } + static PassRefPtr<XMLDocumentParser> create(DocumentFragment* fragment, Element* element, FragmentScriptingPermission permission) + { + return adoptRef(new XMLDocumentParser(fragment, element, permission)); + } + ~XMLDocumentParser(); // Exposed for callbacks: @@ -199,14 +206,19 @@ namespace WebCore { bool isWMLDocument() const; #endif - static bool parseDocumentFragment(const String&, DocumentFragment*, Element* parent = 0, FragmentScriptingPermission = FragmentScriptingAllowed); + static bool parseDocumentFragment(const String&, DocumentFragment*, Element* parent = 0, FragmentScriptingPermission = FragmentScriptingAllowed); // WMLErrorHandling uses these functions. virtual bool wellFormed() const { return !m_sawError; } virtual int lineNumber() const; virtual int columnNumber() const; + static bool supportsXMLVersion(const String&); + private: + XMLDocumentParser(Document*, FrameView* = 0); + XMLDocumentParser(DocumentFragment*, Element*, FragmentScriptingPermission); + // From DocumentParser virtual void insert(const SegmentedString&); virtual void append(const SegmentedString&); @@ -214,6 +226,7 @@ namespace WebCore { virtual bool finishWasCalled(); virtual bool isWaitingForScripts() const; virtual void stopParsing(); + virtual void detach(); // from CachedResourceClient virtual void notifyFinished(CachedResource*); @@ -223,6 +236,8 @@ namespace WebCore { void pauseParsing(); void resumeParsing(); + bool appendFragmentSource(const String&); + #if USE(QXMLSTREAM) private: void parse(); @@ -260,7 +275,7 @@ public: void insertErrorMessageBlock(); - bool enterText(); + void enterText(); void exitText(); void doWrite(const String&); diff --git a/WebCore/dom/XMLDocumentParserLibxml2.cpp b/WebCore/dom/XMLDocumentParserLibxml2.cpp index 1309827..37da83c 100644 --- a/WebCore/dom/XMLDocumentParserLibxml2.cpp +++ b/WebCore/dom/XMLDocumentParserLibxml2.cpp @@ -522,6 +522,11 @@ PassRefPtr<XMLParserContext> XMLParserContext::createMemoryParser(xmlSAXHandlerP // -------------------------------- +bool XMLDocumentParser::supportsXMLVersion(const String& version) +{ + return version == "1.0"; +} + XMLDocumentParser::XMLDocumentParser(Document* document, FrameView* frameView) : ScriptableDocumentParser(document) , m_view(frameView) @@ -616,13 +621,18 @@ XMLParserContext::~XMLParserContext() XMLDocumentParser::~XMLDocumentParser() { - clearCurrentNodeStack(); + // The XMLDocumentParser will always be detached before being destroyed. + ASSERT(m_currentNodeStack.isEmpty()); + ASSERT(!m_currentNode); + + // FIXME: m_pendingScript handling should be moved into XMLDocumentParser.cpp! if (m_pendingScript) m_pendingScript->removeClient(this); } void XMLDocumentParser::doWrite(const String& parseString) { + ASSERT(!isDetached()); if (!m_context) initializeParserContext(); @@ -631,6 +641,10 @@ void XMLDocumentParser::doWrite(const String& parseString) // libXML throws an error if you try to switch the encoding for an empty string. if (parseString.length()) { + // JavaScript may cause the parser to detach during xmlParseChunk + // keep this alive until this function is done. + RefPtr<XMLDocumentParser> protect(this); + // Hack around libxml2's lack of encoding overide support by manually // resetting the encoding to UTF-16 before every chunk. Otherwise libxml // will detect <?xml version="1.0" encoding="<encoding name>"?> blocks @@ -641,14 +655,18 @@ void XMLDocumentParser::doWrite(const String& parseString) XMLDocumentParserScope scope(document()->docLoader()); xmlParseChunk(context->context(), reinterpret_cast<const char*>(parseString.characters()), sizeof(UChar) * parseString.length(), 0); + + // JavaScript (which may be run under the xmlParseChunk callstack) may + // cause the parser to be stopped or detached. + if (isDetached() || m_parserStopped) + return; } + // FIXME: Why is this here? And why is it after we process the passed source? if (document()->decoder() && document()->decoder()->sawError()) { // If the decoder saw an error, report it as fatal (stops parsing) handleError(fatal, "Encoding error", context->context()->input->line, context->context()->input->col); } - - return; } static inline String toString(const xmlChar* str, unsigned len) @@ -790,10 +808,7 @@ void XMLDocumentParser::startElementNs(const xmlChar* xmlLocalName, const xmlCha if (scriptElement) m_scriptStartLine = lineNumber(); - if (!m_currentNode->legacyParserAddChild(newElement.get())) { - stopParsing(); - return; - } + m_currentNode->deprecatedParserAddChild(newElement.get()); pushCurrentNode(newElement.get()); if (m_view && !newElement->attached()) @@ -855,6 +870,13 @@ void XMLDocumentParser::endElementNs() else #endif { + // FIXME: Script execution should be shared should be shared between + // the libxml2 and Qt XMLDocumentParser implementations. + + // JavaScript can detach the parser. Make sure this is not released + // before the end of this method. + RefPtr<XMLDocumentParser> protect(this); + String scriptHref = scriptElement->sourceAttributeValue(); if (!scriptHref.isEmpty()) { // we have a src attribute @@ -871,6 +893,10 @@ void XMLDocumentParser::endElementNs() m_scriptElement = 0; } else m_view->frame()->script()->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartLine)); + + // JavaScript may have detached the parser + if (isDetached()) + return; } m_requestingScript = false; popCurrentNode(); @@ -886,8 +912,9 @@ void XMLDocumentParser::characters(const xmlChar* s, int len) return; } - if (m_currentNode->isTextNode() || enterText()) - m_bufferedText.append(s, len); + if (!m_currentNode->isTextNode()) + enterText(); + m_bufferedText.append(s, len); } void XMLDocumentParser::error(ErrorType type, const char* message, va_list args) @@ -935,8 +962,7 @@ void XMLDocumentParser::processingInstruction(const xmlChar* target, const xmlCh pi->setCreatedByParser(true); - if (!m_currentNode->legacyParserAddChild(pi.get())) - return; + m_currentNode->deprecatedParserAddChild(pi.get()); if (m_view && !pi->attached()) pi->attach(); @@ -962,8 +988,7 @@ void XMLDocumentParser::cdataBlock(const xmlChar* s, int len) exitText(); RefPtr<Node> newNode = CDATASection::create(document(), toString(s, len)); - if (!m_currentNode->legacyParserAddChild(newNode.get())) - return; + m_currentNode->deprecatedParserAddChild(newNode.get()); if (m_view && !newNode->attached()) newNode->attach(); } @@ -981,7 +1006,7 @@ void XMLDocumentParser::comment(const xmlChar* s) exitText(); RefPtr<Node> newNode = Comment::create(document(), toString(s)); - m_currentNode->legacyParserAddChild(newNode.get()); + m_currentNode->deprecatedParserAddChild(newNode.get()); if (m_view && !newNode->attached()) newNode->attach(); } @@ -1045,7 +1070,7 @@ void XMLDocumentParser::internalSubset(const xmlChar* name, const xmlChar* exter } #endif - document()->legacyParserAddChild(DocumentType::create(document(), toString(name), toString(externalID), toString(systemID))); + document()->parserAddChild(DocumentType::create(document(), toString(name), toString(externalID), toString(systemID))); } } @@ -1274,8 +1299,10 @@ void XMLDocumentParser::initializeParserContext(const char* chunk) XMLDocumentParserScope scope(document()->docLoader()); if (m_parsingFragment) m_context = XMLParserContext::createMemoryParser(&sax, this, chunk); - else + else { + ASSERT(!chunk); m_context = XMLParserContext::createStringParser(&sax, this); + } } void XMLDocumentParser::doEnd() @@ -1347,6 +1374,7 @@ void XMLDocumentParser::stopParsing() void XMLDocumentParser::resumeParsing() { + ASSERT(!isDetached()); ASSERT(m_parserPaused); m_parserPaused = false; @@ -1371,29 +1399,29 @@ void XMLDocumentParser::resumeParsing() end(); } -// FIXME: This method should be possible to implement using the DocumentParser -// API, instead of needing to grab at libxml2 state directly. -bool XMLDocumentParser::parseDocumentFragment(const String& chunk, DocumentFragment* fragment, Element* parent, FragmentScriptingPermission scriptingPermission) +bool XMLDocumentParser::appendFragmentSource(const String& chunk) { - if (!chunk.length()) - return true; - - XMLDocumentParser parser(fragment, parent, scriptingPermission); + ASSERT(!m_context); + ASSERT(m_parsingFragment); CString chunkAsUtf8 = chunk.utf8(); - parser.initializeParserContext(chunkAsUtf8.data()); - - xmlParseContent(parser.context()); - - parser.endDocument(); + initializeParserContext(chunkAsUtf8.data()); + xmlParseContent(context()); + endDocument(); // Close any open text nodes. + // FIXME: If this code is actually needed, it should probably move to finish() + // XMLDocumentParserQt has a similar check (m_stream.error() == QXmlStreamReader::PrematureEndOfDocumentError) in doEnd(). // Check if all the chunk has been processed. - long bytesProcessed = xmlByteConsumed(parser.context()); - if (bytesProcessed == -1 || ((unsigned long)bytesProcessed) != chunkAsUtf8.length()) + long bytesProcessed = xmlByteConsumed(context()); + if (bytesProcessed == -1 || ((unsigned long)bytesProcessed) != chunkAsUtf8.length()) { + // FIXME: I don't believe we can hit this case without also having seen an error. + // If we hit this ASSERT, we've found a test case which demonstrates the need for this code. + ASSERT(m_sawError); return false; + } // No error if the chunk is well formed or it is not but we have no error. - return parser.context()->wellFormed || xmlCtxtGetLastError(parser.context()) == 0; + return context()->wellFormed || !xmlCtxtGetLastError(context()); } // -------------------------------- diff --git a/WebCore/dom/XMLDocumentParserQt.cpp b/WebCore/dom/XMLDocumentParserQt.cpp index 715856c..606770f 100644 --- a/WebCore/dom/XMLDocumentParserQt.cpp +++ b/WebCore/dom/XMLDocumentParserQt.cpp @@ -78,6 +78,11 @@ QString EntityResolver::resolveUndeclaredEntity(const QString &name) // -------------------------------- +bool XMLDocumentParser::supportsXMLVersion(const String& version) +{ + return version == "1.0"; +} + XMLDocumentParser::XMLDocumentParser(Document* document, FrameView* frameView) : ScriptableDocumentParser(document) , m_view(frameView) @@ -254,18 +259,13 @@ void XMLDocumentParser::resumeParsing() end(); } -bool XMLDocumentParser::parseDocumentFragment(const String& chunk, DocumentFragment* fragment, Element* parent, FragmentScriptingPermission scriptingPermission) +bool XMLDocumentParser::appendFragmentSource(const String& source) { - if (!chunk.length()) - return true; - - XMLDocumentParser parser(fragment, parent, scriptingPermission); - - parser.append(String("<qxmlstreamdummyelement>")); - parser.append(chunk); - parser.append(String("</qxmlstreamdummyelement>")); - parser.finish(); - return !parser.hasError(); + ASSERT(!m_sawFirstElement); + append(String("<qxmlstreamdummyelement>")); + append(source); + append(String("</qxmlstreamdummyelement>")); + return !hasError(); } // -------------------------------- @@ -410,12 +410,12 @@ void XMLDocumentParser::parse() ) { QString entity = m_stream.name().toString(); UChar c = decodeNamedEntity(entity.toUtf8().constData()); - if (m_currentNode->isTextNode() || enterText()) { - ExceptionCode ec = 0; - String str(&c, 1); - //qDebug()<<" ------- adding entity "<<str; - static_cast<Text*>(m_currentNode)->appendData(str, ec); - } + if (!m_currentNode->isTextNode()) + enterText(); + ExceptionCode ec = 0; + String str(&c, 1); + // qDebug()<<" ------- adding entity "<<str; + static_cast<Text*>(m_currentNode)->appendData(str, ec); } } break; @@ -518,10 +518,7 @@ void XMLDocumentParser::parseStartElement() if (scriptElement) m_scriptStartLine = lineNumber(); - if (!m_currentNode->legacyParserAddChild(newElement.get())) { - stopParsing(); - return; - } + m_currentNode->deprecatedParserAddChild(newElement.get()); pushCurrentNode(newElement.get()); if (m_view && !newElement->attached()) @@ -599,10 +596,10 @@ void XMLDocumentParser::parseEndElement() void XMLDocumentParser::parseCharacters() { - if (m_currentNode->isTextNode() || enterText()) { - ExceptionCode ec = 0; - static_cast<Text*>(m_currentNode)->appendData(m_stream.text(), ec); - } + if (!m_currentNode->isTextNode()) + enterText(); + ExceptionCode ec = 0; + static_cast<Text*>(m_currentNode)->appendData(m_stream.text(), ec); } void XMLDocumentParser::parseProcessingInstruction() @@ -619,8 +616,7 @@ void XMLDocumentParser::parseProcessingInstruction() pi->setCreatedByParser(true); - if (!m_currentNode->legacyParserAddChild(pi.get())) - return; + m_currentNode->deprecatedParserAddChild(pi.get()); if (m_view && !pi->attached()) pi->attach(); @@ -638,8 +634,8 @@ void XMLDocumentParser::parseCdata() exitText(); RefPtr<Node> newNode = CDATASection::create(document(), m_stream.text()); - if (!m_currentNode->legacyParserAddChild(newNode.get())) - return; + + m_currentNode->deprecatedParserAddChild(newNode.get()); if (m_view && !newNode->attached()) newNode->attach(); } @@ -649,7 +645,8 @@ void XMLDocumentParser::parseComment() exitText(); RefPtr<Node> newNode = Comment::create(document(), m_stream.text()); - m_currentNode->legacyParserAddChild(newNode.get()); + + m_currentNode->deprecatedParserAddChild(newNode.get()); if (m_view && !newNode->attached()) newNode->attach(); } @@ -708,7 +705,7 @@ void XMLDocumentParser::parseDtd() handleError(fatal, "Invalid DTD Public ID", lineNumber(), columnNumber()); #endif if (!m_parsingFragment) - document()->legacyParserAddChild(DocumentType::create(document(), name, publicId, systemId)); + document()->parserAddChild(DocumentType::create(document(), name, publicId, systemId)); } } |