diff options
author | Ben Murdoch <benm@google.com> | 2011-05-24 11:24:40 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-06-02 09:53:15 +0100 |
commit | 81bc750723a18f21cd17d1b173cd2a4dda9cea6e (patch) | |
tree | 7a9e5ed86ff429fd347a25153107221543909b19 /Source/WebCore/dom | |
parent | 94088a6d336c1dd80a1e734af51e96abcbb689a7 (diff) | |
download | external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.zip external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.gz external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.bz2 |
Merge WebKit at r80534: Intial merge by Git
Change-Id: Ia7a83357124c9e1cdb1debf55d9661ec0bd09a61
Diffstat (limited to 'Source/WebCore/dom')
49 files changed, 1417 insertions, 749 deletions
diff --git a/Source/WebCore/dom/Attr.cpp b/Source/WebCore/dom/Attr.cpp index f497394..6500a97 100644 --- a/Source/WebCore/dom/Attr.cpp +++ b/Source/WebCore/dom/Attr.cpp @@ -26,6 +26,7 @@ #include "Element.h" #include "ExceptionCode.h" #include "HTMLNames.h" +#include "ScopedEventQueue.h" #include "Text.h" #include "XMLNSNames.h" @@ -119,6 +120,7 @@ String Attr::nodeValue() const void Attr::setValue(const AtomicString& value) { + EventQueueScope scope; m_ignoreChildrenChanged++; removeChildren(); m_attribute->setValue(value); diff --git a/Source/WebCore/dom/CharacterData.cpp b/Source/WebCore/dom/CharacterData.cpp index 640e3fa..b4af02d 100644 --- a/Source/WebCore/dom/CharacterData.cpp +++ b/Source/WebCore/dom/CharacterData.cpp @@ -27,6 +27,9 @@ #include "InspectorInstrumentation.h" #include "MutationEvent.h" #include "RenderText.h" +#include "TextBreakIterator.h" + +using namespace std; namespace WebCore { @@ -51,12 +54,27 @@ String CharacterData::substringData(unsigned offset, unsigned count, ExceptionCo return m_data->substring(offset, count); } -void CharacterData::parserAppendData(const String& data) +unsigned CharacterData::parserAppendData(const UChar* data, unsigned dataLength, unsigned lengthLimit) { - String newStr = m_data; - newStr.append(data); + unsigned oldLength = m_data->length(); + + unsigned end = min(dataLength, lengthLimit - oldLength); - int oldLength = m_data->length(); + // Check that we are not on an unbreakable boundary. + // Some text break iterator implementations work best if the passed buffer is as small as possible, + // see <https://bugs.webkit.org/show_bug.cgi?id=29092>. + // We need at least two characters look-ahead to account for UTF-16 surrogates. + if (end < dataLength) { + TextBreakIterator* it = characterBreakIterator(data, (end + 2 > dataLength) ? dataLength : end + 2); + if (!isTextBreak(it, end)) + end = textBreakPreceding(it, end); + } + + if (!end) + return 0; + + String newStr = m_data; + newStr.append(data, end); m_data = newStr.impl(); updateRenderer(oldLength, 0); @@ -64,6 +82,8 @@ void CharacterData::parserAppendData(const String& data) // parser to dispatch DOM mutation events. if (parentNode()) parentNode()->childrenChanged(); + + return end; } void CharacterData::appendData(const String& data, ExceptionCode&) diff --git a/Source/WebCore/dom/CharacterData.h b/Source/WebCore/dom/CharacterData.h index 31da63c..39a6faa 100644 --- a/Source/WebCore/dom/CharacterData.h +++ b/Source/WebCore/dom/CharacterData.h @@ -43,7 +43,8 @@ public: StringImpl* dataImpl() { return m_data.get(); } // Like appendData, but optimized for the parser (e.g., no mutation events). - void parserAppendData(const String&); + // Returns how much could be added before length limit was met. + unsigned parserAppendData(const UChar*, unsigned dataLength, unsigned lengthLimit); protected: CharacterData(Document* document, const String& text, ConstructionType type) diff --git a/Source/WebCore/dom/ContainerNode.cpp b/Source/WebCore/dom/ContainerNode.cpp index bc881c6..7424875 100644 --- a/Source/WebCore/dom/ContainerNode.cpp +++ b/Source/WebCore/dom/ContainerNode.cpp @@ -158,6 +158,7 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce InspectorInstrumentation::willInsertDOMNode(document(), child, this); #endif + child->setDocumentRecursively(document()); insertBeforeCommon(next.get(), child); // Send notification about the children change. @@ -306,6 +307,8 @@ bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, Exce InspectorInstrumentation::willInsertDOMNode(document(), child.get(), this); #endif + child->setDocumentRecursively(document()); + // Add child after "prev". forbidEventDispatch(); Node* next; @@ -355,7 +358,8 @@ bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, Exce void ContainerNode::willRemove() { - NodeVector nodes; + Vector<RefPtr<Node>, 10> nodes; + nodes.reserveInitialCapacity(childNodeCount()); for (Node* n = m_lastChild; n; n = n->previousSibling()) nodes.append(n); for (; nodes.size(); nodes.removeLast()) @@ -512,10 +516,11 @@ void ContainerNode::removeChildren() document()->removeFocusedNodeOfSubtree(this, true); forbidEventDispatch(); - Vector<RefPtr<Node> > removedChildren; + Vector<RefPtr<Node>, 10> removedChildren; + removedChildren.reserveInitialCapacity(childNodeCount()); while (RefPtr<Node> n = m_firstChild) { Node* next = n->nextSibling(); - + // Remove the node from the tree before calling detach or removedFromDocument (4427024, 4129744). // removeChild() does this after calling detach(). There is no explanation for // this discrepancy between removeChild() and its optimized version removeChildren(). @@ -592,6 +597,8 @@ bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bo InspectorInstrumentation::willInsertDOMNode(document(), child, this); #endif + child->setDocumentRecursively(document()); + // Append child to the end of the list forbidEventDispatch(); child->setParent(this); diff --git a/Source/WebCore/dom/DOMImplementation.cpp b/Source/WebCore/dom/DOMImplementation.cpp index 30e889f..3ba1137 100644 --- a/Source/WebCore/dom/DOMImplementation.cpp +++ b/Source/WebCore/dom/DOMImplementation.cpp @@ -171,6 +171,12 @@ static bool isSVG11Feature(const String &feature) } #endif +DOMImplementation::DOMImplementation(Document* ownerDocument) + : m_ownerDocument(ownerDocument) +{ + ASSERT(m_ownerDocument); +} + bool DOMImplementation::hasFeature(const String& feature, const String& version) { String lower = feature.lower(); @@ -240,6 +246,12 @@ PassRefPtr<Document> DOMImplementation::createDocument(const String& namespaceUR else doc = Document::create(0, KURL()); + if (!m_ownerDocument) { + ec = INVALID_STATE_ERR; + return 0; + } + doc->setSecurityOrigin(m_ownerDocument->securityOrigin()); + RefPtr<Node> documentElement; if (!qualifiedName.isEmpty()) { documentElement = doc->createElementNS(namespaceURI, qualifiedName, ec); @@ -300,6 +312,9 @@ PassRefPtr<HTMLDocument> DOMImplementation::createHTMLDocument(const String& tit d->open(); d->write("<!doctype html><html><body></body></html>"); d->setTitle(title); + ASSERT(m_ownerDocument); + if (m_ownerDocument) + d->setSecurityOrigin(m_ownerDocument->securityOrigin()); return d.release(); } diff --git a/Source/WebCore/dom/DOMImplementation.h b/Source/WebCore/dom/DOMImplementation.h index a20f7d2..4eadba3 100644 --- a/Source/WebCore/dom/DOMImplementation.h +++ b/Source/WebCore/dom/DOMImplementation.h @@ -41,12 +41,12 @@ typedef int ExceptionCode; class DOMImplementation : public RefCounted<DOMImplementation> { public: - static PassRefPtr<DOMImplementation> create() { return adoptRef(new DOMImplementation); } + static PassRefPtr<DOMImplementation> create(Document* ownerDocument) { return adoptRef(new DOMImplementation(ownerDocument)); } // DOM methods & attributes for DOMImplementation static bool hasFeature(const String& feature, const String& version); - static PassRefPtr<DocumentType> createDocumentType(const String& qualifiedName, const String& publicId, const String &systemId, ExceptionCode&); - static PassRefPtr<Document> createDocument(const String& namespaceURI, const String& qualifiedName, DocumentType*, ExceptionCode&); + PassRefPtr<DocumentType> createDocumentType(const String& qualifiedName, const String& publicId, const String& systemId, ExceptionCode&); + PassRefPtr<Document> createDocument(const String& namespaceURI, const String& qualifiedName, DocumentType*, ExceptionCode&); DOMImplementation* getInterface(const String& feature); @@ -54,7 +54,7 @@ public: static PassRefPtr<CSSStyleSheet> createCSSStyleSheet(const String& title, const String& media, ExceptionCode&); // From the HTMLDOMImplementation interface - static PassRefPtr<HTMLDocument> createHTMLDocument(const String& title); + PassRefPtr<HTMLDocument> createHTMLDocument(const String& title); // Other methods (not part of DOM) static PassRefPtr<Document> createDocument(const String& MIMEType, Frame*, const KURL&, bool inViewSourceMode); @@ -62,8 +62,13 @@ public: static bool isXMLMIMEType(const String& MIMEType); static bool isTextMIMEType(const String& MIMEType); + Document* ownerDocument() { return m_ownerDocument; } + void ownerDocumentDestroyed() { m_ownerDocument = 0; } + private: - DOMImplementation() { } + DOMImplementation(Document* ownerDocument); + + Document* m_ownerDocument; }; } //namespace diff --git a/Source/WebCore/dom/DOMImplementation.idl b/Source/WebCore/dom/DOMImplementation.idl index 81df6c8..8420281 100644 --- a/Source/WebCore/dom/DOMImplementation.idl +++ b/Source/WebCore/dom/DOMImplementation.idl @@ -20,7 +20,9 @@ module core { - interface DOMImplementation { + interface [ + CustomMarkFunction + ] DOMImplementation { // DOM Level 1 diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp index ee790ab..a2a8040 100644 --- a/Source/WebCore/dom/Document.cpp +++ b/Source/WebCore/dom/Document.cpp @@ -3,10 +3,11 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) * (C) 2006 Alexey Proskuryakov (ap@webkit.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2008, 2009 Google Inc. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) Research In Motion, Limited 2010-2011. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -33,6 +34,7 @@ #include "Attr.h" #include "Attribute.h" #include "CDATASection.h" +#include "CSSPrimitiveValueCache.h" #include "CSSStyleSelector.h" #include "CSSStyleSheet.h" #include "CSSValueKeywords.h" @@ -121,6 +123,7 @@ #include "RenderTextControl.h" #include "RenderView.h" #include "RenderWidget.h" +#include "ScopedEventQueue.h" #include "ScriptCallStack.h" #include "ScriptController.h" #include "ScriptElement.h" @@ -224,6 +227,7 @@ #if ENABLE(REQUEST_ANIMATION_FRAME) #include "RequestAnimationFrameCallback.h" +#include "ScriptedAnimationController.h" #endif using namespace std; @@ -341,11 +345,13 @@ static bool acceptsEditingFocus(Node* node) static bool disableRangeMutation(Page* page) { -#if PLATFORM(MAC) + // This check is made on super-hot code paths, so we only want this on Tiger and Leopard. +#if defined(TARGETING_TIGER) || defined(TARGETING_LEOPARD) // Disable Range mutation on document modifications in Tiger and Leopard Mail // See <rdar://problem/5865171> return page && (page->settings()->needsLeopardMailQuirks() || page->settings()->needsTigerMailQuirks()); #else + UNUSED_PARAM(page); return false; #endif } @@ -423,6 +429,8 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML) , m_useSecureKeyboardEntryWhenActive(false) , m_isXHTML(isXHTML) , m_isHTML(isHTML) + , m_usesViewSourceStyles(false) + , m_sawElementsInKnownNamespaces(false) , m_numNodeListCaches(0) #if USE(JSC) , m_normalWorldWrapperCache(0) @@ -446,9 +454,7 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML) , m_writingModeSetOnDocumentElement(false) , m_writeRecursionIsTooDeep(false) , m_writeRecursionDepth(0) -#if ENABLE(REQUEST_ANIMATION_FRAME) - , m_nextRequestAnimationFrameCallbackId(0) -#endif + , m_pendingTasksTimer(this, &Document::pendingTasksTimerFired) { m_document = this; @@ -488,17 +494,19 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML) m_inStyleRecalc = false; m_closeAfterStyleRecalc = false; - m_usesDescendantRules = false; m_usesSiblingRules = false; + m_usesSiblingRulesOverride = false; m_usesFirstLineRules = false; m_usesFirstLetterRules = false; m_usesBeforeAfterRules = false; + m_usesBeforeAfterRulesOverride = false; m_usesRemUnits = false; m_usesLinkRules = false; m_gotoAnchorNeededAfterStylesheetsLoad = false; m_didCalculateStyleSelector = false; + m_hasDirtyStyleSelector = false; m_pendingStylesheets = 0; m_ignorePendingStylesheets = false; m_hasNodesWithPlaceholderStyle = false; @@ -622,6 +630,9 @@ Document::~Document() if (m_mediaQueryMatcher) m_mediaQueryMatcher->documentDestroyed(); + + if (m_implementation) + m_implementation->ownerDocumentDestroyed(); } MediaQueryMatcher* Document::mediaQueryMatcher() @@ -713,10 +724,10 @@ void Document::setDocType(PassRefPtr<DocumentType> docType) #endif } -DOMImplementation* Document::implementation() const +DOMImplementation* Document::implementation() { if (!m_implementation) - m_implementation = DOMImplementation::create(); + m_implementation = DOMImplementation::create(this); return m_implementation.get(); } @@ -909,6 +920,8 @@ PassRefPtr<Node> Document::adoptNode(PassRefPtr<Node> source, ExceptionCode& ec) return 0; } + EventQueueScope scope; + switch (source->nodeType()) { case ENTITY_NODE: case NOTATION_NODE: @@ -984,7 +997,9 @@ PassRefPtr<Element> Document::createElement(const QualifiedName& qName, bool cre e = MathMLElementFactory::createMathMLElement(qName, this, createdByParser); #endif - if (!e) + if (e) + m_sawElementsInKnownNamespaces = true; + else e = Element::create(qName, document()); // <image> uses imgTag so we need a special rule. @@ -1120,12 +1135,9 @@ KURL Document::baseURI() const void Document::setContent(const String& content) { - removeAllChildren(); - open(); m_parser->append(content); - m_parser->finish(); - explicitClose(); + close(); } // FIXME: We need to discuss the DOM API here at some point. Ideas: @@ -1184,12 +1196,8 @@ PassRefPtr<NodeList> Document::handleZeroPadding(const HitTestRequest& request, return StaticHashSetNodeList::adopt(list); } -Element* Document::elementFromPoint(int x, int y) const +static Node* nodeFromPoint(Frame* frame, RenderView* renderView, int x, int y, IntPoint* localPoint = 0) { - // FIXME: Share code between this and caretRangeFromPoint. - if (!renderer()) - return 0; - Frame* frame = this->frame(); if (!frame) return 0; FrameView* frameView = frame->view(); @@ -1197,46 +1205,39 @@ Element* Document::elementFromPoint(int x, int y) const return 0; float zoomFactor = frame->pageZoomFactor(); - IntPoint point = roundedIntPoint(FloatPoint(x * zoomFactor + view()->scrollX(), y * zoomFactor + view()->scrollY())); + IntPoint point = roundedIntPoint(FloatPoint(x * zoomFactor + frameView->scrollX(), y * zoomFactor + frameView->scrollY())); if (!frameView->visibleContentRect().contains(point)) return 0; HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); HitTestResult result(point); - renderView()->layer()->hitTest(request, result); + renderView->layer()->hitTest(request, result); - Node* n = result.innerNode(); - while (n && !n->isElementNode()) - n = n->parentNode(); - if (n) - n = n->shadowAncestorNode(); - return static_cast<Element*>(n); + if (localPoint) + *localPoint = result.localPoint(); + + return result.innerNode(); } -PassRefPtr<Range> Document::caretRangeFromPoint(int x, int y) +Element* Document::elementFromPoint(int x, int y) const { - // FIXME: Share code between this and elementFromPoint. if (!renderer()) return 0; - Frame* frame = this->frame(); - if (!frame) - return 0; - FrameView* frameView = frame->view(); - if (!frameView) - return 0; - - float zoomFactor = frame->pageZoomFactor(); - IntPoint point = roundedIntPoint(FloatPoint(x * zoomFactor + view()->scrollX(), y * zoomFactor + view()->scrollY())); + Node* node = nodeFromPoint(frame(), renderView(), x, y); + while (node && !node->isElementNode()) + node = node->parentNode(); + if (node) + node = node->shadowAncestorNode(); + return static_cast<Element*>(node); +} - if (!frameView->visibleContentRect().contains(point)) +PassRefPtr<Range> Document::caretRangeFromPoint(int x, int y) +{ + if (!renderer()) return 0; - - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); - HitTestResult result(point); - renderView()->layer()->hitTest(request, result); - - Node* node = result.innerNode(); + IntPoint localPoint; + Node* node = nodeFromPoint(frame(), renderView(), x, y, &localPoint); if (!node) return 0; @@ -1250,7 +1251,7 @@ PassRefPtr<Range> Document::caretRangeFromPoint(int x, int y) RenderObject* renderer = node->renderer(); if (!renderer) return 0; - VisiblePosition visiblePosition = renderer->positionForPoint(result.localPoint()); + VisiblePosition visiblePosition = renderer->positionForPoint(localPoint); if (visiblePosition.isNull()) return 0; @@ -1519,6 +1520,9 @@ void Document::recalcStyle(StyleChange change) if (m_inStyleRecalc) return; // Guard against re-entrancy. -dwh + + if (m_hasDirtyStyleSelector) + recalcStyleSelector(); InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRecalculateStyle(this); @@ -1570,6 +1574,14 @@ bail_out: clearNeedsStyleRecalc(); clearChildNeedsStyleRecalc(); unscheduleStyleRecalc(); + + // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc. + if (m_styleSelector) { + m_usesSiblingRules = m_styleSelector->usesSiblingRules(); + m_usesFirstLineRules = m_styleSelector->usesFirstLineRules(); + m_usesBeforeAfterRules = m_styleSelector->usesBeforeAfterRules(); + m_usesLinkRules = m_styleSelector->usesLinkRules(); + } if (view()) view()->resumeScheduledEvents(); @@ -1726,6 +1738,13 @@ void Document::pageSizeAndMarginsInPixels(int pageIndex, IntSize& pageSize, int& marginLeft = style->marginLeft().isAuto() ? marginLeft : style->marginLeft().calcValue(width); } +PassRefPtr<CSSPrimitiveValueCache> Document::cssPrimitiveValueCache() const +{ + if (!m_cssPrimitiveValueCache) + m_cssPrimitiveValueCache = CSSPrimitiveValueCache::create(); + return m_cssPrimitiveValueCache; +} + void Document::createStyleSelector() { bool matchAuthorAndUserStyles = true; @@ -1733,6 +1752,11 @@ void Document::createStyleSelector() matchAuthorAndUserStyles = docSettings->authorAndUserStylesEnabled(); m_styleSelector.set(new CSSStyleSelector(this, m_styleSheets.get(), m_mappedElementSheet.get(), pageUserSheet(), pageGroupUserSheets(), !inQuirksMode(), matchAuthorAndUserStyles)); + // Delay resetting the flags until after next style recalc since unapplying the style may not work without these set (this is true at least with before/after). + m_usesSiblingRules = m_usesSiblingRules || m_styleSelector->usesSiblingRules(); + m_usesFirstLineRules = m_usesFirstLineRules || m_styleSelector->usesFirstLineRules(); + m_usesBeforeAfterRules = m_usesBeforeAfterRules || m_styleSelector->usesBeforeAfterRules(); + m_usesLinkRules = m_usesLinkRules || m_styleSelector->usesLinkRules(); } void Document::attach() @@ -1944,9 +1968,16 @@ void Document::open(Document* ownerDocument) } if (m_frame) { - ScriptableDocumentParser* parser = scriptableDocumentParser(); - if (m_frame->loader()->isLoadingMainResource() || (parser && parser->isParsing() && parser->isExecutingScript())) - return; + if (ScriptableDocumentParser* parser = scriptableDocumentParser()) { + if (parser->isParsing()) { + // FIXME: HTML5 doesn't tell us to check this, it might not be correct. + if (parser->isExecutingScript()) + return; + + if (!parser->wasCreatedByScript() && parser->hasInsertionPoint()) + return; + } + } if (m_frame->loader()->state() == FrameStateProvisional) m_frame->loader()->stopAllLoaders(); @@ -2065,13 +2096,16 @@ void Document::explicitClose() // Because we have no frame, we don't know if all loading has completed, // so we just call implicitClose() immediately. FIXME: This might fire // the load event prematurely <http://bugs.webkit.org/show_bug.cgi?id=14568>. + if (m_parser) + m_parser->finish(); implicitClose(); return; } // This code calls implicitClose() if all loading has completed. loader()->writer()->endIfNotLoadingMainResource(); - m_frame->loader()->checkCompleted(); + if (m_frame) + m_frame->loader()->checkCompleted(); } void Document::implicitClose() @@ -2202,6 +2236,14 @@ bool Document::shouldScheduleLayout() return (haveStylesheetsLoaded() && body()) || (documentElement() && !documentElement()->hasTagName(htmlTag)); } + +bool Document::isLayoutTimerActive() +{ + if (!view() || !view()->layoutPending()) + return false; + bool isPendingLayoutImmediate = minimumLayoutDelay() == m_extraLayoutDelay; + return isPendingLayoutImmediate; +} int Document::minimumLayoutDelay() { @@ -2288,6 +2330,14 @@ KURL Document::virtualCompleteURL(const String& url) const return completeURL(url); } +double Document::minimumTimerInterval() const +{ + Page* p = page(); + if (!p) + return ScriptExecutionContext::minimumTimerInterval(); + return p->settings()->minDOMTimerInterval(); +} + EventTarget* Document::errorEventTarget() { return domWindow(); @@ -3007,6 +3057,14 @@ void Document::removeStyleSheetCandidateNode(Node* node) void Document::recalcStyleSelector() { + if (m_inStyleRecalc) { + // SVG <use> element may manage to invalidate style selector in the middle of a style recalc. + // https://bugs.webkit.org/show_bug.cgi?id=54344 + // FIXME: This should be fixed in SVG and this code replaced with ASSERT(!m_inStyleRecalc). + m_hasDirtyStyleSelector = true; + scheduleForcedStyleRecalc(); + return; + } if (!renderer() || !attached()) return; @@ -3109,6 +3167,7 @@ void Document::recalcStyleSelector() m_styleSelector.clear(); m_didCalculateStyleSelector = true; + m_hasDirtyStyleSelector = false; } void Document::setHoverNode(PassRefPtr<Node> newHoverNode) @@ -3200,12 +3259,10 @@ bool Document::setFocusedNode(PassRefPtr<Node> newFocusedNode) oldFocusedNode->setFocus(false); // Dispatch a change event for text fields or textareas that have been edited - RenderObject* r = oldFocusedNode->renderer(); - if (r && r->isTextControl() && toRenderTextControl(r)->wasChangedSinceLastChangeEvent()) { - static_cast<Element*>(oldFocusedNode.get())->dispatchFormControlChangeEvent(); - r = oldFocusedNode->renderer(); - if (r && r->isTextControl()) - toRenderTextControl(r)->setChangedSinceLastChangeEvent(false); + if (oldFocusedNode->isElementNode()) { + Element* element = static_cast<Element*>(oldFocusedNode.get()); + if (element->wasChangedSinceLastFormControlChangeEvent()) + element->dispatchFormControlChangeEvent(); } // Dispatch the blur event and let the node do any other blur related activities (important for text fields) @@ -3360,7 +3417,7 @@ void Document::moveNodeIteratorsToNewDocument(Node* node, Document* newDocument) void Document::nodeChildrenChanged(ContainerNode* container) { - if (!disableRangeMutation(page())) { + if (!disableRangeMutation(page()) && !m_ranges.isEmpty()) { HashSet<Range*>::const_iterator end = m_ranges.end(); for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) (*it)->nodeChildrenChanged(container); @@ -3369,7 +3426,7 @@ void Document::nodeChildrenChanged(ContainerNode* container) void Document::nodeChildrenWillBeRemoved(ContainerNode* container) { - if (!disableRangeMutation(page())) { + if (!disableRangeMutation(page()) && !m_ranges.isEmpty()) { HashSet<Range*>::const_iterator end = m_ranges.end(); for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) (*it)->nodeChildrenWillBeRemoved(container); @@ -3395,7 +3452,7 @@ void Document::nodeWillBeRemoved(Node* n) for (HashSet<NodeIterator*>::const_iterator it = m_nodeIterators.begin(); it != nodeIteratorsEnd; ++it) (*it)->nodeWillBeRemoved(n); - if (!disableRangeMutation(page())) { + if (!disableRangeMutation(page()) && !m_ranges.isEmpty()) { HashSet<Range*>::const_iterator rangesEnd = m_ranges.end(); for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != rangesEnd; ++it) (*it)->nodeWillBeRemoved(n); @@ -3427,7 +3484,7 @@ void Document::nodeWillBeRemoved(Node* n) void Document::textInserted(Node* text, unsigned offset, unsigned length) { - if (!disableRangeMutation(page())) { + if (!disableRangeMutation(page()) && !m_ranges.isEmpty()) { HashSet<Range*>::const_iterator end = m_ranges.end(); for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) (*it)->textInserted(text, offset, length); @@ -3439,7 +3496,7 @@ void Document::textInserted(Node* text, unsigned offset, unsigned length) void Document::textRemoved(Node* text, unsigned offset, unsigned length) { - if (!disableRangeMutation(page())) { + if (!disableRangeMutation(page()) && !m_ranges.isEmpty()) { HashSet<Range*>::const_iterator end = m_ranges.end(); for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) (*it)->textRemoved(text, offset, length); @@ -3452,7 +3509,7 @@ void Document::textRemoved(Node* text, unsigned offset, unsigned length) void Document::textNodesMerged(Text* oldNode, unsigned offset) { - if (!disableRangeMutation(page())) { + if (!disableRangeMutation(page()) && !m_ranges.isEmpty()) { NodeWithIndex oldNodeWithIndex(oldNode); HashSet<Range*>::const_iterator end = m_ranges.end(); for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) @@ -3464,7 +3521,7 @@ void Document::textNodesMerged(Text* oldNode, unsigned offset) void Document::textNodeSplit(Text* oldNode) { - if (!disableRangeMutation(page())) { + if (!disableRangeMutation(page()) && !m_ranges.isEmpty()) { HashSet<Range*>::const_iterator end = m_ranges.end(); for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it) (*it)->textNodeSplit(oldNode); @@ -3527,6 +3584,12 @@ void Document::enqueueWindowEvent(PassRefPtr<Event> event) m_eventQueue->enqueueEvent(event); } +void Document::enqueueDocumentEvent(PassRefPtr<Event> event) +{ + event->setTarget(this); + m_eventQueue->enqueueEvent(event); +} + PassRefPtr<Event> Document::createEvent(const String& eventType, ExceptionCode& ec) { RefPtr<Event> event; @@ -4001,6 +4064,23 @@ void Document::unregisterForMediaVolumeCallbacks(Element* e) m_mediaVolumeCallbackElements.remove(e); } +void Document::privateBrowsingStateDidChange() +{ + HashSet<Element*>::iterator end = m_privateBrowsingStateChangedElements.end(); + for (HashSet<Element*>::iterator it = m_privateBrowsingStateChangedElements.begin(); it != end; ++it) + (*it)->privateBrowsingStateDidChange(); +} + +void Document::registerForPrivateBrowsingStateChangedCallbacks(Element* e) +{ + m_privateBrowsingStateChangedElements.add(e); +} + +void Document::unregisterForPrivateBrowsingStateChangedCallbacks(Element* e) +{ + m_privateBrowsingStateChangedElements.remove(e); +} + void Document::setShouldCreateRenderers(bool f) { m_createRenderers = f; @@ -4463,13 +4543,15 @@ void Document::initSecurityContext() // This can occur via document.implementation.createDocument(). m_cookieURL = KURL(ParsedURLString, ""); ScriptExecutionContext::setSecurityOrigin(SecurityOrigin::createEmpty()); + m_contentSecurityPolicy = ContentSecurityPolicy::create(); return; } // In the common case, create the security context from the currently - // loading URL. + // loading URL with a fresh content security policy. m_cookieURL = m_url; ScriptExecutionContext::setSecurityOrigin(SecurityOrigin::create(m_url, m_frame->loader()->sandboxFlags())); + m_contentSecurityPolicy = ContentSecurityPolicy::create(); if (SecurityOrigin::allowSubstituteDataAccessToLocal()) { // If this document was loaded with substituteData, then the document can @@ -4513,6 +4595,8 @@ void Document::initSecurityContext() // We alias the SecurityOrigins to match Firefox, see Bug 15313 // https://bugs.webkit.org/show_bug.cgi?id=15313 ScriptExecutionContext::setSecurityOrigin(ownerFrame->document()->securityOrigin()); + // FIXME: Consider moving m_contentSecurityPolicy into SecurityOrigin. + m_contentSecurityPolicy = ownerFrame->document()->contentSecurityPolicy(); } } @@ -4696,22 +4780,75 @@ public: OwnPtr<ScriptExecutionContext::Task> task; }; -static void performTask(void* ctx) +void Document::didReceiveTask(void* untypedContext) { ASSERT(isMainThread()); - PerformTaskContext* context = reinterpret_cast<PerformTaskContext*>(ctx); + OwnPtr<PerformTaskContext> context = adoptPtr(static_cast<PerformTaskContext*>(untypedContext)); ASSERT(context); - if (Document* document = context->documentReference->document()) - context->task->performTask(document); + Document* document = context->documentReference->document(); + if (!document) + return; + + Page* page = document->page(); + if ((page && page->defersLoading()) || !document->m_pendingTasks.isEmpty()) { + document->m_pendingTasks.append(context->task.release()); + return; + } - delete context; + context->task->performTask(document); } void Document::postTask(PassOwnPtr<Task> task) { - callOnMainThread(performTask, new PerformTaskContext(m_weakReference, task)); + callOnMainThread(didReceiveTask, new PerformTaskContext(m_weakReference, task)); +} + +void Document::pendingTasksTimerFired(Timer<Document>*) +{ + while (!m_pendingTasks.isEmpty()) { + OwnPtr<Task> task = m_pendingTasks[0].release(); + m_pendingTasks.remove(0); + task->performTask(this); + } +} + +void Document::suspendScheduledTasks() +{ + suspendScriptedAnimationControllerCallbacks(); + suspendActiveDOMObjects(ActiveDOMObject::WillShowDialog); + asyncScriptRunner()->suspend(); + m_pendingTasksTimer.stop(); + if (m_parser) + m_parser->suspendScheduledTasks(); +} + +void Document::resumeScheduledTasks() +{ + if (m_parser) + m_parser->resumeScheduledTasks(); + if (!m_pendingTasks.isEmpty()) + m_pendingTasksTimer.startOneShot(0); + asyncScriptRunner()->resume(); + resumeActiveDOMObjects(); + resumeScriptedAnimationControllerCallbacks(); +} + +void Document::suspendScriptedAnimationControllerCallbacks() +{ +#if ENABLE(REQUEST_ANIMATION_FRAME) + if (m_scriptedAnimationController) + m_scriptedAnimationController->suspend(); +#endif +} + +void Document::resumeScriptedAnimationControllerCallbacks() +{ +#if ENABLE(REQUEST_ANIMATION_FRAME) + if (m_scriptedAnimationController) + m_scriptedAnimationController->resume(); +#endif } Element* Document::findAnchor(const String& name) @@ -4805,7 +4942,7 @@ bool Document::isXHTMLMPDocument() const // MUST accept XHTMLMP document identified as "application/vnd.wap.xhtml+xml" // and SHOULD accept it identified as "application/xhtml+xml" , "application/xhtml+xml" is a // general MIME type for all XHTML documents, not only for XHTMLMP - return frame()->loader()->writer()->mimeType() == "application/vnd.wap.xhtml+xml"; + return loader()->writer()->mimeType() == "application/vnd.wap.xhtml+xml"; } #endif @@ -4944,82 +5081,26 @@ void Document::loadEventDelayTimerFired(Timer<Document>*) } #if ENABLE(REQUEST_ANIMATION_FRAME) -int Document::webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback, Element* e) +int Document::webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback, Element* animationElement) { - if (!m_requestAnimationFrameCallbacks) - m_requestAnimationFrameCallbacks = new RequestAnimationFrameCallbackList; - int id = m_nextRequestAnimationFrameCallbackId++; - callback->m_firedOrCancelled = false; - callback->m_id = id; - callback->m_element = e; - m_requestAnimationFrameCallbacks->append(callback); - if (FrameView* v = view()) - v->scheduleAnimation(); - return id; + if (!m_scriptedAnimationController) + m_scriptedAnimationController = ScriptedAnimationController::create(this); + + return m_scriptedAnimationController->registerCallback(callback, animationElement); } void Document::webkitCancelRequestAnimationFrame(int id) { - if (!m_requestAnimationFrameCallbacks) + if (!m_scriptedAnimationController) return; - for (size_t i = 0; i < m_requestAnimationFrameCallbacks->size(); ++i) { - if (m_requestAnimationFrameCallbacks->at(i)->m_id == id) { - m_requestAnimationFrameCallbacks->at(i)->m_firedOrCancelled = true; - m_requestAnimationFrameCallbacks->remove(i); - return; - } - } + m_scriptedAnimationController->cancelCallback(id); } void Document::serviceScriptedAnimations(DOMTimeStamp time) { - if (!m_requestAnimationFrameCallbacks) + if (!m_scriptedAnimationController) return; - // We want to run the callback for all elements in the document that have registered - // for a callback and that are visible. Running the callbacks can cause new callbacks - // to be registered, existing callbacks to be cancelled, and elements to gain or lose - // visibility so this code has to iterate carefully. - - // FIXME: Currently, this code doesn't do any visibility tests beyond checking display: - - // First, generate a list of callbacks to consider. Callbacks registered from this point - // on are considered only for the "next" frame, not this one. - RequestAnimationFrameCallbackList callbacks(*m_requestAnimationFrameCallbacks); - - // Firing the callback may cause the visibility of other elements to change. To avoid - // missing any callbacks, we keep iterating through the list of candiate callbacks and firing - // them until nothing new becomes visible. - bool firedCallback; - do { - firedCallback = false; - // A previous iteration may have invalidated style (or layout). Update styles for each iteration - // for now since all we check is the existence of a renderer. - updateStyleIfNeeded(); - for (size_t i = 0; i < callbacks.size(); ++i) { - RequestAnimationFrameCallback* callback = callbacks[i].get(); - if (!callback->m_firedOrCancelled && (!callback->m_element || callback->m_element->renderer())) { - callback->m_firedOrCancelled = true; - callback->handleEvent(time); - firedCallback = true; - callbacks.remove(i); - break; - } - } - } while (firedCallback); - - // Remove any callbacks we fired from the list of pending callbacks. - for (size_t i = 0; i < m_requestAnimationFrameCallbacks->size();) { - if (m_requestAnimationFrameCallbacks->at(i)->m_firedOrCancelled) - m_requestAnimationFrameCallbacks->remove(i); - else - ++i; - } - - // In most cases we expect this list to be empty, so no need to keep around the vector's inline buffer. - if (!m_requestAnimationFrameCallbacks->size()) - m_requestAnimationFrameCallbacks.clear(); - else if (FrameView* v = view()) - v->scheduleAnimation(); + m_scriptedAnimationController->serviceScriptedAnimations(time); } #endif diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h index 9ace93d..ae9b24e 100644 --- a/Source/WebCore/dom/Document.h +++ b/Source/WebCore/dom/Document.h @@ -34,7 +34,6 @@ #include "ContainerNode.h" #include "ContentSecurityPolicy.h" #include "DOMTimeStamp.h" -#include "DocumentLoader.h" #include "DocumentOrderedMap.h" #include "DocumentTiming.h" #include "QualifiedName.h" @@ -61,6 +60,7 @@ class CachedResourceLoader; class CachedScript; class CanvasRenderingContext; class CharacterData; +class CSSPrimitiveValueCache; class CSSStyleDeclaration; class CSSStyleSelector; class CSSStyleSheet; @@ -71,6 +71,7 @@ class DOMWindow; class Database; class DatabaseThread; class DocumentFragment; +class DocumentLoader; class DocumentMarkerController; class DocumentType; class DocumentWeakReference; @@ -152,6 +153,7 @@ class TouchList; #if ENABLE(REQUEST_ANIMATION_FRAME) class RequestAnimationFrameCallback; +class ScriptedAnimationController; #endif typedef int ExceptionCode; @@ -290,6 +292,7 @@ public: DEFINE_ATTRIBUTE_EVENT_LISTENER(reset); DEFINE_ATTRIBUTE_EVENT_LISTENER(search); DEFINE_ATTRIBUTE_EVENT_LISTENER(selectstart); + DEFINE_ATTRIBUTE_EVENT_LISTENER(selectionchange); #if ENABLE(TOUCH_EVENTS) DEFINE_ATTRIBUTE_EVENT_LISTENER(touchstart); DEFINE_ATTRIBUTE_EVENT_LISTENER(touchmove); @@ -304,7 +307,7 @@ public: DocumentType* doctype() const { return m_docType.get(); } - DOMImplementation* implementation() const; + DOMImplementation* implementation(); Element* documentElement() const { @@ -432,7 +435,15 @@ public: #endif virtual bool isFrameSet() const { return false; } + PassRefPtr<CSSPrimitiveValueCache> cssPrimitiveValueCache() const; + CSSStyleSelector* styleSelectorIfExists() const { return m_styleSelector.get(); } + + bool usesViewSourceStyles() const { return m_usesViewSourceStyles; } + void setUsesViewSourceStyles(bool usesViewSourceStyles) { m_usesViewSourceStyles = usesViewSourceStyles; } + + bool sawElementsInKnownNamespaces() const { return m_sawElementsInKnownNamespaces; } + CSSStyleSelector* styleSelector() { if (!m_styleSelector) @@ -480,16 +491,13 @@ public: void styleSelectorChanged(StyleSelectorUpdateFlag); void recalcStyleSelector(); - bool usesDescendantRules() const { return m_usesDescendantRules; } - void setUsesDescendantRules(bool b) { m_usesDescendantRules = b; } - bool usesSiblingRules() const { return m_usesSiblingRules; } - void setUsesSiblingRules(bool b) { m_usesSiblingRules = b; } + bool usesSiblingRules() const { return m_usesSiblingRules || m_usesSiblingRulesOverride; } + void setUsesSiblingRules(bool b) { m_usesSiblingRulesOverride = b; } bool usesFirstLineRules() const { return m_usesFirstLineRules; } - void setUsesFirstLineRules(bool b) { m_usesFirstLineRules = b; } bool usesFirstLetterRules() const { return m_usesFirstLetterRules; } void setUsesFirstLetterRules(bool b) { m_usesFirstLetterRules = b; } - bool usesBeforeAfterRules() const { return m_usesBeforeAfterRules; } - void setUsesBeforeAfterRules(bool b) { m_usesBeforeAfterRules = b; } + bool usesBeforeAfterRules() const { return m_usesBeforeAfterRules || m_usesBeforeAfterRulesOverride; } + void setUsesBeforeAfterRules(bool b) { m_usesBeforeAfterRulesOverride = b; } bool usesRemUnits() const { return m_usesRemUnits; } void setUsesRemUnits(bool b) { m_usesRemUnits = b; } bool usesLinkRules() const { return linkColor() != visitedLinkColor() || m_usesLinkRules; } @@ -644,6 +652,7 @@ public: void setExtraLayoutDelay(int delay) { m_extraLayoutDelay = delay; } bool shouldScheduleLayout(); + bool isLayoutTimerActive(); int elapsedTime() const; void setTextColor(const Color& color) { m_textColor = color; } @@ -966,6 +975,9 @@ public: virtual void addMessage(MessageSource, MessageType, MessageLevel, const String& message, unsigned lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack>); virtual void postTask(PassOwnPtr<Task>); // Executes the task on context's thread asynchronously. + virtual void suspendScriptedAnimationControllerCallbacks(); + virtual void resumeScriptedAnimationControllerCallbacks(); + #if USE(JSC) typedef JSC::WeakGCMap<WebCore::Node*, JSNode> JSWrapperCache; typedef HashMap<DOMWrapperWorld*, JSWrapperCache*> JSWrapperCacheMap; @@ -992,6 +1004,10 @@ public: void unregisterForMediaVolumeCallbacks(Element*); void mediaVolumeDidChange(); + void registerForPrivateBrowsingStateChangedCallbacks(Element*); + void unregisterForPrivateBrowsingStateChangedCallbacks(Element*); + void privateBrowsingStateDidChange(); + void setShouldCreateRenderers(bool); bool shouldCreateRenderers(); @@ -1059,6 +1075,7 @@ public: void setContainsValidityStyleRules() { m_containsValidityStyleRules = true; } void enqueueWindowEvent(PassRefPtr<Event>); + void enqueueDocumentEvent(PassRefPtr<Event>); void enqueuePageshowEvent(PageshowEventPersistence); void enqueueHashchangeEvent(const String& oldURL, const String& newURL); void enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject); @@ -1116,7 +1133,10 @@ public: void initDNSPrefetch(); - ContentSecurityPolicy* contentSecurityPolicy() { return &m_contentSecurityPolicy; } + ContentSecurityPolicy* contentSecurityPolicy() { return m_contentSecurityPolicy.get(); } + + void suspendScheduledTasks(); + void resumeScheduledTasks(); protected: Document(Frame*, const KURL&, bool isXHTML, bool isHTML); @@ -1149,6 +1169,8 @@ private: virtual const KURL& virtualURL() const; // Same as url(), but needed for ScriptExecutionContext to implement it without a performance loss for direct calls. virtual KURL virtualCompleteURL(const String&) const; // Same as completeURL() for the same reason as above. + virtual double minimumTimerInterval() const; + String encoding() const; void updateTitle(); @@ -1163,8 +1185,15 @@ private: void loadEventDelayTimerFired(Timer<Document>*); + void pendingTasksTimerFired(Timer<Document>*); + + static void didReceiveTask(void*); + OwnPtr<CSSStyleSelector> m_styleSelector; bool m_didCalculateStyleSelector; + bool m_hasDirtyStyleSelector; + + mutable RefPtr<CSSPrimitiveValueCache> m_cssPrimitiveValueCache; Frame* m_frame; DocumentLoader* m_documentLoader; @@ -1269,11 +1298,12 @@ private: bool m_inStyleRecalc; bool m_closeAfterStyleRecalc; - bool m_usesDescendantRules; bool m_usesSiblingRules; + bool m_usesSiblingRulesOverride; bool m_usesFirstLineRules; bool m_usesFirstLetterRules; bool m_usesBeforeAfterRules; + bool m_usesBeforeAfterRulesOverride; bool m_usesRemUnits; bool m_usesLinkRules; bool m_gotoAnchorNeededAfterStylesheetsLoad; @@ -1373,12 +1403,16 @@ private: HashSet<Element*> m_documentActivationCallbackElements; HashSet<Element*> m_mediaVolumeCallbackElements; + HashSet<Element*> m_privateBrowsingStateChangedElements; bool m_useSecureKeyboardEntryWhenActive; bool m_isXHTML; bool m_isHTML; + bool m_usesViewSourceStyles; + bool m_sawElementsInKnownNamespaces; + unsigned m_numNodeListCaches; #if USE(JSC) @@ -1388,7 +1422,7 @@ private: bool m_usingGeolocation; - OwnPtr<EventQueue> m_eventQueue; + RefPtr<EventQueue> m_eventQueue; #if ENABLE(WML) bool m_containsWMLContent; @@ -1422,12 +1456,13 @@ private: unsigned m_writeRecursionDepth; #if ENABLE(REQUEST_ANIMATION_FRAME) - typedef Vector<RefPtr<RequestAnimationFrameCallback> > RequestAnimationFrameCallbackList; - OwnPtr<RequestAnimationFrameCallbackList> m_requestAnimationFrameCallbacks; - int m_nextRequestAnimationFrameCallbackId; + OwnPtr<ScriptedAnimationController> m_scriptedAnimationController; #endif - ContentSecurityPolicy m_contentSecurityPolicy; + RefPtr<ContentSecurityPolicy> m_contentSecurityPolicy; + + Timer<Document> m_pendingTasksTimer; + Vector<OwnPtr<Task> > m_pendingTasks; }; inline bool Document::hasElementWithId(AtomicStringImpl* id) const diff --git a/Source/WebCore/dom/Document.idl b/Source/WebCore/dom/Document.idl index c215df0..11d6678 100644 --- a/Source/WebCore/dom/Document.idl +++ b/Source/WebCore/dom/Document.idl @@ -317,6 +317,7 @@ module core { attribute [DontEnum] EventListener onreset; attribute [DontEnum] EventListener onsearch; attribute [DontEnum] EventListener onselectstart; + attribute [DontEnum] EventListener onselectionchange; attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchstart; attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchmove; attribute [DontEnum,Conditional=TOUCH_EVENTS,EnabledAtRuntime] EventListener ontouchend; diff --git a/Source/WebCore/dom/DocumentMarker.h b/Source/WebCore/dom/DocumentMarker.h index 76b85bb..81112bf 100644 --- a/Source/WebCore/dom/DocumentMarker.h +++ b/Source/WebCore/dom/DocumentMarker.h @@ -38,7 +38,8 @@ struct DocumentMarker { Grammar = 1 << 1, TextMatch = 1 << 2, // Text has been modified by spell correction. On some platforms, this prevents the text - // to be autocorrected again. + // to be autocorrected again. On post Snow Leopard Mac OS X, if a Replacement marker contains + // non-empty description, a reversion UI will be shown. Replacement = 1 << 3, // Renderer needs to add underline indicating that the text has been modified by spell // correction. Text with Replacement marker doesn't necessarily has CorrectionIndicator diff --git a/Source/WebCore/dom/DocumentMarkerController.cpp b/Source/WebCore/dom/DocumentMarkerController.cpp index 1bc7cb4..f646f3c 100644 --- a/Source/WebCore/dom/DocumentMarkerController.cpp +++ b/Source/WebCore/dom/DocumentMarkerController.cpp @@ -38,8 +38,19 @@ static IntRect placeholderRectForMarker() return IntRect(-1, -1, -1, -1); } +inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerTypes types) +{ + return m_possiblyExistingMarkerTypes & types; +} + +DocumentMarkerController::DocumentMarkerController() + : m_possiblyExistingMarkerTypes(0) +{ +} + void DocumentMarkerController::detach() { + m_possiblyExistingMarkerTypes = 0; if (m_markers.isEmpty()) return; deleteAllValues(m_markers); @@ -59,10 +70,11 @@ void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerTyp void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker) { - if (m_markers.isEmpty()) - return; - for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { + if (!possiblyHasMarkers(markerTypes)) + return; + ASSERT(!m_markers.isEmpty()); + RefPtr<Range> textPiece = markedText.range(); int startOffset = textPiece->startOffset(); int endOffset = textPiece->endOffset(); @@ -79,6 +91,8 @@ void DocumentMarkerController::addMarker(Node* node, DocumentMarker newMarker) if (newMarker.endOffset == newMarker.startOffset) return; + m_possiblyExistingMarkerTypes |= newMarker.type; + MarkerMapVectorPair* vectorPair = m_markers.get(node); if (!vectorPair) { @@ -143,6 +157,10 @@ void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, if (length <= 0) return; + if (!possiblyHasMarkers(markerType)) + return; + ASSERT(!m_markers.isEmpty()); + MarkerMapVectorPair* vectorPair = m_markers.get(srcNode); if (!vectorPair) return; @@ -185,6 +203,10 @@ void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, i if (length <= 0) return; + if (!possiblyHasMarkers(markerTypes)) + return; + ASSERT(!(m_markers.isEmpty())); + MarkerMapVectorPair* vectorPair = m_markers.get(node); if (!vectorPair) return; @@ -242,6 +264,9 @@ void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, i m_markers.remove(node); delete vectorPair; } + + if (m_markers.isEmpty()) + m_possiblyExistingMarkerTypes = 0; // repaint the affected node if (docDirty && node->renderer()) @@ -250,6 +275,10 @@ void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, i DocumentMarker* DocumentMarkerController::markerContainingPoint(const IntPoint& point, DocumentMarker::MarkerType markerType) { + if (!possiblyHasMarkers(markerType)) + return 0; + ASSERT(!(m_markers.isEmpty())); + // 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) { @@ -292,6 +321,10 @@ Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker { Vector<IntRect> result; + if (!possiblyHasMarkers(markerType)) + return result; + ASSERT(!(m_markers.isEmpty())); + // outer loop: process each node MarkerMap::iterator end = m_markers.end(); for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { @@ -322,6 +355,10 @@ Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes) { + if (!possiblyHasMarkers(markerTypes)) + return; + ASSERT(!m_markers.isEmpty()); + MarkerMap::iterator iterator = m_markers.find(node); if (iterator != m_markers.end()) removeMarkersFromMarkerMapVectorPair(node, iterator->second, markerTypes); @@ -329,6 +366,10 @@ void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerT void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerTypes) { + if (!possiblyHasMarkers(markerTypes)) + return; + ASSERT(!m_markers.isEmpty()); + // outer loop: process each markered node in the document MarkerMap markerMapCopy = m_markers; MarkerMap::iterator end = markerMapCopy.end(); @@ -337,6 +378,8 @@ void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerT MarkerMapVectorPair* vectorPair = i->second; removeMarkersFromMarkerMapVectorPair(node, vectorPair, markerTypes); } + + m_possiblyExistingMarkerTypes &= ~markerTypes; } // This function may release node and vectorPair. @@ -383,10 +426,16 @@ void DocumentMarkerController::removeMarkersFromMarkerMapVectorPair(Node* node, delete vectorPair; } } + if (m_markers.isEmpty()) + m_possiblyExistingMarkerTypes = 0; } void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerType markerType) { + if (!possiblyHasMarkers(markerType)) + return; + ASSERT(!m_markers.isEmpty()); + // 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) { @@ -456,6 +505,10 @@ void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const Int void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta, DocumentMarker::MarkerType markerType) { + if (!possiblyHasMarkers(markerType)) + return; + ASSERT(!m_markers.isEmpty()); + MarkerMapVectorPair* vectorPair = m_markers.get(node); if (!vectorPair) return; @@ -485,8 +538,9 @@ void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, in void DocumentMarkerController::setMarkersActive(Range* range, bool active) { - if (m_markers.isEmpty()) + if (!possiblyHasMarkers(DocumentMarker::AllMarkers)) return; + ASSERT(!m_markers.isEmpty()); ExceptionCode ec = 0; Node* startContainer = range->startContainer(ec); @@ -532,8 +586,9 @@ void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes) { - if (m_markers.isEmpty()) + if (!possiblyHasMarkers(markerTypes)) return false; + ASSERT(!m_markers.isEmpty()); Node* startContainer = range->startContainer(); ASSERT(startContainer); @@ -565,6 +620,43 @@ bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTy } return false; } + +void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(Range* range, DocumentMarker::MarkerTypes markerTypes) +{ + if (!possiblyHasMarkers(markerTypes)) + return; + ASSERT(!m_markers.isEmpty()); + + Node* startContainer = range->startContainer(); + Node* endContainer = range->endContainer(); + + Node* pastLastNode = range->pastLastNode(); + for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { + unsigned startOffset = node == startContainer ? range->startOffset() : 0; + unsigned endOffset = node == endContainer ? static_cast<unsigned>(range->endOffset()) : std::numeric_limits<unsigned>::max(); + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) + continue; + + Vector<DocumentMarker>& markers = vectorPair->first; + 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 & markerTypes)) { + i++; + continue; + } + + marker.description = String(); + } + } +} + #ifndef NDEBUG void DocumentMarkerController::showMarkers() const { diff --git a/Source/WebCore/dom/DocumentMarkerController.h b/Source/WebCore/dom/DocumentMarkerController.h index 21b351b..6fa060a 100644 --- a/Source/WebCore/dom/DocumentMarkerController.h +++ b/Source/WebCore/dom/DocumentMarkerController.h @@ -41,7 +41,7 @@ class Range; class DocumentMarkerController { WTF_MAKE_NONCOPYABLE(DocumentMarkerController); WTF_MAKE_FAST_ALLOCATED; public: - DocumentMarkerController() { } + DocumentMarkerController(); ~DocumentMarkerController() { detach(); } void detach(); @@ -69,6 +69,7 @@ public: DocumentMarker* markerContainingPoint(const IntPoint&, DocumentMarker::MarkerType = DocumentMarker::AllMarkers); Vector<DocumentMarker> markersForNode(Node*); Vector<IntRect> renderedRectsForMarkers(DocumentMarker::MarkerType = DocumentMarker::AllMarkers); + void clearDescriptionOnMarkersIntersectingRange(Range*, DocumentMarker::MarkerTypes); #ifndef NDEBUG void showMarkers() const; @@ -77,8 +78,12 @@ public: private: typedef std::pair<Vector<DocumentMarker>, Vector<IntRect> > MarkerMapVectorPair; typedef HashMap<RefPtr<Node>, MarkerMapVectorPair*> MarkerMap; - MarkerMap m_markers; + bool possiblyHasMarkers(DocumentMarker::MarkerTypes); void removeMarkersFromMarkerMapVectorPair(Node*, MarkerMapVectorPair*, DocumentMarker::MarkerTypes); + + MarkerMap m_markers; + // Provide a quick way to determine whether a particular marker type is absent without going through the map. + DocumentMarker::MarkerTypes m_possiblyExistingMarkerTypes; }; } // namespace WebCore diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp index 142febd..41c0fb1 100644 --- a/Source/WebCore/dom/Element.cpp +++ b/Source/WebCore/dom/Element.cpp @@ -888,6 +888,14 @@ RenderObject* Element::createRenderer(RenderArena* arena, RenderStyle* style) return RenderObject::createObject(this, style); } +bool Element::wasChangedSinceLastFormControlChangeEvent() const +{ + return false; +} + +void Element::setChangedSinceLastFormControlChangeEvent(bool) +{ +} void Element::insertedIntoDocument() { @@ -1038,7 +1046,6 @@ void Element::recalcStyle(StyleChange change) // Ref currentStyle in case it would otherwise be deleted when setRenderStyle() is called. RefPtr<RenderStyle> currentStyle(renderStyle()); bool hasParentStyle = parentOrHostNode() ? parentOrHostNode()->renderStyle() : false; - bool hasPositionalRules = needsStyleRecalc() && currentStyle && currentStyle->childrenAffectedByPositionalRules(); bool hasDirectAdjacentRules = currentStyle && currentStyle->childrenAffectedByDirectAdjacentRules(); if ((change > NoChange || needsStyleRecalc())) { @@ -1086,11 +1093,9 @@ void Element::recalcStyle(StyleChange change) if (ch != NoChange || pseudoStyleCacheIsInvalid(currentStyle.get(), newStyle.get()) || (change == Force && renderer() && renderer()->requiresForcedStyleRecalcPropagation())) { setRenderStyle(newStyle); - } else if (needsStyleRecalc() && (styleChangeType() != SyntheticStyleChange) && (document()->usesSiblingRules() || document()->usesDescendantRules())) { + } else if (needsStyleRecalc() && styleChangeType() != SyntheticStyleChange) { // Although no change occurred, we use the new style so that the cousin style sharing code won't get - // fooled into believing this style is the same. This is only necessary if the document actually uses - // sibling/descendant rules, since otherwise it isn't possible for ancestor styles to affect sharing of - // descendants. + // fooled into believing this style is the same. if (renderer()) renderer()->setStyleInternal(newStyle.get()); else @@ -1103,7 +1108,7 @@ void Element::recalcStyle(StyleChange change) // all the way down the tree. This is simpler than having to maintain a cache of objects (and such font size changes should be rare anyway). if (document()->usesRemUnits() && ch != NoChange && currentStyle && newStyle && currentStyle->fontSize() != newStyle->fontSize() && document()->documentElement() == this) change = Force; - else if ((document()->usesDescendantRules() || hasPositionalRules) && styleChangeType() >= FullStyleChange) + else if (styleChangeType() >= FullStyleChange) change = Force; else change = ch; @@ -1155,6 +1160,10 @@ void Element::setShadowRoot(PassRefPtr<Node> node) ensureRareData()->m_shadowRoot = newRoot.get(); newRoot->setShadowHost(this); + if (inDocument()) + newRoot->insertedIntoDocument(); + if (attached() && !newRoot->attached()) + newRoot->lazyAttach(); } void Element::removeShadowRoot() diff --git a/Source/WebCore/dom/Element.h b/Source/WebCore/dom/Element.h index a85f8cd..5c54ce4 100644 --- a/Source/WebCore/dom/Element.h +++ b/Source/WebCore/dom/Element.h @@ -3,7 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Peter Kelly (pmk@post.com) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -274,6 +274,9 @@ public: // Use Document::registerForMediaVolumeCallbacks() to subscribe to this virtual void mediaVolumeDidChange() { } + // Use Document::registerForPrivateBrowsingStateChangedCallbacks() to subscribe to this. + virtual void privateBrowsingStateDidChange() { } + bool isFinishedParsingChildren() const { return isParsingChildrenFinished(); } virtual void finishParsingChildren(); virtual void beginParsingChildren(); @@ -323,6 +326,8 @@ public: virtual bool saveFormControlState(String&) const { return false; } virtual void restoreFormControlState(const String&) { } + virtual bool wasChangedSinceLastFormControlChangeEvent() const; + virtual void setChangedSinceLastFormControlChangeEvent(bool); virtual void dispatchFormControlChangeEvent() { } #if ENABLE(SVG) @@ -424,6 +429,11 @@ inline bool Node::hasTagName(const QualifiedName& name) const { return isElementNode() && toElement(this)->hasTagName(name); } + +inline bool Node::hasLocalName(const AtomicString& name) const +{ + return isElementNode() && toElement(this)->hasLocalName(name); +} inline bool Node::hasAttributes() const { diff --git a/Source/WebCore/dom/Event.cpp b/Source/WebCore/dom/Event.cpp index bdc1c58..795dace 100644 --- a/Source/WebCore/dom/Event.cpp +++ b/Source/WebCore/dom/Event.cpp @@ -193,22 +193,7 @@ bool Event::isStorageEvent() const #endif #if ENABLE(INDEXED_DATABASE) -bool Event::isIDBAbortEvent() const -{ - return false; -} - -bool Event::isIDBCompleteEvent() const -{ - return false; -} - -bool Event::isIDBErrorEvent() const -{ - return false; -} - -bool Event::isIDBSuccessEvent() const +bool Event::isIDBVersionChangeEvent() const { return false; } diff --git a/Source/WebCore/dom/Event.h b/Source/WebCore/dom/Event.h index 81f9e6b..ba9576b 100644 --- a/Source/WebCore/dom/Event.h +++ b/Source/WebCore/dom/Event.h @@ -126,10 +126,7 @@ namespace WebCore { virtual bool isStorageEvent() const; #endif #if ENABLE(INDEXED_DATABASE) - virtual bool isIDBAbortEvent() const; - virtual bool isIDBCompleteEvent() const; - virtual bool isIDBErrorEvent() const; - virtual bool isIDBSuccessEvent() const; + virtual bool isIDBVersionChangeEvent() const; #endif #if ENABLE(WEB_AUDIO) virtual bool isAudioProcessingEvent() const; diff --git a/Source/WebCore/dom/EventListener.h b/Source/WebCore/dom/EventListener.h index 96d0beb..348704f 100644 --- a/Source/WebCore/dom/EventListener.h +++ b/Source/WebCore/dom/EventListener.h @@ -54,7 +54,6 @@ namespace WebCore { #if USE(JSC) virtual void markJSFunction(JSC::MarkStack&) { } - virtual void invalidateJSFunction(JSC::JSObject*) { } #endif bool isAttribute() const { return virtualisAttribute(); } diff --git a/Source/WebCore/dom/EventNames.h b/Source/WebCore/dom/EventNames.h index 3b52838..12afbc4 100644 --- a/Source/WebCore/dom/EventNames.h +++ b/Source/WebCore/dom/EventNames.h @@ -36,6 +36,7 @@ namespace WebCore { macro(beforepaste) \ macro(beforeprocess) \ macro(beforeunload) \ + macro(blocked) \ macro(blur) \ macro(cached) \ macro(change) \ @@ -100,11 +101,13 @@ namespace WebCore { macro(search) \ macro(select) \ macro(selectstart) \ + macro(selectionchange) \ macro(storage) \ macro(submit) \ macro(textInput) \ macro(unload) \ macro(updateready) \ + macro(versionchange) \ macro(write) \ macro(writeend) \ macro(writestart) \ diff --git a/Source/WebCore/dom/EventQueue.cpp b/Source/WebCore/dom/EventQueue.cpp index 5a1abe8..8e544c1 100644 --- a/Source/WebCore/dom/EventQueue.cpp +++ b/Source/WebCore/dom/EventQueue.cpp @@ -47,6 +47,11 @@ private: EventQueue* m_eventQueue; }; +PassRefPtr<EventQueue> EventQueue::create(ScriptExecutionContext* context) +{ + return adoptRef(new EventQueue(context)); +} + EventQueue::EventQueue(ScriptExecutionContext* context) : m_pendingEventTimer(adoptPtr(new EventQueueTimer(this, context))) { @@ -59,7 +64,8 @@ EventQueue::~EventQueue() void EventQueue::enqueueEvent(PassRefPtr<Event> event) { ASSERT(event->target()); - m_queuedEvents.append(event); + bool wasAdded = m_queuedEvents.add(event).second; + ASSERT_UNUSED(wasAdded, wasAdded); // It should not have already been in the list. if (!m_pendingEventTimer->isActive()) m_pendingEventTimer->startOneShot(0); @@ -77,25 +83,43 @@ void EventQueue::enqueueScrollEvent(PassRefPtr<Node> target, ScrollEventTargetTy enqueueEvent(scrollEvent.release()); } +bool EventQueue::cancelEvent(Event* event) +{ + bool found = m_queuedEvents.contains(event); + m_queuedEvents.remove(event); + if (m_queuedEvents.isEmpty()) + m_pendingEventTimer->stop(); + return found; +} + void EventQueue::pendingEventTimerFired() { ASSERT(!m_pendingEventTimer->isActive()); + ASSERT(!m_queuedEvents.isEmpty()); - Vector<RefPtr<Event> > queuedEvents; - queuedEvents.swap(m_queuedEvents); - m_nodesWithQueuedScrollEvents.clear(); - for (size_t i = 0; i < queuedEvents.size(); i++) - dispatchEvent(queuedEvents[i].release()); + // Insert a marker for where we should stop. + ASSERT(!m_queuedEvents.contains(0)); + bool wasAdded = m_queuedEvents.add(0).second; + ASSERT_UNUSED(wasAdded, wasAdded); // It should not have already been in the list. + + RefPtr<EventQueue> protector(this); + + while (!m_queuedEvents.isEmpty()) { + ListHashSet<RefPtr<Event> >::iterator iter = m_queuedEvents.begin(); + RefPtr<Event> event = *iter; + m_queuedEvents.remove(iter); + if (!event) + break; + dispatchEvent(event.get()); + } } void EventQueue::dispatchEvent(PassRefPtr<Event> event) { EventTarget* eventTarget = event->target(); - if (eventTarget->toNode()) - eventTarget->dispatchEvent(event); - else if (eventTarget->toDOMWindow()) + if (eventTarget->toDOMWindow()) eventTarget->toDOMWindow()->dispatchEvent(event, 0); else eventTarget->dispatchEvent(event); diff --git a/Source/WebCore/dom/EventQueue.h b/Source/WebCore/dom/EventQueue.h index a589ed8..94b6eaf 100644 --- a/Source/WebCore/dom/EventQueue.h +++ b/Source/WebCore/dom/EventQueue.h @@ -28,11 +28,10 @@ #define EventQueue_h #include <wtf/HashSet.h> -#include <wtf/Noncopyable.h> +#include <wtf/ListHashSet.h> #include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include <wtf/RefCounted.h> #include <wtf/RefPtr.h> -#include <wtf/Vector.h> namespace WebCore { @@ -41,25 +40,19 @@ class EventQueueTimer; class Node; class ScriptExecutionContext; -class EventQueue { - WTF_MAKE_NONCOPYABLE(EventQueue); - - +class EventQueue : public RefCounted<EventQueue> { public: enum ScrollEventTargetType { ScrollEventDocumentTarget, ScrollEventElementTarget }; - static PassOwnPtr<EventQueue> create(ScriptExecutionContext* context) - { - return adoptPtr(new EventQueue(context)); - } - + static PassRefPtr<EventQueue> create(ScriptExecutionContext*); ~EventQueue(); void enqueueEvent(PassRefPtr<Event>); void enqueueScrollEvent(PassRefPtr<Node>, ScrollEventTargetType); + bool cancelEvent(Event*); private: explicit EventQueue(ScriptExecutionContext*); @@ -68,7 +61,7 @@ private: void dispatchEvent(PassRefPtr<Event>); OwnPtr<EventQueueTimer> m_pendingEventTimer; - Vector<RefPtr<Event> > m_queuedEvents; + ListHashSet<RefPtr<Event> > m_queuedEvents; HashSet<Node*> m_nodesWithQueuedScrollEvents; friend class EventQueueTimer; diff --git a/Source/WebCore/dom/EventTarget.cpp b/Source/WebCore/dom/EventTarget.cpp index ed5995c..7bd5cd6 100644 --- a/Source/WebCore/dom/EventTarget.cpp +++ b/Source/WebCore/dom/EventTarget.cpp @@ -193,6 +193,10 @@ IDBTransaction* EventTarget::toIDBTransaction() { return 0; } +IDBVersionChangeRequest* EventTarget::toIDBVersionChangeRequest() +{ + return 0; +} #endif bool EventTarget::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture) @@ -301,6 +305,10 @@ bool EventTarget::dispatchEvent(PassRefPtr<Event> event) return fireEventListeners(event.get()); } +void EventTarget::uncaughtExceptionInEventHandler() +{ +} + bool EventTarget::fireEventListeners(Event* event) { ASSERT(!eventDispatchForbidden()); diff --git a/Source/WebCore/dom/EventTarget.h b/Source/WebCore/dom/EventTarget.h index a03801b..31644b7 100644 --- a/Source/WebCore/dom/EventTarget.h +++ b/Source/WebCore/dom/EventTarget.h @@ -52,6 +52,7 @@ namespace WebCore { class IDBDatabase; class IDBRequest; class IDBTransaction; + class IDBVersionChangeRequest; class JavaScriptAudioNode; class MessagePort; class Node; @@ -142,6 +143,7 @@ namespace WebCore { virtual IDBDatabase* toIDBDatabase(); virtual IDBRequest* toIDBRequest(); virtual IDBTransaction* toIDBTransaction(); + virtual IDBVersionChangeRequest* toIDBVersionChangeRequest(); #endif virtual ScriptExecutionContext* scriptExecutionContext() const = 0; @@ -151,6 +153,7 @@ namespace WebCore { virtual void removeAllEventListeners(); virtual bool dispatchEvent(PassRefPtr<Event>); bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); // DOM API + virtual void uncaughtExceptionInEventHandler(); // Used for legacy "onEvent" attribute APIs. bool setAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener>); @@ -231,20 +234,6 @@ namespace WebCore { entry[i].listener->markJSFunction(markStack); } } - - inline void EventTarget::invalidateJSEventListeners(JSC::JSObject* wrapper) - { - EventTargetData* d = eventTargetData(); - if (!d) - return; - - EventListenerMap::iterator end = d->eventListenerMap.end(); - for (EventListenerMap::iterator it = d->eventListenerMap.begin(); it != end; ++it) { - EventListenerVector& entry = *it->second; - for (size_t i = 0; i < entry.size(); ++i) - entry[i].listener->invalidateJSFunction(wrapper); - } - } #endif inline bool EventTarget::isFiringEventListeners() diff --git a/Source/WebCore/dom/ExceptionCode.cpp b/Source/WebCore/dom/ExceptionCode.cpp index 1118a1a..9259be8 100644 --- a/Source/WebCore/dom/ExceptionCode.cpp +++ b/Source/WebCore/dom/ExceptionCode.cpp @@ -223,7 +223,8 @@ static const char* const idbDatabaseExceptionNames[] = { "TRANSIENT_ERR", "TIMEOUT_ERR", "DEADLOCK_ERR", - "READ_ONLY_ERR" + "READ_ONLY_ERR", + "ABORT_ERR" }; static const char* const idbDatabaseExceptionDescriptions[] = { @@ -238,7 +239,8 @@ static const char* const idbDatabaseExceptionDescriptions[] = { "TRANSIENT_ERR", // FIXME: This isn't even used. "TIMEOUT_ERR", // This can't be thrown. "DEADLOCK_ERR", // This can't be thrown. - "Write operations cannot be preformed on a read-only transaction." + "Write operations cannot be preformed on a read-only transaction.", + "The transaction was aborted, so the request cannot be fulfilled." }; #endif diff --git a/Source/WebCore/dom/InputElement.cpp b/Source/WebCore/dom/InputElement.cpp index db89c16..b60fd44 100644 --- a/Source/WebCore/dom/InputElement.cpp +++ b/Source/WebCore/dom/InputElement.cpp @@ -218,7 +218,7 @@ void InputElement::handleBeforeTextInsertedEvent(InputElementData& data, InputEl if (!isConformToInputMask(inputElement->data(), candidateString)) { textEvent->setText(""); return; - } + } #endif textEvent->setText(sanitizeUserInputValue(inputElement, textEvent->text(), appendableLength)); } diff --git a/Source/WebCore/dom/InputElement.h b/Source/WebCore/dom/InputElement.h index 02ac5cf..838adf5 100644 --- a/Source/WebCore/dom/InputElement.h +++ b/Source/WebCore/dom/InputElement.h @@ -61,6 +61,7 @@ public: virtual void setValueForUser(const String&) = 0; // The value which is drawn by a renderer. virtual String visibleValue() const = 0; + virtual String convertFromVisibleValue(const String&) const = 0; // Returns true if the specified string can be set as the value of InputElement. virtual bool isAcceptableValue(const String&) const = 0; @@ -116,6 +117,8 @@ public: const AtomicString& name() const; void setName(const AtomicString& value) { m_name = value; } + // The null String represents "use the default value," and the empty String + // represents the empty value. String value() const { return m_value; } void setValue(const String& value) { m_value = value; } diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp index 72592ae..c125d16 100644 --- a/Source/WebCore/dom/Node.cpp +++ b/Source/WebCore/dom/Node.cpp @@ -516,6 +516,11 @@ void Node::setTabIndexExplicitly(short i) ensureRareData()->setTabIndexExplicitly(i); } +void Node::clearTabIndexExplicitly() +{ + ensureRareData()->clearTabIndexExplicitly(); +} + String Node::nodeValue() const { return String(); @@ -1189,8 +1194,6 @@ void Node::checkReplaceChild(Node* newChild, Node* oldChild, ExceptionCode& ec) ec = HIERARCHY_REQUEST_ERR; return; } - - newChild->setDocumentRecursively(document()); } void Node::checkAddChild(Node *newChild, ExceptionCode& ec) @@ -1203,8 +1206,6 @@ void Node::checkAddChild(Node *newChild, ExceptionCode& ec) ec = HIERARCHY_REQUEST_ERR; return; } - - newChild->setDocumentRecursively(document()); } bool Node::isDescendantOf(const Node *other) const @@ -1242,6 +1243,7 @@ void Node::attach() ASSERT(!attached()); ASSERT(!renderer() || (renderer()->style() && renderer()->parent())); + // FIXME: This is O(N^2) for the innerHTML case, where all children are replaced at once (and not attached). // If this node got a renderer it may be the previousRenderer() of sibling text nodes and thus affect the // result of Text::rendererIsNeeded() for those nodes. if (renderer()) { @@ -1285,23 +1287,25 @@ void Node::detach() clearFlag(InDetachFlag); } -RenderObject * Node::previousRenderer() +RenderObject* Node::previousRenderer() { - for (Node *n = previousSibling(); n; n = n->previousSibling()) { + // FIXME: We should have the same O(N^2) avoidance as nextRenderer does + // however, when I tried adding it, several tests failed. + for (Node* n = previousSibling(); n; n = n->previousSibling()) { if (n->renderer()) return n->renderer(); } return 0; } -RenderObject * Node::nextRenderer() +RenderObject* Node::nextRenderer() { - // Avoid an O(n^2) problem with this function by not checking for nextRenderer() when the parent element hasn't even - // been attached yet. + // Avoid an O(n^2) problem with this function by not checking for + // nextRenderer() when the parent element hasn't attached yet. if (parentOrHostNode() && !parentOrHostNode()->attached()) return 0; - for (Node *n = nextSibling(); n; n = n->nextSibling()) { + for (Node* n = nextSibling(); n; n = n->nextSibling()) { if (n->renderer()) return n->renderer(); } @@ -1361,48 +1365,70 @@ Node *Node::nextLeafNode() const return 0; } +RenderObject* Node::createRendererAndStyle() +{ + ASSERT(!renderer()); + ASSERT(document()->shouldCreateRenderers()); + + ContainerNode* parent = parentOrHostNode(); + ASSERT(parent); + RenderObject* parentRenderer = parent->renderer(); + + // FIXME: Ignoring canHaveChildren() in a case of isShadowRoot() might be wrong. + // See https://bugs.webkit.org/show_bug.cgi?id=52423 + if (!parentRenderer || (!parentRenderer->canHaveChildren() && !isShadowRoot()) || !parent->childShouldCreateRenderer(this)) + return 0; + + RefPtr<RenderStyle> style = styleForRenderer(); + if (!rendererIsNeeded(style.get())) + return 0; + + RenderObject* newRenderer = createRenderer(document()->renderArena(), style.get()); + if (!newRenderer) + return 0; + + if (!parentRenderer->isChildAllowed(newRenderer, style.get())) { + newRenderer->destroy(); + return 0; + } + setRenderer(newRenderer); + newRenderer->setAnimatableStyle(style.release()); // setAnimatableStyle() can depend on renderer() already being set. + return newRenderer; +} + +#if ENABLE(FULLSCREEN_API) +static RenderFullScreen* wrapWithRenderFullScreen(RenderObject* object, Document* document) +{ + RenderFullScreen* fullscreenRenderer = new (document->renderArena()) RenderFullScreen(document); + fullscreenRenderer->setStyle(RenderFullScreen::createFullScreenStyle()); + // It's possible that we failed to create the new render and end up wrapping nothing. + // We'll end up displaying a black screen, but Jer says this is expected. + if (object) + fullscreenRenderer->addChild(object); + document->setFullScreenRenderer(fullscreenRenderer); + return fullscreenRenderer; +} +#endif + void Node::createRendererIfNeeded() { if (!document()->shouldCreateRenderers()) return; ASSERT(!renderer()); - - ContainerNode* parent = parentOrHostNode(); - ASSERT(parent); - - RenderObject* parentRenderer = parent->renderer(); - RenderObject* nextRenderer = this->nextRenderer(); - + + RenderObject* newRenderer = createRendererAndStyle(); + #if ENABLE(FULLSCREEN_API) - // If this node is a fullscreen node, create a new anonymous full screen - // renderer. - if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) { - RenderFullScreen* fullscreenRenderer = new (document()->renderArena()) RenderFullScreen(document()); - fullscreenRenderer->setStyle(RenderFullScreen::createFullScreenStyle()); - parentRenderer->addChild(fullscreenRenderer, 0); - parentRenderer = fullscreenRenderer; - nextRenderer = 0; - document()->setFullScreenRenderer(fullscreenRenderer); - } + if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) + newRenderer = wrapWithRenderFullScreen(newRenderer, document()); #endif - // FIXME: Ignoreing canHaveChildren() in a case of isShadowRoot() might be wrong. - // See https://bugs.webkit.org/show_bug.cgi?id=52423 - if (parentRenderer && (parentRenderer->canHaveChildren() || isShadowRoot()) && parent->childShouldCreateRenderer(this)) { - RefPtr<RenderStyle> style = styleForRenderer(); - if (rendererIsNeeded(style.get())) { - if (RenderObject* r = createRenderer(document()->renderArena(), style.get())) { - if (!parentRenderer->isChildAllowed(r, style.get())) - r->destroy(); - else { - setRenderer(r); - renderer()->setAnimatableStyle(style.release()); - parentRenderer->addChild(renderer(), nextRenderer); - } - } - } - } + if (!newRenderer) + return; + + // Note: Adding newRenderer instead of renderer(). renderer() may be a child of newRenderer. + parentOrHostNode()->renderer()->addChild(newRenderer, nextRenderer()); } PassRefPtr<RenderStyle> Node::styleForRenderer() diff --git a/Source/WebCore/dom/Node.h b/Source/WebCore/dom/Node.h index 546b223..7ef7e80 100644 --- a/Source/WebCore/dom/Node.h +++ b/Source/WebCore/dom/Node.h @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * This library is free software; you can redistribute it and/or @@ -134,6 +134,7 @@ public: // DOM methods & attributes for Node bool hasTagName(const QualifiedName&) const; + bool hasLocalName(const AtomicString&) const; virtual String nodeName() const = 0; virtual String nodeValue() const; virtual void setNodeValue(const String&, ExceptionCode&); @@ -217,6 +218,9 @@ public: Element* shadowHost() const; void setShadowHost(Element*); + bool selfOrAncestorHasDirAutoAttribute() const { return getFlag(SelfOrAncestorHasDirAutoFlag); } + void setSelfOrAncestorHasDirAutoAttribute(bool flag) { setFlag(flag, SelfOrAncestorHasDirAutoFlag); } + // Returns the enclosing event parent node (or self) that, when clicked, would trigger a navigation. Node* enclosingLinkEventParentOrSelf(); @@ -354,6 +358,9 @@ public: // removed from its previous document. void setDocument(Document*); + // Used by the basic DOM methods (e.g., appendChild()). + void setDocumentRecursively(Document*); + // Returns true if this node is associated with a document and is in its associated document's // node tree, false otherwise. bool inDocument() const @@ -615,6 +622,8 @@ private: StyleChangeMask = 1 << nodeStyleChangeShift | 1 << (nodeStyleChangeShift + 1), + SelfOrAncestorHasDirAutoFlag = 1 << 27, + #if ENABLE(SVG) DefaultNodeFlags = IsParsingChildrenFinishedFlag | IsStyleAttributeValidFlag | AreSVGAttributesValidFlag #else @@ -647,6 +656,7 @@ protected: virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const { } void setTabIndexExplicitly(short); + void clearTabIndexExplicitly(); bool hasRareData() const { return getFlag(HasRareDataFlag); } @@ -658,12 +668,13 @@ private: void markCachedNodeListsSlow(JSC::MarkStack&, JSC::JSGlobalData&); #endif - void setDocumentRecursively(Document*); void setStyleChange(StyleChangeType); // Used to share code between lazyAttach and setNeedsStyleRecalc. void markAncestorsWithChildNeedsStyleRecalc(); + RenderObject* createRendererAndStyle(); + virtual void refEventTarget(); virtual void derefEventTarget(); diff --git a/Source/WebCore/dom/NodeRareData.h b/Source/WebCore/dom/NodeRareData.h index 7350f80..badc4e1 100644 --- a/Source/WebCore/dom/NodeRareData.h +++ b/Source/WebCore/dom/NodeRareData.h @@ -104,6 +104,7 @@ public: short tabIndex() const { return m_tabIndex; } void setTabIndexExplicitly(short index) { m_tabIndex = index; m_tabIndexWasSetExplicitly = true; } bool tabIndexSetExplicitly() const { return m_tabIndexWasSetExplicitly; } + void clearTabIndexExplicitly() { m_tabIndex = 0; m_tabIndexWasSetExplicitly = false; } EventTargetData* eventTargetData() { return m_eventTargetData.get(); } EventTargetData* ensureEventTargetData() diff --git a/Source/WebCore/dom/Position.cpp b/Source/WebCore/dom/Position.cpp index cbad302..473610a 100644 --- a/Source/WebCore/dom/Position.cpp +++ b/Source/WebCore/dom/Position.cpp @@ -146,6 +146,13 @@ int Position::computeOffsetInContainerNode() const return 0; } +int Position::offsetForPositionAfterAnchor() const +{ + ASSERT(m_anchorType == PositionIsAfterAnchor); + ASSERT(!m_isLegacyEditingPosition); + return lastOffsetForEditing(m_anchorNode.get()); +} + // Neighbor-anchored positions are invalid DOM positions, so they need to be // fixed up before handing them off to the Range object. Position Position::parentAnchoredEquivalent() const @@ -230,7 +237,7 @@ PassRefPtr<CSSComputedStyleDeclaration> Position::computedStyle() const Position Position::previous(PositionMoveType moveType) const { - Node* n = node(); + Node* n = deprecatedNode(); if (!n) return *this; @@ -269,7 +276,7 @@ Position Position::next(PositionMoveType moveType) const { ASSERT(moveType != BackwardDeletion); - Node* n = node(); + Node* n = deprecatedNode(); if (!n) return *this; @@ -323,7 +330,7 @@ bool Position::atLastEditingPositionForNode() const { if (isNull()) return true; - return m_offset >= lastOffsetForEditing(node()); + return m_offset >= lastOffsetForEditing(deprecatedNode()); } // A position is considered at editing boundary if one of the following is true: @@ -336,15 +343,15 @@ bool Position::atLastEditingPositionForNode() const bool Position::atEditingBoundary() const { Position nextPosition = downstream(CanCrossEditingBoundary); - if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.node()->isContentEditable()) + if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.deprecatedNode()->isContentEditable()) return true; Position prevPosition = upstream(CanCrossEditingBoundary); - if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.node()->isContentEditable()) + if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->isContentEditable()) return true; - return nextPosition.isNotNull() && !nextPosition.node()->isContentEditable() - && prevPosition.isNotNull() && !prevPosition.node()->isContentEditable(); + return nextPosition.isNotNull() && !nextPosition.deprecatedNode()->isContentEditable() + && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->isContentEditable(); } Node* Position::parentEditingBoundary() const @@ -368,26 +375,26 @@ bool Position::atStartOfTree() const { if (isNull()) return true; - return !node()->parentNode() && m_offset <= 0; + return !deprecatedNode()->parentNode() && m_offset <= 0; } bool Position::atEndOfTree() const { if (isNull()) return true; - return !node()->parentNode() && m_offset >= lastOffsetForEditing(node()); + return !deprecatedNode()->parentNode() && m_offset >= lastOffsetForEditing(deprecatedNode()); } int Position::renderedOffset() const { - if (!node()->isTextNode()) + if (!deprecatedNode()->isTextNode()) return m_offset; - if (!node()->renderer()) + if (!deprecatedNode()->renderer()) return m_offset; int result = 0; - RenderText *textRenderer = toRenderText(node()->renderer()); + RenderText* textRenderer = toRenderText(deprecatedNode()->renderer()); for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { int start = box->start(); int end = box->start() + box->len(); @@ -408,7 +415,7 @@ Position Position::previousCharacterPosition(EAffinity affinity) const if (isNull()) return Position(); - Node *fromRootEditableElement = node()->rootEditableElement(); + Node* fromRootEditableElement = deprecatedNode()->rootEditableElement(); bool atStartOfLine = isStartOfLine(VisiblePosition(*this, affinity)); bool rendered = isCandidate(); @@ -417,7 +424,7 @@ Position Position::previousCharacterPosition(EAffinity affinity) const while (!currentPos.atStartOfTree()) { currentPos = currentPos.previous(); - if (currentPos.node()->rootEditableElement() != fromRootEditableElement) + if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement) return *this; if (atStartOfLine || !rendered) { @@ -436,7 +443,7 @@ Position Position::nextCharacterPosition(EAffinity affinity) const if (isNull()) return Position(); - Node *fromRootEditableElement = node()->rootEditableElement(); + Node* fromRootEditableElement = deprecatedNode()->rootEditableElement(); bool atEndOfLine = isEndOfLine(VisiblePosition(*this, affinity)); bool rendered = isCandidate(); @@ -445,7 +452,7 @@ Position Position::nextCharacterPosition(EAffinity affinity) const while (!currentPos.atEndOfTree()) { currentPos = currentPos.next(); - if (currentPos.node()->rootEditableElement() != fromRootEditableElement) + if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement) return *this; if (atEndOfLine || !rendered) { @@ -507,13 +514,14 @@ static bool isStreamer(const PositionIterator& pos) // in boundary, where endsOfNodeAreVisuallyDistinctPositions(boundary) is true. Position Position::upstream(EditingBoundaryCrossingRule rule) const { - Node* startNode = node(); + Node* startNode = deprecatedNode(); if (!startNode) return Position(); // iterate backward from there, looking for a qualified position Node* boundary = enclosingVisualBoundary(startNode); - PositionIterator lastVisible = *this; + // FIXME: PositionIterator should respect Before and After positions. + PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? Position(m_anchorNode, caretMaxOffset(m_anchorNode.get())) : *this; PositionIterator currentPos = lastVisible; bool startEditable = startNode->isContentEditable(); Node* lastNode = startNode; @@ -628,13 +636,14 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const // in boundary after the last candidate, where endsOfNodeAreVisuallyDistinctPositions(boundary). Position Position::downstream(EditingBoundaryCrossingRule rule) const { - Node* startNode = node(); + Node* startNode = deprecatedNode(); if (!startNode) return Position(); // iterate forward from there, looking for a qualified position Node* boundary = enclosingVisualBoundary(startNode); - PositionIterator lastVisible = *this; + // FIXME: PositionIterator should respect Before and After positions. + PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? Position(m_anchorNode, caretMaxOffset(m_anchorNode.get())) : *this; PositionIterator currentPos = lastVisible; bool startEditable = startNode->isContentEditable(); Node* lastNode = startNode; @@ -764,7 +773,7 @@ bool Position::isCandidate() const if (isNull()) return false; - RenderObject *renderer = node()->renderer(); + RenderObject* renderer = deprecatedNode()->renderer(); if (!renderer) return false; @@ -772,13 +781,13 @@ bool Position::isCandidate() const return false; if (renderer->isBR()) - return !m_offset && !nodeIsUserSelectNone(node()->parentNode()); + return !m_offset && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); if (renderer->isText()) - return !nodeIsUserSelectNone(node()) && inRenderedText(); + return !nodeIsUserSelectNone(deprecatedNode()) && inRenderedText(); - if (isTableElement(node()) || editingIgnoresContent(node())) - return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(node()->parentNode()); + if (isTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode())) + return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); if (m_anchorNode->hasTagName(htmlTag)) return false; @@ -786,21 +795,21 @@ bool Position::isCandidate() const if (renderer->isBlockFlow()) { if (toRenderBlock(renderer)->height() || m_anchorNode->hasTagName(bodyTag)) { if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer)) - return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(node()); - return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(node()) && atEditingBoundary(); + return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(deprecatedNode()); + return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); } } else - return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(node()) && atEditingBoundary(); + return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); return false; } bool Position::inRenderedText() const { - if (isNull() || !node()->isTextNode()) + if (isNull() || !deprecatedNode()->isTextNode()) return false; - RenderObject *renderer = node()->renderer(); + RenderObject* renderer = deprecatedNode()->renderer(); if (!renderer) return false; @@ -833,10 +842,10 @@ static unsigned caretMaxRenderedOffset(const Node* n) bool Position::isRenderedCharacter() const { - if (isNull() || !node()->isTextNode()) + if (isNull() || !deprecatedNode()->isTextNode()) return false; - RenderObject* renderer = node()->renderer(); + RenderObject* renderer = deprecatedNode()->renderer(); if (!renderer) return false; @@ -860,11 +869,11 @@ bool Position::rendersInDifferentPosition(const Position &pos) const if (isNull() || pos.isNull()) return false; - RenderObject *renderer = node()->renderer(); + RenderObject* renderer = deprecatedNode()->renderer(); if (!renderer) return false; - RenderObject *posRenderer = pos.node()->renderer(); + RenderObject* posRenderer = pos.deprecatedNode()->renderer(); if (!posRenderer) return false; @@ -872,32 +881,32 @@ bool Position::rendersInDifferentPosition(const Position &pos) const posRenderer->style()->visibility() != VISIBLE) return false; - if (node() == pos.node()) { - if (node()->hasTagName(brTag)) + if (deprecatedNode() == pos.deprecatedNode()) { + if (deprecatedNode()->hasTagName(brTag)) return false; if (m_offset == pos.deprecatedEditingOffset()) return false; - if (!node()->isTextNode() && !pos.node()->isTextNode()) { + if (!deprecatedNode()->isTextNode() && !pos.deprecatedNode()->isTextNode()) { if (m_offset != pos.deprecatedEditingOffset()) return true; } } - if (node()->hasTagName(brTag) && pos.isCandidate()) + if (deprecatedNode()->hasTagName(brTag) && pos.isCandidate()) return true; - if (pos.node()->hasTagName(brTag) && isCandidate()) + if (pos.deprecatedNode()->hasTagName(brTag) && isCandidate()) return true; - if (node()->enclosingBlockFlowElement() != pos.node()->enclosingBlockFlowElement()) + if (deprecatedNode()->enclosingBlockFlowElement() != pos.deprecatedNode()->enclosingBlockFlowElement()) return true; - if (node()->isTextNode() && !inRenderedText()) + if (deprecatedNode()->isTextNode() && !inRenderedText()) return false; - if (pos.node()->isTextNode() && !pos.inRenderedText()) + if (pos.deprecatedNode()->isTextNode() && !pos.inRenderedText()) return false; int thisRenderedOffset = renderedOffset(); @@ -916,8 +925,8 @@ bool Position::rendersInDifferentPosition(const Position &pos) const LOG(Editing, "thisRenderedOffset: %d\n", thisRenderedOffset); LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, b2); LOG(Editing, "posRenderedOffset: %d\n", posRenderedOffset); - LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(node()), caretMaxRenderedOffset(node())); - LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(pos.node()), caretMaxRenderedOffset(pos.node())); + LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(deprecatedNode()), caretMaxRenderedOffset(deprecatedNode())); + LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(pos.deprecatedNode()), caretMaxRenderedOffset(pos.deprecatedNode())); LOG(Editing, "----------------------------------------------------------------------\n"); if (!b1 || !b2) { @@ -928,13 +937,13 @@ bool Position::rendersInDifferentPosition(const Position &pos) const return true; } - if (nextRenderedEditable(node()) == pos.node() && - thisRenderedOffset == (int)caretMaxRenderedOffset(node()) && posRenderedOffset == 0) { + if (nextRenderedEditable(deprecatedNode()) == pos.deprecatedNode() + && thisRenderedOffset == (int)caretMaxRenderedOffset(deprecatedNode()) && !posRenderedOffset) { return false; } - if (previousRenderedEditable(node()) == pos.node() && - thisRenderedOffset == 0 && posRenderedOffset == (int)caretMaxRenderedOffset(pos.node())) { + if (previousRenderedEditable(deprecatedNode()) == pos.deprecatedNode() + && !thisRenderedOffset && posRenderedOffset == (int)caretMaxRenderedOffset(pos.deprecatedNode())) { return false; } @@ -948,12 +957,12 @@ Position Position::leadingWhitespacePosition(EAffinity affinity, bool considerNo if (isNull()) return Position(); - if (upstream().node()->hasTagName(brTag)) + if (upstream().deprecatedNode()->hasTagName(brTag)) return Position(); Position prev = previousCharacterPosition(affinity); - if (prev != *this && prev.node()->inSameContainingBlockFlowElement(node()) && prev.node()->isTextNode()) { - String string = static_cast<Text *>(prev.node())->data(); + if (prev != *this && prev.deprecatedNode()->inSameContainingBlockFlowElement(deprecatedNode()) && prev.deprecatedNode()->isTextNode()) { + String string = static_cast<Text *>(prev.deprecatedNode())->data(); UChar c = string[prev.deprecatedEditingOffset()]; if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c)) if (isEditablePosition(prev)) @@ -1045,11 +1054,11 @@ static Position upstreamIgnoringEditingBoundaries(Position position) void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const { caretOffset = m_offset; - RenderObject* renderer = node()->renderer(); + RenderObject* renderer = deprecatedNode()->renderer(); if (!renderer->isText()) { inlineBox = 0; - if (canHaveChildrenForEditing(node()) && renderer->isBlockFlow() && hasRenderedNonAnonymousDescendantsWithHeight(renderer)) { + if (canHaveChildrenForEditing(deprecatedNode()) && renderer->isBlockFlow() && hasRenderedNonAnonymousDescendantsWithHeight(renderer)) { // Try a visually equivalent position with possibly opposite editability. This helps in case |this| is in // an editable block but surrounded by non-editable positions. It acts to negate the logic at the beginning // of RenderObject::createVisiblePosition(). @@ -1211,7 +1220,7 @@ void Position::debugPosition(const char* msg) const if (isNull()) fprintf(stderr, "Position [%s]: null\n", msg); else - fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, node()->nodeName().utf8().data(), node(), m_offset); + fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, deprecatedNode()->nodeName().utf8().data(), deprecatedNode(), m_offset); } #ifndef NDEBUG @@ -1227,18 +1236,36 @@ void Position::formatForDebugger(char* buffer, unsigned length) const result += "offset "; result += String::number(m_offset); result += " of "; - node()->formatForDebugger(s, sizeof(s)); + deprecatedNode()->formatForDebugger(s, sizeof(s)); result += s; } strncpy(buffer, result.utf8().data(), length - 1); } +void Position::showAnchorTypeAndOffset() const +{ + if (m_isLegacyEditingPosition) + fputs("legacy, ", stderr); + switch (anchorType()) { + case PositionIsOffsetInAnchor: + fputs("offset", stderr); + break; + case PositionIsAfterAnchor: + fputs("after", stderr); + break; + case PositionIsBeforeAnchor: + fputs("before", stderr); + break; + } + fprintf(stderr, ", offset:%d\n", m_offset); +} + void Position::showTreeForThis() const { - if (node()) { - node()->showTreeForThis(); - fprintf(stderr, "offset: %d\n", m_offset); + if (anchorNode()) { + anchorNode()->showTreeForThis(); + showAnchorTypeAndOffset(); } } diff --git a/Source/WebCore/dom/Position.h b/Source/WebCore/dom/Position.h index 8b41736..4e1eff4 100644 --- a/Source/WebCore/dom/Position.h +++ b/Source/WebCore/dom/Position.h @@ -92,8 +92,9 @@ public: // New code should not use this function. int deprecatedEditingOffset() const { - // This should probably ASSERT(m_isLegacyEditingPosition); - return m_offset; + if (m_isLegacyEditingPosition || m_anchorType != PositionIsAfterAnchor) + return m_offset; + return offsetForPositionAfterAnchor(); } // These are convenience methods which are smart about whether the position is neighbor anchored or parent anchored @@ -105,7 +106,9 @@ public: // FIXME: Callers should be moved off of node(), node() is not always the container for this position. // For nodes which editingIgnoresContent(node()) returns true, positions like [ignoredNode, 0] // will be treated as before ignoredNode (thus node() is really after the position, not containing it). - Node* node() const { return m_anchorNode.get(); } + Node* deprecatedNode() const { return m_anchorNode.get(); } + + Document* document() const { return m_anchorNode ? m_anchorNode->document() : 0; } // These should only be used for PositionIsOffsetInAnchor positions, unless // the position is a legacy editing position. @@ -166,10 +169,13 @@ public: #ifndef NDEBUG void formatForDebugger(char* buffer, unsigned length) const; + void showAnchorTypeAndOffset() const; void showTreeForThis() const; #endif private: + int offsetForPositionAfterAnchor() const; + int renderedOffset() const; Position previousCharacterPosition(EAffinity) const; diff --git a/Source/WebCore/dom/ProcessingInstruction.cpp b/Source/WebCore/dom/ProcessingInstruction.cpp index ed329bc..ae0e40d 100644 --- a/Source/WebCore/dom/ProcessingInstruction.cpp +++ b/Source/WebCore/dom/ProcessingInstruction.cpp @@ -43,6 +43,7 @@ inline ProcessingInstruction::ProcessingInstruction(Document* document, const St , m_loading(false) , m_alternate(false) , m_createdByParser(false) + , m_isCSS(false) #if ENABLE(XSLT) , m_isXSL(false) #endif @@ -120,13 +121,13 @@ void ProcessingInstruction::checkStyleSheet() if (i != attrs.end()) type = i->second; - bool isCSS = type.isEmpty() || type == "text/css"; + m_isCSS = type.isEmpty() || type == "text/css"; #if ENABLE(XSLT) m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" || type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml"); - if (!isCSS && !m_isXSL) + if (!m_isCSS && !m_isXSL) #else - if (!isCSS) + if (!m_isCSS) #endif return; @@ -208,9 +209,7 @@ void ProcessingInstruction::setCSSStyleSheet(const String& href, const KURL& bas return; } -#if ENABLE(XSLT) - ASSERT(!m_isXSL); -#endif + ASSERT(m_isCSS); RefPtr<CSSStyleSheet> newSheet = CSSStyleSheet::create(this, href, baseURL, charset); m_sheet = newSheet; // We don't need the cross-origin security check here because we are diff --git a/Source/WebCore/dom/ProcessingInstruction.h b/Source/WebCore/dom/ProcessingInstruction.h index 31f680d..8619070 100644 --- a/Source/WebCore/dom/ProcessingInstruction.h +++ b/Source/WebCore/dom/ProcessingInstruction.h @@ -48,6 +48,7 @@ public: StyleSheet* sheet() const { return m_sheet.get(); } void setCSSStyleSheet(PassRefPtr<CSSStyleSheet>); + bool isCSS() const { return m_isCSS; } #if ENABLE(XSLT) bool isXSL() const { return m_isXSL; } #endif @@ -98,6 +99,7 @@ private: bool m_loading; bool m_alternate; bool m_createdByParser; + bool m_isCSS; #if ENABLE(XSLT) bool m_isXSL; #endif diff --git a/Source/WebCore/dom/Range.cpp b/Source/WebCore/dom/Range.cpp index e224843..a0370fa 100644 --- a/Source/WebCore/dom/Range.cpp +++ b/Source/WebCore/dom/Range.cpp @@ -607,7 +607,48 @@ static inline Node* highestAncestorUnderCommonRoot(Node* node, Node* commonRoot) return node; } -static inline unsigned lengthOfContentsInNode() { return numeric_limits<unsigned>::max(); } +static inline Node* childOfCommonRootBeforeOffset(Node* container, unsigned offset, Node* commonRoot) +{ + ASSERT(container); + ASSERT(commonRoot); + ASSERT(commonRoot->contains(container)); + + if (container == commonRoot) { + container = container->firstChild(); + for (unsigned i = 0; container && i < offset; i++) + container = container->nextSibling(); + } else { + while (container->parentNode() != commonRoot) + container = container->parentNode(); + } + + return container; +} + +static inline unsigned lengthOfContentsInNode(Node* node) +{ + // This switch statement must be consistent with that of Range::processContentsBetweenOffsets. + switch (node->nodeType()) { + case Node::TEXT_NODE: + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + return static_cast<CharacterData*>(node)->length(); + case Node::PROCESSING_INSTRUCTION_NODE: + return static_cast<ProcessingInstruction*>(node)->data().length(); + case Node::ELEMENT_NODE: + case Node::ATTRIBUTE_NODE: + case Node::ENTITY_REFERENCE_NODE: + case Node::ENTITY_NODE: + case Node::DOCUMENT_NODE: + case Node::DOCUMENT_TYPE_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::NOTATION_NODE: + case Node::XPATH_NAMESPACE_NODE: + return node->childNodeCount(); + } + ASSERT_NOT_REACHED(); + return 0; +} PassRefPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionCode& ec) { @@ -656,85 +697,21 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception RefPtr<Node> leftContents; if (m_start.container() != commonRoot) { - leftContents = processContentsBetweenOffsets(action, 0, m_start.container(), m_start.offset(), lengthOfContentsInNode(), ec); - - NodeVector ancestorNodes; - for (ContainerNode* n = m_start.container()->parentNode(); n && n != commonRoot; n = n->parentNode()) - ancestorNodes.append(n); - RefPtr<Node> n = m_start.container()->nextSibling(); - for (NodeVector::const_iterator it = ancestorNodes.begin(); it != ancestorNodes.end(); it++) { - Node* leftParent = it->get(); - if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { - RefPtr<Node> leftContentsParent = leftParent->cloneNode(false); - if (leftContentsParent) { // Might have been removed already during mutation event. - leftContentsParent->appendChild(leftContents, ec); - leftContents = leftContentsParent; - } - } - - RefPtr<Node> next; - for (; n; n = next) { - next = n->nextSibling(); - if (action == EXTRACT_CONTENTS) - leftContents->appendChild(n.get(), ec); // will remove n from leftParent - else if (action == CLONE_CONTENTS) - leftContents->appendChild(n->cloneNode(true), ec); - else - leftParent->removeChild(n.get(), ec); - } - n = leftParent->nextSibling(); - } + leftContents = processContentsBetweenOffsets(action, 0, m_start.container(), m_start.offset(), lengthOfContentsInNode(m_start.container()), ec); + leftContents = processAncestorsAndTheirSiblings(action, m_start.container(), ProcessContentsForward, leftContents, commonRoot, ec); } RefPtr<Node> rightContents; if (m_end.container() != commonRoot) { rightContents = processContentsBetweenOffsets(action, 0, m_end.container(), 0, m_end.offset(), ec); - - ContainerNode* rightParent = m_end.container()->parentNode(); - Node* n = m_end.container()->previousSibling(); - for (; rightParent != commonRoot; rightParent = rightParent->parentNode()) { - if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { - RefPtr<Node> rightContentsParent = rightParent->cloneNode(false); - rightContentsParent->appendChild(rightContents, ec); - rightContents = rightContentsParent; - } - Node* prev; - for (; n; n = prev) { - prev = n->previousSibling(); - if (action == EXTRACT_CONTENTS) - rightContents->insertBefore(n, rightContents->firstChild(), ec); // will remove n from its parent - else if (action == CLONE_CONTENTS) - rightContents->insertBefore(n->cloneNode(true), rightContents->firstChild(), ec); - else - rightParent->removeChild(n, ec); - } - n = rightParent->previousSibling(); - } + rightContents = processAncestorsAndTheirSiblings(action, m_end.container(), ProcessContentsBackward, rightContents, commonRoot, ec); } // delete all children of commonRoot between the start and end container - - Node* processStart; // child of commonRoot - if (m_start.container() == commonRoot) { - processStart = m_start.container()->firstChild(); - for (int i = 0; i < m_start.offset(); i++) - processStart = processStart->nextSibling(); - } else { - processStart = m_start.container(); - while (processStart->parentNode() != commonRoot) - processStart = processStart->parentNode(); + Node* processStart = childOfCommonRootBeforeOffset(m_start.container(), m_start.offset(), commonRoot); + if (m_start.container() != commonRoot) // processStart contains nodes before m_start. processStart = processStart->nextSibling(); - } - Node* processEnd; // child of commonRoot - if (m_end.container() == commonRoot) { - processEnd = m_end.container()->firstChild(); - for (int i = 0; i < m_end.offset(); i++) - processEnd = processEnd->nextSibling(); - } else { - processEnd = m_end.container(); - while (processEnd->parentNode() != commonRoot) - processEnd = processEnd->parentNode(); - } + Node* processEnd = childOfCommonRootBeforeOffset(m_end.container(), m_end.offset(), commonRoot); // Collapse the range, making sure that the result is not within a node that was partially selected. if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { @@ -757,15 +734,7 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception NodeVector nodes; for (Node* n = processStart; n && n != processEnd; n = n->nextSibling()) nodes.append(n); - for (NodeVector::const_iterator it = nodes.begin(); it != nodes.end(); it++) { - Node* n = it->get(); - if (action == EXTRACT_CONTENTS) - fragment->appendChild(n, ec); // will remove from commonRoot - else if (action == CLONE_CONTENTS) - fragment->appendChild(n->cloneNode(true), ec); - else - commonRoot->removeChild(n, ec); - } + processNodes(action, nodes, commonRoot, fragment, ec); } if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents) @@ -774,26 +743,30 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception return fragment.release(); } +static inline void deleteCharacterData(PassRefPtr<CharacterData> data, unsigned startOffset, unsigned endOffset, ExceptionCode& ec) +{ + if (data->length() - endOffset) + data->deleteData(endOffset, data->length() - endOffset, ec); + if (startOffset) + data->deleteData(0, startOffset, ec); +} + PassRefPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRefPtr<DocumentFragment> fragment, Node* container, unsigned startOffset, unsigned endOffset, ExceptionCode& ec) { ASSERT(container); ASSERT(startOffset <= endOffset); - - RefPtr<Node> result; + + // This switch statement must be consistent with that of lengthOfContentsInNode. + RefPtr<Node> result; switch (container->nodeType()) { case Node::TEXT_NODE: case Node::CDATA_SECTION_NODE: case Node::COMMENT_NODE: - ASSERT(endOffset <= static_cast<CharacterData*>(container)->length() || endOffset == lengthOfContentsInNode()); - if (endOffset == lengthOfContentsInNode()) - endOffset = static_cast<CharacterData*>(container)->length(); + ASSERT(endOffset <= static_cast<CharacterData*>(container)->length()); if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(container->cloneNode(true)); - if (c->length() - endOffset) - c->deleteData(endOffset, c->length() - endOffset, ec); - if (startOffset) - c->deleteData(0, startOffset, ec); + deleteCharacterData(c, startOffset, endOffset, ec); if (fragment) { result = fragment; result->appendChild(c.release(), ec); @@ -804,9 +777,7 @@ PassRefPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRef static_cast<CharacterData*>(container)->deleteData(startOffset, endOffset - startOffset, ec); break; case Node::PROCESSING_INSTRUCTION_NODE: - ASSERT(endOffset <= static_cast<ProcessingInstruction*>(container)->data().length() || endOffset == lengthOfContentsInNode()); - if (endOffset == lengthOfContentsInNode()) - endOffset = static_cast<ProcessingInstruction*>(container)->data().length(); + ASSERT(endOffset <= static_cast<ProcessingInstruction*>(container)->data().length()); if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(container->cloneNode(true)); c->setData(c->data().substring(startOffset, endOffset - startOffset), ec); @@ -847,23 +818,76 @@ PassRefPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRef for (unsigned i = startOffset; n && i < endOffset; i++, n = n->nextSibling()) nodes.append(n); - for (unsigned i = 0; i < nodes.size(); i++) { + processNodes(action, nodes, container, result, ec); + break; + } + + return result; +} + +void Range::processNodes(ActionType action, Vector<RefPtr<Node> >& nodes, PassRefPtr<Node> oldContainer, PassRefPtr<Node> newContainer, ExceptionCode& ec) +{ + for (unsigned i = 0; i < nodes.size(); i++) { + switch (action) { + case DELETE_CONTENTS: + oldContainer->removeChild(nodes[i].get(), ec); + break; + case EXTRACT_CONTENTS: + newContainer->appendChild(nodes[i].release(), ec); // will remove n from its parent + break; + case CLONE_CONTENTS: + newContainer->appendChild(nodes[i]->cloneNode(true), ec); + break; + } + } +} + +PassRefPtr<Node> Range::processAncestorsAndTheirSiblings(ActionType action, Node* container, ContentsProcessDirection direction, PassRefPtr<Node> passedClonedContainer, Node* commonRoot, ExceptionCode& ec) +{ + RefPtr<Node> clonedContainer = passedClonedContainer; + Vector<RefPtr<Node> > ancestors; + for (ContainerNode* n = container->parentNode(); n && n != commonRoot; n = n->parentNode()) + ancestors.append(n); + + RefPtr<Node> firstChildInAncestorToProcess = direction == ProcessContentsForward ? container->nextSibling() : container->previousSibling(); + for (Vector<RefPtr<Node> >::const_iterator it = ancestors.begin(); it != ancestors.end(); it++) { + RefPtr<Node> ancestor = *it; + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + if (RefPtr<Node> clonedAncestor = ancestor->cloneNode(false)) { // Might have been removed already during mutation event. + clonedAncestor->appendChild(clonedContainer, ec); + clonedContainer = clonedAncestor; + } + } + + // Copy siblings of an ancestor of start/end containers + // FIXME: This assertion may fail if DOM is modified during mutation event + // FIXME: Share code with Range::processNodes + ASSERT(!firstChildInAncestorToProcess || firstChildInAncestorToProcess->parentNode() == ancestor); + RefPtr<Node> next; + for (Node* child = firstChildInAncestorToProcess.get(); child; child = next.get()) { + next = direction == ProcessContentsForward ? child->nextSibling() : child->previousSibling(); switch (action) { case DELETE_CONTENTS: - container->removeChild(nodes[i].get(), ec); + ancestor->removeChild(child, ec); break; - case EXTRACT_CONTENTS: - result->appendChild(nodes[i].release(), ec); // will remove n from its parent + case EXTRACT_CONTENTS: // will remove child from ancestor + if (direction == ProcessContentsForward) + clonedContainer->appendChild(child, ec); + else + clonedContainer->insertBefore(child, clonedContainer->firstChild(), ec); break; case CLONE_CONTENTS: - result->appendChild(nodes[i]->cloneNode(true), ec); + if (direction == ProcessContentsForward) + clonedContainer->appendChild(child->cloneNode(true), ec); + else + clonedContainer->insertBefore(child->cloneNode(true), clonedContainer->firstChild(), ec); break; } } - break; + firstChildInAncestorToProcess = direction == ProcessContentsForward ? ancestor->nextSibling() : ancestor->previousSibling(); } - return result; + return clonedContainer; } PassRefPtr<DocumentFragment> Range::extractContents(ExceptionCode& ec) @@ -1544,7 +1568,7 @@ Position Range::editingStartPosition() const // It is important to skip certain irrelevant content at the start of the selection, so we do not wind up // with a spurious "mixed" style. - VisiblePosition visiblePosition(m_start.container(), m_start.offset(), VP_DEFAULT_AFFINITY); + VisiblePosition visiblePosition = Position(m_start.container(), m_start.offset(), Position::PositionIsOffsetInAnchor); if (visiblePosition.isNull()) return Position(); @@ -1879,22 +1903,8 @@ PassRefPtr<ClientRectList> Range::getClientRects() const PassRefPtr<ClientRect> Range::getBoundingClientRect() const { - if (!m_start.container()) - return 0; - - m_ownerDocument->updateLayoutIgnorePendingStylesheets(); - - Vector<FloatQuad> quads; - getBorderAndTextQuads(quads); - - if (quads.isEmpty()) - return ClientRect::create(); - - FloatRect result; - for (size_t i = 0; i < quads.size(); ++i) - result.unite(quads[i].boundingBox()); - - return ClientRect::create(result); + FloatRect rect = boundingRect(); + return rect.isEmpty() ? 0 : ClientRect::create(rect); } static void adjustFloatQuadsForScrollAndAbsoluteZoom(Vector<FloatQuad>& quads, Document* document, RenderObject* renderer) @@ -1949,6 +1959,26 @@ void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const } } + +FloatRect Range::boundingRect() const +{ + if (!m_start.container()) + return FloatRect(); + + m_ownerDocument->updateLayoutIgnorePendingStylesheets(); + + Vector<FloatQuad> quads; + getBorderAndTextQuads(quads); + if (quads.isEmpty()) + return FloatRect(); + + FloatRect result; + for (size_t i = 0; i < quads.size(); ++i) + result.unite(quads[i].boundingBox()); + + return result; +} + } // namespace WebCore #ifndef NDEBUG @@ -1956,10 +1986,8 @@ void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const void showTree(const WebCore::Range* range) { if (range && range->boundaryPointsValid()) { - WebCore::Position start = range->startPosition(); - WebCore::Position end = range->endPosition(); - start.node()->showTreeAndMark(start.node(), "S", end.node(), "E"); - fprintf(stderr, "start offset: %d, end offset: %d\n", start.deprecatedEditingOffset(), end.deprecatedEditingOffset()); + range->startContainer()->showTreeAndMark(range->startContainer(), "S", range->endContainer(), "E"); + fprintf(stderr, "start offset: %d, end offset: %d\n", range->startOffset(), range->endOffset()); } } diff --git a/Source/WebCore/dom/Range.h b/Source/WebCore/dom/Range.h index 86c1354..5637b77 100644 --- a/Source/WebCore/dom/Range.h +++ b/Source/WebCore/dom/Range.h @@ -29,6 +29,7 @@ #include "RangeBoundaryPoint.h" #include <wtf/Forward.h> #include <wtf/RefCounted.h> +#include <wtf/Vector.h> namespace WebCore { @@ -110,6 +111,7 @@ public: // Transform-friendly void textQuads(Vector<FloatQuad>&, bool useSelectionHeight = false); void getBorderAndTextQuads(Vector<FloatQuad>&) const; + FloatRect boundingRect() const; void nodeChildrenChanged(ContainerNode*); void nodeChildrenWillBeRemoved(ContainerNode*); @@ -147,7 +149,10 @@ private: enum ActionType { DELETE_CONTENTS, EXTRACT_CONTENTS, CLONE_CONTENTS }; PassRefPtr<DocumentFragment> processContents(ActionType, ExceptionCode&); - PassRefPtr<Node> processContentsBetweenOffsets(ActionType, PassRefPtr<DocumentFragment>, Node*, unsigned startOffset, unsigned endOffset, ExceptionCode&); + static PassRefPtr<Node> processContentsBetweenOffsets(ActionType, PassRefPtr<DocumentFragment>, Node*, unsigned startOffset, unsigned endOffset, ExceptionCode&); + static void processNodes(ActionType, Vector<RefPtr<Node> >&, PassRefPtr<Node> oldContainer, PassRefPtr<Node> newContainer, ExceptionCode&); + enum ContentsProcessDirection { ProcessContentsForward, ProcessContentsBackward }; + static PassRefPtr<Node> processAncestorsAndTheirSiblings(ActionType, Node* container, ContentsProcessDirection, PassRefPtr<Node> clonedContainer, Node* commonRoot, ExceptionCode&); RefPtr<Document> m_ownerDocument; RangeBoundaryPoint m_start; diff --git a/Source/WebCore/dom/ScriptElement.cpp b/Source/WebCore/dom/ScriptElement.cpp index 1939a08..3bba9a0 100644 --- a/Source/WebCore/dom/ScriptElement.cpp +++ b/Source/WebCore/dom/ScriptElement.cpp @@ -51,13 +51,16 @@ namespace WebCore { -ScriptElement::ScriptElement(Element* element, bool wasInsertedByParser, bool wasAlreadyStarted) +ScriptElement::ScriptElement(Element* element, bool parserInserted, bool alreadyStarted) : m_element(element) , m_cachedScript(0) - , m_wasInsertedByParser(wasInsertedByParser) + , m_parserInserted(parserInserted) , m_isExternalScript(false) - , m_wasAlreadyStarted(wasAlreadyStarted) + , m_alreadyStarted(alreadyStarted) , m_haveFiredLoad(false) + , m_willBeParserExecuted(false) + , m_readyToBeParserExecuted(false) + , m_willExecuteWhenDocumentFinishedParsing(false) { ASSERT(m_element); } @@ -67,22 +70,10 @@ ScriptElement::~ScriptElement() stopLoadRequest(); } -void ScriptElement::insertedIntoDocument(const String& sourceUrl) +void ScriptElement::insertedIntoDocument() { - if (wasInsertedByParser() && !isAsynchronous()) - return; - - // http://www.whatwg.org/specs/web-apps/current-work/#script - - if (!sourceUrl.isEmpty()) { - requestScript(sourceUrl); - return; - } - - // If there's an empty script node, we shouldn't evaluate the script - // because if a script is inserted afterwards (by setting text or innerText) - // it should be evaluated, and evaluateScript only evaluates a script once. - evaluateScript(ScriptSourceCode(scriptContent(), element()->document()->url())); // FIXME: Provide a real starting line number here. + if (!m_parserInserted) + prepareScript(); // FIXME: Provide a real starting line number here. } void ScriptElement::removedFromDocument() @@ -93,22 +84,8 @@ void ScriptElement::removedFromDocument() void ScriptElement::childrenChanged() { - if (wasInsertedByParser()) - return; - - // If a node is inserted as a child of the script element - // and the script element has been inserted in the document - // we evaluate the script. - if (m_element->inDocument() && m_element->firstChild()) - evaluateScript(ScriptSourceCode(scriptContent(), m_element->document()->url())); // FIXME: Provide a real starting line number here -} - -void ScriptElement::finishParsingChildren(const String& sourceUrl) -{ - // The parser just reached </script>. If we have no src and no text, - // allow dynamic loading later. - if (sourceUrl.isEmpty() && scriptContent().isEmpty()) - m_wasInsertedByParser = false; + if (!m_parserInserted && m_element->inDocument()) + prepareScript(); // FIXME: Provide a real starting line number here. } void ScriptElement::handleSourceAttribute(const String& sourceUrl) @@ -116,12 +93,19 @@ void ScriptElement::handleSourceAttribute(const String& sourceUrl) if (ignoresLoadRequest() || sourceUrl.isEmpty()) return; - requestScript(sourceUrl); + prepareScript(); // FIXME: Provide a real starting line number here. } // Helper function -static bool isSupportedJavaScriptLanguage(const String& language) +static bool isLegacySupportedJavaScriptLanguage(const String& language) { + // Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only javascript1.1 - javascript1.3. + // Mozilla 1.8 and WinIE 7 both accept javascript and livescript. + // WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't. + // Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace. + // We want to accept all the values that either of these browsers accept, but not other values. + + // FIXME: This function is not HTML5 compliant. These belong in the MIME registry as "text/javascript<version>" entries. typedef HashSet<String, CaseFoldingHash> LanguageSet; DEFINE_STATIC_LOCAL(LanguageSet, languages, ()); if (languages.isEmpty()) { @@ -137,58 +121,133 @@ static bool isSupportedJavaScriptLanguage(const String& language) languages.add("javascript1.7"); languages.add("livescript"); languages.add("ecmascript"); - languages.add("jscript"); + languages.add("jscript"); } return languages.contains(language); } -void ScriptElement::requestScript(const String& sourceUrl) +bool ScriptElement::isScriptTypeSupported(LegacyTypeSupport supportLegacyTypes) const +{ + // FIXME: isLegacySupportedJavaScriptLanguage() is not valid HTML5. It is used here to maintain backwards compatibility with existing layout tests. The specific violations are: + // - Allowing type=javascript. type= should only support MIME types, such as text/javascript. + // - Allowing a different set of languages for language= and type=. language= supports Javascript 1.1 and 1.4-1.6, but type= does not. + + String type = typeAttributeValue(); + String language = languageAttributeValue(); + if (type.isEmpty() && language.isEmpty()) + return true; // Assume text/javascript. + if (type.isEmpty()) { + type = "text/" + language.lower(); + if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type) || isLegacySupportedJavaScriptLanguage(language)) + return true; + } else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace().lower()) || (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type))) + return true; + return false; +} + +// http://dev.w3.org/html5/spec/Overview.html#prepare-a-script +bool ScriptElement::prepareScript(const TextPosition1& scriptStartPosition, LegacyTypeSupport supportLegacyTypes) { - // FIXME: Eventually we'd like to evaluate scripts which are inserted into a + if (m_alreadyStarted) + return false; + + bool wasParserInserted; + if (m_parserInserted) { + wasParserInserted = true; + m_parserInserted = false; + } else + wasParserInserted = false; + + // FIXME: HTML5 spec says we should set forceAsync. + + // FIXME: HTML5 spec says we should check that all children are either comments or empty text nodes. + if (!hasSourceAttribute() && !m_element->firstChild()) + return false; + + if (!m_element->inDocument()) + return false; + + if (!isScriptTypeSupported(supportLegacyTypes)) + return false; + + if (wasParserInserted) + m_parserInserted = true; + + m_alreadyStarted = true; + + // FIXME: If script is parser inserted, verify it's still in the original document. + + // FIXME: Eventually we'd like to evaluate scripts which are inserted into a // viewless document but this'll do for now. // See http://bugs.webkit.org/show_bug.cgi?id=5727 if (!m_element->document()->frame()) - return; + return false; + + if (!m_element->document()->frame()->script()->canExecuteScripts(AboutToExecuteScript)) + return false; + + if (!isScriptForEventSupported()) + return false; + + if (!charsetAttributeValue().isEmpty()) + m_characterEncoding = charsetAttributeValue(); + else + m_characterEncoding = m_element->document()->charset(); + + if (hasSourceAttribute()) + if (!requestScript(sourceAttributeValue())) + return false; + + if (hasSourceAttribute() && deferAttributeValue() && m_parserInserted && !asyncAttributeValue()) { + m_willExecuteWhenDocumentFinishedParsing = true; + m_willBeParserExecuted = true; + } else if (hasSourceAttribute() && m_parserInserted && !asyncAttributeValue()) + m_willBeParserExecuted = true; + else if (!hasSourceAttribute() && m_parserInserted && !m_element->document()->haveStylesheetsLoaded()) { + m_willBeParserExecuted = true; + m_readyToBeParserExecuted = true; + } else if (hasSourceAttribute()) + m_cachedScript->addClient(this); + else + executeScript(ScriptSourceCode(scriptContent(), m_element->document()->url(), scriptStartPosition)); + + return true; +} + +bool ScriptElement::requestScript(const String& sourceUrl) +{ + if (!m_element->document()->contentSecurityPolicy()->canLoadExternalScriptFromSrc(sourceUrl)) + return false; RefPtr<Document> originalDocument = m_element->document(); if (!m_element->dispatchBeforeLoadEvent(sourceUrl)) - return; + return false; if (!m_element->inDocument() || m_element->document() != originalDocument) - return; + return false; ASSERT(!m_cachedScript); + // FIXME: If sourceUrl is empty, we should dispatchErrorEvent(). m_cachedScript = m_element->document()->cachedResourceLoader()->requestScript(sourceUrl, scriptCharset()); m_isExternalScript = true; - // m_wasInsertedByParser is never reset - always resied at the initial value set while parsing. - // m_wasAlreadyStarted is left untouched as well to avoid script reexecution, if a <script> element - // is removed and reappended to the document. - m_haveFiredLoad = false; - - if (m_cachedScript) { - m_cachedScript->addClient(this); - return; - } + if (m_cachedScript) + return true; dispatchErrorEvent(); + return false; } -void ScriptElement::evaluateScript(const ScriptSourceCode& sourceCode) +void ScriptElement::executeScript(const ScriptSourceCode& sourceCode) { - if (wasAlreadyStarted() || sourceCode.isEmpty() || !shouldExecuteAsJavaScript()) + ASSERT(m_alreadyStarted); + + if (sourceCode.isEmpty()) return; RefPtr<Document> document = m_element->document(); ASSERT(document); if (Frame* frame = document->frame()) { - if (!frame->script()->canExecuteScripts(AboutToExecuteScript)) - return; - - m_wasAlreadyStarted = true; - - // http://www.whatwg.org/specs/web-apps/current-work/#script - { IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(m_isExternalScript ? document.get() : 0); // Create a script from the script element node, using the script @@ -201,36 +260,23 @@ void ScriptElement::evaluateScript(const ScriptSourceCode& sourceCode) } } -void ScriptElement::executeScript(const ScriptSourceCode& sourceCode) -{ - if (wasAlreadyStarted() || sourceCode.isEmpty()) - return; - RefPtr<Document> document = m_element->document(); - ASSERT(document); - Frame* frame = document->frame(); - if (!frame) - return; - - m_wasAlreadyStarted = true; - - frame->script()->executeScript(sourceCode); -} - void ScriptElement::stopLoadRequest() { if (m_cachedScript) { - m_cachedScript->removeClient(this); + if (!m_willBeParserExecuted) + m_cachedScript->removeClient(this); m_cachedScript = 0; } } void ScriptElement::execute(CachedScript* cachedScript) { + ASSERT(!m_willBeParserExecuted); ASSERT(cachedScript); if (cachedScript->errorOccurred()) dispatchErrorEvent(); else { - evaluateScript(ScriptSourceCode(cachedScript)); + executeScript(ScriptSourceCode(cachedScript)); dispatchLoadEvent(); } cachedScript->removeClient(this); @@ -238,6 +284,7 @@ void ScriptElement::execute(CachedScript* cachedScript) void ScriptElement::notifyFinished(CachedResource* o) { + ASSERT(!m_willBeParserExecuted); ASSERT_UNUSED(o, o == m_cachedScript); m_element->document()->asyncScriptRunner()->executeScriptSoon(this, m_cachedScript); m_cachedScript = 0; @@ -245,59 +292,25 @@ void ScriptElement::notifyFinished(CachedResource* o) bool ScriptElement::ignoresLoadRequest() const { - return wasAlreadyStarted() || m_isExternalScript || wasInsertedByParser() || !m_element->inDocument(); + return m_alreadyStarted || m_isExternalScript || m_parserInserted || !m_element->inDocument(); } -bool ScriptElement::shouldExecuteAsJavaScript() const +bool ScriptElement::isScriptForEventSupported() const { - /* - Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only javascript1.1 - javascript1.3. - Mozilla 1.8 and WinIE 7 both accept javascript and livescript. - WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't. - Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace. - We want to accept all the values that either of these browsers accept, but not other values. - - FIXME: Is this HTML5 compliant? - */ - String type = typeAttributeValue(); - if (!type.isEmpty()) { - if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace().lower())) - return false; - } else { - String language = languageAttributeValue(); - if (!language.isEmpty() && !isSupportedJavaScriptLanguage(language)) - return false; - } - - // No type or language is specified, so we assume the script to be JavaScript. - - String forAttribute = forAttributeValue(); String eventAttribute = eventAttributeValue(); - if (!forAttribute.isEmpty() && !eventAttribute.isEmpty()) { + String forAttribute = forAttributeValue(); + if (!eventAttribute.isEmpty() && !forAttribute.isEmpty()) { forAttribute = forAttribute.stripWhiteSpace(); if (!equalIgnoringCase(forAttribute, "window")) return false; - + eventAttribute = eventAttribute.stripWhiteSpace(); if (!equalIgnoringCase(eventAttribute, "onload") && !equalIgnoringCase(eventAttribute, "onload()")) return false; } - return true; } -String ScriptElement::scriptCharset() const -{ - // First we try to get encoding from charset attribute. - String charset = charsetAttributeValue().stripWhiteSpace(); - - // If charset has not been declared in script tag, fall back to frame encoding. - if (charset.isEmpty()) - charset = m_element->document()->charset(); - - return charset; -} - String ScriptElement::scriptContent() const { Vector<UChar> val; @@ -325,20 +338,6 @@ String ScriptElement::scriptContent() const return String::adopt(val); } -bool ScriptElement::isAsynchronous() const -{ - // Only external scripts may be asynchronous. - // See: http://dev.w3.org/html5/spec/Overview.html#attr-script-async - return !sourceAttributeValue().isEmpty() && asyncAttributeValue(); -} - -bool ScriptElement::isDeferred() const -{ - // Only external scripts may be deferred and async trumps defer to allow for backward compatibility. - // See: http://dev.w3.org/html5/spec/Overview.html#attr-script-defer - return !sourceAttributeValue().isEmpty() && !asyncAttributeValue() && deferAttributeValue(); -} - ScriptElement* toScriptElement(Element* element) { if (element->isHTMLElement() && element->hasTagName(HTMLNames::scriptTag)) diff --git a/Source/WebCore/dom/ScriptElement.h b/Source/WebCore/dom/ScriptElement.h index 5250f3e..79dff33 100644 --- a/Source/WebCore/dom/ScriptElement.h +++ b/Source/WebCore/dom/ScriptElement.h @@ -23,6 +23,7 @@ #include "CachedResourceClient.h" #include "CachedResourceHandle.h" +#include <wtf/text/TextPosition.h> namespace WebCore { @@ -36,47 +37,49 @@ class ScriptElement : private CachedResourceClient { public: ScriptElement(Element*, bool createdByParser, bool isEvaluated); virtual ~ScriptElement(); - + Element* element() const { return m_element; } - // A charset for loading the script (may be overridden by HTTP headers or a BOM). - String scriptCharset() const; + enum LegacyTypeSupport { DisallowLegacyTypeInTypeAttribute, AllowLegacyTypeInTypeAttribute }; + bool prepareScript(const TextPosition1& scriptStartPosition = TextPosition1::minimumPosition(), LegacyTypeSupport = DisallowLegacyTypeInTypeAttribute); + String scriptCharset() const { return m_characterEncoding; } String scriptContent() const; - bool shouldExecuteAsJavaScript() const; void executeScript(const ScriptSourceCode&); void execute(CachedScript*); // XML parser calls these - virtual String sourceAttributeValue() const = 0; virtual void dispatchLoadEvent() = 0; virtual void dispatchErrorEvent() = 0; + bool isScriptTypeSupported(LegacyTypeSupport) const; bool haveFiredLoadEvent() const { return m_haveFiredLoad; } + bool willBeParserExecuted() const { return m_willBeParserExecuted; } + bool readyToBeParserExecuted() const { return m_readyToBeParserExecuted; } + bool willExecuteWhenDocumentFinishedParsing() const { return m_willExecuteWhenDocumentFinishedParsing; } + CachedResourceHandle<CachedScript> cachedScript() { return m_cachedScript; } protected: void setHaveFiredLoadEvent(bool haveFiredLoad) { m_haveFiredLoad = haveFiredLoad; } - bool wasInsertedByParser() const { return m_wasInsertedByParser; } - bool wasAlreadyStarted() const { return m_wasAlreadyStarted; } + bool isParserInserted() const { return m_parserInserted; } + bool alreadyStarted() const { return m_alreadyStarted; } // Helper functions used by our parent classes. - void insertedIntoDocument(const String& sourceUrl); + void insertedIntoDocument(); void removedFromDocument(); void childrenChanged(); - void finishParsingChildren(const String& sourceUrl); void handleSourceAttribute(const String& sourceUrl); private: bool ignoresLoadRequest() const; - bool isAsynchronous() const; - bool isDeferred() const; + bool isScriptForEventSupported() const; - void requestScript(const String& sourceUrl); - void evaluateScript(const ScriptSourceCode&); + bool requestScript(const String& sourceUrl); void stopLoadRequest(); virtual void notifyFinished(CachedResource*); + virtual String sourceAttributeValue() const = 0; virtual String charsetAttributeValue() const = 0; virtual String typeAttributeValue() const = 0; virtual String languageAttributeValue() const = 0; @@ -84,13 +87,19 @@ private: virtual String eventAttributeValue() const = 0; virtual bool asyncAttributeValue() const = 0; virtual bool deferAttributeValue() const = 0; + virtual bool hasSourceAttribute() const = 0; Element* m_element; CachedResourceHandle<CachedScript> m_cachedScript; - bool m_wasInsertedByParser; - bool m_isExternalScript; - bool m_wasAlreadyStarted; - bool m_haveFiredLoad; + bool m_parserInserted : 1; + bool m_isExternalScript : 1; + bool m_alreadyStarted : 1; + bool m_haveFiredLoad : 1; + bool m_willBeParserExecuted : 1; // Same as "The parser will handle executing the script." + bool m_readyToBeParserExecuted : 1; + bool m_willExecuteWhenDocumentFinishedParsing : 1; + String m_characterEncoding; + String m_fallbackCharacterEncoding; }; ScriptElement* toScriptElement(Element*); diff --git a/Source/WebCore/dom/ScriptExecutionContext.cpp b/Source/WebCore/dom/ScriptExecutionContext.cpp index 19267c6..6685416 100644 --- a/Source/WebCore/dom/ScriptExecutionContext.cpp +++ b/Source/WebCore/dom/ScriptExecutionContext.cpp @@ -30,6 +30,7 @@ #include "ActiveDOMObject.h" #include "Blob.h" #include "BlobURL.h" +#include "DOMTimer.h" #include "DOMURL.h" #include "Database.h" #include "DatabaseTask.h" @@ -41,6 +42,7 @@ #include "MessagePort.h" #include "ScriptCallStack.h" #include "SecurityOrigin.h" +#include "Settings.h" #include "ThreadableBlobRegistry.h" #include "WorkerContext.h" #include "WorkerThread.h" @@ -406,6 +408,26 @@ FileThread* ScriptExecutionContext::fileThread() } #endif +void ScriptExecutionContext::adjustMinimumTimerInterval(double oldMinimumTimerInterval) +{ + if (minimumTimerInterval() != oldMinimumTimerInterval) { + for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) { + DOMTimer* timer = iter->second; + timer->adjustMinimumTimerInterval(oldMinimumTimerInterval); + } + } +} + +double ScriptExecutionContext::minimumTimerInterval() const +{ + // The default implementation returns the DOMTimer's default + // minimum timer interval. FIXME: to make it work with dedicated + // workers, we will have to override it in the appropriate + // subclass, and provide a way to enumerate a Document's dedicated + // workers so we can update them all. + return Settings::defaultMinDOMTimerInterval(); +} + ScriptExecutionContext::Task::~Task() { } diff --git a/Source/WebCore/dom/ScriptExecutionContext.h b/Source/WebCore/dom/ScriptExecutionContext.h index 642906c..3b37f0c 100644 --- a/Source/WebCore/dom/ScriptExecutionContext.h +++ b/Source/WebCore/dom/ScriptExecutionContext.h @@ -107,6 +107,9 @@ namespace WebCore { typedef const HashMap<ActiveDOMObject*, void*> ActiveDOMObjectsMap; ActiveDOMObjectsMap& activeDOMObjects() const { return m_activeDOMObjects; } + virtual void suspendScriptedAnimationControllerCallbacks() { } + virtual void resumeScriptedAnimationControllerCallbacks() { } + // MessagePort is conceptually a kind of ActiveDOMObject, but it needs to be tracked separately for message dispatch. void processMessagePortMessagesSoon(); void dispatchMessagePortEvents(); @@ -152,6 +155,10 @@ namespace WebCore { void stopFileThread(); #endif + // Interval is in seconds. + void adjustMinimumTimerInterval(double oldMinimumTimerInterval); + virtual double minimumTimerInterval() const; + protected: // Explicitly override the security origin for this script context. // Note: It is dangerous to change the security origin of a script context @@ -176,7 +183,8 @@ namespace WebCore { bool m_iteratingActiveDOMObjects; bool m_inDestructor; - HashMap<int, DOMTimer*> m_timeouts; + typedef HashMap<int, DOMTimer*> TimeoutMap; + TimeoutMap m_timeouts; #if ENABLE(BLOB) HashSet<String> m_publicBlobURLs; diff --git a/Source/WebCore/dom/ScriptedAnimationController.cpp b/Source/WebCore/dom/ScriptedAnimationController.cpp new file mode 100644 index 0000000..0c70359 --- /dev/null +++ b/Source/WebCore/dom/ScriptedAnimationController.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2011 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "ScriptedAnimationController.h" + +#if ENABLE(REQUEST_ANIMATION_FRAME) + +#include "Document.h" +#include "Element.h" +#include "FrameView.h" +#include "RequestAnimationFrameCallback.h" + +namespace WebCore { + +ScriptedAnimationController::ScriptedAnimationController(Document* document) + : m_document(document) + , m_nextCallbackId(0) + , m_suspendCount(0) +{ +} + +void ScriptedAnimationController::suspend() +{ + ++m_suspendCount; +} + +void ScriptedAnimationController::resume() +{ + --m_suspendCount; + if (!m_suspendCount && m_callbacks.size()) + if (FrameView* fv = m_document->view()) + fv->scheduleAnimation(); +} + +ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(PassRefPtr<RequestAnimationFrameCallback> callback, Element* animationElement) +{ + ScriptedAnimationController::CallbackId id = m_nextCallbackId++; + callback->m_firedOrCancelled = false; + callback->m_id = id; + callback->m_element = animationElement; + m_callbacks.append(callback); + if (!m_suspendCount) + if (FrameView* view = m_document->view()) + view->scheduleAnimation(); + return id; +} + +void ScriptedAnimationController::cancelCallback(CallbackId id) +{ + for (size_t i = 0; i < m_callbacks.size(); ++i) { + if (m_callbacks[i]->m_id == id) { + m_callbacks[i]->m_firedOrCancelled = true; + m_callbacks.remove(i); + return; + } + } +} + +void ScriptedAnimationController::serviceScriptedAnimations(DOMTimeStamp time) +{ + if (!m_callbacks.size() || m_suspendCount) + return; + // We want to run the callback for all elements in the document that have registered + // for a callback and that are visible. Running the callbacks can cause new callbacks + // to be registered, existing callbacks to be cancelled, and elements to gain or lose + // visibility so this code has to iterate carefully. + + // FIXME: Currently, this code doesn't do any visibility tests beyond checking display: + + // First, generate a list of callbacks to consider. Callbacks registered from this point + // on are considered only for the "next" frame, not this one. + CallbackList callbacks(m_callbacks); + + // Firing the callback may cause the visibility of other elements to change. To avoid + // missing any callbacks, we keep iterating through the list of candiate callbacks and firing + // them until nothing new becomes visible. + bool firedCallback; + do { + firedCallback = false; + // A previous iteration may have invalidated style (or layout). Update styles for each iteration + // for now since all we check is the existence of a renderer. + m_document->updateStyleIfNeeded(); + for (size_t i = 0; i < callbacks.size(); ++i) { + RequestAnimationFrameCallback* callback = callbacks[i].get(); + if (!callback->m_firedOrCancelled && (!callback->m_element || callback->m_element->renderer())) { + callback->m_firedOrCancelled = true; + callback->handleEvent(time); + firedCallback = true; + callbacks.remove(i); + break; + } + } + } while (firedCallback); + + // Remove any callbacks we fired from the list of pending callbacks. + for (size_t i = 0; i < m_callbacks.size();) { + if (m_callbacks[i]->m_firedOrCancelled) + m_callbacks.remove(i); + else + ++i; + } + + if (m_callbacks.size()) + if (FrameView* view = m_document->view()) + view->scheduleAnimation(); +} + +} + +#endif + diff --git a/Source/WebCore/dom/ScriptedAnimationController.h b/Source/WebCore/dom/ScriptedAnimationController.h new file mode 100644 index 0000000..7141968 --- /dev/null +++ b/Source/WebCore/dom/ScriptedAnimationController.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef ScriptedAnimationController_h +#define ScriptedAnimationController_h + +#if ENABLE(REQUEST_ANIMATION_FRAME) +#include "DOMTimeStamp.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Document; +class Element; +class RequestAnimationFrameCallback; + +class ScriptedAnimationController { +WTF_MAKE_NONCOPYABLE(ScriptedAnimationController); +public: + static PassOwnPtr<ScriptedAnimationController> create(Document* document) + { + return adoptPtr(new ScriptedAnimationController(document)); + } + + typedef int CallbackId; + + CallbackId registerCallback(PassRefPtr<RequestAnimationFrameCallback>, Element*); + void cancelCallback(CallbackId); + void serviceScriptedAnimations(DOMTimeStamp); + + void suspend(); + void resume(); + +private: + explicit ScriptedAnimationController(Document*); + typedef Vector<RefPtr<RequestAnimationFrameCallback> > CallbackList; + CallbackList m_callbacks; + + Document* m_document; + CallbackId m_nextCallbackId; + int m_suspendCount; +}; + +} + +#endif // ENABLE(REQUEST_ANIMATION_FRAME) + +#endif // ScriptedAnimationController_h + diff --git a/Source/WebCore/dom/SelectElement.cpp b/Source/WebCore/dom/SelectElement.cpp index a4da0ae..15c69ad 100644 --- a/Source/WebCore/dom/SelectElement.cpp +++ b/Source/WebCore/dom/SelectElement.cpp @@ -787,18 +787,24 @@ void SelectElement::listBoxDefaultEventHandler(SelectElementData& data, Element* ASSERT_UNUSED(listItems, !listItems.size() || (endIndex >= 0 && (unsigned) endIndex < listItems.size())); setActiveSelectionEndIndex(data, endIndex); + bool selectNewItem = !data.multiple() || static_cast<KeyboardEvent*>(event)->shiftKey() || !isSpatialNavigationEnabled(element->document()->frame()); + if (selectNewItem) + data.setActiveSelectionState(true); // If the anchor is unitialized, or if we're going to deselect all other options, then set the anchor index equal to the end index. - bool deselectOthers = !data.multiple() || !static_cast<KeyboardEvent*>(event)->shiftKey(); + bool deselectOthers = !data.multiple() || (!static_cast<KeyboardEvent*>(event)->shiftKey() && selectNewItem); if (data.activeSelectionAnchorIndex() < 0 || deselectOthers) { - data.setActiveSelectionState(true); if (deselectOthers) deselectItems(data, element); setActiveSelectionAnchorIndex(data, element, data.activeSelectionEndIndex()); } toRenderListBox(element->renderer())->scrollToRevealElementAtListIndex(endIndex); - updateListBoxSelection(data, element, deselectOthers); - listBoxOnChange(data, element); + if (selectNewItem) { + updateListBoxSelection(data, element, deselectOthers); + listBoxOnChange(data, element); + } else + scrollToSelection(data, element); + event->setDefaultHandled(); } } else if (event->type() == eventNames().keypressEvent) { @@ -810,7 +816,12 @@ void SelectElement::listBoxDefaultEventHandler(SelectElementData& data, Element* if (htmlForm) htmlForm->submitImplicitly(event, false); event->setDefaultHandled(); - return; + } else if (data.multiple() && keyCode == ' ' && isSpatialNavigationEnabled(element->document()->frame())) { + // Use space to toggle selection change. + data.setActiveSelectionState(!data.activeSelectionState()); + updateSelectedState(data, element, listToOptionIndex(data, element, data.activeSelectionEndIndex()), true /*multi*/, false /*shift*/); + listBoxOnChange(data, element); + event->setDefaultHandled(); } } } diff --git a/Source/WebCore/dom/Text.cpp b/Source/WebCore/dom/Text.cpp index 34266f1..5a28e37 100644 --- a/Source/WebCore/dom/Text.cpp +++ b/Source/WebCore/dom/Text.cpp @@ -25,8 +25,6 @@ #include "ExceptionCode.h" #include "RenderCombineText.h" #include "RenderText.h" -#include "TextBreakIterator.h" -#include <wtf/text/CString.h> #if ENABLE(SVG) #include "RenderSVGInlineText.h" @@ -304,37 +302,17 @@ PassRefPtr<Text> Text::virtualCreate(const String& data) return create(document(), data); } -PassRefPtr<Text> Text::createWithLengthLimit(Document* document, const String& data, unsigned& charsLeft, unsigned maxChars) +PassRefPtr<Text> Text::createWithLengthLimit(Document* document, const String& data, unsigned start, unsigned maxChars) { unsigned dataLength = data.length(); - if (charsLeft == dataLength && charsLeft <= maxChars) { - charsLeft = 0; + if (!start && dataLength <= maxChars) return create(document, data); - } - unsigned start = dataLength - charsLeft; - unsigned end = start + min(charsLeft, maxChars); - - // Check we are not on an unbreakable boundary. - // Some text break iterator implementations work best if the passed buffer is as small as possible, - // see <https://bugs.webkit.org/show_bug.cgi?id=29092>. - // We need at least two characters look-ahead to account for UTF-16 surrogates. - if (end < dataLength) { - TextBreakIterator* it = characterBreakIterator(data.characters() + start, (end + 2 > dataLength) ? dataLength - start : end - start + 2); - if (!isTextBreak(it, end - start)) - end = textBreakPreceding(it, end - start) + start; - } - - // If we have maxChars of unbreakable characters the above could lead to - // an infinite loop. - // FIXME: It would be better to just have the old value of end before calling - // textBreakPreceding rather than this, because this exceeds the length limit. - if (end <= start) - end = dataLength; - - charsLeft = dataLength - end; - return create(document, data.substring(start, end - start)); + RefPtr<Text> result = Text::create(document, String()); + result->parserAppendData(data.characters() + start, dataLength - start, maxChars); + + return result; } #ifndef NDEBUG diff --git a/Source/WebCore/dom/Text.h b/Source/WebCore/dom/Text.h index 45678ef..f693f3b 100644 --- a/Source/WebCore/dom/Text.h +++ b/Source/WebCore/dom/Text.h @@ -32,7 +32,7 @@ public: static const unsigned defaultLengthLimit = 1 << 16; static PassRefPtr<Text> create(Document*, const String&); - static PassRefPtr<Text> createWithLengthLimit(Document*, const String&, unsigned& charsLeft, unsigned lengthLimit = defaultLengthLimit); + static PassRefPtr<Text> createWithLengthLimit(Document*, const String&, unsigned positionInString, unsigned lengthLimit = defaultLengthLimit); PassRefPtr<Text> splitText(unsigned offset, ExceptionCode&); diff --git a/Source/WebCore/dom/ViewportArguments.cpp b/Source/WebCore/dom/ViewportArguments.cpp index 6dd1b8a..e75a3eb 100644 --- a/Source/WebCore/dom/ViewportArguments.cpp +++ b/Source/WebCore/dom/ViewportArguments.cpp @@ -3,7 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) * (C) 2006 Alexey Proskuryakov (ap@webkit.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) * @@ -178,6 +178,27 @@ ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktop return result; } +static float numericPrefix(const String& keyString, const String& valueString, Document* document, bool* ok) +{ + // If a prefix of property-value can be converted to a number using strtod, + // the value will be that number. The remainder of the string is ignored. + // So when String::toFloat says there is an error, it may be a false positive, + // and we should check if the valueString prefix was a number. + + bool didReadNumber; + float value = valueString.toFloat(ok, &didReadNumber); + if (!*ok) { + if (!didReadNumber) { + ASSERT(!value); + reportViewportWarning(document, UnrecognizedViewportArgumentValueError, valueString, keyString); + return value; + } + *ok = true; + reportViewportWarning(document, TruncatedViewportArgumentValueError, valueString, keyString); + } + return value; +} + static float findSizeValue(const String& keyString, const String& valueString, Document* document) { // 1) Non-negative number values are translated to px lengths. @@ -193,20 +214,13 @@ static float findSizeValue(const String& keyString, const String& valueString, D return ViewportArguments::ValueDeviceHeight; bool ok; - float value = valueString.toFloat(&ok); - if (!ok) { - reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString); + float value = numericPrefix(keyString, valueString, document, &ok); + if (!ok) return float(0.0); - } if (value < 0) return ViewportArguments::ValueAuto; - if (keyString == "width") - reportViewportWarning(document, DeviceWidthShouldBeUsedWarning, keyString); - else if (keyString == "height") - reportViewportWarning(document, DeviceHeightShouldBeUsedWarning, keyString); - return value; } @@ -230,17 +244,15 @@ static float findScaleValue(const String& keyString, const String& valueString, return float(10.0); bool ok; - float value = valueString.toFloat(&ok); - if (!ok) { - reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString); + float value = numericPrefix(keyString, valueString, document, &ok); + if (!ok) return float(0.0); - } if (value < 0) return ViewportArguments::ValueAuto; if (value > 10.0) - reportViewportWarning(document, MaximumScaleTooLargeError, keyString); + reportViewportWarning(document, MaximumScaleTooLargeError, String(), String()); return value; } @@ -263,11 +275,9 @@ static bool findUserScalableValue(const String& keyString, const String& valueSt return true; bool ok; - float value = valueString.toFloat(&ok); - if (!ok) { - reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString); + float value = numericPrefix(keyString, valueString, document, &ok); + if (!ok) return false; - } if (fabs(value) < 1) return false; @@ -287,14 +297,12 @@ static float findTargetDensityDPIValue(const String& keyString, const String& va return ViewportArguments::ValueHighDPI; bool ok; - float value = valueString.toFloat(&ok); - if (!ok) { - reportViewportWarning(document, UnrecognizedViewportArgumentError, keyString); + float value = numericPrefix(keyString, valueString, document, &ok); + if (!ok) return ViewportArguments::ValueAuto; - } if (value < 70 || value > 400) { - reportViewportWarning(document, TargetDensityDpiTooSmallOrLargeError, keyString); + reportViewportWarning(document, TargetDensityDpiTooSmallOrLargeError, String(), String()); return ViewportArguments::ValueAuto; } @@ -319,15 +327,17 @@ void setViewportFeature(const String& keyString, const String& valueString, Docu arguments->userScalable = findUserScalableValue(keyString, valueString, document); else if (keyString == "target-densitydpi") arguments->targetDensityDpi = findTargetDensityDPIValue(keyString, valueString, document); + else + reportViewportWarning(document, UnrecognizedViewportArgumentKeyError, keyString, String()); } static const char* viewportErrorMessageTemplate(ViewportErrorCode errorCode) { static const char* const errors[] = { - "Viewport width or height set to physical device width, try using \"device-width\" constant instead for future compatibility.", - "Viewport height or height set to physical device height, try using \"device-height\" constant instead for future compatibility.", - "Viewport argument \"%replacement\" not recognized. Content ignored.", - "Viewport maximum-scale cannot be larger than 10.0. The maximum-scale will be set to 10.0.", + "Viewport argument key \"%replacement1\" not recognized and ignored.", + "Viewport argument value \"%replacement1\" for key \"%replacement2\" not recognized. Content ignored.", + "Viewport argument value \"%replacement1\" for key \"%replacement2\" was truncated to its numeric prefix.", + "Viewport maximum-scale cannot be larger than 10.0. The maximum-scale will be set to 10.0.", "Viewport target-densitydpi has to take a number between 70 and 400 as a valid target dpi, try using \"device-dpi\", \"low-dpi\", \"medium-dpi\" or \"high-dpi\" instead for future compatibility." }; @@ -336,7 +346,18 @@ static const char* viewportErrorMessageTemplate(ViewportErrorCode errorCode) static MessageLevel viewportErrorMessageLevel(ViewportErrorCode errorCode) { - return errorCode == UnrecognizedViewportArgumentError || errorCode == MaximumScaleTooLargeError ? ErrorMessageLevel : TipMessageLevel; + switch (errorCode) { + case TruncatedViewportArgumentValueError: + case TargetDensityDpiTooSmallOrLargeError: + return TipMessageLevel; + case UnrecognizedViewportArgumentKeyError: + case UnrecognizedViewportArgumentValueError: + case MaximumScaleTooLargeError: + return ErrorMessageLevel; + } + + ASSERT_NOT_REACHED(); + return ErrorMessageLevel; } // FIXME: Why is this different from SVGDocumentExtensions parserLineNumber? @@ -351,14 +372,17 @@ static int parserLineNumber(Document* document) return parser->lineNumber() + 1; } -void reportViewportWarning(Document* document, ViewportErrorCode errorCode, const String& replacement) +void reportViewportWarning(Document* document, ViewportErrorCode errorCode, const String& replacement1, const String& replacement2) { Frame* frame = document->frame(); if (!frame) return; String message = viewportErrorMessageTemplate(errorCode); - message.replace("%replacement", replacement); + if (!replacement1.isNull()) + message.replace("%replacement1", replacement1); + if (!replacement2.isNull()) + message.replace("%replacement2", replacement2); frame->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, viewportErrorMessageLevel(errorCode), message, parserLineNumber(document), document->url().string()); } diff --git a/Source/WebCore/dom/ViewportArguments.h b/Source/WebCore/dom/ViewportArguments.h index 4f678f3..2e0fd19 100644 --- a/Source/WebCore/dom/ViewportArguments.h +++ b/Source/WebCore/dom/ViewportArguments.h @@ -35,9 +35,9 @@ namespace WebCore { class Document; enum ViewportErrorCode { - DeviceWidthShouldBeUsedWarning, - DeviceHeightShouldBeUsedWarning, - UnrecognizedViewportArgumentError, + UnrecognizedViewportArgumentKeyError, + UnrecognizedViewportArgumentValueError, + TruncatedViewportArgumentValueError, MaximumScaleTooLargeError, TargetDensityDpiTooSmallOrLargeError }; @@ -102,7 +102,7 @@ struct ViewportArguments { ViewportAttributes computeViewportAttributes(ViewportArguments args, int desktopWidth, int deviceWidth, int deviceHeight, int deviceDPI, IntSize visibleViewport); void setViewportFeature(const String& keyString, const String& valueString, Document*, void* data); -void reportViewportWarning(Document*, ViewportErrorCode, const String& replacement); +void reportViewportWarning(Document*, ViewportErrorCode, const String& replacement1, const String& replacement2); } // namespace WebCore diff --git a/Source/WebCore/dom/XMLDocumentParser.h b/Source/WebCore/dom/XMLDocumentParser.h index 2e305c8..9b4a19e 100644 --- a/Source/WebCore/dom/XMLDocumentParser.h +++ b/Source/WebCore/dom/XMLDocumentParser.h @@ -303,6 +303,7 @@ public: Vector<Node*> m_currentNodeStack; bool m_sawError; + bool m_sawCSS; bool m_sawXSLTransform; bool m_sawFirstElement; bool m_isXHTMLDocument; diff --git a/Source/WebCore/dom/XMLDocumentParserLibxml2.cpp b/Source/WebCore/dom/XMLDocumentParserLibxml2.cpp index 10d6e0d..9214391 100644 --- a/Source/WebCore/dom/XMLDocumentParserLibxml2.cpp +++ b/Source/WebCore/dom/XMLDocumentParserLibxml2.cpp @@ -63,14 +63,15 @@ #include <wtf/Vector.h> #if ENABLE(XSLT) +#include "XMLTreeViewer.h" #include <libxslt/xslt.h> #endif #if ENABLE(XHTMLMP) -#include "HTMLNames.h" #include "HTMLScriptElement.h" #endif + using namespace std; namespace WebCore { @@ -548,6 +549,7 @@ XMLDocumentParser::XMLDocumentParser(Document* document, FrameView* frameView) , m_pendingCallbacks(new PendingCallbacks) , m_currentNode(document) , m_sawError(false) + , m_sawCSS(false) , m_sawXSLTransform(false) , m_sawFirstElement(false) , m_isXHTMLDocument(false) @@ -574,6 +576,7 @@ XMLDocumentParser::XMLDocumentParser(DocumentFragment* fragment, Element* parent , m_pendingCallbacks(new PendingCallbacks) , m_currentNode(fragment) , m_sawError(false) + , m_sawCSS(false) , m_sawXSLTransform(false) , m_sawFirstElement(false) , m_isXHTMLDocument(false) @@ -880,35 +883,32 @@ void XMLDocumentParser::endElementNs() ASSERT(!m_pendingScript); m_requestingScript = true; + bool successfullyPrepared = scriptElement->prepareScript(m_scriptStartPosition, ScriptElement::AllowLegacyTypeInTypeAttribute); + if (!successfullyPrepared) { #if ENABLE(XHTMLMP) - if (!scriptElement->shouldExecuteAsJavaScript()) - document()->setShouldProcessNoscriptElement(true); - else + if (!scriptElement->isScriptTypeSupported(ScriptElement::AllowLegacyTypeInTypeAttribute)) + document()->setShouldProcessNoscriptElement(true); #endif - { - // FIXME: Script execution should be shared should be shared between + } else { + // FIXME: Script execution should be shared between // the libxml2 and Qt XMLDocumentParser implementations. // JavaScript can detach the parser. Make sure this is not released // before the end of this method. RefPtr<XMLDocumentParser> protect(this); - String scriptHref = scriptElement->sourceAttributeValue(); - if (!scriptHref.isEmpty()) { - // we have a src attribute - String scriptCharset = scriptElement->scriptCharset(); - if (element->dispatchBeforeLoadEvent(scriptHref) && - (m_pendingScript = document()->cachedResourceLoader()->requestScript(scriptHref, scriptCharset))) { - m_scriptElement = element; - m_pendingScript->addClient(this); - - // m_pendingScript will be 0 if script was already loaded and ref() executed it - if (m_pendingScript) - pauseParsing(); - } else - m_scriptElement = 0; - } else + if (scriptElement->readyToBeParserExecuted()) scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition)); + else if (scriptElement->willBeParserExecuted()) { + m_pendingScript = scriptElement->cachedScript(); + m_scriptElement = element; + m_pendingScript->addClient(this); + + // m_pendingScript will be 0 if script was already loaded and addClient() executed it. + if (m_pendingScript) + pauseParsing(); + } else + m_scriptElement = 0; // JavaScript may have detached the parser if (isDetached()) @@ -970,10 +970,10 @@ void XMLDocumentParser::processingInstruction(const xmlChar* target, const xmlCh exitText(); // ### handle exceptions - int exception = 0; + ExceptionCode ec = 0; RefPtr<ProcessingInstruction> pi = document()->createProcessingInstruction( - toString(target), toString(data), exception); - if (exception) + toString(target), toString(data), ec); + if (ec) return; pi->setCreatedByParser(true); @@ -984,6 +984,8 @@ void XMLDocumentParser::processingInstruction(const xmlChar* target, const xmlCh pi->finishParsingChildren(); + if (pi->isCSS()) + m_sawCSS = true; #if ENABLE(XSLT) m_sawXSLTransform = !m_sawFirstElement && pi->isXSL(); if (m_sawXSLTransform && !document()->transformSourceDocument()) @@ -1310,6 +1312,7 @@ void XMLDocumentParser::initializeParserContext(const char* chunk) sax.initialized = XML_SAX2_MAGIC; DocumentParser::startParsing(); m_sawError = false; + m_sawCSS = false; m_sawXSLTransform = false; m_sawFirstElement = false; @@ -1324,30 +1327,39 @@ void XMLDocumentParser::initializeParserContext(const char* chunk) void XMLDocumentParser::doEnd() { + if (!isStopped()) { + if (m_context) { + // Tell libxml we're done. + { + XMLDocumentParserScope scope(document()->cachedResourceLoader()); + xmlParseChunk(context(), 0, 0, 1); + } + + m_context = 0; + } + } + #if ENABLE(XSLT) - if (m_sawXSLTransform) { + XMLTreeViewer xmlTreeViewer(document()); + + bool xmlViewerMode = !m_sawError && !m_sawCSS && !m_sawXSLTransform && xmlTreeViewer.hasNoStyleInformation(); + + if (xmlViewerMode || m_sawXSLTransform) { void* doc = xmlDocPtrForString(document()->cachedResourceLoader(), m_originalSourceForTransform, document()->url().string()); document()->setTransformSource(new TransformSource(doc)); - document()->setParsing(false); // Make the doc think it's done, so it will apply xsl sheets. - document()->styleSelectorChanged(RecalcStyleImmediately); - document()->setParsing(true); + if (xmlViewerMode) + xmlTreeViewer.transformDocumentToTreeView(); + else { + document()->setParsing(false); // Make the document think it's done, so it will apply XSL stylesheets. + document()->styleSelectorChanged(RecalcStyleImmediately); + document()->setParsing(true); + } + DocumentParser::stopParsing(); } #endif - if (isStopped()) - return; - - if (m_context) { - // Tell libxml we're done. - { - XMLDocumentParserScope scope(document()->cachedResourceLoader()); - xmlParseChunk(context(), 0, 0, 1); - } - - m_context = 0; - } } #if ENABLE(XSLT) diff --git a/Source/WebCore/dom/XMLDocumentParserQt.cpp b/Source/WebCore/dom/XMLDocumentParserQt.cpp index d0bf88d..6219bcd 100644 --- a/Source/WebCore/dom/XMLDocumentParserQt.cpp +++ b/Source/WebCore/dom/XMLDocumentParserQt.cpp @@ -91,6 +91,7 @@ XMLDocumentParser::XMLDocumentParser(Document* document, FrameView* frameView) , m_wroteText(false) , m_currentNode(document) , m_sawError(false) + , m_sawCSS(false) , m_sawXSLTransform(false) , m_sawFirstElement(false) , m_isXHTMLDocument(false) @@ -117,6 +118,7 @@ XMLDocumentParser::XMLDocumentParser(DocumentFragment* fragment, Element* parent , m_wroteText(false) , m_currentNode(fragment) , m_sawError(false) + , m_sawCSS(false) , m_sawXSLTransform(false) , m_sawFirstElement(false) , m_isXHTMLDocument(false) @@ -205,6 +207,7 @@ void XMLDocumentParser::initializeParserContext(const char*) { DocumentParser::startParsing(); m_sawError = false; + m_sawCSS = false; m_sawXSLTransform = false; m_sawFirstElement = false; } @@ -588,28 +591,25 @@ void XMLDocumentParser::parseEndElement() ASSERT(!m_pendingScript); m_requestingScript = true; + bool successfullyPrepared = scriptElement->prepareScript(m_scriptStartPosition, ScriptElement::AllowLegacyTypeInTypeAttribute); + if (!successfullyPrepared) { #if ENABLE(XHTMLMP) - if (!scriptElement->shouldExecuteAsJavaScript()) - document()->setShouldProcessNoscriptElement(true); - else + if (!scriptElement->isScriptTypeSupported(ScriptElement::AllowLegacyTypeInTypeAttribute)) + document()->setShouldProcessNoscriptElement(true); #endif - { - String scriptHref = scriptElement->sourceAttributeValue(); - if (!scriptHref.isEmpty()) { - // we have a src attribute - String scriptCharset = scriptElement->scriptCharset(); - if (element->dispatchBeforeLoadEvent(scriptHref) && - (m_pendingScript = document()->cachedResourceLoader()->requestScript(scriptHref, scriptCharset))) { - m_scriptElement = element; - m_pendingScript->addClient(this); - - // m_pendingScript will be 0 if script was already loaded and ref() executed it - if (m_pendingScript) - pauseParsing(); - } else - m_scriptElement = 0; - } else + } else { + if (scriptElement->readyToBeParserExecuted()) scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition)); + else if (scriptElement->willBeParserExecuted()) { + m_pendingScript = scriptElement->cachedScript(); + m_scriptElement = element; + m_pendingScript->addClient(this); + + // m_pendingScript will be 0 if script was already loaded and addClient() executed it. + if (m_pendingScript) + pauseParsing(); + } else + m_scriptElement = 0; } m_requestingScript = false; popCurrentNode(); @@ -643,6 +643,8 @@ void XMLDocumentParser::parseProcessingInstruction() pi->finishParsingChildren(); + if (pi->isCSS()) + m_sawCSS = true; #if ENABLE(XSLT) m_sawXSLTransform = !m_sawFirstElement && pi->isXSL(); if (m_sawXSLTransform && !document()->transformSourceDocument()) |