summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/dom
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/dom')
-rw-r--r--Source/WebCore/dom/Attr.cpp2
-rw-r--r--Source/WebCore/dom/CharacterData.cpp28
-rw-r--r--Source/WebCore/dom/CharacterData.h3
-rw-r--r--Source/WebCore/dom/ContainerNode.cpp13
-rw-r--r--Source/WebCore/dom/DOMImplementation.cpp15
-rw-r--r--Source/WebCore/dom/DOMImplementation.h15
-rw-r--r--Source/WebCore/dom/DOMImplementation.idl4
-rw-r--r--Source/WebCore/dom/Document.cpp351
-rw-r--r--Source/WebCore/dom/Document.h67
-rw-r--r--Source/WebCore/dom/Document.idl1
-rw-r--r--Source/WebCore/dom/DocumentMarker.h3
-rw-r--r--Source/WebCore/dom/DocumentMarkerController.cpp102
-rw-r--r--Source/WebCore/dom/DocumentMarkerController.h9
-rw-r--r--Source/WebCore/dom/Element.cpp21
-rw-r--r--Source/WebCore/dom/Element.h12
-rw-r--r--Source/WebCore/dom/Event.cpp17
-rw-r--r--Source/WebCore/dom/Event.h5
-rw-r--r--Source/WebCore/dom/EventListener.h1
-rw-r--r--Source/WebCore/dom/EventNames.h3
-rw-r--r--Source/WebCore/dom/EventQueue.cpp42
-rw-r--r--Source/WebCore/dom/EventQueue.h19
-rw-r--r--Source/WebCore/dom/EventTarget.cpp8
-rw-r--r--Source/WebCore/dom/EventTarget.h17
-rw-r--r--Source/WebCore/dom/ExceptionCode.cpp6
-rw-r--r--Source/WebCore/dom/InputElement.cpp2
-rw-r--r--Source/WebCore/dom/InputElement.h3
-rw-r--r--Source/WebCore/dom/Node.cpp112
-rw-r--r--Source/WebCore/dom/Node.h15
-rw-r--r--Source/WebCore/dom/NodeRareData.h1
-rw-r--r--Source/WebCore/dom/Position.cpp143
-rw-r--r--Source/WebCore/dom/Position.h12
-rw-r--r--Source/WebCore/dom/ProcessingInstruction.cpp11
-rw-r--r--Source/WebCore/dom/ProcessingInstruction.h2
-rw-r--r--Source/WebCore/dom/Range.cpp268
-rw-r--r--Source/WebCore/dom/Range.h7
-rw-r--r--Source/WebCore/dom/ScriptElement.cpp259
-rw-r--r--Source/WebCore/dom/ScriptElement.h43
-rw-r--r--Source/WebCore/dom/ScriptExecutionContext.cpp22
-rw-r--r--Source/WebCore/dom/ScriptExecutionContext.h10
-rw-r--r--Source/WebCore/dom/ScriptedAnimationController.cpp134
-rw-r--r--Source/WebCore/dom/ScriptedAnimationController.h74
-rw-r--r--Source/WebCore/dom/SelectElement.cpp21
-rw-r--r--Source/WebCore/dom/Text.cpp34
-rw-r--r--Source/WebCore/dom/Text.h2
-rw-r--r--Source/WebCore/dom/ViewportArguments.cpp86
-rw-r--r--Source/WebCore/dom/ViewportArguments.h8
-rw-r--r--Source/WebCore/dom/XMLDocumentParser.h1
-rw-r--r--Source/WebCore/dom/XMLDocumentParserLibxml2.cpp92
-rw-r--r--Source/WebCore/dom/XMLDocumentParserQt.cpp40
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())