2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
27 #include "AnimationController.h"
28 #include "AXObjectCache.h"
29 #include "CDATASection.h"
30 #include "CSSHelper.h"
31 #include "CSSStyleSelector.h"
32 #include "CSSStyleSheet.h"
33 #include "CSSValueKeywords.h"
35 #include "CachedCSSStyleSheet.h"
37 #include "CookieJar.h"
38 #include "DOMImplementation.h"
39 #include "DOMWindow.h"
40 #include "DocLoader.h"
41 #include "DocumentFragment.h"
42 #include "DocumentLoader.h"
43 #include "DocumentType.h"
44 #include "EditingText.h"
46 #include "EntityReference.h"
48 #include "EventHandler.h"
49 #include "EventListener.h"
50 #include "EventNames.h"
51 #include "ExceptionCode.h"
52 #include "FocusController.h"
54 #include "FrameLoader.h"
55 #include "FrameTree.h"
56 #include "FrameView.h"
57 #include "HTMLBodyElement.h"
58 #include "HTMLCanvasElement.h"
59 #include "HTMLDocument.h"
60 #include "HTMLElementFactory.h"
61 #include "HTMLFrameOwnerElement.h"
62 #include "HTMLHeadElement.h"
63 #include "HTMLInputElement.h"
64 #include "HTMLLinkElement.h"
65 #include "HTMLMapElement.h"
66 #include "HTMLNameCollection.h"
67 #include "HTMLNames.h"
68 #include "HTMLStyleElement.h"
69 #include "HTMLTitleElement.h"
70 #include "HTTPParsers.h"
71 #include "HistoryItem.h"
72 #include "HitTestRequest.h"
73 #include "HitTestResult.h"
74 #include "ImageLoader.h"
75 #include "KeyboardEvent.h"
77 #include "MessageEvent.h"
78 #include "MouseEvent.h"
79 #include "MouseEventWithHitTestResults.h"
80 #include "MutationEvent.h"
81 #include "NameNodeList.h"
82 #include "NodeFilter.h"
83 #include "NodeIterator.h"
84 #include "NodeWithIndex.h"
85 #include "OverflowEvent.h"
87 #include "PlatformKeyboardEvent.h"
88 #include "ProcessingInstruction.h"
89 #include "ProgressEvent.h"
90 #include "RegisteredEventListener.h"
91 #include "RegularExpression.h"
92 #include "RenderArena.h"
93 #include "RenderView.h"
94 #include "RenderWidget.h"
95 #include "SecurityOrigin.h"
96 #include "SegmentedString.h"
97 #include "SelectionController.h"
99 #include "StringHash.h"
100 #include "StyleSheetList.h"
101 #include "SystemTime.h"
102 #include "TextEvent.h"
103 #include "TextIterator.h"
104 #include "TextResourceDecoder.h"
105 #include "TreeWalker.h"
107 #include "WebKitAnimationEvent.h"
108 #include "WebKitTransitionEvent.h"
109 #include "WheelEvent.h"
110 #include "XMLHttpRequest.h"
111 #include "XMLNames.h"
112 #include "XMLTokenizer.h"
113 #include "JSDOMBinding.h"
114 #include "ScriptController.h"
115 #include <runtime/JSLock.h>
118 #include "Database.h"
119 #include "DatabaseThread.h"
123 #include "XPathEvaluator.h"
124 #include "XPathExpression.h"
125 #include "XPathNSResolver.h"
126 #include "XPathResult.h"
130 #include "XSLTProcessor.h"
134 #include "XBLBindingManager.h"
138 #include "SVGDocumentExtensions.h"
139 #include "SVGElementFactory.h"
140 #include "SVGZoomEvent.h"
141 #include "SVGStyleElement.h"
146 using namespace Unicode
;
150 using namespace HTMLNames
;
152 // #define INSTRUMENT_LAYOUT_SCHEDULING 1
154 // This amount of time must have elapsed before we will even consider scheduling a layout without a delay.
155 // FIXME: For faster machines this value can really be lowered to 200. 250 is adequate, but a little high
157 static const int cLayoutScheduleThreshold
= 250;
159 // Use 1 to represent the document's default form.
160 static HTMLFormElement
* const defaultForm
= reinterpret_cast<HTMLFormElement
*>(1);
162 // Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
163 static const unsigned PHI
= 0x9e3779b9U
;
165 // DOM Level 2 says (letters added):
167 // a) Name start characters must have one of the categories Ll, Lu, Lo, Lt, Nl.
168 // b) Name characters other than Name-start characters must have one of the categories Mc, Me, Mn, Lm, or Nd.
169 // c) Characters in the compatibility area (i.e. with character code greater than #xF900 and less than #xFFFE) are not allowed in XML names.
170 // d) Characters which have a font or compatibility decomposition (i.e. those with a "compatibility formatting tag" in field 5 of the database -- marked by field 5 beginning with a "<") are not allowed.
171 // e) The following characters are treated as name-start characters rather than name characters, because the property file classifies them as Alphabetic: [#x02BB-#x02C1], #x0559, #x06E5, #x06E6.
172 // f) Characters #x20DD-#x20E0 are excluded (in accordance with Unicode, section 5.14).
173 // g) Character #x00B7 is classified as an extender, because the property list so identifies it.
174 // h) Character #x0387 is added as a name character, because #x00B7 is its canonical equivalent.
175 // i) Characters ':' and '_' are allowed as name-start characters.
176 // j) Characters '-' and '.' are allowed as name characters.
178 // It also contains complete tables. If we decide it's better, we could include those instead of the following code.
180 static inline bool isValidNameStart(UChar32 c
)
183 if ((c
>= 0x02BB && c
<= 0x02C1) || c
== 0x559 || c
== 0x6E5 || c
== 0x6E6)
187 if (c
== ':' || c
== '_')
190 // rules (a) and (f) above
191 const uint32_t nameStartMask
= Letter_Lowercase
| Letter_Uppercase
| Letter_Other
| Letter_Titlecase
| Number_Letter
;
192 if (!(Unicode::category(c
) & nameStartMask
))
196 if (c
>= 0xF900 && c
< 0xFFFE)
200 DecompositionType decompType
= decompositionType(c
);
201 if (decompType
== DecompositionFont
|| decompType
== DecompositionCompat
)
207 static inline bool isValidNamePart(UChar32 c
)
209 // rules (a), (e), and (i) above
210 if (isValidNameStart(c
))
213 // rules (g) and (h) above
214 if (c
== 0x00B7 || c
== 0x0387)
218 if (c
== '-' || c
== '.')
221 // rules (b) and (f) above
222 const uint32_t otherNamePartMask
= Mark_NonSpacing
| Mark_Enclosing
| Mark_SpacingCombining
| Letter_Modifier
| Number_DecimalDigit
;
223 if (!(Unicode::category(c
) & otherNamePartMask
))
227 if (c
>= 0xF900 && c
< 0xFFFE)
231 DecompositionType decompType
= decompositionType(c
);
232 if (decompType
== DecompositionFont
|| decompType
== DecompositionCompat
)
238 static Widget
* widgetForNode(Node
* focusedNode
)
242 RenderObject
* renderer
= focusedNode
->renderer();
243 if (!renderer
|| !renderer
->isWidget())
245 return static_cast<RenderWidget
*>(renderer
)->widget();
248 static bool acceptsEditingFocus(Node
*node
)
251 ASSERT(node
->isContentEditable());
253 Node
*root
= node
->rootEditableElement();
254 Frame
* frame
= node
->document()->frame();
258 return frame
->editor()->shouldBeginEditing(rangeOfContents(root
).get());
261 static HashSet
<Document
*>* changedDocuments
= 0;
263 Document::Document(Frame
* frame
, bool isXHTML
)
265 , m_domtree_version(0)
266 , m_styleSheets(StyleSheetList::create(this))
268 , m_titleSetExplicitly(false)
269 , m_imageLoadEventTimer(this, &Document::imageLoadEventTimerFired
)
270 , m_updateFocusAppearanceTimer(this, &Document::updateFocusAppearanceTimerFired
)
272 , m_transformSource(0)
274 , m_xmlVersion("1.0")
275 , m_xmlStandalone(false)
277 , m_bindingManager(new XBLBindingManager(this))
281 , m_designMode(inherit
)
282 , m_selfOnlyRefCount(0)
286 #if ENABLE(DASHBOARD_SUPPORT)
287 , m_hasDashboardRegions(false)
288 , m_dashboardRegionsDirty(false)
290 , m_accessKeyMapValid(false)
291 , m_createRenderers(true)
292 , m_inPageCache(false)
293 , m_useSecureKeyboardEntryWhenActive(false)
295 , m_numNodeListCaches(0)
297 , m_hasOpenDatabases(false)
299 , m_usingGeolocation(false)
300 #if USE(LOW_BANDWIDTH_DISPLAY)
301 , m_inLowBandwidthDisplay(false)
304 m_document
.resetSkippingRef(this);
308 m_ignoreAutofocus
= false;
315 m_docLoader
= new DocLoader(this);
317 visuallyOrdered
= false;
319 m_docChanged
= false;
321 m_wellFormed
= false;
323 setParseMode(Strict
);
325 m_textColor
= Color::black
;
328 m_inStyleRecalc
= false;
329 m_closeAfterStyleRecalc
= false;
330 m_usesDescendantRules
= false;
331 m_usesSiblingRules
= false;
332 m_usesFirstLineRules
= false;
333 m_usesFirstLetterRules
= false;
334 m_gotoAnchorNeededAfterStylesheetsLoad
= false;
337 m_didCalculateStyleSelector
= false;
338 m_pendingStylesheets
= 0;
339 m_ignorePendingStylesheets
= false;
340 m_hasNodesWithPlaceholderStyle
= false;
341 m_pendingSheetLayout
= NoLayoutWithPendingSheets
;
346 resetVisitedLinkColor();
347 resetActiveLinkColor();
349 m_processingLoadEvent
= false;
350 m_startTime
= currentTime();
351 m_overMinimumLayoutThreshold
= false;
353 initSecurityContext();
356 static int docID
= 0;
360 void Document::removedLastRef()
362 ASSERT(!m_deletionHasBegun
);
363 if (m_selfOnlyRefCount
) {
364 // If removing a child removes the last self-only ref, we don't
365 // want the document to be destructed until after
366 // removeAllChildren returns, so we guard ourselves with an
367 // extra self-only ref.
369 DocPtr
<Document
> guard(this);
371 // We must make sure not to be retaining any of our children through
372 // these extra pointers or we will create a reference cycle.
378 m_documentElement
= 0;
382 deleteAllValues(m_markers
);
388 m_cssCanvasElements
.clear();
391 m_inRemovedLastRefFunction
= false;
395 m_deletionHasBegun
= true;
401 Document::~Document()
404 ASSERT(!m_inPageCache
);
405 ASSERT(!m_savedRenderer
);
406 ASSERT(m_ranges
.isEmpty());
408 removeAllEventListeners();
410 forgetAllDOMNodesForDocument(this);
412 if (m_docChanged
&& changedDocuments
)
413 changedDocuments
->remove(this);
415 m_document
.resetSkippingRef(0);
416 delete m_styleSelector
;
420 delete m_renderArena
;
425 xmlFreeDoc((xmlDocPtr
)m_transformSource
);
429 delete m_bindingManager
;
432 deleteAllValues(m_markers
);
434 clearAXObjectCache();
438 unsigned count
= sizeof(m_nameCollectionInfo
) / sizeof(m_nameCollectionInfo
[0]);
439 for (unsigned i
= 0; i
< count
; i
++)
440 deleteAllValues(m_nameCollectionInfo
[i
]);
443 if (m_databaseThread
) {
444 ASSERT(m_databaseThread
->terminationRequested());
445 m_databaseThread
= 0;
450 m_styleSheets
->documentDestroyed();
455 void Document::resetLinkColor()
457 m_linkColor
= Color(0, 0, 238);
460 void Document::resetVisitedLinkColor()
462 m_visitedLinkColor
= Color(85, 26, 139);
465 void Document::resetActiveLinkColor()
467 m_activeLinkColor
.setNamedColor("red");
470 void Document::setDocType(PassRefPtr
<DocumentType
> docType
)
472 // This should never be called more than once.
473 // Note: This is not a public DOM method and can only be called by the parser.
474 ASSERT(!m_docType
|| !docType
);
475 if (m_docType
&& docType
)
479 m_docType
->setDocument(this);
480 determineParseMode();
483 DOMImplementation
* Document::implementation() const
485 if (!m_implementation
)
486 m_implementation
= DOMImplementation::create();
487 return m_implementation
.get();
490 void Document::childrenChanged(bool changedByParser
, Node
* beforeChange
, Node
* afterChange
, int childCountDelta
)
492 ContainerNode::childrenChanged(changedByParser
, beforeChange
, afterChange
, childCountDelta
);
494 // Invalidate the document element we have cached in case it was replaced.
495 m_documentElement
= 0;
498 Element
* Document::documentElement() const
500 if (!m_documentElement
) {
501 Node
* n
= firstChild();
502 while (n
&& !n
->isElementNode())
503 n
= n
->nextSibling();
504 m_documentElement
= static_cast<Element
*>(n
);
507 return m_documentElement
.get();
510 PassRefPtr
<Element
> Document::createElement(const AtomicString
& name
, ExceptionCode
& ec
)
512 if (!isValidName(name
)) {
513 ec
= INVALID_CHARACTER_ERR
;
518 return HTMLElementFactory::createHTMLElement(name
, this, 0, false);
520 return createElement(QualifiedName(nullAtom
, name
, nullAtom
), false, ec
);
523 PassRefPtr
<DocumentFragment
> Document::createDocumentFragment()
525 return new DocumentFragment(document());
528 PassRefPtr
<Text
> Document::createTextNode(const String
& data
)
530 return new Text(this, data
);
533 PassRefPtr
<Comment
> Document::createComment(const String
& data
)
535 return new Comment(this, data
);
538 PassRefPtr
<CDATASection
> Document::createCDATASection(const String
& data
, ExceptionCode
& ec
)
540 if (isHTMLDocument()) {
541 ec
= NOT_SUPPORTED_ERR
;
544 return new CDATASection(this, data
);
547 PassRefPtr
<ProcessingInstruction
> Document::createProcessingInstruction(const String
& target
, const String
& data
, ExceptionCode
& ec
)
549 if (!isValidName(target
)) {
550 ec
= INVALID_CHARACTER_ERR
;
553 if (isHTMLDocument()) {
554 ec
= NOT_SUPPORTED_ERR
;
557 return new ProcessingInstruction(this, target
, data
);
560 PassRefPtr
<EntityReference
> Document::createEntityReference(const String
& name
, ExceptionCode
& ec
)
562 if (!isValidName(name
)) {
563 ec
= INVALID_CHARACTER_ERR
;
566 if (isHTMLDocument()) {
567 ec
= NOT_SUPPORTED_ERR
;
570 return new EntityReference(this, name
);
573 PassRefPtr
<EditingText
> Document::createEditingTextNode(const String
& text
)
575 return new EditingText(this, text
);
578 PassRefPtr
<CSSStyleDeclaration
> Document::createCSSStyleDeclaration()
580 return CSSMutableStyleDeclaration::create();
583 PassRefPtr
<Node
> Document::importNode(Node
* importedNode
, bool deep
, ExceptionCode
& ec
)
588 #if ENABLE(SVG) && ENABLE(DASHBOARD_SUPPORT)
589 || (importedNode
->isSVGElement() && page() && page()->settings()->usesDashboardBackwardCompatibilityMode())
592 ec
= NOT_SUPPORTED_ERR
;
596 switch (importedNode
->nodeType()) {
598 return createTextNode(importedNode
->nodeValue());
599 case CDATA_SECTION_NODE
:
600 return createCDATASection(importedNode
->nodeValue(), ec
);
601 case ENTITY_REFERENCE_NODE
:
602 return createEntityReference(importedNode
->nodeName(), ec
);
603 case PROCESSING_INSTRUCTION_NODE
:
604 return createProcessingInstruction(importedNode
->nodeName(), importedNode
->nodeValue(), ec
);
606 return createComment(importedNode
->nodeValue());
608 Element
* oldElement
= static_cast<Element
*>(importedNode
);
609 RefPtr
<Element
> newElement
= createElementNS(oldElement
->namespaceURI(), oldElement
->tagQName().toString(), ec
);
614 NamedAttrMap
* attrs
= oldElement
->attributes(true);
616 unsigned length
= attrs
->length();
617 for (unsigned i
= 0; i
< length
; i
++) {
618 Attribute
* attr
= attrs
->attributeItem(i
);
619 newElement
->setAttribute(attr
->name(), attr
->value().impl(), ec
);
625 newElement
->copyNonAttributeProperties(oldElement
);
628 for (Node
* oldChild
= oldElement
->firstChild(); oldChild
; oldChild
= oldChild
->nextSibling()) {
629 RefPtr
<Node
> newChild
= importNode(oldChild
, true, ec
);
632 newElement
->appendChild(newChild
.release(), ec
);
638 return newElement
.release();
640 case ATTRIBUTE_NODE
: {
641 RefPtr
<Attr
> newAttr
= new Attr(0, this, static_cast<Attr
*>(importedNode
)->attr()->clone());
642 newAttr
->createTextChild();
643 return newAttr
.release();
645 case DOCUMENT_FRAGMENT_NODE
: {
646 DocumentFragment
* oldFragment
= static_cast<DocumentFragment
*>(importedNode
);
647 RefPtr
<DocumentFragment
> newFragment
= createDocumentFragment();
649 for (Node
* oldChild
= oldFragment
->firstChild(); oldChild
; oldChild
= oldChild
->nextSibling()) {
650 RefPtr
<Node
> newChild
= importNode(oldChild
, true, ec
);
653 newFragment
->appendChild(newChild
.release(), ec
);
659 return newFragment
.release();
663 // FIXME: It should be possible to import these node types, however in DOM3 the DocumentType is readonly, so there isn't much sense in doing that.
664 // Ability to add these imported nodes to a DocumentType will be considered for addition to a future release of the DOM.
666 case DOCUMENT_TYPE_NODE
:
667 case XPATH_NAMESPACE_NODE
:
671 ec
= NOT_SUPPORTED_ERR
;
676 PassRefPtr
<Node
> Document::adoptNode(PassRefPtr
<Node
> source
, ExceptionCode
& ec
)
679 ec
= NOT_SUPPORTED_ERR
;
683 if (source
->isReadOnlyNode()) {
684 ec
= NO_MODIFICATION_ALLOWED_ERR
;
688 switch (source
->nodeType()) {
692 case DOCUMENT_TYPE_NODE
:
693 case XPATH_NAMESPACE_NODE
:
694 ec
= NOT_SUPPORTED_ERR
;
696 case ATTRIBUTE_NODE
: {
697 Attr
* attr
= static_cast<Attr
*>(source
.get());
698 if (attr
->ownerElement())
699 attr
->ownerElement()->removeAttributeNode(attr
, ec
);
700 attr
->setSpecified(true);
704 if (source
->parentNode())
705 source
->parentNode()->removeChild(source
.get(), ec
);
708 for (Node
* node
= source
.get(); node
; node
= node
->traverseNextNode(source
.get()))
709 node
->setDocument(this);
714 bool Document::hasPrefixNamespaceMismatch(const QualifiedName
& qName
)
716 static const AtomicString
xmlnsNamespaceURI("http://www.w3.org/2000/xmlns/");
717 static const AtomicString
xmlns("xmlns");
718 static const AtomicString
xml("xml");
720 // These checks are from DOM Core Level 2, createElementNS
721 // http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrElNS
722 if (!qName
.prefix().isEmpty() && qName
.namespaceURI().isNull()) // createElementNS(null, "html:div")
724 if (qName
.prefix() == xml
&& qName
.namespaceURI() != XMLNames::xmlNamespaceURI
) // createElementNS("http://www.example.com", "xml:lang")
727 // Required by DOM Level 3 Core and unspecified by DOM Level 2 Core:
728 // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-DocCrElNS
729 // createElementNS("http://www.w3.org/2000/xmlns/", "foo:bar"), createElementNS(null, "xmlns:bar")
730 if ((qName
.prefix() == xmlns
&& qName
.namespaceURI() != xmlnsNamespaceURI
) || (qName
.prefix() != xmlns
&& qName
.namespaceURI() == xmlnsNamespaceURI
))
736 // FIXME: This should really be in a possible ElementFactory class
737 PassRefPtr
<Element
> Document::createElement(const QualifiedName
& qName
, bool createdByParser
, ExceptionCode
& ec
)
741 // FIXME: Use registered namespaces and look up in a hash to find the right factory.
742 if (qName
.namespaceURI() == xhtmlNamespaceURI
)
743 e
= HTMLElementFactory::createHTMLElement(qName
.localName(), this, 0, createdByParser
);
745 else if (qName
.namespaceURI() == SVGNames::svgNamespaceURI
)
746 e
= SVGElementFactory::createSVGElement(qName
, this, createdByParser
);
750 e
= new Element(qName
, document());
752 // FIXME: The element factories should be fixed to not ignore qName.prefix()
753 // Instead they should pass the entire qName into element creation so we don't
754 // need to manually set the prefix after creation.
755 // Then this code can become ASSERT(qName == e.qname());
756 // and Document::createElement can stop taking ExceptionCode& as well.
757 if (e
&& !qName
.prefix().isNull()) {
759 e
->setPrefix(qName
.prefix(), ec
);
767 PassRefPtr
<Element
> Document::createElementNS(const String
& namespaceURI
, const String
& qualifiedName
, ExceptionCode
& ec
)
769 String prefix
, localName
;
770 if (!parseQualifiedName(qualifiedName
, prefix
, localName
, ec
))
773 QualifiedName
qName(prefix
, localName
, namespaceURI
);
774 if (hasPrefixNamespaceMismatch(qName
)) {
779 return createElement(qName
, false, ec
);
782 Element
* Document::getElementById(const AtomicString
& elementId
) const
784 if (elementId
.isEmpty())
787 Element
* element
= m_elementsById
.get(elementId
.impl());
791 if (m_duplicateIds
.contains(elementId
.impl())) {
792 // We know there's at least one node with this id, but we don't know what the first one is.
793 for (Node
*n
= traverseNextNode(); n
!= 0; n
= n
->traverseNextNode()) {
794 if (n
->isElementNode()) {
795 element
= static_cast<Element
*>(n
);
796 if (element
->hasID() && element
->getAttribute(idAttr
) == elementId
) {
797 m_duplicateIds
.remove(elementId
.impl());
798 m_elementsById
.set(elementId
.impl(), element
);
803 ASSERT_NOT_REACHED();
808 String
Document::readyState() const
810 if (Frame
* f
= frame()) {
811 if (f
->loader()->isComplete())
816 // FIXME: What does "interactive" mean?
817 // FIXME: Missing support for "uninitialized".
822 String
Document::inputEncoding() const
824 if (TextResourceDecoder
* d
= decoder())
825 return d
->encoding().name();
829 String
Document::defaultCharset() const
831 if (Settings
* settings
= this->settings())
832 return settings
->defaultTextEncodingName();
836 void Document::setCharset(const String
& charset
)
840 decoder()->setEncoding(charset
, TextResourceDecoder::UserChosenEncoding
);
843 void Document::setXMLVersion(const String
& version
, ExceptionCode
& ec
)
845 if (!implementation()->hasFeature("XML", String())) {
846 ec
= NOT_SUPPORTED_ERR
;
850 // FIXME: Also raise NOT_SUPPORTED_ERR if the version is set to a value that is not supported by this Document.
852 m_xmlVersion
= version
;
855 void Document::setXMLStandalone(bool standalone
, ExceptionCode
& ec
)
857 if (!implementation()->hasFeature("XML", String())) {
858 ec
= NOT_SUPPORTED_ERR
;
862 m_xmlStandalone
= standalone
;
865 void Document::setDocumentURI(const String
& uri
)
871 KURL
Document::baseURI() const
876 Element
* Document::elementFromPoint(int x
, int y
) const
881 HitTestRequest
request(true, true);
882 HitTestResult
result(IntPoint(x
, y
));
883 renderer()->layer()->hitTest(request
, result
);
885 Node
* n
= result
.innerNode();
886 while (n
&& !n
->isElementNode())
889 n
= n
->shadowAncestorNode();
890 return static_cast<Element
*>(n
);
893 void Document::addElementById(const AtomicString
& elementId
, Element
* element
)
895 typedef HashMap
<AtomicStringImpl
*, Element
*>::iterator iterator
;
896 if (!m_duplicateIds
.contains(elementId
.impl())) {
897 // Fast path. The ID is not already in m_duplicateIds, so we assume that it's
898 // also not already in m_elementsById and do an add. If that add succeeds, we're done.
899 pair
<iterator
, bool> addResult
= m_elementsById
.add(elementId
.impl(), element
);
900 if (addResult
.second
)
902 // The add failed, so this ID was already cached in m_elementsById.
903 // There are multiple elements with this ID. Remove the m_elementsById
904 // cache for this ID so getElementById searches for it next time it is called.
905 m_elementsById
.remove(addResult
.first
);
906 m_duplicateIds
.add(elementId
.impl());
908 // There are multiple elements with this ID. If it exists, remove the m_elementsById
909 // cache for this ID so getElementById searches for it next time it is called.
910 iterator cachedItem
= m_elementsById
.find(elementId
.impl());
911 if (cachedItem
!= m_elementsById
.end()) {
912 m_elementsById
.remove(cachedItem
);
913 m_duplicateIds
.add(elementId
.impl());
916 m_duplicateIds
.add(elementId
.impl());
919 void Document::removeElementById(const AtomicString
& elementId
, Element
* element
)
921 if (m_elementsById
.get(elementId
.impl()) == element
)
922 m_elementsById
.remove(elementId
.impl());
924 m_duplicateIds
.remove(elementId
.impl());
927 Element
* Document::getElementByAccessKey(const String
& key
) const
931 if (!m_accessKeyMapValid
) {
932 for (Node
* n
= firstChild(); n
; n
= n
->traverseNextNode()) {
933 if (!n
->isElementNode())
935 Element
* element
= static_cast<Element
*>(n
);
936 const AtomicString
& accessKey
= element
->getAttribute(accesskeyAttr
);
937 if (!accessKey
.isEmpty())
938 m_elementsByAccessKey
.set(accessKey
.impl(), element
);
940 m_accessKeyMapValid
= true;
942 return m_elementsByAccessKey
.get(key
.impl());
945 void Document::updateTitle()
947 if (Frame
* f
= frame())
948 f
->loader()->setTitle(m_title
);
951 void Document::setTitle(const String
& title
, Element
* titleElement
)
954 // Title set by JavaScript -- overrides any title elements.
955 m_titleSetExplicitly
= true;
956 if (!isHTMLDocument())
958 else if (!m_titleElement
) {
959 if (HTMLElement
* headElement
= head()) {
960 ExceptionCode ec
= 0;
961 m_titleElement
= createElement("title", ec
);
963 headElement
->appendChild(m_titleElement
, ec
);
967 } else if (titleElement
!= m_titleElement
) {
968 if (m_titleElement
|| m_titleSetExplicitly
)
969 // Only allow the first title element to change the title -- others have no effect.
971 m_titleElement
= titleElement
;
974 if (m_title
== title
)
980 if (m_titleSetExplicitly
&& m_titleElement
&& m_titleElement
->hasTagName(titleTag
))
981 static_cast<HTMLTitleElement
*>(m_titleElement
.get())->setText(m_title
);
984 void Document::removeTitle(Element
* titleElement
)
986 if (m_titleElement
!= titleElement
)
990 m_titleSetExplicitly
= false;
992 // Update title based on first title element in the head, if one exists.
993 if (HTMLElement
* headElement
= head()) {
994 for (Node
* e
= headElement
->firstChild(); e
; e
= e
->nextSibling())
995 if (e
->hasTagName(titleTag
)) {
996 HTMLTitleElement
* titleElement
= static_cast<HTMLTitleElement
*>(e
);
997 setTitle(titleElement
->text(), titleElement
);
1002 if (!m_titleElement
&& !m_title
.isEmpty()) {
1008 String
Document::nodeName() const
1013 Node::NodeType
Document::nodeType() const
1015 return DOCUMENT_NODE
;
1018 FrameView
* Document::view() const
1020 return m_frame
? m_frame
->view() : 0;
1023 Page
* Document::page() const
1025 return m_frame
? m_frame
->page() : 0;
1028 Settings
* Document::settings() const
1030 return m_frame
? m_frame
->settings() : 0;
1033 PassRefPtr
<Range
> Document::createRange()
1035 return Range::create(this);
1038 PassRefPtr
<NodeIterator
> Document::createNodeIterator(Node
* root
, unsigned whatToShow
,
1039 PassRefPtr
<NodeFilter
> filter
, bool expandEntityReferences
, ExceptionCode
& ec
)
1042 ec
= NOT_SUPPORTED_ERR
;
1045 return NodeIterator::create(root
, whatToShow
, filter
, expandEntityReferences
);
1048 PassRefPtr
<TreeWalker
> Document::createTreeWalker(Node
*root
, unsigned whatToShow
,
1049 PassRefPtr
<NodeFilter
> filter
, bool expandEntityReferences
, ExceptionCode
& ec
)
1052 ec
= NOT_SUPPORTED_ERR
;
1055 return TreeWalker::create(root
, whatToShow
, filter
, expandEntityReferences
);
1058 void Document::setDocumentChanged(bool b
)
1061 if (!m_docChanged
) {
1062 if (!changedDocuments
)
1063 changedDocuments
= new HashSet
<Document
*>;
1064 changedDocuments
->add(this);
1066 if (m_accessKeyMapValid
) {
1067 m_accessKeyMapValid
= false;
1068 m_elementsByAccessKey
.clear();
1071 if (m_docChanged
&& changedDocuments
)
1072 changedDocuments
->remove(this);
1078 void Document::recalcStyle(StyleChange change
)
1080 // we should not enter style recalc while painting
1081 if (frame() && frame()->view() && frame()->view()->isPainting()) {
1082 ASSERT(!frame()->view()->isPainting());
1086 if (m_inStyleRecalc
)
1087 return; // Guard against re-entrancy. -dwh
1089 m_inStyleRecalc
= true;
1090 suspendPostAttachCallbacks();
1092 ASSERT(!renderer() || renderArena());
1093 if (!renderer() || !renderArena())
1096 if (change
== Force
) {
1097 // style selector may set this again during recalc
1098 m_hasNodesWithPlaceholderStyle
= false;
1100 RefPtr
<RenderStyle
> documentStyle
= RenderStyle::create();
1101 documentStyle
->setDisplay(BLOCK
);
1102 documentStyle
->setVisuallyOrdered(visuallyOrdered
);
1103 documentStyle
->setZoom(frame()->pageZoomFactor());
1104 m_styleSelector
->setStyle(documentStyle
);
1106 FontDescription fontDescription
;
1107 fontDescription
.setUsePrinterFont(printing());
1108 if (Settings
* settings
= this->settings()) {
1109 fontDescription
.setRenderingMode(settings
->fontRenderingMode());
1110 if (printing() && !settings
->shouldPrintBackgrounds())
1111 documentStyle
->setForceBackgroundsToWhite(true);
1112 const AtomicString
& stdfont
= settings
->standardFontFamily();
1113 if (!stdfont
.isEmpty()) {
1114 fontDescription
.firstFamily().setFamily(stdfont
);
1115 fontDescription
.firstFamily().appendFamily(0);
1117 fontDescription
.setKeywordSize(CSSValueMedium
- CSSValueXxSmall
+ 1);
1118 m_styleSelector
->setFontSize(fontDescription
, m_styleSelector
->fontSizeForKeyword(CSSValueMedium
, inCompatMode(), false));
1121 documentStyle
->setFontDescription(fontDescription
);
1122 documentStyle
->font().update(m_styleSelector
->fontSelector());
1124 documentStyle
->setHtmlHacks(true); // enable html specific rendering tricks
1126 StyleChange ch
= diff(documentStyle
.get(), renderer()->style());
1127 if (renderer() && ch
!= NoChange
)
1128 renderer()->setStyle(documentStyle
.release());
1129 if (change
!= Force
)
1133 for (Node
* n
= firstChild(); n
; n
= n
->nextSibling())
1134 if (change
>= Inherit
|| n
->hasChangedChild() || n
->changed())
1135 n
->recalcStyle(change
);
1137 if (changed() && view())
1141 setChanged(NoStyleChange
);
1142 setHasChangedChild(false);
1143 setDocumentChanged(false);
1145 resumePostAttachCallbacks();
1146 m_inStyleRecalc
= false;
1148 // If we wanted to call implicitClose() during recalcStyle, do so now that we're finished.
1149 if (m_closeAfterStyleRecalc
) {
1150 m_closeAfterStyleRecalc
= false;
1155 void Document::updateRendering()
1157 if (hasChangedChild() && !inPageCache())
1158 recalcStyle(NoChange
);
1160 // Tell the animation controller that the style is available and it can start animations
1162 m_frame
->animation()->styleAvailable();
1165 void Document::updateDocumentsRendering()
1167 if (!changedDocuments
)
1170 while (changedDocuments
->size()) {
1171 HashSet
<Document
*>::iterator it
= changedDocuments
->begin();
1172 Document
* doc
= *it
;
1173 changedDocuments
->remove(it
);
1175 doc
->m_docChanged
= false;
1176 doc
->updateRendering();
1180 void Document::updateLayout()
1182 if (Element
* oe
= ownerElement())
1183 oe
->document()->updateLayout();
1185 // FIXME: Dave Hyatt's pretty sure we can remove this because layout calls recalcStyle as needed.
1188 // Only do a layout if changes have occurred that make it necessary.
1189 FrameView
* v
= view();
1190 if (v
&& renderer() && (v
->layoutPending() || renderer()->needsLayout()))
1194 // FIXME: This is a bad idea and needs to be removed eventually.
1195 // Other browsers load stylesheets before they continue parsing the web page.
1196 // Since we don't, we can run JavaScript code that needs answers before the
1197 // stylesheets are loaded. Doing a layout ignoring the pending stylesheets
1198 // lets us get reasonable answers. The long term solution to this problem is
1199 // to instead suspend JavaScript execution.
1200 void Document::updateLayoutIgnorePendingStylesheets()
1202 bool oldIgnore
= m_ignorePendingStylesheets
;
1204 if (!haveStylesheetsLoaded()) {
1205 m_ignorePendingStylesheets
= true;
1206 // FIXME: We are willing to attempt to suppress painting with outdated style info only once. Our assumption is that it would be
1207 // dangerous to try to stop it a second time, after page content has already been loaded and displayed
1208 // with accurate style information. (Our suppression involves blanking the whole page at the
1209 // moment. If it were more refined, we might be able to do something better.)
1210 // It's worth noting though that this entire method is a hack, since what we really want to do is
1211 // suspend JS instead of doing a layout with inaccurate information.
1212 if (body() && !body()->renderer() && m_pendingSheetLayout
== NoLayoutWithPendingSheets
) {
1213 m_pendingSheetLayout
= DidLayoutWithPendingSheets
;
1214 updateStyleSelector();
1215 } else if (m_hasNodesWithPlaceholderStyle
)
1216 // If new nodes have been added or style recalc has been done with style sheets still pending, some nodes
1217 // may not have had their real style calculated yet. Normally this gets cleaned when style sheets arrive
1218 // but here we need up-to-date style immediatly.
1224 m_ignorePendingStylesheets
= oldIgnore
;
1227 void Document::attach()
1229 ASSERT(!attached());
1230 ASSERT(!m_inPageCache
);
1231 ASSERT(!m_axObjectCache
);
1234 m_renderArena
= new RenderArena();
1236 // Create the rendering tree
1237 setRenderer(new (m_renderArena
) RenderView(this, view()));
1239 if (!m_styleSelector
) {
1240 bool matchAuthorAndUserStyles
= true;
1241 if (Settings
* docSettings
= settings())
1242 matchAuthorAndUserStyles
= docSettings
->authorAndUserStylesEnabled();
1243 m_styleSelector
= new CSSStyleSelector(this, userStyleSheet(), m_styleSheets
.get(), m_mappedElementSheet
.get(), !inCompatMode(), matchAuthorAndUserStyles
);
1248 RenderObject
* render
= renderer();
1251 ContainerNode::attach();
1253 setRenderer(render
);
1256 void Document::detach()
1259 ASSERT(!m_inPageCache
);
1261 clearAXObjectCache();
1263 RenderObject
* render
= renderer();
1265 // indicate destruction mode, i.e. attached() but renderer == 0
1268 // Empty out these lists as a performance optimization, since detaching
1269 // all the individual render objects will cause all the RenderImage
1270 // objects to remove themselves from the lists.
1271 m_imageLoadEventDispatchSoonList
.clear();
1272 m_imageLoadEventDispatchingList
.clear();
1278 ContainerNode::detach();
1283 // This is required, as our Frame might delete itself as soon as it detaches
1284 // us. However, this violates Node::detach() symantics, as it's never
1285 // possible to re-attach. Eventually Document::detach() should be renamed
1286 // or this call made explicit in each of the callers of Document::detach().
1287 clearFramePointer();
1289 if (m_renderArena
) {
1290 delete m_renderArena
;
1295 void Document::clearFramePointer()
1300 void Document::removeAllEventListenersFromAllNodes()
1302 m_windowEventListeners
.clear();
1303 removeAllDisconnectedNodeEventListeners();
1304 for (Node
*n
= this; n
; n
= n
->traverseNextNode()) {
1305 if (!n
->isEventTargetNode())
1307 EventTargetNodeCast(n
)->removeAllEventListeners();
1311 void Document::registerDisconnectedNodeWithEventListeners(Node
* node
)
1313 m_disconnectedNodesWithEventListeners
.add(node
);
1316 void Document::unregisterDisconnectedNodeWithEventListeners(Node
* node
)
1318 m_disconnectedNodesWithEventListeners
.remove(node
);
1321 void Document::removeAllDisconnectedNodeEventListeners()
1323 HashSet
<Node
*>::iterator end
= m_disconnectedNodesWithEventListeners
.end();
1324 for (HashSet
<Node
*>::iterator i
= m_disconnectedNodesWithEventListeners
.begin(); i
!= end
; ++i
)
1325 EventTargetNodeCast(*i
)->removeAllEventListeners();
1326 m_disconnectedNodesWithEventListeners
.clear();
1329 void Document::clearAXObjectCache()
1331 // clear cache in top document
1332 if (m_axObjectCache
) {
1333 delete m_axObjectCache
;
1334 m_axObjectCache
= 0;
1338 // ask the top-level document to clear its cache
1339 Document
* doc
= topDocument();
1341 doc
->clearAXObjectCache();
1344 AXObjectCache
* Document::axObjectCache() const
1346 // The only document that actually has a AXObjectCache is the top-level
1347 // document. This is because we need to be able to get from any WebCoreAXObject
1348 // to any other WebCoreAXObject on the same page. Using a single cache allows
1349 // lookups across nested webareas (i.e. multiple documents).
1351 if (m_axObjectCache
) {
1352 // return already known top-level cache
1353 if (!ownerElement())
1354 return m_axObjectCache
;
1356 // In some pages with frames, the cache is created before the sub-webarea is
1357 // inserted into the tree. Here, we catch that case and just toss the old
1358 // cache and start over.
1359 // NOTE: This recovery may no longer be needed. I have been unable to trigger
1360 // it again. See rdar://5794454
1361 // FIXME: Can this be fixed when inserting the subframe instead of now?
1362 // FIXME: If this function was called to get the cache in order to remove
1363 // an AXObject, we are now deleting the cache as a whole and returning a
1364 // new empty cache that does not contain the AXObject! That should actually
1365 // be OK. I am concerned about other cases like this where accessing the
1366 // cache blows away the AXObject being operated on.
1367 delete m_axObjectCache
;
1368 m_axObjectCache
= 0;
1371 // ask the top-level document for its cache
1372 Document
* doc
= topDocument();
1374 return doc
->axObjectCache();
1376 // this is the top-level document, so install a new cache
1377 m_axObjectCache
= new AXObjectCache
;
1378 return m_axObjectCache
;
1381 void Document::setVisuallyOrdered()
1383 visuallyOrdered
= true;
1385 renderer()->style()->setVisuallyOrdered(true);
1388 Tokenizer
* Document::createTokenizer()
1390 // FIXME: this should probably pass the frame instead
1391 return new XMLTokenizer(this, view());
1394 void Document::open(Document
* ownerDocument
)
1396 if (ownerDocument
) {
1397 setURL(ownerDocument
->url());
1398 m_cookieURL
= ownerDocument
->cookieURL();
1399 m_securityOrigin
= ownerDocument
->securityOrigin();
1403 if (m_frame
->loader()->isLoadingMainResource() || (tokenizer() && tokenizer()->executingScript()))
1406 if (m_frame
->loader()->state() == FrameStateProvisional
)
1407 m_frame
->loader()->stopAllLoaders();
1413 m_frame
->loader()->didExplicitOpen();
1416 void Document::cancelParsing()
1419 // We have to clear the tokenizer to avoid possibly triggering
1420 // the onload handler when closing as a side effect of a cancel-style
1421 // change, such as opening a new document or closing the window while
1429 void Document::implicitOpen()
1434 m_tokenizer
= createTokenizer();
1438 HTMLElement
* Document::body()
1440 Node
* de
= documentElement();
1444 // try to prefer a FRAMESET element over BODY
1446 for (Node
* i
= de
->firstChild(); i
; i
= i
->nextSibling()) {
1447 if (i
->hasTagName(framesetTag
))
1448 return static_cast<HTMLElement
*>(i
);
1450 if (i
->hasTagName(bodyTag
) && !body
)
1453 return static_cast<HTMLElement
*>(body
);
1456 void Document::setBody(PassRefPtr
<HTMLElement
> newBody
, ExceptionCode
& ec
)
1458 if (!newBody
|| !documentElement()) {
1459 ec
= HIERARCHY_REQUEST_ERR
;
1463 HTMLElement
* b
= body();
1465 documentElement()->appendChild(newBody
, ec
);
1467 documentElement()->replaceChild(newBody
, b
, ec
);
1470 HTMLHeadElement
* Document::head()
1472 Node
* de
= documentElement();
1476 for (Node
* e
= de
->firstChild(); e
; e
= e
->nextSibling())
1477 if (e
->hasTagName(headTag
))
1478 return static_cast<HTMLHeadElement
*>(e
);
1483 void Document::close()
1485 Frame
* frame
= this->frame();
1487 // This code calls implicitClose() if all loading has completed.
1488 FrameLoader
* frameLoader
= frame
->loader();
1489 frameLoader
->endIfNotLoadingMainResource();
1490 frameLoader
->checkCompleted();
1492 // Because we have no frame, we don't know if all loading has completed,
1493 // so we just call implicitClose() immediately. FIXME: This might fire
1494 // the load event prematurely <http://bugs.webkit.org/show_bug.cgi?id=14568>.
1499 void Document::implicitClose()
1501 // If we're in the middle of recalcStyle, we need to defer the close until the style information is accurate and all elements are re-attached.
1502 if (m_inStyleRecalc
) {
1503 m_closeAfterStyleRecalc
= true;
1507 bool wasLocationChangePending
= frame() && frame()->loader()->isScheduledLocationChangePending();
1508 bool doload
= !parsing() && m_tokenizer
&& !m_processingLoadEvent
&& !wasLocationChangePending
;
1513 m_processingLoadEvent
= true;
1515 m_wellFormed
= m_tokenizer
&& m_tokenizer
->wellFormed();
1517 // We have to clear the tokenizer, in case someone document.write()s from the
1518 // onLoad event handler, as in Radar 3206524.
1522 // Parser should have picked up all preloads by now
1523 m_docLoader
->clearPreloads();
1525 // Create a body element if we don't already have one. See Radar 3758785.
1526 if (!this->body() && isHTMLDocument()) {
1527 if (Node
* documentElement
= this->documentElement()) {
1528 ExceptionCode ec
= 0;
1529 documentElement
->appendChild(new HTMLBodyElement(this), ec
);
1534 // FIXME: We kick off the icon loader when the Document is done parsing.
1535 // There are earlier opportunities we could start it:
1536 // -When the <head> finishes parsing
1537 // -When any new HTMLLinkElement is inserted into the document
1538 // But those add a dynamic component to the favicon that has UI
1539 // ramifications, and we need to decide what is the Right Thing To Do(tm)
1542 f
->loader()->startIconLoader();
1544 // Resume the animations (or start them)
1546 f
->animation()->resumeAnimations(this);
1548 dispatchImageLoadEventsNow();
1549 this->dispatchWindowEvent(eventNames().loadEvent
, false, false);
1551 f
->loader()->handledOnloadEvents();
1552 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1553 if (!ownerElement())
1554 printf("onload fired at %d\n", elapsedTime());
1557 m_processingLoadEvent
= false;
1559 // An event handler may have removed the frame
1563 // Make sure both the initial layout and reflow happen after the onload
1564 // fires. This will improve onload scores, and other browsers do it.
1565 // If they wanna cheat, we can too. -dwh
1567 if (frame()->loader()->isScheduledLocationChangePending() && elapsedTime() < cLayoutScheduleThreshold
) {
1568 // Just bail out. Before or during the onload we were shifted to another page.
1569 // The old i-Bench suite does this. When this happens don't bother painting or laying out.
1570 view()->unscheduleRelayout();
1574 frame()->loader()->checkCallImplicitClose();
1576 // Now do our painting/layout, but only if we aren't in a subframe or if we're in a subframe
1577 // that has been sized already. Otherwise, our view size would be incorrect, so doing any
1578 // layout/painting now would be pointless.
1579 if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) {
1582 // Always do a layout after loading if needed.
1583 if (view() && renderer() && (!renderer()->firstChild() || renderer()->needsLayout()))
1586 // Paint immediately after the document is ready. We do this to ensure that any timers set by the
1587 // onload don't have a chance to fire before we would have painted. To avoid over-flushing we only
1588 // worry about this for the top-level document. For platforms that use native widgets for ScrollViews, this
1589 // call does nothing (Mac, wx).
1590 // FIXME: This causes a timing issue with the dispatchDidFinishLoad delegate callback on Mac, so think
1591 // before enabling it, even if Mac becomes viewless later.
1592 // See <rdar://problem/5092361>
1593 if (view() && !ownerElement())
1594 view()->hostWindow()->paint();
1598 if (f
&& renderer() && this == topDocument() && AXObjectCache::accessibilityEnabled())
1599 axObjectCache()->postNotificationToElement(renderer(), "AXLoadComplete");
1603 // FIXME: Officially, time 0 is when the outermost <svg> receives its
1604 // SVGLoad event, but we don't implement those yet. This is close enough
1605 // for now. In some cases we should have fired earlier.
1606 if (svgExtensions())
1607 accessSVGExtensions()->startAnimations();
1611 void Document::setParsing(bool b
)
1614 if (!m_bParsing
&& view())
1615 view()->scheduleRelayout();
1617 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1618 if (!ownerElement() && !m_bParsing
)
1619 printf("Parsing finished at %d\n", elapsedTime());
1623 bool Document::shouldScheduleLayout()
1625 // We can update layout if:
1626 // (a) we actually need a layout
1627 // (b) our stylesheets are all loaded
1628 // (c) we have a <body>
1629 return (renderer() && renderer()->needsLayout() && haveStylesheetsLoaded() &&
1630 documentElement() && documentElement()->renderer() &&
1631 (!documentElement()->hasTagName(htmlTag
) || body()));
1634 int Document::minimumLayoutDelay()
1636 if (m_overMinimumLayoutThreshold
)
1639 int elapsed
= elapsedTime();
1640 m_overMinimumLayoutThreshold
= elapsed
> cLayoutScheduleThreshold
;
1642 // We'll want to schedule the timer to fire at the minimum layout threshold.
1643 return max(0, cLayoutScheduleThreshold
- elapsed
);
1646 int Document::elapsedTime() const
1648 return static_cast<int>((currentTime() - m_startTime
) * 1000);
1651 void Document::write(const String
& text
, Document
* ownerDocument
)
1653 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1654 if (!ownerElement())
1655 printf("Beginning a document.write at %d\n", elapsedTime());
1659 open(ownerDocument
);
1660 ASSERT(m_tokenizer
);
1663 write("<html>", ownerDocument
);
1665 m_tokenizer
->write(text
, false);
1667 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1668 if (!ownerElement())
1669 printf("Ending a document.write at %d\n", elapsedTime());
1673 void Document::writeln(const String
& text
, Document
* ownerDocument
)
1675 write(text
, ownerDocument
);
1676 write("\n", ownerDocument
);
1679 void Document::finishParsing()
1681 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1682 if (!ownerElement())
1683 printf("Received all data at %d\n", elapsedTime());
1686 // Let the tokenizer go through as much data as it can. There will be three possible outcomes after
1687 // finish() is called:
1688 // (1) All remaining data is parsed, document isn't loaded yet
1689 // (2) All remaining data is parsed, document is loaded, tokenizer gets deleted
1690 // (3) Data is still remaining to be parsed.
1692 m_tokenizer
->finish();
1695 void Document::clear()
1702 m_windowEventListeners
.clear();
1705 const KURL
& Document::virtualURL() const
1710 void Document::setURL(const KURL
& url
)
1712 const KURL
& newURL
= url
.isEmpty() ? blankURL() : url
;
1713 if (newURL
== m_url
)
1717 m_documentURI
= m_url
.string();
1721 void Document::setBaseElementURL(const KURL
& baseElementURL
)
1723 m_baseElementURL
= baseElementURL
;
1727 void Document::updateBaseURL()
1729 m_baseURL
= m_baseElementURL
.isEmpty() ? KURL(documentURI()) : m_baseElementURL
;
1730 if (!m_baseURL
.isValid())
1734 m_elemSheet
->setHref(m_baseURL
.string());
1735 if (m_mappedElementSheet
)
1736 m_mappedElementSheet
->setHref(m_baseURL
.string());
1739 void Document::setCSSStyleSheet(const String
& url
, const String
& charset
, const CachedCSSStyleSheet
* sheet
)
1741 m_sheet
= CSSStyleSheet::create(this, url
, charset
);
1742 m_sheet
->parseString(sheet
->sheetText());
1744 updateStyleSelector();
1747 #if FRAME_LOADS_USER_STYLESHEET
1748 void Document::setUserStyleSheet(const String
& sheet
)
1750 if (m_usersheet
!= sheet
) {
1751 m_usersheet
= sheet
;
1752 updateStyleSelector();
1757 String
Document::userStyleSheet() const
1759 #if FRAME_LOADS_USER_STYLESHEET
1762 Page
* page
= this->page();
1765 return page
->userStyleSheet();
1769 CSSStyleSheet
* Document::elementSheet()
1772 m_elemSheet
= CSSStyleSheet::create(this, m_baseURL
.string());
1773 return m_elemSheet
.get();
1776 CSSStyleSheet
* Document::mappedElementSheet()
1778 if (!m_mappedElementSheet
)
1779 m_mappedElementSheet
= CSSStyleSheet::create(this, m_baseURL
.string());
1780 return m_mappedElementSheet
.get();
1783 static Node
* nextNodeWithExactTabIndex(Node
* start
, int tabIndex
, KeyboardEvent
* event
)
1785 // Search is inclusive of start
1786 for (Node
* n
= start
; n
; n
= n
->traverseNextNode())
1787 if (n
->isKeyboardFocusable(event
) && n
->tabIndex() == tabIndex
)
1793 static Node
* previousNodeWithExactTabIndex(Node
* start
, int tabIndex
, KeyboardEvent
* event
)
1795 // Search is inclusive of start
1796 for (Node
* n
= start
; n
; n
= n
->traversePreviousNode())
1797 if (n
->isKeyboardFocusable(event
) && n
->tabIndex() == tabIndex
)
1803 static Node
* nextNodeWithGreaterTabIndex(Node
* start
, int tabIndex
, KeyboardEvent
* event
)
1805 // Search is inclusive of start
1806 int winningTabIndex
= SHRT_MAX
+ 1;
1808 for (Node
* n
= start
; n
; n
= n
->traverseNextNode())
1809 if (n
->isKeyboardFocusable(event
) && n
->tabIndex() > tabIndex
&& n
->tabIndex() < winningTabIndex
) {
1811 winningTabIndex
= n
->tabIndex();
1817 static Node
* previousNodeWithLowerTabIndex(Node
* start
, int tabIndex
, KeyboardEvent
* event
)
1819 // Search is inclusive of start
1820 int winningTabIndex
= 0;
1822 for (Node
* n
= start
; n
; n
= n
->traversePreviousNode())
1823 if (n
->isKeyboardFocusable(event
) && n
->tabIndex() < tabIndex
&& n
->tabIndex() > winningTabIndex
) {
1825 winningTabIndex
= n
->tabIndex();
1831 Node
* Document::nextFocusableNode(Node
* start
, KeyboardEvent
* event
)
1834 // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order
1835 if (start
->tabIndex() < 0) {
1836 for (Node
* n
= start
->traverseNextNode(); n
; n
= n
->traverseNextNode())
1837 if (n
->isKeyboardFocusable(event
) && n
->tabIndex() >= 0)
1841 // First try to find a node with the same tabindex as start that comes after start in the document.
1842 if (Node
* winner
= nextNodeWithExactTabIndex(start
->traverseNextNode(), start
->tabIndex(), event
))
1845 if (start
->tabIndex() == 0)
1846 // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order.
1850 // Look for the first node in the document that:
1851 // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if start is null), and
1852 // 2) comes first in the document, if there's a tie.
1853 if (Node
* winner
= nextNodeWithGreaterTabIndex(this, start
? start
->tabIndex() : 0, event
))
1856 // There are no nodes with a tabindex greater than start's tabindex,
1857 // so find the first node with a tabindex of 0.
1858 return nextNodeWithExactTabIndex(this, 0, event
);
1861 Node
* Document::previousFocusableNode(Node
* start
, KeyboardEvent
* event
)
1864 for (last
= this; last
->lastChild(); last
= last
->lastChild())
1867 // First try to find the last node in the document that comes before start and has the same tabindex as start.
1868 // If start is null, find the last node in the document with a tabindex of 0.
1870 int startingTabIndex
;
1872 startingNode
= start
->traversePreviousNode();
1873 startingTabIndex
= start
->tabIndex();
1875 startingNode
= last
;
1876 startingTabIndex
= 0;
1879 // However, if a node is excluded from the normal tabbing cycle, the previous focusable node is determined by tree order
1880 if (startingTabIndex
< 0) {
1881 for (Node
* n
= startingNode
; n
; n
= n
->traversePreviousNode())
1882 if (n
->isKeyboardFocusable(event
) && n
->tabIndex() >= 0)
1886 if (Node
* winner
= previousNodeWithExactTabIndex(startingNode
, startingTabIndex
, event
))
1889 // There are no nodes before start with the same tabindex as start, so look for a node that:
1890 // 1) has the highest non-zero tabindex (that is less than start's tabindex), and
1891 // 2) comes last in the document, if there's a tie.
1892 startingTabIndex
= (start
&& start
->tabIndex()) ? start
->tabIndex() : SHRT_MAX
;
1893 return previousNodeWithLowerTabIndex(last
, startingTabIndex
, event
);
1896 int Document::nodeAbsIndex(Node
*node
)
1898 ASSERT(node
->document() == this);
1901 for (Node
*n
= node
; n
&& n
!= this; n
= n
->traversePreviousNode())
1906 Node
*Document::nodeWithAbsIndex(int absIndex
)
1909 for (int i
= 0; n
&& (i
< absIndex
); i
++) {
1910 n
= n
->traverseNextNode();
1915 void Document::processHttpEquiv(const String
&equiv
, const String
&content
)
1917 ASSERT(!equiv
.isNull() && !content
.isNull());
1919 Frame
*frame
= this->frame();
1921 if (equalIgnoringCase(equiv
, "default-style")) {
1922 // The preferred style set has been overridden as per section
1923 // 14.3.2 of the HTML4.0 specification. We need to update the
1924 // sheet used variable and then update our style selector.
1925 // For more info, see the test at:
1926 // http://www.hixie.ch/tests/evil/css/import/main/preferred.html
1928 m_selectedStylesheetSet
= content
;
1929 m_preferredStylesheetSet
= content
;
1930 updateStyleSelector();
1931 } else if (equalIgnoringCase(equiv
, "refresh")) {
1934 if (frame
&& parseHTTPRefresh(content
, true, delay
, url
)) {
1936 url
= frame
->loader()->url().string();
1938 url
= completeURL(url
).string();
1939 frame
->loader()->scheduleHTTPRedirection(delay
, url
);
1941 } else if (equalIgnoringCase(equiv
, "set-cookie")) {
1942 // FIXME: make setCookie work on XML documents too; e.g. in case of <html:meta .....>
1943 if (isHTMLDocument())
1944 static_cast<HTMLDocument
*>(this)->setCookie(content
);
1945 } else if (equalIgnoringCase(equiv
, "content-language"))
1946 setContentLanguage(content
);
1947 else if (equalIgnoringCase(equiv
, "x-dns-prefetch-control"))
1948 parseDNSPrefetchControlHeader(content
);
1951 MouseEventWithHitTestResults
Document::prepareMouseEvent(const HitTestRequest
& request
, const IntPoint
& documentPoint
, const PlatformMouseEvent
& event
)
1953 ASSERT(!renderer() || renderer()->isRenderView());
1956 return MouseEventWithHitTestResults(event
, HitTestResult(IntPoint()));
1958 HitTestResult
result(documentPoint
);
1959 renderer()->layer()->hitTest(request
, result
);
1961 if (!request
.readonly
)
1964 return MouseEventWithHitTestResults(event
, result
);
1967 // DOM Section 1.1.1
1968 bool Document::childTypeAllowed(NodeType type
)
1971 case ATTRIBUTE_NODE
:
1972 case CDATA_SECTION_NODE
:
1973 case DOCUMENT_FRAGMENT_NODE
:
1976 case ENTITY_REFERENCE_NODE
:
1979 case XPATH_NAMESPACE_NODE
:
1982 case PROCESSING_INSTRUCTION_NODE
:
1984 case DOCUMENT_TYPE_NODE
:
1986 // Documents may contain no more than one of each of these.
1987 // (One Element and one DocumentType.)
1988 for (Node
* c
= firstChild(); c
; c
= c
->nextSibling())
1989 if (c
->nodeType() == type
)
1996 bool Document::canReplaceChild(Node
* newChild
, Node
* oldChild
)
1999 // ContainerNode::replaceChild will raise a NOT_FOUND_ERR.
2002 if (oldChild
->nodeType() == newChild
->nodeType())
2005 int numDoctypes
= 0;
2006 int numElements
= 0;
2008 // First, check how many doctypes and elements we have, not counting
2009 // the child we're about to remove.
2010 for (Node
* c
= firstChild(); c
; c
= c
->nextSibling()) {
2014 switch (c
->nodeType()) {
2015 case DOCUMENT_TYPE_NODE
:
2026 // Then, see how many doctypes and elements might be added by the new child.
2027 if (newChild
->nodeType() == DOCUMENT_FRAGMENT_NODE
) {
2028 for (Node
* c
= firstChild(); c
; c
= c
->nextSibling()) {
2029 switch (c
->nodeType()) {
2030 case ATTRIBUTE_NODE
:
2031 case CDATA_SECTION_NODE
:
2032 case DOCUMENT_FRAGMENT_NODE
:
2035 case ENTITY_REFERENCE_NODE
:
2038 case XPATH_NAMESPACE_NODE
:
2041 case PROCESSING_INSTRUCTION_NODE
:
2043 case DOCUMENT_TYPE_NODE
:
2052 switch (newChild
->nodeType()) {
2053 case ATTRIBUTE_NODE
:
2054 case CDATA_SECTION_NODE
:
2055 case DOCUMENT_FRAGMENT_NODE
:
2058 case ENTITY_REFERENCE_NODE
:
2061 case XPATH_NAMESPACE_NODE
:
2064 case PROCESSING_INSTRUCTION_NODE
:
2066 case DOCUMENT_TYPE_NODE
:
2075 if (numElements
> 1 || numDoctypes
> 1)
2081 PassRefPtr
<Node
> Document::cloneNode(bool /*deep*/)
2083 // Spec says cloning Document nodes is "implementation dependent"
2084 // so we do not support it...
2088 StyleSheetList
* Document::styleSheets()
2090 return m_styleSheets
.get();
2093 String
Document::preferredStylesheetSet() const
2095 return m_preferredStylesheetSet
;
2098 String
Document::selectedStylesheetSet() const
2100 return m_selectedStylesheetSet
;
2103 void Document::setSelectedStylesheetSet(const String
& aString
)
2105 m_selectedStylesheetSet
= aString
;
2106 updateStyleSelector();
2108 renderer()->repaint();
2111 // This method is called whenever a top-level stylesheet has finished loading.
2112 void Document::removePendingSheet()
2114 // Make sure we knew this sheet was pending, and that our count isn't out of sync.
2115 ASSERT(m_pendingStylesheets
> 0);
2117 m_pendingStylesheets
--;
2119 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2120 if (!ownerElement())
2121 printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets
);
2124 updateStyleSelector();
2126 if (!m_pendingStylesheets
&& m_tokenizer
)
2127 m_tokenizer
->executeScriptsWaitingForStylesheets();
2129 if (!m_pendingStylesheets
&& m_gotoAnchorNeededAfterStylesheetsLoad
&& m_frame
)
2130 m_frame
->loader()->gotoAnchor();
2133 void Document::updateStyleSelector()
2135 // Don't bother updating, since we haven't loaded all our style info yet
2136 // and haven't calculated the style selector for the first time.
2137 if (!m_didCalculateStyleSelector
&& !haveStylesheetsLoaded())
2140 if (didLayoutWithPendingStylesheets() && m_pendingStylesheets
<= 0) {
2141 m_pendingSheetLayout
= IgnoreLayoutWithPendingSheets
;
2143 renderer()->repaint();
2146 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2147 if (!ownerElement())
2148 printf("Beginning update of style selector at time %d.\n", elapsedTime());
2151 recalcStyleSelector();
2154 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2155 if (!ownerElement())
2156 printf("Finished update of style selector at time %d\n", elapsedTime());
2160 renderer()->setNeedsLayoutAndPrefWidthsRecalc();
2162 view()->scheduleRelayout();
2166 void Document::addStyleSheetCandidateNode(Node
* node
, bool createdByParser
)
2168 // Until the <body> exists, we have no choice but to compare document positions,
2169 // since styles outside of the body and head continue to be shunted into the head
2170 // (and thus can shift to end up before dynamically added DOM content that is also
2171 // outside the body).
2172 if ((createdByParser
&& body()) || m_styleSheetCandidateNodes
.isEmpty()) {
2173 m_styleSheetCandidateNodes
.add(node
);
2177 // Determine an appropriate insertion point.
2178 ListHashSet
<Node
*>::iterator begin
= m_styleSheetCandidateNodes
.begin();
2179 ListHashSet
<Node
*>::iterator end
= m_styleSheetCandidateNodes
.end();
2180 ListHashSet
<Node
*>::iterator it
= end
;
2181 Node
* followingNode
= 0;
2185 unsigned short position
= n
->compareDocumentPosition(node
);
2186 if (position
== DOCUMENT_POSITION_FOLLOWING
) {
2187 m_styleSheetCandidateNodes
.insertBefore(followingNode
, node
);
2191 } while (it
!= begin
);
2193 m_styleSheetCandidateNodes
.insertBefore(followingNode
, node
);
2196 void Document::removeStyleSheetCandidateNode(Node
* node
)
2198 m_styleSheetCandidateNodes
.remove(node
);
2201 void Document::recalcStyleSelector()
2203 if (!renderer() || !attached())
2206 StyleSheetVector sheets
;
2208 bool matchAuthorAndUserStyles
= true;
2209 if (Settings
* settings
= this->settings())
2210 matchAuthorAndUserStyles
= settings
->authorAndUserStylesEnabled();
2212 ListHashSet
<Node
*>::iterator begin
= m_styleSheetCandidateNodes
.begin();
2213 ListHashSet
<Node
*>::iterator end
= m_styleSheetCandidateNodes
.end();
2214 if (!matchAuthorAndUserStyles
)
2216 for (ListHashSet
<Node
*>::iterator it
= begin
; it
!= end
; ++it
) {
2219 StyleSheet
* sheet
= 0;
2221 if (n
->nodeType() == PROCESSING_INSTRUCTION_NODE
) {
2222 // Processing instruction (XML documents only)
2223 ProcessingInstruction
* pi
= static_cast<ProcessingInstruction
*>(n
);
2224 sheet
= pi
->sheet();
2226 // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806>
2227 if (pi
->isXSL() && !transformSourceDocument()) {
2228 // Don't apply XSL transforms until loading is finished.
2230 applyXSLTransform(pi
);
2234 if (!sheet
&& !pi
->localHref().isEmpty()) {
2235 // Processing instruction with reference to an element in this document - e.g.
2236 // <?xml-stylesheet href="#mystyle">, with the element
2237 // <foo id="mystyle">heading { color: red; }</foo> at some location in
2239 Element
* elem
= getElementById(pi
->localHref().impl());
2241 String
sheetText("");
2242 for (Node
* c
= elem
->firstChild(); c
; c
= c
->nextSibling()) {
2243 if (c
->nodeType() == TEXT_NODE
|| c
->nodeType() == CDATA_SECTION_NODE
)
2244 sheetText
+= c
->nodeValue();
2247 RefPtr
<CSSStyleSheet
> cssSheet
= CSSStyleSheet::create(this);
2248 cssSheet
->parseString(sheetText
);
2249 pi
->setCSSStyleSheet(cssSheet
);
2250 sheet
= cssSheet
.get();
2253 } else if (n
->isHTMLElement() && (n
->hasTagName(linkTag
) || n
->hasTagName(styleTag
))
2255 || (n
->isSVGElement() && n
->hasTagName(SVGNames::styleTag
))
2258 Element
* e
= static_cast<Element
*>(n
);
2259 AtomicString title
= e
->getAttribute(titleAttr
);
2260 bool enabledViaScript
= false;
2261 if (e
->hasLocalName(linkTag
)) {
2263 HTMLLinkElement
* l
= static_cast<HTMLLinkElement
*>(n
);
2264 if (l
->isDisabled())
2266 enabledViaScript
= l
->isEnabledViaScript();
2267 if (l
->isLoading()) {
2268 // it is loading but we should still decide which style sheet set to use
2269 if (!enabledViaScript
&& !title
.isEmpty() && m_preferredStylesheetSet
.isEmpty()) {
2270 const AtomicString
& rel
= e
->getAttribute(relAttr
);
2271 if (!rel
.contains("alternate")) {
2272 m_preferredStylesheetSet
= title
;
2273 m_selectedStylesheetSet
= title
;
2282 // Get the current preferred styleset. This is the
2283 // set of sheets that will be enabled.
2285 if (n
->isSVGElement() && n
->hasTagName(SVGNames::styleTag
))
2286 sheet
= static_cast<SVGStyleElement
*>(n
)->sheet();
2289 if (e
->hasLocalName(linkTag
))
2290 sheet
= static_cast<HTMLLinkElement
*>(n
)->sheet();
2293 sheet
= static_cast<HTMLStyleElement
*>(n
)->sheet();
2295 // Check to see if this sheet belongs to a styleset
2296 // (thus making it PREFERRED or ALTERNATE rather than
2298 if (!enabledViaScript
&& !title
.isEmpty()) {
2299 // Yes, we have a title.
2300 if (m_preferredStylesheetSet
.isEmpty()) {
2301 // No preferred set has been established. If
2302 // we are NOT an alternate sheet, then establish
2303 // us as the preferred set. Otherwise, just ignore
2305 AtomicString rel
= e
->getAttribute(relAttr
);
2306 if (e
->hasLocalName(styleTag
) || !rel
.contains("alternate"))
2307 m_preferredStylesheetSet
= m_selectedStylesheetSet
= title
;
2310 if (title
!= m_preferredStylesheetSet
)
2316 sheets
.append(sheet
);
2319 m_styleSheets
->swap(sheets
);
2321 // Create a new style selector
2322 delete m_styleSelector
;
2323 m_styleSelector
= new CSSStyleSelector(this, userStyleSheet(), m_styleSheets
.get(), m_mappedElementSheet
.get(), !inCompatMode(), matchAuthorAndUserStyles
);
2324 m_didCalculateStyleSelector
= true;
2327 void Document::setHoverNode(PassRefPtr
<Node
> newHoverNode
)
2329 m_hoverNode
= newHoverNode
;
2332 void Document::setActiveNode(PassRefPtr
<Node
> newActiveNode
)
2334 m_activeNode
= newActiveNode
;
2337 void Document::focusedNodeRemoved()
2342 void Document::removeFocusedNodeOfSubtree(Node
* node
, bool amongChildrenOnly
)
2344 if (!m_focusedNode
|| this->inPageCache()) // If the document is in the page cache, then we don't need to clear out the focused node.
2347 bool nodeInSubtree
= false;
2348 if (amongChildrenOnly
)
2349 nodeInSubtree
= m_focusedNode
->isDescendantOf(node
);
2351 nodeInSubtree
= (m_focusedNode
== node
) || m_focusedNode
->isDescendantOf(node
);
2354 document()->focusedNodeRemoved();
2357 void Document::hoveredNodeDetached(Node
* node
)
2359 if (!m_hoverNode
|| (node
!= m_hoverNode
&& (!m_hoverNode
->isTextNode() || node
!= m_hoverNode
->parent())))
2362 m_hoverNode
= node
->parent();
2363 while (m_hoverNode
&& !m_hoverNode
->renderer())
2364 m_hoverNode
= m_hoverNode
->parent();
2366 frame()->eventHandler()->scheduleHoverStateUpdate();
2369 void Document::activeChainNodeDetached(Node
* node
)
2371 if (!m_activeNode
|| (node
!= m_activeNode
&& (!m_activeNode
->isTextNode() || node
!= m_activeNode
->parent())))
2374 m_activeNode
= node
->parent();
2375 while (m_activeNode
&& !m_activeNode
->renderer())
2376 m_activeNode
= m_activeNode
->parent();
2379 #if ENABLE(DASHBOARD_SUPPORT)
2380 const Vector
<DashboardRegionValue
>& Document::dashboardRegions() const
2382 return m_dashboardRegions
;
2385 void Document::setDashboardRegions(const Vector
<DashboardRegionValue
>& regions
)
2387 m_dashboardRegions
= regions
;
2388 setDashboardRegionsDirty(false);
2392 bool Document::setFocusedNode(PassRefPtr
<Node
> newFocusedNode
)
2394 // Make sure newFocusedNode is actually in this document
2395 if (newFocusedNode
&& (newFocusedNode
->document() != this))
2398 if (m_focusedNode
== newFocusedNode
)
2404 bool focusChangeBlocked
= false;
2405 RefPtr
<Node
> oldFocusedNode
= m_focusedNode
;
2408 // Remove focus from the existing focus node (if any)
2409 if (oldFocusedNode
&& !oldFocusedNode
->m_inDetach
) {
2410 if (oldFocusedNode
->active())
2411 oldFocusedNode
->setActive(false);
2413 oldFocusedNode
->setFocus(false);
2415 // Dispatch a change event for text fields or textareas that have been edited
2416 RenderObject
* r
= static_cast<RenderObject
*>(oldFocusedNode
.get()->renderer());
2417 if (r
&& (r
->isTextArea() || r
->isTextField()) && r
->isEdited()) {
2418 EventTargetNodeCast(oldFocusedNode
.get())->dispatchEventForType(eventNames().changeEvent
, true, false);
2419 if ((r
= static_cast<RenderObject
*>(oldFocusedNode
.get()->renderer())))
2420 r
->setEdited(false);
2423 // Dispatch the blur event and let the node do any other blur related activities (important for text fields)
2424 EventTargetNodeCast(oldFocusedNode
.get())->dispatchBlurEvent();
2426 if (m_focusedNode
) {
2427 // handler shifted focus
2428 focusChangeBlocked
= true;
2431 EventTargetNodeCast(oldFocusedNode
.get())->dispatchUIEvent(eventNames().DOMFocusOutEvent
);
2432 if (m_focusedNode
) {
2433 // handler shifted focus
2434 focusChangeBlocked
= true;
2437 if ((oldFocusedNode
.get() == this) && oldFocusedNode
->hasOneRef())
2440 if (oldFocusedNode
.get() == oldFocusedNode
->rootEditableElement())
2441 frame()->editor()->didEndEditing();
2444 if (newFocusedNode
) {
2445 if (newFocusedNode
== newFocusedNode
->rootEditableElement() && !acceptsEditingFocus(newFocusedNode
.get())) {
2446 // delegate blocks focus change
2447 focusChangeBlocked
= true;
2448 goto SetFocusedNodeDone
;
2450 // Set focus on the new node
2451 m_focusedNode
= newFocusedNode
.get();
2453 // Dispatch the focus event and let the node do any other focus related activities (important for text fields)
2454 EventTargetNodeCast(m_focusedNode
.get())->dispatchFocusEvent();
2456 if (m_focusedNode
!= newFocusedNode
) {
2457 // handler shifted focus
2458 focusChangeBlocked
= true;
2459 goto SetFocusedNodeDone
;
2461 EventTargetNodeCast(m_focusedNode
.get())->dispatchUIEvent(eventNames().DOMFocusInEvent
);
2462 if (m_focusedNode
!= newFocusedNode
) {
2463 // handler shifted focus
2464 focusChangeBlocked
= true;
2465 goto SetFocusedNodeDone
;
2467 m_focusedNode
->setFocus();
2469 if (m_focusedNode
.get() == m_focusedNode
->rootEditableElement())
2470 frame()->editor()->didBeginEditing();
2472 // eww, I suck. set the qt focus correctly
2473 // ### find a better place in the code for this
2475 Widget
*focusWidget
= widgetForNode(m_focusedNode
.get());
2477 // Make sure a widget has the right size before giving it focus.
2478 // Otherwise, we are testing edge cases of the Widget code.
2479 // Specifically, in WebCore this does not work well for text fields.
2481 // Re-get the widget in case updating the layout changed things.
2482 focusWidget
= widgetForNode(m_focusedNode
.get());
2485 focusWidget
->setFocus();
2492 if (!focusChangeBlocked
&& m_focusedNode
&& AXObjectCache::accessibilityEnabled())
2493 axObjectCache()->handleFocusedUIElementChanged();
2498 return !focusChangeBlocked
;
2501 void Document::setCSSTarget(Node
* n
)
2504 m_cssTarget
->setChanged();
2510 Node
* Document::getCSSTarget() const
2515 void Document::attachNodeIterator(NodeIterator
*ni
)
2517 m_nodeIterators
.add(ni
);
2520 void Document::detachNodeIterator(NodeIterator
*ni
)
2522 m_nodeIterators
.remove(ni
);
2525 void Document::nodeChildrenChanged(ContainerNode
* container
)
2527 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) {
2528 HashSet
<Range
*>::const_iterator end
= m_ranges
.end();
2529 for (HashSet
<Range
*>::const_iterator it
= m_ranges
.begin(); it
!= end
; ++it
)
2530 (*it
)->nodeChildrenChanged(container
);
2534 void Document::nodeWillBeRemoved(Node
* n
)
2536 HashSet
<NodeIterator
*>::const_iterator nodeIteratorsEnd
= m_nodeIterators
.end();
2537 for (HashSet
<NodeIterator
*>::const_iterator it
= m_nodeIterators
.begin(); it
!= nodeIteratorsEnd
; ++it
)
2538 (*it
)->nodeWillBeRemoved(n
);
2540 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) {
2541 HashSet
<Range
*>::const_iterator rangesEnd
= m_ranges
.end();
2542 for (HashSet
<Range
*>::const_iterator it
= m_ranges
.begin(); it
!= rangesEnd
; ++it
)
2543 (*it
)->nodeWillBeRemoved(n
);
2546 if (Frame
* frame
= this->frame()) {
2547 frame
->selection()->nodeWillBeRemoved(n
);
2548 frame
->dragCaretController()->nodeWillBeRemoved(n
);
2552 void Document::textInserted(Node
* text
, unsigned offset
, unsigned length
)
2554 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) {
2555 HashSet
<Range
*>::const_iterator end
= m_ranges
.end();
2556 for (HashSet
<Range
*>::const_iterator it
= m_ranges
.begin(); it
!= end
; ++it
)
2557 (*it
)->textInserted(text
, offset
, length
);
2560 // Update the markers for spelling and grammar checking.
2561 shiftMarkers(text
, offset
, length
);
2564 void Document::textRemoved(Node
* text
, unsigned offset
, unsigned length
)
2566 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) {
2567 HashSet
<Range
*>::const_iterator end
= m_ranges
.end();
2568 for (HashSet
<Range
*>::const_iterator it
= m_ranges
.begin(); it
!= end
; ++it
)
2569 (*it
)->textRemoved(text
, offset
, length
);
2572 // Update the markers for spelling and grammar checking.
2573 removeMarkers(text
, offset
, length
);
2574 shiftMarkers(text
, offset
+ length
, 0 - length
);
2577 void Document::textNodesMerged(Text
* oldNode
, unsigned offset
)
2579 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) {
2580 NodeWithIndex
oldNodeWithIndex(oldNode
);
2581 HashSet
<Range
*>::const_iterator end
= m_ranges
.end();
2582 for (HashSet
<Range
*>::const_iterator it
= m_ranges
.begin(); it
!= end
; ++it
)
2583 (*it
)->textNodesMerged(oldNodeWithIndex
, offset
);
2586 // FIXME: This should update markers for spelling and grammar checking.
2589 void Document::textNodeSplit(Text
* oldNode
)
2591 if (!page() || !page()->settings()->rangeMutationDisabledForOldAppleMail()) {
2592 HashSet
<Range
*>::const_iterator end
= m_ranges
.end();
2593 for (HashSet
<Range
*>::const_iterator it
= m_ranges
.begin(); it
!= end
; ++it
)
2594 (*it
)->textNodeSplit(oldNode
);
2597 // FIXME: This should update markers for spelling and grammar checking.
2600 // FIXME: eventually, this should return a DOMWindow stored in the document.
2601 DOMWindow
* Document::domWindow() const
2605 return frame()->domWindow();
2608 PassRefPtr
<Event
> Document::createEvent(const String
& eventType
, ExceptionCode
& ec
)
2610 if (eventType
== "UIEvents" || eventType
== "UIEvent")
2611 return UIEvent::create();
2612 if (eventType
== "MouseEvents" || eventType
== "MouseEvent")
2613 return MouseEvent::create();
2614 if (eventType
== "MutationEvents" || eventType
== "MutationEvent")
2615 return MutationEvent::create();
2616 if (eventType
== "KeyboardEvents" || eventType
== "KeyboardEvent")
2617 return KeyboardEvent::create();
2618 if (eventType
== "HTMLEvents" || eventType
== "Event" || eventType
== "Events")
2619 return Event::create();
2620 if (eventType
== "ProgressEvent")
2621 return ProgressEvent::create();
2622 if (eventType
== "TextEvent")
2623 return TextEvent::create();
2624 if (eventType
== "OverflowEvent")
2625 return OverflowEvent::create();
2626 if (eventType
== "WheelEvent")
2627 return WheelEvent::create();
2629 if (eventType
== "SVGEvents")
2630 return Event::create();
2631 if (eventType
== "SVGZoomEvents")
2632 return SVGZoomEvent::create();
2634 if (eventType
== "MessageEvent")
2635 return MessageEvent::create();
2636 if (eventType
== "WebKitAnimationEvent")
2637 return WebKitAnimationEvent::create();
2638 if (eventType
== "WebKitTransitionEvent")
2639 return WebKitTransitionEvent::create();
2640 ec
= NOT_SUPPORTED_ERR
;
2644 void Document::addListenerTypeIfNeeded(const AtomicString
& eventType
)
2646 if (eventType
== eventNames().DOMSubtreeModifiedEvent
)
2647 addListenerType(DOMSUBTREEMODIFIED_LISTENER
);
2648 else if (eventType
== eventNames().DOMNodeInsertedEvent
)
2649 addListenerType(DOMNODEINSERTED_LISTENER
);
2650 else if (eventType
== eventNames().DOMNodeRemovedEvent
)
2651 addListenerType(DOMNODEREMOVED_LISTENER
);
2652 else if (eventType
== eventNames().DOMNodeRemovedFromDocumentEvent
)
2653 addListenerType(DOMNODEREMOVEDFROMDOCUMENT_LISTENER
);
2654 else if (eventType
== eventNames().DOMNodeInsertedIntoDocumentEvent
)
2655 addListenerType(DOMNODEINSERTEDINTODOCUMENT_LISTENER
);
2656 else if (eventType
== eventNames().DOMAttrModifiedEvent
)
2657 addListenerType(DOMATTRMODIFIED_LISTENER
);
2658 else if (eventType
== eventNames().DOMCharacterDataModifiedEvent
)
2659 addListenerType(DOMCHARACTERDATAMODIFIED_LISTENER
);
2660 else if (eventType
== eventNames().overflowchangedEvent
)
2661 addListenerType(OVERFLOWCHANGED_LISTENER
);
2662 else if (eventType
== eventNames().webkitAnimationStartEvent
)
2663 addListenerType(ANIMATIONSTART_LISTENER
);
2664 else if (eventType
== eventNames().webkitAnimationEndEvent
)
2665 addListenerType(ANIMATIONEND_LISTENER
);
2666 else if (eventType
== eventNames().webkitAnimationIterationEvent
)
2667 addListenerType(ANIMATIONITERATION_LISTENER
);
2668 else if (eventType
== eventNames().webkitTransitionEndEvent
)
2669 addListenerType(TRANSITIONEND_LISTENER
);
2672 CSSStyleDeclaration
* Document::getOverrideStyle(Element
*, const String
&)
2677 void Document::handleWindowEvent(Event
* evt
, bool useCapture
)
2679 if (m_windowEventListeners
.isEmpty())
2682 // if any html event listeners are registered on the window, then dispatch them here
2683 RegisteredEventListenerList listenersCopy
= m_windowEventListeners
;
2684 RegisteredEventListenerList::iterator it
= listenersCopy
.begin();
2686 for (; it
!= listenersCopy
.end(); ++it
)
2687 if ((*it
)->eventType() == evt
->type() && (*it
)->useCapture() == useCapture
&& !(*it
)->removed())
2688 (*it
)->listener()->handleEvent(evt
, true);
2691 void Document::setWindowInlineEventListenerForType(const AtomicString
& eventType
, PassRefPtr
<EventListener
> listener
)
2693 // If we already have it we don't want removeWindowEventListener to delete it
2694 removeWindowInlineEventListenerForType(eventType
);
2696 addWindowEventListener(eventType
, listener
, false);
2699 EventListener
* Document::windowInlineEventListenerForType(const AtomicString
& eventType
)
2701 RegisteredEventListenerList::iterator it
= m_windowEventListeners
.begin();
2702 for (; it
!= m_windowEventListeners
.end(); ++it
) {
2703 if ((*it
)->eventType() == eventType
&& (*it
)->listener()->isInline())
2704 return (*it
)->listener();
2709 void Document::removeWindowInlineEventListenerForType(const AtomicString
& eventType
)
2711 RegisteredEventListenerList::iterator it
= m_windowEventListeners
.begin();
2712 for (; it
!= m_windowEventListeners
.end(); ++it
) {
2713 if ((*it
)->eventType() == eventType
&& (*it
)->listener()->isInline()) {
2714 if (eventType
== eventNames().unloadEvent
)
2715 removePendingFrameUnloadEventCount();
2716 else if (eventType
== eventNames().beforeunloadEvent
)
2717 removePendingFrameBeforeUnloadEventCount();
2718 m_windowEventListeners
.remove(it
);
2724 void Document::addWindowEventListener(const AtomicString
& eventType
, PassRefPtr
<EventListener
> listener
, bool useCapture
)
2726 if (eventType
== eventNames().unloadEvent
)
2727 addPendingFrameUnloadEventCount();
2728 else if (eventType
== eventNames().beforeunloadEvent
)
2729 addPendingFrameBeforeUnloadEventCount();
2730 // Remove existing identical listener set with identical arguments.
2731 // The DOM 2 spec says that "duplicate instances are discarded" in this case.
2732 removeWindowEventListener(eventType
, listener
.get(), useCapture
);
2733 m_windowEventListeners
.append(RegisteredEventListener::create(eventType
, listener
, useCapture
));
2736 void Document::removeWindowEventListener(const AtomicString
& eventType
, EventListener
* listener
, bool useCapture
)
2738 RegisteredEventListenerList::iterator it
= m_windowEventListeners
.begin();
2739 for (; it
!= m_windowEventListeners
.end(); ++it
) {
2740 RegisteredEventListener
& r
= **it
;
2741 if (r
.eventType() == eventType
&& r
.listener() == listener
&& r
.useCapture() == useCapture
) {
2742 if (eventType
== eventNames().unloadEvent
)
2743 removePendingFrameUnloadEventCount();
2744 else if (eventType
== eventNames().beforeunloadEvent
)
2745 removePendingFrameBeforeUnloadEventCount();
2746 m_windowEventListeners
.remove(it
);
2752 bool Document::hasWindowEventListener(const AtomicString
& eventType
)
2754 RegisteredEventListenerList::iterator it
= m_windowEventListeners
.begin();
2755 for (; it
!= m_windowEventListeners
.end(); ++it
)
2756 if ((*it
)->eventType() == eventType
)
2761 void Document::addPendingFrameUnloadEventCount()
2764 m_frame
->eventHandler()->addPendingFrameUnloadEventCount();
2767 void Document::removePendingFrameUnloadEventCount()
2770 m_frame
->eventHandler()->removePendingFrameUnloadEventCount();
2773 void Document::addPendingFrameBeforeUnloadEventCount()
2776 m_frame
->eventHandler()->addPendingFrameBeforeUnloadEventCount();
2779 void Document::removePendingFrameBeforeUnloadEventCount()
2782 m_frame
->eventHandler()->removePendingFrameBeforeUnloadEventCount();
2785 PassRefPtr
<EventListener
> Document::createEventListener(const String
& functionName
, const String
& code
, Node
* node
)
2787 Frame
* frm
= frame();
2788 if (!frm
|| !frm
->script()->isEnabled())
2792 if (node
? node
->isSVGElement() : isSVGDocument())
2793 return frm
->script()->createSVGEventHandler(functionName
, code
, node
);
2796 // We may want to treat compound document event handlers in a different way, in future.
2797 return frm
->script()->createInlineEventListener(functionName
, code
, node
);
2800 void Document::setWindowInlineEventListenerForTypeAndAttribute(const AtomicString
& eventType
, Attribute
* attr
)
2802 setWindowInlineEventListenerForType(eventType
, createEventListener(attr
->localName().string(), attr
->value(), 0));
2805 void Document::dispatchImageLoadEventSoon(ImageLoader
* image
)
2807 m_imageLoadEventDispatchSoonList
.append(image
);
2808 if (!m_imageLoadEventTimer
.isActive())
2809 m_imageLoadEventTimer
.startOneShot(0);
2812 void Document::removeImage(ImageLoader
* image
)
2814 // Remove instances of this image from both lists.
2815 // Use loops because we allow multiple instances to get into the lists.
2816 while (m_imageLoadEventDispatchSoonList
.removeRef(image
)) { }
2817 while (m_imageLoadEventDispatchingList
.removeRef(image
)) { }
2818 if (m_imageLoadEventDispatchSoonList
.isEmpty())
2819 m_imageLoadEventTimer
.stop();
2822 void Document::dispatchImageLoadEventsNow()
2824 // need to avoid re-entering this function; if new dispatches are
2825 // scheduled before the parent finishes processing the list, they
2826 // will set a timer and eventually be processed
2827 if (!m_imageLoadEventDispatchingList
.isEmpty())
2830 m_imageLoadEventTimer
.stop();
2832 m_imageLoadEventDispatchingList
= m_imageLoadEventDispatchSoonList
;
2833 m_imageLoadEventDispatchSoonList
.clear();
2834 for (DeprecatedPtrListIterator
<ImageLoader
> it(m_imageLoadEventDispatchingList
); it
.current();) {
2835 ImageLoader
* image
= it
.current();
2836 // Must advance iterator *before* dispatching call.
2837 // Otherwise, it might be advanced automatically if dispatching the call had a side effect
2838 // of destroying the current ImageLoader, and then we would advance past the *next* item,
2839 // missing one altogether.
2841 image
->dispatchLoadEvent();
2843 m_imageLoadEventDispatchingList
.clear();
2846 void Document::imageLoadEventTimerFired(Timer
<Document
>*)
2848 dispatchImageLoadEventsNow();
2851 Element
* Document::ownerElement() const
2855 return frame()->ownerElement();
2858 String
Document::cookie() const
2860 if (page() && !page()->cookieEnabled())
2863 return cookies(this, cookieURL());
2866 void Document::setCookie(const String
& value
)
2868 if (page() && !page()->cookieEnabled())
2871 setCookies(this, cookieURL(), policyBaseURL(), value
);
2874 String
Document::referrer() const
2877 return frame()->loader()->referrer();
2881 String
Document::domain() const
2883 return m_securityOrigin
->domain();
2886 void Document::setDomain(const String
& newDomain
)
2888 // Both NS and IE specify that changing the domain is only allowed when
2889 // the new domain is a suffix of the old domain.
2891 // FIXME: We should add logging indicating why a domain was not allowed.
2893 // If the new domain is the same as the old domain, still call
2894 // m_securityOrigin.setDomainForDOM. This will change the
2895 // security check behavior. For example, if a page loaded on port 8000
2896 // assigns its current domain using document.domain, the page will
2897 // allow other pages loaded on different ports in the same domain that
2898 // have also assigned to access this page.
2899 if (equalIgnoringCase(domain(), newDomain
)) {
2900 m_securityOrigin
->setDomainFromDOM(newDomain
);
2904 int oldLength
= domain().length();
2905 int newLength
= newDomain
.length();
2906 // e.g. newDomain = webkit.org (10) and domain() = www.webkit.org (14)
2907 if (newLength
>= oldLength
)
2910 String test
= domain();
2911 // Check that it's a subdomain, not e.g. "ebkit.org"
2912 if (test
[oldLength
- newLength
- 1] != '.')
2915 // Now test is "webkit.org" from domain()
2916 // and we check that it's the same thing as newDomain
2917 test
.remove(0, oldLength
- newLength
);
2918 if (test
!= newDomain
)
2921 m_securityOrigin
->setDomainFromDOM(newDomain
);
2924 String
Document::lastModified() const
2929 DocumentLoader
* loader
= f
->loader()->documentLoader();
2932 return loader
->response().httpHeaderField("Last-Modified");
2935 static bool isValidNameNonASCII(const UChar
* characters
, unsigned length
)
2940 U16_NEXT(characters
, i
, length
, c
)
2941 if (!isValidNameStart(c
))
2944 while (i
< length
) {
2945 U16_NEXT(characters
, i
, length
, c
)
2946 if (!isValidNamePart(c
))
2953 static inline bool isValidNameASCII(const UChar
* characters
, unsigned length
)
2955 UChar c
= characters
[0];
2956 if (!(isASCIIAlpha(c
) || c
== ':' || c
== '_'))
2959 for (unsigned i
= 1; i
< length
; ++i
) {
2961 if (!(isASCIIAlphanumeric(c
) || c
== ':' || c
== '_' || c
== '-' || c
== '.'))
2968 bool Document::isValidName(const String
& name
)
2970 unsigned length
= name
.length();
2974 const UChar
* characters
= name
.characters();
2975 return isValidNameASCII(characters
, length
) || isValidNameNonASCII(characters
, length
);
2978 bool Document::parseQualifiedName(const String
& qualifiedName
, String
& prefix
, String
& localName
, ExceptionCode
& ec
)
2980 unsigned length
= qualifiedName
.length();
2983 ec
= INVALID_CHARACTER_ERR
;
2987 bool nameStart
= true;
2988 bool sawColon
= false;
2991 const UChar
* s
= qualifiedName
.characters();
2992 for (unsigned i
= 0; i
< length
;) {
2994 U16_NEXT(s
, i
, length
, c
)
2998 return false; // multiple colons: not allowed
3003 } else if (nameStart
) {
3004 if (!isValidNameStart(c
)) {
3005 ec
= INVALID_CHARACTER_ERR
;
3010 if (!isValidNamePart(c
)) {
3011 ec
= INVALID_CHARACTER_ERR
;
3019 localName
= qualifiedName
;
3021 prefix
= qualifiedName
.substring(0, colonPos
);
3022 if (prefix
.isEmpty()) {
3026 localName
= qualifiedName
.substring(colonPos
+ 1);
3029 if (localName
.isEmpty()) {
3037 void Document::addImageMap(HTMLMapElement
* imageMap
)
3039 const AtomicString
& name
= imageMap
->getName();
3043 // Add the image map, unless there's already another with that name.
3044 // "First map wins" is the rule other browsers seem to implement.
3045 m_imageMapsByName
.add(name
.impl(), imageMap
);
3048 void Document::removeImageMap(HTMLMapElement
* imageMap
)
3050 // Remove the image map by name.
3051 // But don't remove some other image map that just happens to have the same name.
3052 // FIXME: Use a HashCountedSet as we do for IDs to find the first remaining map
3053 // once a map has been removed.
3054 const AtomicString
& name
= imageMap
->getName();
3058 ImageMapsByName::iterator it
= m_imageMapsByName
.find(name
.impl());
3059 if (it
!= m_imageMapsByName
.end() && it
->second
== imageMap
)
3060 m_imageMapsByName
.remove(it
);
3063 HTMLMapElement
*Document::getImageMap(const String
& url
) const
3067 int hashPos
= url
.find('#');
3068 String name
= (hashPos
< 0 ? url
: url
.substring(hashPos
+ 1)).impl();
3069 AtomicString mapName
= isHTMLDocument() ? name
.lower() : name
;
3070 return m_imageMapsByName
.get(mapName
.impl());
3073 void Document::setDecoder(PassRefPtr
<TextResourceDecoder
> decoder
)
3075 m_decoder
= decoder
;
3078 UChar
Document::backslashAsCurrencySymbol() const
3082 return m_decoder
->encoding().backslashAsCurrencySymbol();
3085 KURL
Document::completeURL(const String
& url
) const
3087 // Always return a null URL when passed a null string.
3088 // FIXME: Should we change the KURL constructor to have this behavior?
3092 return KURL(m_baseURL
, url
);
3093 return KURL(m_baseURL
, url
, m_decoder
->encoding());
3096 bool Document::inPageCache()
3098 return m_inPageCache
;
3101 void Document::setInPageCache(bool flag
)
3103 if (m_inPageCache
== flag
)
3106 m_inPageCache
= flag
;
3108 ASSERT(m_savedRenderer
== 0);
3109 m_savedRenderer
= renderer();
3110 if (FrameView
* v
= view())
3111 v
->resetScrollbars();
3113 ASSERT(renderer() == 0 || renderer() == m_savedRenderer
);
3114 ASSERT(m_renderArena
);
3115 setRenderer(m_savedRenderer
);
3116 m_savedRenderer
= 0;
3120 void Document::documentWillBecomeInactive()
3122 HashSet
<Element
*>::iterator end
= m_documentActivationCallbackElements
.end();
3123 for (HashSet
<Element
*>::iterator i
= m_documentActivationCallbackElements
.begin(); i
!= end
; ++i
)
3124 (*i
)->documentWillBecomeInactive();
3127 void Document::documentDidBecomeActive()
3129 HashSet
<Element
*>::iterator end
= m_documentActivationCallbackElements
.end();
3130 for (HashSet
<Element
*>::iterator i
= m_documentActivationCallbackElements
.begin(); i
!= end
; ++i
)
3131 (*i
)->documentDidBecomeActive();
3134 void Document::registerForDocumentActivationCallbacks(Element
* e
)
3136 m_documentActivationCallbackElements
.add(e
);
3139 void Document::unregisterForDocumentActivationCallbacks(Element
* e
)
3141 m_documentActivationCallbackElements
.remove(e
);
3144 void Document::setShouldCreateRenderers(bool f
)
3146 m_createRenderers
= f
;
3149 bool Document::shouldCreateRenderers()
3151 return m_createRenderers
;
3154 // Support for Javascript execCommand, and related methods
3156 static Editor::Command
command(Document
* document
, const String
& commandName
, bool userInterface
= false)
3158 Frame
* frame
= document
->frame();
3159 if (!frame
|| frame
->document() != document
)
3160 return Editor::Command();
3161 return frame
->editor()->command(commandName
,
3162 userInterface
? CommandFromDOMWithUserInterface
: CommandFromDOM
);
3165 bool Document::execCommand(const String
& commandName
, bool userInterface
, const String
& value
)
3167 return command(this, commandName
, userInterface
).execute(value
);
3170 bool Document::queryCommandEnabled(const String
& commandName
)
3172 return command(this, commandName
).isEnabled();
3175 bool Document::queryCommandIndeterm(const String
& commandName
)
3177 return command(this, commandName
).state() == MixedTriState
;
3180 bool Document::queryCommandState(const String
& commandName
)
3182 return command(this, commandName
).state() != FalseTriState
;
3185 bool Document::queryCommandSupported(const String
& commandName
)
3187 return command(this, commandName
).isSupported();
3190 String
Document::queryCommandValue(const String
& commandName
)
3192 return command(this, commandName
).value();
3195 static IntRect
placeholderRectForMarker()
3197 return IntRect(-1, -1, -1, -1);
3200 void Document::addMarker(Range
*range
, DocumentMarker::MarkerType type
, String description
)
3202 // Use a TextIterator to visit the potentially multiple nodes the range covers.
3203 for (TextIterator
markedText(range
); !markedText
.atEnd(); markedText
.advance()) {
3204 RefPtr
<Range
> textPiece
= markedText
.range();
3206 DocumentMarker marker
= {type
, textPiece
->startOffset(exception
), textPiece
->endOffset(exception
), description
};
3207 addMarker(textPiece
->startContainer(exception
), marker
);
3211 void Document::removeMarkers(Range
* range
, DocumentMarker::MarkerType markerType
)
3213 if (m_markers
.isEmpty())
3216 ExceptionCode ec
= 0;
3217 Node
* startContainer
= range
->startContainer(ec
);
3218 Node
* endContainer
= range
->endContainer(ec
);
3220 Node
* pastLastNode
= range
->pastLastNode();
3221 for (Node
* node
= range
->firstNode(); node
!= pastLastNode
; node
= node
->traverseNextNode()) {
3222 int startOffset
= node
== startContainer
? range
->startOffset(ec
) : 0;
3223 int endOffset
= node
== endContainer
? range
->endOffset(ec
) : INT_MAX
;
3224 int length
= endOffset
- startOffset
;
3225 removeMarkers(node
, startOffset
, length
, markerType
);
3229 // Markers are stored in order sorted by their start offset.
3230 // Markers of the same type do not overlap each other.
3232 void Document::addMarker(Node
* node
, DocumentMarker newMarker
)
3234 ASSERT(newMarker
.endOffset
>= newMarker
.startOffset
);
3235 if (newMarker
.endOffset
== newMarker
.startOffset
)
3238 MarkerMapVectorPair
* vectorPair
= m_markers
.get(node
);
3241 vectorPair
= new MarkerMapVectorPair
;
3242 vectorPair
->first
.append(newMarker
);
3243 vectorPair
->second
.append(placeholderRectForMarker());
3244 m_markers
.set(node
, vectorPair
);
3246 Vector
<DocumentMarker
>& markers
= vectorPair
->first
;
3247 Vector
<IntRect
>& rects
= vectorPair
->second
;
3248 size_t numMarkers
= markers
.size();
3249 ASSERT(numMarkers
== rects
.size());
3251 // Iterate over all markers whose start offset is less than or equal to the new marker's.
3252 // If one of them is of the same type as the new marker and touches it or intersects with it
3253 // (there is at most one), remove it and adjust the new marker's start offset to encompass it.
3254 for (i
= 0; i
< numMarkers
; ++i
) {
3255 DocumentMarker marker
= markers
[i
];
3256 if (marker
.startOffset
> newMarker
.startOffset
)
3258 if (marker
.type
== newMarker
.type
&& marker
.endOffset
>= newMarker
.startOffset
) {
3259 newMarker
.startOffset
= marker
.startOffset
;
3267 // Iterate over all markers whose end offset is less than or equal to the new marker's,
3268 // removing markers of the same type as the new marker which touch it or intersect with it,
3269 // adjusting the new marker's end offset to cover them if necessary.
3270 while (j
< numMarkers
) {
3271 DocumentMarker marker
= markers
[j
];
3272 if (marker
.startOffset
> newMarker
.endOffset
)
3274 if (marker
.type
== newMarker
.type
) {
3277 if (newMarker
.endOffset
<= marker
.endOffset
) {
3278 newMarker
.endOffset
= marker
.endOffset
;
3285 // At this point i points to the node before which we want to insert.
3286 markers
.insert(i
, newMarker
);
3287 rects
.insert(i
, placeholderRectForMarker());
3290 // repaint the affected node
3291 if (node
->renderer())
3292 node
->renderer()->repaint();
3295 // copies markers from srcNode to dstNode, applying the specified shift delta to the copies. The shift is
3296 // useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode.
3297 void Document::copyMarkers(Node
*srcNode
, unsigned startOffset
, int length
, Node
*dstNode
, int delta
, DocumentMarker::MarkerType markerType
)
3302 MarkerMapVectorPair
* vectorPair
= m_markers
.get(srcNode
);
3306 ASSERT(vectorPair
->first
.size() == vectorPair
->second
.size());
3308 bool docDirty
= false;
3309 unsigned endOffset
= startOffset
+ length
- 1;
3310 Vector
<DocumentMarker
>& markers
= vectorPair
->first
;
3311 for (size_t i
= 0; i
!= markers
.size(); ++i
) {
3312 DocumentMarker marker
= markers
[i
];
3314 // stop if we are now past the specified range
3315 if (marker
.startOffset
> endOffset
)
3318 // skip marker that is before the specified range or is the wrong type
3319 if (marker
.endOffset
< startOffset
|| (marker
.type
!= markerType
&& markerType
!= DocumentMarker::AllMarkers
))
3322 // pin the marker to the specified range and apply the shift delta
3324 if (marker
.startOffset
< startOffset
)
3325 marker
.startOffset
= startOffset
;
3326 if (marker
.endOffset
> endOffset
)
3327 marker
.endOffset
= endOffset
;
3328 marker
.startOffset
+= delta
;
3329 marker
.endOffset
+= delta
;
3331 addMarker(dstNode
, marker
);
3334 // repaint the affected node
3335 if (docDirty
&& dstNode
->renderer())
3336 dstNode
->renderer()->repaint();
3339 void Document::removeMarkers(Node
* node
, unsigned startOffset
, int length
, DocumentMarker::MarkerType markerType
)
3344 MarkerMapVectorPair
* vectorPair
= m_markers
.get(node
);
3348 Vector
<DocumentMarker
>& markers
= vectorPair
->first
;
3349 Vector
<IntRect
>& rects
= vectorPair
->second
;
3350 ASSERT(markers
.size() == rects
.size());
3351 bool docDirty
= false;
3352 unsigned endOffset
= startOffset
+ length
;
3353 for (size_t i
= 0; i
< markers
.size();) {
3354 DocumentMarker marker
= markers
[i
];
3356 // markers are returned in order, so stop if we are now past the specified range
3357 if (marker
.startOffset
>= endOffset
)
3360 // skip marker that is wrong type or before target
3361 if (marker
.endOffset
< startOffset
|| (marker
.type
!= markerType
&& markerType
!= DocumentMarker::AllMarkers
)) {
3366 // at this point we know that marker and target intersect in some way
3369 // pitch the old marker and any associated rect
3373 // add either of the resulting slices that are left after removing target
3374 if (startOffset
> marker
.startOffset
) {
3375 DocumentMarker newLeft
= marker
;
3376 newLeft
.endOffset
= startOffset
;
3377 markers
.insert(i
, newLeft
);
3378 rects
.insert(i
, placeholderRectForMarker());
3379 // i now points to the newly-inserted node, but we want to skip that one
3382 if (marker
.endOffset
> endOffset
) {
3383 DocumentMarker newRight
= marker
;
3384 newRight
.startOffset
= endOffset
;
3385 markers
.insert(i
, newRight
);
3386 rects
.insert(i
, placeholderRectForMarker());
3387 // i now points to the newly-inserted node, but we want to skip that one
3392 if (markers
.isEmpty()) {
3393 ASSERT(rects
.isEmpty());
3394 m_markers
.remove(node
);
3398 // repaint the affected node
3399 if (docDirty
&& node
->renderer())
3400 node
->renderer()->repaint();
3403 DocumentMarker
* Document::markerContainingPoint(const IntPoint
& point
, DocumentMarker::MarkerType markerType
)
3405 // outer loop: process each node that contains any markers
3406 MarkerMap::iterator end
= m_markers
.end();
3407 for (MarkerMap::iterator nodeIterator
= m_markers
.begin(); nodeIterator
!= end
; ++nodeIterator
) {
3408 // inner loop; process each marker in this node
3409 MarkerMapVectorPair
* vectorPair
= nodeIterator
->second
;
3410 Vector
<DocumentMarker
>& markers
= vectorPair
->first
;
3411 Vector
<IntRect
>& rects
= vectorPair
->second
;
3412 ASSERT(markers
.size() == rects
.size());
3413 unsigned markerCount
= markers
.size();
3414 for (unsigned markerIndex
= 0; markerIndex
< markerCount
; ++markerIndex
) {
3415 DocumentMarker
& marker
= markers
[markerIndex
];
3417 // skip marker that is wrong type
3418 if (marker
.type
!= markerType
&& markerType
!= DocumentMarker::AllMarkers
)
3421 IntRect
& r
= rects
[markerIndex
];
3423 // skip placeholder rects
3424 if (r
== placeholderRectForMarker())
3427 if (r
.contains(point
))
3435 Vector
<DocumentMarker
> Document::markersForNode(Node
* node
)
3437 MarkerMapVectorPair
* vectorPair
= m_markers
.get(node
);
3439 return vectorPair
->first
;
3440 return Vector
<DocumentMarker
>();
3443 Vector
<IntRect
> Document::renderedRectsForMarkers(DocumentMarker::MarkerType markerType
)
3445 Vector
<IntRect
> result
;
3447 // outer loop: process each node
3448 MarkerMap::iterator end
= m_markers
.end();
3449 for (MarkerMap::iterator nodeIterator
= m_markers
.begin(); nodeIterator
!= end
; ++nodeIterator
) {
3450 // inner loop; process each marker in this node
3451 MarkerMapVectorPair
* vectorPair
= nodeIterator
->second
;
3452 Vector
<DocumentMarker
>& markers
= vectorPair
->first
;
3453 Vector
<IntRect
>& rects
= vectorPair
->second
;
3454 ASSERT(markers
.size() == rects
.size());
3455 unsigned markerCount
= markers
.size();
3456 for (unsigned markerIndex
= 0; markerIndex
< markerCount
; ++markerIndex
) {
3457 DocumentMarker marker
= markers
[markerIndex
];
3459 // skip marker that is wrong type
3460 if (marker
.type
!= markerType
&& markerType
!= DocumentMarker::AllMarkers
)
3463 IntRect r
= rects
[markerIndex
];
3464 // skip placeholder rects
3465 if (r
== placeholderRectForMarker())
3475 void Document::removeMarkers(Node
* node
)
3477 MarkerMap::iterator i
= m_markers
.find(node
);
3478 if (i
!= m_markers
.end()) {
3480 m_markers
.remove(i
);
3481 if (RenderObject
* renderer
= node
->renderer())
3482 renderer
->repaint();
3486 void Document::removeMarkers(DocumentMarker::MarkerType markerType
)
3488 // outer loop: process each markered node in the document
3489 MarkerMap markerMapCopy
= m_markers
;
3490 MarkerMap::iterator end
= markerMapCopy
.end();
3491 for (MarkerMap::iterator i
= markerMapCopy
.begin(); i
!= end
; ++i
) {
3492 Node
* node
= i
->first
.get();
3493 bool nodeNeedsRepaint
= false;
3495 // inner loop: process each marker in the current node
3496 MarkerMapVectorPair
* vectorPair
= i
->second
;
3497 Vector
<DocumentMarker
>& markers
= vectorPair
->first
;
3498 Vector
<IntRect
>& rects
= vectorPair
->second
;
3499 ASSERT(markers
.size() == rects
.size());
3500 for (size_t i
= 0; i
!= markers
.size();) {
3501 DocumentMarker marker
= markers
[i
];
3503 // skip nodes that are not of the specified type
3504 if (marker
.type
!= markerType
&& markerType
!= DocumentMarker::AllMarkers
) {
3509 // pitch the old marker
3512 nodeNeedsRepaint
= true;
3513 // markerIterator now points to the next node
3516 // Redraw the node if it changed. Do this before the node is removed from m_markers, since
3517 // m_markers might contain the last reference to the node.
3518 if (nodeNeedsRepaint
) {
3519 RenderObject
* renderer
= node
->renderer();
3521 renderer
->repaint();
3524 // delete the node's list if it is now empty
3525 if (markers
.isEmpty()) {
3526 ASSERT(rects
.isEmpty());
3527 m_markers
.remove(node
);
3533 void Document::repaintMarkers(DocumentMarker::MarkerType markerType
)
3535 // outer loop: process each markered node in the document
3536 MarkerMap::iterator end
= m_markers
.end();
3537 for (MarkerMap::iterator i
= m_markers
.begin(); i
!= end
; ++i
) {
3538 Node
* node
= i
->first
.get();
3540 // inner loop: process each marker in the current node
3541 MarkerMapVectorPair
* vectorPair
= i
->second
;
3542 Vector
<DocumentMarker
>& markers
= vectorPair
->first
;
3543 bool nodeNeedsRepaint
= false;
3544 for (size_t i
= 0; i
!= markers
.size(); ++i
) {
3545 DocumentMarker marker
= markers
[i
];
3547 // skip nodes that are not of the specified type
3548 if (marker
.type
== markerType
|| markerType
== DocumentMarker::AllMarkers
) {
3549 nodeNeedsRepaint
= true;
3554 if (!nodeNeedsRepaint
)
3557 // cause the node to be redrawn
3558 if (RenderObject
* renderer
= node
->renderer())
3559 renderer
->repaint();
3563 void Document::setRenderedRectForMarker(Node
* node
, DocumentMarker marker
, const IntRect
& r
)
3565 MarkerMapVectorPair
* vectorPair
= m_markers
.get(node
);
3567 ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about
3571 Vector
<DocumentMarker
>& markers
= vectorPair
->first
;
3572 ASSERT(markers
.size() == vectorPair
->second
.size());
3573 unsigned markerCount
= markers
.size();
3574 for (unsigned markerIndex
= 0; markerIndex
< markerCount
; ++markerIndex
) {
3575 DocumentMarker m
= markers
[markerIndex
];
3577 vectorPair
->second
[markerIndex
] = r
;
3582 ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about
3585 void Document::invalidateRenderedRectsForMarkersInRect(const IntRect
& r
)
3587 // outer loop: process each markered node in the document
3588 MarkerMap::iterator end
= m_markers
.end();
3589 for (MarkerMap::iterator i
= m_markers
.begin(); i
!= end
; ++i
) {
3591 // inner loop: process each rect in the current node
3592 MarkerMapVectorPair
* vectorPair
= i
->second
;
3593 Vector
<IntRect
>& rects
= vectorPair
->second
;
3595 unsigned rectCount
= rects
.size();
3596 for (unsigned rectIndex
= 0; rectIndex
< rectCount
; ++rectIndex
)
3597 if (rects
[rectIndex
].intersects(r
))
3598 rects
[rectIndex
] = placeholderRectForMarker();
3602 void Document::shiftMarkers(Node
*node
, unsigned startOffset
, int delta
, DocumentMarker::MarkerType markerType
)
3604 MarkerMapVectorPair
* vectorPair
= m_markers
.get(node
);
3608 Vector
<DocumentMarker
>& markers
= vectorPair
->first
;
3609 Vector
<IntRect
>& rects
= vectorPair
->second
;
3610 ASSERT(markers
.size() == rects
.size());
3612 bool docDirty
= false;
3613 for (size_t i
= 0; i
!= markers
.size(); ++i
) {
3614 DocumentMarker
&marker
= markers
[i
];
3615 if (marker
.startOffset
>= startOffset
&& (markerType
== DocumentMarker::AllMarkers
|| marker
.type
== markerType
)) {
3616 ASSERT((int)marker
.startOffset
+ delta
>= 0);
3617 marker
.startOffset
+= delta
;
3618 marker
.endOffset
+= delta
;
3621 // Marker moved, so previously-computed rendered rectangle is now invalid
3622 rects
[i
] = placeholderRectForMarker();
3626 // repaint the affected node
3627 if (docDirty
&& node
->renderer())
3628 node
->renderer()->repaint();
3633 void Document::applyXSLTransform(ProcessingInstruction
* pi
)
3635 RefPtr
<XSLTProcessor
> processor
= XSLTProcessor::create();
3636 processor
->setXSLStyleSheet(static_cast<XSLStyleSheet
*>(pi
->sheet()));
3637 String resultMIMEType
;
3639 String resultEncoding
;
3640 if (!processor
->transformToString(this, resultMIMEType
, newSource
, resultEncoding
))
3642 // FIXME: If the transform failed we should probably report an error (like Mozilla does).
3643 processor
->createDocumentFromSource(newSource
, resultEncoding
, resultMIMEType
, this, frame());
3646 void Document::setTransformSource(void* doc
)
3648 if (doc
== m_transformSource
)
3651 xmlFreeDoc((xmlDocPtr
)m_transformSource
);
3652 m_transformSource
= doc
;
3657 void Document::setDesignMode(InheritedBool value
)
3659 m_designMode
= value
;
3662 Document::InheritedBool
Document::getDesignMode() const
3664 return m_designMode
;
3667 bool Document::inDesignMode() const
3669 for (const Document
* d
= this; d
; d
= d
->parentDocument()) {
3670 if (d
->m_designMode
!= inherit
)
3671 return d
->m_designMode
;
3676 Document
*Document::parentDocument() const
3678 Frame
*childPart
= frame();
3681 Frame
*parent
= childPart
->tree()->parent();
3684 return parent
->document();
3687 Document
*Document::topDocument() const
3689 Document
*doc
= const_cast<Document
*>(this);
3691 while ((element
= doc
->ownerElement()))
3692 doc
= element
->document();
3697 PassRefPtr
<Attr
> Document::createAttributeNS(const String
& namespaceURI
, const String
& qualifiedName
, ExceptionCode
& ec
, bool shouldIgnoreNamespaceChecks
)
3699 String prefix
, localName
;
3700 if (!parseQualifiedName(qualifiedName
, prefix
, localName
, ec
))
3703 QualifiedName
qName(prefix
, localName
, namespaceURI
);
3704 if (!shouldIgnoreNamespaceChecks
&& hasPrefixNamespaceMismatch(qName
)) {
3709 // Spec: DOM Level 2 Core: http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-DocCrAttrNS
3710 if (!shouldIgnoreNamespaceChecks
&& qName
.localName() == "xmlns" && qName
.namespaceURI() != "http://www.w3.org/2000/xmlns/") {
3715 // FIXME: Assume this is a mapped attribute, since createAttribute isn't namespace-aware. There's no harm to XML
3716 // documents if we're wrong.
3717 return new Attr(0, this, MappedAttribute::create(qName
, StringImpl::empty()));
3721 const SVGDocumentExtensions
* Document::svgExtensions()
3723 return m_svgExtensions
.get();
3726 SVGDocumentExtensions
* Document::accessSVGExtensions()
3728 if (!m_svgExtensions
)
3729 m_svgExtensions
.set(new SVGDocumentExtensions(this));
3730 return m_svgExtensions
.get();
3734 PassRefPtr
<HTMLCollection
> Document::images()
3736 return HTMLCollection::create(this, HTMLCollection::DocImages
);
3739 PassRefPtr
<HTMLCollection
> Document::applets()
3741 return HTMLCollection::create(this, HTMLCollection::DocApplets
);
3744 PassRefPtr
<HTMLCollection
> Document::embeds()
3746 return HTMLCollection::create(this, HTMLCollection::DocEmbeds
);
3749 PassRefPtr
<HTMLCollection
> Document::plugins()
3751 // This is an alias for embeds() required for the JS DOM bindings.
3752 return HTMLCollection::create(this, HTMLCollection::DocEmbeds
);
3755 PassRefPtr
<HTMLCollection
> Document::objects()
3757 return HTMLCollection::create(this, HTMLCollection::DocObjects
);
3760 PassRefPtr
<HTMLCollection
> Document::scripts()
3762 return HTMLCollection::create(this, HTMLCollection::DocScripts
);
3765 PassRefPtr
<HTMLCollection
> Document::links()
3767 return HTMLCollection::create(this, HTMLCollection::DocLinks
);
3770 PassRefPtr
<HTMLCollection
> Document::forms()
3772 return HTMLCollection::create(this, HTMLCollection::DocForms
);
3775 PassRefPtr
<HTMLCollection
> Document::anchors()
3777 return HTMLCollection::create(this, HTMLCollection::DocAnchors
);
3780 PassRefPtr
<HTMLCollection
> Document::all()
3782 return HTMLCollection::create(this, HTMLCollection::DocAll
);
3785 PassRefPtr
<HTMLCollection
> Document::windowNamedItems(const String
&name
)
3787 return HTMLNameCollection::create(this, HTMLCollection::WindowNamedItems
, name
);
3790 PassRefPtr
<HTMLCollection
> Document::documentNamedItems(const String
&name
)
3792 return HTMLNameCollection::create(this, HTMLCollection::DocumentNamedItems
, name
);
3795 HTMLCollection::CollectionInfo
* Document::nameCollectionInfo(HTMLCollection::Type type
, const AtomicString
& name
)
3797 ASSERT(type
>= HTMLCollection::FirstNamedDocumentCachedType
);
3798 unsigned index
= type
- HTMLCollection::FirstNamedDocumentCachedType
;
3799 ASSERT(index
< HTMLCollection::NumNamedDocumentCachedTypes
);
3801 NamedCollectionMap
& map
= m_nameCollectionInfo
[index
];
3802 NamedCollectionMap::iterator iter
= map
.find(name
.impl());
3803 if (iter
== map
.end())
3804 iter
= map
.add(name
.impl(), new HTMLCollection::CollectionInfo
).first
;
3805 return iter
->second
;
3808 void Document::finishedParsing()
3812 ExceptionCode ec
= 0;
3813 dispatchEvent(Event::create(eventNames().DOMContentLoadedEvent
, true, false), ec
);
3815 if (Frame
* f
= frame())
3816 f
->loader()->finishedParsing();
3819 Vector
<String
> Document::formElementsState() const
3821 Vector
<String
> stateVector
;
3822 stateVector
.reserveCapacity(m_formElementsWithState
.size() * 3);
3823 typedef ListHashSet
<HTMLFormControlElementWithState
*>::const_iterator Iterator
;
3824 Iterator end
= m_formElementsWithState
.end();
3825 for (Iterator it
= m_formElementsWithState
.begin(); it
!= end
; ++it
) {
3826 HTMLFormControlElementWithState
* e
= *it
;
3828 if (e
->saveState(value
)) {
3829 stateVector
.append(e
->name().string());
3830 stateVector
.append(e
->type().string());
3831 stateVector
.append(value
);
3839 PassRefPtr
<XPathExpression
> Document::createExpression(const String
& expression
,
3840 XPathNSResolver
* resolver
,
3843 if (!m_xpathEvaluator
)
3844 m_xpathEvaluator
= XPathEvaluator::create();
3845 return m_xpathEvaluator
->createExpression(expression
, resolver
, ec
);
3848 PassRefPtr
<XPathNSResolver
> Document::createNSResolver(Node
* nodeResolver
)
3850 if (!m_xpathEvaluator
)
3851 m_xpathEvaluator
= XPathEvaluator::create();
3852 return m_xpathEvaluator
->createNSResolver(nodeResolver
);
3855 PassRefPtr
<XPathResult
> Document::evaluate(const String
& expression
,
3857 XPathNSResolver
* resolver
,
3858 unsigned short type
,
3859 XPathResult
* result
,
3862 if (!m_xpathEvaluator
)
3863 m_xpathEvaluator
= XPathEvaluator::create();
3864 return m_xpathEvaluator
->evaluate(expression
, contextNode
, resolver
, type
, result
, ec
);
3867 #endif // ENABLE(XPATH)
3869 void Document::setStateForNewFormElements(const Vector
<String
>& stateVector
)
3871 // Walk the state vector backwards so that the value to use for each
3872 // name/type pair first is the one at the end of each individual vector
3873 // in the FormElementStateMap. We're using them like stacks.
3874 typedef FormElementStateMap::iterator Iterator
;
3875 m_formElementsWithState
.clear();
3876 for (size_t i
= stateVector
.size() / 3 * 3; i
; i
-= 3) {
3877 AtomicString a
= stateVector
[i
- 3];
3878 AtomicString b
= stateVector
[i
- 2];
3879 const String
& c
= stateVector
[i
- 1];
3880 FormElementKey
key(a
.impl(), b
.impl());
3881 Iterator it
= m_stateForNewFormElements
.find(key
);
3882 if (it
!= m_stateForNewFormElements
.end())
3883 it
->second
.append(c
);
3885 Vector
<String
> v(1);
3887 m_stateForNewFormElements
.set(key
, v
);
3892 bool Document::hasStateForNewFormElements() const
3894 return !m_stateForNewFormElements
.isEmpty();
3897 bool Document::takeStateForFormElement(AtomicStringImpl
* name
, AtomicStringImpl
* type
, String
& state
)
3899 typedef FormElementStateMap::iterator Iterator
;
3900 Iterator it
= m_stateForNewFormElements
.find(FormElementKey(name
, type
));
3901 if (it
== m_stateForNewFormElements
.end())
3903 ASSERT(it
->second
.size());
3904 state
= it
->second
.last();
3905 if (it
->second
.size() > 1)
3906 it
->second
.removeLast();
3908 m_stateForNewFormElements
.remove(it
);
3912 FormElementKey::FormElementKey(AtomicStringImpl
* name
, AtomicStringImpl
* type
)
3913 : m_name(name
), m_type(type
)
3918 FormElementKey::~FormElementKey()
3923 FormElementKey::FormElementKey(const FormElementKey
& other
)
3924 : m_name(other
.name()), m_type(other
.type())
3929 FormElementKey
& FormElementKey::operator=(const FormElementKey
& other
)
3933 m_name
= other
.name();
3934 m_type
= other
.type();
3938 void FormElementKey::ref() const
3946 void FormElementKey::deref() const
3954 unsigned FormElementKeyHash::hash(const FormElementKey
& k
)
3956 ASSERT(sizeof(k
) % (sizeof(uint16_t) * 2) == 0);
3958 unsigned l
= sizeof(k
) / (sizeof(uint16_t) * 2);
3959 const uint16_t* s
= reinterpret_cast<const uint16_t*>(&k
);
3960 uint32_t hash
= PHI
;
3963 for (; l
> 0; l
--) {
3965 uint32_t tmp
= (s
[1] << 11) ^ hash
;
3966 hash
= (hash
<< 16) ^ tmp
;
3971 // Force "avalanching" of final 127 bits
3978 // this avoids ever returning a hash code of 0, since that is used to
3979 // signal "hash not computed yet", using a value that is likely to be
3980 // effectively the same as 0 when the low bits are masked
3987 void Document::setIconURL(const String
& iconURL
, const String
& type
)
3989 // FIXME - <rdar://problem/4727645> - At some point in the future, we might actually honor the "type"
3990 if (m_iconURL
.isEmpty())
3991 m_iconURL
= iconURL
;
3992 else if (!type
.isEmpty())
3993 m_iconURL
= iconURL
;
3996 void Document::setUseSecureKeyboardEntryWhenActive(bool usesSecureKeyboard
)
3998 if (m_useSecureKeyboardEntryWhenActive
== usesSecureKeyboard
)
4001 m_useSecureKeyboardEntryWhenActive
= usesSecureKeyboard
;
4002 m_frame
->updateSecureKeyboardEntryIfActive();
4005 bool Document::useSecureKeyboardEntryWhenActive() const
4007 return m_useSecureKeyboardEntryWhenActive
;
4010 void Document::initSecurityContext()
4012 if (m_securityOrigin
&& !m_securityOrigin
->isEmpty())
4013 return; // m_securityOrigin has already been initialized.
4016 // No source for a security context.
4017 // This can occur via document.implementation.createDocument().
4018 m_cookieURL
= KURL("");
4019 m_securityOrigin
= SecurityOrigin::createEmpty();
4023 // In the common case, create the security context from the currently
4025 const KURL
& url
= m_frame
->loader()->url();
4027 m_securityOrigin
= SecurityOrigin::create(url
);
4029 if (FrameLoader::allowSubstituteDataAccessToLocal()) {
4030 // If this document was loaded with substituteData, then the document can
4031 // load local resources. See https://bugs.webkit.org/show_bug.cgi?id=16756
4032 // and https://bugs.webkit.org/show_bug.cgi?id=19760 for further
4034 DocumentLoader
* documentLoader
= m_frame
->loader()->documentLoader();
4035 if (documentLoader
&& documentLoader
->substituteData().isValid())
4036 m_securityOrigin
->grantLoadLocalResources();
4039 if (!m_securityOrigin
->isEmpty())
4042 // If we do not obtain a meaningful origin from the URL, then we try to
4043 // find one via the frame hierarchy.
4045 Frame
* ownerFrame
= m_frame
->tree()->parent();
4047 ownerFrame
= m_frame
->loader()->opener();
4049 if (ownerFrame
&& ownerFrame
->document()) {
4050 m_cookieURL
= ownerFrame
->document()->cookieURL();
4051 // We alias the SecurityOrigins to match Firefox, see Bug 15313
4052 // https://bugs.webkit.org/show_bug.cgi?id=15313
4053 m_securityOrigin
= ownerFrame
->document()->securityOrigin();
4057 void Document::setSecurityOrigin(SecurityOrigin
* securityOrigin
)
4059 m_securityOrigin
= securityOrigin
;
4063 void Document::updateFocusAppearanceSoon()
4065 if (!m_updateFocusAppearanceTimer
.isActive())
4066 m_updateFocusAppearanceTimer
.startOneShot(0);
4069 void Document::cancelFocusAppearanceUpdate()
4071 m_updateFocusAppearanceTimer
.stop();
4074 void Document::updateFocusAppearanceTimerFired(Timer
<Document
>*)
4076 Node
* node
= focusedNode();
4079 if (!node
->isElementNode())
4084 Element
* element
= static_cast<Element
*>(node
);
4085 if (element
->isFocusable())
4086 element
->updateFocusAppearance(false);
4089 // FF method for accessing the selection added for compatability.
4090 DOMSelection
* Document::getSelection() const
4092 return frame() ? frame()->domWindow()->getSelection() : 0;
4095 static inline int findSlashDotDotSlash(const UChar
* characters
, size_t length
)
4099 unsigned loopLimit
= length
- 3;
4100 for (unsigned i
= 0; i
< loopLimit
; ++i
) {
4101 if (characters
[i
] == '/' && characters
[i
+ 1] == '.' && characters
[i
+ 2] == '.' && characters
[i
+ 3] == '/')
4107 static inline int findSlashSlash(const UChar
* characters
, size_t length
, int position
)
4111 unsigned loopLimit
= length
- 1;
4112 for (unsigned i
= position
; i
< loopLimit
; ++i
) {
4113 if (characters
[i
] == '/' && characters
[i
+ 1] == '/')
4119 static inline int findSlashDotSlash(const UChar
* characters
, size_t length
)
4123 unsigned loopLimit
= length
- 2;
4124 for (unsigned i
= 0; i
< loopLimit
; ++i
) {
4125 if (characters
[i
] == '/' && characters
[i
+ 1] == '.' && characters
[i
+ 2] == '/')
4131 static inline bool containsColonSlashSlash(const UChar
* characters
, unsigned length
)
4135 unsigned loopLimit
= length
- 2;
4136 for (unsigned i
= 0; i
< loopLimit
; ++i
) {
4137 if (characters
[i
] == ':' && characters
[i
+ 1] == '/' && characters
[i
+ 2] == '/')
4143 static inline void cleanPath(Vector
<UChar
, 512>& path
)
4145 // FIXME: Shold not do this in the query or anchor part.
4147 while ((pos
= findSlashDotDotSlash(path
.data(), path
.size())) != -1) {
4148 int prev
= reverseFind(path
.data(), path
.size(), '/', pos
- 1);
4149 // don't remove the host, i.e. http://foo.org/../foo.html
4150 if (prev
< 0 || (prev
> 3 && path
[prev
- 2] == ':' && path
[prev
- 1] == '/'))
4151 path
.remove(pos
, 3);
4153 path
.remove(prev
, pos
- prev
+ 3);
4156 // FIXME: Shold not do this in the query part.
4157 // Set refPos to -2 to mean "I haven't looked for the anchor yet".
4158 // We don't want to waste a function call on the search for the the anchor
4159 // in the vast majority of cases where there is no "//" in the path.
4162 while ((pos
= findSlashSlash(path
.data(), path
.size(), pos
)) != -1) {
4164 refPos
= find(path
.data(), path
.size(), '#');
4165 if (refPos
> 0 && pos
>= refPos
)
4168 if (pos
== 0 || path
[pos
- 1] != ':')
4174 // FIXME: Shold not do this in the query or anchor part.
4175 while ((pos
= findSlashDotSlash(path
.data(), path
.size())) != -1)
4176 path
.remove(pos
, 2);
4179 static inline bool matchLetter(UChar c
, UChar lowercaseLetter
)
4181 return (c
| 0x20) == lowercaseLetter
;
4184 static inline bool needsTrailingSlash(const UChar
* characters
, unsigned length
)
4188 if (!matchLetter(characters
[0], 'h')
4189 || !matchLetter(characters
[1], 't')
4190 || !matchLetter(characters
[2], 't')
4191 || !matchLetter(characters
[3], 'p'))
4193 if (!(characters
[4] == ':'
4194 || (matchLetter(characters
[4], 's') && characters
[5] == ':')))
4197 unsigned pos
= characters
[4] == ':' ? 5 : 6;
4199 // Skip initial two slashes if present.
4200 if (pos
+ 1 < length
&& characters
[pos
] == '/' && characters
[pos
+ 1] == '/')
4204 while (pos
< length
&& characters
[pos
] != '/')
4207 return pos
== length
;
4210 unsigned Document::visitedLinkHash(const AtomicString
& attributeURL
) const
4212 const UChar
* characters
= attributeURL
.characters();
4213 unsigned length
= attributeURL
.length();
4217 // This is a poor man's completeURL. Faster with less memory allocation.
4218 // FIXME: It's missing a lot of what completeURL does and a lot of what KURL does.
4219 // For example, it does not handle international domain names properly.
4221 // FIXME: It is wrong that we do not do further processing on strings that have "://" in them:
4222 // 1) The "://" could be in the query or anchor.
4223 // 2) The URL's path could have a "/./" or a "/../" or a "//" sequence in it.
4225 // FIXME: needsTrailingSlash does not properly return true for a URL that has no path, but does
4226 // have a query or anchor.
4228 bool hasColonSlashSlash
= containsColonSlashSlash(characters
, length
);
4230 if (hasColonSlashSlash
&& !needsTrailingSlash(characters
, length
))
4231 return AlreadyHashed::avoidDeletedValue(attributeURL
.string().impl()->hash());
4233 Vector
<UChar
, 512> buffer
;
4235 if (hasColonSlashSlash
) {
4236 // FIXME: This is incorrect for URLs that have a query or anchor; the "/" needs to go at the
4237 // end of the path, *before* the query or anchor.
4238 buffer
.append(characters
, length
);
4240 return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(buffer
.data(), buffer
.size()));
4243 switch (characters
[0]) {
4245 buffer
.append(m_baseURL
.string().characters(), m_baseURL
.pathStart());
4248 buffer
.append(m_baseURL
.string().characters(), m_baseURL
.pathEnd());
4251 buffer
.append(m_baseURL
.string().characters(), m_baseURL
.pathAfterLastSlash());
4254 buffer
.append(characters
, length
);
4256 if (needsTrailingSlash(buffer
.data(), buffer
.size())) {
4257 // FIXME: This is incorrect for URLs that have a query or anchor; the "/" needs to go at the
4258 // end of the path, *before* the query or anchor.
4262 return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(buffer
.data(), buffer
.size()));
4265 #if ENABLE(DATABASE)
4267 void Document::addOpenDatabase(Database
* database
)
4269 if (!m_openDatabaseSet
)
4270 m_openDatabaseSet
.set(new DatabaseSet
);
4272 ASSERT(!m_openDatabaseSet
->contains(database
));
4273 m_openDatabaseSet
->add(database
);
4276 void Document::removeOpenDatabase(Database
* database
)
4278 ASSERT(m_openDatabaseSet
&& m_openDatabaseSet
->contains(database
));
4279 if (!m_openDatabaseSet
)
4282 m_openDatabaseSet
->remove(database
);
4285 DatabaseThread
* Document::databaseThread()
4287 if (!m_databaseThread
&& !m_hasOpenDatabases
) {
4288 // Create the database thread on first request - but not if at least one database was already opened,
4289 // because in that case we already had a database thread and terminated it and should not create another.
4290 m_databaseThread
= DatabaseThread::create(this);
4291 if (!m_databaseThread
->start())
4292 m_databaseThread
= 0;
4295 return m_databaseThread
.get();
4298 void Document::stopDatabases()
4300 if (m_openDatabaseSet
) {
4301 DatabaseSet::iterator i
= m_openDatabaseSet
->begin();
4302 DatabaseSet::iterator end
= m_openDatabaseSet
->end();
4303 for (; i
!= end
; ++i
) {
4305 if (m_databaseThread
)
4306 m_databaseThread
->unscheduleDatabaseTasks(*i
);
4310 if (m_databaseThread
)
4311 m_databaseThread
->requestTermination();
4316 void Document::attachRange(Range
* range
)
4318 ASSERT(!m_ranges
.contains(range
));
4319 m_ranges
.add(range
);
4322 void Document::detachRange(Range
* range
)
4324 ASSERT(m_ranges
.contains(range
));
4325 m_ranges
.remove(range
);
4328 CanvasRenderingContext2D
* Document::getCSSCanvasContext(const String
& type
, const String
& name
, int width
, int height
)
4330 HTMLCanvasElement
* result
= getCSSCanvasElement(name
);
4333 result
->setSize(IntSize(width
, height
));
4334 return result
->getContext(type
);
4337 HTMLCanvasElement
* Document::getCSSCanvasElement(const String
& name
)
4339 RefPtr
<HTMLCanvasElement
> result
= m_cssCanvasElements
.get(name
).get();
4341 result
= new HTMLCanvasElement(this);
4342 m_cssCanvasElements
.set(name
, result
);
4344 return result
.get();
4347 void Document::initDNSPrefetch()
4349 m_haveExplicitlyDisabledDNSPrefetch
= false;
4350 m_isDNSPrefetchEnabled
= securityOrigin()->protocol() == "http";
4352 // Inherit DNS prefetch opt-out from parent frame
4353 if (Document
* parent
= parentDocument()) {
4354 if (!parent
->isDNSPrefetchEnabled())
4355 m_isDNSPrefetchEnabled
= false;
4359 void Document::parseDNSPrefetchControlHeader(const String
& dnsPrefetchControl
)
4361 if (equalIgnoringCase(dnsPrefetchControl
, "on") && !m_haveExplicitlyDisabledDNSPrefetch
) {
4362 m_isDNSPrefetchEnabled
= true;
4366 m_isDNSPrefetchEnabled
= false;
4367 m_haveExplicitlyDisabledDNSPrefetch
= true;
4370 } // namespace WebCore