From 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 17 Dec 2008 18:05:15 -0800 Subject: Code drop from //branches/cupcake/...@124589 --- WebCore/dom/Document.cpp | 1443 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 1012 insertions(+), 431 deletions(-) (limited to 'WebCore/dom/Document.cpp') diff --git a/WebCore/dom/Document.cpp b/WebCore/dom/Document.cpp index 4b3e8ad..3e866a6 100644 --- a/WebCore/dom/Document.cpp +++ b/WebCore/dom/Document.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 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,10 +20,14 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ +#ifdef ANDROID_INSTRUMENT +#define LOG_TAG "WebCore" +#endif #include "config.h" #include "Document.h" +#include "AnimationController.h" #include "AXObjectCache.h" #include "CDATASection.h" #include "CSSHelper.h" @@ -31,18 +35,17 @@ #include "CSSStyleSheet.h" #include "CSSValueKeywords.h" #include "CString.h" -#include "ClassNodeList.h" +#include "CachedCSSStyleSheet.h" #include "Comment.h" #include "CookieJar.h" -#include "Database.h" #include "DOMImplementation.h" +#include "DOMWindow.h" #include "DocLoader.h" #include "DocumentFragment.h" #include "DocumentLoader.h" #include "DocumentType.h" #include "EditingText.h" #include "Editor.h" -#include "EditorClient.h" #include "EntityReference.h" #include "Event.h" #include "EventHandler.h" @@ -55,11 +58,11 @@ #include "FrameTree.h" #include "FrameView.h" #include "HTMLBodyElement.h" +#include "HTMLCanvasElement.h" #include "HTMLDocument.h" #include "HTMLElementFactory.h" #include "HTMLFrameOwnerElement.h" #include "HTMLHeadElement.h" -#include "HTMLImageLoader.h" #include "HTMLInputElement.h" #include "HTMLLinkElement.h" #include "HTMLMapElement.h" @@ -71,14 +74,17 @@ #include "HistoryItem.h" #include "HitTestRequest.h" #include "HitTestResult.h" +#include "ImageLoader.h" #include "KeyboardEvent.h" #include "Logging.h" +#include "MessageEvent.h" #include "MouseEvent.h" #include "MouseEventWithHitTestResults.h" #include "MutationEvent.h" #include "NameNodeList.h" #include "NodeFilter.h" #include "NodeIterator.h" +#include "NodeWithIndex.h" #include "OverflowEvent.h" #include "Page.h" #include "PlatformKeyboardEvent.h" @@ -101,13 +107,18 @@ #include "TextResourceDecoder.h" #include "TreeWalker.h" #include "UIEvent.h" +#include "WebKitAnimationEvent.h" +#include "WebKitTransitionEvent.h" #include "WheelEvent.h" #include "XMLHttpRequest.h" +#include "XMLNames.h" #include "XMLTokenizer.h" -#include "kjs_binding.h" -#include "kjs_proxy.h" +#include "JSDOMBinding.h" +#include "ScriptController.h" +#include #if ENABLE(DATABASE) +#include "Database.h" #include "DatabaseThread.h" #endif @@ -131,12 +142,13 @@ #include "SVGElementFactory.h" #include "SVGZoomEvent.h" #include "SVGStyleElement.h" -#include "TimeScheduler.h" #endif -#ifdef ANDROID_INSTRUMENT -#undef LOG -#include +#if ENABLE(TOUCH_EVENTS) // Android +#include "TouchEvent.h" +#if PLATFORM(ANDROID) +#include "WebViewCore.h" +#endif #endif #ifdef ANDROID_META_SUPPORT @@ -145,7 +157,6 @@ #ifdef ANDROID_RESET_SELECTION #include "CacheBuilder.h" -#include "FrameAndroid.h" #include "HTMLTextAreaElement.h" #endif @@ -155,7 +166,6 @@ using namespace Unicode; namespace WebCore { -using namespace EventNames; using namespace HTMLNames; // #define INSTRUMENT_LAYOUT_SCHEDULING 1 @@ -245,7 +255,7 @@ static inline bool isValidNamePart(UChar32 c) } static Widget* widgetForNode(Node* focusedNode) - { +{ if (!focusedNode) return 0; RenderObject* renderer = focusedNode->renderer(); @@ -267,14 +277,12 @@ static bool acceptsEditingFocus(Node *node) return frame->editor()->shouldBeginEditing(rangeOfContents(root).get()); } -DeprecatedPtrList* Document::changedDocuments = 0; +static HashSet* changedDocuments = 0; -// FrameView might be 0 -Document::Document(DOMImplementation* impl, Frame* frame, bool isXHTML) +Document::Document(Frame* frame, bool isXHTML) : ContainerNode(0) - , m_implementation(impl) , m_domtree_version(0) - , m_styleSheets(new StyleSheetList(this)) + , m_styleSheets(StyleSheetList::create(this)) , m_title("") , m_titleSetExplicitly(false) , m_imageLoadEventTimer(this, &Document::imageLoadEventTimerFired) @@ -294,18 +302,20 @@ Document::Document(DOMImplementation* impl, Frame* frame, bool isXHTML) #if ENABLE(SVG) , m_svgExtensions(0) #endif +#if ENABLE(DASHBOARD_SUPPORT) , m_hasDashboardRegions(false) , m_dashboardRegionsDirty(false) +#endif , m_accessKeyMapValid(false) , m_createRenderers(true) , m_inPageCache(false) - , m_isAllowedToLoadLocalResources(false) , m_useSecureKeyboardEntryWhenActive(false) , m_isXHTML(isXHTML) - , m_numNodeLists(0) + , m_numNodeListCaches(0) #if ENABLE(DATABASE) , m_hasOpenDatabases(false) #endif + , m_usingGeolocation(false) #if USE(LOW_BANDWIDTH_DISPLAY) , m_inLowBandwidthDisplay(false) #endif @@ -316,14 +326,15 @@ Document::Document(DOMImplementation* impl, Frame* frame, bool isXHTML) m_document.resetSkippingRef(this); m_printing = false; + + m_ignoreAutofocus = false; m_frame = frame; m_renderArena = 0; m_axObjectCache = 0; - // FIXME: DocLoader probably no longer needs the frame argument - m_docLoader = new DocLoader(frame, this); + m_docLoader = new DocLoader(this); visuallyOrdered = false; m_bParsing = false; @@ -331,8 +342,8 @@ Document::Document(DOMImplementation* impl, Frame* frame, bool isXHTML) m_tokenizer = 0; m_wellFormed = false; - pMode = Strict; - hMode = XHtml; + setParseMode(Strict); + m_textColor = Color::black; m_listenerTypes = 0; m_inDocument = true; @@ -343,12 +354,8 @@ Document::Document(DOMImplementation* impl, Frame* frame, bool isXHTML) m_usesFirstLineRules = false; m_usesFirstLetterRules = false; m_gotoAnchorNeededAfterStylesheetsLoad = false; - - bool matchAuthorAndUserStyles = true; - if (Settings* settings = this->settings()) - matchAuthorAndUserStyles = settings->authorAndUserStylesEnabled(); - m_styleSelector = new CSSStyleSelector(this, userStyleSheet(), m_styleSheets.get(), m_mappedElementSheet.get(), !inCompatMode(), matchAuthorAndUserStyles); - + + m_styleSelector = 0; m_didCalculateStyleSelector = false; m_pendingStylesheets = 0; m_ignorePendingStylesheets = false; @@ -365,7 +372,8 @@ Document::Document(DOMImplementation* impl, Frame* frame, bool isXHTML) m_startTime = currentTime(); m_overMinimumLayoutThreshold = false; - initSecurityOrigin(); + initSecurityContext(); + initDNSPrefetch(); static int docID = 0; m_docID = docID++; @@ -399,6 +407,8 @@ void Document::removedLastRef() delete m_tokenizer; m_tokenizer = 0; + m_cssCanvasElements.clear(); + #ifndef NDEBUG m_inRemovedLastRefFunction = false; #endif @@ -415,18 +425,11 @@ Document::~Document() ASSERT(!renderer()); ASSERT(!m_inPageCache); ASSERT(!m_savedRenderer); + ASSERT(m_ranges.isEmpty()); removeAllEventListeners(); -#if ENABLE(SVG) - delete m_svgExtensions; -#endif - - XMLHttpRequest::detachRequests(this); - { - KJS::JSLock lock; - KJS::ScriptInterpreter::forgetAllDOMNodesForDocument(this); - } + forgetAllDOMNodesForDocument(this); if (m_docChanged && changedDocuments) changedDocuments->remove(this); @@ -450,10 +453,8 @@ Document::~Document() deleteAllValues(m_markers); - if (m_axObjectCache) { - delete m_axObjectCache; - m_axObjectCache = 0; - } + clearAXObjectCache(); + m_decoder = 0; unsigned count = sizeof(m_nameCollectionInfo) / sizeof(m_nameCollectionInfo[0]); @@ -469,6 +470,8 @@ Document::~Document() if (m_styleSheets) m_styleSheets->documentDestroyed(); + + m_document = 0; } void Document::resetLinkColor() @@ -488,22 +491,29 @@ void Document::resetActiveLinkColor() void Document::setDocType(PassRefPtr docType) { + // This should never be called more than once. + // Note: This is not a public DOM method and can only be called by the parser. + ASSERT(!m_docType || !docType); + if (m_docType && docType) + return; m_docType = docType; -} - -DocumentType *Document::doctype() const -{ - return m_docType.get(); + if (m_docType) + m_docType->setDocument(this); + determineParseMode(); } DOMImplementation* Document::implementation() const { + if (!m_implementation) + m_implementation = DOMImplementation::create(); return m_implementation.get(); } -void Document::childrenChanged(bool changedByParser) +void Document::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) { - // invalidate the document element we have cached in case it was replaced + ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + + // Invalidate the document element we have cached in case it was replaced. m_documentElement = 0; } @@ -519,17 +529,17 @@ Element* Document::documentElement() const return m_documentElement.get(); } -PassRefPtr Document::createElement(const String &name, ExceptionCode& ec) +PassRefPtr Document::createElement(const AtomicString& name, ExceptionCode& ec) { - if (m_isXHTML) { - if (!isValidName(name)) { - ec = INVALID_CHARACTER_ERR; - return 0; - } + if (!isValidName(name)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + + if (m_isXHTML) + return HTMLElementFactory::createHTMLElement(name, this, 0, false); - return HTMLElementFactory::createHTMLElement(AtomicString(name), this, 0, false); - } else - return createElementNS(nullAtom, name, ec); + return createElement(QualifiedName(nullAtom, name, nullAtom), false, ec); } PassRefPtr Document::createDocumentFragment() @@ -537,17 +547,17 @@ PassRefPtr Document::createDocumentFragment() return new DocumentFragment(document()); } -PassRefPtr Document::createTextNode(const String &data) +PassRefPtr Document::createTextNode(const String& data) { return new Text(this, data); } -PassRefPtr Document::createComment (const String &data) +PassRefPtr Document::createComment(const String& data) { return new Comment(this, data); } -PassRefPtr Document::createCDATASection(const String &data, ExceptionCode& ec) +PassRefPtr Document::createCDATASection(const String& data, ExceptionCode& ec) { if (isHTMLDocument()) { ec = NOT_SUPPORTED_ERR; @@ -556,7 +566,7 @@ PassRefPtr Document::createCDATASection(const String &data, Except return new CDATASection(this, data); } -PassRefPtr Document::createProcessingInstruction(const String &target, const String &data, ExceptionCode& ec) +PassRefPtr Document::createProcessingInstruction(const String& target, const String& data, ExceptionCode& ec) { if (!isValidName(target)) { ec = INVALID_CHARACTER_ERR; @@ -569,7 +579,7 @@ PassRefPtr Document::createProcessingInstruction(const St return new ProcessingInstruction(this, target, data); } -PassRefPtr Document::createEntityReference(const String &name, ExceptionCode& ec) +PassRefPtr Document::createEntityReference(const String& name, ExceptionCode& ec) { if (!isValidName(name)) { ec = INVALID_CHARACTER_ERR; @@ -582,14 +592,14 @@ PassRefPtr Document::createEntityReference(const String &name, return new EntityReference(this, name); } -PassRefPtr Document::createEditingTextNode(const String &text) +PassRefPtr Document::createEditingTextNode(const String& text) { return new EditingText(this, text); } PassRefPtr Document::createCSSStyleDeclaration() { - return new CSSMutableStyleDeclaration; + return CSSMutableStyleDeclaration::create(); } PassRefPtr Document::importNode(Node* importedNode, bool deep, ExceptionCode& ec) @@ -597,7 +607,7 @@ PassRefPtr Document::importNode(Node* importedNode, bool deep, ExceptionCo ec = 0; if (!importedNode -#if ENABLE(SVG) +#if ENABLE(SVG) && ENABLE(DASHBOARD_SUPPORT) || (importedNode->isSVGElement() && page() && page()->settings()->usesDashboardBackwardCompatibilityMode()) #endif ) { @@ -691,7 +701,12 @@ PassRefPtr Document::adoptNode(PassRefPtr source, ExceptionCode& ec) ec = NOT_SUPPORTED_ERR; return 0; } - + + if (source->isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return 0; + } + switch (source->nodeType()) { case ENTITY_NODE: case NOTATION_NODE: @@ -704,7 +719,7 @@ PassRefPtr Document::adoptNode(PassRefPtr source, ExceptionCode& ec) Attr* attr = static_cast(source.get()); if (attr->ownerElement()) attr->ownerElement()->removeAttributeNode(attr, ec); - attr->m_attrWasSpecifiedOrElementHasRareData = true; + attr->setSpecified(true); break; } default: @@ -718,6 +733,28 @@ PassRefPtr Document::adoptNode(PassRefPtr source, ExceptionCode& ec) return source; } +bool Document::hasPrefixNamespaceMismatch(const QualifiedName& qName) +{ + static const AtomicString xmlnsNamespaceURI("http://www.w3.org/2000/xmlns/"); + static const AtomicString xmlns("xmlns"); + static const AtomicString xml("xml"); + + // These checks are from DOM Core Level 2, createElementNS + // http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrElNS + if (!qName.prefix().isEmpty() && qName.namespaceURI().isNull()) // createElementNS(null, "html:div") + return true; + if (qName.prefix() == xml && qName.namespaceURI() != XMLNames::xmlNamespaceURI) // createElementNS("http://www.example.com", "xml:lang") + return true; + + // Required by DOM Level 3 Core and unspecified by DOM Level 2 Core: + // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS + // createElementNS("http://www.w3.org/2000/xmlns/", "foo:bar"), createElementNS(null, "xmlns:bar") + if ((qName.prefix() == xmlns && qName.namespaceURI() != xmlnsNamespaceURI) || (qName.prefix() != xmlns && qName.namespaceURI() == xmlnsNamespaceURI)) + return true; + + return false; +} + // FIXME: This should really be in a possible ElementFactory class PassRefPtr Document::createElement(const QualifiedName& qName, bool createdByParser, ExceptionCode& ec) { @@ -734,36 +771,42 @@ PassRefPtr Document::createElement(const QualifiedName& qName, bool cre if (!e) e = new Element(qName, document()); + // FIXME: The element factories should be fixed to not ignore qName.prefix() + // Instead they should pass the entire qName into element creation so we don't + // need to manually set the prefix after creation. + // Then this code can become ASSERT(qName == e.qname()); + // and Document::createElement can stop taking ExceptionCode& as well. if (e && !qName.prefix().isNull()) { ec = 0; e->setPrefix(qName.prefix(), ec); if (ec) return 0; - } + } return e.release(); } -PassRefPtr Document::createElementNS(const String &_namespaceURI, const String &qualifiedName, ExceptionCode& ec) +PassRefPtr Document::createElementNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec) { String prefix, localName; - if (!parseQualifiedName(qualifiedName, prefix, localName)) { - ec = INVALID_CHARACTER_ERR; + if (!parseQualifiedName(qualifiedName, prefix, localName, ec)) + return 0; + + QualifiedName qName(prefix, localName, namespaceURI); + if (hasPrefixNamespaceMismatch(qName)) { + ec = NAMESPACE_ERR; return 0; } - RefPtr e; - QualifiedName qName = QualifiedName(AtomicString(prefix), AtomicString(localName), AtomicString(_namespaceURI)); - return createElement(qName, false, ec); } -Element *Document::getElementById(const AtomicString& elementId) const +Element* Document::getElementById(const AtomicString& elementId) const { - if (elementId.length() == 0) + if (elementId.isEmpty()) return 0; - Element *element = m_elementsById.get(elementId.impl()); + Element* element = m_elementsById.get(elementId.impl()); if (element) return element; @@ -821,12 +864,13 @@ void Document::setCharset(const String& charset) void Document::setXMLVersion(const String& version, ExceptionCode& ec) { - // FIXME: also raise NOT_SUPPORTED_ERR if the version is set to a value that is not supported by this Document. if (!implementation()->hasFeature("XML", String())) { ec = NOT_SUPPORTED_ERR; return; } + // FIXME: Also raise NOT_SUPPORTED_ERR if the version is set to a value that is not supported by this Document. + m_xmlVersion = version; } @@ -840,19 +884,15 @@ void Document::setXMLStandalone(bool standalone, ExceptionCode& ec) m_xmlStandalone = standalone; } -String Document::documentURI() const -{ - return m_baseURL; -} - -void Document::setDocumentURI(const String &uri) +void Document::setDocumentURI(const String& uri) { - m_baseURL = uri.deprecatedString(); + m_documentURI = uri; + updateBaseURL(); } -String Document::baseURI() const +KURL Document::baseURI() const { - return documentURI(); + return m_baseURL; } Element* Document::elementFromPoint(int x, int y) const @@ -1014,27 +1054,27 @@ Settings* Document::settings() const PassRefPtr Document::createRange() { - return new Range(this); + return Range::create(this); } PassRefPtr Document::createNodeIterator(Node* root, unsigned whatToShow, - NodeFilter* filter, bool expandEntityReferences, ExceptionCode& ec) + PassRefPtr filter, bool expandEntityReferences, ExceptionCode& ec) { if (!root) { ec = NOT_SUPPORTED_ERR; return 0; } - return new NodeIterator(root, whatToShow, filter, expandEntityReferences); + return NodeIterator::create(root, whatToShow, filter, expandEntityReferences); } PassRefPtr Document::createTreeWalker(Node *root, unsigned whatToShow, - NodeFilter* filter, bool expandEntityReferences, ExceptionCode& ec) + PassRefPtr filter, bool expandEntityReferences, ExceptionCode& ec) { if (!root) { ec = NOT_SUPPORTED_ERR; return 0; } - return new TreeWalker(root, whatToShow, filter, expandEntityReferences); + return TreeWalker::create(root, whatToShow, filter, expandEntityReferences); } void Document::setDocumentChanged(bool b) @@ -1042,8 +1082,8 @@ void Document::setDocumentChanged(bool b) if (b) { if (!m_docChanged) { if (!changedDocuments) - changedDocuments = new DeprecatedPtrList; - changedDocuments->append(this); + changedDocuments = new HashSet; + changedDocuments->add(this); } if (m_accessKeyMapValid) { m_accessKeyMapValid = false; @@ -1069,7 +1109,7 @@ void Frame::resetCalculateStyleTimeCounter() void Frame::reportCalculateStyleTimeCounter() { - LOG(LOG_DEBUG, "WebCore", "*-* Total calcStyle time: %d ms called %d times\n", + LOGD("*-* Total calcStyle time: %d ms called %d times\n", sTotalTimeUsed, sCounter); } #endif @@ -1077,8 +1117,8 @@ void Frame::reportCalculateStyleTimeCounter() void Document::recalcStyle(StyleChange change) { // we should not enter style recalc while painting - if (frame() && frame()->isPainting()) { - ASSERT(!frame()->isPainting()); + if (frame() && frame()->view() && frame()->view()->isPainting()) { + ASSERT(!frame()->view()->isPainting()); return; } @@ -1100,44 +1140,37 @@ void Document::recalcStyle(StyleChange change) // style selector may set this again during recalc m_hasNodesWithPlaceholderStyle = false; - RenderStyle* oldStyle = renderer()->style(); - if (oldStyle) - oldStyle->ref(); - RenderStyle* _style = new (m_renderArena) RenderStyle(); - _style->ref(); - _style->setDisplay(BLOCK); - _style->setVisuallyOrdered(visuallyOrdered); - // ### make the font stuff _really_ work!!!! - + RefPtr documentStyle = RenderStyle::create(); + documentStyle->setDisplay(BLOCK); + documentStyle->setVisuallyOrdered(visuallyOrdered); + documentStyle->setZoom(frame()->pageZoomFactor()); + m_styleSelector->setStyle(documentStyle); + FontDescription fontDescription; fontDescription.setUsePrinterFont(printing()); if (Settings* settings = this->settings()) { fontDescription.setRenderingMode(settings->fontRenderingMode()); if (printing() && !settings->shouldPrintBackgrounds()) - _style->setForceBackgroundsToWhite(true); + documentStyle->setForceBackgroundsToWhite(true); const AtomicString& stdfont = settings->standardFontFamily(); if (!stdfont.isEmpty()) { fontDescription.firstFamily().setFamily(stdfont); fontDescription.firstFamily().appendFamily(0); } - fontDescription.setKeywordSize(CSS_VAL_MEDIUM - CSS_VAL_XX_SMALL + 1); - m_styleSelector->setFontSize(fontDescription, m_styleSelector->fontSizeForKeyword(CSS_VAL_MEDIUM, inCompatMode(), false)); + fontDescription.setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1); + m_styleSelector->setFontSize(fontDescription, m_styleSelector->fontSizeForKeyword(CSSValueMedium, inCompatMode(), false)); } - _style->setFontDescription(fontDescription); - _style->font().update(m_styleSelector->fontSelector()); + documentStyle->setFontDescription(fontDescription); + documentStyle->font().update(m_styleSelector->fontSelector()); if (inCompatMode()) - _style->setHtmlHacks(true); // enable html specific rendering tricks + documentStyle->setHtmlHacks(true); // enable html specific rendering tricks - StyleChange ch = diff(_style, oldStyle); + StyleChange ch = diff(documentStyle.get(), renderer()->style()); if (renderer() && ch != NoChange) - renderer()->setStyle(_style); + renderer()->setStyle(documentStyle.release()); if (change != Force) change = ch; - - _style->deref(m_renderArena); - if (oldStyle) - oldStyle->deref(m_renderArena); } for (Node* n = firstChild(); n; n = n->nextSibling()) @@ -1172,8 +1205,12 @@ bail_out: void Document::updateRendering() { - if (hasChangedChild()) + if (hasChangedChild() && !inPageCache()) recalcStyle(NoChange); + + // Tell the animation controller that the style is available and it can start animations + if (m_frame) + m_frame->animation()->styleAvailable(); } void Document::updateDocumentsRendering() @@ -1181,7 +1218,11 @@ void Document::updateDocumentsRendering() if (!changedDocuments) return; - while (Document* doc = changedDocuments->take()) { + while (changedDocuments->size()) { + HashSet::iterator it = changedDocuments->begin(); + Document* doc = *it; + changedDocuments->remove(it); + doc->m_docChanged = false; doc->updateRendering(); } @@ -1238,6 +1279,7 @@ void Document::attach() { ASSERT(!attached()); ASSERT(!m_inPageCache); + ASSERT(!m_axObjectCache); if (!m_renderArena) m_renderArena = new RenderArena(); @@ -1245,6 +1287,13 @@ void Document::attach() // Create the rendering tree setRenderer(new (m_renderArena) RenderView(this, view())); + if (!m_styleSelector) { + bool matchAuthorAndUserStyles = true; + if (Settings* docSettings = settings()) + matchAuthorAndUserStyles = docSettings->authorAndUserStylesEnabled(); + m_styleSelector = new CSSStyleSelector(this, userStyleSheet(), m_styleSheets.get(), m_mappedElementSheet.get(), !inCompatMode(), matchAuthorAndUserStyles); + } + recalcStyle(Force); RenderObject* render = renderer(); @@ -1260,6 +1309,8 @@ void Document::detach() ASSERT(attached()); ASSERT(!m_inPageCache); + clearAXObjectCache(); + RenderObject* render = renderer(); // indicate destruction mode, i.e. attached() but renderer == 0 @@ -1279,9 +1330,12 @@ void Document::detach() if (render) render->destroy(); - - // FIXME: is this needed or desirable? - m_frame = 0; + + // This is required, as our Frame might delete itself as soon as it detaches + // us. However, this violates Node::detach() symantics, as it's never + // possible to re-attach. Eventually Document::detach() should be renamed + // or this call made explicit in each of the callers of Document::detach(). + clearFramePointer(); if (m_renderArena) { delete m_renderArena; @@ -1289,6 +1343,11 @@ void Document::detach() } } +void Document::clearFramePointer() +{ + m_frame = 0; +} + void Document::removeAllEventListenersFromAllNodes() { m_windowEventListeners.clear(); @@ -1318,6 +1377,21 @@ void Document::removeAllDisconnectedNodeEventListeners() m_disconnectedNodesWithEventListeners.clear(); } +void Document::clearAXObjectCache() +{ + // clear cache in top document + if (m_axObjectCache) { + delete m_axObjectCache; + m_axObjectCache = 0; + return; + } + + // ask the top-level document to clear its cache + Document* doc = topDocument(); + if (doc != this) + doc->clearAXObjectCache(); +} + AXObjectCache* Document::axObjectCache() const { // The only document that actually has a AXObjectCache is the top-level @@ -1333,6 +1407,14 @@ AXObjectCache* Document::axObjectCache() const // In some pages with frames, the cache is created before the sub-webarea is // inserted into the tree. Here, we catch that case and just toss the old // cache and start over. + // NOTE: This recovery may no longer be needed. I have been unable to trigger + // it again. See rdar://5794454 + // FIXME: Can this be fixed when inserting the subframe instead of now? + // FIXME: If this function was called to get the cache in order to remove + // an AXObject, we are now deleting the cache as a whole and returning a + // new empty cache that does not contain the AXObject! That should actually + // be OK. I am concerned about other cases like this where accessing the + // cache blows away the AXObject being operated on. delete m_axObjectCache; m_axObjectCache = 0; } @@ -1360,15 +1442,12 @@ Tokenizer* Document::createTokenizer() return new XMLTokenizer(this, view()); } -void Document::open() +void Document::open(Document* ownerDocument) { - // This is work that we should probably do in clear(), but we can't have it - // happen when implicitOpen() is called unless we reorganize Frame code. - if (Document *parent = parentDocument()) { - if (m_url.isEmpty() || m_url == "about:blank") - setURL(parent->baseURL()); - if (m_baseURL.isEmpty() || m_baseURL == "about:blank") - setBaseURL(parent->baseURL()); + if (ownerDocument) { + setURL(ownerDocument->url()); + m_cookieURL = ownerDocument->cookieURL(); + m_securityOrigin = ownerDocument->securityOrigin(); } if (m_frame) { @@ -1419,7 +1498,7 @@ HTMLElement* Document::body() if (i->hasTagName(framesetTag)) return static_cast(i); - if (i->hasTagName(bodyTag)) + if (i->hasTagName(bodyTag) && !body) body = i; } return static_cast(body); @@ -1427,7 +1506,7 @@ HTMLElement* Document::body() void Document::setBody(PassRefPtr newBody, ExceptionCode& ec) { - if (!newBody) { + if (!newBody || !documentElement()) { ec = HIERARCHY_REQUEST_ERR; return; } @@ -1491,10 +1570,8 @@ void Document::implicitClose() delete m_tokenizer; m_tokenizer = 0; -#ifdef ANDROID_PRELOAD_CHANGES // Parser should have picked up all preloads by now m_docLoader->clearPreloads(); -#endif // Create a body element if we don't already have one. See Radar 3758785. if (!this->body() && isHTMLDocument()) { @@ -1504,10 +1581,24 @@ void Document::implicitClose() ASSERT(!ec); } } - + + // FIXME: We kick off the icon loader when the Document is done parsing. + // There are earlier opportunities we could start it: + // -When the finishes parsing + // -When any new HTMLLinkElement is inserted into the document + // But those add a dynamic component to the favicon that has UI + // ramifications, and we need to decide what is the Right Thing To Do(tm) + Frame* f = frame(); + if (f) + f->loader()->startIconLoader(); + + // Resume the animations (or start them) + if (f) + f->animation()->resumeAnimations(this); + dispatchImageLoadEventsNow(); - this->dispatchWindowEvent(loadEvent, false, false); - if (Frame* f = frame()) + this->dispatchWindowEvent(eventNames().loadEvent, false, false); + if (f) f->loader()->handledOnloadEvents(); #ifdef INSTRUMENT_LAYOUT_SCHEDULING if (!ownerElement()) @@ -1545,17 +1636,17 @@ void Document::implicitClose() // Paint immediately after the document is ready. We do this to ensure that any timers set by the // onload don't have a chance to fire before we would have painted. To avoid over-flushing we only - // worry about this for the top-level document. -#if !PLATFORM(MAC) - // FIXME: This causes a timing issue with the dispatchDidFinishLoad delegate callback. + // worry about this for the top-level document. For platforms that use native widgets for ScrollViews, this + // call does nothing (Mac, wx). + // FIXME: This causes a timing issue with the dispatchDidFinishLoad delegate callback on Mac, so think + // before enabling it, even if Mac becomes viewless later. // See if (view() && !ownerElement()) - view()->update(); -#endif + view()->hostWindow()->paint(); } #if PLATFORM(MAC) - if (renderer() && AXObjectCache::accessibilityEnabled()) + if (f && renderer() && this == topDocument() && AXObjectCache::accessibilityEnabled()) axObjectCache()->postNotificationToElement(renderer(), "AXLoadComplete"); #endif @@ -1608,7 +1699,7 @@ int Document::elapsedTime() const return static_cast((currentTime() - m_startTime) * 1000); } -void Document::write(const String& text) +void Document::write(const String& text, Document* ownerDocument) { #ifdef INSTRUMENT_LAYOUT_SCHEDULING if (!ownerElement()) @@ -1616,11 +1707,11 @@ void Document::write(const String& text) #endif if (!m_tokenizer) { - open(); + open(ownerDocument); ASSERT(m_tokenizer); if (!m_tokenizer) return; - write(""); + write("", ownerDocument); } m_tokenizer->write(text, false); @@ -1630,10 +1721,10 @@ void Document::write(const String& text) #endif } -void Document::writeln(const String& text) +void Document::writeln(const String& text, Document* ownerDocument) { - write(text); - write("\n"); + write(text, ownerDocument); + write("\n", ownerDocument); } void Document::finishParsing() @@ -1662,48 +1753,44 @@ void Document::clear() m_windowEventListeners.clear(); } -void Document::setURL(const DeprecatedString& url) +const KURL& Document::virtualURL() const { - if (url == m_url) - return; - - m_url = url; - if (m_styleSelector) - m_styleSelector->setEncodedURL(m_url); + return m_url; +} - m_isAllowedToLoadLocalResources = shouldBeAllowedToLoadLocalResources(); - } - -bool Document::shouldBeAllowedToLoadLocalResources() const +void Document::setURL(const KURL& url) { - if (FrameLoader::shouldTreatURLAsLocal(m_url)) - return true; - - Frame* frame = this->frame(); - if (!frame) - return false; - - DocumentLoader* documentLoader = frame->loader()->documentLoader(); - if (!documentLoader) - return false; + const KURL& newURL = url.isEmpty() ? blankURL() : url; + if (newURL == m_url) + return; - if (m_url == "about:blank" && frame->loader()->opener() && frame->loader()->opener()->document()->isAllowedToLoadLocalResources()) - return true; - - return documentLoader->substituteData().isValid(); + m_url = newURL; + m_documentURI = m_url.string(); + updateBaseURL(); } -void Document::setBaseURL(const DeprecatedString& baseURL) +void Document::setBaseElementURL(const KURL& baseElementURL) { - m_baseURL = baseURL; + m_baseElementURL = baseElementURL; + updateBaseURL(); +} + +void Document::updateBaseURL() +{ + m_baseURL = m_baseElementURL.isEmpty() ? KURL(documentURI()) : m_baseElementURL; + if (!m_baseURL.isValid()) + m_baseURL = KURL(); + if (m_elemSheet) - m_elemSheet->setHref(m_baseURL); + m_elemSheet->setHref(m_baseURL.string()); + if (m_mappedElementSheet) + m_mappedElementSheet->setHref(m_baseURL.string()); } -void Document::setCSSStyleSheet(const String &url, const String& charset, const String &sheet) +void Document::setCSSStyleSheet(const String& url, const String& charset, const CachedCSSStyleSheet* sheet) { - m_sheet = new CSSStyleSheet(this, url, charset); - m_sheet->parseString(sheet); + m_sheet = CSSStyleSheet::create(this, url, charset); + m_sheet->parseString(sheet->sheetText()); updateStyleSelector(); } @@ -1733,25 +1820,17 @@ String Document::userStyleSheet() const CSSStyleSheet* Document::elementSheet() { if (!m_elemSheet) - m_elemSheet = new CSSStyleSheet(this, baseURL()); + m_elemSheet = CSSStyleSheet::create(this, m_baseURL.string()); return m_elemSheet.get(); } CSSStyleSheet* Document::mappedElementSheet() { if (!m_mappedElementSheet) - m_mappedElementSheet = new CSSStyleSheet(this, baseURL()); + m_mappedElementSheet = CSSStyleSheet::create(this, m_baseURL.string()); return m_mappedElementSheet.get(); } -void Document::determineParseMode(const String&) -{ - // For XML documents use strict parse mode. - // HTML overrides this method to determine the parse mode. - pMode = Strict; - hMode = XHtml; -} - static Node* nextNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event) { // Search is inclusive of start @@ -1803,6 +1882,13 @@ static Node* previousNodeWithLowerTabIndex(Node* start, int tabIndex, KeyboardEv Node* Document::nextFocusableNode(Node* start, KeyboardEvent* event) { if (start) { + // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order + if (start->tabIndex() < 0) { + for (Node* n = start->traverseNextNode(); n; n = n->traverseNextNode()) + if (n->isKeyboardFocusable(event) && n->tabIndex() >= 0) + return n; + } + // First try to find a node with the same tabindex as start that comes after start in the document. if (Node* winner = nextNodeWithExactTabIndex(start->traverseNextNode(), start->tabIndex(), event)) return winner; @@ -1840,6 +1926,13 @@ Node* Document::previousFocusableNode(Node* start, KeyboardEvent* event) startingNode = last; startingTabIndex = 0; } + + // However, if a node is excluded from the normal tabbing cycle, the previous focusable node is determined by tree order + if (startingTabIndex < 0) { + for (Node* n = startingNode; n; n = n->traversePreviousNode()) + if (n->isKeyboardFocusable(event) && n->tabIndex() >= 0) + return n; + } if (Node* winner = previousNodeWithExactTabIndex(startingNode, startingTabIndex, event)) return winner; @@ -1874,7 +1967,7 @@ Node *Document::nodeWithAbsIndex(int absIndex) // Though isspace() considers \t and \v to be whitespace, Win IE doesn't. static bool isSeparator(::UChar c) { - return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0'; + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == ';' || c == '\0'; } void Document::processMetadataSettings(const String& content) @@ -1899,16 +1992,16 @@ void Document::processMetadataSettings(const String& content) i++; keyEnd = i; - // skip to first '=', but don't skip past a ',' or the end of the string + // skip to first '=', but don't skip past a ',', ';' or the end of the string while (buffer[i] != '=') { - if (buffer[i] == ',' || i >= length) + if (buffer[i] == ',' || buffer[i] == ';' || i >= length) break; i++; } - // skip to first non-separator, but don't skip past a ',' or the end of the string + // skip to first non-separator, but don't skip past a ',', ';' or the end of the string while (isSeparator(buffer[i])) { - if (buffer[i] == ',' || i >= length) + if (buffer[i] == ',' || buffer[i] == ';' || i >= length) break; i++; } @@ -1951,7 +2044,7 @@ void Document::processHttpEquiv(const String &equiv, const String &content) if (url.isEmpty()) url = frame->loader()->url().string(); else - url = completeURL(url); + url = completeURL(url).string(); frame->loader()->scheduleHTTPRedirection(delay, url); } } else if (equalIgnoringCase(equiv, "set-cookie")) { @@ -1960,6 +2053,8 @@ void Document::processHttpEquiv(const String &equiv, const String &content) static_cast(this)->setCookie(content); } else if (equalIgnoringCase(equiv, "content-language")) setContentLanguage(content); + else if (equalIgnoringCase(equiv, "x-dns-prefetch-control")) + parseDNSPrefetchControlHeader(content); } MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& request, const IntPoint& documentPoint, const PlatformMouseEvent& event) @@ -2177,20 +2272,59 @@ void Document::updateStyleSelector() } } +void Document::addStyleSheetCandidateNode(Node* node, bool createdByParser) +{ + // Until the exists, we have no choice but to compare document positions, + // since styles outside of the body and head continue to be shunted into the head + // (and thus can shift to end up before dynamically added DOM content that is also + // outside the body). + if ((createdByParser && body()) || m_styleSheetCandidateNodes.isEmpty()) { + m_styleSheetCandidateNodes.add(node); + return; + } + + // Determine an appropriate insertion point. + ListHashSet::iterator begin = m_styleSheetCandidateNodes.begin(); + ListHashSet::iterator end = m_styleSheetCandidateNodes.end(); + ListHashSet::iterator it = end; + Node* followingNode = 0; + do { + --it; + Node* n = *it; + unsigned short position = n->compareDocumentPosition(node); + if (position == DOCUMENT_POSITION_FOLLOWING) { + m_styleSheetCandidateNodes.insertBefore(followingNode, node); + return; + } + followingNode = n; + } while (it != begin); + + m_styleSheetCandidateNodes.insertBefore(followingNode, node); +} + +void Document::removeStyleSheetCandidateNode(Node* node) +{ + m_styleSheetCandidateNodes.remove(node); +} + void Document::recalcStyleSelector() { if (!renderer() || !attached()) return; - DeprecatedPtrList oldStyleSheets = m_styleSheets->styleSheets; - m_styleSheets->styleSheets.clear(); + StyleSheetVector sheets; bool matchAuthorAndUserStyles = true; if (Settings* settings = this->settings()) matchAuthorAndUserStyles = settings->authorAndUserStylesEnabled(); - Node* n = matchAuthorAndUserStyles ? this : 0; - for ( ; n; n = n->traverseNextNode()) { + ListHashSet::iterator begin = m_styleSheetCandidateNodes.begin(); + ListHashSet::iterator end = m_styleSheetCandidateNodes.end(); + if (!matchAuthorAndUserStyles) + end = begin; + for (ListHashSet::iterator it = begin; it != end; ++it) { + Node* n = *it; + StyleSheet* sheet = 0; if (n->nodeType() == PROCESSING_INSTRUCTION_NODE) { @@ -2219,10 +2353,10 @@ void Document::recalcStyleSelector() sheetText += c->nodeValue(); } - CSSStyleSheet* cssSheet = new CSSStyleSheet(this); + RefPtr cssSheet = CSSStyleSheet::create(this); cssSheet->parseString(sheetText); pi->setCSSStyleSheet(cssSheet); - sheet = cssSheet; + sheet = cssSheet.get(); } } } else if (n->isHTMLElement() && (n->hasTagName(linkTag) || n->hasTagName(styleTag)) @@ -2243,7 +2377,7 @@ void Document::recalcStyleSelector() // it is loading but we should still decide which style sheet set to use if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSet.isEmpty()) { const AtomicString& rel = e->getAttribute(relAttr); - if (!rel.domString().contains("alternate")) { + if (!rel.contains("alternate")) { m_preferredStylesheetSet = title; m_selectedStylesheetSet = title; } @@ -2284,34 +2418,18 @@ void Document::recalcStyleSelector() if (title != m_preferredStylesheetSet) sheet = 0; - -#if ENABLE(SVG) - if (!n->isHTMLElement()) - title = title.deprecatedString().replace('&', "&&"); -#endif } } - if (sheet) { - sheet->ref(); - m_styleSheets->styleSheets.append(sheet); - } - - // For HTML documents, stylesheets are not allowed within/after the tag. So we - // can stop searching here. - if (isHTMLDocument() && n->hasTagName(bodyTag)) - break; + if (sheet) + sheets.append(sheet); } - // De-reference all the stylesheets in the old list - DeprecatedPtrListIterator it(oldStyleSheets); - for (; it.current(); ++it) - it.current()->deref(); + m_styleSheets->swap(sheets); // Create a new style selector delete m_styleSelector; m_styleSelector = new CSSStyleSelector(this, userStyleSheet(), m_styleSheets.get(), m_mappedElementSheet.get(), !inCompatMode(), matchAuthorAndUserStyles); - m_styleSelector->setEncodedURL(m_url); m_didCalculateStyleSelector = true; } @@ -2367,6 +2485,7 @@ void Document::activeChainNodeDetached(Node* node) m_activeNode = m_activeNode->parent(); } +#if ENABLE(DASHBOARD_SUPPORT) const Vector& Document::dashboardRegions() const { return m_dashboardRegions; @@ -2377,6 +2496,7 @@ void Document::setDashboardRegions(const Vector& regions) m_dashboardRegions = regions; setDashboardRegionsDirty(false); } +#endif bool Document::setFocusedNode(PassRefPtr newFocusedNode) { @@ -2391,8 +2511,7 @@ bool Document::setFocusedNode(PassRefPtr newFocusedNode) return false; #ifdef ANDROID_RESET_SELECTION - WebCore::FrameAndroid* frameAndroid = static_cast(frame()); - WebCore::Node* oldFocus = frameAndroid->getCacheBuilder().currentFocus(); + WebCore::Node* oldFocus = frame()->getCacheBuilder().currentFocus(); if (oldFocus) { if (oldFocus->hasTagName(WebCore::HTMLNames::inputTag)) { WebCore::HTMLInputElement* input = static_cast(oldFocus); @@ -2416,9 +2535,9 @@ bool Document::setFocusedNode(PassRefPtr newFocusedNode) oldFocusedNode->setFocus(false); // Dispatch a change event for text fields or textareas that have been edited - RenderObject *r = static_cast(oldFocusedNode.get()->renderer()); + RenderObject* r = static_cast(oldFocusedNode.get()->renderer()); if (r && (r->isTextArea() || r->isTextField()) && r->isEdited()) { - EventTargetNodeCast(oldFocusedNode.get())->dispatchHTMLEvent(changeEvent, true, false); + EventTargetNodeCast(oldFocusedNode.get())->dispatchEventForType(eventNames().changeEvent, true, false); if ((r = static_cast(oldFocusedNode.get()->renderer()))) r->setEdited(false); } @@ -2431,7 +2550,7 @@ bool Document::setFocusedNode(PassRefPtr newFocusedNode) focusChangeBlocked = true; newFocusedNode = 0; } - EventTargetNodeCast(oldFocusedNode.get())->dispatchUIEvent(DOMFocusOutEvent); + EventTargetNodeCast(oldFocusedNode.get())->dispatchUIEvent(eventNames().DOMFocusOutEvent); if (m_focusedNode) { // handler shifted focus focusChangeBlocked = true; @@ -2461,7 +2580,7 @@ bool Document::setFocusedNode(PassRefPtr newFocusedNode) focusChangeBlocked = true; goto SetFocusedNodeDone; } - EventTargetNodeCast(m_focusedNode.get())->dispatchUIEvent(DOMFocusInEvent); + EventTargetNodeCast(m_focusedNode.get())->dispatchUIEvent(eventNames().DOMFocusInEvent); if (m_focusedNode != newFocusedNode) { // handler shifted focus focusChangeBlocked = true; @@ -2525,62 +2644,163 @@ void Document::detachNodeIterator(NodeIterator *ni) m_nodeIterators.remove(ni); } -void Document::notifyBeforeNodeRemoval(Node *n) +void Document::nodeChildrenChanged(ContainerNode* container) { - if (Frame* f = frame()) { - f->selectionController()->nodeWillBeRemoved(n); - f->dragCaretController()->nodeWillBeRemoved(n); + if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) { + HashSet::const_iterator end = m_ranges.end(); + for (HashSet::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->nodeChildrenChanged(container); } +} + +void Document::nodeWillBeRemoved(Node* n) +{ + HashSet::const_iterator nodeIteratorsEnd = m_nodeIterators.end(); + for (HashSet::const_iterator it = m_nodeIterators.begin(); it != nodeIteratorsEnd; ++it) + (*it)->nodeWillBeRemoved(n); - HashSet::const_iterator end = m_nodeIterators.end(); - for (HashSet::const_iterator it = m_nodeIterators.begin(); it != end; ++it) - (*it)->notifyBeforeNodeRemoval(n); + if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) { + HashSet::const_iterator rangesEnd = m_ranges.end(); + for (HashSet::const_iterator it = m_ranges.begin(); it != rangesEnd; ++it) + (*it)->nodeWillBeRemoved(n); + } + + if (Frame* frame = this->frame()) { + frame->selection()->nodeWillBeRemoved(n); + frame->dragCaretController()->nodeWillBeRemoved(n); + } } -DOMWindow* Document::defaultView() const +void Document::textInserted(Node* text, unsigned offset, unsigned length) +{ + if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) { + HashSet::const_iterator end = m_ranges.end(); + for (HashSet::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->textInserted(text, offset, length); + } + + // Update the markers for spelling and grammar checking. + shiftMarkers(text, offset, length); +} + +void Document::textRemoved(Node* text, unsigned offset, unsigned length) +{ + if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) { + HashSet::const_iterator end = m_ranges.end(); + for (HashSet::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->textRemoved(text, offset, length); + } + + // Update the markers for spelling and grammar checking. + removeMarkers(text, offset, length); + shiftMarkers(text, offset + length, 0 - length); +} + +void Document::textNodesMerged(Text* oldNode, unsigned offset) +{ + if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) { + NodeWithIndex oldNodeWithIndex(oldNode); + HashSet::const_iterator end = m_ranges.end(); + for (HashSet::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->textNodesMerged(oldNodeWithIndex, offset); + } + + // FIXME: This should update markers for spelling and grammar checking. +} + +void Document::textNodeSplit(Text* oldNode) +{ + if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) { + HashSet::const_iterator end = m_ranges.end(); + for (HashSet::const_iterator it = m_ranges.begin(); it != end; ++it) + (*it)->textNodeSplit(oldNode); + } + + // FIXME: This should update markers for spelling and grammar checking. +} + +// FIXME: eventually, this should return a DOMWindow stored in the document. +DOMWindow* Document::domWindow() const { if (!frame()) return 0; - return frame()->domWindow(); } -PassRefPtr Document::createEvent(const String &eventType, ExceptionCode& ec) +PassRefPtr Document::createEvent(const String& eventType, ExceptionCode& ec) { if (eventType == "UIEvents" || eventType == "UIEvent") - return new UIEvent; + return UIEvent::create(); if (eventType == "MouseEvents" || eventType == "MouseEvent") - return new MouseEvent; + return MouseEvent::create(); if (eventType == "MutationEvents" || eventType == "MutationEvent") - return new MutationEvent; + return MutationEvent::create(); if (eventType == "KeyboardEvents" || eventType == "KeyboardEvent") - return new KeyboardEvent; + return KeyboardEvent::create(); if (eventType == "HTMLEvents" || eventType == "Event" || eventType == "Events") - return new Event; + return Event::create(); if (eventType == "ProgressEvent") - return new ProgressEvent; + return ProgressEvent::create(); if (eventType == "TextEvent") - return new TextEvent; + return TextEvent::create(); if (eventType == "OverflowEvent") - return new OverflowEvent; + return OverflowEvent::create(); if (eventType == "WheelEvent") - return new WheelEvent; + return WheelEvent::create(); #if ENABLE(SVG) if (eventType == "SVGEvents") - return new Event; + return Event::create(); if (eventType == "SVGZoomEvents") - return new SVGZoomEvent; + return SVGZoomEvent::create(); +#endif + if (eventType == "MessageEvent") + return MessageEvent::create(); + if (eventType == "WebKitAnimationEvent") + return WebKitAnimationEvent::create(); + if (eventType == "WebKitTransitionEvent") + return WebKitTransitionEvent::create(); +#if ENABLE(TOUCH_EVENTS) // Android + if (eventType == "TouchEvent") + return TouchEvent::create(); #endif ec = NOT_SUPPORTED_ERR; return 0; } -CSSStyleDeclaration *Document::getOverrideStyle(Element */*elt*/, const String &/*pseudoElt*/) +void Document::addListenerTypeIfNeeded(const AtomicString& eventType) +{ + if (eventType == eventNames().DOMSubtreeModifiedEvent) + addListenerType(DOMSUBTREEMODIFIED_LISTENER); + else if (eventType == eventNames().DOMNodeInsertedEvent) + addListenerType(DOMNODEINSERTED_LISTENER); + else if (eventType == eventNames().DOMNodeRemovedEvent) + addListenerType(DOMNODEREMOVED_LISTENER); + else if (eventType == eventNames().DOMNodeRemovedFromDocumentEvent) + addListenerType(DOMNODEREMOVEDFROMDOCUMENT_LISTENER); + else if (eventType == eventNames().DOMNodeInsertedIntoDocumentEvent) + addListenerType(DOMNODEINSERTEDINTODOCUMENT_LISTENER); + else if (eventType == eventNames().DOMAttrModifiedEvent) + addListenerType(DOMATTRMODIFIED_LISTENER); + else if (eventType == eventNames().DOMCharacterDataModifiedEvent) + addListenerType(DOMCHARACTERDATAMODIFIED_LISTENER); + else if (eventType == eventNames().overflowchangedEvent) + addListenerType(OVERFLOWCHANGED_LISTENER); + else if (eventType == eventNames().webkitAnimationStartEvent) + addListenerType(ANIMATIONSTART_LISTENER); + else if (eventType == eventNames().webkitAnimationEndEvent) + addListenerType(ANIMATIONEND_LISTENER); + else if (eventType == eventNames().webkitAnimationIterationEvent) + addListenerType(ANIMATIONITERATION_LISTENER); + else if (eventType == eventNames().webkitTransitionEndEvent) + addListenerType(TRANSITIONEND_LISTENER); +} + +CSSStyleDeclaration* Document::getOverrideStyle(Element*, const String&) { - return 0; // ### + return 0; } -void Document::handleWindowEvent(Event *evt, bool useCapture) +void Document::handleWindowEvent(Event* evt, bool useCapture) { if (m_windowEventListeners.isEmpty()) return; @@ -2594,84 +2814,128 @@ void Document::handleWindowEvent(Event *evt, bool useCapture) (*it)->listener()->handleEvent(evt, true); } -void Document::setHTMLWindowEventListener(const AtomicString &eventType, PassRefPtr listener) +void Document::setWindowInlineEventListenerForType(const AtomicString& eventType, PassRefPtr listener) { // If we already have it we don't want removeWindowEventListener to delete it - removeHTMLWindowEventListener(eventType); + removeWindowInlineEventListenerForType(eventType); if (listener) addWindowEventListener(eventType, listener, false); } -EventListener *Document::getHTMLWindowEventListener(const AtomicString &eventType) +EventListener* Document::windowInlineEventListenerForType(const AtomicString& eventType) { RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); - for (; it != m_windowEventListeners.end(); ++it) - if ( (*it)->eventType() == eventType && (*it)->listener()->isHTMLEventListener()) + for (; it != m_windowEventListeners.end(); ++it) { + if ((*it)->eventType() == eventType && (*it)->listener()->isInline()) return (*it)->listener(); + } return 0; } -void Document::removeHTMLWindowEventListener(const AtomicString &eventType) +void Document::removeWindowInlineEventListenerForType(const AtomicString& eventType) { RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); - for (; it != m_windowEventListeners.end(); ++it) - if ( (*it)->eventType() == eventType && (*it)->listener()->isHTMLEventListener()) { + for (; it != m_windowEventListeners.end(); ++it) { + if ((*it)->eventType() == eventType && (*it)->listener()->isInline()) { + if (eventType == eventNames().unloadEvent) + removePendingFrameUnloadEventCount(); + else if (eventType == eventNames().beforeunloadEvent) + removePendingFrameBeforeUnloadEventCount(); m_windowEventListeners.remove(it); return; } + } } -void Document::addWindowEventListener(const AtomicString &eventType, PassRefPtr listener, bool useCapture) +void Document::addWindowEventListener(const AtomicString& eventType, PassRefPtr listener, bool useCapture) { + if (eventType == eventNames().unloadEvent) + addPendingFrameUnloadEventCount(); + else if (eventType == eventNames().beforeunloadEvent) + addPendingFrameBeforeUnloadEventCount(); // Remove existing identical listener set with identical arguments. // The DOM 2 spec says that "duplicate instances are discarded" in this case. removeWindowEventListener(eventType, listener.get(), useCapture); - m_windowEventListeners.append(new RegisteredEventListener(eventType, listener, useCapture)); + m_windowEventListeners.append(RegisteredEventListener::create(eventType, listener, useCapture)); } -void Document::removeWindowEventListener(const AtomicString &eventType, EventListener *listener, bool useCapture) +void Document::removeWindowEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) { - RegisteredEventListener rl(eventType, listener, useCapture); RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); - for (; it != m_windowEventListeners.end(); ++it) - if (*(*it) == rl) { + for (; it != m_windowEventListeners.end(); ++it) { + RegisteredEventListener& r = **it; + if (r.eventType() == eventType && r.listener() == listener && r.useCapture() == useCapture) { + if (eventType == eventNames().unloadEvent) + removePendingFrameUnloadEventCount(); + else if (eventType == eventNames().beforeunloadEvent) + removePendingFrameBeforeUnloadEventCount(); m_windowEventListeners.remove(it); return; } + } } -bool Document::hasWindowEventListener(const AtomicString &eventType) +bool Document::hasWindowEventListener(const AtomicString& eventType) { RegisteredEventListenerList::iterator it = m_windowEventListeners.begin(); for (; it != m_windowEventListeners.end(); ++it) - if ((*it)->eventType() == eventType) { + if ((*it)->eventType() == eventType) return true; - } return false; } -PassRefPtr Document::createHTMLEventListener(const String& functionName, const String& code, Node *node) +void Document::addPendingFrameUnloadEventCount() { - if (Frame* frm = frame()) - if (frm->scriptProxy()->isEnabled()) - return frm->scriptProxy()->createHTMLEventHandler(functionName, code, node); - return 0; + if (m_frame) + m_frame->eventHandler()->addPendingFrameUnloadEventCount(); +} + +void Document::removePendingFrameUnloadEventCount() +{ + if (m_frame) + m_frame->eventHandler()->removePendingFrameUnloadEventCount(); +} + +void Document::addPendingFrameBeforeUnloadEventCount() +{ + if (m_frame) + m_frame->eventHandler()->addPendingFrameBeforeUnloadEventCount(); +} + +void Document::removePendingFrameBeforeUnloadEventCount() +{ + if (m_frame) + m_frame->eventHandler()->removePendingFrameBeforeUnloadEventCount(); } -void Document::setHTMLWindowEventListener(const AtomicString& eventType, Attribute* attr) +PassRefPtr Document::createEventListener(const String& functionName, const String& code, Node* node) { - setHTMLWindowEventListener(eventType, - createHTMLEventListener(attr->localName().domString(), attr->value(), 0)); + Frame* frm = frame(); + if (!frm || !frm->script()->isEnabled()) + return 0; + +#if ENABLE(SVG) + if (node ? node->isSVGElement() : isSVGDocument()) + return frm->script()->createSVGEventHandler(functionName, code, node); +#endif + + // We may want to treat compound document event handlers in a different way, in future. + return frm->script()->createInlineEventListener(functionName, code, node); } -void Document::dispatchImageLoadEventSoon(HTMLImageLoader *image) +void Document::setWindowInlineEventListenerForTypeAndAttribute(const AtomicString& eventType, Attribute* attr) +{ + setWindowInlineEventListenerForType(eventType, createEventListener(attr->localName().string(), attr->value(), 0)); +} + +void Document::dispatchImageLoadEventSoon(ImageLoader* image) { m_imageLoadEventDispatchSoonList.append(image); if (!m_imageLoadEventTimer.isActive()) m_imageLoadEventTimer.startOneShot(0); } -void Document::removeImage(HTMLImageLoader* image) +void Document::removeImage(ImageLoader* image) { // Remove instances of this image from both lists. // Use loops because we allow multiple instances to get into the lists. @@ -2686,19 +2950,18 @@ void Document::dispatchImageLoadEventsNow() // need to avoid re-entering this function; if new dispatches are // scheduled before the parent finishes processing the list, they // will set a timer and eventually be processed - if (!m_imageLoadEventDispatchingList.isEmpty()) { + if (!m_imageLoadEventDispatchingList.isEmpty()) return; - } m_imageLoadEventTimer.stop(); m_imageLoadEventDispatchingList = m_imageLoadEventDispatchSoonList; m_imageLoadEventDispatchSoonList.clear(); - for (DeprecatedPtrListIterator it(m_imageLoadEventDispatchingList); it.current();) { - HTMLImageLoader* image = it.current(); + for (DeprecatedPtrListIterator it(m_imageLoadEventDispatchingList); it.current();) { + ImageLoader* image = it.current(); // Must advance iterator *before* dispatching call. // Otherwise, it might be advanced automatically if dispatching the call had a side effect - // of destroying the current HTMLImageLoader, and then we would advance past the *next* item, + // of destroying the current ImageLoader, and then we would advance past the *next* item, // missing one altogether. ++it; image->dispatchLoadEvent(); @@ -2720,12 +2983,18 @@ Element* Document::ownerElement() const String Document::cookie() const { - return cookies(this, url()); + if (page() && !page()->cookieEnabled()) + return String(); + + return cookies(this, cookieURL()); } void Document::setCookie(const String& value) { - setCookies(this, url(), policyBaseURL().deprecatedString(), value); + if (page() && !page()->cookieEnabled()) + return; + + setCookies(this, cookieURL(), policyBaseURL(), value); } String Document::referrer() const @@ -2737,7 +3006,7 @@ String Document::referrer() const String Document::domain() const { - return m_securityOrigin->host(); + return m_securityOrigin->domain(); } void Document::setDomain(const String& newDomain) @@ -2789,23 +3058,17 @@ String Document::lastModified() const return loader->response().httpHeaderField("Last-Modified"); } -bool Document::isValidName(const String &name) +static bool isValidNameNonASCII(const UChar* characters, unsigned length) { - const UChar* s = reinterpret_cast(name.characters()); - unsigned length = name.length(); - - if (length == 0) - return false; - unsigned i = 0; UChar32 c; - U16_NEXT(s, i, length, c) + U16_NEXT(characters, i, length, c) if (!isValidNameStart(c)) return false; while (i < length) { - U16_NEXT(s, i, length, c) + U16_NEXT(characters, i, length, c) if (!isValidNamePart(c)) return false; } @@ -2813,34 +3076,67 @@ bool Document::isValidName(const String &name) return true; } -bool Document::parseQualifiedName(const String &qualifiedName, String &prefix, String &localName) +static inline bool isValidNameASCII(const UChar* characters, unsigned length) +{ + UChar c = characters[0]; + if (!(isASCIIAlpha(c) || c == ':' || c == '_')) + return false; + + for (unsigned i = 1; i < length; ++i) { + c = characters[i]; + if (!(isASCIIAlphanumeric(c) || c == ':' || c == '_' || c == '-' || c == '.')) + return false; + } + + return true; +} + +bool Document::isValidName(const String& name) +{ + unsigned length = name.length(); + if (!length) + return false; + + const UChar* characters = name.characters(); + return isValidNameASCII(characters, length) || isValidNameNonASCII(characters, length); +} + +bool Document::parseQualifiedName(const String& qualifiedName, String& prefix, String& localName, ExceptionCode& ec) { unsigned length = qualifiedName.length(); - if (length == 0) + if (length == 0) { + ec = INVALID_CHARACTER_ERR; return false; + } bool nameStart = true; bool sawColon = false; int colonPos = 0; - const UChar* s = reinterpret_cast(qualifiedName.characters()); + const UChar* s = qualifiedName.characters(); for (unsigned i = 0; i < length;) { UChar32 c; U16_NEXT(s, i, length, c) if (c == ':') { - if (sawColon) + if (sawColon) { + ec = NAMESPACE_ERR; return false; // multiple colons: not allowed + } nameStart = true; sawColon = true; colonPos = i - 1; } else if (nameStart) { - if (!isValidNameStart(c)) + if (!isValidNameStart(c)) { + ec = INVALID_CHARACTER_ERR; return false; + } nameStart = false; } else { - if (!isValidNamePart(c)) + if (!isValidNamePart(c)) { + ec = INVALID_CHARACTER_ERR; return false; + } } } @@ -2849,9 +3145,18 @@ bool Document::parseQualifiedName(const String &qualifiedName, String &prefix, S localName = qualifiedName; } else { prefix = qualifiedName.substring(0, colonPos); + if (prefix.isEmpty()) { + ec = NAMESPACE_ERR; + return false; + } localName = qualifiedName.substring(colonPos + 1); } + if (localName.isEmpty()) { + ec = NAMESPACE_ERR; + return false; + } + return true; } @@ -2887,11 +3192,11 @@ HTMLMapElement *Document::getImageMap(const String& url) const return 0; int hashPos = url.find('#'); String name = (hashPos < 0 ? url : url.substring(hashPos + 1)).impl(); - AtomicString mapName = hMode == XHtml ? name : name.lower(); + AtomicString mapName = isHTMLDocument() ? name.lower() : name; return m_imageMapsByName.get(mapName.impl()); } -void Document::setDecoder(TextResourceDecoder *decoder) +void Document::setDecoder(PassRefPtr decoder) { m_decoder = decoder; } @@ -2903,27 +3208,15 @@ UChar Document::backslashAsCurrencySymbol() const return m_decoder->encoding().backslashAsCurrencySymbol(); } -DeprecatedString Document::completeURL(const DeprecatedString& url) +KURL Document::completeURL(const String& url) const { - // FIXME: This treats null URLs the same as empty URLs, unlike the String function below. - - // If both the URL and base URL are empty, like they are for documents - // created using DOMImplementation::createDocument, just return the passed in URL. - // (We do this because url() returns "about:blank" for empty URLs. - if (m_url.isEmpty() && m_baseURL.isEmpty()) - return url; - if (!m_decoder) - return KURL(baseURL(), url).deprecatedString(); - return KURL(baseURL(), url, m_decoder->encoding()).deprecatedString(); -} - -String Document::completeURL(const String& url) -{ - // FIXME: This always returns null when passed a null URL, unlike the DeprecatedString function above. - // Code relies on this behavior, namely the href property of and the data property of . + // Always return a null URL when passed a null string. + // FIXME: Should we change the KURL constructor to have this behavior? if (url.isNull()) - return url; - return completeURL(url.deprecatedString()); + return KURL(); + if (!m_decoder) + return KURL(m_baseURL, url); + return KURL(m_baseURL, url, m_decoder->encoding()); } bool Document::inPageCache() @@ -2950,28 +3243,28 @@ void Document::setInPageCache(bool flag) } } -void Document::willSaveToCache() +void Document::documentWillBecomeInactive() { - HashSet::iterator end = m_pageCacheCallbackElements.end(); - for (HashSet::iterator i = m_pageCacheCallbackElements.begin(); i != end; ++i) - (*i)->willSaveToCache(); + HashSet::iterator end = m_documentActivationCallbackElements.end(); + for (HashSet::iterator i = m_documentActivationCallbackElements.begin(); i != end; ++i) + (*i)->documentWillBecomeInactive(); } -void Document::didRestoreFromCache() +void Document::documentDidBecomeActive() { - HashSet::iterator end = m_pageCacheCallbackElements.end(); - for (HashSet::iterator i = m_pageCacheCallbackElements.begin(); i != end; ++i) - (*i)->didRestoreFromCache(); + HashSet::iterator end = m_documentActivationCallbackElements.end(); + for (HashSet::iterator i = m_documentActivationCallbackElements.begin(); i != end; ++i) + (*i)->documentDidBecomeActive(); } -void Document::registerForCacheCallbacks(Element* e) +void Document::registerForDocumentActivationCallbacks(Element* e) { - m_pageCacheCallbackElements.add(e); + m_documentActivationCallbackElements.add(e); } -void Document::unregisterForCacheCallbacks(Element* e) +void Document::unregisterForDocumentActivationCallbacks(Element* e) { - m_pageCacheCallbackElements.remove(e); + m_documentActivationCallbackElements.remove(e); } void Document::setShouldCreateRenderers(bool f) @@ -2984,17 +3277,6 @@ bool Document::shouldCreateRenderers() return m_createRenderers; } -String Document::toString() const -{ - String result; - - for (Node *child = firstChild(); child != NULL; child = child->nextSibling()) { - result += child->toString(); - } - - return result; -} - // Support for Javascript execCommand, and related methods static Editor::Command command(Document* document, const String& commandName, bool userInterface = false) @@ -3061,8 +3343,8 @@ void Document::removeMarkers(Range* range, DocumentMarker::MarkerType markerType Node* startContainer = range->startContainer(ec); Node* endContainer = range->endContainer(ec); - Node* pastEndNode = range->pastEndNode(); - for (Node* node = range->startNode(); node != pastEndNode; node = node->traverseNextNode()) { + Node* pastLastNode = range->pastLastNode(); + for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { int startOffset = node == startContainer ? range->startOffset(ec) : 0; int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; int length = endOffset - startOffset; @@ -3070,17 +3352,17 @@ void Document::removeMarkers(Range* range, DocumentMarker::MarkerType markerType } } -// Markers are stored in order sorted by their location. -// They do not overlap each other, as currently required by the drawing code in RenderText.cpp. +// Markers are stored in order sorted by their start offset. +// Markers of the same type do not overlap each other. -void Document::addMarker(Node *node, DocumentMarker newMarker) +void Document::addMarker(Node* node, DocumentMarker newMarker) { ASSERT(newMarker.endOffset >= newMarker.startOffset); if (newMarker.endOffset == newMarker.startOffset) return; - + MarkerMapVectorPair* vectorPair = m_markers.get(node); - + if (!vectorPair) { vectorPair = new MarkerMapVectorPair; vectorPair->first.append(newMarker); @@ -3089,32 +3371,44 @@ void Document::addMarker(Node *node, DocumentMarker newMarker) } else { Vector& markers = vectorPair->first; Vector& rects = vectorPair->second; - ASSERT(markers.size() == rects.size()); + size_t numMarkers = markers.size(); + ASSERT(numMarkers == rects.size()); size_t i; - for (i = 0; i != markers.size();) { + // Iterate over all markers whose start offset is less than or equal to the new marker's. + // If one of them is of the same type as the new marker and touches it or intersects with it + // (there is at most one), remove it and adjust the new marker's start offset to encompass it. + for (i = 0; i < numMarkers; ++i) { DocumentMarker marker = markers[i]; - - if (newMarker.endOffset < marker.startOffset+1) { - // This is the first marker that is completely after newMarker, and disjoint from it. - // We found our insertion point. + if (marker.startOffset > newMarker.startOffset) break; - } else if (newMarker.startOffset > marker.endOffset) { - // maker is before newMarker, and disjoint from it. Keep scanning. - i++; - } else if (newMarker == marker) { - // already have this one, NOP - return; - } else { - // marker and newMarker intersect or touch - merge them into newMarker - newMarker.startOffset = min(newMarker.startOffset, marker.startOffset); - newMarker.endOffset = max(newMarker.endOffset, marker.endOffset); - // remove old one, we'll add newMarker later + if (marker.type == newMarker.type && marker.endOffset >= newMarker.startOffset) { + newMarker.startOffset = marker.startOffset; markers.remove(i); rects.remove(i); - // it points to the next marker to consider + numMarkers--; + break; } } - // at this point i points to the node before which we want to insert + size_t j = i; + // Iterate over all markers whose end offset is less than or equal to the new marker's, + // removing markers of the same type as the new marker which touch it or intersect with it, + // adjusting the new marker's end offset to cover them if necessary. + while (j < numMarkers) { + DocumentMarker marker = markers[j]; + if (marker.startOffset > newMarker.endOffset) + break; + if (marker.type == newMarker.type) { + markers.remove(j); + rects.remove(j); + if (newMarker.endOffset <= marker.endOffset) { + newMarker.endOffset = marker.endOffset; + break; + } + numMarkers--; + } else + j++; + } + // At this point i points to the node before which we want to insert. markers.insert(i, newMarker); rects.insert(i, placeholderRectForMarker()); } @@ -3464,9 +3758,8 @@ void Document::shiftMarkers(Node *node, unsigned startOffset, int delta, Documen void Document::applyXSLTransform(ProcessingInstruction* pi) { - RefPtr processor = new XSLTProcessor; - processor->setXSLStylesheet(static_cast(pi->sheet())); - + RefPtr processor = XSLTProcessor::create(); + processor->setXSLStyleSheet(static_cast(pi->sheet())); String resultMIMEType; String newSource; String resultEncoding; @@ -3527,104 +3820,102 @@ Document *Document::topDocument() const return doc; } -PassRefPtr Document::createAttributeNS(const String &namespaceURI, const String &qualifiedName, ExceptionCode& ec) +PassRefPtr Document::createAttributeNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec, bool shouldIgnoreNamespaceChecks) { - if (qualifiedName.isNull()) { - ec = NAMESPACE_ERR; + String prefix, localName; + if (!parseQualifiedName(qualifiedName, prefix, localName, ec)) return 0; - } - String localName = qualifiedName; - String prefix; - int colonpos; - if ((colonpos = qualifiedName.find(':')) >= 0) { - prefix = qualifiedName.substring(0, colonpos); - localName = qualifiedName.substring(colonpos + 1); + QualifiedName qName(prefix, localName, namespaceURI); + if (!shouldIgnoreNamespaceChecks && hasPrefixNamespaceMismatch(qName)) { + ec = NAMESPACE_ERR; + return 0; } - if (!isValidName(localName)) { - ec = INVALID_CHARACTER_ERR; + // Spec: DOM Level 2 Core: http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrAttrNS + if (!shouldIgnoreNamespaceChecks && qName.localName() == "xmlns" && qName.namespaceURI() != "http://www.w3.org/2000/xmlns/") { + ec = NAMESPACE_ERR; return 0; } - + // FIXME: Assume this is a mapped attribute, since createAttribute isn't namespace-aware. There's no harm to XML // documents if we're wrong. - return new Attr(0, this, new MappedAttribute(QualifiedName(prefix, localName, namespaceURI), StringImpl::empty())); + return new Attr(0, this, MappedAttribute::create(qName, StringImpl::empty())); } #if ENABLE(SVG) const SVGDocumentExtensions* Document::svgExtensions() { - return m_svgExtensions; + return m_svgExtensions.get(); } SVGDocumentExtensions* Document::accessSVGExtensions() { if (!m_svgExtensions) - m_svgExtensions = new SVGDocumentExtensions(this); - return m_svgExtensions; + m_svgExtensions.set(new SVGDocumentExtensions(this)); + return m_svgExtensions.get(); } #endif PassRefPtr Document::images() { - return new HTMLCollection(this, HTMLCollection::DocImages); + return HTMLCollection::create(this, HTMLCollection::DocImages); } PassRefPtr Document::applets() { - return new HTMLCollection(this, HTMLCollection::DocApplets); + return HTMLCollection::create(this, HTMLCollection::DocApplets); } PassRefPtr Document::embeds() { - return new HTMLCollection(this, HTMLCollection::DocEmbeds); + return HTMLCollection::create(this, HTMLCollection::DocEmbeds); } PassRefPtr Document::plugins() { // This is an alias for embeds() required for the JS DOM bindings. - return new HTMLCollection(this, HTMLCollection::DocEmbeds); + return HTMLCollection::create(this, HTMLCollection::DocEmbeds); } PassRefPtr Document::objects() { - return new HTMLCollection(this, HTMLCollection::DocObjects); + return HTMLCollection::create(this, HTMLCollection::DocObjects); } PassRefPtr Document::scripts() { - return new HTMLCollection(this, HTMLCollection::DocScripts); + return HTMLCollection::create(this, HTMLCollection::DocScripts); } PassRefPtr Document::links() { - return new HTMLCollection(this, HTMLCollection::DocLinks); + return HTMLCollection::create(this, HTMLCollection::DocLinks); } PassRefPtr Document::forms() { - return new HTMLCollection(this, HTMLCollection::DocForms); + return HTMLCollection::create(this, HTMLCollection::DocForms); } PassRefPtr Document::anchors() { - return new HTMLCollection(this, HTMLCollection::DocAnchors); + return HTMLCollection::create(this, HTMLCollection::DocAnchors); } PassRefPtr Document::all() { - return new HTMLCollection(this, HTMLCollection::DocAll); + return HTMLCollection::create(this, HTMLCollection::DocAll); } PassRefPtr Document::windowNamedItems(const String &name) { - return new HTMLNameCollection(this, HTMLCollection::WindowNamedItems, name); + return HTMLNameCollection::create(this, HTMLCollection::WindowNamedItems, name); } PassRefPtr Document::documentNamedItems(const String &name) { - return new HTMLNameCollection(this, HTMLCollection::DocumentNamedItems, name); + return HTMLNameCollection::create(this, HTMLCollection::DocumentNamedItems, name); } HTMLCollection::CollectionInfo* Document::nameCollectionInfo(HTMLCollection::Type type, const AtomicString& name) @@ -3645,7 +3936,7 @@ void Document::finishedParsing() setParsing(false); ExceptionCode ec = 0; - dispatchEvent(new Event(DOMContentLoadedEvent, true, false), ec); + dispatchEvent(Event::create(eventNames().DOMContentLoadedEvent, true, false), ec); if (Frame* f = frame()) f->loader()->finishedParsing(); @@ -3661,8 +3952,8 @@ Vector Document::formElementsState() const HTMLFormControlElementWithState* e = *it; String value; if (e->saveState(value)) { - stateVector.append(e->name().domString()); - stateVector.append(e->type().domString()); + stateVector.append(e->name().string()); + stateVector.append(e->type().string()); stateVector.append(value); } } @@ -3676,14 +3967,14 @@ PassRefPtr Document::createExpression(const String& expression, ExceptionCode& ec) { if (!m_xpathEvaluator) - m_xpathEvaluator = new XPathEvaluator; + m_xpathEvaluator = XPathEvaluator::create(); return m_xpathEvaluator->createExpression(expression, resolver, ec); } PassRefPtr Document::createNSResolver(Node* nodeResolver) { if (!m_xpathEvaluator) - m_xpathEvaluator = new XPathEvaluator; + m_xpathEvaluator = XPathEvaluator::create(); return m_xpathEvaluator->createNSResolver(nodeResolver); } @@ -3695,7 +3986,7 @@ PassRefPtr Document::evaluate(const String& expression, ExceptionCode& ec) { if (!m_xpathEvaluator) - m_xpathEvaluator = new XPathEvaluator; + m_xpathEvaluator = XPathEvaluator::create(); return m_xpathEvaluator->evaluate(expression, contextNode, resolver, type, result, ec); } @@ -3772,7 +4063,7 @@ FormElementKey& FormElementKey::operator=(const FormElementKey& other) void FormElementKey::ref() const { - if (name() && name() != HashTraits::deletedValue()) + if (name()) name()->ref(); if (type()) type()->ref(); @@ -3780,7 +4071,7 @@ void FormElementKey::ref() const void FormElementKey::deref() const { - if (name() && name() != HashTraits::deletedValue()) + if (name()) name()->deref(); if (type()) type()->deref(); @@ -3819,17 +4110,6 @@ unsigned FormElementKeyHash::hash(const FormElementKey& k) return hash; } -FormElementKey FormElementKeyHashTraits::deletedValue() -{ - return HashTraits::deletedValue(); -} - - -String Document::iconURL() -{ - return m_iconURL; -} - void Document::setIconURL(const String& iconURL, const String& type) { // FIXME - - At some point in the future, we might actually honor the "type" @@ -3853,17 +4133,57 @@ bool Document::useSecureKeyboardEntryWhenActive() const return m_useSecureKeyboardEntryWhenActive; } -void Document::initSecurityOrigin() +void Document::initSecurityContext() { if (m_securityOrigin && !m_securityOrigin->isEmpty()) return; // m_securityOrigin has already been initialized. - m_securityOrigin = SecurityOrigin::createForFrame(m_frame); + if (!m_frame) { + // No source for a security context. + // This can occur via document.implementation.createDocument(). + m_cookieURL = KURL(""); + m_securityOrigin = SecurityOrigin::createEmpty(); + return; + } + + // In the common case, create the security context from the currently + // loading URL. + const KURL& url = m_frame->loader()->url(); + m_cookieURL = url; + m_securityOrigin = SecurityOrigin::create(url); + + if (FrameLoader::allowSubstituteDataAccessToLocal()) { + // If this document was loaded with substituteData, then the document can + // load local resources. See https://bugs.webkit.org/show_bug.cgi?id=16756 + // and https://bugs.webkit.org/show_bug.cgi?id=19760 for further + // discussion. + DocumentLoader* documentLoader = m_frame->loader()->documentLoader(); + if (documentLoader && documentLoader->substituteData().isValid()) + m_securityOrigin->grantLoadLocalResources(); + } + + if (!m_securityOrigin->isEmpty()) + return; + + // If we do not obtain a meaningful origin from the URL, then we try to + // find one via the frame hierarchy. + + Frame* ownerFrame = m_frame->tree()->parent(); + if (!ownerFrame) + ownerFrame = m_frame->loader()->opener(); + + if (ownerFrame && ownerFrame->document()) { + m_cookieURL = ownerFrame->document()->cookieURL(); + // We alias the SecurityOrigins to match Firefox, see Bug 15313 + // https://bugs.webkit.org/show_bug.cgi?id=15313 + m_securityOrigin = ownerFrame->document()->securityOrigin(); + } } void Document::setSecurityOrigin(SecurityOrigin* securityOrigin) { m_securityOrigin = securityOrigin; + initDNSPrefetch(); } void Document::updateFocusAppearanceSoon() @@ -3892,6 +4212,182 @@ void Document::updateFocusAppearanceTimerFired(Timer*) element->updateFocusAppearance(false); } +// FF method for accessing the selection added for compatability. +DOMSelection* Document::getSelection() const +{ + return frame() ? frame()->domWindow()->getSelection() : 0; +} + +static inline int findSlashDotDotSlash(const UChar* characters, size_t length) +{ + if (length < 4) + return -1; + unsigned loopLimit = length - 3; + for (unsigned i = 0; i < loopLimit; ++i) { + if (characters[i] == '/' && characters[i + 1] == '.' && characters[i + 2] == '.' && characters[i + 3] == '/') + return i; + } + return -1; +} + +static inline int findSlashSlash(const UChar* characters, size_t length, int position) +{ + if (length < 2) + return -1; + unsigned loopLimit = length - 1; + for (unsigned i = position; i < loopLimit; ++i) { + if (characters[i] == '/' && characters[i + 1] == '/') + return i; + } + return -1; +} + +static inline int findSlashDotSlash(const UChar* characters, size_t length) +{ + if (length < 3) + return -1; + unsigned loopLimit = length - 2; + for (unsigned i = 0; i < loopLimit; ++i) { + if (characters[i] == '/' && characters[i + 1] == '.' && characters[i + 2] == '/') + return i; + } + return -1; +} + +static inline bool containsColonSlashSlash(const UChar* characters, unsigned length) +{ + if (length < 3) + return false; + unsigned loopLimit = length - 2; + for (unsigned i = 0; i < loopLimit; ++i) { + if (characters[i] == ':' && characters[i + 1] == '/' && characters[i + 2] == '/') + return true; + } + return false; +} + +static inline void cleanPath(Vector& path) +{ + // FIXME: Shold not do this in the query or anchor part. + int pos; + while ((pos = findSlashDotDotSlash(path.data(), path.size())) != -1) { + int prev = reverseFind(path.data(), path.size(), '/', pos - 1); + // don't remove the host, i.e. http://foo.org/../foo.html + if (prev < 0 || (prev > 3 && path[prev - 2] == ':' && path[prev - 1] == '/')) + path.remove(pos, 3); + else + path.remove(prev, pos - prev + 3); + } + + // FIXME: Shold not do this in the query part. + // Set refPos to -2 to mean "I haven't looked for the anchor yet". + // We don't want to waste a function call on the search for the the anchor + // in the vast majority of cases where there is no "//" in the path. + pos = 0; + int refPos = -2; + while ((pos = findSlashSlash(path.data(), path.size(), pos)) != -1) { + if (refPos == -2) + refPos = find(path.data(), path.size(), '#'); + if (refPos > 0 && pos >= refPos) + break; + + if (pos == 0 || path[pos - 1] != ':') + path.remove(pos); + else + pos += 2; + } + + // FIXME: Shold not do this in the query or anchor part. + while ((pos = findSlashDotSlash(path.data(), path.size())) != -1) + path.remove(pos, 2); +} + +static inline bool matchLetter(UChar c, UChar lowercaseLetter) +{ + return (c | 0x20) == lowercaseLetter; +} + +static inline bool needsTrailingSlash(const UChar* characters, unsigned length) +{ + if (length < 6) + return false; + if (!matchLetter(characters[0], 'h') + || !matchLetter(characters[1], 't') + || !matchLetter(characters[2], 't') + || !matchLetter(characters[3], 'p')) + return false; + if (!(characters[4] == ':' + || (matchLetter(characters[4], 's') && characters[5] == ':'))) + return false; + + unsigned pos = characters[4] == ':' ? 5 : 6; + + // Skip initial two slashes if present. + if (pos + 1 < length && characters[pos] == '/' && characters[pos + 1] == '/') + pos += 2; + + // Find next slash. + while (pos < length && characters[pos] != '/') + ++pos; + + return pos == length; +} + +unsigned Document::visitedLinkHash(const AtomicString& attributeURL) const +{ + const UChar* characters = attributeURL.characters(); + unsigned length = attributeURL.length(); + if (!length) + return 0; + + // This is a poor man's completeURL. Faster with less memory allocation. + // FIXME: It's missing a lot of what completeURL does and a lot of what KURL does. + // For example, it does not handle international domain names properly. + + // FIXME: It is wrong that we do not do further processing on strings that have "://" in them: + // 1) The "://" could be in the query or anchor. + // 2) The URL's path could have a "/./" or a "/../" or a "//" sequence in it. + + // FIXME: needsTrailingSlash does not properly return true for a URL that has no path, but does + // have a query or anchor. + + bool hasColonSlashSlash = containsColonSlashSlash(characters, length); + + if (hasColonSlashSlash && !needsTrailingSlash(characters, length)) + return AlreadyHashed::avoidDeletedValue(attributeURL.string().impl()->hash()); + + Vector buffer; + + if (hasColonSlashSlash) { + // FIXME: This is incorrect for URLs that have a query or anchor; the "/" needs to go at the + // end of the path, *before* the query or anchor. + buffer.append(characters, length); + buffer.append('/'); + return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(buffer.data(), buffer.size())); + } + + switch (characters[0]) { + case '/': + buffer.append(m_baseURL.string().characters(), m_baseURL.pathStart()); + break; + case '#': + buffer.append(m_baseURL.string().characters(), m_baseURL.pathEnd()); + break; + default: + buffer.append(m_baseURL.string().characters(), m_baseURL.pathAfterLastSlash()); + break; + } + buffer.append(characters, length); + cleanPath(buffer); + if (needsTrailingSlash(buffer.data(), buffer.size())) { + // FIXME: This is incorrect for URLs that have a query or anchor; the "/" needs to go at the + // end of the path, *before* the query or anchor. + buffer.append('/'); + } + + return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(buffer.data(), buffer.size())); +} + #if ENABLE(DATABASE) void Document::addOpenDatabase(Database* database) @@ -3917,7 +4413,7 @@ DatabaseThread* Document::databaseThread() if (!m_databaseThread && !m_hasOpenDatabases) { // Create the database thread on first request - but not if at least one database was already opened, // because in that case we already had a database thread and terminated it and should not create another. - m_databaseThread = new DatabaseThread(this); + m_databaseThread = DatabaseThread::create(this); if (!m_databaseThread->start()) m_databaseThread = 0; } @@ -3943,4 +4439,89 @@ void Document::stopDatabases() #endif +void Document::attachRange(Range* range) +{ + ASSERT(!m_ranges.contains(range)); + m_ranges.add(range); +} + +void Document::detachRange(Range* range) +{ + ASSERT(m_ranges.contains(range)); + m_ranges.remove(range); +} + +CanvasRenderingContext2D* Document::getCSSCanvasContext(const String& type, const String& name, int width, int height) +{ + HTMLCanvasElement* result = getCSSCanvasElement(name); + if (!result) + return 0; + result->setSize(IntSize(width, height)); + return result->getContext(type); +} + +HTMLCanvasElement* Document::getCSSCanvasElement(const String& name) +{ + RefPtr result = m_cssCanvasElements.get(name).get(); + if (!result) { + result = new HTMLCanvasElement(this); + m_cssCanvasElements.set(name, result); + } + return result.get(); +} + +void Document::initDNSPrefetch() +{ + m_haveExplicitlyDisabledDNSPrefetch = false; + m_isDNSPrefetchEnabled = securityOrigin()->protocol() == "http"; + + // Inherit DNS prefetch opt-out from parent frame + if (Document* parent = parentDocument()) { + if (!parent->isDNSPrefetchEnabled()) + m_isDNSPrefetchEnabled = false; + } +} + +void Document::parseDNSPrefetchControlHeader(const String& dnsPrefetchControl) +{ + if (equalIgnoringCase(dnsPrefetchControl, "on") && !m_haveExplicitlyDisabledDNSPrefetch) { + m_isDNSPrefetchEnabled = true; + return; + } + + m_isDNSPrefetchEnabled = false; + m_haveExplicitlyDisabledDNSPrefetch = true; +} + +#if ENABLE(TOUCH_EVENTS) // Android +void Document::addTouchEventListener(Node* node) +{ + // Note: we only keep track of touch listener in the top frame + if (m_frame && m_frame->tree()->parent()) { + m_frame->page()->mainFrame()->document()->addTouchEventListener(node); + } else { +#if PLATFORM(ANDROID) + if (m_frame && m_frame->view() && m_touchEventListeners.isEmpty()) + android::WebViewCore::getWebViewCore(m_frame->view())->needTouchEvents(true); +#endif + m_touchEventListeners.add(node, 0); + } +} + +void Document::removeTouchEventListener(Node* node) +{ + // Note: we only keep track of touch listener in the top frame + if (m_frame && m_frame->tree()->parent()) { + m_frame->page()->mainFrame()->document()->removeTouchEventListener(node); + } else { +#if PLATFORM(ANDROID) + if (m_frame && m_frame->view() && m_touchEventListeners.size() == 1 && + m_touchEventListeners.contains(node)) + android::WebViewCore::getWebViewCore(m_frame->view())->needTouchEvents(false); +#endif + m_touchEventListeners.remove(node); + } +} + +#endif } // namespace WebCore -- cgit v1.1