diff options
Diffstat (limited to 'WebCore/dom/Document.cpp')
-rw-r--r-- | WebCore/dom/Document.cpp | 601 |
1 files changed, 337 insertions, 264 deletions
diff --git a/WebCore/dom/Document.cpp b/WebCore/dom/Document.cpp index e280c81..ec8dfee 100644 --- a/WebCore/dom/Document.cpp +++ b/WebCore/dom/Document.cpp @@ -4,7 +4,7 @@ * (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) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2008 David Levin (levin@chromium.org) * * This library is free software; you can redistribute it and/or @@ -25,8 +25,9 @@ #include "config.h" #include "Document.h" -#include "AnimationController.h" #include "AXObjectCache.h" +#include "AnimationController.h" +#include "Attr.h" #include "CDATASection.h" #include "CSSHelper.h" #include "CSSStyleSelector.h" @@ -59,6 +60,7 @@ #include "HTMLAnchorElement.h" #include "HTMLBodyElement.h" #include "HTMLCanvasElement.h" +#include "HTMLCollection.h" #include "HTMLDocument.h" #include "HTMLElementFactory.h" #include "HTMLFrameOwnerElement.h" @@ -68,6 +70,7 @@ #include "HTMLMapElement.h" #include "HTMLNameCollection.h" #include "HTMLNames.h" +#include "HTMLParser.h" #include "HTMLStyleElement.h" #include "HTMLTitleElement.h" #include "HTTPParsers.h" @@ -76,8 +79,10 @@ #include "HitTestResult.h" #include "ImageLoader.h" #include "InspectorController.h" +#include "ScriptEventListener.h" #include "KeyboardEvent.h" #include "Logging.h" +#include "MappedAttribute.h" #include "MessageEvent.h" #include "MouseEvent.h" #include "MouseEventWithHitTestResults.h" @@ -97,6 +102,7 @@ #include "RenderView.h" #include "RenderWidget.h" #include "ScriptController.h" +#include "ScriptElement.h" #include "SecurityOrigin.h" #include "SegmentedString.h" #include "SelectionController.h" @@ -105,8 +111,8 @@ #include "TextEvent.h" #include "TextIterator.h" #include "TextResourceDecoder.h" -#include "TreeWalker.h" #include "Timer.h" +#include "TreeWalker.h" #include "UIEvent.h" #include "WebKitAnimationEvent.h" #include "WebKitTransitionEvent.h" @@ -114,15 +120,11 @@ #include "XMLHttpRequest.h" #include "XMLNames.h" #include "XMLTokenizer.h" -#if USE(JSC) -#include "JSDOMBinding.h" -#endif -#include "ScriptController.h" #include <wtf/CurrentTime.h> #include <wtf/HashFunctions.h> #include <wtf/MainThread.h> -#include <wtf/StdLibExtras.h> #include <wtf/PassRefPtr.h> +#include <wtf/StdLibExtras.h> #if ENABLE(DATABASE) #include "Database.h" @@ -182,6 +184,10 @@ #include "WMLNames.h" #endif +#if ENABLE(XHTMLMP) +#include "HTMLNoScriptElement.h" +#endif + using namespace std; using namespace WTF; using namespace Unicode; @@ -307,16 +313,18 @@ static bool disableRangeMutation(Page* page) #endif } -static HashSet<Document*>* changedDocuments = 0; +static HashSet<Document*>* documentsThatNeedStyleRecalc = 0; Document::Document(Frame* frame, bool isXHTML) : ContainerNode(0) , m_domtree_version(0) , m_styleSheets(StyleSheetList::create(this)) + , m_styleRecalcTimer(this, &Document::styleRecalcTimerFired) , m_frameElementsShouldIgnoreScrolling(false) , m_title("") , m_titleSetExplicitly(false) , m_updateFocusAppearanceTimer(this, &Document::updateFocusAppearanceTimerFired) + , m_executeScriptSoonTimer(this, &Document::executeScriptSoonTimerFired) #if ENABLE(XSLT) , m_transformSource(0) #endif @@ -358,14 +366,13 @@ Document::Document(Frame* frame, bool isXHTML) m_frame = frame; m_renderArena = 0; - +#if !PLATFORM(ANDROID) m_axObjectCache = 0; - +#endif m_docLoader = new DocLoader(this); visuallyOrdered = false; m_bParsing = false; - m_docChanged = false; m_tokenizer = 0; m_wellFormed = false; @@ -405,6 +412,9 @@ Document::Document(Frame* frame, bool isXHTML) static int docID = 0; m_docID = docID++; +#if ENABLE(XHTMLMP) + m_shouldProcessNoScriptElement = settings() && !settings()->isJavaScriptEnabled(); +#endif } void Document::removedLastRef() @@ -454,16 +464,17 @@ Document::~Document() ASSERT(!m_inPageCache); ASSERT(!m_savedRenderer); ASSERT(m_ranges.isEmpty()); + ASSERT(!m_styleRecalcTimer.isActive()); - removeAllEventListeners(); + for (size_t i = 0; i < m_scriptsToExecuteSoon.size(); ++i) + m_scriptsToExecuteSoon[i].first->element()->deref(); // Balances ref() in executeScriptSoon(). + removeAllEventListeners(); #if USE(JSC) forgetAllDOMNodesForDocument(this); #endif - if (m_docChanged && changedDocuments) - changedDocuments->remove(this); delete m_tokenizer; m_document.resetSkippingRef(0); delete m_styleSelector; @@ -661,7 +672,7 @@ PassRefPtr<Node> Document::importNode(Node* importedNode, bool deep, ExceptionCo if (ec) return 0; - NamedAttrMap* attrs = oldElement->attributes(true); + NamedNodeMap* attrs = oldElement->attributes(true); if (attrs) { unsigned length = attrs->length(); for (unsigned i = 0; i < length; i++) { @@ -796,14 +807,19 @@ PassRefPtr<Element> Document::createElement(const QualifiedName& qName, bool cre e = SVGElementFactory::createSVGElement(qName, this, createdByParser); #endif #if ENABLE(WML) - else if (qName.namespaceURI() == WMLNames::wmlNamespaceURI || isWMLDocument()) + else if (qName.namespaceURI() == WMLNames::wmlNamespaceURI) e = WMLElementFactory::createWMLElement(qName, this, createdByParser); + else if (isWMLDocument()) + e = WMLElementFactory::createWMLElement(QualifiedName(nullAtom, qName.localName(), WMLNames::wmlNamespaceURI), this, createdByParser); #endif if (!e) e = new Element(qName, document()); // <image> uses imgTag so we need a special rule. +#if ENABLE(WML) + if (!isWMLDocument()) +#endif ASSERT((qName.matches(imageTag) && e->tagQName().matches(imgTag) && e->tagQName().prefix() == qName.prefix()) || qName == e->tagQName()); return e.release(); @@ -867,7 +883,7 @@ String Document::readyState() const String Document::encoding() const { if (TextResourceDecoder* d = decoder()) - return d->encoding().name(); + return d->encoding().domName(); return String(); } @@ -1103,24 +1119,39 @@ PassRefPtr<TreeWalker> Document::createTreeWalker(Node *root, unsigned whatToSho return TreeWalker::create(root, whatToShow, filter, expandEntityReferences); } -void Document::setDocumentChanged(bool b) +void Document::scheduleStyleRecalc() { - if (b) { - if (!m_docChanged) { - if (!changedDocuments) - changedDocuments = new HashSet<Document*>; - changedDocuments->add(this); - } - if (m_accessKeyMapValid) { - m_accessKeyMapValid = false; - m_elementsByAccessKey.clear(); - } - } else { - if (m_docChanged && changedDocuments) - changedDocuments->remove(this); + if (m_styleRecalcTimer.isActive() || inPageCache()) + return; + + ASSERT(childNeedsStyleRecalc()); + + if (!documentsThatNeedStyleRecalc) + documentsThatNeedStyleRecalc = new HashSet<Document*>; + documentsThatNeedStyleRecalc->add(this); + + // FIXME: Why on earth is this here? This is clearly misplaced. + if (m_accessKeyMapValid) { + m_accessKeyMapValid = false; + m_elementsByAccessKey.clear(); } + + m_styleRecalcTimer.startOneShot(0); +} + +void Document::unscheduleStyleRecalc() +{ + ASSERT(!childNeedsStyleRecalc()); - m_docChanged = b; + if (documentsThatNeedStyleRecalc) + documentsThatNeedStyleRecalc->remove(this); + + m_styleRecalcTimer.stop(); +} + +void Document::styleRecalcTimerFired(Timer<Document>*) +{ + updateStyleIfNeeded(); } void Document::recalcStyle(StyleChange change) @@ -1183,28 +1214,26 @@ void Document::recalcStyle(StyleChange change) } for (Node* n = firstChild(); n; n = n->nextSibling()) - if (change >= Inherit || n->hasChangedChild() || n->changed()) + if (change >= Inherit || n->childNeedsStyleRecalc() || n->needsStyleRecalc()) n->recalcStyle(change); #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::CalculateStyleTimeCounter, __FUNCTION__); #endif - if (view()) { - if (changed()) - view()->layout(); #if USE(ACCELERATED_COMPOSITING) - else { - // If we didn't update compositing layers because of layout(), we need to do so here. + if (view()) { + bool layoutPending = view()->layoutPending() || renderer()->needsLayout(); + // If we didn't update compositing layers because of layout(), we need to do so here. + if (!layoutPending) view()->updateCompositingLayers(); - } -#endif } +#endif bail_out: - setChanged(NoStyleChange); - setHasChangedChild(false); - setDocumentChanged(false); + setNeedsStyleRecalc(NoStyleChange); + setChildNeedsStyleRecalc(false); + unscheduleStyleRecalc(); if (view()) view()->resumeScheduledEvents(); @@ -1218,9 +1247,9 @@ bail_out: } } -void Document::updateRendering() +void Document::updateStyleIfNeeded() { - if (!hasChangedChild() || inPageCache()) + if (!childNeedsStyleRecalc() || inPageCache()) return; if (m_frame) @@ -1228,23 +1257,22 @@ void Document::updateRendering() recalcStyle(NoChange); - // Tell the animation controller that updateRendering is finished and it can do any post-processing + // Tell the animation controller that updateStyleIfNeeded is finished and it can do any post-processing if (m_frame) m_frame->animation()->endAnimationUpdate(); } -void Document::updateDocumentsRendering() +void Document::updateStyleForAllDocuments() { - if (!changedDocuments) + if (!documentsThatNeedStyleRecalc) return; - while (changedDocuments->size()) { - HashSet<Document*>::iterator it = changedDocuments->begin(); + while (documentsThatNeedStyleRecalc->size()) { + HashSet<Document*>::iterator it = documentsThatNeedStyleRecalc->begin(); Document* doc = *it; - changedDocuments->remove(it); - - doc->m_docChanged = false; - doc->updateRendering(); + documentsThatNeedStyleRecalc->remove(doc); + ASSERT(doc->childNeedsStyleRecalc() && !doc->inPageCache()); + doc->updateStyleIfNeeded(); } } @@ -1253,7 +1281,7 @@ void Document::updateLayout() if (Element* oe = ownerElement()) oe->document()->updateLayout(); - updateRendering(); + updateStyleIfNeeded(); // Only do a layout if changes have occurred that make it necessary. FrameView* v = view(); @@ -1298,8 +1326,9 @@ void Document::attach() { ASSERT(!attached()); ASSERT(!m_inPageCache); +#if !PLATFORM(ANDROID) ASSERT(!m_axObjectCache); - +#endif if (!m_renderArena) m_renderArena = new RenderArena(); @@ -1349,14 +1378,17 @@ void Document::detach() ContainerNode::detach(); + unscheduleStyleRecalc(); + if (render) render->destroy(); // 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(); + // us. However, this violates Node::detach() symantics, as it's never + // possible to re-attach. Eventually Document::detach() should be renamed, + // or this setting of the frame to 0 could be made explicit in each of the + // callers of Document::detach(). + m_frame = 0; if (m_renderArena) { delete m_renderArena; @@ -1364,17 +1396,10 @@ void Document::detach() } } -void Document::clearFramePointer() +void Document::removeAllEventListeners() { - m_frame = 0; -} - -void Document::removeAllEventListenersFromAllNodes() -{ - size_t size = m_windowEventListeners.size(); - for (size_t i = 0; i < size; ++i) - m_windowEventListeners[i]->setRemoved(true); - m_windowEventListeners.clear(); + if (DOMWindow* domWindow = this->domWindow()) + domWindow->removeAllEventListeners(); removeAllDisconnectedNodeEventListeners(); for (Node* node = this; node; node = node->traverseNextNode()) node->removeAllEventListeners(); @@ -1405,6 +1430,9 @@ RenderView* Document::renderView() const void Document::clearAXObjectCache() { +#if PLATFORM(ANDROID) + return; +#else // clear cache in top document if (m_axObjectCache) { delete m_axObjectCache; @@ -1416,10 +1444,14 @@ void Document::clearAXObjectCache() Document* doc = topDocument(); if (doc != this) doc->clearAXObjectCache(); +#endif } AXObjectCache* Document::axObjectCache() const { +#if PLATFORM(ANDROID) + return 0; +#else // The only document that actually has a AXObjectCache is the top-level // document. This is because we need to be able to get from any WebCoreAXObject // to any other WebCoreAXObject on the same page. Using a single cache allows @@ -1453,6 +1485,7 @@ AXObjectCache* Document::axObjectCache() const // this is the top-level document, so install a new cache m_axObjectCache = new AXObjectCache; return m_axObjectCache; +#endif // ANDROID } void Document::setVisuallyOrdered() @@ -1510,6 +1543,11 @@ void Document::implicitOpen() clear(); m_tokenizer = createTokenizer(); setParsing(true); + + // If we reload, the animation controller sticks around and has + // a stale animation time. We need to update it here. + if (m_frame && m_frame->animation()) + m_frame->animation()->beginAnimationUpdate(); } HTMLElement* Document::body() const @@ -1599,10 +1637,16 @@ void Document::implicitClose() // Parser should have picked up all preloads by now m_docLoader->clearPreloads(); - // Create a body element if we don't already have one. See Radar 3758785. + // Create a head and a body if we don't have those yet (e.g. for about:blank). if (!this->body() && isHTMLDocument()) { if (Node* documentElement = this->documentElement()) { ExceptionCode ec = 0; + + // The implicit <head> isn't expected in older versions of Mail - <rdar://problem/6863795> + if (!head() && shouldCreateImplicitHead(this)) { + documentElement->appendChild(new HTMLHeadElement(headTag, this), ec); + ASSERT(!ec); + } documentElement->appendChild(new HTMLBodyElement(bodyTag, this), ec); ASSERT(!ec); } @@ -1623,7 +1667,7 @@ void Document::implicitClose() f->animation()->resumeAnimations(this); ImageLoader::dispatchPendingLoadEvents(); - dispatchWindowEvent(eventNames().loadEvent, false, false); + dispatchLoadEvent(); if (f) f->loader()->handledOnloadEvents(); #ifdef INSTRUMENT_LAYOUT_SCHEDULING @@ -1650,30 +1694,21 @@ void Document::implicitClose() frame()->loader()->checkCallImplicitClose(); - // Now do our painting/layout, but only if we aren't in a subframe or if we're in a subframe - // that has been sized already. Otherwise, our view size would be incorrect, so doing any - // layout/painting now would be pointless. + // We used to force a synchronous display and flush here. This really isn't + // necessary and can in fact be actively harmful if pages are loading at a rate of > 60fps + // (if your platform is syncing flushes and limiting them to 60fps). + m_overMinimumLayoutThreshold = true; if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) { - updateRendering(); + updateStyleIfNeeded(); // Always do a layout after loading if needed. if (view() && renderer() && (!renderer()->firstChild() || renderer()->needsLayout())) view()->layout(); - - // 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. 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 <rdar://problem/5092361> - if (view() && !ownerElement()) - view()->hostWindow()->paint(); } #if PLATFORM(MAC) if (f && renderer() && this == topDocument() && AXObjectCache::accessibilityEnabled()) - axObjectCache()->postNotificationToElement(renderer(), "AXLoadComplete"); + axObjectCache()->postNotification(renderer(), "AXLoadComplete", true); #endif #if ENABLE(SVG) @@ -1778,11 +1813,8 @@ void Document::clear() m_tokenizer = 0; removeChildren(); - - size_t size = m_windowEventListeners.size(); - for (size_t i = 0; i < size; ++i) - m_windowEventListeners[i]->setRemoved(true); - m_windowEventListeners.clear(); + if (DOMWindow* domWindow = this->domWindow()) + domWindow->removeAllEventListeners(); } const KURL& Document::virtualURL() const @@ -2071,11 +2103,11 @@ void Document::processMetadataSettings(const String& content) } #endif -void Document::processHttpEquiv(const String &equiv, const String &content) +void Document::processHttpEquiv(const String& equiv, const String& content) { ASSERT(!equiv.isNull() && !content.isNull()); - Frame *frame = this->frame(); + Frame* frame = this->frame(); if (equalIgnoringCase(equiv, "default-style")) { // The preferred style set has been overridden as per section @@ -2105,6 +2137,13 @@ void Document::processHttpEquiv(const String &equiv, const String &content) setContentLanguage(content); else if (equalIgnoringCase(equiv, "x-dns-prefetch-control")) parseDNSPrefetchControlHeader(content); + else if (equalIgnoringCase(equiv, "x-frame-options")) { + FrameLoader* frameLoader = frame->loader(); + if (frameLoader->shouldInterruptLoadForXFrameOptions(content, url())) { + frameLoader->stopAllLoaders(); + frameLoader->scheduleHTTPRedirection(0, blankURL()); + } + } } MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& request, const IntPoint& documentPoint, const PlatformMouseEvent& event) @@ -2118,7 +2157,7 @@ MouseEventWithHitTestResults Document::prepareMouseEvent(const HitTestRequest& r renderView()->layer()->hitTest(request, result); if (!request.readOnly()) - updateRendering(); + updateStyleIfNeeded(); return MouseEventWithHitTestResults(event, result); } @@ -2574,7 +2613,7 @@ bool Document::setFocusedNode(PassRefPtr<Node> newFocusedNode) // Dispatch a change event for text fields or textareas that have been edited RenderObject* r = static_cast<RenderObject*>(oldFocusedNode.get()->renderer()); if (r && r->isTextControl() && toRenderTextControl(r)->isEdited()) { - oldFocusedNode->dispatchEventForType(eventNames().changeEvent, true, false); + oldFocusedNode->dispatchEvent(eventNames().changeEvent, true, false); if ((r = static_cast<RenderObject*>(oldFocusedNode.get()->renderer()))) { if (r->isTextControl()) toRenderTextControl(r)->setEdited(false); @@ -2649,23 +2688,45 @@ bool Document::setFocusedNode(PassRefPtr<Node> newFocusedNode) } } -#if PLATFORM(MAC) +#if PLATFORM(MAC) && !PLATFORM(CHROMIUM) if (!focusChangeBlocked && m_focusedNode && AXObjectCache::accessibilityEnabled()) axObjectCache()->handleFocusedUIElementChanged(); +#elif PLATFORM(GTK) + if (!focusChangeBlocked && m_focusedNode && AXObjectCache::accessibilityEnabled()) { + RenderObject* oldFocusedRenderer = 0; + RenderObject* newFocusedRenderer = 0; + + if (oldFocusedNode) + oldFocusedRenderer = oldFocusedNode.get()->renderer(); + if (newFocusedNode) + newFocusedRenderer = newFocusedNode.get()->renderer(); + + axObjectCache()->handleFocusedUIElementChangedWithRenderers(oldFocusedRenderer, newFocusedRenderer); + } #endif SetFocusedNodeDone: - updateRendering(); + updateStyleIfNeeded(); return !focusChangeBlocked; - } +} + +void Document::getFocusableNodes(Vector<RefPtr<Node> >& nodes) +{ + updateLayout(); + + for (Node* node = firstChild(); node; node = node->traverseNextNode()) { + if (node->isFocusable()) + nodes.append(node); + } +} void Document::setCSSTarget(Element* n) { if (m_cssTarget) - m_cssTarget->setChanged(); + m_cssTarget->setNeedsStyleRecalc(); m_cssTarget = n; if (n) - n->setChanged(); + n->setNeedsStyleRecalc(); } void Document::attachNodeIterator(NodeIterator *ni) @@ -2758,9 +2819,51 @@ DOMWindow* Document::domWindow() const { if (!frame()) return 0; + + // The m_frame pointer is not (not always?) zeroed out when the document is put into b/f cache, so the frame can hold an unrelated document/window pair. + // FIXME: We should always zero out the frame pointer on navigation to avoid accidentally accessing the new frame content. + if (m_frame->document() != this) + return 0; + return frame()->domWindow(); } +void Document::setWindowAttributeEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener) +{ + DOMWindow* domWindow = this->domWindow(); + if (!domWindow) + return; + domWindow->setAttributeEventListener(eventType, listener); +} + +void Document::dispatchWindowEvent(PassRefPtr<Event> event) +{ + ASSERT(!eventDispatchForbidden()); + DOMWindow* domWindow = this->domWindow(); + if (!domWindow) + return; + ExceptionCode ec; + domWindow->dispatchEvent(event, ec); +} + +void Document::dispatchWindowEvent(const AtomicString& eventType, bool canBubbleArg, bool cancelableArg) +{ + ASSERT(!eventDispatchForbidden()); + DOMWindow* domWindow = this->domWindow(); + if (!domWindow) + return; + domWindow->dispatchEvent(eventType, canBubbleArg, cancelableArg); +} + +void Document::dispatchLoadEvent() +{ + ASSERT(!eventDispatchForbidden()); + DOMWindow* domWindow = this->domWindow(); + if (!domWindow) + return; + domWindow->dispatchLoadEvent(); +} + PassRefPtr<Event> Document::createEvent(const String& eventType, ExceptionCode& ec) { if (eventType == "Event" || eventType == "Events" || eventType == "HTMLEvents") @@ -2838,141 +2941,6 @@ CSSStyleDeclaration* Document::getOverrideStyle(Element*, const String&) return 0; } -void Document::handleWindowEvent(Event* event, bool useCapture) -{ - if (m_windowEventListeners.isEmpty()) - return; - - // If any HTML event listeners are registered on the window, dispatch them here. - RegisteredEventListenerVector listenersCopy = m_windowEventListeners; - size_t size = listenersCopy.size(); - for (size_t i = 0; i < size; ++i) { - RegisteredEventListener& r = *listenersCopy[i]; - if (r.eventType() == event->type() && r.useCapture() == useCapture && !r.removed()) - r.listener()->handleEvent(event, true); - } -} - -void Document::setWindowInlineEventListenerForType(const AtomicString& eventType, PassRefPtr<EventListener> listener) -{ - // If we already have it we don't want removeWindowEventListener to delete it - removeWindowInlineEventListenerForType(eventType); - if (listener) - addWindowEventListener(eventType, listener, false); -} - -EventListener* Document::windowInlineEventListenerForType(const AtomicString& eventType) -{ - size_t size = m_windowEventListeners.size(); - for (size_t i = 0; i < size; ++i) { - RegisteredEventListener& r = *m_windowEventListeners[i]; - if (r.eventType() == eventType && r.listener()->isInline()) - return r.listener(); - } - return 0; -} - -void Document::removeWindowInlineEventListenerForType(const AtomicString& eventType) -{ - size_t size = m_windowEventListeners.size(); - for (size_t i = 0; i < size; ++i) { - RegisteredEventListener& r = *m_windowEventListeners[i]; - if (r.eventType() == eventType && r.listener()->isInline()) { - if (eventType == eventNames().unloadEvent) - removePendingFrameUnloadEventCount(); - else if (eventType == eventNames().beforeunloadEvent) - removePendingFrameBeforeUnloadEventCount(); - r.setRemoved(true); - m_windowEventListeners.remove(i); - return; - } - } -} - -void Document::addWindowEventListener(const AtomicString& eventType, PassRefPtr<EventListener> 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); - addListenerTypeIfNeeded(eventType); - m_windowEventListeners.append(RegisteredEventListener::create(eventType, listener, useCapture)); -} - -void Document::removeWindowEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) -{ - size_t size = m_windowEventListeners.size(); - for (size_t i = 0; i < size; ++i) { - RegisteredEventListener& r = *m_windowEventListeners[i]; - if (r.eventType() == eventType && r.listener() == listener && r.useCapture() == useCapture) { - if (eventType == eventNames().unloadEvent) - removePendingFrameUnloadEventCount(); - else if (eventType == eventNames().beforeunloadEvent) - removePendingFrameBeforeUnloadEventCount(); - r.setRemoved(true); - m_windowEventListeners.remove(i); - return; - } - } -} - -bool Document::hasWindowEventListener(const AtomicString& eventType) -{ - size_t size = m_windowEventListeners.size(); - for (size_t i = 0; i < size; ++i) { - if (m_windowEventListeners[i]->eventType() == eventType) - return true; - } - return false; -} - -void Document::addPendingFrameUnloadEventCount() -{ - 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(); -} - -PassRefPtr<EventListener> Document::createEventListener(const String& functionName, const String& code, Node* node) -{ - 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::setWindowInlineEventListenerForTypeAndAttribute(const AtomicString& eventType, Attribute* attr) -{ - setWindowInlineEventListenerForType(eventType, createEventListener(attr->localName().string(), attr->value(), 0)); -} - Element* Document::ownerElement() const { if (!frame()) @@ -3001,7 +2969,7 @@ void Document::setCookie(const String& value) if (cookieURL.isEmpty()) return; - setCookies(this, cookieURL, policyBaseURL(), value); + setCookies(this, cookieURL, value); } String Document::referrer() const @@ -3236,11 +3204,14 @@ void Document::setInPageCache(bool flag) m_savedRenderer = renderer(); if (FrameView* v = view()) v->resetScrollbars(); + unscheduleStyleRecalc(); } else { ASSERT(renderer() == 0 || renderer() == m_savedRenderer); ASSERT(m_renderArena); setRenderer(m_savedRenderer); m_savedRenderer = 0; + if (childNeedsStyleRecalc()) + scheduleStyleRecalc(); } } @@ -3357,7 +3328,7 @@ void Document::addMarker(Range *range, DocumentMarker::MarkerType type, String d for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { RefPtr<Range> textPiece = markedText.range(); int exception = 0; - DocumentMarker marker = {type, textPiece->startOffset(exception), textPiece->endOffset(exception), description}; + DocumentMarker marker = {type, textPiece->startOffset(exception), textPiece->endOffset(exception), description, false}; addMarker(textPiece->startContainer(exception), marker); } } @@ -3782,6 +3753,53 @@ void Document::shiftMarkers(Node *node, unsigned startOffset, int delta, Documen node->renderer()->repaint(); } +void Document::setMarkersActive(Range* range, bool active) +{ + if (m_markers.isEmpty()) + return; + + ExceptionCode ec = 0; + Node* startContainer = range->startContainer(ec); + Node* endContainer = range->endContainer(ec); + + Node* pastLastNode = range->pastLastNode(); + for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { + int startOffset = node == startContainer ? range->startOffset(ec) : 0; + int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; + setMarkersActive(node, startOffset, endOffset, active); + } +} + +void Document::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active) +{ + MarkerMapVectorPair* vectorPair = m_markers.get(node); + if (!vectorPair) + return; + + Vector<DocumentMarker>& markers = vectorPair->first; + ASSERT(markers.size() == vectorPair->second.size()); + + bool docDirty = false; + for (size_t i = 0; i != markers.size(); ++i) { + DocumentMarker &marker = markers[i]; + + // Markers are returned in order, so stop if we are now past the specified range. + if (marker.startOffset >= endOffset) + break; + + // Skip marker that is wrong type or before target. + if (marker.endOffset < startOffset || marker.type != DocumentMarker::TextMatch) + continue; + + marker.activeMatch = active; + docDirty = true; + } + + // repaint the affected node + if (docDirty && node->renderer()) + node->renderer()->repaint(); +} + #if ENABLE(XSLT) void Document::applyXSLTransform(ProcessingInstruction* pi) @@ -3848,6 +3866,11 @@ Document *Document::topDocument() const return doc; } +PassRefPtr<Attr> Document::createAttribute(const String& name, ExceptionCode& ec) +{ + return createAttributeNS(String(), name, ec, true); +} + PassRefPtr<Attr> Document::createAttributeNS(const String& namespaceURI, const String& qualifiedName, ExceptionCode& ec, bool shouldIgnoreNamespaceChecks) { String prefix, localName; @@ -3887,75 +3910,75 @@ SVGDocumentExtensions* Document::accessSVGExtensions() PassRefPtr<HTMLCollection> Document::images() { - return HTMLCollection::create(this, HTMLCollection::DocImages); + return HTMLCollection::create(this, DocImages); } PassRefPtr<HTMLCollection> Document::applets() { - return HTMLCollection::create(this, HTMLCollection::DocApplets); + return HTMLCollection::create(this, DocApplets); } PassRefPtr<HTMLCollection> Document::embeds() { - return HTMLCollection::create(this, HTMLCollection::DocEmbeds); + return HTMLCollection::create(this, DocEmbeds); } PassRefPtr<HTMLCollection> Document::plugins() { // This is an alias for embeds() required for the JS DOM bindings. - return HTMLCollection::create(this, HTMLCollection::DocEmbeds); + return HTMLCollection::create(this, DocEmbeds); } PassRefPtr<HTMLCollection> Document::objects() { - return HTMLCollection::create(this, HTMLCollection::DocObjects); + return HTMLCollection::create(this, DocObjects); } PassRefPtr<HTMLCollection> Document::scripts() { - return HTMLCollection::create(this, HTMLCollection::DocScripts); + return HTMLCollection::create(this, DocScripts); } PassRefPtr<HTMLCollection> Document::links() { - return HTMLCollection::create(this, HTMLCollection::DocLinks); + return HTMLCollection::create(this, DocLinks); } PassRefPtr<HTMLCollection> Document::forms() { - return HTMLCollection::create(this, HTMLCollection::DocForms); + return HTMLCollection::create(this, DocForms); } PassRefPtr<HTMLCollection> Document::anchors() { - return HTMLCollection::create(this, HTMLCollection::DocAnchors); + return HTMLCollection::create(this, DocAnchors); } PassRefPtr<HTMLCollection> Document::all() { - return HTMLCollection::create(this, HTMLCollection::DocAll); + return HTMLCollection::create(this, DocAll); } PassRefPtr<HTMLCollection> Document::windowNamedItems(const String &name) { - return HTMLNameCollection::create(this, HTMLCollection::WindowNamedItems, name); + return HTMLNameCollection::create(this, WindowNamedItems, name); } PassRefPtr<HTMLCollection> Document::documentNamedItems(const String &name) { - return HTMLNameCollection::create(this, HTMLCollection::DocumentNamedItems, name); + return HTMLNameCollection::create(this, DocumentNamedItems, name); } -HTMLCollection::CollectionInfo* Document::nameCollectionInfo(HTMLCollection::Type type, const AtomicString& name) +CollectionCache* Document::nameCollectionInfo(CollectionType type, const AtomicString& name) { - ASSERT(type >= HTMLCollection::FirstNamedDocumentCachedType); - unsigned index = type - HTMLCollection::FirstNamedDocumentCachedType; - ASSERT(index < HTMLCollection::NumNamedDocumentCachedTypes); + ASSERT(type >= FirstNamedDocumentCachedType); + unsigned index = type - FirstNamedDocumentCachedType; + ASSERT(index < NumNamedDocumentCachedTypes); NamedCollectionMap& map = m_nameCollectionInfo[index]; NamedCollectionMap::iterator iter = map.find(name.impl()); if (iter == map.end()) - iter = map.add(name.impl(), new HTMLCollection::CollectionInfo).first; + iter = map.add(name.impl(), new CollectionCache).first; return iter->second; } @@ -3974,17 +3997,14 @@ Vector<String> Document::formElementsState() const { Vector<String> stateVector; stateVector.reserveInitialCapacity(m_formElementsWithState.size() * 3); - typedef ListHashSet<FormControlElementWithState*>::const_iterator Iterator; + typedef ListHashSet<Element*>::const_iterator Iterator; Iterator end = m_formElementsWithState.end(); for (Iterator it = m_formElementsWithState.begin(); it != end; ++it) { - FormControlElementWithState* e = *it; + Element* e = *it; String value; - if (e->saveState(value)) { - FormControlElement* formControlElement = e->toFormControlElement(); - ASSERT(formControlElement); - - stateVector.append(formControlElement->name().string()); - stateVector.append(formControlElement->type().string()); + if (e->saveFormControlState(value)) { + stateVector.append(e->formControlName().string()); + stateVector.append(e->formControlType().string()); stateVector.append(value); } } @@ -4259,6 +4279,34 @@ void Document::updateFocusAppearanceTimerFired(Timer<Document>*) element->updateFocusAppearance(false); } +void Document::executeScriptSoonTimerFired(Timer<Document>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_executeScriptSoonTimer); + + Vector<pair<ScriptElementData*, CachedResourceHandle<CachedScript> > > scripts; + scripts.swap(m_scriptsToExecuteSoon); + size_t size = scripts.size(); + for (size_t i = 0; i < size; ++i) { + scripts[i].first->execute(scripts[i].second.get()); + scripts[i].first->element()->deref(); // Balances ref() in executeScriptSoon(). + } +} + +void Document::executeScriptSoon(ScriptElementData* data, CachedResourceHandle<CachedScript> cachedScript) +{ + ASSERT_ARG(data, data); + + Element* element = data->element(); + ASSERT(element); + ASSERT(element->document() == this); + ASSERT(element->inDocument()); + + m_scriptsToExecuteSoon.append(make_pair(data, cachedScript)); + element->ref(); // Balanced by deref()s in executeScriptSoonTimerFired() and ~Document(). + if (!m_executeScriptSoonTimer.isActive()) + m_executeScriptSoonTimer.startOneShot(0); +} + // FF method for accessing the selection added for compatability. DOMSelection* Document::getSelection() const { @@ -4322,6 +4370,14 @@ void Document::resetWMLPageState() if (WMLPageState* pageState = wmlPageStateForDocument(this)) pageState->reset(); } + +void Document::initializeWMLPageState() +{ + if (!isWMLDocument()) + return; + + static_cast<WMLDocument*>(this)->initialize(); +} #endif void Document::attachRange(Range* range) @@ -4435,6 +4491,11 @@ void Document::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const { if (page()) page()->inspectorController()->resourceRetrievedByXMLHttpRequest(identifier, sourceString); + Frame* frame = this->frame(); + if (frame) { + FrameLoader* frameLoader = frame->loader(); + frameLoader->didLoadResourceByXMLHttpRequest(identifier, sourceString); + } } void Document::scriptImported(unsigned long identifier, const String& sourceString) @@ -4533,4 +4594,16 @@ void Document::displayBufferModifiedByEncoding(UChar* buffer, unsigned len) cons m_decoder->encoding().displayBuffer(buffer, len); } +#if ENABLE(XHTMLMP) +bool Document::isXHTMLMPDocument() const +{ + if (!frame() || !frame()->loader()) + return false; + // As per section 7.2 of OMA-WAP-XHTMLMP-V1_1-20061020-A.pdf, a conforming user agent + // MUST accept XHTMLMP document identified as "application/vnd.wap.xhtml+xml" + // and SHOULD accept it identified as "application/xhtml+xml" + return frame()->loader()->responseMIMEType() == "application/vnd.wap.xhtml+xml" || frame()->loader()->responseMIMEType() == "application/xhtml+xml"; +} +#endif + } // namespace WebCore |