Bug 454821. Better signature for GetChildArray. r+sr=sicking
[mozilla-central.git] / content / base / src / nsDocument.cpp
blob62e0c83341f589cd1e4e890ccc723f057d05bc09
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Johnny Stenback <jst@netscape.com>
25 * L. David Baron <dbaron@dbaron.org>
26 * Pierre Phaneuf <pp@ludusdesign.com>
27 * Pete Collins <petejc@collab.net>
28 * James Ross <silver@warwickcompsoc.co.uk>
29 * Ryan Jones <sciguyryan@gmail.com>
31 * Alternatively, the contents of this file may be used under the terms of
32 * either of the GNU General Public License Version 2 or later (the "GPL"),
33 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34 * in which case the provisions of the GPL or the LGPL are applicable instead
35 * of those above. If you wish to allow use of your version of this file only
36 * under the terms of either the GPL or the LGPL, and not to allow others to
37 * use your version of this file under the terms of the MPL, indicate your
38 * decision by deleting the provisions above and replace them with the notice
39 * and other provisions required by the GPL or the LGPL. If you do not delete
40 * the provisions above, a recipient may use your version of this file under
41 * the terms of any one of the MPL, the GPL or the LGPL.
43 * ***** END LICENSE BLOCK ***** */
46 * Base class for all our document implementations.
49 #include "plstr.h"
50 #include "prprf.h"
52 #include "nsIInterfaceRequestor.h"
53 #include "nsIInterfaceRequestorUtils.h"
54 #include "nsDocument.h"
55 #include "nsUnicharUtils.h"
56 #include "nsIPrivateDOMEvent.h"
57 #include "nsIEventStateManager.h"
58 #include "nsIFocusController.h"
59 #include "nsContentList.h"
60 #include "nsIObserver.h"
61 #include "nsIBaseWindow.h"
62 #include "nsIDocShell.h"
63 #include "nsIDocShellTreeItem.h"
64 #include "nsIScriptRuntime.h"
65 #include "nsCOMArray.h"
67 #include "nsGUIEvent.h"
69 #include "nsIDOMStyleSheet.h"
70 #include "nsDOMAttribute.h"
71 #include "nsIDOMDOMStringList.h"
72 #include "nsIDOMDOMImplementation.h"
73 #include "nsIDOMDocumentView.h"
74 #include "nsIDOMAbstractView.h"
75 #include "nsIDOMDocumentXBL.h"
76 #include "nsGenericElement.h"
77 #include "nsGenericHTMLElement.h"
78 #include "nsIDOMEventGroup.h"
79 #include "nsIDOMCDATASection.h"
80 #include "nsIDOMProcessingInstruction.h"
81 #include "nsDOMString.h"
82 #include "nsNodeUtils.h"
83 #include "nsLayoutUtils.h" // for GetFrameForPoint
84 #include "nsIFrame.h"
86 #include "nsRange.h"
87 #include "nsIDOMText.h"
88 #include "nsIDOMComment.h"
89 #include "nsDOMDocumentType.h"
90 #include "nsNodeIterator.h"
91 #include "nsTreeWalker.h"
93 #include "nsIServiceManager.h"
95 #include "nsContentCID.h"
96 #include "nsDOMError.h"
97 #include "nsIPresShell.h"
98 #include "nsPresContext.h"
99 #include "nsThreadUtils.h"
100 #include "nsNodeInfoManager.h"
101 #include "nsIXBLService.h"
102 #include "nsIXPointer.h"
103 #include "nsIFileChannel.h"
104 #include "nsIMultiPartChannel.h"
105 #include "nsIRefreshURI.h"
106 #include "nsIWebNavigation.h"
107 #include "nsIScriptError.h"
109 #include "nsNetUtil.h" // for NS_MakeAbsoluteURI
111 #include "nsIScriptSecurityManager.h"
112 #include "nsIPrincipal.h"
113 #include "nsIPrivateDOMImplementation.h"
115 #include "nsIDOMWindowInternal.h"
116 #include "nsPIDOMWindow.h"
117 #include "nsIDOMElement.h"
119 // for radio group stuff
120 #include "nsIDOMHTMLInputElement.h"
121 #include "nsIRadioVisitor.h"
122 #include "nsIFormControl.h"
124 #include "nsXMLEventsManager.h"
126 #include "nsBidiUtils.h"
128 static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID);
130 #include "nsIDOMUserDataHandler.h"
131 #include "nsScriptEventManager.h"
132 #include "nsIDOMXPathEvaluator.h"
133 #include "nsIXPathEvaluatorInternal.h"
134 #include "nsIParserService.h"
135 #include "nsContentCreatorFunctions.h"
137 #include "nsIScriptContext.h"
138 #include "nsBindingManager.h"
139 #include "nsIDOMHTMLDocument.h"
140 #include "nsIDOMHTMLFormElement.h"
141 #include "nsIRequest.h"
142 #include "nsILink.h"
144 #include "nsICharsetAlias.h"
145 #include "nsIParser.h"
146 #include "nsIContentSink.h"
148 #include "nsDateTimeFormatCID.h"
149 #include "nsIDateTimeFormat.h"
150 #include "nsEventDispatcher.h"
151 #include "nsMutationEvent.h"
152 #include "nsIDOMXPathEvaluator.h"
153 #include "nsDOMCID.h"
155 #include "nsIJSContextStack.h"
156 #include "nsIXPConnect.h"
157 #include "nsCycleCollector.h"
158 #include "nsCCUncollectableMarker.h"
159 #include "nsIContentPolicy.h"
160 #include "nsContentPolicyUtils.h"
161 #include "nsICategoryManager.h"
162 #include "nsIDocumentLoaderFactory.h"
163 #include "nsIContentViewer.h"
164 #include "nsIXMLContentSink.h"
165 #include "nsContentErrors.h"
166 #include "nsIXULDocument.h"
167 #include "nsIPrompt.h"
168 #include "nsIPropertyBag2.h"
170 #include "nsFrameLoader.h"
172 #include "mozAutoDocUpdate.h"
175 #ifdef MOZ_LOGGING
176 // so we can get logging even in release builds
177 #define FORCE_PR_LOG 1
178 #endif
179 #include "prlog.h"
181 #ifdef PR_LOGGING
182 static PRLogModuleInfo* gDocumentLeakPRLog;
183 #endif
185 void
186 nsUint32ToContentHashEntry::Destroy()
188 HashSet* set = GetHashSet();
189 if (set) {
190 delete set;
191 } else {
192 nsIContent* content = GetContent();
193 NS_IF_RELEASE(content);
197 nsresult
198 nsUint32ToContentHashEntry::PutContent(nsIContent* aVal)
200 // Add the value to the hash if it is there
201 HashSet* set = GetHashSet();
202 if (set) {
203 nsISupportsHashKey* entry = set->PutEntry(aVal);
204 return entry ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
207 // If an element is already there, create a hashtable and both of these to it
208 nsIContent* oldVal = GetContent();
209 if (oldVal) {
210 nsresult rv = InitHashSet(&set);
211 NS_ENSURE_SUCCESS(rv, rv);
213 nsISupportsHashKey* entry = set->PutEntry(oldVal);
214 if (!entry) {
215 // OOM - we can't insert aVal, but we can at least put oldVal back (even
216 // if we didn't, we'd still have to release oldVal so that we don't leak)
217 delete set;
218 SetContent(oldVal);
219 // SetContent adds another reference, so release the one we had
220 NS_RELEASE(oldVal);
221 return NS_ERROR_OUT_OF_MEMORY;
223 // The hashset adds its own reference, so release the one we had
224 NS_RELEASE(oldVal);
226 entry = set->PutEntry(aVal);
227 return entry ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
230 // Nothing exists in the hash right now, so just set the single pointer
231 return SetContent(aVal);
234 void
235 nsUint32ToContentHashEntry::RemoveContent(nsIContent* aVal)
237 // Remove from the hash if the hash is there
238 HashSet* set = GetHashSet();
239 if (set) {
240 set->RemoveEntry(aVal);
241 if (set->Count() == 0) {
242 delete set;
243 mValOrHash = nsnull;
245 return;
248 // Remove the ptr if there is just a ptr
249 nsIContent* v = GetContent();
250 if (v == aVal) {
251 NS_IF_RELEASE(v);
252 mValOrHash = nsnull;
256 nsresult
257 nsUint32ToContentHashEntry::InitHashSet(HashSet** aSet)
259 HashSet* newSet = new HashSet();
260 if (!newSet) {
261 return NS_ERROR_OUT_OF_MEMORY;
264 nsresult rv = newSet->Init();
265 NS_ENSURE_SUCCESS(rv, rv);
267 mValOrHash = newSet;
268 *aSet = newSet;
269 return NS_OK;
272 static PLDHashOperator
273 nsUint32ToContentHashEntryVisitorCallback(nsISupportsHashKey* aEntry,
274 void* aClosure)
276 nsUint32ToContentHashEntry::Visitor* visitor =
277 static_cast<nsUint32ToContentHashEntry::Visitor*>(aClosure);
278 visitor->Visit(static_cast<nsIContent*>(aEntry->GetKey()));
279 return PL_DHASH_NEXT;
282 void
283 nsUint32ToContentHashEntry::VisitContent(Visitor* aVisitor)
285 HashSet* set = GetHashSet();
286 if (set) {
287 set->EnumerateEntries(nsUint32ToContentHashEntryVisitorCallback, aVisitor);
288 if (set->Count() == 0) {
289 delete set;
290 mValOrHash = nsnull;
292 return;
295 nsIContent* v = GetContent();
296 if (v) {
297 aVisitor->Visit(v);
301 #define ID_NOT_IN_DOCUMENT ((nsIContent *)2)
302 #define NAME_NOT_VALID ((nsBaseContentList*)1)
304 nsIdentifierMapEntry::~nsIdentifierMapEntry()
306 if (mNameContentList && mNameContentList != NAME_NOT_VALID) {
307 NS_RELEASE(mNameContentList);
311 void
312 nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
314 if (mNameContentList != NAME_NOT_VALID) {
315 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
316 "mIdentifierMap mNameContentList");
317 aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList));
320 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "mIdentifierMap mDocAllList");
321 aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mDocAllList));
324 void
325 nsIdentifierMapEntry::SetInvalidName()
327 mNameContentList = NAME_NOT_VALID;
330 PRBool
331 nsIdentifierMapEntry::IsInvalidName()
333 return mNameContentList == NAME_NOT_VALID;
336 nsresult
337 nsIdentifierMapEntry::CreateNameContentList()
339 mNameContentList = new nsBaseContentList();
340 NS_ENSURE_TRUE(mNameContentList, NS_ERROR_OUT_OF_MEMORY);
341 NS_ADDREF(mNameContentList);
342 return NS_OK;
345 nsIContent*
346 nsIdentifierMapEntry::GetIdContent(PRBool* aNotInDocument)
348 nsIContent* c = static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
349 if (aNotInDocument) {
350 *aNotInDocument = c == ID_NOT_IN_DOCUMENT;
352 return c != ID_NOT_IN_DOCUMENT ? c : nsnull;
355 void
356 nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
358 for (PRInt32 i = 0; i < mIdContentList.Count(); ++i) {
359 aElements->AppendObject(static_cast<nsIContent*>(mIdContentList[i]));
363 void
364 nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
365 void* aData)
367 if (!mChangeCallbacks) {
368 mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
369 if (!mChangeCallbacks)
370 return;
371 mChangeCallbacks->Init();
374 ChangeCallback cc = { aCallback, aData };
375 mChangeCallbacks->PutEntry(cc);
378 void
379 nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
380 void* aData)
382 if (!mChangeCallbacks)
383 return;
384 ChangeCallback cc = { aCallback, aData };
385 mChangeCallbacks->RemoveEntry(cc);
386 if (mChangeCallbacks->Count() == 0) {
387 mChangeCallbacks = nsnull;
391 struct FireChangeArgs {
392 nsIContent* mFrom;
393 nsIContent* mTo;
396 static PLDHashOperator
397 FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg)
399 FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg);
400 return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData)
401 ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
404 void
405 nsIdentifierMapEntry::FireChangeCallbacks(nsIContent* aOldContent,
406 nsIContent* aNewContent)
408 if (!mChangeCallbacks)
409 return;
411 FireChangeArgs args = { aOldContent, aNewContent };
412 mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args);
415 PRBool
416 nsIdentifierMapEntry::AddIdContent(nsIContent* aContent)
418 NS_PRECONDITION(aContent, "Must have content");
419 NS_PRECONDITION(mIdContentList.IndexOf(nsnull) < 0,
420 "Why is null in our list?");
421 NS_PRECONDITION(aContent != ID_NOT_IN_DOCUMENT,
422 "Bogus content pointer");
424 nsIContent* currentContent = static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
425 if (currentContent == ID_NOT_IN_DOCUMENT) {
426 NS_ASSERTION(mIdContentList.Count() == 1, "Bogus count");
427 mIdContentList.ReplaceElementAt(aContent, 0);
428 FireChangeCallbacks(nsnull, aContent);
429 return PR_TRUE;
432 // Common case
433 if (mIdContentList.Count() == 0) {
434 if (!mIdContentList.AppendElement(aContent))
435 return PR_FALSE;
436 FireChangeCallbacks(nsnull, aContent);
437 return PR_TRUE;
440 // We seem to have multiple content nodes for the same id, or we're doing our
441 // top-down registration when the id table is going live. Search for the
442 // right place to insert the content.
443 PRInt32 start = 0;
444 PRInt32 end = mIdContentList.Count();
445 do {
446 NS_ASSERTION(start < end, "Bogus start/end");
448 PRInt32 cur = (start + end) / 2;
449 NS_ASSERTION(cur >= start && cur < end, "What happened here?");
451 nsIContent* curContent = static_cast<nsIContent*>(mIdContentList[cur]);
452 if (curContent == aContent) {
453 // Already in the list, so already in the right spot. Get out of here.
454 return PR_TRUE;
457 if (nsContentUtils::PositionIsBefore(aContent, curContent)) {
458 end = cur;
459 } else {
460 start = cur + 1;
462 } while (start != end);
464 if (!mIdContentList.InsertElementAt(aContent, start))
465 return PR_FALSE;
466 if (start == 0) {
467 FireChangeCallbacks(currentContent, aContent);
469 return PR_TRUE;
472 PRBool
473 nsIdentifierMapEntry::RemoveIdContent(nsIContent* aContent)
475 // This should only be called while the document is in an update.
476 // Assertions near the call to this method guarantee this.
478 // XXXbz should this ever Compact() I guess when all the content is gone
479 // we'll just get cleaned up in the natural order of things...
480 nsIContent* currentContent = static_cast<nsIContent*>(mIdContentList.SafeElementAt(0));
481 if (!mIdContentList.RemoveElement(aContent))
482 return PR_FALSE;
483 if (currentContent == aContent) {
484 FireChangeCallbacks(currentContent,
485 static_cast<nsIContent*>(mIdContentList.SafeElementAt(0)));
487 return mIdContentList.Count() == 0 && !mNameContentList && !mChangeCallbacks;
490 void
491 nsIdentifierMapEntry::FlagIDNotInDocument()
493 NS_ASSERTION(mIdContentList.Count() == 0,
494 "Flagging ID not in document when we have content?");
495 // Note that if this fails that's OK; this is just an optimization
496 mIdContentList.AppendElement(ID_NOT_IN_DOCUMENT);
499 void
500 nsIdentifierMapEntry::AddNameContent(nsIContent* aContent)
502 if (!mNameContentList || mNameContentList == NAME_NOT_VALID)
503 return;
505 // NOTE: this indexof is absolutely needed, since we don't flush
506 // content notifications when we do document.foo resolution. So
507 // aContent may be in our list already and just now getting notified
508 // for!
509 if (mNameContentList->IndexOf(aContent, PR_FALSE) < 0) {
510 mNameContentList->AppendElement(aContent);
514 void
515 nsIdentifierMapEntry::RemoveNameContent(nsIContent* aContent)
517 if (mNameContentList && mNameContentList != NAME_NOT_VALID) {
518 mNameContentList->RemoveElement(aContent);
522 // Helper structs for the content->subdoc map
524 class SubDocMapEntry : public PLDHashEntryHdr
526 public:
527 // Both of these are strong references
528 nsIContent *mKey; // must be first, to look like PLDHashEntryStub
529 nsIDocument *mSubDocument;
532 struct FindContentData
534 FindContentData(nsIDocument *aSubDoc)
535 : mSubDocument(aSubDoc), mResult(nsnull)
539 nsISupports *mSubDocument;
540 nsIContent *mResult;
545 * A struct that holds all the information about a radio group.
547 struct nsRadioGroupStruct
550 * A strong pointer to the currently selected radio button.
552 nsCOMPtr<nsIDOMHTMLInputElement> mSelectedRadioButton;
553 nsCOMArray<nsIFormControl> mRadioButtons;
557 nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument)
559 mLength = -1;
560 // Not reference counted to avoid circular references.
561 // The document will tell us when its going away.
562 mDocument = aDocument;
563 mDocument->AddObserver(this);
566 nsDOMStyleSheetList::~nsDOMStyleSheetList()
568 if (mDocument) {
569 mDocument->RemoveObserver(this);
573 // XXX couldn't we use the GetIIDs method from CSSStyleSheetList here?
574 // QueryInterface implementation for nsDOMStyleSheetList
575 NS_INTERFACE_TABLE_HEAD(nsDOMStyleSheetList)
576 NS_INTERFACE_TABLE3(nsDOMStyleSheetList,
577 nsIDOMStyleSheetList,
578 nsIDocumentObserver,
579 nsIMutationObserver)
580 NS_INTERFACE_TABLE_TO_MAP_SEGUE
581 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(DocumentStyleSheetList)
582 NS_INTERFACE_MAP_END
585 NS_IMPL_ADDREF(nsDOMStyleSheetList)
586 NS_IMPL_RELEASE(nsDOMStyleSheetList)
589 NS_IMETHODIMP
590 nsDOMStyleSheetList::GetLength(PRUint32* aLength)
592 if (mDocument) {
593 // XXX Find the number and then cache it. We'll use the
594 // observer notification to figure out if new ones have
595 // been added or removed.
596 if (-1 == mLength) {
597 mLength = mDocument->GetNumberOfStyleSheets();
599 #ifdef DEBUG
600 PRInt32 i;
601 for (i = 0; i < mLength; i++) {
602 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i);
603 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet));
604 NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet");
606 #endif
608 *aLength = mLength;
610 else {
611 *aLength = 0;
614 return NS_OK;
617 nsIStyleSheet*
618 nsDOMStyleSheetList::GetItemAt(PRUint32 aIndex)
620 if (!mDocument || aIndex >= (PRUint32)mDocument->GetNumberOfStyleSheets()) {
621 return nsnull;
624 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex);
625 NS_ASSERTION(sheet, "Must have a sheet");
627 return sheet;
630 NS_IMETHODIMP
631 nsDOMStyleSheetList::Item(PRUint32 aIndex, nsIDOMStyleSheet** aReturn)
633 nsIStyleSheet *sheet = GetItemAt(aIndex);
634 if (!sheet) {
635 *aReturn = nsnull;
637 return NS_OK;
640 return CallQueryInterface(sheet, aReturn);
643 void
644 nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
646 mDocument = nsnull;
649 void
650 nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
651 nsIStyleSheet* aStyleSheet,
652 PRBool aDocumentSheet)
654 if (aDocumentSheet && -1 != mLength) {
655 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
656 if (domss) {
657 mLength++;
662 void
663 nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument,
664 nsIStyleSheet* aStyleSheet,
665 PRBool aDocumentSheet)
667 if (aDocumentSheet && -1 != mLength) {
668 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
669 if (domss) {
670 mLength--;
675 // nsOnloadBlocker implementation
676 NS_IMPL_ISUPPORTS1(nsOnloadBlocker, nsIRequest)
678 NS_IMETHODIMP
679 nsOnloadBlocker::GetName(nsACString &aResult)
681 aResult.AssignLiteral("about:document-onload-blocker");
682 return NS_OK;
685 NS_IMETHODIMP
686 nsOnloadBlocker::IsPending(PRBool *_retval)
688 *_retval = PR_TRUE;
689 return NS_OK;
692 NS_IMETHODIMP
693 nsOnloadBlocker::GetStatus(nsresult *status)
695 *status = NS_OK;
696 return NS_OK;
699 NS_IMETHODIMP
700 nsOnloadBlocker::Cancel(nsresult status)
702 return NS_OK;
704 NS_IMETHODIMP
705 nsOnloadBlocker::Suspend(void)
707 return NS_OK;
709 NS_IMETHODIMP
710 nsOnloadBlocker::Resume(void)
712 return NS_OK;
715 NS_IMETHODIMP
716 nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup)
718 *aLoadGroup = nsnull;
719 return NS_OK;
722 NS_IMETHODIMP
723 nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup)
725 return NS_OK;
728 NS_IMETHODIMP
729 nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags)
731 *aLoadFlags = nsIRequest::LOAD_NORMAL;
732 return NS_OK;
735 NS_IMETHODIMP
736 nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
738 return NS_OK;
741 // ==================================================================
743 nsExternalResourceMap::nsExternalResourceMap()
744 : mHaveShutDown(PR_FALSE)
746 mMap.Init();
747 mPendingLoads.Init();
750 nsIDocument*
751 nsExternalResourceMap::RequestResource(nsIURI* aURI,
752 nsINode* aRequestingNode,
753 nsDocument* aDisplayDocument,
754 ExternalResourceLoad** aPendingLoad)
756 // If we ever start allowing non-same-origin loads here, we might need to do
757 // something interesting with aRequestingPrincipal even for the hashtable
758 // gets.
759 NS_PRECONDITION(aURI, "Must have a URI");
760 NS_PRECONDITION(aRequestingNode, "Must have a node");
761 *aPendingLoad = nsnull;
762 if (mHaveShutDown) {
763 return nsnull;
766 // First, make sure we strip the ref from aURI.
767 nsCOMPtr<nsIURI> clone;
768 aURI->Clone(getter_AddRefs(clone));
769 if (!clone) {
770 return nsnull;
772 nsCOMPtr<nsIURL> url(do_QueryInterface(clone));
773 if (url) {
774 url->SetRef(EmptyCString());
777 ExternalResource* resource;
778 mMap.Get(clone, &resource);
779 if (resource) {
780 return resource->mDocument;
783 nsRefPtr<PendingLoad> load;
784 mPendingLoads.Get(clone, getter_AddRefs(load));
785 if (load) {
786 NS_ADDREF(*aPendingLoad = load);
787 return nsnull;
790 load = new PendingLoad(aDisplayDocument);
791 if (!load) {
792 return nsnull;
795 if (!mPendingLoads.Put(clone, load)) {
796 return nsnull;
799 if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) {
800 // Make sure we don't thrash things by trying this load again, since
801 // chances are it failed for good reasons (security check, etc).
802 AddExternalResource(clone, nsnull, nsnull, aDisplayDocument);
803 } else {
804 NS_ADDREF(*aPendingLoad = load);
807 return nsnull;
810 struct
811 nsExternalResourceEnumArgs
813 nsIDocument::nsSubDocEnumFunc callback;
814 void *data;
817 static PLDHashOperator
818 ExternalResourceEnumerator(nsIURI* aKey,
819 nsExternalResourceMap::ExternalResource* aData,
820 void* aClosure)
822 nsExternalResourceEnumArgs* args =
823 static_cast<nsExternalResourceEnumArgs*>(aClosure);
824 PRBool next =
825 aData->mDocument ? args->callback(aData->mDocument, args->data) : PR_TRUE;
826 return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
829 void
830 nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,
831 void* aData)
833 nsExternalResourceEnumArgs args = { aCallback, aData };
834 mMap.EnumerateRead(ExternalResourceEnumerator, &args);
837 static PLDHashOperator
838 ExternalResourceTraverser(nsIURI* aKey,
839 nsExternalResourceMap::ExternalResource* aData,
840 void* aClosure)
842 nsCycleCollectionTraversalCallback *cb =
843 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
845 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
846 "mExternalResourceMap.mMap entry"
847 "->mDocument");
848 cb->NoteXPCOMChild(aData->mDocument);
850 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
851 "mExternalResourceMap.mMap entry"
852 "->mViewer");
853 cb->NoteXPCOMChild(aData->mViewer);
855 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
856 "mExternalResourceMap.mMap entry"
857 "->mLoadGroup");
858 cb->NoteXPCOMChild(aData->mLoadGroup);
860 return PL_DHASH_NEXT;
863 void
864 nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const
866 // mPendingLoads will get cleared out as the requests complete, so
867 // no need to worry about those here.
868 mMap.EnumerateRead(ExternalResourceTraverser, aCallback);
871 nsresult
872 nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
873 nsIDocumentViewer* aViewer,
874 nsILoadGroup* aLoadGroup,
875 nsIDocument* aDisplayDocument)
877 NS_PRECONDITION(aURI, "Unexpected call");
878 NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
879 "Must have both or neither");
881 nsRefPtr<PendingLoad> load;
882 mPendingLoads.Get(aURI, getter_AddRefs(load));
883 mPendingLoads.Remove(aURI);
885 nsresult rv = NS_OK;
887 nsCOMPtr<nsIDocument> doc;
888 if (aViewer) {
889 aViewer->GetDocument(getter_AddRefs(doc));
890 NS_ASSERTION(doc, "Must have a document");
892 nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(doc);
893 if (xulDoc) {
894 // We don't handle XUL stuff here yet.
895 rv = NS_ERROR_NOT_AVAILABLE;
896 } else {
897 doc->SetDisplayDocument(aDisplayDocument);
899 rv = aViewer->Init(nsnull, nsRect(0, 0, 0, 0));
900 if (NS_SUCCEEDED(rv)) {
901 rv = aViewer->Open(nsnull, nsnull);
905 if (NS_FAILED(rv)) {
906 doc = nsnull;
907 aViewer = nsnull;
908 aLoadGroup = nsnull;
912 ExternalResource* newResource = new ExternalResource();
913 if (newResource && !mMap.Put(aURI, newResource)) {
914 delete newResource;
915 newResource = nsnull;
916 if (NS_SUCCEEDED(rv)) {
917 rv = NS_ERROR_OUT_OF_MEMORY;
921 if (newResource) {
922 newResource->mDocument = doc;
923 newResource->mViewer = aViewer;
924 newResource->mLoadGroup = aLoadGroup;
927 const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers();
928 for (PRUint32 i = 0; i < obs.Length(); ++i) {
929 obs[i]->Observe(doc, "external-resource-document-created", nsnull);
932 return rv;
935 NS_IMPL_ISUPPORTS2(nsExternalResourceMap::PendingLoad,
936 nsIStreamListener,
937 nsIRequestObserver)
939 NS_IMETHODIMP
940 nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest,
941 nsISupports *aContext)
943 nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
944 if (map.HaveShutDown()) {
945 return NS_BINDING_ABORTED;
948 nsCOMPtr<nsIDocumentViewer> viewer;
949 nsCOMPtr<nsILoadGroup> loadGroup;
950 nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer),
951 getter_AddRefs(loadGroup));
953 // Make sure to do this no matter what
954 nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup,
955 mDisplayDocument);
956 if (NS_FAILED(rv)) {
957 return rv;
959 if (NS_FAILED(rv2)) {
960 mTargetListener = nsnull;
961 return rv2;
964 return mTargetListener->OnStartRequest(aRequest, aContext);
967 nsresult
968 nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
969 nsIDocumentViewer** aViewer,
970 nsILoadGroup** aLoadGroup)
972 NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest");
973 *aViewer = nsnull;
974 *aLoadGroup = nsnull;
976 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
977 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
979 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
980 if (httpChannel) {
981 PRBool requestSucceeded;
982 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
983 !requestSucceeded) {
984 // Bail out on this load, since it looks like we have an HTTP error page
985 return NS_BINDING_ABORTED;
989 nsCAutoString type;
990 chan->GetContentType(type);
992 nsCOMPtr<nsILoadGroup> loadGroup;
993 chan->GetLoadGroup(getter_AddRefs(loadGroup));
995 // Give this document its own loadgroup
996 nsCOMPtr<nsILoadGroup> newLoadGroup =
997 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
998 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
999 newLoadGroup->SetLoadGroup(loadGroup);
1001 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1002 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
1004 nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
1005 new LoadgroupCallbacks(callbacks);
1006 newLoadGroup->SetNotificationCallbacks(newCallbacks);
1008 // This is some serious hackery cribbed from docshell
1009 nsCOMPtr<nsICategoryManager> catMan =
1010 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
1011 NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
1012 nsXPIDLCString contractId;
1013 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
1014 getter_Copies(contractId));
1015 NS_ENSURE_SUCCESS(rv, rv);
1016 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
1017 do_GetService(contractId);
1018 NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
1020 nsCOMPtr<nsIContentViewer> viewer;
1021 nsCOMPtr<nsIStreamListener> listener;
1022 rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
1023 type.get(), nsnull, nsnull,
1024 getter_AddRefs(listener),
1025 getter_AddRefs(viewer));
1026 NS_ENSURE_SUCCESS(rv, rv);
1028 nsCOMPtr<nsIDocumentViewer> docViewer = do_QueryInterface(viewer);
1029 NS_ENSURE_TRUE(docViewer, NS_ERROR_UNEXPECTED);
1031 nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
1032 if (!parser) {
1033 /// We don't want to deal with the various fake documents yet
1034 return NS_ERROR_NOT_IMPLEMENTED;
1037 // We can't handle HTML and other weird things here yet.
1038 nsIContentSink* sink = parser->GetContentSink();
1039 nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
1040 if (!xmlSink) {
1041 return NS_ERROR_NOT_IMPLEMENTED;
1044 listener.swap(mTargetListener);
1045 docViewer.swap(*aViewer);
1046 newLoadGroup.swap(*aLoadGroup);
1047 return NS_OK;
1050 NS_IMETHODIMP
1051 nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
1052 nsISupports* aContext,
1053 nsIInputStream* aStream,
1054 PRUint32 aOffset,
1055 PRUint32 aCount)
1057 NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!");
1058 if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
1059 return NS_BINDING_ABORTED;
1061 return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
1062 aCount);
1065 NS_IMETHODIMP
1066 nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
1067 nsISupports* aContext,
1068 nsresult aStatus)
1070 // mTargetListener might be null if SetupViewer or AddExternalResource failed
1071 if (mTargetListener) {
1072 nsCOMPtr<nsIStreamListener> listener;
1073 mTargetListener.swap(listener);
1074 return listener->OnStopRequest(aRequest, aContext, aStatus);
1077 return NS_OK;
1080 nsresult
1081 nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
1082 nsINode* aRequestingNode)
1084 NS_PRECONDITION(aURI, "Must have a URI");
1085 NS_PRECONDITION(aRequestingNode, "Must have a node");
1087 // Time to start a load. First, the security checks.
1089 nsIPrincipal* requestingPrincipal = aRequestingNode->NodePrincipal();
1091 nsresult rv = nsContentUtils::GetSecurityManager()->
1092 CheckLoadURIWithPrincipal(requestingPrincipal, aURI,
1093 nsIScriptSecurityManager::STANDARD);
1094 NS_ENSURE_SUCCESS(rv, rv);
1096 rv = requestingPrincipal->CheckMayLoad(aURI, PR_TRUE);
1097 NS_ENSURE_SUCCESS(rv, rv);
1099 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
1100 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER,
1101 aURI,
1102 requestingPrincipal,
1103 aRequestingNode,
1104 EmptyCString(), //mime guess
1105 nsnull, //extra
1106 &shouldLoad,
1107 nsContentUtils::GetContentPolicy(),
1108 nsContentUtils::GetSecurityManager());
1109 if (NS_FAILED(rv)) return rv;
1110 if (NS_CP_REJECTED(shouldLoad)) {
1111 // Disallowed by content policy
1112 return NS_ERROR_CONTENT_BLOCKED;
1115 nsIDocument* doc = aRequestingNode->GetOwnerDoc();
1116 if (!doc) {
1117 return NS_ERROR_NOT_AVAILABLE;
1120 nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker();
1121 NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY);
1123 nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
1124 nsCOMPtr<nsIChannel> channel;
1125 rv = NS_NewChannel(getter_AddRefs(channel), aURI, nsnull, loadGroup, req);
1126 NS_ENSURE_SUCCESS(rv, rv);
1128 mURI = aURI;
1130 return channel->AsyncOpen(this, nsnull);
1133 NS_IMPL_ISUPPORTS1(nsExternalResourceMap::LoadgroupCallbacks,
1134 nsIInterfaceRequestor)
1136 #define IMPL_SHIM(_i) \
1137 NS_IMPL_ISUPPORTS1(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1139 IMPL_SHIM(nsILoadContext)
1140 IMPL_SHIM(nsIProgressEventSink)
1141 IMPL_SHIM(nsIChannelEventSink)
1142 IMPL_SHIM(nsISecurityEventSink)
1143 IMPL_SHIM(nsIApplicationCacheContainer)
1145 #undef IMPL_SHIM
1147 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1149 #define TRY_SHIM(_i) \
1150 PR_BEGIN_MACRO \
1151 if (IID_IS(_i)) { \
1152 nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1153 if (!real) { \
1154 return NS_NOINTERFACE; \
1156 nsCOMPtr<_i> shim = new _i##Shim(this, real); \
1157 if (!shim) { \
1158 return NS_ERROR_OUT_OF_MEMORY; \
1160 *aSink = shim.forget().get(); \
1161 return NS_OK; \
1163 PR_END_MACRO
1165 NS_IMETHODIMP
1166 nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
1167 void **aSink)
1169 if (mCallbacks &&
1170 (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2))) {
1171 return mCallbacks->GetInterface(aIID, aSink);
1174 *aSink = nsnull;
1176 TRY_SHIM(nsILoadContext);
1177 TRY_SHIM(nsIProgressEventSink);
1178 TRY_SHIM(nsIChannelEventSink);
1179 TRY_SHIM(nsISecurityEventSink);
1180 TRY_SHIM(nsIApplicationCacheContainer);
1182 return NS_NOINTERFACE;
1185 #undef TRY_SHIM
1186 #undef IID_IS
1188 nsExternalResourceMap::ExternalResource::~ExternalResource()
1190 if (mViewer) {
1191 mViewer->Close(nsnull);
1192 mViewer->Destroy();
1196 // ==================================================================
1197 // =
1198 // ==================================================================
1200 // If we ever have an nsIDocumentObserver notification for stylesheet title
1201 // changes, we could make this inherit from nsDOMStringList instead of
1202 // reimplementing nsIDOMDOMStringList.
1203 class nsDOMStyleSheetSetList : public nsIDOMDOMStringList
1206 public:
1207 NS_DECL_ISUPPORTS
1209 NS_DECL_NSIDOMDOMSTRINGLIST
1211 nsDOMStyleSheetSetList(nsIDocument* aDocument);
1213 void Disconnect()
1215 mDocument = nsnull;
1218 protected:
1219 // Rebuild our list of style sets
1220 nsresult GetSets(nsStringArray& aStyleSets);
1222 nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it
1223 // dies.
1226 NS_IMPL_ADDREF(nsDOMStyleSheetSetList)
1227 NS_IMPL_RELEASE(nsDOMStyleSheetSetList)
1228 NS_INTERFACE_TABLE_HEAD(nsDOMStyleSheetSetList)
1229 NS_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsDOMStyleSheetSetList)
1230 NS_INTERFACE_TABLE_ENTRY(nsDOMStyleSheetSetList, nsIDOMDOMStringList)
1231 NS_OFFSET_AND_INTERFACE_TABLE_END
1232 NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
1233 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(DOMStringList)
1234 NS_INTERFACE_MAP_END
1236 nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument)
1237 : mDocument(aDocument)
1239 NS_ASSERTION(mDocument, "Must have document!");
1242 NS_IMETHODIMP
1243 nsDOMStyleSheetSetList::Item(PRUint32 aIndex, nsAString& aResult)
1245 nsStringArray styleSets;
1246 nsresult rv = GetSets(styleSets);
1247 NS_ENSURE_SUCCESS(rv, rv);
1249 if (aIndex >= (PRUint32)styleSets.Count()) {
1250 SetDOMStringToNull(aResult);
1251 } else {
1252 styleSets.StringAt(aIndex, aResult);
1255 return NS_OK;
1258 NS_IMETHODIMP
1259 nsDOMStyleSheetSetList::GetLength(PRUint32 *aLength)
1261 nsStringArray styleSets;
1262 nsresult rv = GetSets(styleSets);
1263 NS_ENSURE_SUCCESS(rv, rv);
1265 *aLength = (PRUint32)styleSets.Count();
1267 return NS_OK;
1270 NS_IMETHODIMP
1271 nsDOMStyleSheetSetList::Contains(const nsAString& aString, PRBool *aResult)
1273 nsStringArray styleSets;
1274 nsresult rv = GetSets(styleSets);
1275 NS_ENSURE_SUCCESS(rv, rv);
1277 *aResult = styleSets.IndexOf(aString) != -1;
1279 return NS_OK;
1282 nsresult
1283 nsDOMStyleSheetSetList::GetSets(nsStringArray& aStyleSets)
1285 if (!mDocument) {
1286 return NS_OK; // Spec says "no exceptions", and we have no style sets if we
1287 // have no document, for sure
1290 PRInt32 count = mDocument->GetNumberOfStyleSheets();
1291 nsAutoString title;
1292 nsAutoString temp;
1293 for (PRInt32 index = 0; index < count; index++) {
1294 nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index);
1295 NS_ASSERTION(sheet, "Null sheet in sheet list!");
1296 sheet->GetTitle(title);
1297 if (!title.IsEmpty() && aStyleSets.IndexOf(title) == -1 &&
1298 !aStyleSets.AppendString(title)) {
1299 return NS_ERROR_OUT_OF_MEMORY;
1303 return NS_OK;
1306 // ==================================================================
1307 // =
1308 // ==================================================================
1310 class nsDOMImplementation : public nsIDOMDOMImplementation,
1311 public nsIPrivateDOMImplementation
1313 public:
1314 nsDOMImplementation(nsIScriptGlobalObject* aScriptObject,
1315 nsIURI* aDocumentURI,
1316 nsIURI* aBaseURI,
1317 nsIPrincipal* aPrincipal);
1318 virtual ~nsDOMImplementation();
1320 NS_DECL_ISUPPORTS
1322 // nsIDOMDOMImplementation
1323 NS_DECL_NSIDOMDOMIMPLEMENTATION
1325 // nsIPrivateDOMImplementation
1326 NS_IMETHOD Init(nsIURI* aDocumentURI, nsIURI* aBaseURI,
1327 nsIPrincipal* aPrincipal);
1329 protected:
1330 nsWeakPtr mScriptObject;
1331 nsCOMPtr<nsIURI> mDocumentURI;
1332 nsCOMPtr<nsIURI> mBaseURI;
1333 nsCOMPtr<nsIPrincipal> mPrincipal;
1337 nsresult
1338 NS_NewDOMImplementation(nsIDOMDOMImplementation** aInstancePtrResult)
1340 *aInstancePtrResult = new nsDOMImplementation(nsnull, nsnull, nsnull, nsnull);
1341 if (!*aInstancePtrResult) {
1342 return NS_ERROR_OUT_OF_MEMORY;
1345 NS_ADDREF(*aInstancePtrResult);
1347 return NS_OK;
1350 nsDOMImplementation::nsDOMImplementation(nsIScriptGlobalObject* aScriptObject,
1351 nsIURI* aDocumentURI,
1352 nsIURI* aBaseURI,
1353 nsIPrincipal* aPrincipal)
1354 : mScriptObject(do_GetWeakReference(aScriptObject)),
1355 mDocumentURI(aDocumentURI),
1356 mBaseURI(aBaseURI),
1357 mPrincipal(aPrincipal)
1361 nsDOMImplementation::~nsDOMImplementation()
1365 // QueryInterface implementation for nsDOMImplementation
1366 NS_INTERFACE_MAP_BEGIN(nsDOMImplementation)
1367 NS_INTERFACE_MAP_ENTRY(nsIDOMDOMImplementation)
1368 NS_INTERFACE_MAP_ENTRY(nsIPrivateDOMImplementation)
1369 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDOMImplementation)
1370 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(DOMImplementation)
1371 NS_INTERFACE_MAP_END
1374 NS_IMPL_ADDREF(nsDOMImplementation)
1375 NS_IMPL_RELEASE(nsDOMImplementation)
1378 NS_IMETHODIMP
1379 nsDOMImplementation::HasFeature(const nsAString& aFeature,
1380 const nsAString& aVersion,
1381 PRBool* aReturn)
1383 return nsGenericElement::InternalIsSupported(
1384 static_cast<nsIDOMDOMImplementation*>(this),
1385 aFeature, aVersion, aReturn);
1388 NS_IMETHODIMP
1389 nsDOMImplementation::CreateDocumentType(const nsAString& aQualifiedName,
1390 const nsAString& aPublicId,
1391 const nsAString& aSystemId,
1392 nsIDOMDocumentType** aReturn)
1394 *aReturn = nsnull;
1396 nsresult rv = nsContentUtils::CheckQName(aQualifiedName);
1397 NS_ENSURE_SUCCESS(rv, rv);
1399 nsCOMPtr<nsIAtom> name = do_GetAtom(aQualifiedName);
1400 NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
1402 // Indicate that there is no internal subset (not just an empty one)
1403 nsAutoString voidString;
1404 voidString.SetIsVoid(PR_TRUE);
1405 return NS_NewDOMDocumentType(aReturn, nsnull, mPrincipal, name, nsnull,
1406 nsnull, aPublicId, aSystemId, voidString);
1409 NS_IMETHODIMP
1410 nsDOMImplementation::CreateDocument(const nsAString& aNamespaceURI,
1411 const nsAString& aQualifiedName,
1412 nsIDOMDocumentType* aDoctype,
1413 nsIDOMDocument** aReturn)
1415 *aReturn = nsnull;
1417 nsresult rv;
1418 if (!aQualifiedName.IsEmpty()) {
1419 nsIParserService *parserService = nsContentUtils::GetParserService();
1420 NS_ENSURE_TRUE(parserService, NS_ERROR_FAILURE);
1422 const nsAFlatString& qName = PromiseFlatString(aQualifiedName);
1423 const PRUnichar *colon;
1424 rv = parserService->CheckQName(qName, PR_TRUE, &colon);
1425 NS_ENSURE_SUCCESS(rv, rv);
1427 if (colon &&
1428 (DOMStringIsNull(aNamespaceURI) ||
1429 (Substring(qName.get(), colon).EqualsLiteral("xml") &&
1430 !aNamespaceURI.EqualsLiteral("http://www.w3.org/XML/1998/namespace")))) {
1431 return NS_ERROR_DOM_NAMESPACE_ERR;
1434 else if (DOMStringIsNull(aQualifiedName) &&
1435 !DOMStringIsNull(aNamespaceURI)) {
1436 return NS_ERROR_DOM_NAMESPACE_ERR;
1439 if (aDoctype) {
1440 nsCOMPtr<nsIDOMDocument> owner;
1441 aDoctype->GetOwnerDocument(getter_AddRefs(owner));
1442 if (owner) {
1443 return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
1447 nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
1448 do_QueryReferent(mScriptObject);
1450 return nsContentUtils::CreateDocument(aNamespaceURI, aQualifiedName, aDoctype,
1451 mDocumentURI, mBaseURI, mPrincipal,
1452 scriptHandlingObject, aReturn);
1455 NS_IMETHODIMP
1456 nsDOMImplementation::Init(nsIURI* aDocumentURI, nsIURI* aBaseURI,
1457 nsIPrincipal* aPrincipal)
1459 // Note: can't require that the args be non-null, since at least one
1460 // caller (XMLHttpRequest) doesn't have decent args to pass in.
1461 mDocumentURI = aDocumentURI;
1462 mBaseURI = aBaseURI;
1463 mPrincipal = aPrincipal;
1464 return NS_OK;
1467 // ==================================================================
1468 // =
1469 // ==================================================================
1471 // NOTE! nsDocument::operator new() zeroes out all members, so don't
1472 // bother initializing members to 0.
1474 nsDocument::nsDocument(const char* aContentType)
1475 : nsIDocument(),
1476 mVisible(PR_TRUE)
1478 mContentType = aContentType;
1480 #ifdef PR_LOGGING
1481 if (!gDocumentLeakPRLog)
1482 gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak");
1484 if (gDocumentLeakPRLog)
1485 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
1486 ("DOCUMENT %p created", this));
1487 #endif
1489 // Start out mLastStyleSheetSet as null, per spec
1490 SetDOMStringToNull(mLastStyleSheetSet);
1493 static PLDHashOperator
1494 ClearAllBoxObjects(const void* aKey, nsPIBoxObject* aBoxObject, void* aUserArg)
1496 if (aBoxObject) {
1497 aBoxObject->Clear();
1499 return PL_DHASH_NEXT;
1502 nsDocument::~nsDocument()
1504 #ifdef PR_LOGGING
1505 if (gDocumentLeakPRLog)
1506 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
1507 ("DOCUMENT %p destroyed", this));
1508 #endif
1510 #ifdef DEBUG
1511 nsCycleCollector_DEBUG_wasFreed(static_cast<nsIDocument*>(this));
1512 #endif
1514 mInDestructor = PR_TRUE;
1516 // Clear mObservers to keep it in sync with the mutationobserver list
1517 mObservers.Clear();
1519 if (mStyleSheetSetList) {
1520 mStyleSheetSetList->Disconnect();
1523 mParentDocument = nsnull;
1525 // Kill the subdocument map, doing this will release its strong
1526 // references, if any.
1527 if (mSubDocuments) {
1528 PL_DHashTableDestroy(mSubDocuments);
1530 mSubDocuments = nsnull;
1533 // Destroy link map now so we don't waste time removing
1534 // links one by one
1535 DestroyLinkMap();
1537 nsAutoScriptBlocker scriptBlocker;
1539 PRInt32 indx; // must be signed
1540 PRUint32 count = mChildren.ChildCount();
1541 for (indx = PRInt32(count) - 1; indx >= 0; --indx) {
1542 mChildren.ChildAt(indx)->UnbindFromTree();
1543 mChildren.RemoveChildAt(indx);
1545 mCachedRootContent = nsnull;
1547 // Let the stylesheets know we're going away
1548 indx = mStyleSheets.Count();
1549 while (--indx >= 0) {
1550 mStyleSheets[indx]->SetOwningDocument(nsnull);
1552 indx = mCatalogSheets.Count();
1553 while (--indx >= 0) {
1554 mCatalogSheets[indx]->SetOwningDocument(nsnull);
1556 if (mAttrStyleSheet)
1557 mAttrStyleSheet->SetOwningDocument(nsnull);
1558 if (mStyleAttrStyleSheet)
1559 mStyleAttrStyleSheet->SetOwningDocument(nsnull);
1561 if (mListenerManager) {
1562 mListenerManager->Disconnect();
1565 if (mScriptLoader) {
1566 mScriptLoader->DropDocumentReference();
1569 if (mCSSLoader) {
1570 // Could be null here if Init() failed
1571 mCSSLoader->DropDocumentReference();
1572 NS_RELEASE(mCSSLoader);
1575 // XXX Ideally we'd do this cleanup in the nsIDocument destructor.
1576 if (mNodeInfoManager) {
1577 mNodeInfoManager->DropDocumentReference();
1578 NS_RELEASE(mNodeInfoManager);
1581 if (mAttrStyleSheet) {
1582 mAttrStyleSheet->SetOwningDocument(nsnull);
1585 if (mStyleAttrStyleSheet) {
1586 mStyleAttrStyleSheet->SetOwningDocument(nsnull);
1589 delete mHeaderData;
1591 if (mBoxObjectTable) {
1592 mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nsnull);
1593 delete mBoxObjectTable;
1597 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
1599 NS_INTERFACE_TABLE_HEAD(nsDocument)
1600 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1601 NS_DOCUMENT_INTERFACE_TABLE_BEGIN(nsDocument)
1602 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
1603 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
1604 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOM3DocumentEvent)
1605 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentStyle)
1606 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNSDocumentStyle)
1607 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentRange)
1608 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL)
1609 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
1610 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOM3EventTarget)
1611 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNSEventTarget)
1612 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsPIDOMEventTarget)
1613 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
1614 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
1615 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
1616 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNodeSelector)
1617 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
1618 NS_OFFSET_AND_INTERFACE_TABLE_END
1619 NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
1620 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDocument)
1621 if (aIID.Equals(NS_GET_IID(nsIDOMXPathEvaluator)) ||
1622 aIID.Equals(NS_GET_IID(nsIXPathEvaluatorInternal))) {
1623 if (!mXPathEvaluatorTearoff) {
1624 nsresult rv;
1625 mXPathEvaluatorTearoff =
1626 do_CreateInstance(NS_XPATH_EVALUATOR_CONTRACTID,
1627 static_cast<nsIDocument *>(this), &rv);
1628 NS_ENSURE_SUCCESS(rv, rv);
1631 return mXPathEvaluatorTearoff->QueryInterface(aIID, aInstancePtr);
1633 else
1634 NS_INTERFACE_MAP_END
1637 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsDocument, nsIDocument)
1638 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS_WITH_DESTROY(nsDocument,
1639 nsIDocument,
1640 nsNodeUtils::LastRelease(this))
1643 static PLDHashOperator
1644 SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number,
1645 void *arg)
1647 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
1648 nsCycleCollectionTraversalCallback *cb =
1649 static_cast<nsCycleCollectionTraversalCallback*>(arg);
1651 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mKey");
1652 cb->NoteXPCOMChild(entry->mKey);
1653 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mSubDocument");
1654 cb->NoteXPCOMChild(entry->mSubDocument);
1656 return PL_DHASH_NEXT;
1659 static PLDHashOperator
1660 RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData,
1661 void* aClosure)
1663 nsCycleCollectionTraversalCallback *cb =
1664 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
1666 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1667 "mRadioGroups entry->mSelectedRadioButton");
1668 cb->NoteXPCOMChild(aData->mSelectedRadioButton);
1670 PRUint32 i, count = aData->mRadioButtons.Count();
1671 for (i = 0; i < count; ++i) {
1672 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1673 "mRadioGroups entry->mRadioButtons[i]");
1674 cb->NoteXPCOMChild(aData->mRadioButtons[i]);
1677 return PL_DHASH_NEXT;
1680 static PLDHashOperator
1681 BoxObjectTraverser(const void* key, nsPIBoxObject* boxObject, void* userArg)
1683 nsCycleCollectionTraversalCallback *cb =
1684 static_cast<nsCycleCollectionTraversalCallback*>(userArg);
1686 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry");
1687 cb->NoteXPCOMChild(boxObject);
1689 return PL_DHASH_NEXT;
1692 class LinkMapTraversalVisitor : public nsUint32ToContentHashEntry::Visitor
1694 public:
1695 nsCycleCollectionTraversalCallback *mCb;
1696 virtual void Visit(nsIContent* aContent)
1698 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*mCb, "mLinkMap entry");
1699 mCb->NoteXPCOMChild(aContent);
1703 static PLDHashOperator
1704 LinkMapTraverser(nsUint32ToContentHashEntry* aEntry, void* userArg)
1706 LinkMapTraversalVisitor visitor;
1707 visitor.mCb = static_cast<nsCycleCollectionTraversalCallback*>(userArg);
1708 aEntry->VisitContent(&visitor);
1709 return PL_DHASH_NEXT;
1712 static PLDHashOperator
1713 IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg)
1715 nsCycleCollectionTraversalCallback *cb =
1716 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
1717 aEntry->Traverse(cb);
1718 return PL_DHASH_NEXT;
1721 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument)
1722 if (nsCCUncollectableMarker::InGeneration(tmp->GetMarkedCCGeneration())) {
1723 return NS_OK;
1726 tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
1728 tmp->mExternalResourceMap.Traverse(&cb);
1730 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNodeInfo)
1732 // Traverse the mChildren nsAttrAndChildArray.
1733 for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()); indx > 0; --indx) {
1734 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
1735 cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
1738 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_USERDATA
1740 // Traverse all nsIDocument pointer members.
1741 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCachedRootContent)
1742 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNodeInfoManager,
1743 nsNodeInfoManager)
1744 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSecurityInfo)
1745 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDisplayDocument)
1747 // Traverse all nsDocument nsCOMPtrs.
1748 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParser)
1749 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptGlobalObject)
1750 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mListenerManager)
1751 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMStyleSheets)
1752 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptLoader)
1754 tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb);
1756 // The boxobject for an element will only exist as long as it's in the
1757 // document, so we'll traverse the table here instead of from the element.
1758 if (tmp->mBoxObjectTable) {
1759 tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb);
1762 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
1763 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStyleAttrStyleSheet)
1764 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptEventManager)
1765 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXPathEvaluatorTearoff)
1766 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLayoutHistoryState)
1767 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnloadBlocker)
1769 // An element will only be in the linkmap as long as it's in the
1770 // document, so we'll traverse the table here instead of from the element.
1771 if (tmp->mLinkMap.IsInitialized()) {
1772 tmp->mLinkMap.EnumerateEntries(LinkMapTraverser, &cb);
1775 // Traverse all our nsCOMArrays.
1776 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
1777 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets)
1778 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mVisitednessChangedURIs)
1780 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER
1782 if (tmp->mSubDocuments && tmp->mSubDocuments->ops) {
1783 PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
1785 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1788 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
1789 // Tear down linkmap. This is a performance optimization so that we
1790 // don't waste time removing links one by one as they are removed
1791 // from the doc.
1792 tmp->DestroyLinkMap();
1794 // Clear out our external resources
1795 tmp->mExternalResourceMap.Shutdown();
1797 nsAutoScriptBlocker scriptBlocker;
1799 // Unlink the mChildren nsAttrAndChildArray.
1800 for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()) - 1;
1801 indx >= 0; --indx) {
1802 tmp->mChildren.ChildAt(indx)->UnbindFromTree();
1803 tmp->mChildren.RemoveChildAt(indx);
1806 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedRootContent)
1807 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDisplayDocument)
1809 NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA
1811 tmp->mParentDocument = nsnull;
1813 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1815 // nsDocument has a pretty complex destructor, so we're going to
1816 // assume that *most* cycles you actually want to break somewhere
1817 // else, and not unlink an awful lot here.
1819 // In rare cases where you think an unlink will help here, add one
1820 // manually.
1821 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1824 nsresult
1825 nsDocument::Init()
1827 if (mCSSLoader || mNodeInfoManager || mScriptLoader) {
1828 return NS_ERROR_ALREADY_INITIALIZED;
1831 mIdentifierMap.Init();
1832 mLinkMap.Init();
1833 mRadioGroups.Init();
1835 // Force initialization.
1836 nsINode::nsSlots* slots = GetSlots();
1837 NS_ENSURE_TRUE(slots,NS_ERROR_OUT_OF_MEMORY);
1839 // Prepend self as mutation-observer whether we need it or not (some
1840 // subclasses currently do, other don't). This is because the code in
1841 // nsNodeUtils always notifies the first observer first, expecting the
1842 // first observer to be the document.
1843 NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
1844 NS_ERROR_OUT_OF_MEMORY);
1847 mOnloadBlocker = new nsOnloadBlocker();
1848 NS_ENSURE_TRUE(mOnloadBlocker, NS_ERROR_OUT_OF_MEMORY);
1850 NS_NewCSSLoader(this, &mCSSLoader);
1851 NS_ENSURE_TRUE(mCSSLoader, NS_ERROR_OUT_OF_MEMORY);
1852 // Assume we're not HTML and not quirky, until we know otherwise
1853 mCSSLoader->SetCaseSensitive(PR_TRUE);
1854 mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
1856 mNodeInfoManager = new nsNodeInfoManager();
1857 NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_OUT_OF_MEMORY);
1859 NS_ADDREF(mNodeInfoManager);
1861 nsresult rv = mNodeInfoManager->Init(this);
1862 NS_ENSURE_SUCCESS(rv, rv);
1864 mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
1865 NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
1867 NS_ASSERTION(GetOwnerDoc() == this, "Our nodeinfo is busted!");
1869 mScriptLoader = new nsScriptLoader(this);
1870 NS_ENSURE_TRUE(mScriptLoader, NS_ERROR_OUT_OF_MEMORY);
1872 return NS_OK;
1875 nsresult
1876 nsDocument::AddXMLEventsContent(nsIContent *aXMLEventsElement)
1878 if (!mXMLEventsManager) {
1879 mXMLEventsManager = new nsXMLEventsManager();
1880 NS_ENSURE_TRUE(mXMLEventsManager, NS_ERROR_OUT_OF_MEMORY);
1881 AddObserver(mXMLEventsManager);
1883 mXMLEventsManager->AddXMLEventsContent(aXMLEventsElement);
1884 return NS_OK;
1887 void
1888 nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
1890 nsCOMPtr<nsIURI> uri;
1891 nsCOMPtr<nsIPrincipal> principal;
1892 if (aChannel) {
1893 // Note: this code is duplicated in nsXULDocument::StartDocumentLoad and
1894 // nsScriptSecurityManager::GetChannelPrincipal.
1895 // Note: this should match nsDocShell::OnLoadingSite
1896 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
1898 nsIScriptSecurityManager *securityManager =
1899 nsContentUtils::GetSecurityManager();
1900 if (securityManager) {
1901 securityManager->GetChannelPrincipal(aChannel,
1902 getter_AddRefs(principal));
1906 ResetToURI(uri, aLoadGroup, principal);
1908 nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
1909 if (bag) {
1910 nsCOMPtr<nsIURI> baseURI;
1911 bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
1912 NS_GET_IID(nsIURI), getter_AddRefs(baseURI));
1913 if (baseURI) {
1914 mDocumentBaseURI = baseURI;
1918 mChannel = aChannel;
1921 void
1922 nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
1923 nsIPrincipal* aPrincipal)
1925 NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
1927 #ifdef PR_LOGGING
1928 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
1929 nsCAutoString spec;
1930 aURI->GetSpec(spec);
1931 PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get());
1933 #endif
1935 mIdentifierMap.Clear();
1937 SetPrincipal(nsnull);
1938 mSecurityInfo = nsnull;
1940 mDocumentLoadGroup = nsnull;
1942 // Delete references to sub-documents and kill the subdocument map,
1943 // if any. It holds strong references
1944 if (mSubDocuments) {
1945 PL_DHashTableDestroy(mSubDocuments);
1947 mSubDocuments = nsnull;
1950 // Destroy link map now so we don't waste time removing
1951 // links one by one
1952 DestroyLinkMap();
1954 PRUint32 count = mChildren.ChildCount();
1955 { // Scope for update
1956 MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, PR_TRUE);
1957 for (PRInt32 i = PRInt32(count) - 1; i >= 0; i--) {
1958 nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
1960 // XXXbz this is backwards from how ContentRemoved normally works. That
1961 // is, usually it's dispatched after the content has been removed from
1962 // the tree.
1963 nsNodeUtils::ContentRemoved(this, content, i);
1964 content->UnbindFromTree();
1965 mChildren.RemoveChildAt(i);
1968 mCachedRootContent = nsnull;
1970 // Reset our stylesheets
1971 ResetStylesheetsToURI(aURI);
1973 // Release the listener manager
1974 if (mListenerManager) {
1975 mListenerManager->Disconnect();
1976 mListenerManager = nsnull;
1979 // Release the stylesheets list.
1980 mDOMStyleSheets = nsnull;
1982 SetDocumentURI(aURI);
1983 mDocumentBaseURI = mDocumentURI;
1985 if (aLoadGroup) {
1986 mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
1987 // there was an assertion here that aLoadGroup was not null. This
1988 // is no longer valid nsWebShell::SetDocument does not create a
1989 // load group, and it works just fine.
1992 mLastModified.Truncate();
1993 // XXXbz I guess we're assuming that the caller will either pass in
1994 // a channel with a useful type or call SetContentType?
1995 mContentType.Truncate();
1996 mContentLanguage.Truncate();
1997 mBaseTarget.Truncate();
1998 mReferrer.Truncate();
2000 mXMLDeclarationBits = 0;
2002 // Now get our new principal
2003 if (aPrincipal) {
2004 SetPrincipal(aPrincipal);
2005 } else {
2006 nsIScriptSecurityManager *securityManager =
2007 nsContentUtils::GetSecurityManager();
2008 if (securityManager) {
2009 nsCOMPtr<nsIPrincipal> principal;
2010 nsresult rv =
2011 securityManager->GetCodebasePrincipal(mDocumentURI,
2012 getter_AddRefs(principal));
2013 if (NS_SUCCEEDED(rv)) {
2014 SetPrincipal(principal);
2020 nsresult
2021 nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
2023 NS_PRECONDITION(aURI, "Null URI passed to ResetStylesheetsToURI");
2025 mozAutoDocUpdate upd(this, UPDATE_STYLE, PR_TRUE);
2027 // The stylesheets should forget us
2028 PRInt32 indx = mStyleSheets.Count();
2029 while (--indx >= 0) {
2030 nsIStyleSheet* sheet = mStyleSheets[indx];
2031 sheet->SetOwningDocument(nsnull);
2033 PRBool applicable;
2034 sheet->GetApplicable(applicable);
2035 if (applicable) {
2036 RemoveStyleSheetFromStyleSets(sheet);
2039 // XXX Tell observers?
2042 indx = mCatalogSheets.Count();
2043 while (--indx >= 0) {
2044 nsIStyleSheet* sheet = mCatalogSheets[indx];
2045 sheet->SetOwningDocument(nsnull);
2047 PRBool applicable;
2048 sheet->GetApplicable(applicable);
2049 if (applicable) {
2050 nsPresShellIterator iter(this);
2051 nsCOMPtr<nsIPresShell> shell;
2052 while ((shell = iter.GetNextShell())) {
2053 shell->StyleSet()->RemoveStyleSheet(nsStyleSet::eAgentSheet, sheet);
2057 // XXX Tell observers?
2061 // Release all the sheets
2062 mStyleSheets.Clear();
2063 // NOTE: We don't release the catalog sheets. It doesn't really matter
2064 // now, but it could in the future -- in which case not releasing them
2065 // is probably the right thing to do.
2067 // Now reset our inline style and attribute sheets.
2068 nsresult rv;
2069 nsStyleSet::sheetType attrSheetType = GetAttrSheetType();
2070 if (mAttrStyleSheet) {
2071 // Remove this sheet from all style sets
2072 nsPresShellIterator iter(this);
2073 nsCOMPtr<nsIPresShell> shell;
2074 while ((shell = iter.GetNextShell())) {
2075 shell->StyleSet()->RemoveStyleSheet(attrSheetType, mAttrStyleSheet);
2077 rv = mAttrStyleSheet->Reset(aURI);
2078 } else {
2079 rv = NS_NewHTMLStyleSheet(getter_AddRefs(mAttrStyleSheet), aURI, this);
2081 NS_ENSURE_SUCCESS(rv, rv);
2083 // Don't use AddStyleSheet, since it'll put the sheet into style
2084 // sets in the document level, which is not desirable here.
2085 mAttrStyleSheet->SetOwningDocument(this);
2087 if (mStyleAttrStyleSheet) {
2088 // Remove this sheet from all style sets
2089 nsPresShellIterator iter(this);
2090 nsCOMPtr<nsIPresShell> shell;
2091 while ((shell = iter.GetNextShell())) {
2092 shell->StyleSet()->
2093 RemoveStyleSheet(nsStyleSet::eStyleAttrSheet, mStyleAttrStyleSheet);
2095 rv = mStyleAttrStyleSheet->Reset(aURI);
2096 } else {
2097 rv = NS_NewHTMLCSSStyleSheet(getter_AddRefs(mStyleAttrStyleSheet), aURI,
2098 this);
2100 NS_ENSURE_SUCCESS(rv, rv);
2102 // The loop over style sets below will handle putting this sheet
2103 // into style sets as needed.
2104 mStyleAttrStyleSheet->SetOwningDocument(this);
2106 // Now set up our style sets
2107 nsPresShellIterator iter(this);
2108 nsCOMPtr<nsIPresShell> shell;
2109 while ((shell = iter.GetNextShell())) {
2110 FillStyleSet(shell->StyleSet());
2113 return rv;
2116 nsStyleSet::sheetType
2117 nsDocument::GetAttrSheetType()
2119 return nsStyleSet::ePresHintSheet;
2122 void
2123 nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
2125 NS_PRECONDITION(aStyleSet, "Must have a style set");
2126 NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::ePresHintSheet) == 0,
2127 "Style set already has a preshint sheet?");
2128 NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eHTMLPresHintSheet) == 0,
2129 "Style set already has a HTML preshint sheet?");
2130 NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0,
2131 "Style set already has document sheets?");
2132 NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eStyleAttrSheet) == 0,
2133 "Style set already has style attr sheets?");
2134 NS_PRECONDITION(mStyleAttrStyleSheet, "No style attr stylesheet?");
2135 NS_PRECONDITION(mAttrStyleSheet, "No attr stylesheet?");
2137 aStyleSet->AppendStyleSheet(GetAttrSheetType(), mAttrStyleSheet);
2139 aStyleSet->AppendStyleSheet(nsStyleSet::eStyleAttrSheet,
2140 mStyleAttrStyleSheet);
2142 PRInt32 i;
2143 for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
2144 nsIStyleSheet* sheet = mStyleSheets[i];
2145 PRBool sheetApplicable;
2146 sheet->GetApplicable(sheetApplicable);
2147 if (sheetApplicable) {
2148 aStyleSet->AddDocStyleSheet(sheet, this);
2152 for (i = mCatalogSheets.Count() - 1; i >= 0; --i) {
2153 nsIStyleSheet* sheet = mCatalogSheets[i];
2154 PRBool sheetApplicable;
2155 sheet->GetApplicable(sheetApplicable);
2156 if (sheetApplicable) {
2157 aStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
2162 nsresult
2163 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
2164 nsILoadGroup* aLoadGroup,
2165 nsISupports* aContainer,
2166 nsIStreamListener **aDocListener,
2167 PRBool aReset, nsIContentSink* aSink)
2169 #ifdef PR_LOGGING
2170 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
2171 nsCOMPtr<nsIURI> uri;
2172 aChannel->GetURI(getter_AddRefs(uri));
2173 nsCAutoString spec;
2174 if (uri)
2175 uri->GetSpec(spec);
2176 PR_LogPrint("DOCUMENT %p StartDocumentLoad %s", this, spec.get());
2178 #endif
2180 if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
2181 mLoadedAsData = PR_TRUE;
2182 // We need to disable script & style loading in this case.
2183 // We leave them disabled even in EndLoad(), and let anyone
2184 // who puts the document on display to worry about enabling.
2186 // Do not load/process scripts when loading as data
2187 ScriptLoader()->SetEnabled(PR_FALSE);
2189 // styles
2190 CSSLoader()->SetEnabled(PR_FALSE); // Do not load/process styles when loading as data
2191 } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
2192 // Allow CSS, but not scripts
2193 ScriptLoader()->SetEnabled(PR_FALSE);
2196 mMayStartLayout = PR_FALSE;
2198 mHaveInputEncoding = PR_TRUE;
2200 if (aReset) {
2201 Reset(aChannel, aLoadGroup);
2204 nsCAutoString contentType;
2205 if (NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
2206 // XXX this is only necessary for viewsource:
2207 nsACString::const_iterator start, end, semicolon;
2208 contentType.BeginReading(start);
2209 contentType.EndReading(end);
2210 semicolon = start;
2211 FindCharInReadable(';', semicolon, end);
2212 mContentType = Substring(start, semicolon);
2215 RetrieveRelevantHeaders(aChannel);
2217 mChannel = aChannel;
2219 return NS_OK;
2222 void
2223 nsDocument::StopDocumentLoad()
2225 if (mParser) {
2226 mParser->Terminate();
2230 void
2231 nsDocument::SetDocumentURI(nsIURI* aURI)
2233 mDocumentURI = NS_TryToMakeImmutable(aURI);
2236 NS_IMETHODIMP
2237 nsDocument::GetLastModified(nsAString& aLastModified)
2239 if (!mLastModified.IsEmpty()) {
2240 aLastModified.Assign(mLastModified);
2241 } else {
2242 // If we for whatever reason failed to find the last modified time
2243 // (or even the current time), fall back to what NS4.x returned.
2244 aLastModified.Assign(NS_LITERAL_STRING("01/01/1970 00:00:00"));
2247 return NS_OK;
2250 void
2251 nsDocument::UpdateNameTableEntry(nsIContent *aContent)
2253 if (!mIsRegularHTML)
2254 return;
2256 nsIAtom* name = nsContentUtils::IsNamedItem(aContent);
2257 if (!name)
2258 return;
2260 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(name);
2261 if (!entry) {
2262 // We're not tracking the elements with this name
2263 return;
2266 entry->AddNameContent(aContent);
2269 void
2270 nsDocument::RemoveFromNameTable(nsIContent *aContent)
2272 if (!mIsRegularHTML)
2273 return;
2275 nsIAtom* name = nsContentUtils::IsNamedItem(aContent);
2276 if (!name)
2277 return;
2279 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(name);
2280 if (!entry) {
2281 // We're not tracking the elements with this name
2282 return;
2285 entry->RemoveNameContent(aContent);
2288 void
2289 nsDocument::UpdateIdTableEntry(nsIContent *aContent)
2291 nsIAtom* id = aContent->GetID();
2292 if (!id)
2293 return;
2295 PRBool liveTable = IdTableIsLive();
2296 nsIdentifierMapEntry *entry =
2297 liveTable ? mIdentifierMap.PutEntry(id) : mIdentifierMap.GetEntry(id);
2299 if (entry) {
2300 entry->AddIdContent(aContent);
2304 void
2305 nsDocument::RemoveFromIdTable(nsIContent *aContent)
2307 nsIAtom* id = aContent->GetID();
2308 if (!id)
2309 return;
2311 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
2312 if (!entry)
2313 return;
2315 if (entry->RemoveIdContent(aContent)) {
2316 mIdentifierMap.RemoveEntry(id);
2320 void
2321 nsDocument::UnregisterNamedItems(nsIContent *aContent)
2323 if (aContent->IsNodeOfType(nsINode::eTEXT)) {
2324 // Text nodes are not named items nor can they have children.
2325 return;
2328 RemoveFromNameTable(aContent);
2329 RemoveFromIdTable(aContent);
2331 PRUint32 i, count = aContent->GetChildCount();
2332 for (i = 0; i < count; ++i) {
2333 UnregisterNamedItems(aContent->GetChildAt(i));
2337 void
2338 nsDocument::RegisterNamedItems(nsIContent *aContent)
2340 if (aContent->IsNodeOfType(nsINode::eTEXT)) {
2341 // Text nodes are not named items nor can they have children.
2342 return;
2345 UpdateNameTableEntry(aContent);
2346 UpdateIdTableEntry(aContent);
2348 PRUint32 i, count = aContent->GetChildCount();
2349 for (i = 0; i < count; ++i) {
2350 RegisterNamedItems(aContent->GetChildAt(i));
2354 void
2355 nsDocument::ContentAppended(nsIDocument* aDocument,
2356 nsIContent* aContainer,
2357 PRInt32 aNewIndexInContainer)
2359 NS_ASSERTION(aDocument == this, "unexpected doc");
2361 PRUint32 count = aContainer->GetChildCount();
2362 for (PRUint32 i = aNewIndexInContainer; i < count; ++i) {
2363 RegisterNamedItems(aContainer->GetChildAt(i));
2367 void
2368 nsDocument::ContentInserted(nsIDocument* aDocument,
2369 nsIContent* aContainer,
2370 nsIContent* aContent,
2371 PRInt32 aIndexInContainer)
2373 NS_ASSERTION(aDocument == this, "unexpected doc");
2375 NS_ABORT_IF_FALSE(aContent, "Null content!");
2377 RegisterNamedItems(aContent);
2380 void
2381 nsDocument::ContentRemoved(nsIDocument* aDocument,
2382 nsIContent* aContainer,
2383 nsIContent* aChild,
2384 PRInt32 aIndexInContainer)
2386 NS_ASSERTION(aDocument == this, "unexpected doc");
2388 NS_ABORT_IF_FALSE(aChild, "Null content!");
2390 UnregisterNamedItems(aChild);
2393 void
2394 nsDocument::AttributeWillChange(nsIContent* aContent, PRInt32 aNameSpaceID,
2395 nsIAtom* aAttribute)
2397 NS_ABORT_IF_FALSE(aContent, "Null content!");
2398 NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
2400 if (aNameSpaceID != kNameSpaceID_None)
2401 return;
2402 if (aAttribute == nsGkAtoms::name) {
2403 RemoveFromNameTable(aContent);
2404 } else if (aAttribute == aContent->GetIDAttributeName()) {
2405 RemoveFromIdTable(aContent);
2409 void
2410 nsDocument::AttributeChanged(nsIDocument* aDocument,
2411 nsIContent* aContent, PRInt32 aNameSpaceID,
2412 nsIAtom* aAttribute, PRInt32 aModType,
2413 PRUint32 aStateMask)
2415 NS_ASSERTION(aDocument == this, "unexpected doc");
2417 NS_ABORT_IF_FALSE(aContent, "Null content!");
2418 NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
2420 if (aNameSpaceID != kNameSpaceID_None)
2421 return;
2422 if (aAttribute == nsGkAtoms::name) {
2423 UpdateNameTableEntry(aContent);
2424 } else if (aAttribute == aContent->GetIDAttributeName()) {
2425 UpdateIdTableEntry(aContent);
2429 nsIPrincipal*
2430 nsDocument::GetPrincipal()
2432 return NodePrincipal();
2435 void
2436 nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
2438 mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
2441 NS_IMETHODIMP
2442 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
2444 NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
2446 return NS_OK;
2449 NS_IMETHODIMP
2450 nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
2452 mApplicationCache = aApplicationCache;
2454 return NS_OK;
2457 NS_IMETHODIMP
2458 nsDocument::GetContentType(nsAString& aContentType)
2460 CopyUTF8toUTF16(mContentType, aContentType);
2462 return NS_OK;
2465 void
2466 nsDocument::SetContentType(const nsAString& aContentType)
2468 NS_ASSERTION(mContentType.IsEmpty() ||
2469 mContentType.Equals(NS_ConvertUTF16toUTF8(aContentType)),
2470 "Do you really want to change the content-type?");
2472 CopyUTF16toUTF8(aContentType, mContentType);
2475 /* Return true if the document is in the focused top-level window, and is an
2476 * ancestor of the focused DOMWindow. */
2477 NS_IMETHODIMP
2478 nsDocument::HasFocus(PRBool* aResult)
2480 *aResult = PR_FALSE;
2482 nsPIDOMWindow* window = GetWindow();
2483 nsIFocusController* focusController = window ?
2484 window->GetRootFocusController() : nsnull;
2485 if (!focusController) {
2486 return NS_OK;
2489 // Does the top-level window have focus?
2490 PRBool active;
2491 nsresult rv = focusController->GetActive(&active);
2492 NS_ENSURE_SUCCESS(rv, rv);
2493 if (!active){
2494 return NS_OK;
2497 // Is there a focused DOMWindow?
2498 nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
2499 rv = focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
2500 NS_ENSURE_SUCCESS(rv, rv);
2501 if (!focusedWindow) {
2502 return NS_ERROR_FAILURE;
2505 // Are we an ancestor of the focused DOMWindow?
2506 nsCOMPtr<nsIDOMDocument> domDocument;
2507 focusedWindow->GetDocument(getter_AddRefs(domDocument));
2508 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
2510 for (nsIDocument* currentDoc = document; currentDoc;
2511 currentDoc = currentDoc->GetParentDocument()) {
2512 if (currentDoc == this) {
2513 // Yes, we are an ancestor
2514 *aResult = PR_TRUE;
2515 return NS_OK;
2519 return NS_OK;
2522 NS_IMETHODIMP
2523 nsDocument::GetReferrer(nsAString& aReferrer)
2525 CopyUTF8toUTF16(mReferrer, aReferrer);
2526 return NS_OK;
2529 NS_IMETHODIMP
2530 nsDocument::GetActiveElement(nsIDOMElement **aElement)
2532 *aElement = nsnull;
2534 // Get the focused element.
2535 nsPIDOMWindow* window = GetWindow();
2536 if (!window) {
2537 return NS_ERROR_NOT_AVAILABLE;
2540 nsIFocusController* focusController = window->GetRootFocusController();
2541 if (!focusController) {
2542 return NS_ERROR_FAILURE;
2545 nsCOMPtr<nsIDOMElement> focusedElement;
2546 focusController->GetFocusedElement(getter_AddRefs(focusedElement));
2547 nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement);
2548 if (content) {
2549 // Found a focused element. See if it's in this document.
2550 nsIDocument* currentDoc = content->GetCurrentDoc();
2551 if (currentDoc == this) {
2552 focusedElement.swap(*aElement);
2553 return NS_OK;
2556 // Not in this document. If it's in a child document, return the iframe in
2557 // this document that's an ancestor of the child.
2558 if (currentDoc) {
2559 *aElement = CheckAncestryAndGetFrame(currentDoc).get();
2560 if (*aElement) {
2561 return NS_OK;
2566 // Couldn't find a focused element. Check if something like an IFRAME is
2567 // focused, which will give us a focused window rather than a focused
2568 // element.
2569 nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
2570 focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
2571 if (focusedWindow) {
2572 // Found a focused window. See if it's in a child of this document. (If
2573 // the window's document is this, then we should just fall through to
2574 // returning the BODY below).
2575 nsCOMPtr<nsIDOMDocument> domDocument;
2576 focusedWindow->GetDocument(getter_AddRefs(domDocument));
2577 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
2579 if (document && (document != this)) {
2580 *aElement = CheckAncestryAndGetFrame(document).get();
2581 if (*aElement) {
2582 return NS_OK;
2587 // No focused element anywhere in this document. Try to get the BODY.
2588 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc =
2589 do_QueryInterface(static_cast<nsIDocument*>(this));
2590 if (htmlDoc) {
2591 nsCOMPtr<nsIDOMHTMLElement> bodyElement;
2592 htmlDoc->GetBody(getter_AddRefs(bodyElement));
2593 if (bodyElement) {
2594 *aElement = bodyElement;
2595 NS_ADDREF(*aElement);
2597 // Because of IE compatibility, return null when html document doesn't have
2598 // a body.
2599 return NS_OK;
2602 // If we couldn't get a BODY, return the root element.
2603 return GetDocumentElement(aElement);
2606 NS_IMETHODIMP
2607 nsDocument::ElementFromPoint(PRInt32 aX, PRInt32 aY, nsIDOMElement** aReturn)
2609 return ElementFromPointHelper(aX, aY, PR_FALSE, PR_TRUE, aReturn);
2612 nsresult
2613 nsDocument::ElementFromPointHelper(PRInt32 aX, PRInt32 aY,
2614 PRBool aIgnoreRootScrollFrame,
2615 PRBool aFlushLayout,
2616 nsIDOMElement** aReturn)
2618 NS_ENSURE_ARG_POINTER(aReturn);
2619 *aReturn = nsnull;
2620 // As per the the spec, we return null if either coord is negative
2621 if (aX < 0 || aY < 0)
2622 return NS_OK;
2624 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
2625 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
2626 nsPoint pt(x, y);
2628 // Make sure the layout information we get is up-to-date, and
2629 // ensure we get a root frame (for everything but XUL)
2630 if (aFlushLayout)
2631 FlushPendingNotifications(Flush_Layout);
2633 nsIPresShell *ps = GetPrimaryShell();
2634 NS_ENSURE_STATE(ps);
2635 nsIFrame *rootFrame = ps->GetRootFrame();
2637 // XUL docs, unlike HTML, have no frame tree until everything's done loading
2638 if (!rootFrame)
2639 return NS_OK; // return null to premature XUL callers as a reminder to wait
2641 nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, PR_TRUE,
2642 aIgnoreRootScrollFrame);
2643 if (!ptFrame)
2644 return NS_OK;
2646 nsIContent* ptContent = ptFrame->GetContent();
2647 NS_ENSURE_STATE(ptContent);
2649 // If the content is in a subdocument, try to get the element from |this| doc
2650 nsIDocument *currentDoc = ptContent->GetCurrentDoc();
2651 if (currentDoc && (currentDoc != this)) {
2652 *aReturn = CheckAncestryAndGetFrame(currentDoc).get();
2653 return NS_OK;
2656 // If we have an anonymous element (such as an internal div from a textbox),
2657 // or a node that isn't an element (such as a text frame node),
2658 // replace it with the first non-anonymous parent node of type element.
2659 while (ptContent &&
2660 !ptContent->IsNodeOfType(nsINode::eELEMENT) ||
2661 ptContent->IsInAnonymousSubtree()) {
2662 // XXXldb: Faster to jump to GetBindingParent if non-null?
2663 ptContent = ptContent->GetParent();
2666 if (ptContent)
2667 CallQueryInterface(ptContent, aReturn);
2668 return NS_OK;
2671 NS_IMETHODIMP
2672 nsDocument::GetElementsByClassName(const nsAString& aClasses,
2673 nsIDOMNodeList** aReturn)
2675 return GetElementsByClassNameHelper(this, aClasses, aReturn);
2679 // static GetElementsByClassName helpers
2680 nsresult
2681 nsDocument::GetElementsByClassNameHelper(nsINode* aRootNode,
2682 const nsAString& aClasses,
2683 nsIDOMNodeList** aReturn)
2685 NS_PRECONDITION(aRootNode, "Must have root node");
2687 nsAttrValue attrValue;
2688 attrValue.ParseAtomArray(aClasses);
2689 // nsAttrValue::Equals is sensitive to order, so we'll send an array
2690 nsCOMArray<nsIAtom>* classes = new nsCOMArray<nsIAtom>;
2691 NS_ENSURE_TRUE(classes, NS_ERROR_OUT_OF_MEMORY);
2693 if (attrValue.Type() == nsAttrValue::eAtomArray) {
2694 classes->AppendObjects(*(attrValue.GetAtomArrayValue()));
2695 } else if (attrValue.Type() == nsAttrValue::eAtom) {
2696 classes->AppendObject(attrValue.GetAtomValue());
2699 nsBaseContentList* elements;
2700 if (classes->Count() > 0) {
2701 elements = new nsContentList(aRootNode, MatchClassNames,
2702 DestroyClassNameArray, classes);
2703 } else {
2704 delete classes;
2705 classes = nsnull;
2706 elements = new nsBaseContentList();
2708 if (!elements) {
2709 delete classes;
2710 return NS_ERROR_OUT_OF_MEMORY;
2713 *aReturn = elements;
2714 NS_ADDREF(*aReturn);
2716 return NS_OK;
2719 // static
2720 PRBool
2721 nsDocument::MatchClassNames(nsIContent* aContent,
2722 PRInt32 aNamespaceID,
2723 nsIAtom* aAtom, void* aData)
2725 // We can't match if there are no class names
2726 const nsAttrValue* classAttr = aContent->GetClasses();
2727 if (!classAttr) {
2728 return PR_FALSE;
2731 // need to match *all* of the classes
2732 nsCOMArray<nsIAtom>* classes = static_cast<nsCOMArray<nsIAtom>*>(aData);
2733 PRInt32 length = classes->Count();
2734 PRInt32 i;
2735 for (i = 0; i < length; ++i) {
2736 if (!classAttr->Contains(classes->ObjectAt(i), eCaseMatters)) {
2737 return PR_FALSE;
2741 return PR_TRUE;
2744 // static
2745 void
2746 nsDocument::DestroyClassNameArray(void* aData)
2748 nsCOMArray<nsIAtom>* classes = static_cast<nsCOMArray<nsIAtom>*>(aData);
2749 delete classes;
2752 nsresult
2753 nsDocument::SetBaseURI(nsIURI* aURI)
2755 nsresult rv = NS_OK;
2757 if (aURI) {
2758 rv = nsContentUtils::GetSecurityManager()->
2759 CheckLoadURIWithPrincipal(NodePrincipal(), aURI,
2760 nsIScriptSecurityManager::STANDARD);
2761 if (NS_SUCCEEDED(rv)) {
2762 mDocumentBaseURI = NS_TryToMakeImmutable(aURI);
2764 } else {
2765 mDocumentBaseURI = nsnull;
2768 return rv;
2771 void
2772 nsDocument::GetBaseTarget(nsAString &aBaseTarget) const
2774 aBaseTarget.Assign(mBaseTarget);
2777 void
2778 nsDocument::SetBaseTarget(const nsAString &aBaseTarget)
2780 mBaseTarget.Assign(aBaseTarget);
2783 void
2784 nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
2786 if (!mCharacterSet.Equals(aCharSetID)) {
2787 mCharacterSet = aCharSetID;
2789 #ifdef DEBUG
2790 nsCOMPtr<nsICharsetAlias> calias(do_GetService(NS_CHARSETALIAS_CONTRACTID));
2791 if (calias) {
2792 nsCAutoString canonicalName;
2793 calias->GetPreferred(aCharSetID, canonicalName);
2794 NS_ASSERTION(canonicalName.Equals(aCharSetID),
2795 "charset name must be canonical");
2797 #endif
2799 PRInt32 n = mCharSetObservers.Count();
2801 for (PRInt32 i = 0; i < n; i++) {
2802 nsIObserver* observer =
2803 static_cast<nsIObserver *>(mCharSetObservers.ElementAt(i));
2805 observer->Observe(static_cast<nsIDocument *>(this), "charset",
2806 NS_ConvertASCIItoUTF16(aCharSetID).get());
2811 nsresult
2812 nsDocument::AddCharSetObserver(nsIObserver* aObserver)
2814 NS_ENSURE_ARG_POINTER(aObserver);
2816 NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE);
2818 return NS_OK;
2821 void
2822 nsDocument::RemoveCharSetObserver(nsIObserver* aObserver)
2824 mCharSetObservers.RemoveElement(aObserver);
2827 void
2828 nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const
2830 aData.Truncate();
2831 const nsDocHeaderData* data = mHeaderData;
2832 while (data) {
2833 if (data->mField == aHeaderField) {
2834 aData = data->mData;
2836 break;
2838 data = data->mNext;
2842 void
2843 nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData)
2845 if (!aHeaderField) {
2846 NS_ERROR("null headerField");
2847 return;
2850 if (!mHeaderData) {
2851 if (!aData.IsEmpty()) { // don't bother storing empty string
2852 mHeaderData = new nsDocHeaderData(aHeaderField, aData);
2855 else {
2856 nsDocHeaderData* data = mHeaderData;
2857 nsDocHeaderData** lastPtr = &mHeaderData;
2858 PRBool found = PR_FALSE;
2859 do { // look for existing and replace
2860 if (data->mField == aHeaderField) {
2861 if (!aData.IsEmpty()) {
2862 data->mData.Assign(aData);
2864 else { // don't store empty string
2865 *lastPtr = data->mNext;
2866 data->mNext = nsnull;
2867 delete data;
2869 found = PR_TRUE;
2871 break;
2873 lastPtr = &(data->mNext);
2874 data = *lastPtr;
2875 } while (data);
2877 if (!aData.IsEmpty() && !found) {
2878 // didn't find, append
2879 *lastPtr = new nsDocHeaderData(aHeaderField, aData);
2883 if (aHeaderField == nsGkAtoms::headerContentLanguage) {
2884 CopyUTF16toUTF8(aData, mContentLanguage);
2887 // Set the default script-type on the root element.
2888 if (aHeaderField == nsGkAtoms::headerContentScriptType) {
2889 nsIContent *root = GetRootContent();
2890 if (root) {
2891 // Get the script-type ID for this value.
2892 nsresult rv;
2893 nsCOMPtr<nsIScriptRuntime> runtime;
2894 rv = NS_GetScriptRuntime(aData, getter_AddRefs(runtime));
2895 if (NS_FAILED(rv) || runtime == nsnull) {
2896 NS_WARNING("The script-type is unknown");
2897 } else {
2898 root->SetScriptTypeID(runtime->GetScriptTypeID());
2903 if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
2904 // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
2905 // spec.
2906 if (DOMStringIsNull(mLastStyleSheetSet)) {
2907 // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
2908 // per spec. The idea here is that we're changing our preferred set and
2909 // that shouldn't change the value of lastStyleSheetSet. Also, we're
2910 // using the Internal version so we can update the CSSLoader and not have
2911 // to worry about null strings.
2912 EnableStyleSheetsForSetInternal(aData, PR_TRUE);
2916 if (aHeaderField == nsGkAtoms::refresh) {
2917 // We get into this code before we have a script global yet, so get to
2918 // our container via mDocumentContainer.
2919 nsCOMPtr<nsIRefreshURI> refresher = do_QueryReferent(mDocumentContainer);
2920 if (refresher) {
2921 // Note: using mDocumentURI instead of mBaseURI here, for consistency
2922 // (used to just use the current URI of our webnavigation, but that
2923 // should really be the same thing). Note that this code can run
2924 // before the current URI of the webnavigation has been updated, so we
2925 // can't assert equality here.
2926 refresher->SetupRefreshURIFromHeader(mDocumentURI,
2927 NS_ConvertUTF16toUTF8(aData));
2932 PRBool
2933 nsDocument::TryChannelCharset(nsIChannel *aChannel,
2934 PRInt32& aCharsetSource,
2935 nsACString& aCharset)
2937 if(kCharsetFromChannel <= aCharsetSource) {
2938 return PR_TRUE;
2941 if (aChannel) {
2942 nsCAutoString charsetVal;
2943 nsresult rv = aChannel->GetContentCharset(charsetVal);
2944 if (NS_SUCCEEDED(rv)) {
2945 nsCOMPtr<nsICharsetAlias> calias(do_GetService(NS_CHARSETALIAS_CONTRACTID));
2946 if (calias) {
2947 nsCAutoString preferred;
2948 rv = calias->GetPreferred(charsetVal,
2949 preferred);
2950 if(NS_SUCCEEDED(rv)) {
2951 aCharset = preferred;
2952 aCharsetSource = kCharsetFromChannel;
2953 return PR_TRUE;
2958 return PR_FALSE;
2961 nsresult
2962 nsDocument::CreateShell(nsPresContext* aContext, nsIViewManager* aViewManager,
2963 nsStyleSet* aStyleSet,
2964 nsIPresShell** aInstancePtrResult)
2966 // Don't add anything here. Add it to |doCreateShell| instead.
2967 // This exists so that subclasses can pass other values for the 4th
2968 // parameter some of the time.
2969 return doCreateShell(aContext, aViewManager, aStyleSet,
2970 eCompatibility_FullStandards, aInstancePtrResult);
2973 nsresult
2974 nsDocument::doCreateShell(nsPresContext* aContext,
2975 nsIViewManager* aViewManager, nsStyleSet* aStyleSet,
2976 nsCompatibility aCompatMode,
2977 nsIPresShell** aInstancePtrResult)
2979 *aInstancePtrResult = nsnull;
2981 NS_ENSURE_FALSE(mShellsAreHidden, NS_ERROR_FAILURE);
2983 FillStyleSet(aStyleSet);
2985 nsCOMPtr<nsIPresShell> shell;
2986 nsresult rv = NS_NewPresShell(getter_AddRefs(shell));
2987 if (NS_FAILED(rv)) {
2988 return rv;
2991 rv = shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
2992 NS_ENSURE_SUCCESS(rv, rv);
2994 // Note: we don't hold a ref to the shell (it holds a ref to us)
2995 NS_ENSURE_TRUE(mPresShells.AppendElementUnlessExists(shell),
2996 NS_ERROR_OUT_OF_MEMORY);
2997 shell.swap(*aInstancePtrResult);
2999 return NS_OK;
3002 PRBool
3003 nsDocument::DeleteShell(nsIPresShell* aShell)
3005 return mPresShells.RemoveElement(aShell);
3009 nsIPresShell *
3010 nsDocument::GetPrimaryShell() const
3012 return mShellsAreHidden ? nsnull : mPresShells.SafeElementAt(0, nsnull);
3015 static void
3016 SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
3018 SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry);
3020 NS_RELEASE(e->mKey);
3021 if (e->mSubDocument) {
3022 e->mSubDocument->SetParentDocument(nsnull);
3023 NS_RELEASE(e->mSubDocument);
3027 static PRBool
3028 SubDocInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, const void *key)
3030 SubDocMapEntry *e =
3031 const_cast<SubDocMapEntry *>
3032 (static_cast<const SubDocMapEntry *>(entry));
3034 e->mKey = const_cast<nsIContent *>
3035 (static_cast<const nsIContent *>(key));
3036 NS_ADDREF(e->mKey);
3038 e->mSubDocument = nsnull;
3039 return PR_TRUE;
3042 nsresult
3043 nsDocument::SetSubDocumentFor(nsIContent *aContent, nsIDocument* aSubDoc)
3045 NS_ENSURE_TRUE(aContent, NS_ERROR_UNEXPECTED);
3047 if (!aSubDoc) {
3048 // aSubDoc is nsnull, remove the mapping
3050 if (mSubDocuments) {
3051 SubDocMapEntry *entry =
3052 static_cast<SubDocMapEntry*>
3053 (PL_DHashTableOperate(mSubDocuments, aContent,
3054 PL_DHASH_LOOKUP));
3056 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3057 PL_DHashTableRawRemove(mSubDocuments, entry);
3060 } else {
3061 if (!mSubDocuments) {
3062 // Create a new hashtable
3064 static PLDHashTableOps hash_table_ops =
3066 PL_DHashAllocTable,
3067 PL_DHashFreeTable,
3068 PL_DHashVoidPtrKeyStub,
3069 PL_DHashMatchEntryStub,
3070 PL_DHashMoveEntryStub,
3071 SubDocClearEntry,
3072 PL_DHashFinalizeStub,
3073 SubDocInitEntry
3076 mSubDocuments = PL_NewDHashTable(&hash_table_ops, nsnull,
3077 sizeof(SubDocMapEntry), 16);
3078 if (!mSubDocuments) {
3079 return NS_ERROR_OUT_OF_MEMORY;
3083 // Add a mapping to the hash table
3084 SubDocMapEntry *entry =
3085 static_cast<SubDocMapEntry*>
3086 (PL_DHashTableOperate(mSubDocuments, aContent,
3087 PL_DHASH_ADD));
3089 if (!entry) {
3090 return NS_ERROR_OUT_OF_MEMORY;
3093 if (entry->mSubDocument) {
3094 entry->mSubDocument->SetParentDocument(nsnull);
3096 // Release the old sub document
3097 NS_RELEASE(entry->mSubDocument);
3100 entry->mSubDocument = aSubDoc;
3101 NS_ADDREF(entry->mSubDocument);
3103 aSubDoc->SetParentDocument(this);
3106 return NS_OK;
3109 nsIDocument*
3110 nsDocument::GetSubDocumentFor(nsIContent *aContent) const
3112 if (mSubDocuments) {
3113 SubDocMapEntry *entry =
3114 static_cast<SubDocMapEntry*>
3115 (PL_DHashTableOperate(mSubDocuments, aContent,
3116 PL_DHASH_LOOKUP));
3118 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3119 return entry->mSubDocument;
3123 return nsnull;
3126 static PLDHashOperator
3127 FindContentEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
3128 PRUint32 number, void *arg)
3130 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
3131 FindContentData *data = static_cast<FindContentData*>(arg);
3133 if (entry->mSubDocument == data->mSubDocument) {
3134 data->mResult = entry->mKey;
3136 return PL_DHASH_STOP;
3139 return PL_DHASH_NEXT;
3142 nsIContent*
3143 nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const
3145 NS_ENSURE_TRUE(aDocument, nsnull);
3147 if (!mSubDocuments) {
3148 return nsnull;
3151 FindContentData data(aDocument);
3152 PL_DHashTableEnumerate(mSubDocuments, FindContentEnumerator, &data);
3154 return data.mResult;
3157 PRBool
3158 nsDocument::IsNodeOfType(PRUint32 aFlags) const
3160 return !(aFlags & ~eDOCUMENT);
3163 nsIContent*
3164 nsDocument::GetRootContentInternal() const
3166 // Loop backwards because any non-elements, such as doctypes and PIs
3167 // are likely to appear before the root element.
3168 PRUint32 i;
3169 for (i = mChildren.ChildCount(); i > 0; --i) {
3170 nsIContent* child = mChildren.ChildAt(i - 1);
3171 if (child->IsNodeOfType(nsINode::eELEMENT)) {
3172 const_cast<nsDocument*>(this)->mCachedRootContent = child;
3173 return child;
3177 const_cast<nsDocument*>(this)->mCachedRootContent = nsnull;
3178 return nsnull;
3181 nsIContent *
3182 nsDocument::GetChildAt(PRUint32 aIndex) const
3184 return mChildren.GetSafeChildAt(aIndex);
3187 PRInt32
3188 nsDocument::IndexOf(nsINode* aPossibleChild) const
3190 return mChildren.IndexOfChild(aPossibleChild);
3193 PRUint32
3194 nsDocument::GetChildCount() const
3196 return mChildren.ChildCount();
3199 nsIContent * const *
3200 nsDocument::GetChildArray(PRUint32* aChildCount) const
3202 return mChildren.GetChildArray(aChildCount);
3206 nsresult
3207 nsDocument::InsertChildAt(nsIContent* aKid, PRUint32 aIndex,
3208 PRBool aNotify)
3210 if (aKid->IsNodeOfType(nsINode::eELEMENT) &&
3211 GetRootContent()) {
3212 NS_ERROR("Inserting element child when we already have one");
3213 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
3216 return nsGenericElement::doInsertChildAt(aKid, aIndex, aNotify,
3217 nsnull, this, mChildren);
3220 nsresult
3221 nsDocument::AppendChildTo(nsIContent* aKid, PRBool aNotify)
3223 // Make sure to _not_ call the subclass InsertChildAt here. If
3224 // subclasses wanted to hook into this stuff, they would have
3225 // overridden AppendChildTo.
3226 // XXXbz maybe this should just be a non-virtual method on nsINode?
3227 // Feels that way to me...
3228 return nsDocument::InsertChildAt(aKid, GetChildCount(), aNotify);
3231 nsresult
3232 nsDocument::RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
3234 nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex);
3235 if (!oldKid) {
3236 return NS_OK;
3239 if (oldKid->IsNodeOfType(nsINode::eELEMENT)) {
3240 // Destroy the link map up front before we mess with the child list.
3241 DestroyLinkMap();
3244 nsresult rv = nsGenericElement::doRemoveChildAt(aIndex, aNotify, oldKid,
3245 nsnull, this, mChildren);
3246 mCachedRootContent = nsnull;
3247 return rv;
3250 PRInt32
3251 nsDocument::GetNumberOfStyleSheets() const
3253 return mStyleSheets.Count();
3256 nsIStyleSheet*
3257 nsDocument::GetStyleSheetAt(PRInt32 aIndex) const
3259 NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nsnull);
3260 return mStyleSheets[aIndex];
3263 PRInt32
3264 nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const
3266 return mStyleSheets.IndexOf(aSheet);
3269 void
3270 nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet)
3272 nsPresShellIterator iter(this);
3273 nsCOMPtr<nsIPresShell> shell;
3274 while ((shell = iter.GetNextShell())) {
3275 shell->StyleSet()->AddDocStyleSheet(aSheet, this);
3279 void
3280 nsDocument::AddStyleSheet(nsIStyleSheet* aSheet)
3282 NS_PRECONDITION(aSheet, "null arg");
3283 mStyleSheets.AppendObject(aSheet);
3284 aSheet->SetOwningDocument(this);
3286 PRBool applicable;
3287 aSheet->GetApplicable(applicable);
3289 if (applicable) {
3290 AddStyleSheetToStyleSets(aSheet);
3293 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, PR_TRUE));
3296 void
3297 nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet)
3299 nsPresShellIterator iter(this);
3300 nsCOMPtr<nsIPresShell> shell;
3301 while ((shell = iter.GetNextShell())) {
3302 shell->StyleSet()->RemoveStyleSheet(nsStyleSet::eDocSheet, aSheet);
3306 void
3307 nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet)
3309 NS_PRECONDITION(aSheet, "null arg");
3310 nsCOMPtr<nsIStyleSheet> sheet = aSheet; // hold ref so it won't die too soon
3312 if (!mStyleSheets.RemoveObject(aSheet)) {
3313 NS_NOTREACHED("stylesheet not found");
3314 return;
3317 if (!mIsGoingAway) {
3318 PRBool applicable = PR_TRUE;
3319 aSheet->GetApplicable(applicable);
3320 if (applicable) {
3321 RemoveStyleSheetFromStyleSets(aSheet);
3324 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, PR_TRUE));
3327 aSheet->SetOwningDocument(nsnull);
3330 void
3331 nsDocument::UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets,
3332 nsCOMArray<nsIStyleSheet>& aNewSheets)
3334 BeginUpdate(UPDATE_STYLE);
3336 // XXX Need to set the sheet on the ownernode, if any
3337 NS_PRECONDITION(aOldSheets.Count() == aNewSheets.Count(),
3338 "The lists must be the same length!");
3339 PRInt32 count = aOldSheets.Count();
3341 nsCOMPtr<nsIStyleSheet> oldSheet;
3342 PRInt32 i;
3343 for (i = 0; i < count; ++i) {
3344 oldSheet = aOldSheets[i];
3346 // First remove the old sheet.
3347 NS_ASSERTION(oldSheet, "None of the old sheets should be null");
3348 PRInt32 oldIndex = mStyleSheets.IndexOf(oldSheet);
3349 RemoveStyleSheet(oldSheet); // This does the right notifications
3351 // Now put the new one in its place. If it's null, just ignore it.
3352 nsIStyleSheet* newSheet = aNewSheets[i];
3353 if (newSheet) {
3354 mStyleSheets.InsertObjectAt(newSheet, oldIndex);
3355 newSheet->SetOwningDocument(this);
3356 PRBool applicable = PR_TRUE;
3357 newSheet->GetApplicable(applicable);
3358 if (applicable) {
3359 AddStyleSheetToStyleSets(newSheet);
3362 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, newSheet, PR_TRUE));
3366 EndUpdate(UPDATE_STYLE);
3369 void
3370 nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, PRInt32 aIndex)
3372 NS_PRECONDITION(aSheet, "null ptr");
3373 mStyleSheets.InsertObjectAt(aSheet, aIndex);
3375 aSheet->SetOwningDocument(this);
3377 PRBool applicable;
3378 aSheet->GetApplicable(applicable);
3380 if (applicable) {
3381 AddStyleSheetToStyleSets(aSheet);
3384 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, PR_TRUE));
3388 void
3389 nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
3390 PRBool aApplicable)
3392 NS_PRECONDITION(aSheet, "null arg");
3394 // If we're actually in the document style sheet list
3395 if (-1 != mStyleSheets.IndexOf(aSheet)) {
3396 if (aApplicable) {
3397 AddStyleSheetToStyleSets(aSheet);
3398 } else {
3399 RemoveStyleSheetFromStyleSets(aSheet);
3403 // We have to always notify, since this will be called for sheets
3404 // that are children of sheets in our style set, as well as some
3405 // sheets for nsHTMLEditor.
3407 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged,
3408 (this, aSheet, aApplicable));
3411 // These three functions are a lot like the implementation of the
3412 // corresponding API for regular stylesheets.
3414 PRInt32
3415 nsDocument::GetNumberOfCatalogStyleSheets() const
3417 return mCatalogSheets.Count();
3420 nsIStyleSheet*
3421 nsDocument::GetCatalogStyleSheetAt(PRInt32 aIndex) const
3423 NS_ENSURE_TRUE(0 <= aIndex && aIndex < mCatalogSheets.Count(), nsnull);
3424 return mCatalogSheets[aIndex];
3427 void
3428 nsDocument::AddCatalogStyleSheet(nsIStyleSheet* aSheet)
3430 mCatalogSheets.AppendObject(aSheet);
3431 aSheet->SetOwningDocument(this);
3433 PRBool applicable;
3434 aSheet->GetApplicable(applicable);
3436 if (applicable) {
3437 // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
3438 nsPresShellIterator iter(this);
3439 nsCOMPtr<nsIPresShell> shell;
3440 while ((shell = iter.GetNextShell())) {
3441 shell->StyleSet()->AppendStyleSheet(nsStyleSet::eAgentSheet, aSheet);
3445 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, PR_FALSE));
3448 void
3449 nsDocument::EnsureCatalogStyleSheet(const char *aStyleSheetURI)
3451 nsICSSLoader* cssLoader = CSSLoader();
3452 PRBool enabled;
3453 if (NS_SUCCEEDED(cssLoader->GetEnabled(&enabled)) && enabled) {
3454 PRInt32 sheetCount = GetNumberOfCatalogStyleSheets();
3455 for (PRInt32 i = 0; i < sheetCount; i++) {
3456 nsIStyleSheet* sheet = GetCatalogStyleSheetAt(i);
3457 NS_ASSERTION(sheet, "unexpected null stylesheet in the document");
3458 if (sheet) {
3459 nsCOMPtr<nsIURI> uri;
3460 sheet->GetSheetURI(getter_AddRefs(uri));
3461 nsCAutoString uriStr;
3462 uri->GetSpec(uriStr);
3463 if (uriStr.Equals(aStyleSheetURI))
3464 return;
3468 nsCOMPtr<nsIURI> uri;
3469 NS_NewURI(getter_AddRefs(uri), aStyleSheetURI);
3470 if (uri) {
3471 nsCOMPtr<nsICSSStyleSheet> sheet;
3472 cssLoader->LoadSheetSync(uri, PR_TRUE, getter_AddRefs(sheet));
3473 if (sheet) {
3474 BeginUpdate(UPDATE_STYLE);
3475 AddCatalogStyleSheet(sheet);
3476 EndUpdate(UPDATE_STYLE);
3482 nsIScriptGlobalObject*
3483 nsDocument::GetScriptGlobalObject() const
3485 // If we're going away, we've already released the reference to our
3486 // ScriptGlobalObject. We can, however, try to obtain it for the
3487 // caller through our docshell.
3489 // We actually need to start returning the docshell's script global
3490 // object as soon as nsDocumentViewer::Close has called
3491 // RemovedFromDocShell on us.
3492 if (mRemovedFromDocShell) {
3493 nsCOMPtr<nsIInterfaceRequestor> requestor =
3494 do_QueryReferent(mDocumentContainer);
3495 if (requestor) {
3496 nsCOMPtr<nsIScriptGlobalObject> globalObject = do_GetInterface(requestor);
3497 return globalObject;
3501 return mScriptGlobalObject;
3504 nsIScriptGlobalObject*
3505 nsDocument::GetScopeObject()
3507 nsCOMPtr<nsIScriptGlobalObject> scope(do_QueryReferent(mScopeObject));
3508 return scope;
3511 void
3512 nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
3514 #ifdef DEBUG
3516 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobalObject));
3518 NS_ASSERTION(!win || win->IsInnerWindow(),
3519 "Script global object must be an inner window!");
3521 #endif
3523 if (mScriptGlobalObject && !aScriptGlobalObject) {
3524 // We're detaching from the window. We need to grab a pointer to
3525 // our layout history state now.
3526 mLayoutHistoryState = GetLayoutHistoryState();
3528 // Also make sure to remove our onload blocker now if we haven't done it yet
3529 if (mOnloadBlockCount != 0) {
3530 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
3531 if (loadGroup) {
3532 loadGroup->RemoveRequest(mOnloadBlocker, nsnull, NS_OK);
3537 mScriptGlobalObject = aScriptGlobalObject;
3539 if (aScriptGlobalObject) {
3540 mScriptObject = nsnull;
3541 mHasHadScriptHandlingObject = PR_TRUE;
3542 // Go back to using the docshell for the layout history state
3543 mLayoutHistoryState = nsnull;
3544 mScopeObject = do_GetWeakReference(aScriptGlobalObject);
3547 // Remember the pointer to our window (or lack there of), to avoid
3548 // having to QI every time it's asked for.
3549 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobalObject);
3550 mWindow = window;
3553 nsIScriptGlobalObject*
3554 nsDocument::GetScriptHandlingObject(PRBool& aHasHadScriptHandlingObject) const
3556 aHasHadScriptHandlingObject = mHasHadScriptHandlingObject;
3557 if (mScriptGlobalObject) {
3558 return mScriptGlobalObject;
3561 nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
3562 do_QueryReferent(mScriptObject);
3563 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptHandlingObject);
3564 if (win) {
3565 NS_ASSERTION(win->IsInnerWindow(), "Should have inner window here!");
3566 nsPIDOMWindow* outer = win->GetOuterWindow();
3567 if (!outer || outer->GetCurrentInnerWindow() != win) {
3568 NS_WARNING("Wrong inner/outer window combination!");
3569 return nsnull;
3572 return scriptHandlingObject;
3574 void
3575 nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
3577 NS_ASSERTION(!mScriptGlobalObject ||
3578 mScriptGlobalObject == aScriptObject,
3579 "Wrong script object!");
3580 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aScriptObject);
3581 NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!");
3582 mScopeObject = mScriptObject = do_GetWeakReference(aScriptObject);
3583 if (aScriptObject) {
3584 mHasHadScriptHandlingObject = PR_TRUE;
3588 nsPIDOMWindow *
3589 nsDocument::GetWindow()
3591 if (mWindow) {
3592 return mWindow->GetOuterWindow();
3595 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(GetScriptGlobalObject()));
3597 if (!win) {
3598 return nsnull;
3601 return win->GetOuterWindow();
3604 nsPIDOMWindow *
3605 nsDocument::GetInnerWindow()
3607 if (!mRemovedFromDocShell) {
3608 return mWindow;
3611 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(GetScriptGlobalObject()));
3613 return win;
3616 nsScriptLoader*
3617 nsDocument::ScriptLoader()
3619 return mScriptLoader;
3622 // Note: We don't hold a reference to the document observer; we assume
3623 // that it has a live reference to the document.
3624 void
3625 nsDocument::AddObserver(nsIDocumentObserver* aObserver)
3627 // The array makes sure the observer isn't already in the list
3628 mObservers.AppendElementUnlessExists(aObserver);
3629 AddMutationObserver(aObserver);
3632 PRBool
3633 nsDocument::RemoveObserver(nsIDocumentObserver* aObserver)
3635 // If we're in the process of destroying the document (and we're
3636 // informing the observers of the destruction), don't remove the
3637 // observers from the list. This is not a big deal, since we
3638 // don't hold a live reference to the observers.
3639 if (!mInDestructor) {
3640 RemoveMutationObserver(aObserver);
3641 return mObservers.RemoveElement(aObserver);
3644 return mObservers.Contains(aObserver);
3647 void
3648 nsDocument::BeginUpdate(nsUpdateType aUpdateType)
3650 if (mUpdateNestLevel == 0) {
3651 BindingManager()->BeginOutermostUpdate();
3654 ++mUpdateNestLevel;
3655 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType));
3657 if (aUpdateType == UPDATE_CONTENT_MODEL) {
3658 nsContentUtils::AddRemovableScriptBlocker();
3660 else {
3661 nsContentUtils::AddScriptBlocker();
3665 void
3666 nsDocument::EndUpdate(nsUpdateType aUpdateType)
3668 if (aUpdateType == UPDATE_CONTENT_MODEL) {
3669 nsContentUtils::RemoveRemovableScriptBlocker();
3671 else {
3672 nsContentUtils::RemoveScriptBlocker();
3674 NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType));
3676 --mUpdateNestLevel;
3677 if (mUpdateNestLevel == 0) {
3678 // This set of updates may have created XBL bindings. Let the
3679 // binding manager know we're done.
3680 BindingManager()->EndOutermostUpdate();
3683 if (mUpdateNestLevel == 0 && !mDelayFrameLoaderInitialization) {
3684 InitializeFinalizeFrameLoaders();
3688 void
3689 nsDocument::BeginLoad()
3691 // Block onload here to prevent having to deal with blocking and
3692 // unblocking it while we know the document is loading.
3693 BlockOnload();
3695 if (mScriptLoader) {
3696 mScriptLoader->BeginDeferringScripts();
3699 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
3702 PRBool
3703 nsDocument::CheckGetElementByIdArg(const nsIAtom* aId)
3705 if (aId == nsGkAtoms::_empty) {
3706 nsContentUtils::ReportToConsole(
3707 nsContentUtils::eDOM_PROPERTIES,
3708 "EmptyGetElementByIdParam",
3709 nsnull, 0,
3710 nsnull,
3711 EmptyString(), 0, 0,
3712 nsIScriptError::warningFlag,
3713 "DOM");
3714 return PR_FALSE;
3716 return PR_TRUE;
3719 static void
3720 MatchAllElementsId(nsIContent* aContent, nsIAtom* aId, nsIdentifierMapEntry* aEntry)
3722 if (aId == aContent->GetID()) {
3723 aEntry->AddIdContent(aContent);
3726 PRUint32 i, count = aContent->GetChildCount();
3727 for (i = 0; i < count; i++) {
3728 MatchAllElementsId(aContent->GetChildAt(i), aId, aEntry);
3732 nsIdentifierMapEntry*
3733 nsDocument::GetElementByIdInternal(nsIAtom* aID)
3735 // We don't have to flush before we do the initial hashtable lookup, since if
3736 // the id is already in the hashtable it couldn't have been removed without
3737 // us being notified (all removals notify immediately, as far as I can tell).
3738 // So do the lookup first.
3739 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aID);
3740 NS_ENSURE_TRUE(entry, nsnull);
3742 if (entry->GetIdContent())
3743 return entry;
3745 // Now we have to flush. It could be that we have a cached "not in
3746 // document" or know nothing about this ID yet but more content has been
3747 // added to the document since. Note that we have to flush notifications,
3748 // so that the entry will get updated properly.
3750 // Make sure to stash away the current generation so we can check whether
3751 // the table changes when we flush.
3752 PRUint32 generation = mIdentifierMap.GetGeneration();
3754 FlushPendingNotifications(Flush_ContentAndNotify);
3756 if (generation != mIdentifierMap.GetGeneration()) {
3757 // Table changed, so the entry pointer is no longer valid; look up the
3758 // entry again, adding if necessary (the adding may be necessary in case
3759 // the flush actually deleted entries).
3760 entry = mIdentifierMap.PutEntry(aID);
3763 PRBool isNotInDocument;
3764 nsIContent *e = entry->GetIdContent(&isNotInDocument);
3765 if (e || isNotInDocument)
3766 return entry;
3768 // Status of this id is unknown, search document
3769 nsIContent* root = GetRootContent();
3770 if (!IdTableIsLive()) {
3771 if (IdTableShouldBecomeLive()) {
3772 // Just make sure our table is up to date and call this method again
3773 // to look up in the hashtable.
3774 if (root) {
3775 RegisterNamedItems(root);
3777 return GetElementByIdInternal(aID);
3780 if (root) {
3781 // No-one should have registered an ID change callback yet. We don't
3782 // want to fire one as a side-effect of getElementById! This shouldn't
3783 // happen, since if someone called AddIDTargetObserver already for
3784 // this ID, we should have filled in this entry with content or
3785 // not-in-document.
3786 NS_ASSERTION(!entry->HasContentChangeCallback(),
3787 "No callbacks should be registered while we set up this entry");
3788 MatchAllElementsId(root, aID, entry);
3789 e = entry->GetIdContent();
3793 if (!e) {
3794 #ifdef DEBUG
3795 // No reason to call MatchElementId if !IdTableIsLive, since
3796 // we'd have done just that already
3797 if (IdTableIsLive() && root && aID != nsGkAtoms::_empty) {
3798 nsIContent* eDebug =
3799 nsContentUtils::MatchElementId(root, aID);
3800 NS_ASSERTION(!eDebug,
3801 "We got null for |e| but MatchElementId found something?");
3803 #endif
3804 // There is no element with the given id in the document, cache
3805 // the fact that it's not in the document
3806 entry->FlagIDNotInDocument();
3807 return entry;
3810 return entry;
3813 NS_IMETHODIMP
3814 nsDocument::GetElementById(const nsAString& aElementId,
3815 nsIDOMElement** aReturn)
3817 NS_ENSURE_ARG_POINTER(aReturn);
3818 *aReturn = nsnull;
3820 nsCOMPtr<nsIAtom> idAtom(do_GetAtom(aElementId));
3821 NS_ENSURE_TRUE(idAtom, NS_ERROR_OUT_OF_MEMORY);
3822 if (!CheckGetElementByIdArg(idAtom))
3823 return NS_OK;
3825 nsIdentifierMapEntry *entry = GetElementByIdInternal(idAtom);
3826 NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
3828 PRBool isNotInDocument;
3829 nsIContent *e = entry->GetIdContent(&isNotInDocument);
3830 NS_ASSERTION(e || isNotInDocument, "Incomplete map entry!");
3831 if (isNotInDocument)
3832 return NS_OK;
3834 return CallQueryInterface(e, aReturn);
3837 nsIContent*
3838 nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
3839 void* aData)
3841 if (!CheckGetElementByIdArg(aID))
3842 return nsnull;
3844 nsIdentifierMapEntry *entry = GetElementByIdInternal(aID);
3845 NS_ENSURE_TRUE(entry, nsnull);
3847 entry->AddContentChangeCallback(aObserver, aData);
3848 return entry->GetIdContent();
3851 void
3852 nsDocument::RemoveIDTargetObserver(nsIAtom* aID,
3853 IDTargetObserver aObserver, void* aData)
3855 if (!CheckGetElementByIdArg(aID))
3856 return;
3858 nsIdentifierMapEntry *entry = GetElementByIdInternal(aID);
3859 if (!entry)
3860 return;
3862 entry->RemoveContentChangeCallback(aObserver, aData);
3865 void
3866 nsDocument::DispatchContentLoadedEvents()
3868 // If you add early returns from this method, make sure you're
3869 // calling UnblockOnload properly.
3871 // Fire a DOM event notifying listeners that this document has been
3872 // loaded (excluding images and other loads initiated by this
3873 // document).
3874 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
3875 NS_LITERAL_STRING("DOMContentLoaded"),
3876 PR_TRUE, PR_TRUE);
3878 // If this document is a [i]frame, fire a DOMFrameContentLoaded
3879 // event on all parent documents notifying that the HTML (excluding
3880 // other external files such as images and stylesheets) in a frame
3881 // has finished loading.
3883 // target_frame is the [i]frame element that will be used as the
3884 // target for the event. It's the [i]frame whose content is done
3885 // loading.
3886 nsCOMPtr<nsIDOMEventTarget> target_frame;
3888 if (mParentDocument) {
3889 target_frame =
3890 do_QueryInterface(mParentDocument->FindContentForSubDocument(this));
3893 if (target_frame) {
3894 nsCOMPtr<nsIDocument> parent = mParentDocument;
3895 do {
3896 nsCOMPtr<nsIDOMDocumentEvent> document_event =
3897 do_QueryInterface(parent);
3899 nsCOMPtr<nsIDOMEvent> event;
3900 nsCOMPtr<nsIPrivateDOMEvent> privateEvent;
3901 if (document_event) {
3902 document_event->CreateEvent(NS_LITERAL_STRING("Events"),
3903 getter_AddRefs(event));
3905 privateEvent = do_QueryInterface(event);
3908 if (event && privateEvent) {
3909 event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), PR_TRUE,
3910 PR_TRUE);
3912 privateEvent->SetTarget(target_frame);
3913 privateEvent->SetTrusted(PR_TRUE);
3915 // To dispatch this event we must manually call
3916 // nsEventDispatcher::Dispatch() on the ancestor document since the
3917 // target is not in the same document, so the event would never reach
3918 // the ancestor document if we used the normal event
3919 // dispatching code.
3921 nsEvent* innerEvent = privateEvent->GetInternalNSEvent();
3922 if (innerEvent) {
3923 nsEventStatus status = nsEventStatus_eIgnore;
3925 nsIPresShell *shell = parent->GetPrimaryShell();
3926 if (shell) {
3927 nsCOMPtr<nsPresContext> context = shell->GetPresContext();
3929 if (context) {
3930 nsEventDispatcher::Dispatch(parent, context, innerEvent, event,
3931 &status);
3937 parent = parent->GetParentDocument();
3938 } while (parent);
3941 if (mScriptLoader) {
3942 mScriptLoader->EndDeferringScripts();
3945 UnblockOnload(PR_TRUE);
3948 void
3949 nsDocument::EndLoad()
3951 // Drop the ref to our parser, if any, but keep hold of the sink so that we
3952 // can flush it from FlushPendingNotifications as needed. We might have to
3953 // do that to get a StartLayout() to happen.
3954 if (mParser) {
3955 mWeakSink = do_GetWeakReference(mParser->GetContentSink());
3956 mParser = nsnull;
3959 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
3961 if (!mSynchronousDOMContentLoaded) {
3962 nsRefPtr<nsIRunnable> ev =
3963 new nsRunnableMethod<nsDocument>(this,
3964 &nsDocument::DispatchContentLoadedEvents);
3965 NS_DispatchToCurrentThread(ev);
3966 } else {
3967 DispatchContentLoadedEvents();
3971 void
3972 nsDocument::ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2,
3973 PRInt32 aStateMask)
3975 NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStatesChanged,
3976 (this, aContent1, aContent2, aStateMask));
3979 void
3980 nsDocument::StyleRuleChanged(nsIStyleSheet* aStyleSheet,
3981 nsIStyleRule* aOldStyleRule,
3982 nsIStyleRule* aNewStyleRule)
3984 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
3985 (this, aStyleSheet,
3986 aOldStyleRule, aNewStyleRule));
3989 void
3990 nsDocument::StyleRuleAdded(nsIStyleSheet* aStyleSheet,
3991 nsIStyleRule* aStyleRule)
3993 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
3994 (this, aStyleSheet, aStyleRule));
3997 void
3998 nsDocument::StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
3999 nsIStyleRule* aStyleRule)
4001 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved,
4002 (this, aStyleSheet, aStyleRule));
4007 // nsIDOMDocument interface
4009 NS_IMETHODIMP
4010 nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype)
4012 NS_ENSURE_ARG_POINTER(aDoctype);
4014 *aDoctype = nsnull;
4015 PRInt32 i, count;
4016 count = mChildren.ChildCount();
4017 for (i = 0; i < count; i++) {
4018 CallQueryInterface(mChildren.ChildAt(i), aDoctype);
4020 if (*aDoctype) {
4021 return NS_OK;
4025 return NS_OK;
4028 NS_IMETHODIMP
4029 nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation)
4031 // For now, create a new implementation every time. This shouldn't
4032 // be a high bandwidth operation
4033 nsCOMPtr<nsIURI> uri;
4034 NS_NewURI(getter_AddRefs(uri), "about:blank");
4035 NS_ENSURE_TRUE(uri, NS_ERROR_OUT_OF_MEMORY);
4036 PRBool hasHadScriptObject = PR_TRUE;
4037 nsIScriptGlobalObject* scriptObject =
4038 GetScriptHandlingObject(hasHadScriptObject);
4039 NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
4040 *aImplementation = new nsDOMImplementation(scriptObject, uri, uri,
4041 NodePrincipal());
4042 if (!*aImplementation) {
4043 return NS_ERROR_OUT_OF_MEMORY;
4046 NS_ADDREF(*aImplementation);
4048 return NS_OK;
4051 NS_IMETHODIMP
4052 nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement)
4054 NS_ENSURE_ARG_POINTER(aDocumentElement);
4056 nsIContent* root = GetRootContent();
4057 if (root) {
4058 return CallQueryInterface(root, aDocumentElement);
4061 *aDocumentElement = nsnull;
4063 return NS_OK;
4066 NS_IMETHODIMP
4067 nsDocument::CreateElement(const nsAString& aTagName,
4068 nsIDOMElement** aReturn)
4070 *aReturn = nsnull;
4072 nsresult rv = nsContentUtils::CheckQName(aTagName, PR_FALSE);
4073 NS_ENSURE_SUCCESS(rv, rv);
4075 NS_ASSERTION(IsCaseSensitive(),
4076 "nsDocument::CreateElement() called on document that is not "
4077 "case sensitive. Fix caller, or fix "
4078 "nsDocument::CreateElement()!");
4080 nsCOMPtr<nsIAtom> name = do_GetAtom(aTagName);
4082 nsCOMPtr<nsIContent> content;
4083 rv = CreateElem(name, nsnull, GetDefaultNamespaceID(), PR_TRUE,
4084 getter_AddRefs(content));
4085 NS_ENSURE_SUCCESS(rv, rv);
4087 return CallQueryInterface(content, aReturn);
4090 NS_IMETHODIMP
4091 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
4092 const nsAString& aQualifiedName,
4093 nsIDOMElement** aReturn)
4095 *aReturn = nsnull;
4097 nsCOMPtr<nsINodeInfo> nodeInfo;
4098 nsresult rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
4099 aQualifiedName,
4100 mNodeInfoManager,
4101 getter_AddRefs(nodeInfo));
4102 NS_ENSURE_SUCCESS(rv, rv);
4104 nsCOMPtr<nsIContent> content;
4105 NS_NewElement(getter_AddRefs(content), nodeInfo->NamespaceID(), nodeInfo,
4106 PR_FALSE);
4107 NS_ENSURE_SUCCESS(rv, rv);
4109 return CallQueryInterface(content, aReturn);
4112 NS_IMETHODIMP
4113 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
4115 *aReturn = nsnull;
4117 nsCOMPtr<nsIContent> text;
4118 nsresult rv = NS_NewTextNode(getter_AddRefs(text), mNodeInfoManager);
4120 if (NS_SUCCEEDED(rv)) {
4121 // Don't notify; this node is still being created.
4122 text->SetText(aData, PR_FALSE);
4124 rv = CallQueryInterface(text, aReturn);
4127 return rv;
4130 NS_IMETHODIMP
4131 nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn)
4133 return NS_NewDocumentFragment(aReturn, mNodeInfoManager);
4136 NS_IMETHODIMP
4137 nsDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn)
4139 *aReturn = nsnull;
4141 // Make sure the substring "--" is not present in aData. Otherwise
4142 // we'll create a document that can't be serialized.
4143 if (FindInReadable(NS_LITERAL_STRING("--"), aData)) {
4144 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
4147 nsCOMPtr<nsIContent> comment;
4148 nsresult rv = NS_NewCommentNode(getter_AddRefs(comment), mNodeInfoManager);
4150 if (NS_SUCCEEDED(rv)) {
4151 // Don't notify; this node is still being created.
4152 comment->SetText(aData, PR_FALSE);
4154 rv = CallQueryInterface(comment, aReturn);
4157 return rv;
4160 NS_IMETHODIMP
4161 nsDocument::CreateCDATASection(const nsAString& aData,
4162 nsIDOMCDATASection** aReturn)
4164 NS_ENSURE_ARG_POINTER(aReturn);
4165 *aReturn = nsnull;
4167 if (FindInReadable(NS_LITERAL_STRING("]]>"), aData))
4168 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
4170 nsCOMPtr<nsIContent> content;
4171 nsresult rv = NS_NewXMLCDATASection(getter_AddRefs(content),
4172 mNodeInfoManager);
4174 if (NS_SUCCEEDED(rv)) {
4175 // Don't notify; this node is still being created.
4176 content->SetText(aData, PR_FALSE);
4178 rv = CallQueryInterface(content, aReturn);
4181 return rv;
4184 NS_IMETHODIMP
4185 nsDocument::CreateProcessingInstruction(const nsAString& aTarget,
4186 const nsAString& aData,
4187 nsIDOMProcessingInstruction** aReturn)
4189 *aReturn = nsnull;
4191 nsresult rv = nsContentUtils::CheckQName(aTarget, PR_FALSE);
4192 NS_ENSURE_SUCCESS(rv, rv);
4194 if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) {
4195 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
4198 nsCOMPtr<nsIContent> content;
4199 rv = NS_NewXMLProcessingInstruction(getter_AddRefs(content),
4200 mNodeInfoManager, aTarget, aData);
4201 if (NS_FAILED(rv)) {
4202 return rv;
4205 return CallQueryInterface(content, aReturn);
4208 NS_IMETHODIMP
4209 nsDocument::CreateAttribute(const nsAString& aName,
4210 nsIDOMAttr** aReturn)
4212 *aReturn = nsnull;
4213 NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_NOT_INITIALIZED);
4215 nsresult rv = nsContentUtils::CheckQName(aName, PR_FALSE);
4216 NS_ENSURE_SUCCESS(rv, rv);
4218 nsAutoString value;
4219 nsDOMAttribute* attribute;
4221 nsCOMPtr<nsINodeInfo> nodeInfo;
4222 rv = mNodeInfoManager->GetNodeInfo(aName, nsnull, kNameSpaceID_None,
4223 getter_AddRefs(nodeInfo));
4224 NS_ENSURE_SUCCESS(rv, rv);
4226 attribute = new nsDOMAttribute(nsnull, nodeInfo, value);
4227 NS_ENSURE_TRUE(attribute, NS_ERROR_OUT_OF_MEMORY);
4229 return CallQueryInterface(attribute, aReturn);
4232 NS_IMETHODIMP
4233 nsDocument::CreateAttributeNS(const nsAString & aNamespaceURI,
4234 const nsAString & aQualifiedName,
4235 nsIDOMAttr **aResult)
4237 NS_ENSURE_ARG_POINTER(aResult);
4238 *aResult = nsnull;
4240 nsCOMPtr<nsINodeInfo> nodeInfo;
4241 nsresult rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
4242 aQualifiedName,
4243 mNodeInfoManager,
4244 getter_AddRefs(nodeInfo));
4245 NS_ENSURE_SUCCESS(rv, rv);
4247 nsAutoString value;
4248 nsDOMAttribute* attribute = new nsDOMAttribute(nsnull, nodeInfo, value);
4249 NS_ENSURE_TRUE(attribute, NS_ERROR_OUT_OF_MEMORY);
4251 return CallQueryInterface(attribute, aResult);
4254 NS_IMETHODIMP
4255 nsDocument::CreateEntityReference(const nsAString& aName,
4256 nsIDOMEntityReference** aReturn)
4258 NS_ENSURE_ARG_POINTER(aReturn);
4260 *aReturn = nsnull;
4261 return NS_OK;
4264 NS_IMETHODIMP
4265 nsDocument::GetElementsByTagName(const nsAString& aTagname,
4266 nsIDOMNodeList** aReturn)
4268 nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aTagname);
4269 NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
4271 nsContentList *list = NS_GetContentList(this, nameAtom, kNameSpaceID_Unknown).get();
4272 NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
4274 // transfer ref to aReturn
4275 *aReturn = list;
4276 return NS_OK;
4279 NS_IMETHODIMP
4280 nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
4281 const nsAString& aLocalName,
4282 nsIDOMNodeList** aReturn)
4284 PRInt32 nameSpaceId = kNameSpaceID_Wildcard;
4286 if (!aNamespaceURI.EqualsLiteral("*")) {
4287 nsresult rv =
4288 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
4289 nameSpaceId);
4290 NS_ENSURE_SUCCESS(rv, rv);
4293 nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aLocalName);
4294 NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
4296 nsContentList *list = NS_GetContentList(this, nameAtom, nameSpaceId).get();
4297 NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
4299 // transfer ref to aReturn
4300 *aReturn = list;
4301 return NS_OK;
4304 NS_IMETHODIMP
4305 nsDocument::GetAsync(PRBool *aAsync)
4307 NS_ERROR("nsDocument::GetAsync() should be overriden by subclass!");
4309 return NS_ERROR_NOT_IMPLEMENTED;
4312 NS_IMETHODIMP
4313 nsDocument::SetAsync(PRBool aAsync)
4315 NS_ERROR("nsDocument::SetAsync() should be overriden by subclass!");
4317 return NS_ERROR_NOT_IMPLEMENTED;
4320 NS_IMETHODIMP
4321 nsDocument::Load(const nsAString& aUrl, PRBool *aReturn)
4323 NS_ERROR("nsDocument::Load() should be overriden by subclass!");
4325 return NS_ERROR_NOT_IMPLEMENTED;
4328 NS_IMETHODIMP
4329 nsDocument::EvaluateFIXptr(const nsAString& aExpression, nsIDOMRange **aRange)
4331 NS_ERROR("nsDocument::EvaluateFIXptr() should be overriden by subclass!");
4333 return NS_ERROR_NOT_IMPLEMENTED;
4336 NS_IMETHODIMP
4337 nsDocument::EvaluateXPointer(const nsAString& aExpression,
4338 nsIXPointerResult **aResult)
4340 NS_ERROR("nsDocument::EvaluateXPointer() should be overriden by subclass!");
4342 return NS_ERROR_NOT_IMPLEMENTED;
4345 NS_IMETHODIMP
4346 nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets)
4348 if (!mDOMStyleSheets) {
4349 mDOMStyleSheets = new nsDOMStyleSheetList(this);
4350 if (!mDOMStyleSheets) {
4351 return NS_ERROR_OUT_OF_MEMORY;
4355 *aStyleSheets = mDOMStyleSheets;
4356 NS_ADDREF(*aStyleSheets);
4358 return NS_OK;
4361 NS_IMETHODIMP
4362 nsDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
4364 aSheetSet.Truncate();
4366 // Look through our sheets, find the selected set title
4367 PRInt32 count = GetNumberOfStyleSheets();
4368 nsAutoString title;
4369 for (PRInt32 index = 0; index < count; index++) {
4370 nsIStyleSheet* sheet = GetStyleSheetAt(index);
4371 NS_ASSERTION(sheet, "Null sheet in sheet list!");
4373 nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(sheet);
4374 NS_ASSERTION(domSheet, "Sheet must QI to nsIDOMStyleSheet");
4375 PRBool disabled;
4376 domSheet->GetDisabled(&disabled);
4377 if (disabled) {
4378 // Disabled sheets don't affect the currently selected set
4379 continue;
4382 sheet->GetTitle(title);
4384 if (aSheetSet.IsEmpty()) {
4385 aSheetSet = title;
4386 } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
4387 // Sheets from multiple sets enabled; return null string, per spec.
4388 SetDOMStringToNull(aSheetSet);
4389 break;
4393 return NS_OK;
4396 NS_IMETHODIMP
4397 nsDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet)
4399 if (DOMStringIsNull(aSheetSet)) {
4400 return NS_OK;
4403 // Must update mLastStyleSheetSet before doing anything else with stylesheets
4404 // or CSSLoaders.
4405 mLastStyleSheetSet = aSheetSet;
4406 EnableStyleSheetsForSetInternal(aSheetSet, PR_TRUE);
4407 return NS_OK;
4410 NS_IMETHODIMP
4411 nsDocument::GetLastStyleSheetSet(nsAString& aSheetSet)
4413 aSheetSet = mLastStyleSheetSet;
4414 return NS_OK;
4417 NS_IMETHODIMP
4418 nsDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
4420 GetHeaderData(nsGkAtoms::headerDefaultStyle, aSheetSet);
4421 return NS_OK;
4424 NS_IMETHODIMP
4425 nsDocument::GetStyleSheetSets(nsIDOMDOMStringList** aList)
4427 if (!mStyleSheetSetList) {
4428 mStyleSheetSetList = new nsDOMStyleSheetSetList(this);
4429 if (!mStyleSheetSetList) {
4430 return NS_ERROR_OUT_OF_MEMORY;
4434 NS_ADDREF(*aList = mStyleSheetSetList);
4435 return NS_OK;
4438 NS_IMETHODIMP
4439 nsDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet)
4441 // Per spec, passing in null is a no-op.
4442 if (!DOMStringIsNull(aSheetSet)) {
4443 // Note: must make sure to not change the CSSLoader's preferred sheet --
4444 // that value should be equal to either our lastStyleSheetSet (if that's
4445 // non-null) or to our preferredStyleSheetSet. And this method doesn't
4446 // change either of those.
4447 EnableStyleSheetsForSetInternal(aSheetSet, PR_FALSE);
4450 return NS_OK;
4453 void
4454 nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
4455 PRBool aUpdateCSSLoader)
4457 BeginUpdate(UPDATE_STYLE);
4458 PRInt32 count = GetNumberOfStyleSheets();
4459 nsAutoString title;
4460 for (PRInt32 index = 0; index < count; index++) {
4461 nsIStyleSheet* sheet = GetStyleSheetAt(index);
4462 NS_ASSERTION(sheet, "Null sheet in sheet list!");
4463 sheet->GetTitle(title);
4464 if (!title.IsEmpty()) {
4465 sheet->SetEnabled(title.Equals(aSheetSet));
4468 if (aUpdateCSSLoader) {
4469 CSSLoader()->SetPreferredSheet(aSheetSet);
4471 EndUpdate(UPDATE_STYLE);
4474 NS_IMETHODIMP
4475 nsDocument::GetCharacterSet(nsAString& aCharacterSet)
4477 CopyASCIItoUTF16(GetDocumentCharacterSet(), aCharacterSet);
4478 return NS_OK;
4481 NS_IMETHODIMP
4482 nsDocument::ImportNode(nsIDOMNode* aImportedNode,
4483 PRBool aDeep,
4484 nsIDOMNode** aResult)
4486 NS_ENSURE_ARG(aImportedNode);
4488 *aResult = nsnull;
4490 nsresult rv = nsContentUtils::CheckSameOrigin(this, aImportedNode);
4491 if (NS_FAILED(rv)) {
4492 return rv;
4495 PRUint16 nodeType;
4496 aImportedNode->GetNodeType(&nodeType);
4497 switch (nodeType) {
4498 case nsIDOMNode::ATTRIBUTE_NODE:
4499 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
4500 case nsIDOMNode::ELEMENT_NODE:
4501 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
4502 case nsIDOMNode::TEXT_NODE:
4503 case nsIDOMNode::CDATA_SECTION_NODE:
4504 case nsIDOMNode::COMMENT_NODE:
4506 nsCOMPtr<nsINode> imported = do_QueryInterface(aImportedNode);
4507 NS_ENSURE_TRUE(imported, NS_ERROR_FAILURE);
4509 nsCOMPtr<nsIDOMNode> newNode;
4510 nsCOMArray<nsINode> nodesWithProperties;
4511 rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
4512 nodesWithProperties, getter_AddRefs(newNode));
4513 NS_ENSURE_SUCCESS(rv, rv);
4515 nsIDocument *ownerDoc = imported->GetOwnerDoc();
4516 if (ownerDoc) {
4517 rv = nsNodeUtils::CallUserDataHandlers(nodesWithProperties, ownerDoc,
4518 nsIDOMUserDataHandler::NODE_IMPORTED,
4519 PR_TRUE);
4520 NS_ENSURE_SUCCESS(rv, rv);
4523 newNode.swap(*aResult);
4525 return NS_OK;
4527 case nsIDOMNode::ENTITY_NODE:
4528 case nsIDOMNode::ENTITY_REFERENCE_NODE:
4529 case nsIDOMNode::NOTATION_NODE:
4531 return NS_ERROR_NOT_IMPLEMENTED;
4533 default:
4535 NS_WARNING("Don't know how to clone this nodetype for importNode.");
4537 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
4542 NS_IMETHODIMP
4543 nsDocument::AddBinding(nsIDOMElement* aContent, const nsAString& aURI)
4545 NS_ENSURE_ARG(aContent);
4547 nsresult rv = nsContentUtils::CheckSameOrigin(this, aContent);
4548 if (NS_FAILED(rv)) {
4549 return rv;
4552 nsCOMPtr<nsIContent> content(do_QueryInterface(aContent));
4554 nsCOMPtr<nsIURI> uri;
4555 rv = NS_NewURI(getter_AddRefs(uri), aURI);
4556 if (NS_FAILED(rv)) {
4557 return rv;
4560 // Figure out the right principal to use
4561 nsCOMPtr<nsIPrincipal> subject;
4562 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
4563 if (secMan) {
4564 rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
4565 NS_ENSURE_SUCCESS(rv, rv);
4568 if (!subject) {
4569 // Fall back to our principal. Or should we fall back to the null
4570 // principal? The latter would just mean no binding loads....
4571 subject = NodePrincipal();
4574 return BindingManager()->AddLayeredBinding(content, uri, subject);
4577 NS_IMETHODIMP
4578 nsDocument::RemoveBinding(nsIDOMElement* aContent, const nsAString& aURI)
4580 NS_ENSURE_ARG(aContent);
4582 nsresult rv = nsContentUtils::CheckSameOrigin(this, aContent);
4583 if (NS_FAILED(rv)) {
4584 return rv;
4587 nsCOMPtr<nsIURI> uri;
4588 rv = NS_NewURI(getter_AddRefs(uri), aURI);
4589 if (NS_FAILED(rv)) {
4590 return rv;
4593 nsCOMPtr<nsIContent> content(do_QueryInterface(aContent));
4594 return BindingManager()->RemoveLayeredBinding(content, uri);
4597 NS_IMETHODIMP
4598 nsDocument::LoadBindingDocument(const nsAString& aURI)
4600 nsCOMPtr<nsIURI> uri;
4601 nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI,
4602 mCharacterSet.get(),
4603 static_cast<nsIDocument *>(this)->GetBaseURI());
4604 NS_ENSURE_SUCCESS(rv, rv);
4606 // Figure out the right principal to use
4607 nsCOMPtr<nsIPrincipal> subject;
4608 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
4609 if (secMan) {
4610 rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
4611 NS_ENSURE_SUCCESS(rv, rv);
4614 if (!subject) {
4615 // Fall back to our principal. Or should we fall back to the null
4616 // principal? The latter would just mean no binding loads....
4617 subject = NodePrincipal();
4620 BindingManager()->LoadBindingDocument(this, uri, subject);
4622 return NS_OK;
4625 NS_IMETHODIMP
4626 nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult)
4628 *aResult = nsnull;
4629 nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
4630 if (!content)
4631 return NS_ERROR_FAILURE;
4633 nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(content->GetBindingParent()));
4634 NS_IF_ADDREF(*aResult = elt);
4635 return NS_OK;
4638 static nsresult
4639 GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName,
4640 const nsAString& aAttrValue, PRBool aUniversalMatch,
4641 nsIDOMElement** aResult)
4643 if (aUniversalMatch ? aContent->HasAttr(kNameSpaceID_None, aAttrName) :
4644 aContent->AttrValueIs(kNameSpaceID_None, aAttrName,
4645 aAttrValue, eCaseMatters)) {
4646 return CallQueryInterface(aContent, aResult);
4649 PRUint32 childCount = aContent->GetChildCount();
4651 for (PRUint32 i = 0; i < childCount; ++i) {
4652 nsIContent *current = aContent->GetChildAt(i);
4654 GetElementByAttribute(current, aAttrName, aAttrValue, aUniversalMatch,
4655 aResult);
4657 if (*aResult)
4658 return NS_OK;
4661 return NS_OK;
4664 NS_IMETHODIMP
4665 nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement,
4666 const nsAString& aAttrName,
4667 const nsAString& aAttrValue,
4668 nsIDOMElement** aResult)
4670 *aResult = nsnull;
4672 nsCOMPtr<nsIDOMNodeList> nodeList;
4673 GetAnonymousNodes(aElement, getter_AddRefs(nodeList));
4675 if (!nodeList)
4676 return NS_OK;
4678 nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttrName);
4680 PRUint32 length;
4681 nodeList->GetLength(&length);
4683 PRBool universalMatch = aAttrValue.EqualsLiteral("*");
4685 for (PRUint32 i = 0; i < length; ++i) {
4686 nsCOMPtr<nsIDOMNode> current;
4687 nodeList->Item(i, getter_AddRefs(current));
4689 nsCOMPtr<nsIContent> content(do_QueryInterface(current));
4691 GetElementByAttribute(content, attribute, aAttrValue, universalMatch,
4692 aResult);
4693 if (*aResult)
4694 return NS_OK;
4697 return NS_OK;
4701 NS_IMETHODIMP
4702 nsDocument::GetAnonymousNodes(nsIDOMElement* aElement,
4703 nsIDOMNodeList** aResult)
4705 *aResult = nsnull;
4707 nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
4708 return BindingManager()->GetAnonymousNodesFor(content, aResult);
4711 NS_IMETHODIMP
4712 nsDocument::CreateRange(nsIDOMRange** aReturn)
4714 nsresult rv = NS_NewRange(aReturn);
4716 if (NS_SUCCEEDED(rv)) {
4717 (*aReturn)->SetStart(this, 0);
4718 (*aReturn)->SetEnd(this, 0);
4721 return rv;
4724 NS_IMETHODIMP
4725 nsDocument::CreateNodeIterator(nsIDOMNode *aRoot,
4726 PRUint32 aWhatToShow,
4727 nsIDOMNodeFilter *aFilter,
4728 PRBool aEntityReferenceExpansion,
4729 nsIDOMNodeIterator **_retval)
4731 *_retval = nsnull;
4733 if (!aRoot)
4734 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
4736 nsresult rv = nsContentUtils::CheckSameOrigin(this, aRoot);
4737 NS_ENSURE_SUCCESS(rv, rv);
4739 NS_ENSURE_ARG_POINTER(_retval);
4741 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
4742 NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4744 nsNodeIterator *iterator = new nsNodeIterator(root,
4745 aWhatToShow,
4746 aFilter,
4747 aEntityReferenceExpansion);
4748 NS_ENSURE_TRUE(iterator, NS_ERROR_OUT_OF_MEMORY);
4750 NS_ADDREF(*_retval = iterator);
4752 return NS_OK;
4755 NS_IMETHODIMP
4756 nsDocument::CreateTreeWalker(nsIDOMNode *aRoot,
4757 PRUint32 aWhatToShow,
4758 nsIDOMNodeFilter *aFilter,
4759 PRBool aEntityReferenceExpansion,
4760 nsIDOMTreeWalker **_retval)
4762 *_retval = nsnull;
4764 if (!aRoot)
4765 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
4767 nsresult rv = nsContentUtils::CheckSameOrigin(this, aRoot);
4768 NS_ENSURE_SUCCESS(rv, rv);
4770 NS_ENSURE_ARG_POINTER(_retval);
4772 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
4773 NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
4775 nsTreeWalker* walker = new nsTreeWalker(root,
4776 aWhatToShow,
4777 aFilter,
4778 aEntityReferenceExpansion);
4779 NS_ENSURE_TRUE(walker, NS_ERROR_OUT_OF_MEMORY);
4781 NS_ADDREF(*_retval = walker);
4783 return NS_OK;
4787 NS_IMETHODIMP
4788 nsDocument::GetDefaultView(nsIDOMAbstractView** aDefaultView)
4790 nsPIDOMWindow* win = GetWindow();
4791 if (win) {
4792 return CallQueryInterface(win, aDefaultView);
4795 *aDefaultView = nsnull;
4797 return NS_OK;
4800 NS_IMETHODIMP
4801 nsDocument::GetLocation(nsIDOMLocation **_retval)
4803 NS_ENSURE_ARG_POINTER(_retval);
4804 *_retval = nsnull;
4806 nsCOMPtr<nsIDOMWindowInternal> w(do_QueryInterface(mScriptGlobalObject));
4808 if (!w) {
4809 return NS_OK;
4812 return w->GetLocation(_retval);
4815 nsIContent*
4816 nsDocument::GetHtmlContent()
4818 nsIContent* rootContent = GetRootContent();
4819 if (rootContent && rootContent->Tag() == nsGkAtoms::html &&
4820 rootContent->IsNodeOfType(nsINode::eHTML))
4821 return rootContent;
4822 return nsnull;
4825 nsIContent*
4826 nsDocument::GetHtmlChildContent(nsIAtom* aTag)
4828 nsIContent* html = GetHtmlContent();
4829 if (!html)
4830 return nsnull;
4832 // Look for the element with aTag inside html. This needs to run
4833 // forwards to find the first such element.
4834 for (PRUint32 i = 0; i < html->GetChildCount(); ++i) {
4835 nsIContent* result = html->GetChildAt(i);
4836 if (result->Tag() == aTag && result->IsNodeOfType(nsINode::eHTML))
4837 return result;
4839 return nsnull;
4842 nsIContent*
4843 nsDocument::GetTitleContent(PRUint32 aNodeType)
4845 // mMayHaveTitleElement will have been set to true if any HTML or SVG
4846 // <title> element has been bound to this document. So if it's false,
4847 // we know there is nothing to do here. This avoids us having to search
4848 // the whole DOM if someone calls document.title on a large document
4849 // without a title.
4850 if (!mMayHaveTitleElement)
4851 return nsnull;
4853 nsRefPtr<nsContentList> list =
4854 NS_GetContentList(this, nsGkAtoms::title, kNameSpaceID_Unknown);
4855 if (!list)
4856 return nsnull;
4858 for (PRUint32 i = 0; ; ++i) {
4859 // Avoid calling list->Length --- by calling Item one at a time,
4860 // we can avoid scanning the whole document to build the list of all
4861 // matches
4862 nsIContent* elem = list->Item(i, PR_FALSE);
4863 if (!elem)
4864 return nsnull;
4865 if (elem->IsNodeOfType(aNodeType))
4866 return elem;
4870 void
4871 nsDocument::GetTitleFromElement(PRUint32 aNodeType, nsAString& aTitle)
4873 nsIContent* title = GetTitleContent(aNodeType);
4874 if (!title)
4875 return;
4876 nsContentUtils::GetNodeTextContent(title, PR_FALSE, aTitle);
4879 NS_IMETHODIMP
4880 nsDocument::GetTitle(nsAString& aTitle)
4882 aTitle.Truncate();
4884 nsIContent *rootContent = GetRootContent();
4885 if (!rootContent)
4886 return NS_OK;
4888 nsAutoString tmp;
4890 switch (rootContent->GetNameSpaceID()) {
4891 #ifdef MOZ_XUL
4892 case kNameSpaceID_XUL:
4893 rootContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
4894 break;
4895 #endif
4896 #ifdef MOZ_SVG
4897 case kNameSpaceID_SVG:
4898 if (rootContent->Tag() == nsGkAtoms::svg) {
4899 GetTitleFromElement(nsINode::eSVG, tmp);
4900 break;
4901 } // else fall through
4902 #endif
4903 default:
4904 GetTitleFromElement(nsINode::eHTML, tmp);
4905 break;
4908 tmp.CompressWhitespace();
4909 aTitle = tmp;
4910 return NS_OK;
4913 NS_IMETHODIMP
4914 nsDocument::SetTitle(const nsAString& aTitle)
4916 nsIContent *rootContent = GetRootContent();
4917 if (!rootContent)
4918 return NS_OK;
4920 switch (rootContent->GetNameSpaceID()) {
4921 #ifdef MOZ_SVG
4922 case kNameSpaceID_SVG:
4923 return NS_OK; // SVG doesn't support setting a title
4924 #endif
4925 #ifdef MOZ_XUL
4926 case kNameSpaceID_XUL:
4927 return rootContent->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
4928 aTitle, PR_TRUE);
4929 #endif
4932 // Batch updates so that mutation events don't change "the title
4933 // element" under us
4934 mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, PR_TRUE);
4936 nsIContent* title = GetTitleContent(nsINode::eHTML);
4937 if (!title) {
4938 nsIContent *head = GetHeadContent();
4939 if (!head)
4940 return NS_OK;
4943 nsCOMPtr<nsINodeInfo> titleInfo;
4944 titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nsnull,
4945 kNameSpaceID_None);
4946 if (!titleInfo)
4947 return NS_OK;
4948 title = NS_NewHTMLTitleElement(titleInfo);
4949 if (!title)
4950 return NS_OK;
4953 head->AppendChildTo(title, PR_TRUE);
4956 return nsContentUtils::SetNodeTextContent(title, aTitle, PR_FALSE);
4959 void
4960 nsDocument::NotifyPossibleTitleChange(PRBool aBoundTitleElement)
4962 if (aBoundTitleElement) {
4963 mMayHaveTitleElement = PR_TRUE;
4965 if (mPendingTitleChangeEvent.IsPending())
4966 return;
4968 nsRefPtr<nsRunnableMethod<nsDocument> > event =
4969 new nsRunnableMethod<nsDocument>(this,
4970 &nsDocument::DoNotifyPossibleTitleChange);
4971 nsresult rv = NS_DispatchToCurrentThread(event);
4972 if (NS_SUCCEEDED(rv)) {
4973 mPendingTitleChangeEvent = event;
4977 void
4978 nsDocument::DoNotifyPossibleTitleChange()
4980 mPendingTitleChangeEvent.Forget();
4981 mHaveFiredTitleChange = PR_TRUE;
4983 nsAutoString title;
4984 GetTitle(title);
4986 nsPresShellIterator iter(this);
4987 nsCOMPtr<nsIPresShell> shell;
4988 while ((shell = iter.GetNextShell())) {
4989 nsCOMPtr<nsISupports> container = shell->GetPresContext()->GetContainer();
4990 if (!container)
4991 continue;
4993 nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
4994 if (!docShellWin)
4995 continue;
4997 docShellWin->SetTitle(PromiseFlatString(title).get());
5000 // Fire a DOM event for the title change.
5001 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
5002 NS_LITERAL_STRING("DOMTitleChanged"),
5003 PR_TRUE, PR_TRUE);
5006 NS_IMETHODIMP
5007 nsDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult)
5009 nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
5010 NS_ENSURE_TRUE(content, NS_ERROR_UNEXPECTED);
5012 nsIDocument* doc = content->GetOwnerDoc();
5013 NS_ENSURE_TRUE(doc == this, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
5015 if (!mHasWarnedAboutBoxObjects && !content->IsNodeOfType(eXUL)) {
5016 mHasWarnedAboutBoxObjects = PR_TRUE;
5017 nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
5018 "UseOfGetBoxObjectForWarning",
5019 nsnull, 0,
5020 static_cast<nsIDocument*>(this)->
5021 GetDocumentURI(),
5022 EmptyString(), 0, 0,
5023 nsIScriptError::warningFlag,
5024 "BoxObjects");
5027 *aResult = nsnull;
5029 if (!mBoxObjectTable) {
5030 mBoxObjectTable = new nsInterfaceHashtable<nsVoidPtrHashKey, nsPIBoxObject>;
5031 if (mBoxObjectTable && !mBoxObjectTable->Init(12)) {
5032 mBoxObjectTable = nsnull;
5034 } else {
5035 // Want to use Get(content, aResult); but it's the wrong type
5036 *aResult = mBoxObjectTable->GetWeak(content);
5037 if (*aResult) {
5038 NS_ADDREF(*aResult);
5039 return NS_OK;
5043 PRInt32 namespaceID;
5044 nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(content, &namespaceID);
5046 nsCAutoString contractID("@mozilla.org/layout/xul-boxobject");
5047 if (namespaceID == kNameSpaceID_XUL) {
5048 if (tag == nsGkAtoms::browser ||
5049 tag == nsGkAtoms::editor ||
5050 tag == nsGkAtoms::iframe)
5051 contractID += "-container";
5052 else if (tag == nsGkAtoms::menu)
5053 contractID += "-menu";
5054 else if (tag == nsGkAtoms::popup ||
5055 tag == nsGkAtoms::menupopup ||
5056 tag == nsGkAtoms::panel ||
5057 tag == nsGkAtoms::tooltip)
5058 contractID += "-popup";
5059 else if (tag == nsGkAtoms::tree)
5060 contractID += "-tree";
5061 else if (tag == nsGkAtoms::listbox)
5062 contractID += "-listbox";
5063 else if (tag == nsGkAtoms::scrollbox)
5064 contractID += "-scrollbox";
5066 contractID += ";1";
5068 nsCOMPtr<nsPIBoxObject> boxObject(do_CreateInstance(contractID.get()));
5069 if (!boxObject)
5070 return NS_ERROR_FAILURE;
5072 boxObject->Init(content);
5074 if (mBoxObjectTable) {
5075 mBoxObjectTable->Put(content, boxObject.get());
5078 *aResult = boxObject;
5079 NS_ADDREF(*aResult);
5081 return NS_OK;
5084 void
5085 nsDocument::ClearBoxObjectFor(nsIContent* aContent)
5087 if (mBoxObjectTable) {
5088 nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent);
5089 if (boxObject) {
5090 boxObject->Clear();
5091 mBoxObjectTable->Remove(aContent);
5096 nsresult
5097 nsDocument::GetXBLChildNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult)
5099 return BindingManager()->GetXBLChildNodesFor(aContent, aResult);
5102 nsresult
5103 nsDocument::GetContentListFor(nsIContent* aContent, nsIDOMNodeList** aResult)
5105 return BindingManager()->GetContentListFor(aContent, aResult);
5108 void
5109 nsDocument::FlushSkinBindings()
5111 BindingManager()->FlushSkinBindings();
5114 nsresult
5115 nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
5117 mInitializableFrameLoaders.RemoveElement(aLoader);
5118 // Don't even try to initialize.
5119 if (mInDestructor) {
5120 NS_WARNING("Trying to initialize a frame loader while"
5121 "document is being deleted");
5122 return NS_ERROR_FAILURE;
5124 if (mUpdateNestLevel == 0 && !mDelayFrameLoaderInitialization) {
5125 nsRefPtr<nsFrameLoader> loader = aLoader;
5126 return loader->ReallyStartLoading();
5127 } else {
5128 mInitializableFrameLoaders.AppendElement(aLoader);
5130 return NS_OK;
5133 nsresult
5134 nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader)
5136 mInitializableFrameLoaders.RemoveElement(aLoader);
5137 if (mInDestructor) {
5138 return NS_ERROR_FAILURE;
5140 if (mUpdateNestLevel == 0) {
5141 nsRefPtr<nsFrameLoader> loader = aLoader;
5142 loader->Finalize();
5143 } else {
5144 mFinalizableFrameLoaders.AppendElement(aLoader);
5146 return NS_OK;
5149 void
5150 nsDocument::InitializeFinalizeFrameLoaders()
5152 NS_ASSERTION(mUpdateNestLevel == 0 && !mDelayFrameLoaderInitialization,
5153 "Wrong time to call InitializeFinalizeFrameLoaders!");
5154 // Don't use a temporary array for mInitializableFrameLoaders, because
5155 // loading a frame may cause some other frameloader to be removed from the
5156 // array. But be careful to keep the loader alive when starting the load!
5157 while (mInitializableFrameLoaders.Length()) {
5158 nsRefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
5159 mInitializableFrameLoaders.RemoveElementAt(0);
5160 NS_ASSERTION(loader, "null frameloader in the array?");
5161 loader->ReallyStartLoading();
5164 PRUint32 length = mFinalizableFrameLoaders.Length();
5165 if (length > 0) {
5166 nsTArray<nsRefPtr<nsFrameLoader> > loaders;
5167 mFinalizableFrameLoaders.SwapElements(loaders);
5168 for (PRUint32 i = 0; i < length; ++i) {
5169 loaders[i]->Finalize();
5174 void
5175 nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
5177 PRUint32 length = mInitializableFrameLoaders.Length();
5178 for (PRUint32 i = 0; i < length; ++i) {
5179 if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
5180 mInitializableFrameLoaders.RemoveElementAt(i);
5181 return;
5186 PRBool
5187 nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell)
5189 if (aShell) {
5190 PRUint32 length = mFinalizableFrameLoaders.Length();
5191 for (PRUint32 i = 0; i < length; ++i) {
5192 if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) {
5193 return PR_TRUE;
5197 return PR_FALSE;
5200 nsIDocument*
5201 nsDocument::RequestExternalResource(nsIURI* aURI,
5202 nsINode* aRequestingNode,
5203 ExternalResourceLoad** aPendingLoad)
5205 NS_PRECONDITION(aURI, "Must have a URI");
5206 NS_PRECONDITION(aRequestingNode, "Must have a node");
5207 if (mDisplayDocument) {
5208 return mDisplayDocument->RequestExternalResource(aURI,
5209 aRequestingNode,
5210 aPendingLoad);
5213 return mExternalResourceMap.RequestResource(aURI, aRequestingNode,
5214 this, aPendingLoad);
5217 void
5218 nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
5220 mExternalResourceMap.EnumerateResources(aCallback, aData);
5223 struct DirTable {
5224 const char* mName;
5225 PRUint8 mValue;
5228 static const DirTable dirAttributes[] = {
5229 {"ltr", IBMBIDI_TEXTDIRECTION_LTR},
5230 {"rtl", IBMBIDI_TEXTDIRECTION_RTL},
5235 * Retrieve the "direction" property of the document.
5237 * @lina 01/09/2001
5239 NS_IMETHODIMP
5240 nsDocument::GetDir(nsAString& aDirection)
5242 PRUint32 options = GetBidiOptions();
5243 for (const DirTable* elt = dirAttributes; elt->mName; elt++) {
5244 if (GET_BIDI_OPTION_DIRECTION(options) == elt->mValue) {
5245 CopyASCIItoUTF16(elt->mName, aDirection);
5246 break;
5250 return NS_OK;
5254 * Set the "direction" property of the document.
5256 * @lina 01/09/2001
5258 NS_IMETHODIMP
5259 nsDocument::SetDir(const nsAString& aDirection)
5261 PRUint32 options = GetBidiOptions();
5263 for (const DirTable* elt = dirAttributes; elt->mName; elt++) {
5264 if (aDirection == NS_ConvertASCIItoUTF16(elt->mName)) {
5265 if (GET_BIDI_OPTION_DIRECTION(options) != elt->mValue) {
5266 SET_BIDI_OPTION_DIRECTION(options, elt->mValue);
5267 nsIPresShell *shell = GetPrimaryShell();
5268 if (shell) {
5269 nsPresContext *context = shell->GetPresContext();
5270 NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
5271 context->SetBidi(options, PR_TRUE);
5272 } else {
5273 // No presentation; just set it on ourselves
5274 SetBidiOptions(options);
5278 break;
5282 return NS_OK;
5287 // nsIDOMNode methods
5289 NS_IMETHODIMP
5290 nsDocument::GetNodeName(nsAString& aNodeName)
5292 aNodeName.AssignLiteral("#document");
5294 return NS_OK;
5297 NS_IMETHODIMP
5298 nsDocument::GetNodeValue(nsAString& aNodeValue)
5300 SetDOMStringToNull(aNodeValue);
5302 return NS_OK;
5305 NS_IMETHODIMP
5306 nsDocument::SetNodeValue(const nsAString& aNodeValue)
5308 // The DOM spec says that when nodeValue is defined to be null "setting it
5309 // has no effect", so we don't throw an exception.
5310 return NS_OK;
5313 NS_IMETHODIMP
5314 nsDocument::GetNodeType(PRUint16* aNodeType)
5316 *aNodeType = nsIDOMNode::DOCUMENT_NODE;
5318 return NS_OK;
5321 NS_IMETHODIMP
5322 nsDocument::GetParentNode(nsIDOMNode** aParentNode)
5324 *aParentNode = nsnull;
5326 return NS_OK;
5329 NS_IMETHODIMP
5330 nsDocument::GetChildNodes(nsIDOMNodeList** aChildNodes)
5332 nsSlots *slots = GetSlots();
5333 NS_ENSURE_TRUE(slots, NS_ERROR_OUT_OF_MEMORY);
5335 if (!slots->mChildNodes) {
5336 slots->mChildNodes = new nsChildContentList(this);
5337 NS_ENSURE_TRUE(slots->mChildNodes, NS_ERROR_OUT_OF_MEMORY);
5338 NS_ADDREF(slots->mChildNodes);
5341 NS_ADDREF(*aChildNodes = slots->mChildNodes);
5343 return NS_OK;
5346 NS_IMETHODIMP
5347 nsDocument::HasChildNodes(PRBool* aHasChildNodes)
5349 NS_ENSURE_ARG(aHasChildNodes);
5351 *aHasChildNodes = (mChildren.ChildCount() != 0);
5353 return NS_OK;
5356 NS_IMETHODIMP
5357 nsDocument::HasAttributes(PRBool* aHasAttributes)
5359 NS_ENSURE_ARG(aHasAttributes);
5361 *aHasAttributes = PR_FALSE;
5363 return NS_OK;
5366 NS_IMETHODIMP
5367 nsDocument::GetFirstChild(nsIDOMNode** aFirstChild)
5369 if (mChildren.ChildCount()) {
5370 return CallQueryInterface(mChildren.ChildAt(0), aFirstChild);
5373 *aFirstChild = nsnull;
5375 return NS_OK;
5378 NS_IMETHODIMP
5379 nsDocument::GetLastChild(nsIDOMNode** aLastChild)
5381 PRInt32 count = mChildren.ChildCount();
5382 if (count) {
5383 return CallQueryInterface(mChildren.ChildAt(count-1), aLastChild);
5386 *aLastChild = nsnull;
5388 return NS_OK;
5391 NS_IMETHODIMP
5392 nsDocument::GetPreviousSibling(nsIDOMNode** aPreviousSibling)
5394 *aPreviousSibling = nsnull;
5396 return NS_OK;
5399 NS_IMETHODIMP
5400 nsDocument::GetNextSibling(nsIDOMNode** aNextSibling)
5402 *aNextSibling = nsnull;
5404 return NS_OK;
5407 NS_IMETHODIMP
5408 nsDocument::GetAttributes(nsIDOMNamedNodeMap** aAttributes)
5410 *aAttributes = nsnull;
5412 return NS_OK;
5415 NS_IMETHODIMP
5416 nsDocument::GetNamespaceURI(nsAString& aNamespaceURI)
5418 SetDOMStringToNull(aNamespaceURI);
5420 return NS_OK;
5423 NS_IMETHODIMP
5424 nsDocument::GetPrefix(nsAString& aPrefix)
5426 SetDOMStringToNull(aPrefix);
5428 return NS_OK;
5431 NS_IMETHODIMP
5432 nsDocument::SetPrefix(const nsAString& aPrefix)
5434 return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
5437 NS_IMETHODIMP
5438 nsDocument::GetLocalName(nsAString& aLocalName)
5440 SetDOMStringToNull(aLocalName);
5442 return NS_OK;
5445 NS_IMETHODIMP
5446 nsDocument::InsertBefore(nsIDOMNode* aNewChild, nsIDOMNode* aRefChild,
5447 nsIDOMNode** aReturn)
5449 return nsGenericElement::doReplaceOrInsertBefore(PR_FALSE, aNewChild, aRefChild, nsnull, this,
5450 aReturn);
5453 NS_IMETHODIMP
5454 nsDocument::ReplaceChild(nsIDOMNode* aNewChild, nsIDOMNode* aOldChild,
5455 nsIDOMNode** aReturn)
5457 return nsGenericElement::doReplaceOrInsertBefore(PR_TRUE, aNewChild, aOldChild, nsnull, this,
5458 aReturn);
5461 NS_IMETHODIMP
5462 nsDocument::RemoveChild(nsIDOMNode* aOldChild, nsIDOMNode** aReturn)
5464 return nsGenericElement::doRemoveChild(aOldChild, nsnull, this, aReturn);
5467 NS_IMETHODIMP
5468 nsDocument::AppendChild(nsIDOMNode* aNewChild, nsIDOMNode** aReturn)
5470 return nsDocument::InsertBefore(aNewChild, nsnull, aReturn);
5473 NS_IMETHODIMP
5474 nsDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
5476 return nsNodeUtils::CloneNodeImpl(this, aDeep, aReturn);
5479 NS_IMETHODIMP
5480 nsDocument::Normalize()
5482 PRInt32 count = mChildren.ChildCount();
5483 for (PRInt32 i = 0; i < count; ++i) {
5484 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mChildren.ChildAt(i)));
5486 if (node) {
5487 node->Normalize();
5491 return NS_OK;
5494 NS_IMETHODIMP
5495 nsDocument::IsSupported(const nsAString& aFeature, const nsAString& aVersion,
5496 PRBool* aReturn)
5498 return nsGenericElement::InternalIsSupported(static_cast<nsIDOMDocument*>(this),
5499 aFeature, aVersion, aReturn);
5502 NS_IMETHODIMP
5503 nsDocument::GetBaseURI(nsAString &aURI)
5505 nsCAutoString spec;
5506 if (mDocumentBaseURI) {
5507 mDocumentBaseURI->GetSpec(spec);
5510 CopyUTF8toUTF16(spec, aURI);
5512 return NS_OK;
5515 NS_IMETHODIMP
5516 nsDocument::GetTextContent(nsAString &aTextContent)
5518 SetDOMStringToNull(aTextContent);
5520 return NS_OK;
5523 NS_IMETHODIMP
5524 nsDocument::SetTextContent(const nsAString& aTextContent)
5526 return NS_OK;
5530 NS_IMETHODIMP
5531 nsDocument::CompareDocumentPosition(nsIDOMNode* aOther, PRUint16* aReturn)
5533 NS_ENSURE_ARG_POINTER(aOther);
5535 // We could optimize this by getting the other nodes current document
5536 // and comparing with ourself. But then we'd have to deal with the
5537 // current document being null and such so it's easier this way.
5538 // It's hardly a case to optimize anyway.
5540 nsCOMPtr<nsINode> other = do_QueryInterface(aOther);
5541 NS_ENSURE_TRUE(other, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5543 *aReturn = nsContentUtils::ComparePosition(other, this);
5544 return NS_OK;
5547 NS_IMETHODIMP
5548 nsDocument::IsSameNode(nsIDOMNode* aOther, PRBool* aReturn)
5550 PRBool sameNode = PR_FALSE;
5552 if (this == aOther) {
5553 sameNode = PR_TRUE;
5556 *aReturn = sameNode;
5558 return NS_OK;
5561 NS_IMETHODIMP
5562 nsDocument::IsEqualNode(nsIDOMNode* aOther, PRBool* aReturn)
5564 NS_ENSURE_ARG_POINTER(aOther);
5566 *aReturn = PR_FALSE;
5568 // Node type check by QI. We also reuse this later.
5569 nsCOMPtr<nsIDocument> aOtherDoc = do_QueryInterface(aOther);
5570 if (!aOtherDoc) {
5571 return NS_OK;
5574 // Child nodes check.
5575 PRUint32 childCount = GetChildCount();
5576 if (childCount != aOtherDoc->GetChildCount()) {
5577 return NS_OK;
5580 for (PRUint32 i = 0; i < childCount; i++) {
5581 nsIContent* aChild1 = GetChildAt(i);
5582 nsIContent* aChild2 = aOtherDoc->GetChildAt(i);
5583 if (!nsNode3Tearoff::AreNodesEqual(aChild1, aChild2)) {
5584 return NS_OK;
5588 /* Checks not needed: Prefix, namespace URI, local name, node name,
5589 node value, attributes.
5592 *aReturn = PR_TRUE;
5593 return NS_OK;
5596 NS_IMETHODIMP
5597 nsDocument::IsDefaultNamespace(const nsAString& aNamespaceURI,
5598 PRBool* aReturn)
5600 nsAutoString defaultNamespace;
5601 LookupNamespaceURI(EmptyString(), defaultNamespace);
5602 *aReturn = aNamespaceURI.Equals(defaultNamespace);
5603 return NS_OK;
5606 NS_IMETHODIMP
5607 nsDocument::GetFeature(const nsAString& aFeature,
5608 const nsAString& aVersion,
5609 nsISupports** aReturn)
5611 return nsGenericElement::InternalGetFeature(static_cast<nsIDOMDocument*>(this),
5612 aFeature, aVersion, aReturn);
5615 NS_IMETHODIMP
5616 nsDocument::SetUserData(const nsAString &aKey,
5617 nsIVariant *aData,
5618 nsIDOMUserDataHandler *aHandler,
5619 nsIVariant **aResult)
5621 return nsNodeUtils::SetUserData(this, aKey, aData, aHandler, aResult);
5624 NS_IMETHODIMP
5625 nsDocument::GetUserData(const nsAString &aKey,
5626 nsIVariant **aResult)
5628 return nsNodeUtils::GetUserData(this, aKey, aResult);
5631 NS_IMETHODIMP
5632 nsDocument::LookupPrefix(const nsAString& aNamespaceURI,
5633 nsAString& aPrefix)
5635 nsCOMPtr<nsIDOM3Node> root(do_QueryInterface(GetRootContent()));
5636 if (root) {
5637 return root->LookupPrefix(aNamespaceURI, aPrefix);
5640 SetDOMStringToNull(aPrefix);
5641 return NS_OK;
5644 NS_IMETHODIMP
5645 nsDocument::LookupNamespaceURI(const nsAString& aNamespacePrefix,
5646 nsAString& aNamespaceURI)
5648 if (NS_FAILED(nsContentUtils::LookupNamespaceURI(GetRootContent(),
5649 aNamespacePrefix,
5650 aNamespaceURI))) {
5651 SetDOMStringToNull(aNamespaceURI);
5653 return NS_OK;
5656 NS_IMETHODIMP
5657 nsDocument::GetInputEncoding(nsAString& aInputEncoding)
5659 if (mHaveInputEncoding) {
5660 return GetCharacterSet(aInputEncoding);
5663 SetDOMStringToNull(aInputEncoding);
5664 return NS_OK;
5667 NS_IMETHODIMP
5668 nsDocument::GetXmlEncoding(nsAString& aXmlEncoding)
5670 if (mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS &&
5671 mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
5672 // XXX We don't store the encoding given in the xml declaration.
5673 // For now, just output the inputEncoding which we do store.
5674 GetInputEncoding(aXmlEncoding);
5675 } else {
5676 SetDOMStringToNull(aXmlEncoding);
5679 return NS_OK;
5682 NS_IMETHODIMP
5683 nsDocument::GetXmlStandalone(PRBool *aXmlStandalone)
5685 *aXmlStandalone =
5686 mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS &&
5687 mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS &&
5688 mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES;
5690 return NS_OK;
5693 NS_IMETHODIMP
5694 nsDocument::SetXmlStandalone(PRBool aXmlStandalone)
5696 return NS_ERROR_NOT_IMPLEMENTED;
5699 NS_IMETHODIMP
5700 nsDocument::GetXmlVersion(nsAString& aXmlVersion)
5702 // If there is no declaration, the value is "1.0".
5704 // XXX We only support "1.0", so always output "1.0" until that changes.
5705 aXmlVersion.AssignLiteral("1.0");
5707 return NS_OK;
5710 NS_IMETHODIMP
5711 nsDocument::SetXmlVersion(const nsAString& aXmlVersion)
5713 return NS_ERROR_NOT_IMPLEMENTED;
5716 NS_IMETHODIMP
5717 nsDocument::GetStrictErrorChecking(PRBool *aStrictErrorChecking)
5719 // This attribute is true by default, and we don't really support it being false.
5720 *aStrictErrorChecking = PR_TRUE;
5721 return NS_OK;
5724 NS_IMETHODIMP
5725 nsDocument::SetStrictErrorChecking(PRBool aStrictErrorChecking)
5727 // We don't really support non-strict error checking, so just no-op for now.
5728 return NS_OK;
5731 NS_IMETHODIMP
5732 nsDocument::GetDocumentURI(nsAString& aDocumentURI)
5734 if (mDocumentURI) {
5735 nsCAutoString uri;
5736 mDocumentURI->GetSpec(uri);
5737 CopyUTF8toUTF16(uri, aDocumentURI);
5738 } else {
5739 SetDOMStringToNull(aDocumentURI);
5742 return NS_OK;
5745 NS_IMETHODIMP
5746 nsDocument::SetDocumentURI(const nsAString& aDocumentURI)
5748 // Not allowing this yet, need to think about security ramifications first.
5749 // We use mDocumentURI to get principals for this document.
5750 return NS_ERROR_NOT_IMPLEMENTED;
5753 static void BlastSubtreeToPieces(nsINode *aNode);
5755 PLDHashOperator
5756 BlastFunc(nsAttrHashKey::KeyType aKey, nsIDOMNode *aData, void* aUserArg)
5758 nsCOMPtr<nsIAttribute> *attr =
5759 static_cast<nsCOMPtr<nsIAttribute>*>(aUserArg);
5761 *attr = do_QueryInterface(aData);
5763 NS_ASSERTION(attr->get(),
5764 "non-nsIAttribute somehow made it into the hashmap?!");
5766 return PL_DHASH_STOP;
5769 static void
5770 BlastSubtreeToPieces(nsINode *aNode)
5772 PRUint32 i, count;
5773 if (aNode->IsNodeOfType(nsINode::eELEMENT)) {
5774 nsGenericElement *element = static_cast<nsGenericElement*>(aNode);
5775 const nsDOMAttributeMap *map = element->GetAttributeMap();
5776 if (map) {
5777 nsCOMPtr<nsIAttribute> attr;
5778 while (map->Enumerate(BlastFunc, &attr) > 0) {
5779 BlastSubtreeToPieces(attr);
5781 #ifdef DEBUG
5782 nsresult rv =
5783 #endif
5784 element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
5785 attr->NodeInfo()->NameAtom(),
5786 PR_FALSE);
5788 // XXX Should we abort here?
5789 NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, UnsetAttr shouldn't fail!");
5794 count = aNode->GetChildCount();
5795 for (i = 0; i < count; ++i) {
5796 BlastSubtreeToPieces(aNode->GetChildAt(0));
5797 #ifdef DEBUG
5798 nsresult rv =
5799 #endif
5800 aNode->RemoveChildAt(0, PR_FALSE);
5802 // XXX Should we abort here?
5803 NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, RemoveChildAt shouldn't fail!");
5807 NS_IMETHODIMP
5808 nsDocument::AdoptNode(nsIDOMNode *aAdoptedNode, nsIDOMNode **aResult)
5810 NS_ENSURE_ARG(aAdoptedNode);
5812 *aResult = nsnull;
5814 nsresult rv = nsContentUtils::CheckSameOrigin(this, aAdoptedNode);
5815 NS_ENSURE_SUCCESS(rv, rv);
5817 nsCOMPtr<nsINode> adoptedNode;
5818 PRUint16 nodeType;
5819 aAdoptedNode->GetNodeType(&nodeType);
5820 switch (nodeType) {
5821 case nsIDOMNode::ATTRIBUTE_NODE:
5823 // Remove from ownerElement.
5824 nsCOMPtr<nsIDOMAttr> adoptedAttr = do_QueryInterface(aAdoptedNode, &rv);
5825 NS_ENSURE_SUCCESS(rv, rv);
5827 nsCOMPtr<nsIDOMElement> ownerElement;
5828 rv = adoptedAttr->GetOwnerElement(getter_AddRefs(ownerElement));
5829 NS_ENSURE_SUCCESS(rv, rv);
5831 if (ownerElement) {
5832 nsCOMPtr<nsIDOMAttr> newAttr;
5833 rv = ownerElement->RemoveAttributeNode(adoptedAttr,
5834 getter_AddRefs(newAttr));
5835 NS_ENSURE_SUCCESS(rv, rv);
5837 newAttr.swap(adoptedAttr);
5840 adoptedNode = do_QueryInterface(adoptedAttr, &rv);
5841 NS_ENSURE_SUCCESS(rv, rv);
5842 break;
5844 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
5845 case nsIDOMNode::ELEMENT_NODE:
5846 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
5847 case nsIDOMNode::TEXT_NODE:
5848 case nsIDOMNode::CDATA_SECTION_NODE:
5849 case nsIDOMNode::COMMENT_NODE:
5851 adoptedNode = do_QueryInterface(aAdoptedNode, &rv);
5852 NS_ENSURE_SUCCESS(rv, rv);
5854 // We don't want to adopt an element into its own contentDocument or into
5855 // a descendant contentDocument, so we check if the frameElement of this
5856 // document or any of its parents is the adopted node or one of its
5857 // descendants.
5858 nsIDocument *doc = this;
5859 do {
5860 nsPIDOMWindow *win = doc->GetWindow();
5861 if (win) {
5862 nsCOMPtr<nsINode> node =
5863 do_QueryInterface(win->GetFrameElementInternal());
5864 if (node &&
5865 nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
5866 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
5869 } while ((doc = doc->GetParentDocument()));
5871 // Remove from parent.
5872 nsCOMPtr<nsIDOMNode> parent;
5873 aAdoptedNode->GetParentNode(getter_AddRefs(parent));
5874 NS_ENSURE_SUCCESS(rv, rv);
5876 if (parent) {
5877 nsCOMPtr<nsIDOMNode> newChild;
5878 rv = parent->RemoveChild(aAdoptedNode, getter_AddRefs(newChild));
5879 NS_ENSURE_SUCCESS(rv, rv);
5882 break;
5884 case nsIDOMNode::ENTITY_REFERENCE_NODE:
5886 return NS_ERROR_NOT_IMPLEMENTED;
5888 case nsIDOMNode::DOCUMENT_NODE:
5889 case nsIDOMNode::DOCUMENT_TYPE_NODE:
5890 case nsIDOMNode::ENTITY_NODE:
5891 case nsIDOMNode::NOTATION_NODE:
5893 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
5895 default:
5897 NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
5899 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
5903 nsIDocument *oldDocument = adoptedNode->GetOwnerDoc();
5904 PRBool sameDocument = oldDocument == this;
5906 JSContext *cx = nsnull;
5907 JSObject *oldScope = nsnull;
5908 JSObject *newScope = nsnull;
5909 if (!sameDocument && oldDocument) {
5910 rv = nsContentUtils::GetContextAndScopes(oldDocument, this, &cx, &oldScope,
5911 &newScope);
5912 NS_ENSURE_SUCCESS(rv, rv);
5915 nsCOMArray<nsINode> nodesWithProperties;
5916 rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nsnull : mNodeInfoManager,
5917 cx, oldScope, newScope, nodesWithProperties);
5918 if (NS_FAILED(rv)) {
5919 // Disconnect all nodes from their parents, since some have the old document
5920 // as their ownerDocument and some have this as their ownerDocument.
5921 BlastSubtreeToPieces(adoptedNode);
5923 if (!sameDocument && oldDocument) {
5924 PRUint32 i, count = nodesWithProperties.Count();
5925 for (i = 0; i < count; ++i) {
5926 // Remove all properties.
5927 oldDocument->PropertyTable()->
5928 DeleteAllPropertiesFor(nodesWithProperties[i]);
5932 return rv;
5935 PRUint32 i, count = nodesWithProperties.Count();
5936 if (!sameDocument && oldDocument) {
5937 nsPropertyTable *oldTable = oldDocument->PropertyTable();
5938 nsPropertyTable *newTable = PropertyTable();
5939 for (i = 0; i < count; ++i) {
5940 rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
5941 newTable);
5942 if (NS_FAILED(rv)) {
5943 while (++i < count) {
5944 oldTable->DeleteAllPropertiesFor(nodesWithProperties[i]);
5947 // Disconnect all nodes from their parents.
5948 BlastSubtreeToPieces(adoptedNode);
5950 return rv;
5955 rv = nsNodeUtils::CallUserDataHandlers(nodesWithProperties, this,
5956 nsIDOMUserDataHandler::NODE_ADOPTED,
5957 PR_FALSE);
5958 NS_ENSURE_SUCCESS(rv, rv);
5960 return CallQueryInterface(adoptedNode, aResult);
5963 NS_IMETHODIMP
5964 nsDocument::GetDomConfig(nsIDOMDOMConfiguration **aConfig)
5966 return NS_ERROR_NOT_IMPLEMENTED;
5969 NS_IMETHODIMP
5970 nsDocument::NormalizeDocument()
5972 // We don't support DOMConfigurations yet, so this just
5973 // does a straight shot of normalization.
5974 return Normalize();
5977 NS_IMETHODIMP
5978 nsDocument::RenameNode(nsIDOMNode *aNode,
5979 const nsAString& namespaceURI,
5980 const nsAString& qualifiedName,
5981 nsIDOMNode **aReturn)
5983 return NS_ERROR_NOT_IMPLEMENTED;
5987 NS_IMETHODIMP
5988 nsDocument::GetOwnerDocument(nsIDOMDocument** aOwnerDocument)
5990 *aOwnerDocument = nsnull;
5992 return NS_OK;
5995 nsresult
5996 nsDocument::GetListenerManager(PRBool aCreateIfNotFound,
5997 nsIEventListenerManager** aInstancePtrResult)
5999 if (mListenerManager) {
6000 *aInstancePtrResult = mListenerManager;
6001 NS_ADDREF(*aInstancePtrResult);
6003 return NS_OK;
6005 if (!aCreateIfNotFound) {
6006 *aInstancePtrResult = nsnull;
6007 return NS_OK;
6010 nsresult rv = NS_NewEventListenerManager(getter_AddRefs(mListenerManager));
6011 NS_ENSURE_SUCCESS(rv, rv);
6013 mListenerManager->SetListenerTarget(static_cast<nsIDocument *>(this));
6015 *aInstancePtrResult = mListenerManager;
6016 NS_ADDREF(*aInstancePtrResult);
6018 return NS_OK;
6021 nsresult
6022 nsDocument::GetSystemEventGroup(nsIDOMEventGroup **aGroup)
6024 nsCOMPtr<nsIEventListenerManager> manager;
6025 if (NS_SUCCEEDED(GetListenerManager(PR_TRUE, getter_AddRefs(manager))) &&
6026 manager) {
6027 return manager->GetSystemEventGroupLM(aGroup);
6030 return NS_ERROR_FAILURE;
6033 nsresult
6034 nsDocument::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
6036 aVisitor.mCanHandle = PR_TRUE;
6037 // FIXME! This is a hack to make middle mouse paste working also in Editor.
6038 // Bug 329119
6039 aVisitor.mForceContentDispatch = PR_TRUE;
6041 // Load events must not propagate to |window| object, see bug 335251.
6042 if (aVisitor.mEvent->message != NS_LOAD) {
6043 nsCOMPtr<nsPIDOMEventTarget> parentTarget = do_QueryInterface(GetWindow());
6044 aVisitor.mParentTarget = parentTarget;
6046 return NS_OK;
6049 nsresult
6050 nsDocument::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
6052 return NS_OK;
6055 nsresult
6056 nsDocument::DispatchDOMEvent(nsEvent* aEvent,
6057 nsIDOMEvent* aDOMEvent,
6058 nsPresContext* aPresContext,
6059 nsEventStatus* aEventStatus)
6061 return nsEventDispatcher::DispatchDOMEvent(static_cast<nsINode*>(this),
6062 aEvent, aDOMEvent,
6063 aPresContext, aEventStatus);
6066 nsresult
6067 nsDocument::AddEventListenerByIID(nsIDOMEventListener *aListener,
6068 const nsIID& aIID)
6070 nsCOMPtr<nsIEventListenerManager> manager;
6072 GetListenerManager(PR_TRUE, getter_AddRefs(manager));
6073 if (manager) {
6074 manager->AddEventListenerByIID(aListener, aIID, NS_EVENT_FLAG_BUBBLE);
6075 return NS_OK;
6078 return NS_ERROR_FAILURE;
6081 nsresult
6082 nsDocument::RemoveEventListenerByIID(nsIDOMEventListener *aListener,
6083 const nsIID& aIID)
6085 if (!mListenerManager) {
6086 return NS_ERROR_FAILURE;
6089 mListenerManager->RemoveEventListenerByIID(aListener, aIID,
6090 NS_EVENT_FLAG_BUBBLE);
6091 return NS_OK;
6094 nsresult
6095 nsDocument::AddEventListener(const nsAString& aType,
6096 nsIDOMEventListener* aListener,
6097 PRBool aUseCapture)
6099 return AddEventListener(aType, aListener, aUseCapture,
6100 !nsContentUtils::IsChromeDoc(this));
6103 nsresult
6104 nsDocument::RemoveEventListener(const nsAString& aType,
6105 nsIDOMEventListener* aListener,
6106 PRBool aUseCapture)
6108 return RemoveGroupedEventListener(aType, aListener, aUseCapture, nsnull);
6111 NS_IMETHODIMP
6112 nsDocument::DispatchEvent(nsIDOMEvent* aEvent, PRBool *_retval)
6114 // Obtain a presentation context
6115 nsIPresShell *shell = GetPrimaryShell();
6116 nsCOMPtr<nsPresContext> context;
6117 if (shell) {
6118 context = shell->GetPresContext();
6121 nsEventStatus status = nsEventStatus_eIgnore;
6122 nsresult rv =
6123 nsEventDispatcher::DispatchDOMEvent(static_cast<nsINode*>(this),
6124 nsnull, aEvent, context, &status);
6126 *_retval = (status != nsEventStatus_eConsumeNoDefault);
6127 return rv;
6130 NS_IMETHODIMP
6131 nsDocument::AddGroupedEventListener(const nsAString& aType,
6132 nsIDOMEventListener *aListener,
6133 PRBool aUseCapture,
6134 nsIDOMEventGroup *aEvtGrp)
6136 nsCOMPtr<nsIEventListenerManager> manager;
6138 nsresult rv = GetListenerManager(PR_TRUE, getter_AddRefs(manager));
6139 if (NS_SUCCEEDED(rv) && manager) {
6140 PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
6142 manager->AddEventListenerByType(aListener, aType, flags, aEvtGrp);
6143 return NS_OK;
6146 return rv;
6149 NS_IMETHODIMP
6150 nsDocument::RemoveGroupedEventListener(const nsAString& aType,
6151 nsIDOMEventListener *aListener,
6152 PRBool aUseCapture,
6153 nsIDOMEventGroup *aEvtGrp)
6155 if (!mListenerManager) {
6156 return NS_ERROR_FAILURE;
6159 PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
6161 mListenerManager->RemoveEventListenerByType(aListener, aType, flags,
6162 aEvtGrp);
6163 return NS_OK;
6166 NS_IMETHODIMP
6167 nsDocument::CanTrigger(const nsAString & type, PRBool *_retval)
6169 return NS_ERROR_NOT_IMPLEMENTED;
6172 NS_IMETHODIMP
6173 nsDocument::IsRegisteredHere(const nsAString & type, PRBool *_retval)
6175 return NS_ERROR_NOT_IMPLEMENTED;
6178 NS_IMETHODIMP
6179 nsDocument::AddEventListener(const nsAString& aType,
6180 nsIDOMEventListener *aListener,
6181 PRBool aUseCapture, PRBool aWantsUntrusted)
6183 nsCOMPtr<nsIEventListenerManager> manager;
6184 nsresult rv = GetListenerManager(PR_TRUE, getter_AddRefs(manager));
6185 NS_ENSURE_SUCCESS(rv, rv);
6187 PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
6189 if (aWantsUntrusted) {
6190 flags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
6193 return manager->AddEventListenerByType(aListener, aType, flags, nsnull);
6196 NS_IMETHODIMP
6197 nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn)
6199 NS_ENSURE_ARG_POINTER(aReturn);
6200 *aReturn = nsnull;
6202 // Obtain a presentation shell
6204 nsIPresShell *shell = GetPrimaryShell();
6206 nsPresContext *presContext = nsnull;
6208 if (shell) {
6209 // Retrieve the context
6210 presContext = shell->GetPresContext();
6213 // Create event even without presContext.
6214 return nsEventDispatcher::CreateEvent(presContext, nsnull,
6215 aEventType, aReturn);
6218 NS_IMETHODIMP
6219 nsDocument::CreateEventGroup(nsIDOMEventGroup **aInstancePtrResult)
6221 nsresult rv;
6222 nsCOMPtr<nsIDOMEventGroup> group(do_CreateInstance(kDOMEventGroupCID, &rv));
6223 NS_ENSURE_SUCCESS(rv, rv);
6225 *aInstancePtrResult = group;
6226 NS_ADDREF(*aInstancePtrResult);
6228 return NS_OK;
6231 void
6232 nsDocument::FlushPendingNotifications(mozFlushType aType)
6234 nsCOMPtr<nsIContentSink> sink;
6235 if (mParser) {
6236 sink = mParser->GetContentSink();
6237 } else {
6238 sink = do_QueryReferent(mWeakSink);
6240 // Determine if it is safe to flush the sink notifications
6241 // by determining if it safe to flush all the presshells.
6242 if (sink && (aType == Flush_Content || IsSafeToFlush())) {
6243 sink->FlushPendingNotifications(aType);
6246 // Should we be flushing pending binding constructors in here?
6248 if (aType <= Flush_ContentAndNotify) {
6249 // Nothing to do here
6250 return;
6253 // If we have a parent we must flush the parent too to ensure that our
6254 // container is reflown if its size was changed. But if it's not safe to
6255 // flush ourselves, then don't flush the parent, since that can cause things
6256 // like resizes of our frame's widget, which we can't handle while flushing
6257 // is unsafe.
6258 // Since media queries mean that a size change of our container can
6259 // affect style, we need to promote a style flush on ourself to a
6260 // layout flush on our parent, since we need our container to be the
6261 // correct size to determine the correct style.
6262 if (mParentDocument && IsSafeToFlush()) {
6263 mozFlushType parentType = aType;
6264 if (aType == Flush_Style)
6265 parentType = Flush_Layout;
6266 mParentDocument->FlushPendingNotifications(parentType);
6269 nsPresShellIterator iter(this);
6270 nsCOMPtr<nsIPresShell> shell;
6271 while ((shell = iter.GetNextShell())) {
6272 shell->FlushPendingNotifications(aType);
6276 nsIScriptEventManager*
6277 nsDocument::GetScriptEventManager()
6279 if (!mScriptEventManager) {
6280 mScriptEventManager = new nsScriptEventManager(this);
6281 // automatically AddRefs
6284 return mScriptEventManager;
6287 void
6288 nsDocument::SetXMLDeclaration(const PRUnichar *aVersion,
6289 const PRUnichar *aEncoding,
6290 const PRInt32 aStandalone)
6292 if (!aVersion || *aVersion == '\0') {
6293 mXMLDeclarationBits = 0;
6294 return;
6297 mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
6299 if (aEncoding && *aEncoding != '\0') {
6300 mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
6303 if (aStandalone == 1) {
6304 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
6305 XML_DECLARATION_BITS_STANDALONE_YES;
6307 else if (aStandalone == 0) {
6308 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
6312 void
6313 nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
6314 nsAString& aStandalone)
6316 aVersion.Truncate();
6317 aEncoding.Truncate();
6318 aStandalone.Truncate();
6320 if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
6321 return;
6324 // always until we start supporting 1.1 etc.
6325 aVersion.AssignLiteral("1.0");
6327 if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
6328 // This is what we have stored, not necessarily what was written
6329 // in the original
6330 GetCharacterSet(aEncoding);
6333 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
6334 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
6335 aStandalone.AssignLiteral("yes");
6336 } else {
6337 aStandalone.AssignLiteral("no");
6342 PRBool
6343 nsDocument::IsScriptEnabled()
6345 nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
6346 NS_ENSURE_TRUE(sm, PR_FALSE);
6348 nsIScriptGlobalObject* globalObject = GetScriptGlobalObject();
6349 NS_ENSURE_TRUE(globalObject, PR_FALSE);
6351 nsIScriptContext *scriptContext = globalObject->GetContext();
6352 NS_ENSURE_TRUE(scriptContext, PR_FALSE);
6354 JSContext* cx = (JSContext *) scriptContext->GetNativeContext();
6355 NS_ENSURE_TRUE(cx, PR_FALSE);
6357 PRBool enabled;
6358 nsresult rv = sm->CanExecuteScripts(cx, NodePrincipal(), &enabled);
6359 NS_ENSURE_SUCCESS(rv, PR_FALSE);
6360 return enabled;
6363 nsresult
6364 nsDocument::GetRadioGroup(const nsAString& aName,
6365 nsRadioGroupStruct **aRadioGroup)
6367 nsAutoString tmKey(aName);
6368 if(!IsCaseSensitive())
6369 ToLowerCase(tmKey); //should case-insensitive.
6370 if (mRadioGroups.Get(tmKey, aRadioGroup))
6371 return NS_OK;
6373 nsAutoPtr<nsRadioGroupStruct> radioGroup(new nsRadioGroupStruct());
6374 NS_ENSURE_TRUE(radioGroup, NS_ERROR_OUT_OF_MEMORY);
6375 NS_ENSURE_TRUE(mRadioGroups.Put(tmKey, radioGroup), NS_ERROR_OUT_OF_MEMORY);
6377 *aRadioGroup = radioGroup;
6378 radioGroup.forget();
6380 return NS_OK;
6383 NS_IMETHODIMP
6384 nsDocument::SetCurrentRadioButton(const nsAString& aName,
6385 nsIDOMHTMLInputElement* aRadio)
6387 nsRadioGroupStruct* radioGroup = nsnull;
6388 GetRadioGroup(aName, &radioGroup);
6389 if (radioGroup) {
6390 radioGroup->mSelectedRadioButton = aRadio;
6393 return NS_OK;
6396 NS_IMETHODIMP
6397 nsDocument::GetCurrentRadioButton(const nsAString& aName,
6398 nsIDOMHTMLInputElement** aRadio)
6400 nsRadioGroupStruct* radioGroup = nsnull;
6401 GetRadioGroup(aName, &radioGroup);
6402 if (radioGroup) {
6403 *aRadio = radioGroup->mSelectedRadioButton;
6404 NS_IF_ADDREF(*aRadio);
6407 return NS_OK;
6410 NS_IMETHODIMP
6411 nsDocument::GetPositionInGroup(nsIDOMHTMLInputElement *aRadio,
6412 PRInt32 *aPositionIndex,
6413 PRInt32 *aItemsInGroup)
6415 *aPositionIndex = 0;
6416 *aItemsInGroup = 1;
6417 nsAutoString name;
6418 aRadio->GetName(name);
6419 if (name.IsEmpty()) {
6420 return NS_OK;
6423 nsRadioGroupStruct* radioGroup = nsnull;
6424 nsresult rv = GetRadioGroup(name, &radioGroup);
6425 NS_ENSURE_SUCCESS(rv, rv);
6427 nsCOMPtr<nsIFormControl> radioControl(do_QueryInterface(aRadio));
6428 NS_ASSERTION(radioControl, "Radio button should implement nsIFormControl");
6429 *aPositionIndex = radioGroup->mRadioButtons.IndexOf(radioControl);
6430 NS_ASSERTION(*aPositionIndex >= 0, "Radio button not found in its own group");
6431 *aItemsInGroup = radioGroup->mRadioButtons.Count();
6433 return NS_OK;
6436 NS_IMETHODIMP
6437 nsDocument::GetNextRadioButton(const nsAString& aName,
6438 const PRBool aPrevious,
6439 nsIDOMHTMLInputElement* aFocusedRadio,
6440 nsIDOMHTMLInputElement** aRadioOut)
6442 // XXX Can we combine the HTML radio button method impls of
6443 // nsDocument and nsHTMLFormControl?
6444 // XXX Why is HTML radio button stuff in nsDocument, as
6445 // opposed to nsHTMLDocument?
6446 *aRadioOut = nsnull;
6448 nsRadioGroupStruct* radioGroup = nsnull;
6449 GetRadioGroup(aName, &radioGroup);
6450 if (!radioGroup) {
6451 return NS_ERROR_FAILURE;
6454 // Return the radio button relative to the focused radio button.
6455 // If no radio is focused, get the radio relative to the selected one.
6456 nsCOMPtr<nsIDOMHTMLInputElement> currentRadio;
6457 if (aFocusedRadio) {
6458 currentRadio = aFocusedRadio;
6460 else {
6461 currentRadio = radioGroup->mSelectedRadioButton;
6462 if (!currentRadio) {
6463 return NS_ERROR_FAILURE;
6466 nsCOMPtr<nsIFormControl> radioControl(do_QueryInterface(currentRadio));
6467 PRInt32 index = radioGroup->mRadioButtons.IndexOf(radioControl);
6468 if (index < 0) {
6469 return NS_ERROR_FAILURE;
6472 PRInt32 numRadios = radioGroup->mRadioButtons.Count();
6473 PRBool disabled;
6474 nsCOMPtr<nsIDOMHTMLInputElement> radio;
6475 do {
6476 if (aPrevious) {
6477 if (--index < 0) {
6478 index = numRadios -1;
6481 else if (++index >= numRadios) {
6482 index = 0;
6484 radio = do_QueryInterface(radioGroup->mRadioButtons[index]);
6485 NS_ASSERTION(radio, "mRadioButtons holding a non-radio button");
6486 radio->GetDisabled(&disabled);
6487 } while (disabled && radio != currentRadio);
6489 NS_IF_ADDREF(*aRadioOut = radio);
6490 return NS_OK;
6493 NS_IMETHODIMP
6494 nsDocument::AddToRadioGroup(const nsAString& aName,
6495 nsIFormControl* aRadio)
6497 nsRadioGroupStruct* radioGroup = nsnull;
6498 GetRadioGroup(aName, &radioGroup);
6499 if (radioGroup) {
6500 radioGroup->mRadioButtons.AppendObject(aRadio);
6503 return NS_OK;
6506 NS_IMETHODIMP
6507 nsDocument::RemoveFromRadioGroup(const nsAString& aName,
6508 nsIFormControl* aRadio)
6510 nsRadioGroupStruct* radioGroup = nsnull;
6511 GetRadioGroup(aName, &radioGroup);
6512 if (radioGroup) {
6513 radioGroup->mRadioButtons.RemoveObject(aRadio);
6516 return NS_OK;
6519 NS_IMETHODIMP
6520 nsDocument::WalkRadioGroup(const nsAString& aName,
6521 nsIRadioVisitor* aVisitor,
6522 PRBool aFlushContent)
6524 nsRadioGroupStruct* radioGroup = nsnull;
6525 GetRadioGroup(aName, &radioGroup);
6526 if (!radioGroup) {
6527 return NS_OK;
6530 PRBool stop = PR_FALSE;
6531 for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
6532 aVisitor->Visit(radioGroup->mRadioButtons[i], &stop);
6533 if (stop) {
6534 return NS_OK;
6538 return NS_OK;
6541 void
6542 nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
6544 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
6545 PRTime modDate = LL_ZERO;
6546 nsresult rv;
6548 if (httpChannel) {
6549 nsCAutoString tmp;
6550 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
6551 tmp);
6553 if (NS_SUCCEEDED(rv)) {
6554 PRTime time;
6555 PRStatus st = PR_ParseTimeString(tmp.get(), PR_TRUE, &time);
6556 if (st == PR_SUCCESS) {
6557 modDate = time;
6561 // The misspelled key 'referer' is as per the HTTP spec
6562 rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"),
6563 mReferrer);
6564 if (NS_FAILED(rv)) {
6565 mReferrer.Truncate();
6568 static const char *const headers[] = {
6569 "default-style",
6570 "content-style-type",
6571 "content-language",
6572 "content-disposition",
6573 "refresh",
6574 "x-dns-prefetch-control",
6575 // add more http headers if you need
6576 // XXXbz don't add content-location support without reading bug
6577 // 238654 and its dependencies/dups first.
6581 nsCAutoString headerVal;
6582 const char *const *name = headers;
6583 while (*name) {
6584 rv =
6585 httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
6586 if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
6587 nsCOMPtr<nsIAtom> key = do_GetAtom(*name);
6588 SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
6590 ++name;
6592 } else {
6593 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
6594 if (fileChannel) {
6595 nsCOMPtr<nsIFile> file;
6596 fileChannel->GetFile(getter_AddRefs(file));
6597 if (file) {
6598 PRTime msecs;
6599 rv = file->GetLastModifiedTime(&msecs);
6601 if (NS_SUCCEEDED(rv)) {
6602 PRInt64 intermediateValue;
6603 LL_I2L(intermediateValue, PR_USEC_PER_MSEC);
6604 LL_MUL(modDate, msecs, intermediateValue);
6607 } else {
6608 nsCOMPtr<nsIMultiPartChannel> partChannel = do_QueryInterface(aChannel);
6609 if (partChannel) {
6610 nsCAutoString contentDisp;
6611 rv = partChannel->GetContentDisposition(contentDisp);
6612 if (NS_SUCCEEDED(rv) && !contentDisp.IsEmpty()) {
6613 SetHeaderData(nsGkAtoms::headerContentDisposition,
6614 NS_ConvertASCIItoUTF16(contentDisp));
6620 if (LL_IS_ZERO(modDate)) {
6621 // We got nothing from our attempt to ask nsIFileChannel and
6622 // nsIHttpChannel for the last modified time. Return the current
6623 // time.
6624 modDate = PR_Now();
6627 mLastModified.Truncate();
6628 if (LL_NE(modDate, LL_ZERO)) {
6629 PRExplodedTime prtime;
6630 PR_ExplodeTime(modDate, PR_LocalTimeParameters, &prtime);
6631 // "MM/DD/YYYY hh:mm:ss"
6632 char formatedTime[24];
6633 if (PR_snprintf(formatedTime, sizeof(formatedTime),
6634 "%02ld/%02ld/%04hd %02ld:%02ld:%02ld",
6635 prtime.tm_month + 1, prtime.tm_mday, prtime.tm_year,
6636 prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) {
6637 CopyASCIItoUTF16(nsDependentCString(formatedTime), mLastModified);
6642 nsresult
6643 nsDocument::CreateElem(nsIAtom *aName, nsIAtom *aPrefix, PRInt32 aNamespaceID,
6644 PRBool aDocumentDefaultType, nsIContent **aResult)
6646 #ifdef DEBUG
6647 nsAutoString qName;
6648 if (aPrefix) {
6649 aPrefix->ToString(qName);
6650 qName.Append(':');
6652 const char *name;
6653 aName->GetUTF8String(&name);
6654 AppendUTF8toUTF16(name, qName);
6656 // Note: "a:b:c" is a valid name in non-namespaces XML, and
6657 // nsDocument::CreateElement can call us with such a name and no prefix,
6658 // which would cause an error if we just used PR_TRUE here.
6659 PRBool nsAware = aPrefix != nsnull || aNamespaceID != GetDefaultNamespaceID();
6660 NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
6661 "Don't pass invalid prefixes to nsDocument::CreateElem, "
6662 "check caller.");
6663 #endif
6665 *aResult = nsnull;
6667 PRInt32 elementType = aDocumentDefaultType ? mDefaultElementType :
6668 aNamespaceID;
6670 nsCOMPtr<nsINodeInfo> nodeInfo;
6671 nodeInfo = mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID);
6672 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
6674 return NS_NewElement(aResult, elementType, nodeInfo, PR_FALSE);
6677 PRBool
6678 nsDocument::IsSafeToFlush() const
6680 PRBool isSafeToFlush = PR_TRUE;
6681 nsPresShellIterator iter(const_cast<nsIDocument*>
6682 (static_cast<const nsIDocument*>(this)));
6683 nsCOMPtr<nsIPresShell> shell;
6684 while ((shell = iter.GetNextShell()) && isSafeToFlush) {
6685 shell->IsSafeToFlush(isSafeToFlush);
6687 return isSafeToFlush;
6690 nsresult
6691 nsDocument::Sanitize()
6693 // Sanitize the document by resetting all password fields and any form
6694 // fields with autocomplete=off to their default values. We do this now,
6695 // instead of when the presentation is restored, to offer some protection
6696 // in case there is ever an exploit that allows a cached document to be
6697 // accessed from a different document.
6699 // First locate all input elements, regardless of whether they are
6700 // in a form, and reset the password and autocomplete=off elements.
6702 nsCOMPtr<nsIDOMNodeList> nodes;
6703 nsresult rv = GetElementsByTagName(NS_LITERAL_STRING("input"),
6704 getter_AddRefs(nodes));
6705 NS_ENSURE_SUCCESS(rv, rv);
6707 PRUint32 length = 0;
6708 if (nodes)
6709 nodes->GetLength(&length);
6711 nsCOMPtr<nsIDOMNode> item;
6712 nsAutoString value;
6713 PRUint32 i;
6715 for (i = 0; i < length; ++i) {
6716 nodes->Item(i, getter_AddRefs(item));
6717 NS_ASSERTION(item, "null item in node list!");
6719 nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(item);
6720 if (!input)
6721 continue;
6723 PRBool resetValue = PR_FALSE;
6725 input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
6726 if (value.LowerCaseEqualsLiteral("off")) {
6727 resetValue = PR_TRUE;
6728 } else {
6729 input->GetType(value);
6730 if (value.LowerCaseEqualsLiteral("password"))
6731 resetValue = PR_TRUE;
6734 if (resetValue) {
6735 nsCOMPtr<nsIFormControl> fc = do_QueryInterface(input);
6736 fc->Reset();
6740 // Now locate all _form_ elements that have autocomplete=off and reset them
6741 rv = GetElementsByTagName(NS_LITERAL_STRING("form"), getter_AddRefs(nodes));
6742 NS_ENSURE_SUCCESS(rv, rv);
6744 length = 0;
6745 if (nodes)
6746 nodes->GetLength(&length);
6748 for (i = 0; i < length; ++i) {
6749 nodes->Item(i, getter_AddRefs(item));
6750 NS_ASSERTION(item, "null item in nodelist");
6752 nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(item);
6753 if (!form)
6754 continue;
6756 form->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
6757 if (value.LowerCaseEqualsLiteral("off"))
6758 form->Reset();
6761 return NS_OK;
6764 struct SubDocEnumArgs
6766 nsIDocument::nsSubDocEnumFunc callback;
6767 void *data;
6770 static PLDHashOperator
6771 SubDocHashEnum(PLDHashTable *table, PLDHashEntryHdr *hdr,
6772 PRUint32 number, void *arg)
6774 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
6775 SubDocEnumArgs *args = static_cast<SubDocEnumArgs*>(arg);
6777 nsIDocument *subdoc = entry->mSubDocument;
6778 PRBool next = subdoc ? args->callback(subdoc, args->data) : PR_TRUE;
6780 return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
6783 void
6784 nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
6786 if (mSubDocuments) {
6787 SubDocEnumArgs args = { aCallback, aData };
6788 PL_DHashTableEnumerate(mSubDocuments, SubDocHashEnum, &args);
6792 static PLDHashOperator
6793 CanCacheSubDocument(PLDHashTable *table, PLDHashEntryHdr *hdr,
6794 PRUint32 number, void *arg)
6796 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
6797 PRBool *canCacheArg = static_cast<PRBool*>(arg);
6799 nsIDocument *subdoc = entry->mSubDocument;
6801 // The aIgnoreRequest we were passed is only for us, so don't pass it on.
6802 PRBool canCache = subdoc ? subdoc->CanSavePresentation(nsnull) : PR_FALSE;
6803 if (!canCache) {
6804 *canCacheArg = PR_FALSE;
6805 return PL_DHASH_STOP;
6808 return PL_DHASH_NEXT;
6811 #ifdef DEBUG_bryner
6812 #define DEBUG_PAGE_CACHE
6813 #endif
6815 PRBool
6816 nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
6818 // Check our event listener manager for unload/beforeunload listeners.
6819 nsCOMPtr<nsPIDOMEventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
6820 if (piTarget) {
6821 nsCOMPtr<nsIEventListenerManager> manager;
6822 piTarget->GetListenerManager(PR_FALSE, getter_AddRefs(manager));
6823 if (manager && manager->HasUnloadListeners()) {
6824 return PR_FALSE;
6828 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
6829 if (loadGroup) {
6830 nsCOMPtr<nsISimpleEnumerator> requests;
6831 loadGroup->GetRequests(getter_AddRefs(requests));
6833 PRBool hasMore = PR_FALSE;
6835 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
6836 nsCOMPtr<nsISupports> elem;
6837 requests->GetNext(getter_AddRefs(elem));
6839 nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
6840 if (request && request != aNewRequest) {
6841 #ifdef DEBUG_PAGE_CACHE
6842 nsCAutoString requestName, docSpec;
6843 request->GetName(requestName);
6844 if (mDocumentURI)
6845 mDocumentURI->GetSpec(docSpec);
6847 printf("document %s has request %s\n",
6848 docSpec.get(), requestName.get());
6849 #endif
6850 return PR_FALSE;
6855 PRBool canCache = PR_TRUE;
6856 if (mSubDocuments)
6857 PL_DHashTableEnumerate(mSubDocuments, CanCacheSubDocument, &canCache);
6859 return canCache;
6862 void
6863 nsDocument::Destroy()
6865 // The ContentViewer wants to release the document now. So, tell our content
6866 // to drop any references to the document so that it can be destroyed.
6867 if (mIsGoingAway)
6868 return;
6870 mIsGoingAway = PR_TRUE;
6872 RemovedFromDocShell();
6874 PRUint32 i, count = mChildren.ChildCount();
6875 for (i = 0; i < count; ++i) {
6876 mChildren.ChildAt(i)->DestroyContent();
6879 mLayoutHistoryState = nsnull;
6881 nsContentList::OnDocumentDestroy(this);
6883 // Shut down our external resource map. We might not need this for
6884 // leak-fixing if we fix DocumentViewerImpl to do cycle-collection, but
6885 // tearing down all those frame trees right now is the right thing to do.
6886 mExternalResourceMap.Shutdown();
6888 // XXX We really should let cycle collection do this, but that currently still
6889 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
6890 ReleaseWrapper();
6893 void
6894 nsDocument::RemovedFromDocShell()
6896 if (mRemovedFromDocShell)
6897 return;
6899 mRemovedFromDocShell = PR_TRUE;
6901 PRUint32 i, count = mChildren.ChildCount();
6902 for (i = 0; i < count; ++i) {
6903 mChildren.ChildAt(i)->SaveSubtreeState();
6907 already_AddRefed<nsILayoutHistoryState>
6908 nsDocument::GetLayoutHistoryState() const
6910 nsILayoutHistoryState* state = nsnull;
6911 if (!mScriptGlobalObject) {
6912 NS_IF_ADDREF(state = mLayoutHistoryState);
6913 } else {
6914 nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocumentContainer));
6915 if (docShell) {
6916 docShell->GetLayoutHistoryState(&state);
6920 return state;
6923 void
6924 nsDocument::BlockOnload()
6926 if (mDisplayDocument) {
6927 mDisplayDocument->BlockOnload();
6928 return;
6931 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
6932 // -- it's not ours.
6933 if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
6934 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
6935 if (loadGroup) {
6936 loadGroup->AddRequest(mOnloadBlocker, nsnull);
6939 ++mOnloadBlockCount;
6942 void
6943 nsDocument::UnblockOnload(PRBool aFireSync)
6945 if (mDisplayDocument) {
6946 mDisplayDocument->UnblockOnload(aFireSync);
6947 return;
6950 if (mOnloadBlockCount == 0) {
6951 NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call");
6952 return;
6955 --mOnloadBlockCount;
6957 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
6958 // -- it's not ours.
6959 if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
6960 if (aFireSync) {
6961 // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
6962 ++mOnloadBlockCount;
6963 DoUnblockOnload();
6964 } else {
6965 PostUnblockOnloadEvent();
6970 class nsUnblockOnloadEvent : public nsRunnable {
6971 public:
6972 nsUnblockOnloadEvent(nsDocument *doc) : mDoc(doc) {}
6973 NS_IMETHOD Run() {
6974 mDoc->DoUnblockOnload();
6975 return NS_OK;
6977 private:
6978 nsRefPtr<nsDocument> mDoc;
6981 void
6982 nsDocument::PostUnblockOnloadEvent()
6984 nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
6985 nsresult rv = NS_DispatchToCurrentThread(evt);
6986 if (NS_SUCCEEDED(rv)) {
6987 // Stabilize block count so we don't post more events while this one is up
6988 ++mOnloadBlockCount;
6989 } else {
6990 NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
6994 void
6995 nsDocument::DoUnblockOnload()
6997 NS_PRECONDITION(!mDisplayDocument,
6998 "Shouldn't get here for resource document");
6999 NS_PRECONDITION(mOnloadBlockCount != 0,
7000 "Shouldn't have a count of zero here, since we stabilized in "
7001 "PostUnblockOnloadEvent");
7003 --mOnloadBlockCount;
7005 if (mOnloadBlockCount != 0) {
7006 // We blocked again after the last unblock. Nothing to do here. We'll
7007 // post a new event when we unblock again.
7008 return;
7011 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
7012 // -- it's not ours.
7013 if (mScriptGlobalObject) {
7014 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
7015 if (loadGroup) {
7016 loadGroup->RemoveRequest(mOnloadBlocker, nsnull, NS_OK);
7021 /* See if document is a child of this. If so, return the frame element in this
7022 * document that holds currentDoc (or an ancestor). */
7023 already_AddRefed<nsIDOMElement>
7024 nsDocument::CheckAncestryAndGetFrame(nsIDocument* aDocument) const
7026 nsIDocument* parentDoc;
7027 for (parentDoc = aDocument->GetParentDocument();
7028 parentDoc != static_cast<const nsIDocument* const>(this);
7029 parentDoc = parentDoc->GetParentDocument()) {
7030 if (!parentDoc) {
7031 return nsnull;
7034 aDocument = parentDoc;
7037 // In a child document. Get the appropriate frame.
7038 nsPIDOMWindow* currentWindow = aDocument->GetWindow();
7039 if (!currentWindow) {
7040 return nsnull;
7042 nsIDOMElement* frameElement = currentWindow->GetFrameElementInternal();
7043 if (!frameElement) {
7044 return nsnull;
7047 // Sanity check result
7048 nsCOMPtr<nsIDOMDocument> domDocument;
7049 frameElement->GetOwnerDocument(getter_AddRefs(domDocument));
7050 if (domDocument != this) {
7051 NS_ERROR("Child documents should live in windows the parent owns");
7052 return nsnull;
7055 NS_ADDREF(frameElement);
7056 return frameElement;
7059 void
7060 nsDocument::DispatchEventToWindow(nsEvent *aEvent)
7062 nsPIDOMWindow *window = GetWindow();
7063 if (!window)
7064 return;
7066 aEvent->target = static_cast<nsIDocument*>(this);
7067 nsEventDispatcher::Dispatch(window, nsnull, aEvent);
7070 void
7071 nsDocument::OnPageShow(PRBool aPersisted)
7073 mVisible = PR_TRUE;
7074 UpdateLinkMap();
7076 nsIContent* root = GetRootContent();
7077 if (aPersisted && root) {
7078 // Send out notifications that our <link> elements are attached.
7079 nsRefPtr<nsContentList> links = NS_GetContentList(root,
7080 nsGkAtoms::link,
7081 kNameSpaceID_Unknown);
7083 if (links) {
7084 PRUint32 linkCount = links->Length(PR_TRUE);
7085 for (PRUint32 i = 0; i < linkCount; ++i) {
7086 nsCOMPtr<nsILink> link = do_QueryInterface(links->Item(i, PR_FALSE));
7087 if (link) {
7088 link->LinkAdded();
7094 nsPageTransitionEvent event(PR_TRUE, NS_PAGE_SHOW, aPersisted);
7095 DispatchEventToWindow(&event);
7098 void
7099 nsDocument::OnPageHide(PRBool aPersisted)
7101 // Send out notifications that our <link> elements are detached,
7102 // but only if this is not a full unload.
7103 nsIContent* root = GetRootContent();
7104 if (aPersisted && root) {
7105 nsRefPtr<nsContentList> links = NS_GetContentList(root,
7106 nsGkAtoms::link,
7107 kNameSpaceID_Unknown);
7109 if (links) {
7110 PRUint32 linkCount = links->Length(PR_TRUE);
7111 for (PRUint32 i = 0; i < linkCount; ++i) {
7112 nsCOMPtr<nsILink> link = do_QueryInterface(links->Item(i, PR_FALSE));
7113 if (link) {
7114 link->LinkRemoved();
7120 // Now send out a PageHide event.
7121 nsPageTransitionEvent event(PR_TRUE, NS_PAGE_HIDE, aPersisted);
7122 DispatchEventToWindow(&event);
7124 mVisible = PR_FALSE;
7127 void
7128 nsDocument::WillDispatchMutationEvent(nsINode* aTarget)
7130 NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
7131 mSubtreeModifiedTargets.Count() == 0,
7132 "mSubtreeModifiedTargets not cleared after dispatching?");
7133 ++mSubtreeModifiedDepth;
7134 if (aTarget) {
7135 // MayDispatchMutationEvent is often called just before this method,
7136 // so it has already appended the node to mSubtreeModifiedTargets.
7137 PRInt32 count = mSubtreeModifiedTargets.Count();
7138 if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
7139 mSubtreeModifiedTargets.AppendObject(aTarget);
7144 void
7145 nsDocument::MutationEventDispatched(nsINode* aTarget)
7147 --mSubtreeModifiedDepth;
7148 if (mSubtreeModifiedDepth == 0) {
7149 PRInt32 count = mSubtreeModifiedTargets.Count();
7150 if (!count) {
7151 return;
7154 nsCOMPtr<nsPIDOMWindow> window;
7155 window = do_QueryInterface(GetScriptGlobalObject());
7156 if (window &&
7157 !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
7158 mSubtreeModifiedTargets.Clear();
7159 return;
7162 nsCOMArray<nsINode> realTargets;
7163 for (PRInt32 i = 0; i < count; ++i) {
7164 nsINode* possibleTarget = mSubtreeModifiedTargets[i];
7165 nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
7166 if (content && content->IsInNativeAnonymousSubtree()) {
7167 continue;
7170 nsINode* commonAncestor = nsnull;
7171 PRInt32 realTargetCount = realTargets.Count();
7172 for (PRInt32 j = 0; j < realTargetCount; ++j) {
7173 commonAncestor =
7174 nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
7175 if (commonAncestor) {
7176 realTargets.ReplaceObjectAt(commonAncestor, j);
7177 break;
7180 if (!commonAncestor) {
7181 realTargets.AppendObject(possibleTarget);
7185 mSubtreeModifiedTargets.Clear();
7187 PRInt32 realTargetCount = realTargets.Count();
7188 for (PRInt32 k = 0; k < realTargetCount; ++k) {
7189 mozAutoRemovableBlockerRemover blockerRemover;
7191 nsMutationEvent mutation(PR_TRUE, NS_MUTATION_SUBTREEMODIFIED);
7192 nsEventDispatcher::Dispatch(realTargets[k], nsnull, &mutation);
7197 static PRUint32 GetURIHash(nsIURI* aURI)
7199 nsCAutoString str;
7200 aURI->GetSpec(str);
7201 return HashString(str);
7204 void
7205 nsDocument::AddStyleRelevantLink(nsIContent* aContent, nsIURI* aURI)
7207 nsUint32ToContentHashEntry* entry = mLinkMap.PutEntry(GetURIHash(aURI));
7208 if (!entry) // out of memory?
7209 return;
7210 entry->PutContent(aContent);
7213 void
7214 nsDocument::ForgetLink(nsIContent* aContent)
7216 // Important optimization! If the link map is empty (as it will be
7217 // during teardown because we destroy the map early), then stop
7218 // now before we waste time constructing a URI object.
7219 if (mLinkMap.Count() == 0)
7220 return;
7222 nsCOMPtr<nsIURI> uri;
7223 if (!aContent->IsLink(getter_AddRefs(uri)))
7224 return;
7225 PRUint32 hash = GetURIHash(uri);
7226 nsUint32ToContentHashEntry* entry = mLinkMap.GetEntry(hash);
7227 if (!entry)
7228 return;
7230 entry->RemoveContent(aContent);
7231 if (entry->IsEmpty()) {
7232 // Remove the entry and allow the table to resize, in case
7233 // a lot of links are being removed from the document or modified
7234 mLinkMap.RemoveEntry(hash);
7238 class URIVisitNotifier : public nsUint32ToContentHashEntry::Visitor
7240 public:
7241 nsCAutoString matchURISpec;
7242 nsCOMArray<nsIContent> contentVisited;
7244 virtual void Visit(nsIContent* aContent) {
7245 // Ensure that the URIs really match before we try to do anything
7246 nsCOMPtr<nsIURI> uri;
7247 if (!aContent->IsLink(getter_AddRefs(uri))) {
7248 NS_ERROR("Should have found a URI for content in the link map");
7249 return;
7251 nsCAutoString spec;
7252 uri->GetSpec(spec);
7253 // We use nsCString::Equals here instead of nsIURI::Equals because
7254 // history matching is all based on spec equality
7255 if (!spec.Equals(matchURISpec))
7256 return;
7258 // Throw away the cached link state so it gets refetched by the style
7259 // system
7260 nsCOMPtr<nsILink> link = do_QueryInterface(aContent);
7261 if (link) {
7262 link->SetLinkState(eLinkState_Unknown);
7264 contentVisited.AppendObject(aContent);
7268 void
7269 nsDocument::NotifyURIVisitednessChanged(nsIURI* aURI)
7271 if (!mVisible) {
7272 mVisitednessChangedURIs.AppendObject(aURI);
7273 return;
7276 nsUint32ToContentHashEntry* entry = mLinkMap.GetEntry(GetURIHash(aURI));
7277 if (!entry)
7278 return;
7280 URIVisitNotifier visitor;
7281 aURI->GetSpec(visitor.matchURISpec);
7282 entry->VisitContent(&visitor);
7283 for (PRUint32 count = visitor.contentVisited.Count(), i = 0; i < count; ++i) {
7284 ContentStatesChanged(visitor.contentVisited[i],
7285 nsnull, NS_EVENT_STATE_VISITED);
7289 void
7290 nsDocument::DestroyLinkMap()
7292 mVisitednessChangedURIs.Clear();
7293 mLinkMap.Clear();
7296 void
7297 nsDocument::UpdateLinkMap()
7299 NS_ASSERTION(mVisible,
7300 "Should only be updating the link map in visible documents");
7301 if (!mVisible)
7302 return;
7304 PRInt32 count = mVisitednessChangedURIs.Count();
7305 for (PRInt32 i = 0; i < count; ++i) {
7306 NotifyURIVisitednessChanged(mVisitednessChangedURIs[i]);
7308 mVisitednessChangedURIs.Clear();
7311 NS_IMETHODIMP
7312 nsDocument::GetScriptTypeID(PRUint32 *aScriptType)
7314 NS_ERROR("No default script type here - ask some element");
7315 return nsIProgrammingLanguage::UNKNOWN;
7318 NS_IMETHODIMP
7319 nsDocument::SetScriptTypeID(PRUint32 aScriptType)
7321 NS_ERROR("Can't change default script type for a document");
7322 return NS_ERROR_NOT_IMPLEMENTED;
7325 NS_IMETHODIMP
7326 nsDocument::QuerySelector(const nsAString& aSelector,
7327 nsIDOMElement **aReturn)
7329 return nsGenericElement::doQuerySelector(this, aSelector, aReturn);
7332 NS_IMETHODIMP
7333 nsDocument::QuerySelectorAll(const nsAString& aSelector,
7334 nsIDOMNodeList **aReturn)
7336 return nsGenericElement::doQuerySelectorAll(this, aSelector, aReturn);
7339 nsresult
7340 nsDocument::CloneDocHelper(nsDocument* clone) const
7342 // Init document
7343 nsresult rv = clone->Init();
7344 NS_ENSURE_SUCCESS(rv, rv);
7346 // Set URI/principal
7347 clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI());
7348 // Must set the principal first, since SetBaseURI checks it.
7349 clone->SetPrincipal(NodePrincipal());
7350 rv = clone->SetBaseURI(nsIDocument::GetBaseURI());
7351 NS_ENSURE_SUCCESS(rv, rv);
7353 // Set scripting object
7354 PRBool hasHadScriptObject = PR_TRUE;
7355 nsIScriptGlobalObject* scriptObject =
7356 GetScriptHandlingObject(hasHadScriptObject);
7357 NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
7358 clone->SetScriptHandlingObject(scriptObject);
7360 // Make the clone a data document
7361 clone->SetLoadedAsData(PR_TRUE);
7363 // Misc state
7365 // State from nsIDocument
7366 clone->mCharacterSet = mCharacterSet;
7367 clone->mCharacterSetSource = mCharacterSetSource;
7368 clone->mCompatMode = mCompatMode;
7369 clone->mBidiOptions = mBidiOptions;
7370 clone->mContentLanguage = mContentLanguage;
7371 clone->mContentType = mContentType;
7372 clone->mSecurityInfo = mSecurityInfo;
7374 // State from nsDocument
7375 clone->mIsRegularHTML = mIsRegularHTML;
7376 clone->mXMLDeclarationBits = mXMLDeclarationBits;
7377 clone->mBaseTarget = mBaseTarget;
7379 return NS_OK;