Deal with a dead docshell during document.open. bug 435128, r+sr=jst
[mozilla-central.git] / content / html / document / src / nsHTMLDocument.cpp
blob9ba307ce26fb7db123391ed1543a68f21347b515
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 et tw=80: */
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 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Kathleen Brade <brade@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
40 #include "nsICharsetAlias.h"
42 #include "nsCOMPtr.h"
43 #include "nsXPIDLString.h"
44 #include "nsPrintfCString.h"
45 #include "nsReadableUtils.h"
46 #include "nsUnicharUtils.h"
47 #include "nsHTMLDocument.h"
48 #include "nsIParserFilter.h"
49 #include "nsIHTMLContentSink.h"
50 #include "nsIXMLContentSink.h"
51 #include "nsHTMLParts.h"
52 #include "nsHTMLStyleSheet.h"
53 #include "nsGkAtoms.h"
54 #include "nsIPresShell.h"
55 #include "nsPresContext.h"
56 #include "nsIDOMNode.h" // for Find
57 #include "nsIDOMNodeList.h"
58 #include "nsIDOMElement.h"
59 #include "nsIDOMText.h"
60 #include "nsIDOMComment.h"
61 #include "nsIDOMDOMImplementation.h"
62 #include "nsIDOMDocumentType.h"
63 #include "nsPIDOMWindow.h"
64 #include "nsIDOMHTMLFormElement.h"
65 #include "nsDOMString.h"
66 #include "nsIStreamListener.h"
67 #include "nsIURI.h"
68 #include "nsIIOService.h"
69 #include "nsNetUtil.h"
70 #include "nsIContentViewerContainer.h"
71 #include "nsIContentViewer.h"
72 #include "nsIMarkupDocumentViewer.h"
73 #include "nsIDocShell.h"
74 #include "nsIDocShellTreeItem.h"
75 #include "nsDocShellLoadTypes.h"
76 #include "nsIWebNavigation.h"
77 #include "nsIBaseWindow.h"
78 #include "nsIWebShellServices.h"
79 #include "nsIScriptContext.h"
80 #include "nsIXPConnect.h"
81 #include "nsContentList.h"
82 #include "nsDOMError.h"
83 #include "nsIPrincipal.h"
84 #include "nsIScriptSecurityManager.h"
85 #include "nsAttrName.h"
86 #include "nsNodeUtils.h"
88 #include "nsNetCID.h"
89 #include "nsIIOService.h"
90 #include "nsICookieService.h"
92 #include "nsIServiceManager.h"
93 #include "nsIConsoleService.h"
94 #include "nsIComponentManager.h"
95 #include "nsParserCIID.h"
96 #include "nsIDOMHTMLElement.h"
97 #include "nsIDOMHTMLMapElement.h"
98 #include "nsIDOMHTMLBodyElement.h"
99 #include "nsINameSpaceManager.h"
100 #include "nsGenericHTMLElement.h"
101 #include "nsGenericDOMNodeList.h"
102 #include "nsICSSLoader.h"
103 #include "nsIHttpChannel.h"
104 #include "nsIFile.h"
105 #include "nsIEventListenerManager.h"
106 #include "nsISelectElement.h"
107 #include "nsFrameSelection.h"
108 #include "nsISelectionPrivate.h"//for toStringwithformat code
110 #include "nsICharsetDetector.h"
111 #include "nsICharsetDetectionAdaptor.h"
112 #include "nsCharsetDetectionAdaptorCID.h"
113 #include "nsICharsetAlias.h"
114 #include "nsContentUtils.h"
115 #include "nsJSUtils.h"
116 #include "nsIDocumentCharsetInfo.h"
117 #include "nsIDocumentEncoder.h" //for outputting selection
118 #include "nsICharsetResolver.h"
119 #include "nsICachingChannel.h"
120 #include "nsICacheEntryDescriptor.h"
121 #include "nsIJSContextStack.h"
122 #include "nsIDocumentViewer.h"
123 #include "nsIWyciwygChannel.h"
124 #include "nsIScriptElement.h"
125 #include "nsIScriptError.h"
126 #include "nsIMutableArray.h"
127 #include "nsArrayUtils.h"
128 #include "nsIEffectiveTLDService.h"
129 #include "nsIEventStateManager.h"
131 #include "nsIPrompt.h"
132 //AHMED 12-2
133 #include "nsBidiUtils.h"
135 #include "nsIEditingSession.h"
136 #include "nsIEditor.h"
137 #include "nsNodeInfoManager.h"
138 #include "nsIEditor.h"
139 #include "nsIEditorDocShell.h"
140 #include "nsIEditorStyleSheets.h"
141 #include "nsIInlineSpellChecker.h"
142 #include "nsRange.h"
143 #include "mozAutoDocUpdate.h"
145 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
147 #define DETECTOR_CONTRACTID_MAX 127
148 static char g_detector_contractid[DETECTOR_CONTRACTID_MAX + 1];
149 static PRBool gInitDetector = PR_FALSE;
150 static PRBool gPlugDetector = PR_FALSE;
152 #include "prmem.h"
153 #include "prtime.h"
155 // Find/Search Includes
156 const PRInt32 kForward = 0;
157 const PRInt32 kBackward = 1;
159 //#define DEBUG_charset
161 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
163 PRUint32 nsHTMLDocument::gWyciwygSessionCnt = 0;
165 // this function will return false if the command is not recognized
166 // inCommandID will be converted as necessary for internal operations
167 // inParam will be converted as necessary for internal operations
168 // outParam will be Empty if no parameter is needed or if returning a boolean
169 // outIsBoolean will determine whether to send param as a boolean or string
170 // outBooleanParam will not be set unless outIsBoolean
171 static PRBool ConvertToMidasInternalCommand(const nsAString & inCommandID,
172 const nsAString & inParam,
173 nsACString& outCommandID,
174 nsACString& outParam,
175 PRBool& isBoolean,
176 PRBool& boolValue);
178 static PRBool ConvertToMidasInternalCommand(const nsAString & inCommandID,
179 nsACString& outCommandID);
180 static int PR_CALLBACK
181 MyPrefChangedCallback(const char*aPrefName, void* instance_data)
183 const nsAdoptingString& detector_name =
184 nsContentUtils::GetLocalizedStringPref("intl.charset.detector");
186 if (detector_name.Length() > 0) {
187 PL_strncpy(g_detector_contractid, NS_CHARSET_DETECTOR_CONTRACTID_BASE,
188 DETECTOR_CONTRACTID_MAX);
189 PL_strncat(g_detector_contractid,
190 NS_ConvertUTF16toUTF8(detector_name).get(),
191 DETECTOR_CONTRACTID_MAX);
192 gPlugDetector = PR_TRUE;
193 } else {
194 g_detector_contractid[0]=0;
195 gPlugDetector = PR_FALSE;
198 return 0;
201 // ==================================================================
202 // =
203 // ==================================================================
204 nsresult
205 NS_NewHTMLDocument(nsIDocument** aInstancePtrResult)
207 nsHTMLDocument* doc = new nsHTMLDocument();
208 NS_ENSURE_TRUE(doc, NS_ERROR_OUT_OF_MEMORY);
210 NS_ADDREF(doc);
211 nsresult rv = doc->Init();
213 if (NS_FAILED(rv)) {
214 NS_RELEASE(doc);
217 *aInstancePtrResult = doc;
219 return rv;
222 // NOTE! nsDocument::operator new() zeroes out all members, so don't
223 // bother initializing members to 0.
225 nsHTMLDocument::nsHTMLDocument()
226 : nsDocument("text/html")
228 // NOTE! nsDocument::operator new() zeroes out all members, so don't
229 // bother initializing members to 0.
231 mIsRegularHTML = PR_TRUE;
232 mDefaultElementType = kNameSpaceID_XHTML;
233 mCompatMode = eCompatibility_NavQuirks;
236 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLDocument)
238 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLDocument, nsDocument)
239 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mImageMaps)
240 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mImages)
241 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mApplets)
242 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEmbeds)
243 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLinks)
244 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mAnchors)
245 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFragmentParser)
246 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mForms, nsIDOMNodeList)
247 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mFormControls,
248 nsIDOMNodeList)
249 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
251 NS_IMPL_ADDREF_INHERITED(nsHTMLDocument, nsDocument)
252 NS_IMPL_RELEASE_INHERITED(nsHTMLDocument, nsDocument)
255 // QueryInterface implementation for nsHTMLDocument
256 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLDocument)
257 NS_INTERFACE_TABLE_INHERITED3(nsHTMLDocument,
258 nsIHTMLDocument,
259 nsIDOMHTMLDocument,
260 nsIDOMNSHTMLDocument)
261 NS_INTERFACE_TABLE_TO_MAP_SEGUE
262 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLDocument)
263 NS_INTERFACE_MAP_END_INHERITING(nsDocument)
266 nsresult
267 nsHTMLDocument::Init()
269 nsresult rv = nsDocument::Init();
270 NS_ENSURE_SUCCESS(rv, rv);
272 // Now reset the case-sensitivity of the CSSLoader, since we default
273 // to being HTML, not XHTML. Also, reset the compatibility mode to
274 // match our compat mode.
275 CSSLoader()->SetCaseSensitive(IsXHTML());
276 CSSLoader()->SetCompatibilityMode(mCompatMode);
278 PrePopulateIdentifierMap();
279 return NS_OK;
283 void
284 nsHTMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
286 nsDocument::Reset(aChannel, aLoadGroup);
288 if (aChannel) {
289 aChannel->GetLoadFlags(&mLoadFlags);
293 void
294 nsHTMLDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
295 nsIPrincipal* aPrincipal)
297 mLoadFlags = nsIRequest::LOAD_NORMAL;
299 nsDocument::ResetToURI(aURI, aLoadGroup, aPrincipal);
301 PrePopulateIdentifierMap();
303 mImages = nsnull;
304 mApplets = nsnull;
305 mEmbeds = nsnull;
306 mLinks = nsnull;
307 mAnchors = nsnull;
309 mImageMaps.Clear();
310 mForms = nsnull;
312 NS_ASSERTION(!mWyciwygChannel,
313 "nsHTMLDocument::Reset() - Wyciwyg Channel still exists!");
315 mWyciwygChannel = nsnull;
317 // Make the content type default to "text/html", we are a HTML
318 // document, after all. Once we start getting data, this may be
319 // changed.
320 mContentType = "text/html";
323 nsStyleSet::sheetType
324 nsHTMLDocument::GetAttrSheetType()
326 if (IsXHTML()) {
327 return nsDocument::GetAttrSheetType();
330 return nsStyleSet::eHTMLPresHintSheet;
333 nsresult
334 nsHTMLDocument::CreateShell(nsPresContext* aContext,
335 nsIViewManager* aViewManager,
336 nsStyleSet* aStyleSet,
337 nsIPresShell** aInstancePtrResult)
339 return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode,
340 aInstancePtrResult);
343 // The following Try*Charset will return PR_FALSE only if the charset source
344 // should be considered (ie. aCharsetSource < thisCharsetSource) but we failed
345 // to get the charset from this source.
347 PRBool
348 nsHTMLDocument::TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV,
349 PRInt32& aCharsetSource, nsACString& aCharset)
351 if (aMarkupDV) {
352 PRInt32 requestCharsetSource;
353 nsresult rv = aMarkupDV->GetHintCharacterSetSource(&requestCharsetSource);
355 if(NS_SUCCEEDED(rv) && kCharsetUninitialized != requestCharsetSource) {
356 nsCAutoString requestCharset;
357 rv = aMarkupDV->GetHintCharacterSet(requestCharset);
358 aMarkupDV->SetHintCharacterSetSource((PRInt32)(kCharsetUninitialized));
360 if(requestCharsetSource <= aCharsetSource)
361 return PR_TRUE;
363 if(NS_SUCCEEDED(rv)) {
364 aCharsetSource = requestCharsetSource;
365 aCharset = requestCharset;
367 return PR_TRUE;
371 return PR_FALSE;
375 PRBool
376 nsHTMLDocument::TryUserForcedCharset(nsIMarkupDocumentViewer* aMarkupDV,
377 nsIDocumentCharsetInfo* aDocInfo,
378 PRInt32& aCharsetSource,
379 nsACString& aCharset)
381 nsresult rv = NS_OK;
383 if(kCharsetFromUserForced <= aCharsetSource)
384 return PR_TRUE;
386 nsCAutoString forceCharsetFromDocShell;
387 if (aMarkupDV) {
388 rv = aMarkupDV->GetForceCharacterSet(forceCharsetFromDocShell);
391 if(NS_SUCCEEDED(rv) && !forceCharsetFromDocShell.IsEmpty()) {
392 aCharset = forceCharsetFromDocShell;
393 //TODO: we should define appropriate constant for force charset
394 aCharsetSource = kCharsetFromUserForced;
395 } else if (aDocInfo) {
396 nsCOMPtr<nsIAtom> csAtom;
397 aDocInfo->GetForcedCharset(getter_AddRefs(csAtom));
398 if (csAtom) {
399 csAtom->ToUTF8String(aCharset);
400 aCharsetSource = kCharsetFromUserForced;
401 aDocInfo->SetForcedCharset(nsnull);
402 return PR_TRUE;
406 return PR_FALSE;
409 PRBool
410 nsHTMLDocument::TryCacheCharset(nsICacheEntryDescriptor* aCacheDescriptor,
411 PRInt32& aCharsetSource,
412 nsACString& aCharset)
414 nsresult rv;
416 if (kCharsetFromCache <= aCharsetSource) {
417 return PR_TRUE;
420 nsXPIDLCString cachedCharset;
421 rv = aCacheDescriptor->GetMetaDataElement("charset",
422 getter_Copies(cachedCharset));
423 if (NS_SUCCEEDED(rv) && !cachedCharset.IsEmpty())
425 aCharset = cachedCharset;
426 aCharsetSource = kCharsetFromCache;
428 return PR_TRUE;
431 return PR_FALSE;
434 PRBool
435 nsHTMLDocument::TryBookmarkCharset(nsIDocShell* aDocShell,
436 nsIChannel* aChannel,
437 PRInt32& aCharsetSource,
438 nsACString& aCharset)
440 if (kCharsetFromBookmarks <= aCharsetSource) {
441 return PR_TRUE;
444 if (!aChannel) {
445 return PR_FALSE;
448 nsCOMPtr<nsICharsetResolver> bookmarksResolver =
449 do_GetService("@mozilla.org/embeddor.implemented/bookmark-charset-resolver;1");
451 if (!bookmarksResolver) {
452 return PR_FALSE;
455 PRBool wantCharset; // ignored for now
456 nsCAutoString charset;
457 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(aDocShell));
458 nsCOMPtr<nsISupports> closure;
459 nsresult rv = bookmarksResolver->RequestCharset(webNav,
460 aChannel,
461 &wantCharset,
462 getter_AddRefs(closure),
463 charset);
464 // FIXME: Bug 337970
465 NS_ASSERTION(!wantCharset, "resolved charset notification not implemented!");
467 if (NS_SUCCEEDED(rv) && !charset.IsEmpty()) {
468 aCharset = charset;
469 aCharsetSource = kCharsetFromBookmarks;
470 return PR_TRUE;
473 return PR_FALSE;
476 static PRBool
477 CheckSameOrigin(nsINode* aNode1, nsINode* aNode2)
479 NS_PRECONDITION(aNode1, "Null node?");
480 NS_PRECONDITION(aNode2, "Null node?");
482 PRBool equal;
483 return
484 NS_SUCCEEDED(aNode1->NodePrincipal()->
485 Equals(aNode2->NodePrincipal(), &equal)) &&
486 equal;
489 PRBool
490 nsHTMLDocument::TryParentCharset(nsIDocumentCharsetInfo* aDocInfo,
491 nsIDocument* aParentDocument,
492 PRInt32& aCharsetSource,
493 nsACString& aCharset)
495 if (aDocInfo) {
496 PRInt32 source;
497 nsCOMPtr<nsIAtom> csAtom;
498 PRInt32 parentSource;
499 aDocInfo->GetParentCharsetSource(&parentSource);
500 if (kCharsetFromParentForced <= parentSource)
501 source = kCharsetFromParentForced;
502 else if (kCharsetFromHintPrevDoc == parentSource) {
503 // Make sure that's OK
504 if (!aParentDocument || !CheckSameOrigin(this, aParentDocument)) {
505 return PR_FALSE;
508 // if parent is posted doc, set this prevent autodections
509 // I'm not sure this makes much sense... but whatever.
510 source = kCharsetFromHintPrevDoc;
512 else if (kCharsetFromCache <= parentSource) {
513 // Make sure that's OK
514 if (!aParentDocument || !CheckSameOrigin(this, aParentDocument)) {
515 return PR_FALSE;
518 source = kCharsetFromParentFrame;
520 else
521 return PR_FALSE;
523 if (source < aCharsetSource)
524 return PR_TRUE;
526 aDocInfo->GetParentCharset(getter_AddRefs(csAtom));
527 if (csAtom) {
528 csAtom->ToUTF8String(aCharset);
529 aCharsetSource = source;
530 return PR_TRUE;
533 return PR_FALSE;
536 PRBool
537 nsHTMLDocument::UseWeakDocTypeDefault(PRInt32& aCharsetSource,
538 nsACString& aCharset)
540 if (kCharsetFromWeakDocTypeDefault <= aCharsetSource)
541 return PR_TRUE;
542 // fallback value in case docshell return error
543 aCharset.AssignLiteral("ISO-8859-1");
545 const nsAdoptingString& defCharset =
546 nsContentUtils::GetLocalizedStringPref("intl.charset.default");
548 if (!defCharset.IsEmpty()) {
549 LossyCopyUTF16toASCII(defCharset, aCharset);
550 aCharsetSource = kCharsetFromWeakDocTypeDefault;
552 return PR_TRUE;
555 PRBool
556 nsHTMLDocument::TryDefaultCharset( nsIMarkupDocumentViewer* aMarkupDV,
557 PRInt32& aCharsetSource,
558 nsACString& aCharset)
560 if(kCharsetFromUserDefault <= aCharsetSource)
561 return PR_TRUE;
563 nsCAutoString defaultCharsetFromDocShell;
564 if (aMarkupDV) {
565 nsresult rv =
566 aMarkupDV->GetDefaultCharacterSet(defaultCharsetFromDocShell);
567 if(NS_SUCCEEDED(rv)) {
568 aCharset = defaultCharsetFromDocShell;
570 aCharsetSource = kCharsetFromUserDefault;
571 return PR_TRUE;
574 return PR_FALSE;
577 void
578 nsHTMLDocument::StartAutodetection(nsIDocShell *aDocShell, nsACString& aCharset,
579 const char* aCommand)
581 nsCOMPtr <nsIParserFilter> cdetflt;
583 nsresult rv_detect;
584 if(!gInitDetector) {
585 const nsAdoptingString& detector_name =
586 nsContentUtils::GetLocalizedStringPref("intl.charset.detector");
588 if(!detector_name.IsEmpty()) {
589 PL_strncpy(g_detector_contractid, NS_CHARSET_DETECTOR_CONTRACTID_BASE,
590 DETECTOR_CONTRACTID_MAX);
591 PL_strncat(g_detector_contractid,
592 NS_ConvertUTF16toUTF8(detector_name).get(),
593 DETECTOR_CONTRACTID_MAX);
594 gPlugDetector = PR_TRUE;
597 nsContentUtils::RegisterPrefCallback("intl.charset.detector",
598 MyPrefChangedCallback,
599 nsnull);
601 gInitDetector = PR_TRUE;
604 if (gPlugDetector) {
605 nsCOMPtr <nsICharsetDetector> cdet =
606 do_CreateInstance(g_detector_contractid, &rv_detect);
607 if (NS_SUCCEEDED(rv_detect)) {
608 cdetflt = do_CreateInstance(NS_CHARSET_DETECTION_ADAPTOR_CONTRACTID,
609 &rv_detect);
611 nsCOMPtr<nsICharsetDetectionAdaptor> adp = do_QueryInterface(cdetflt);
612 if (adp) {
613 nsCOMPtr<nsIWebShellServices> wss = do_QueryInterface(aDocShell);
614 if (wss) {
615 rv_detect = adp->Init(wss, cdet, this, mParser,
616 PromiseFlatCString(aCharset).get(), aCommand);
618 if (mParser)
619 mParser->SetParserFilter(cdetflt);
623 else {
624 // IF we cannot create the detector, don't bother to
625 // create one next time.
626 gPlugDetector = PR_FALSE;
631 void
632 nsHTMLDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
634 nsDocument::SetDocumentCharacterSet(aCharSetID);
635 // Make sure to stash this charset on our channel as needed if it's a wyciwyg
636 // channel.
637 nsCOMPtr<nsIWyciwygChannel> wyciwygChannel = do_QueryInterface(mChannel);
638 if (wyciwygChannel) {
639 wyciwygChannel->SetCharsetAndSource(GetDocumentCharacterSetSource(),
640 aCharSetID);
644 nsresult
645 nsHTMLDocument::StartDocumentLoad(const char* aCommand,
646 nsIChannel* aChannel,
647 nsILoadGroup* aLoadGroup,
648 nsISupports* aContainer,
649 nsIStreamListener **aDocListener,
650 PRBool aReset,
651 nsIContentSink* aSink)
653 nsCAutoString contentType;
654 aChannel->GetContentType(contentType);
656 if (contentType.Equals("application/xhtml+xml") &&
657 (!aCommand || nsCRT::strcmp(aCommand, "view-source") != 0)) {
658 // We're parsing XHTML as XML, remember that.
660 mIsRegularHTML = PR_FALSE;
661 mCompatMode = eCompatibility_FullStandards;
663 #ifdef DEBUG
664 else {
665 NS_ASSERTION(mIsRegularHTML,
666 "Hey, someone forgot to reset mIsRegularHTML!!!");
668 #endif
670 CSSLoader()->SetCaseSensitive(IsXHTML());
671 CSSLoader()->SetCompatibilityMode(mCompatMode);
673 PRBool needsParser = PR_TRUE;
674 if (aCommand)
676 if (!nsCRT::strcmp(aCommand, "view delayedContentLoad")) {
677 needsParser = PR_FALSE;
681 nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor;
682 nsresult rv = nsDocument::StartDocumentLoad(aCommand,
683 aChannel, aLoadGroup,
684 aContainer,
685 aDocListener, aReset);
686 if (NS_FAILED(rv)) {
687 return rv;
690 // Store the security info for future use with wyciwyg channels.
691 aChannel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
693 nsCOMPtr<nsIURI> uri;
694 rv = aChannel->GetURI(getter_AddRefs(uri));
695 if (NS_FAILED(rv)) {
696 return rv;
699 nsCOMPtr<nsICachingChannel> cachingChan = do_QueryInterface(aChannel);
700 if (cachingChan) {
701 nsCOMPtr<nsISupports> cacheToken;
702 cachingChan->GetCacheToken(getter_AddRefs(cacheToken));
703 if (cacheToken)
704 cacheDescriptor = do_QueryInterface(cacheToken);
707 if (needsParser) {
708 mParser = do_CreateInstance(kCParserCID, &rv);
709 NS_ENSURE_SUCCESS(rv, rv);
712 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
714 nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
715 docShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
716 PRInt32 textType = GET_BIDI_OPTION_TEXTTYPE(GetBidiOptions());
718 // Look for the parent document. Note that at this point we don't have our
719 // content viewer set up yet, and therefore do not have a useful
720 // mParentDocument.
722 // in this block of code, if we get an error result, we return it
723 // but if we get a null pointer, that's perfectly legal for parent
724 // and parentContentViewer
725 nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(docShell));
726 NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
728 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
729 docShellAsItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
731 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
732 nsCOMPtr<nsIDocument> parentDocument;
733 nsCOMPtr<nsIContentViewer> parentContentViewer;
734 if (parent) {
735 rv = parent->GetContentViewer(getter_AddRefs(parentContentViewer));
736 NS_ENSURE_SUCCESS(rv, rv);
737 nsCOMPtr<nsIDocumentViewer> docViewer =
738 do_QueryInterface(parentContentViewer);
739 if (docViewer) {
740 docViewer->GetDocument(getter_AddRefs(parentDocument));
745 // The following logic is mirrored in nsWebShell::Embed!
747 nsCOMPtr<nsIMarkupDocumentViewer> muCV;
748 PRBool muCVIsParent = PR_FALSE;
749 nsCOMPtr<nsIContentViewer> cv;
750 docShell->GetContentViewer(getter_AddRefs(cv));
751 if (cv) {
752 muCV = do_QueryInterface(cv);
753 } else {
754 muCV = do_QueryInterface(parentContentViewer);
755 if (muCV) {
756 muCVIsParent = PR_TRUE;
760 nsCAutoString scheme;
761 uri->GetScheme(scheme);
763 nsCAutoString urlSpec;
764 uri->GetSpec(urlSpec);
765 #ifdef DEBUG_charset
766 printf("Determining charset for %s\n", urlSpec.get());
767 #endif
769 // These are the charset source and charset for our document
770 PRInt32 charsetSource;
771 nsCAutoString charset;
773 // These are the charset source and charset for the parser. This can differ
774 // from that for the document if the channel is a wyciwyg channel.
775 PRInt32 parserCharsetSource;
776 nsCAutoString parserCharset;
778 nsCOMPtr<nsIWyciwygChannel> wyciwygChannel;
780 if (IsXHTML()) {
781 charsetSource = kCharsetFromDocTypeDefault;
782 charset.AssignLiteral("UTF-8");
783 TryChannelCharset(aChannel, charsetSource, charset);
784 parserCharsetSource = charsetSource;
785 parserCharset = charset;
786 } else {
787 charsetSource = kCharsetUninitialized;
788 wyciwygChannel = do_QueryInterface(aChannel);
790 // The following charset resolving calls has implied knowledge
791 // about charset source priority order. Each try will return true
792 // if the source is higher or equal to the source as its name
793 // describes. Some try call might change charset source to
794 // multiple values, like TryHintCharset and TryParentCharset. It
795 // should be always safe to try more sources.
796 if (!TryUserForcedCharset(muCV, dcInfo, charsetSource, charset)) {
797 TryHintCharset(muCV, charsetSource, charset);
798 TryParentCharset(dcInfo, parentDocument, charsetSource, charset);
800 // Don't actually get the charset from the channel if this is a
801 // wyciwyg channel; it'll always be UTF-16
802 if (!wyciwygChannel &&
803 TryChannelCharset(aChannel, charsetSource, charset)) {
804 // Use the channel's charset (e.g., charset from HTTP
805 // "Content-Type" header).
807 else if (!scheme.EqualsLiteral("about") && // don't try to access bookmarks for about:blank
808 TryBookmarkCharset(docShell, aChannel, charsetSource, charset)) {
809 // Use the bookmark's charset.
811 else if (cacheDescriptor && !urlSpec.IsEmpty() &&
812 TryCacheCharset(cacheDescriptor, charsetSource, charset)) {
813 // Use the cache's charset.
815 else if (TryDefaultCharset(muCV, charsetSource, charset)) {
816 // Use the default charset.
817 // previous document charset might be inherited as default charset.
819 else {
820 // Use the weak doc type default charset
821 UseWeakDocTypeDefault(charsetSource, charset);
825 PRBool isPostPage = PR_FALSE;
826 // check if current doc is from POST command
827 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
828 if (httpChannel) {
829 nsCAutoString methodStr;
830 rv = httpChannel->GetRequestMethod(methodStr);
831 isPostPage = (NS_SUCCEEDED(rv) &&
832 methodStr.EqualsLiteral("POST"));
835 if (isPostPage && muCV && kCharsetFromHintPrevDoc > charsetSource) {
836 nsCAutoString requestCharset;
837 muCV->GetPrevDocCharacterSet(requestCharset);
838 if (!requestCharset.IsEmpty()) {
839 charsetSource = kCharsetFromHintPrevDoc;
840 charset = requestCharset;
844 if (wyciwygChannel) {
845 // We know for sure that the parser needs to be using UTF16.
846 parserCharset = "UTF-16";
847 parserCharsetSource = charsetSource < kCharsetFromChannel ?
848 kCharsetFromChannel : charsetSource;
850 nsCAutoString cachedCharset;
851 PRInt32 cachedSource;
852 rv = wyciwygChannel->GetCharsetAndSource(&cachedSource, cachedCharset);
853 if (NS_SUCCEEDED(rv)) {
854 if (cachedSource > charsetSource) {
855 charsetSource = cachedSource;
856 charset = cachedCharset;
858 } else {
859 // Don't propagate this error.
860 rv = NS_OK;
863 } else {
864 parserCharset = charset;
865 parserCharsetSource = charsetSource;
868 if(kCharsetFromAutoDetection > charsetSource && !isPostPage) {
869 StartAutodetection(docShell, charset, aCommand);
872 // ahmed
873 // Check if 864 but in Implicit mode !
874 // XXXbz why is this happening after StartAutodetection ?
875 if ((textType == IBMBIDI_TEXTTYPE_LOGICAL) &&
876 (charset.LowerCaseEqualsLiteral("ibm864"))) {
877 charset.AssignLiteral("IBM864i");
881 SetDocumentCharacterSetSource(charsetSource);
882 SetDocumentCharacterSet(charset);
884 // set doc charset to muCV for next document.
885 // Don't propagate this back up to the parent document if we have one.
886 if (muCV && !muCVIsParent)
887 muCV->SetPrevDocCharacterSet(charset);
889 if(cacheDescriptor) {
890 NS_ASSERTION(charset == parserCharset,
891 "How did those end up different here? wyciwyg channels are "
892 "not nsICachingChannel");
893 rv = cacheDescriptor->SetMetaDataElement("charset",
894 charset.get());
895 NS_ASSERTION(NS_SUCCEEDED(rv),"cannot SetMetaDataElement");
898 // Set the parser as the stream listener for the document loader...
899 if (mParser) {
900 rv = CallQueryInterface(mParser, aDocListener);
901 if (NS_FAILED(rv)) {
902 return rv;
905 #ifdef DEBUG_charset
906 printf(" charset = %s source %d\n",
907 charset.get(), charsetSource);
908 #endif
909 mParser->SetDocumentCharset(parserCharset, parserCharsetSource);
910 mParser->SetCommand(aCommand);
912 // create the content sink
913 nsCOMPtr<nsIContentSink> sink;
915 if (aSink)
916 sink = aSink;
917 else {
918 if (IsXHTML()) {
919 nsCOMPtr<nsIXMLContentSink> xmlsink;
920 rv = NS_NewXMLContentSink(getter_AddRefs(xmlsink), this, uri,
921 docShell, aChannel);
923 sink = xmlsink;
924 } else {
925 nsCOMPtr<nsIHTMLContentSink> htmlsink;
927 rv = NS_NewHTMLContentSink(getter_AddRefs(htmlsink), this, uri,
928 docShell, aChannel);
930 sink = htmlsink;
932 NS_ENSURE_SUCCESS(rv, rv);
934 NS_ASSERTION(sink,
935 "null sink with successful result from factory method");
938 mParser->SetContentSink(sink);
939 // parser the content of the URI
940 mParser->Parse(uri, nsnull, (void *)this);
943 return rv;
946 void
947 nsHTMLDocument::StopDocumentLoad()
949 // If we're writing (i.e., there's been a document.open call), then
950 // nsDocument::StopDocumentLoad will do the wrong thing and simply terminate
951 // our parser.
952 if (mWriteState != eNotWriting) {
953 Close();
954 } else {
955 nsDocument::StopDocumentLoad();
959 // static
960 void
961 nsHTMLDocument::DocumentWriteTerminationFunc(nsISupports *aRef)
963 nsCOMPtr<nsIArray> arr = do_QueryInterface(aRef);
964 NS_ASSERTION(arr, "Must have array!");
966 nsCOMPtr<nsIDocument> doc = do_QueryElementAt(arr, 0);
967 NS_ASSERTION(doc, "Must have document!");
969 nsCOMPtr<nsIParser> parser = do_QueryElementAt(arr, 1);
970 NS_ASSERTION(parser, "Must have parser!");
972 nsHTMLDocument *htmldoc = static_cast<nsHTMLDocument*>(doc.get());
974 // Check whether htmldoc still has the same parser. If not, it's
975 // not for us to mess with it.
976 if (htmldoc->mParser != parser) {
977 return;
980 // If the document is in the middle of a document.write() call, this
981 // most likely means that script on a page document.write()'d out a
982 // script tag that did location="..." and we're right now finishing
983 // up executing the script that was written with
984 // document.write(). Since there's still script on the stack (the
985 // script that called document.write()) we don't want to release the
986 // parser now, that would cause the next document.write() call to
987 // cancel the load that was initiated by the location="..." in the
988 // script that was written out by document.write().
990 if (!htmldoc->mWriteLevel && htmldoc->mWriteState != eDocumentOpened) {
991 // Release the document's parser so that the call to EndLoad()
992 // doesn't just return early and set the termination function again.
994 htmldoc->mParser = nsnull;
997 htmldoc->EndLoad();
1000 void
1001 nsHTMLDocument::EndLoad()
1003 if (mParser && mWriteState != eDocumentClosed) {
1004 nsCOMPtr<nsIJSContextStack> stack =
1005 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
1007 if (stack) {
1008 JSContext *cx = nsnull;
1009 stack->Peek(&cx);
1011 if (cx) {
1012 nsIScriptContext *scx = nsJSUtils::GetDynamicScriptContext(cx);
1014 if (scx) {
1015 // The load of the document was terminated while we're
1016 // called from within JS and we have a parser (i.e. we're in
1017 // the middle of doing document.write()). In stead of
1018 // releasing the parser and ending the document load
1019 // directly, we'll make that happen once the script is done
1020 // executing. This way subsequent document.write() calls
1021 // won't end up creating a new parser and interrupting other
1022 // loads that were started while the script was
1023 // running. I.e. this makes the following case work as
1024 // expected:
1026 // document.write("foo");
1027 // location.href = "http://www.mozilla.org";
1028 // document.write("bar");
1030 nsresult rv;
1032 nsCOMPtr<nsIMutableArray> arr =
1033 do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
1034 if (NS_SUCCEEDED(rv)) {
1035 rv = arr->AppendElement(static_cast<nsIDocument*>(this),
1036 PR_FALSE);
1037 if (NS_SUCCEEDED(rv)) {
1038 rv = arr->AppendElement(mParser, PR_FALSE);
1039 if (NS_SUCCEEDED(rv)) {
1040 rv = scx->SetTerminationFunction(DocumentWriteTerminationFunc,
1041 arr);
1042 // If we fail to set the termination function, just go ahead
1043 // and EndLoad now. The slight bugginess involved is better
1044 // than leaking.
1045 if (NS_SUCCEEDED(rv)) {
1046 return;
1056 // Reset this now, since we're really done "loading" this document.written
1057 // document.
1058 NS_ASSERTION(mWriteState == eNotWriting || mWriteState == ePendingClose ||
1059 mWriteState == eDocumentClosed, "EndLoad called early");
1060 mWriteState = eNotWriting;
1062 PRBool turnOnEditing =
1063 mParser && (HasFlag(NODE_IS_EDITABLE) || mContentEditableCount > 0);
1064 // Note: nsDocument::EndLoad nulls out mParser.
1065 nsDocument::EndLoad();
1066 if (turnOnEditing) {
1067 EditingStateChanged();
1071 NS_IMETHODIMP
1072 nsHTMLDocument::SetTitle(const nsAString& aTitle)
1074 return nsDocument::SetTitle(aTitle);
1077 nsresult
1078 nsHTMLDocument::AddImageMap(nsIDOMHTMLMapElement* aMap)
1080 // XXX We should order the maps based on their order in the document.
1081 // XXX Otherwise scripts that add/remove maps with duplicate names
1082 // XXX will cause problems
1083 NS_PRECONDITION(nsnull != aMap, "null ptr");
1084 if (nsnull == aMap) {
1085 return NS_ERROR_NULL_POINTER;
1087 if (mImageMaps.AppendObject(aMap)) {
1088 return NS_OK;
1090 return NS_ERROR_OUT_OF_MEMORY;
1093 void
1094 nsHTMLDocument::RemoveImageMap(nsIDOMHTMLMapElement* aMap)
1096 NS_PRECONDITION(nsnull != aMap, "null ptr");
1097 mImageMaps.RemoveObject(aMap);
1100 nsIDOMHTMLMapElement *
1101 nsHTMLDocument::GetImageMap(const nsAString& aMapName)
1103 nsAutoString name;
1104 PRUint32 i, n = mImageMaps.Count();
1105 nsIDOMHTMLMapElement *firstMatch = nsnull;
1107 for (i = 0; i < n; ++i) {
1108 nsIDOMHTMLMapElement *map = mImageMaps[i];
1109 NS_ASSERTION(map, "Null map in map list!");
1111 PRBool match;
1112 nsresult rv;
1114 if (IsXHTML()) {
1115 rv = map->GetId(name);
1117 match = name.Equals(aMapName);
1118 } else {
1119 rv = map->GetName(name);
1121 match = name.Equals(aMapName, nsCaseInsensitiveStringComparator());
1124 if (match && NS_SUCCEEDED(rv)) {
1125 // Quirk: if the first matching map is empty, remember it, but keep
1126 // searching for a non-empty one, only use it if none was found (bug 264624).
1127 if (mCompatMode == eCompatibility_NavQuirks) {
1128 nsCOMPtr<nsIDOMHTMLCollection> mapAreas;
1129 rv = map->GetAreas(getter_AddRefs(mapAreas));
1130 if (NS_SUCCEEDED(rv) && mapAreas) {
1131 PRUint32 length = 0;
1132 mapAreas->GetLength(&length);
1133 if (length == 0) {
1134 if (!firstMatch) {
1135 firstMatch = map;
1137 continue;
1141 return map;
1145 return firstMatch;
1148 void
1149 nsHTMLDocument::SetCompatibilityMode(nsCompatibility aMode)
1151 NS_ASSERTION(!IsXHTML() || aMode == eCompatibility_FullStandards,
1152 "Bad compat mode for XHTML document!");
1154 mCompatMode = aMode;
1155 CSSLoader()->SetCompatibilityMode(mCompatMode);
1156 nsCOMPtr<nsIPresShell> shell = GetPrimaryShell();
1157 if (shell) {
1158 nsPresContext *pc = shell->GetPresContext();
1159 if (pc) {
1160 pc->CompatibilityModeChanged();
1165 PRBool
1166 nsHTMLDocument::IsCaseSensitive()
1168 return IsXHTML();
1172 // nsIDOMDocument interface implementation
1174 NS_IMETHODIMP
1175 nsHTMLDocument::CreateElement(const nsAString& aTagName,
1176 nsIDOMElement** aReturn)
1178 *aReturn = nsnull;
1179 nsresult rv;
1181 nsAutoString tagName(aTagName);
1183 // if we are in quirks, allow surrounding '<' '>' for IE compat
1184 if (mCompatMode == eCompatibility_NavQuirks &&
1185 tagName.Length() > 2 &&
1186 tagName.First() == '<' &&
1187 tagName.Last() == '>') {
1188 tagName = Substring(tagName, 1, tagName.Length() - 2);
1191 rv = nsContentUtils::CheckQName(tagName, PR_FALSE);
1192 NS_ENSURE_SUCCESS(rv, rv);
1194 if (!IsXHTML()) {
1195 ToLowerCase(tagName);
1198 nsCOMPtr<nsIAtom> name = do_GetAtom(tagName);
1200 nsCOMPtr<nsIContent> content;
1201 rv = CreateElem(name, nsnull, GetDefaultNamespaceID(), PR_TRUE,
1202 getter_AddRefs(content));
1203 NS_ENSURE_SUCCESS(rv, rv);
1205 return CallQueryInterface(content, aReturn);
1208 NS_IMETHODIMP
1209 nsHTMLDocument::CreateElementNS(const nsAString& aNamespaceURI,
1210 const nsAString& aQualifiedName,
1211 nsIDOMElement** aReturn)
1213 return nsDocument::CreateElementNS(aNamespaceURI, aQualifiedName, aReturn);
1216 NS_IMETHODIMP
1217 nsHTMLDocument::CreateProcessingInstruction(const nsAString& aTarget,
1218 const nsAString& aData,
1219 nsIDOMProcessingInstruction** aReturn)
1221 if (IsXHTML()) {
1222 return nsDocument::CreateProcessingInstruction(aTarget, aData, aReturn);
1225 // There are no PIs for HTML
1226 *aReturn = nsnull;
1228 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1231 NS_IMETHODIMP
1232 nsHTMLDocument::CreateCDATASection(const nsAString& aData,
1233 nsIDOMCDATASection** aReturn)
1235 if (IsXHTML()) {
1236 return nsDocument::CreateCDATASection(aData, aReturn);
1239 // There are no CDATASections in HTML
1240 *aReturn = nsnull;
1242 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1245 NS_IMETHODIMP
1246 nsHTMLDocument::CreateEntityReference(const nsAString& aName,
1247 nsIDOMEntityReference** aReturn)
1249 if (IsXHTML()) {
1250 return nsDocument::CreateEntityReference(aName, aReturn);
1253 // There are no EntityReferences in HTML
1254 *aReturn = nsnull;
1256 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1259 NS_IMETHODIMP
1260 nsHTMLDocument::GetDoctype(nsIDOMDocumentType** aDocumentType)
1262 return nsDocument::GetDoctype(aDocumentType);
1265 NS_IMETHODIMP
1266 nsHTMLDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation)
1268 return nsDocument::GetImplementation(aImplementation);
1271 NS_IMETHODIMP
1272 nsHTMLDocument::GetDocumentElement(nsIDOMElement** aDocumentElement)
1274 return nsDocument::GetDocumentElement(aDocumentElement);
1277 NS_IMETHODIMP
1278 nsHTMLDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn)
1280 return nsDocument::CreateDocumentFragment(aReturn);
1283 NS_IMETHODIMP
1284 nsHTMLDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn)
1286 return nsDocument::CreateComment(aData, aReturn);
1289 NS_IMETHODIMP
1290 nsHTMLDocument::CreateAttribute(const nsAString& aName, nsIDOMAttr** aReturn)
1292 return nsDocument::CreateAttribute(aName, aReturn);
1295 NS_IMETHODIMP
1296 nsHTMLDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
1298 return nsDocument::CreateTextNode(aData, aReturn);
1301 NS_IMETHODIMP
1302 nsHTMLDocument::GetElementsByTagName(const nsAString& aTagname,
1303 nsIDOMNodeList** aReturn)
1305 nsAutoString tmp(aTagname);
1306 if (!IsXHTML()) {
1307 ToLowerCase(tmp); // HTML elements are lower case internally.
1309 return nsDocument::GetElementsByTagName(tmp, aReturn);
1312 NS_IMETHODIMP
1313 nsHTMLDocument::GetBaseURI(nsAString &aURI)
1315 aURI.Truncate();
1316 nsIURI *uri = mDocumentBaseURI; // WEAK
1318 if (!uri) {
1319 uri = mDocumentURI;
1322 if (uri) {
1323 nsCAutoString spec;
1324 uri->GetSpec(spec);
1326 CopyUTF8toUTF16(spec, aURI);
1329 return NS_OK;
1332 // nsIDOM3Document interface implementation
1333 NS_IMETHODIMP
1334 nsHTMLDocument::GetXmlEncoding(nsAString& aXmlEncoding)
1336 if (IsXHTML()) {
1337 return nsDocument::GetXmlEncoding(aXmlEncoding);
1340 SetDOMStringToNull(aXmlEncoding);
1342 return NS_OK;
1345 NS_IMETHODIMP
1346 nsHTMLDocument::GetXmlStandalone(PRBool *aXmlStandalone)
1348 if (IsXHTML()) {
1349 return nsDocument::GetXmlStandalone(aXmlStandalone);
1352 *aXmlStandalone = PR_FALSE;
1354 return NS_OK;
1357 NS_IMETHODIMP
1358 nsHTMLDocument::SetXmlStandalone(PRBool aXmlStandalone)
1360 if (IsXHTML()) {
1361 return nsDocument::SetXmlStandalone(aXmlStandalone);
1364 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1368 NS_IMETHODIMP
1369 nsHTMLDocument::GetXmlVersion(nsAString& aXmlVersion)
1371 if (IsXHTML()) {
1372 return nsDocument::GetXmlVersion(aXmlVersion);
1375 SetDOMStringToNull(aXmlVersion);
1377 return NS_OK;
1380 NS_IMETHODIMP
1381 nsHTMLDocument::SetXmlVersion(const nsAString& aXmlVersion)
1383 if (IsXHTML()) {
1384 return nsDocument::SetXmlVersion(aXmlVersion);
1387 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1391 // nsIDOMHTMLDocument interface implementation
1393 NS_IMETHODIMP
1394 nsHTMLDocument::GetTitle(nsAString& aTitle)
1396 return nsDocument::GetTitle(aTitle);
1399 NS_IMETHODIMP
1400 nsHTMLDocument::GetReferrer(nsAString& aReferrer)
1402 return nsDocument::GetReferrer(aReferrer);
1405 void
1406 nsHTMLDocument::GetDomainURI(nsIURI **aURI)
1408 nsIPrincipal *principal = NodePrincipal();
1410 principal->GetDomain(aURI);
1411 if (!*aURI) {
1412 principal->GetURI(aURI);
1417 NS_IMETHODIMP
1418 nsHTMLDocument::GetDomain(nsAString& aDomain)
1420 nsCOMPtr<nsIURI> uri;
1421 GetDomainURI(getter_AddRefs(uri));
1423 if (!uri) {
1424 return NS_ERROR_FAILURE;
1427 nsCAutoString hostName;
1429 if (NS_SUCCEEDED(uri->GetHost(hostName))) {
1430 CopyUTF8toUTF16(hostName, aDomain);
1431 } else {
1432 // If we can't get the host from the URI (e.g. about:, javascript:,
1433 // etc), just return an null string.
1434 SetDOMStringToNull(aDomain);
1437 return NS_OK;
1440 NS_IMETHODIMP
1441 nsHTMLDocument::SetDomain(const nsAString& aDomain)
1443 if (aDomain.IsEmpty())
1444 return NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN;
1446 // Create new URI
1447 nsCOMPtr<nsIURI> uri;
1448 GetDomainURI(getter_AddRefs(uri));
1450 if (!uri) {
1451 return NS_ERROR_FAILURE;
1454 nsCAutoString newURIString;
1455 if (NS_FAILED(uri->GetScheme(newURIString)))
1456 return NS_ERROR_FAILURE;
1457 nsCAutoString path;
1458 if (NS_FAILED(uri->GetPath(path)))
1459 return NS_ERROR_FAILURE;
1460 newURIString.AppendLiteral("://");
1461 AppendUTF16toUTF8(aDomain, newURIString);
1462 newURIString.Append(path);
1464 nsCOMPtr<nsIURI> newURI;
1465 if (NS_FAILED(NS_NewURI(getter_AddRefs(newURI), newURIString)))
1466 return NS_ERROR_FAILURE;
1468 // Check new domain - must be a superdomain of the current host
1469 // For example, a page from foo.bar.com may set domain to bar.com,
1470 // but not to ar.com, baz.com, or fi.foo.bar.com.
1471 nsCAutoString current, domain;
1472 if (NS_FAILED(uri->GetAsciiHost(current)))
1473 current.Truncate();
1474 if (NS_FAILED(newURI->GetAsciiHost(domain)))
1475 domain.Truncate();
1477 PRBool ok = current.Equals(domain);
1478 if (current.Length() > domain.Length() &&
1479 StringEndsWith(current, domain) &&
1480 current.CharAt(current.Length() - domain.Length() - 1) == '.') {
1481 // We're golden if the new domain is the current page's base domain or a
1482 // subdomain of it.
1483 nsCOMPtr<nsIEffectiveTLDService> tldService =
1484 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
1485 if (!tldService)
1486 return NS_ERROR_NOT_AVAILABLE;
1488 nsCAutoString currentBaseDomain;
1489 ok = NS_SUCCEEDED(tldService->GetBaseDomain(uri, 0, currentBaseDomain));
1490 NS_ASSERTION(StringEndsWith(domain, currentBaseDomain) ==
1491 (domain.Length() >= currentBaseDomain.Length()),
1492 "uh-oh! slight optimization wasn't valid somehow!");
1493 ok = ok && domain.Length() >= currentBaseDomain.Length();
1495 if (!ok) {
1496 // Error: illegal domain
1497 return NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN;
1500 return NodePrincipal()->SetDomain(newURI);
1503 NS_IMETHODIMP
1504 nsHTMLDocument::GetURL(nsAString& aURL)
1506 nsCAutoString str;
1508 if (mDocumentURI) {
1509 mDocumentURI->GetSpec(str);
1512 CopyUTF8toUTF16(str, aURL);
1514 return NS_OK;
1517 NS_IMETHODIMP
1518 nsHTMLDocument::GetBody(nsIDOMHTMLElement** aBody)
1520 *aBody = nsnull;
1522 nsIContent* body = GetBodyContent();
1524 if (body) {
1525 // There is a body element, return that as the body.
1526 return CallQueryInterface(body, aBody);
1529 // The document is most likely a frameset document so look for the
1530 // outer most frameset element
1531 nsCOMPtr<nsIDOMNodeList> nodeList;
1533 nsresult rv;
1534 if (IsXHTML()) {
1535 rv = GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"),
1536 NS_LITERAL_STRING("frameset"),
1537 getter_AddRefs(nodeList));
1538 } else {
1539 rv = GetElementsByTagName(NS_LITERAL_STRING("frameset"),
1540 getter_AddRefs(nodeList));
1542 NS_ENSURE_SUCCESS(rv, rv);
1544 nsCOMPtr<nsIDOMNode> node;
1545 nodeList->Item(0, getter_AddRefs(node));
1547 return node ? CallQueryInterface(node, aBody) : NS_OK;
1550 NS_IMETHODIMP
1551 nsHTMLDocument::SetBody(nsIDOMHTMLElement* aBody)
1553 nsCOMPtr<nsIContent> newBody = do_QueryInterface(aBody);
1554 nsIContent* root = GetRootContent();
1556 // The body element must be either a body tag or a frameset tag. And we must
1557 // have a html root tag, otherwise GetBody will not return the newly set
1558 // body.
1559 if (!newBody || !(newBody->Tag() == nsGkAtoms::body ||
1560 newBody->Tag() == nsGkAtoms::frameset) ||
1561 !root || !root->IsNodeOfType(nsINode::eHTML) ||
1562 root->Tag() != nsGkAtoms::html) {
1563 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1566 nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(root);
1567 nsCOMPtr<nsIDOMNode> tmp;
1569 // Use DOM methods so that we pass through the appropriate security checks.
1570 nsCOMPtr<nsIDOMNode> currentBody = do_QueryInterface(GetBodyContent());
1571 if (currentBody) {
1572 return rootElem->ReplaceChild(aBody, currentBody, getter_AddRefs(tmp));
1575 return rootElem->AppendChild(aBody, getter_AddRefs(tmp));
1578 NS_IMETHODIMP
1579 nsHTMLDocument::GetImages(nsIDOMHTMLCollection** aImages)
1581 if (!mImages) {
1582 mImages = new nsContentList(this, nsGkAtoms::img, GetDefaultNamespaceID());
1583 if (!mImages) {
1584 return NS_ERROR_OUT_OF_MEMORY;
1588 *aImages = mImages;
1589 NS_ADDREF(*aImages);
1591 return NS_OK;
1594 NS_IMETHODIMP
1595 nsHTMLDocument::GetApplets(nsIDOMHTMLCollection** aApplets)
1597 if (!mApplets) {
1598 mApplets = new nsContentList(this, nsGkAtoms::applet,
1599 GetDefaultNamespaceID());
1600 if (!mApplets) {
1601 return NS_ERROR_OUT_OF_MEMORY;
1605 *aApplets = mApplets;
1606 NS_ADDREF(*aApplets);
1608 return NS_OK;
1611 PRBool
1612 nsHTMLDocument::MatchLinks(nsIContent *aContent, PRInt32 aNamespaceID,
1613 nsIAtom* aAtom, void* aData)
1615 nsIDocument* doc = aContent->GetCurrentDoc();
1617 if (doc) {
1618 NS_ASSERTION(aContent->IsInDoc(),
1619 "This method should never be called on content nodes that "
1620 "are not in a document!");
1621 #ifdef DEBUG
1623 nsCOMPtr<nsIHTMLDocument> htmldoc =
1624 do_QueryInterface(aContent->GetCurrentDoc());
1625 NS_ASSERTION(htmldoc,
1626 "Huh, how did this happen? This should only be used with "
1627 "HTML documents!");
1629 #endif
1631 nsINodeInfo *ni = aContent->NodeInfo();
1632 PRInt32 namespaceID = doc->GetDefaultNamespaceID();
1634 nsIAtom *localName = ni->NameAtom();
1635 if (ni->NamespaceID() == namespaceID &&
1636 (localName == nsGkAtoms::a || localName == nsGkAtoms::area)) {
1637 return aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
1641 return PR_FALSE;
1644 NS_IMETHODIMP
1645 nsHTMLDocument::GetLinks(nsIDOMHTMLCollection** aLinks)
1647 if (!mLinks) {
1648 mLinks = new nsContentList(this, MatchLinks, nsnull, nsnull);
1649 if (!mLinks) {
1650 return NS_ERROR_OUT_OF_MEMORY;
1654 *aLinks = mLinks;
1655 NS_ADDREF(*aLinks);
1657 return NS_OK;
1660 PRBool
1661 nsHTMLDocument::MatchAnchors(nsIContent *aContent, PRInt32 aNamespaceID,
1662 nsIAtom* aAtom, void* aData)
1664 NS_ASSERTION(aContent->IsInDoc(),
1665 "This method should never be called on content nodes that "
1666 "are not in a document!");
1667 #ifdef DEBUG
1669 nsCOMPtr<nsIHTMLDocument> htmldoc =
1670 do_QueryInterface(aContent->GetCurrentDoc());
1671 NS_ASSERTION(htmldoc,
1672 "Huh, how did this happen? This should only be used with "
1673 "HTML documents!");
1675 #endif
1677 PRInt32 namespaceID = aContent->GetCurrentDoc()->GetDefaultNamespaceID();
1678 if (aContent->NodeInfo()->Equals(nsGkAtoms::a, namespaceID)) {
1679 return aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::name);
1682 return PR_FALSE;
1685 NS_IMETHODIMP
1686 nsHTMLDocument::GetAnchors(nsIDOMHTMLCollection** aAnchors)
1688 if (!mAnchors) {
1689 mAnchors = new nsContentList(this, MatchAnchors, nsnull, nsnull);
1690 if (!mAnchors) {
1691 return NS_ERROR_OUT_OF_MEMORY;
1695 *aAnchors = mAnchors;
1696 NS_ADDREF(*aAnchors);
1698 return NS_OK;
1701 NS_IMETHODIMP
1702 nsHTMLDocument::GetCookie(nsAString& aCookie)
1704 aCookie.Truncate(); // clear current cookie in case service fails;
1705 // no cookie isn't an error condition.
1707 // not having a cookie service isn't an error
1708 nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
1709 if (service) {
1710 // Get a URI from the document principal. We use the original
1711 // codebase in case the codebase was changed by SetDomain
1712 nsCOMPtr<nsIURI> codebaseURI;
1713 NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
1715 if (!codebaseURI) {
1716 // Document's principal is not a codebase (may be system), so
1717 // can't set cookies
1719 return NS_OK;
1722 nsXPIDLCString cookie;
1723 service->GetCookieString(codebaseURI, mChannel, getter_Copies(cookie));
1724 CopyASCIItoUTF16(cookie, aCookie);
1727 return NS_OK;
1730 NS_IMETHODIMP
1731 nsHTMLDocument::SetCookie(const nsAString& aCookie)
1733 // not having a cookie service isn't an error
1734 nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
1735 if (service && mDocumentURI) {
1736 nsCOMPtr<nsIPrompt> prompt;
1737 nsCOMPtr<nsPIDOMWindow> window = GetWindow();
1738 if (window) {
1739 window->GetPrompter(getter_AddRefs(prompt));
1742 nsCOMPtr<nsIURI> codebaseURI;
1743 NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
1745 if (!codebaseURI) {
1746 // Document's principal is not a codebase (may be system), so
1747 // can't set cookies
1749 return NS_OK;
1752 NS_LossyConvertUTF16toASCII cookie(aCookie);
1753 service->SetCookieString(codebaseURI, prompt, cookie.get(), mChannel);
1756 return NS_OK;
1759 // XXX TBI: accepting arguments to the open method.
1760 nsresult
1761 nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
1763 if (IsXHTML()) {
1764 // No calling document.open() on XHTML
1766 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1769 nsresult rv = NS_OK;
1771 // If we already have a parser we ignore the document.open call.
1772 if (mParser) {
1774 return NS_OK;
1777 NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
1778 "XOW should have caught this!");
1780 if (!aContentType.EqualsLiteral("text/html") &&
1781 !aContentType.EqualsLiteral("text/plain")) {
1782 NS_WARNING("Unsupported type; fix the caller");
1783 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1786 // check whether we're in the middle of unload. If so, ignore this call.
1787 nsCOMPtr<nsIDocShell> shell = do_QueryReferent(mDocumentContainer);
1788 if (!shell) {
1789 // We won't be able to create a parser anyway.
1790 return NS_OK;
1793 PRBool inUnload;
1794 shell->GetIsInUnload(&inUnload);
1795 if (inUnload) {
1796 return NS_OK;
1799 // Note: We want to use GetDocumentFromContext here because this document
1800 // should inherit the security information of the document that's opening us,
1801 // (since if it's secure, then it's presumeably trusted).
1802 nsCOMPtr<nsIDocument> callerDoc =
1803 do_QueryInterface(nsContentUtils::GetDocumentFromContext());
1805 // Grab a reference to the calling documents security info (if any)
1806 // and principal as it may be lost in the call to Reset().
1807 nsCOMPtr<nsISupports> securityInfo;
1808 if (callerDoc) {
1809 securityInfo = callerDoc->GetSecurityInfo();
1812 nsCOMPtr<nsIPrincipal> callerPrincipal;
1813 nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
1815 secMan->GetSubjectPrincipal(getter_AddRefs(callerPrincipal));
1817 if (!callerPrincipal) {
1818 // If we're called from C++ we can't do a document.open w/o
1819 // changing the principal of the document to something like
1820 // about:blank (as that's the only sane thing to do when we don't
1821 // know the origin of this call), and since we can't change the
1822 // principals of a document for security reasons we'll have to
1823 // refuse to go ahead with this call.
1825 return NS_ERROR_DOM_SECURITY_ERR;
1828 // We're called from script. Make sure the script is from the same
1829 // origin, not just that the caller can access the document. This is
1830 // needed to keep document principals from ever changing, which is
1831 // needed because of the way we use our XOW code, and is a sane
1832 // thing to do anyways.
1834 PRBool equals = PR_FALSE;
1835 if (NS_FAILED(callerPrincipal->Equals(NodePrincipal(), &equals)) ||
1836 !equals) {
1837 return NS_ERROR_DOM_SECURITY_ERR;
1840 // The URI for the document after this call. Get it from the calling
1841 // principal (if available), or set it to "about:blank" if no
1842 // principal is reachable.
1843 nsCOMPtr<nsIURI> uri;
1844 callerPrincipal->GetURI(getter_AddRefs(uri));
1846 if (!uri) {
1847 rv = NS_NewURI(getter_AddRefs(uri),
1848 NS_LITERAL_CSTRING("about:blank"));
1849 NS_ENSURE_SUCCESS(rv, rv);
1852 // Stop current loads targeted at the window this document is in.
1853 if (mScriptGlobalObject) {
1854 nsCOMPtr<nsIContentViewer> cv;
1855 shell->GetContentViewer(getter_AddRefs(cv));
1857 if (cv) {
1858 PRBool okToUnload;
1859 rv = cv->PermitUnload(&okToUnload);
1861 if (NS_SUCCEEDED(rv) && !okToUnload) {
1862 // We don't want to unload, so stop here, but don't throw an
1863 // exception.
1864 return NS_OK;
1868 nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(shell));
1869 webnav->Stop(nsIWebNavigation::STOP_NETWORK);
1872 // The open occurred after the document finished loading.
1873 // So we reset the document and create a new one.
1874 nsCOMPtr<nsIChannel> channel;
1875 nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
1877 rv = NS_NewChannel(getter_AddRefs(channel), uri, nsnull, group);
1879 if (NS_FAILED(rv)) {
1880 return rv;
1883 // Set the caller principal, if any, on the channel so that we'll
1884 // make sure to use it when we reset.
1885 rv = channel->SetOwner(callerPrincipal);
1886 NS_ENSURE_SUCCESS(rv, rv);
1888 // Before we reset the doc notify the globalwindow of the change,
1889 // but only if we still have a window (i.e. our window object the
1890 // current inner window in our outer window).
1892 // Hold onto ourselves on the offchance that we're down to one ref
1893 nsCOMPtr<nsIDOMDocument> kungFuDeathGrip =
1894 do_QueryInterface((nsIHTMLDocument*)this);
1896 nsPIDOMWindow *window = GetInnerWindow();
1897 if (window) {
1898 // Remember the old scope in case the call to SetNewDocument changes it.
1899 nsCOMPtr<nsIScriptGlobalObject> oldScope(do_QueryReferent(mScopeObject));
1901 rv = window->SetNewDocument(this, nsnull, PR_FALSE);
1902 NS_ENSURE_SUCCESS(rv, rv);
1904 // Now make sure we're not flagged as the initial document anymore, now
1905 // that we've had stuff done to us. From now on, if anyone tries to
1906 // document.open() us, they get a new inner window.
1907 SetIsInitialDocument(PR_FALSE);
1909 nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
1910 if (oldScope && newScope != oldScope) {
1911 nsContentUtils::ReparentContentWrappersInScope(oldScope, newScope);
1915 // XXX This is a nasty workaround for a scrollbar code bug
1916 // (http://bugzilla.mozilla.org/show_bug.cgi?id=55334).
1918 // Hold on to our root element
1919 nsCOMPtr<nsIContent> root = GetRootContent();
1921 if (root) {
1922 PRInt32 rootIndex = mChildren.IndexOfChild(root);
1923 NS_ASSERTION(rootIndex >= 0, "Root must be in list!");
1925 PRUint32 count = root->GetChildCount();
1927 // Remove all the children from the root.
1928 while (count-- > 0) {
1929 root->RemoveChildAt(count, PR_TRUE);
1932 count = root->GetAttrCount();
1934 // Remove all attributes from the root element
1935 while (count-- > 0) {
1936 const nsAttrName* name = root->GetAttrNameAt(count);
1937 // Hold a strong reference here so that the atom doesn't go away during
1938 // UnsetAttr.
1939 nsCOMPtr<nsIAtom> localName = name->LocalName();
1940 root->UnsetAttr(name->NamespaceID(), localName, PR_FALSE);
1943 // Remove the root from the childlist
1944 mChildren.RemoveChildAt(rootIndex);
1945 mCachedRootContent = nsnull;
1948 // Call Reset(), this will now do the full reset, except removing
1949 // the root from the document, doing that confuses the scrollbar
1950 // code in mozilla since the document in the root element and all
1951 // the anonymous content (i.e. scrollbar elements) is set to
1952 // null.
1954 Reset(channel, group);
1956 if (root) {
1957 // Tear down the frames for the root element.
1958 MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, PR_TRUE);
1959 nsNodeUtils::ContentRemoved(this, root, 0);
1961 // Put the root element back into the document, we don't notify
1962 // the document about this insertion since the sink will do that
1963 // for us and that'll create frames for the root element and the
1964 // scrollbars work as expected (since the document in the root
1965 // element was never set to null)
1967 mChildren.AppendChild(root);
1970 if (IsEditingOn()) {
1971 // Reset() blows away all event listeners in the document, and our
1972 // editor relies heavily on those. Midas is turned on, to make it
1973 // work, re-initialize it to give it a chance to add its event
1974 // listeners again.
1976 TurnEditingOff();
1977 EditingStateChanged();
1980 // Zap the old title -- otherwise it would hang around until document.close()
1981 // (which might never come) if the new document doesn't explicitly set one.
1982 // Void the title to make sure that we actually respect any titles set by the
1983 // new document.
1984 SetTitle(EmptyString());
1985 mDocumentTitle.SetIsVoid(PR_TRUE);
1987 // Store the security info of the caller now that we're done
1988 // resetting the document.
1989 mSecurityInfo = securityInfo;
1991 mParser = do_CreateInstance(kCParserCID, &rv);
1993 // This will be propagated to the parser when someone actually calls write()
1994 mContentType = aContentType;
1996 mWriteState = eDocumentOpened;
1998 if (NS_SUCCEEDED(rv)) {
1999 nsCOMPtr<nsIHTMLContentSink> sink;
2001 rv = NS_NewHTMLContentSink(getter_AddRefs(sink), this, uri, shell,
2002 channel);
2003 if (NS_FAILED(rv)) {
2004 // Don't use a parser without a content sink.
2005 mParser = nsnull;
2006 mWriteState = eNotWriting;
2007 return rv;
2010 mParser->SetContentSink(sink);
2013 // Prepare the docshell and the document viewer for the impending
2014 // out of band document.write()
2015 shell->PrepareForNewContentModel();
2017 // Now check whether we were opened with a "replace" argument. If
2018 // so, we need to tell the docshell to not create a new history
2019 // entry for this load. Otherwise, make sure that we're doing a normal load,
2020 // not whatever type of load was previously done on this docshell.
2021 shell->SetLoadType(aReplace ? LOAD_NORMAL_REPLACE : LOAD_NORMAL);
2023 nsCOMPtr<nsIContentViewer> cv;
2024 shell->GetContentViewer(getter_AddRefs(cv));
2025 nsCOMPtr<nsIDocumentViewer> docViewer = do_QueryInterface(cv);
2026 if (docViewer) {
2027 docViewer->LoadStart(static_cast<nsIHTMLDocument *>(this));
2030 // Add a wyciwyg channel request into the document load group
2031 NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::OpenCommon(): wyciwyg "
2032 "channel already exists!");
2034 // In case the editor is listening and will see the new channel
2035 // being added, make sure mWriteLevel is non-zero so that the editor
2036 // knows that document.open/write/close() is being called on this
2037 // document.
2038 ++mWriteLevel;
2040 CreateAndAddWyciwygChannel();
2042 --mWriteLevel;
2044 return rv;
2047 NS_IMETHODIMP
2048 nsHTMLDocument::Open()
2050 nsCOMPtr<nsIDOMDocument> doc;
2051 return Open(NS_LITERAL_CSTRING("text/html"), PR_FALSE, getter_AddRefs(doc));
2054 NS_IMETHODIMP
2055 nsHTMLDocument::Open(const nsACString& aContentType, PRBool aReplace,
2056 nsIDOMDocument** aReturn)
2058 nsresult rv = OpenCommon(aContentType, aReplace);
2059 NS_ENSURE_SUCCESS(rv, rv);
2061 return CallQueryInterface(this, aReturn);
2064 NS_IMETHODIMP
2065 nsHTMLDocument::Clear()
2067 // This method has been deprecated
2068 return NS_OK;
2071 NS_IMETHODIMP
2072 nsHTMLDocument::Close()
2074 if (IsXHTML()) {
2075 // No calling document.close() on XHTML!
2077 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2080 nsresult rv = NS_OK;
2082 if (mParser && mWriteState == eDocumentOpened) {
2083 mPendingScripts.RemoveElement(GenerateParserKey());
2085 mWriteState = mPendingScripts.Count() == 0
2086 ? eDocumentClosed
2087 : ePendingClose;
2089 ++mWriteLevel;
2090 rv = mParser->Parse(EmptyString(), mParser->GetRootContextKey(),
2091 mContentType, PR_TRUE);
2092 --mWriteLevel;
2094 // XXX Make sure that all the document.written content is
2095 // reflowed. We should remove this call once we change
2096 // nsHTMLDocument::OpenCommon() so that it completely destroys the
2097 // earlier document's content and frame hierarchy. Right now, it
2098 // re-uses the earlier document's root content object and
2099 // corresponding frame objects. These re-used frame objects think
2100 // that they have already been reflowed, so they drop initial
2101 // reflows. For certain cases of document.written content, like a
2102 // frameset document, the dropping of the initial reflow means
2103 // that we end up in document.close() without appended any reflow
2104 // commands to the reflow queue and, consequently, without adding
2105 // the dummy layout request to the load group. Since the dummy
2106 // layout request is not added to the load group, the onload
2107 // handler of the frameset fires before the frames get reflowed
2108 // and loaded. That is the long explanation for why we need this
2109 // one line of code here!
2110 // XXXbz as far as I can tell this may not be needed anymore; all
2111 // the testcases in bug 57636 pass without this line... Leaving
2112 // it be for now, though. In any case, there's no reason to do
2113 // this if we have no presshell, since in that case none of the
2114 // above about reusing frames applies.
2115 if (GetPrimaryShell()) {
2116 FlushPendingNotifications(Flush_Layout);
2119 // Remove the wyciwyg channel request from the document load group
2120 // that we added in OpenCommon(). If all other requests between
2121 // document.open() and document.close() have completed, then this
2122 // method should cause the firing of an onload event.
2123 NS_ASSERTION(mWyciwygChannel, "nsHTMLDocument::Close(): Trying to remove "
2124 "non-existent wyciwyg channel!");
2125 RemoveWyciwygChannel();
2126 NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Close(): "
2127 "nsIWyciwygChannel could not be removed!");
2130 return NS_OK;
2133 nsresult
2134 nsHTMLDocument::WriteCommon(const nsAString& aText,
2135 PRBool aNewlineTerminate)
2137 mTooDeepWriteRecursion =
2138 (mWriteLevel > NS_MAX_DOCUMENT_WRITE_DEPTH || mTooDeepWriteRecursion);
2139 NS_ENSURE_STATE(!mTooDeepWriteRecursion);
2141 if (IsXHTML()) {
2142 // No calling document.write*() on XHTML!
2144 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2147 nsresult rv = NS_OK;
2149 void *key = GenerateParserKey();
2150 if (mWriteState == eDocumentClosed ||
2151 (mWriteState == ePendingClose &&
2152 mPendingScripts.IndexOf(key) == kNotFound)) {
2153 mWriteState = eDocumentClosed;
2154 mParser->Terminate();
2155 NS_ASSERTION(!mParser, "mParser should have been null'd out");
2158 if (!mParser) {
2159 rv = Open();
2161 // If Open() fails, or if it didn't create a parser (as it won't
2162 // if the user chose to not discard the current document through
2163 // onbeforeunload), don't write anything.
2164 if (NS_FAILED(rv) || !mParser) {
2165 return rv;
2169 static NS_NAMED_LITERAL_STRING(new_line, "\n");
2171 // Save the data in cache
2172 if (mWyciwygChannel) {
2173 if (!aText.IsEmpty()) {
2174 mWyciwygChannel->WriteToCacheEntry(aText);
2177 if (aNewlineTerminate) {
2178 mWyciwygChannel->WriteToCacheEntry(new_line);
2182 ++mWriteLevel;
2184 // This could be done with less code, but for performance reasons it
2185 // makes sense to have the code for two separate Parse() calls here
2186 // since the concatenation of strings costs more than we like. And
2187 // why pay that price when we don't need to?
2188 if (aNewlineTerminate) {
2189 rv = mParser->Parse(aText + new_line,
2190 key, mContentType,
2191 (mWriteState == eNotWriting || (mWriteLevel > 1)));
2192 } else {
2193 rv = mParser->Parse(aText,
2194 key, mContentType,
2195 (mWriteState == eNotWriting || (mWriteLevel > 1)));
2198 --mWriteLevel;
2200 mTooDeepWriteRecursion = (mWriteLevel != 0 && mTooDeepWriteRecursion);
2202 return rv;
2205 NS_IMETHODIMP
2206 nsHTMLDocument::Write(const nsAString& aText)
2208 return WriteCommon(aText, PR_FALSE);
2211 NS_IMETHODIMP
2212 nsHTMLDocument::Writeln(const nsAString& aText)
2214 return WriteCommon(aText, PR_TRUE);
2217 nsresult
2218 nsHTMLDocument::ScriptWriteCommon(PRBool aNewlineTerminate)
2220 nsAXPCNativeCallContext *ncc = nsnull;
2222 nsresult rv = nsContentUtils::XPConnect()->
2223 GetCurrentNativeCallContext(&ncc);
2224 NS_ENSURE_SUCCESS(rv, rv);
2226 if (ncc) {
2227 // We're called from JS, concatenate the extra arguments into
2228 // string_buffer
2229 PRUint32 i, argc;
2231 ncc->GetArgc(&argc);
2233 JSContext *cx = nsnull;
2234 rv = ncc->GetJSContext(&cx);
2235 NS_ENSURE_SUCCESS(rv, rv);
2237 jsval *argv = nsnull;
2238 ncc->GetArgvPtr(&argv);
2239 NS_ENSURE_TRUE(argv, NS_ERROR_UNEXPECTED);
2241 if (argc == 1) {
2242 JSAutoRequest ar(cx);
2244 JSString *jsstr = JS_ValueToString(cx, argv[0]);
2245 NS_ENSURE_TRUE(jsstr, NS_ERROR_OUT_OF_MEMORY);
2247 nsDependentString str(reinterpret_cast<const PRUnichar *>
2248 (::JS_GetStringChars(jsstr)),
2249 ::JS_GetStringLength(jsstr));
2251 return WriteCommon(str, aNewlineTerminate);
2254 if (argc > 1) {
2255 nsAutoString string_buffer;
2257 for (i = 0; i < argc; ++i) {
2258 JSAutoRequest ar(cx);
2260 JSString *str = JS_ValueToString(cx, argv[i]);
2261 NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
2263 string_buffer.Append(reinterpret_cast<const PRUnichar *>
2264 (::JS_GetStringChars(str)),
2265 ::JS_GetStringLength(str));
2268 return WriteCommon(string_buffer, aNewlineTerminate);
2272 // No arguments...
2273 return WriteCommon(EmptyString(), aNewlineTerminate);
2276 NS_IMETHODIMP
2277 nsHTMLDocument::Write()
2279 return ScriptWriteCommon(PR_FALSE);
2282 NS_IMETHODIMP
2283 nsHTMLDocument::Writeln()
2285 return ScriptWriteCommon(PR_TRUE);
2288 NS_IMETHODIMP
2289 nsHTMLDocument::ImportNode(nsIDOMNode* aImportedNode,
2290 PRBool aDeep,
2291 nsIDOMNode** aReturn)
2293 return nsDocument::ImportNode(aImportedNode, aDeep, aReturn);
2296 NS_IMETHODIMP
2297 nsHTMLDocument::CreateAttributeNS(const nsAString& aNamespaceURI,
2298 const nsAString& aQualifiedName,
2299 nsIDOMAttr** aReturn)
2301 return nsDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, aReturn);
2304 NS_IMETHODIMP
2305 nsHTMLDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
2306 const nsAString& aLocalName,
2307 nsIDOMNodeList** aReturn)
2309 nsAutoString tmp(aLocalName);
2311 if (!IsXHTML()) {
2312 ToLowerCase(tmp); // HTML elements are lower case internally.
2315 return nsDocument::GetElementsByTagNameNS(aNamespaceURI, tmp, aReturn);
2318 NS_IMETHODIMP
2319 nsHTMLDocument::GetElementById(const nsAString& aElementId,
2320 nsIDOMElement** aReturn)
2322 return nsDocument::GetElementById(aElementId, aReturn);
2325 PRBool
2326 nsHTMLDocument::MatchNameAttribute(nsIContent* aContent, PRInt32 aNamespaceID,
2327 nsIAtom* aAtom, void* aData)
2329 NS_PRECONDITION(aContent, "Must have content node to work with!");
2330 nsString* elementName = static_cast<nsString*>(aData);
2331 return aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
2332 *elementName, eCaseMatters);
2335 NS_IMETHODIMP
2336 nsHTMLDocument::GetElementsByName(const nsAString& aElementName,
2337 nsIDOMNodeList** aReturn)
2339 void* elementNameData = new nsString(aElementName);
2340 NS_ENSURE_TRUE(elementNameData, NS_ERROR_OUT_OF_MEMORY);
2341 nsContentList* elements =
2342 new nsContentList(this,
2343 MatchNameAttribute,
2344 nsContentUtils::DestroyMatchString,
2345 elementNameData);
2346 NS_ENSURE_TRUE(elements, NS_ERROR_OUT_OF_MEMORY);
2348 *aReturn = elements;
2349 NS_ADDREF(*aReturn);
2351 return NS_OK;
2354 void
2355 nsHTMLDocument::ScriptLoading(nsIScriptElement *aScript)
2357 if (mWriteState == eNotWriting) {
2358 return;
2361 mPendingScripts.AppendElement(aScript);
2364 void
2365 nsHTMLDocument::ScriptExecuted(nsIScriptElement *aScript)
2367 if (mWriteState == eNotWriting) {
2368 return;
2371 mPendingScripts.RemoveElement(aScript);
2372 if (mPendingScripts.Count() == 0 && mWriteState == ePendingClose) {
2373 // The last pending script just finished, terminate our parser now.
2374 mWriteState = eDocumentClosed;
2378 void
2379 nsHTMLDocument::AddedForm()
2381 ++mNumForms;
2384 void
2385 nsHTMLDocument::RemovedForm()
2387 --mNumForms;
2390 PRInt32
2391 nsHTMLDocument::GetNumFormsSynchronous()
2393 return mNumForms;
2396 nsresult
2397 nsHTMLDocument::GetBodySize(PRInt32* aWidth,
2398 PRInt32* aHeight)
2400 *aWidth = *aHeight = 0;
2402 FlushPendingNotifications(Flush_Layout);
2404 nsCOMPtr<nsIPresShell> shell = GetPrimaryShell();
2406 if (!shell)
2407 return NS_OK;
2409 // Find the <body> element: this is what we'll want to use for the
2410 // document's width and height values.
2411 nsIContent* body = GetBodyContent();
2412 if (!body) {
2413 return NS_OK;
2416 // Now grab its frame
2417 nsIFrame* frame = shell->GetPrimaryFrameFor(body);
2418 if (!frame)
2419 return NS_OK;
2421 nsSize size = frame->GetSize();
2423 *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width);
2424 *aHeight = nsPresContext::AppUnitsToIntCSSPixels(size.height);
2426 return NS_OK;
2429 NS_IMETHODIMP
2430 nsHTMLDocument::GetWidth(PRInt32* aWidth)
2432 NS_ENSURE_ARG_POINTER(aWidth);
2434 PRInt32 height;
2435 return GetBodySize(aWidth, &height);
2438 NS_IMETHODIMP
2439 nsHTMLDocument::GetHeight(PRInt32* aHeight)
2441 NS_ENSURE_ARG_POINTER(aHeight);
2443 PRInt32 width;
2444 return GetBodySize(&width, aHeight);
2447 NS_IMETHODIMP
2448 nsHTMLDocument::GetAlinkColor(nsAString& aAlinkColor)
2450 aAlinkColor.Truncate();
2452 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2454 if (body) {
2455 body->GetALink(aAlinkColor);
2456 } else if (mAttrStyleSheet) {
2457 nscolor color;
2458 nsresult rv = mAttrStyleSheet->GetActiveLinkColor(color);
2459 if (NS_SUCCEEDED(rv)) {
2460 NS_RGBToHex(color, aAlinkColor);
2464 return NS_OK;
2467 NS_IMETHODIMP
2468 nsHTMLDocument::SetAlinkColor(const nsAString& aAlinkColor)
2470 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2472 if (body) {
2473 body->SetALink(aAlinkColor);
2474 } else if (mAttrStyleSheet) {
2475 nsAttrValue value;
2476 if (value.ParseColor(aAlinkColor, this)) {
2477 nscolor color;
2478 value.GetColorValue(color);
2479 mAttrStyleSheet->SetActiveLinkColor(color);
2483 return NS_OK;
2486 NS_IMETHODIMP
2487 nsHTMLDocument::GetLinkColor(nsAString& aLinkColor)
2489 aLinkColor.Truncate();
2491 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2493 if (body) {
2494 body->GetLink(aLinkColor);
2495 } else if (mAttrStyleSheet) {
2496 nscolor color;
2497 nsresult rv = mAttrStyleSheet->GetLinkColor(color);
2498 if (NS_SUCCEEDED(rv)) {
2499 NS_RGBToHex(color, aLinkColor);
2503 return NS_OK;
2506 NS_IMETHODIMP
2507 nsHTMLDocument::SetLinkColor(const nsAString& aLinkColor)
2509 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2511 if (body) {
2512 body->SetLink(aLinkColor);
2513 } else if (mAttrStyleSheet) {
2514 nsAttrValue value;
2515 if (value.ParseColor(aLinkColor, this)) {
2516 nscolor color;
2517 value.GetColorValue(color);
2518 mAttrStyleSheet->SetLinkColor(color);
2522 return NS_OK;
2525 NS_IMETHODIMP
2526 nsHTMLDocument::GetVlinkColor(nsAString& aVlinkColor)
2528 aVlinkColor.Truncate();
2530 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2532 if (body) {
2533 body->GetVLink(aVlinkColor);
2534 } else if (mAttrStyleSheet) {
2535 nscolor color;
2536 nsresult rv = mAttrStyleSheet->GetVisitedLinkColor(color);
2537 if (NS_SUCCEEDED(rv)) {
2538 NS_RGBToHex(color, aVlinkColor);
2542 return NS_OK;
2545 NS_IMETHODIMP
2546 nsHTMLDocument::SetVlinkColor(const nsAString& aVlinkColor)
2548 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2550 if (body) {
2551 body->SetVLink(aVlinkColor);
2552 } else if (mAttrStyleSheet) {
2553 nsAttrValue value;
2554 if (value.ParseColor(aVlinkColor, this)) {
2555 nscolor color;
2556 value.GetColorValue(color);
2557 mAttrStyleSheet->SetVisitedLinkColor(color);
2561 return NS_OK;
2564 NS_IMETHODIMP
2565 nsHTMLDocument::GetBgColor(nsAString& aBgColor)
2567 aBgColor.Truncate();
2569 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2571 if (body) {
2572 body->GetBgColor(aBgColor);
2575 return NS_OK;
2578 NS_IMETHODIMP
2579 nsHTMLDocument::SetBgColor(const nsAString& aBgColor)
2581 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2583 if (body) {
2584 body->SetBgColor(aBgColor);
2586 // XXXldb And otherwise?
2588 return NS_OK;
2591 NS_IMETHODIMP
2592 nsHTMLDocument::GetFgColor(nsAString& aFgColor)
2594 aFgColor.Truncate();
2596 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2598 if (body) {
2599 body->GetText(aFgColor);
2602 return NS_OK;
2605 NS_IMETHODIMP
2606 nsHTMLDocument::SetFgColor(const nsAString& aFgColor)
2608 nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
2610 if (body) {
2611 body->SetText(aFgColor);
2613 // XXXldb And otherwise?
2615 return NS_OK;
2619 NS_IMETHODIMP
2620 nsHTMLDocument::GetEmbeds(nsIDOMHTMLCollection** aEmbeds)
2622 if (!mEmbeds) {
2623 mEmbeds = new nsContentList(this, nsGkAtoms::embed, GetDefaultNamespaceID());
2624 if (!mEmbeds) {
2625 return NS_ERROR_OUT_OF_MEMORY;
2629 *aEmbeds = mEmbeds;
2630 NS_ADDREF(*aEmbeds);
2632 return NS_OK;
2635 NS_IMETHODIMP
2636 nsHTMLDocument::GetSelection(nsAString& aReturn)
2638 aReturn.Truncate();
2640 nsCOMPtr<nsIConsoleService> consoleService
2641 (do_GetService("@mozilla.org/consoleservice;1"));
2643 if (consoleService) {
2644 consoleService->LogStringMessage(NS_LITERAL_STRING("Deprecated method document.getSelection() called. Please use window.getSelection() instead.").get());
2647 nsIDOMWindow *window = GetWindow();
2648 NS_ENSURE_TRUE(window, NS_OK);
2650 nsCOMPtr<nsISelection> selection;
2651 nsresult rv = window->GetSelection(getter_AddRefs(selection));
2652 NS_ENSURE_TRUE(selection && NS_SUCCEEDED(rv), rv);
2654 nsXPIDLString str;
2656 rv = selection->ToString(getter_Copies(str));
2658 aReturn.Assign(str);
2660 return rv;
2663 static void
2664 ReportUseOfDeprecatedMethod(nsHTMLDocument* aDoc, const char* aWarning)
2666 nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
2667 aWarning,
2668 nsnull, 0,
2669 static_cast<nsIDocument*>(aDoc)->
2670 GetDocumentURI(),
2671 EmptyString(), 0, 0,
2672 nsIScriptError::warningFlag,
2673 "DOM Events");
2676 NS_IMETHODIMP
2677 nsHTMLDocument::CaptureEvents(PRInt32 aEventFlags)
2679 ReportUseOfDeprecatedMethod(this, "UseOfCaptureEventsWarning");
2680 return NS_OK;
2683 NS_IMETHODIMP
2684 nsHTMLDocument::ReleaseEvents(PRInt32 aEventFlags)
2686 ReportUseOfDeprecatedMethod(this, "UseOfReleaseEventsWarning");
2687 return NS_OK;
2690 NS_IMETHODIMP
2691 nsHTMLDocument::RouteEvent(nsIDOMEvent* aEvt)
2693 ReportUseOfDeprecatedMethod(this, "UseOfRouteEventWarning");
2694 return NS_OK;
2697 // readonly attribute DOMString compatMode;
2698 // Returns "BackCompat" if we are in quirks mode, "CSS1Compat" if we are
2699 // in almost standards or full standards mode. See bug 105640. This was
2700 // implemented to match MSIE's compatMode property
2701 NS_IMETHODIMP
2702 nsHTMLDocument::GetCompatMode(nsAString& aCompatMode)
2704 NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
2705 mCompatMode == eCompatibility_AlmostStandards ||
2706 mCompatMode == eCompatibility_FullStandards,
2707 "mCompatMode is neither quirks nor strict for this document");
2709 if (mCompatMode == eCompatibility_NavQuirks) {
2710 aCompatMode.AssignLiteral("BackCompat");
2711 } else {
2712 aCompatMode.AssignLiteral("CSS1Compat");
2715 return NS_OK;
2718 // Mapped to document.embeds for NS4 compatibility
2719 NS_IMETHODIMP
2720 nsHTMLDocument::GetPlugins(nsIDOMHTMLCollection** aPlugins)
2722 *aPlugins = nsnull;
2724 return GetEmbeds(aPlugins);
2727 static void
2728 FindNamedItems(nsIAtom* aName, nsIContent *aContent,
2729 nsIdentifierMapEntry* aEntry)
2731 NS_ASSERTION(aEntry->HasNameContentList(),
2732 "Entry w/o content list passed to FindNamedItems()!");
2733 NS_ASSERTION(!aEntry->IsInvalidName(),
2734 "Entry that should never have a list passed to FindNamedItems()!");
2736 if (aContent->IsNodeOfType(nsINode::eTEXT)) {
2737 // Text nodes are not named items nor can they have children.
2738 return;
2741 if (aName == nsContentUtils::IsNamedItem(aContent)) {
2742 aEntry->AddNameContent(aContent);
2745 if (!aEntry->GetIdContent() &&
2746 // Maybe this node has the right id?
2747 aName == aContent->GetID()) {
2748 aEntry->AddIdContent(aContent);
2751 PRUint32 i, count = aContent->GetChildCount();
2752 for (i = 0; i < count; ++i) {
2753 FindNamedItems(aName, aContent->GetChildAt(i), aEntry);
2757 nsresult
2758 nsHTMLDocument::ResolveName(const nsAString& aName,
2759 nsIDOMHTMLFormElement *aForm,
2760 nsISupports **aResult)
2762 *aResult = nsnull;
2764 if (!mIsRegularHTML) {
2765 // We don't dynamically resolve names on non-HTML documents.
2766 return NS_OK;
2769 nsCOMPtr<nsIAtom> name(do_GetAtom(aName));
2771 // We have built a table and cache the named items. The table will
2772 // be updated as content is added and removed.
2773 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(name);
2774 NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
2776 if (entry->IsInvalidName()) {
2777 // There won't be any named items by this name -- it's reserved
2778 return NS_OK;
2781 // Now we know we _might_ have items. Before looking at
2782 // entry->mNameContentList, make sure to flush out content (see
2783 // bug 69826).
2784 // This is a perf killer while the document is loading!
2786 // Make sure to stash away the current generation so we can check whether the
2787 // table changes when we flush.
2788 PRUint32 generation = mIdentifierMap.GetGeneration();
2790 // If we already have an entry->mNameContentList, we need to flush out
2791 // notifications too, so that it will get updated properly.
2792 FlushPendingNotifications(entry->HasNameContentList() ?
2793 Flush_ContentAndNotify : Flush_Content);
2795 if (generation != mIdentifierMap.GetGeneration()) {
2796 // Table changed, so the entry pointer is no longer valid; look up the
2797 // entry again, adding if necessary (the adding may be necessary in case
2798 // the flush actually deleted entries).
2799 entry = mIdentifierMap.PutEntry(name);
2800 NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
2803 if (!entry->HasNameContentList()) {
2804 #ifdef DEBUG_jst
2806 printf ("nsHTMLDocument name cache miss for name '%s'\n",
2807 NS_ConvertUTF16toUTF8(aName).get());
2809 #endif
2811 nsresult rv = entry->CreateNameContentList();
2812 if (NS_FAILED(rv))
2813 return rv;
2815 nsIContent* root = GetRootContent();
2816 if (root && !aName.IsEmpty()) {
2817 FindNamedItems(name, root, entry);
2821 nsBaseContentList *list = entry->GetNameContentList();
2823 PRUint32 length;
2824 list->GetLength(&length);
2826 if (length > 0) {
2827 if (length == 1) {
2828 // Only one element in the list, return the element instead of
2829 // returning the list
2831 nsCOMPtr<nsIDOMNode> node;
2833 list->Item(0, getter_AddRefs(node));
2835 nsCOMPtr<nsIContent> ourContent(do_QueryInterface(node));
2836 if (aForm && ourContent &&
2837 !nsContentUtils::BelongsInForm(aForm, ourContent)) {
2838 // This is not the content you are looking for
2839 node = nsnull;
2842 *aResult = node;
2843 NS_IF_ADDREF(*aResult);
2845 return NS_OK;
2848 // The list contains more than one element, return the whole
2849 // list, unless...
2851 if (aForm) {
2852 // ... we're called from a form, in that case we create a
2853 // nsFormNameContentList which will filter out the elements in the
2854 // list that don't belong to aForm
2856 nsFormContentList *fc_list = new nsFormContentList(aForm, *list);
2857 NS_ENSURE_TRUE(fc_list, NS_ERROR_OUT_OF_MEMORY);
2859 PRUint32 len;
2860 fc_list->GetLength(&len);
2862 if (len < 2) {
2863 // After the nsFormContentList is done filtering there's either
2864 // nothing or one element in the list. Return that element, or null
2865 // if there's no element in the list.
2867 nsCOMPtr<nsIDOMNode> node;
2869 fc_list->Item(0, getter_AddRefs(node));
2871 NS_IF_ADDREF(*aResult = node);
2873 delete fc_list;
2875 return NS_OK;
2878 list = fc_list;
2881 return CallQueryInterface(list, aResult);
2884 // No named items were found, see if there's one registerd by id for
2885 // aName. If we get this far, FindNamedItems() will have been called
2886 // for aName, so we're guaranteed that if there is an element with
2887 // the id aName, it'll be entry's IdContent.
2889 nsIContent *e = entry->GetIdContent();
2891 if (e && e->IsNodeOfType(nsINode::eHTML)) {
2892 nsIAtom *tag = e->Tag();
2894 if ((tag == nsGkAtoms::embed ||
2895 tag == nsGkAtoms::img ||
2896 tag == nsGkAtoms::object ||
2897 tag == nsGkAtoms::applet) &&
2898 (!aForm || nsContentUtils::BelongsInForm(aForm, e))) {
2899 NS_ADDREF(*aResult = e);
2903 return NS_OK;
2906 // Pre-fill the name hash with names that are likely to be resolved in
2907 // this document to avoid walking the tree looking for elements with
2908 // these names.
2910 nsresult
2911 nsHTMLDocument::PrePopulateIdentifierMap()
2913 static const char names[][13] = {
2914 "write", "writeln", "open", "close", "forms", "elements",
2915 "characterSet", "nodeType", "parentNode", "cookie"
2918 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(names); ++i) {
2919 nsCOMPtr<nsIAtom> atom(do_GetAtom(names[i]));
2920 NS_ENSURE_TRUE(atom, NS_ERROR_OUT_OF_MEMORY);
2922 nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(atom);
2923 NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
2925 entry->SetInvalidName();
2928 return NS_OK;
2931 //----------------------------
2933 /* virtual */ nsIContent*
2934 nsHTMLDocument::GetBodyContentExternal()
2936 return GetBodyContent();
2939 nsIContent*
2940 nsHTMLDocument::GetHtmlContent()
2942 nsIContent* rootContent = GetRootContent();
2943 if (rootContent && rootContent->Tag() == nsGkAtoms::html &&
2944 rootContent->IsNodeOfType(nsINode::eHTML))
2945 return rootContent;
2946 return nsnull;
2949 nsIContent*
2950 nsHTMLDocument::GetBodyContent()
2952 nsIContent* html = GetHtmlContent();
2953 if (!html)
2954 return nsnull;
2956 // Look for body inside html. This needs to run forwards to find
2957 // the first body element.
2958 for (PRUint32 i = 0; i < html->GetChildCount(); ++i) {
2959 nsIContent* body = html->GetChildAt(i);
2960 if (body->Tag() == nsGkAtoms::body &&
2961 body->IsNodeOfType(nsINode::eHTML))
2962 return body;
2964 return nsnull;
2967 // forms related stuff
2969 NS_IMETHODIMP
2970 nsHTMLDocument::GetForms(nsIDOMHTMLCollection** aForms)
2972 nsContentList *forms = nsHTMLDocument::GetForms();
2973 if (!forms)
2974 return NS_ERROR_OUT_OF_MEMORY;
2976 NS_ADDREF(*aForms = forms);
2977 return NS_OK;
2980 nsContentList*
2981 nsHTMLDocument::GetForms()
2983 if (!mForms)
2984 mForms = new nsContentList(this, nsGkAtoms::form, GetDefaultNamespaceID());
2986 return mForms;
2989 static PRBool MatchFormControls(nsIContent* aContent, PRInt32 aNamespaceID,
2990 nsIAtom* aAtom, void* aData)
2992 return aContent->IsNodeOfType(nsIContent::eHTML_FORM_CONTROL);
2995 nsContentList*
2996 nsHTMLDocument::GetFormControls()
2998 if (!mFormControls) {
2999 mFormControls = new nsContentList(this, MatchFormControls, nsnull, nsnull);
3002 return mFormControls;
3005 nsresult
3006 nsHTMLDocument::CreateAndAddWyciwygChannel(void)
3008 nsresult rv = NS_OK;
3009 nsCAutoString url, originalSpec;
3011 mDocumentURI->GetSpec(originalSpec);
3013 // Generate the wyciwyg url
3014 url = NS_LITERAL_CSTRING("wyciwyg://")
3015 + nsPrintfCString("%d", gWyciwygSessionCnt++)
3016 + NS_LITERAL_CSTRING("/")
3017 + originalSpec;
3019 nsCOMPtr<nsIURI> wcwgURI;
3020 NS_NewURI(getter_AddRefs(wcwgURI), url);
3022 // Create the nsIWyciwygChannel to store out-of-band
3023 // document.write() script to cache
3024 nsCOMPtr<nsIChannel> channel;
3025 // Create a wyciwyg Channel
3026 rv = NS_NewChannel(getter_AddRefs(channel), wcwgURI);
3027 NS_ENSURE_SUCCESS(rv, rv);
3029 mWyciwygChannel = do_QueryInterface(channel);
3031 mWyciwygChannel->SetSecurityInfo(mSecurityInfo);
3033 // Note: we want to treat this like a "previous document" hint so that,
3034 // e.g. a <meta> tag in the document.write content can override it.
3035 SetDocumentCharacterSetSource(kCharsetFromHintPrevDoc);
3036 mWyciwygChannel->SetCharsetAndSource(kCharsetFromHintPrevDoc,
3037 GetDocumentCharacterSet());
3039 // Use our new principal
3040 channel->SetOwner(NodePrincipal());
3042 // Inherit load flags from the original document's channel
3043 channel->SetLoadFlags(mLoadFlags);
3045 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
3047 // Use the Parent document's loadgroup to trigger load notifications
3048 if (loadGroup && channel) {
3049 rv = channel->SetLoadGroup(loadGroup);
3050 NS_ENSURE_SUCCESS(rv, rv);
3052 nsLoadFlags loadFlags = 0;
3053 channel->GetLoadFlags(&loadFlags);
3054 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
3055 channel->SetLoadFlags(loadFlags);
3057 channel->SetOriginalURI(wcwgURI);
3059 rv = loadGroup->AddRequest(mWyciwygChannel, nsnull);
3060 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to add request to load group.");
3063 return rv;
3066 nsresult
3067 nsHTMLDocument::RemoveWyciwygChannel(void)
3069 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
3071 // note there can be a write request without a load group if
3072 // this is a synchronously constructed about:blank document
3073 if (loadGroup && mWyciwygChannel) {
3074 mWyciwygChannel->CloseCacheEntry(NS_OK);
3075 loadGroup->RemoveRequest(mWyciwygChannel, nsnull, NS_OK);
3078 mWyciwygChannel = nsnull;
3080 return NS_OK;
3083 void *
3084 nsHTMLDocument::GenerateParserKey(void)
3086 if (!mScriptLoader) {
3087 // If we don't have a script loader, then the parser probably isn't parsing
3088 // anything anyway, so just return null.
3089 return nsnull;
3092 // The script loader provides us with the currently executing script element,
3093 // which is guaranteed to be unique per script.
3094 return mScriptLoader->GetCurrentScript();
3097 /* attribute DOMString designMode; */
3098 NS_IMETHODIMP
3099 nsHTMLDocument::GetDesignMode(nsAString & aDesignMode)
3101 if (HasFlag(NODE_IS_EDITABLE)) {
3102 aDesignMode.AssignLiteral("on");
3104 else {
3105 aDesignMode.AssignLiteral("off");
3107 return NS_OK;
3110 void
3111 nsHTMLDocument::EndUpdate(nsUpdateType aUpdateType)
3113 nsDocument::EndUpdate(aUpdateType);
3115 if (mUpdateNestLevel == 0 && mContentEditableCount > 0 != IsEditingOn()) {
3116 EditingStateChanged();
3120 nsresult
3121 nsHTMLDocument::ChangeContentEditableCount(nsIContent *aElement,
3122 PRInt32 aChange)
3124 NS_ASSERTION(mContentEditableCount + aChange >= 0,
3125 "Trying to decrement too much.");
3127 mContentEditableCount += aChange;
3129 if (mParser ||
3130 (mUpdateNestLevel > 0 && mContentEditableCount > 0 != IsEditingOn())) {
3131 return NS_OK;
3134 EditingState oldState = mEditingState;
3136 nsresult rv = EditingStateChanged();
3137 NS_ENSURE_SUCCESS(rv, rv);
3139 if (oldState == mEditingState && mEditingState == eContentEditable) {
3140 // We just changed the contentEditable state of a node, we need to reset
3141 // the spellchecking state of that node.
3142 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
3143 if (node) {
3144 nsPIDOMWindow *window = GetWindow();
3145 if (!window)
3146 return NS_ERROR_FAILURE;
3148 nsIDocShell *docshell = window->GetDocShell();
3149 if (!docshell)
3150 return NS_ERROR_FAILURE;
3152 nsCOMPtr<nsIEditorDocShell> editorDocShell =
3153 do_QueryInterface(docshell, &rv);
3154 NS_ENSURE_SUCCESS(rv, rv);
3156 nsCOMPtr<nsIEditor> editor;
3157 editorDocShell->GetEditor(getter_AddRefs(editor));
3158 if (editor) {
3159 nsCOMPtr<nsIDOMRange> range;
3160 rv = NS_NewRange(getter_AddRefs(range));
3161 NS_ENSURE_SUCCESS(rv, rv);
3163 rv = range->SelectNode(node);
3164 NS_ENSURE_SUCCESS(rv, rv);
3166 nsCOMPtr<nsIInlineSpellChecker> spellChecker;
3167 rv = editor->GetInlineSpellChecker(PR_FALSE,
3168 getter_AddRefs(spellChecker));
3169 NS_ENSURE_SUCCESS(rv, rv);
3171 if (spellChecker) {
3172 rv = spellChecker->SpellCheckRange(range);
3173 NS_ENSURE_SUCCESS(rv, rv);
3179 return NS_OK;
3182 static PRBool
3183 DocAllResultMatch(nsIContent* aContent, PRInt32 aNamespaceID, nsIAtom* aAtom,
3184 void* aData)
3186 if (aContent->GetID() == aAtom) {
3187 return PR_TRUE;
3190 nsGenericHTMLElement* elm = nsGenericHTMLElement::FromContent(aContent);
3191 if (!elm || aContent->GetNameSpaceID() != kNameSpaceID_None) {
3192 return PR_FALSE;
3195 nsIAtom* tag = elm->Tag();
3196 if (tag != nsGkAtoms::img &&
3197 tag != nsGkAtoms::form &&
3198 tag != nsGkAtoms::applet &&
3199 tag != nsGkAtoms::embed &&
3200 tag != nsGkAtoms::object &&
3201 tag != nsGkAtoms::input) {
3202 return PR_FALSE;
3205 const nsAttrValue* val = elm->GetParsedAttr(nsGkAtoms::name);
3206 return val && val->Type() == nsAttrValue::eAtom &&
3207 val->GetAtomValue() == aAtom;
3211 nsresult
3212 nsHTMLDocument::GetDocumentAllResult(const nsAString& aID, nsISupports** aResult)
3214 *aResult = nsnull;
3216 nsCOMPtr<nsIAtom> id = do_GetAtom(aID);
3217 nsIdentifierMapEntry *entry;
3218 if (IdTableIsLive()) {
3219 entry = mIdentifierMap.GetEntry(id);
3220 // If we did a lookup and it failed, there are no items with this id
3221 if (!entry)
3222 return NS_OK;
3223 } else {
3224 entry = mIdentifierMap.PutEntry(id);
3225 NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
3228 nsIContent* root = GetRootContent();
3229 if (!root) {
3230 return NS_OK;
3233 nsRefPtr<nsContentList> docAllList = entry->GetDocAllList();
3234 if (!docAllList) {
3235 docAllList = new nsContentList(root, DocAllResultMatch,
3236 nsnull, nsnull, PR_TRUE, id);
3237 NS_ENSURE_TRUE(docAllList, NS_ERROR_OUT_OF_MEMORY);
3238 entry->SetDocAllList(docAllList);
3241 // Check if there are more than 1 entries. Do this by getting the second one
3242 // rather than the length since getting the length always requires walking
3243 // the entire document.
3245 nsIContent* cont = docAllList->Item(1, PR_TRUE);
3246 if (cont) {
3247 NS_ADDREF(*aResult = static_cast<nsIDOMNodeList*>(docAllList));
3248 return NS_OK;
3251 // There's only 0 or 1 items. Return the first one or null.
3252 NS_IF_ADDREF(*aResult = docAllList->Item(0, PR_TRUE));
3254 return NS_OK;
3257 static void
3258 NotifyEditableStateChange(nsINode *aNode, nsIDocument *aDocument,
3259 PRBool aEditable)
3261 PRUint32 i, n = aNode->GetChildCount();
3262 for (i = 0; i < n; ++i) {
3263 nsIContent *child = aNode->GetChildAt(i);
3264 if (child->HasFlag(NODE_IS_EDITABLE) != aEditable) {
3265 aDocument->ContentStatesChanged(child, nsnull,
3266 NS_EVENT_STATE_MOZ_READONLY |
3267 NS_EVENT_STATE_MOZ_READWRITE);
3269 NotifyEditableStateChange(child, aDocument, aEditable);
3273 void
3274 nsHTMLDocument::TearingDownEditor(nsIEditor *aEditor)
3276 if (IsEditingOn()) {
3277 mEditingState = eTearingDown;
3279 nsCOMPtr<nsIEditorStyleSheets> editorss = do_QueryInterface(aEditor);
3280 if (editorss) {
3281 editorss->RemoveOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/contenteditable.css"));
3282 if (mEditingState == eDesignMode)
3283 editorss->RemoveOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/designmode.css"));
3288 nsresult
3289 nsHTMLDocument::TurnEditingOff()
3291 NS_ASSERTION(mEditingState != eOff, "Editing is already off.");
3293 nsPIDOMWindow *window = GetWindow();
3294 if (!window)
3295 return NS_ERROR_FAILURE;
3297 nsIDocShell *docshell = window->GetDocShell();
3298 if (!docshell)
3299 return NS_ERROR_FAILURE;
3301 nsresult rv;
3302 nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell, &rv);
3303 NS_ENSURE_SUCCESS(rv, rv);
3305 // turn editing off
3306 rv = editSession->TearDownEditorOnWindow(window);
3307 NS_ENSURE_SUCCESS(rv, rv);
3309 mEditingState = eOff;
3311 return NS_OK;
3314 static PRBool HasPresShell(nsPIDOMWindow *aWindow)
3316 nsIDocShell *docShell = aWindow->GetDocShell();
3317 if (!docShell)
3318 return PR_FALSE;
3319 nsCOMPtr<nsIPresShell> presShell;
3320 docShell->GetPresShell(getter_AddRefs(presShell));
3321 return presShell != nsnull;
3324 nsresult
3325 nsHTMLDocument::SetEditingState(EditingState aState)
3327 mEditingState = aState;
3328 return NS_OK;
3331 nsresult
3332 nsHTMLDocument::EditingStateChanged()
3334 if (mRemovedFromDocShell) {
3335 return NS_OK;
3338 if (mEditingState == eSettingUp || mEditingState == eTearingDown) {
3339 // XXX We shouldn't recurse.
3340 return NS_OK;
3343 PRBool designMode = HasFlag(NODE_IS_EDITABLE);
3344 EditingState newState = designMode ? eDesignMode :
3345 (mContentEditableCount > 0 ? eContentEditable : eOff);
3346 if (mEditingState == newState) {
3347 // No changes in editing mode.
3348 return NS_OK;
3351 if (newState == eOff) {
3352 // Editing is being turned off.
3353 return TurnEditingOff();
3356 // get editing session
3357 nsPIDOMWindow *window = GetWindow();
3358 if (!window)
3359 return NS_ERROR_FAILURE;
3361 nsIDocShell *docshell = window->GetDocShell();
3362 if (!docshell)
3363 return NS_ERROR_FAILURE;
3365 nsresult rv;
3366 nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell, &rv);
3367 NS_ENSURE_SUCCESS(rv, rv);
3369 if (!HasPresShell(window)) {
3370 // We should not make the window editable or setup its editor.
3371 // It's probably style=display:none.
3372 return NS_OK;
3375 PRBool makeWindowEditable = mEditingState == eOff;
3376 if (makeWindowEditable) {
3377 // Editing is being turned on (through designMode or contentEditable)
3378 // Turn on editor.
3379 // XXX This can cause flushing which can change the editing state, so make
3380 // sure to avoid recursing.
3381 EditingState oldState = mEditingState;
3382 mEditingState = eSettingUp;
3384 rv = editSession->MakeWindowEditable(window, "html", PR_FALSE, PR_FALSE,
3385 PR_TRUE);
3386 NS_ENSURE_SUCCESS(rv, rv);
3388 mEditingState = oldState;
3391 // XXX Need to call TearDownEditorOnWindow for all failures.
3392 nsCOMPtr<nsIEditorDocShell> editorDocShell =
3393 do_QueryInterface(docshell, &rv);
3394 NS_ENSURE_SUCCESS(rv, rv);
3396 nsCOMPtr<nsIEditor> editor;
3397 editorDocShell->GetEditor(getter_AddRefs(editor));
3398 if (!editor)
3399 return NS_ERROR_FAILURE;
3401 nsCOMPtr<nsIEditorStyleSheets> editorss = do_QueryInterface(editor, &rv);
3402 NS_ENSURE_SUCCESS(rv, rv);
3404 editorss->AddOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/contenteditable.css"));
3406 // Should we update the editable state of all the nodes in the document? We
3407 // need to do this when the designMode value changes, as that overrides
3408 // specific states on the elements.
3409 PRBool updateState;
3411 PRBool spellRecheckAll = PR_FALSE;
3412 if (designMode) {
3413 // designMode is being turned on (overrides contentEditable).
3414 editorss->AddOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/designmode.css"));
3416 // Disable scripting and plugins.
3417 rv = editSession->DisableJSAndPlugins(window);
3418 NS_ENSURE_SUCCESS(rv, rv);
3420 updateState = PR_TRUE;
3421 spellRecheckAll = mEditingState == eContentEditable;
3423 else if (mEditingState == eDesignMode) {
3424 // designMode is being turned off (contentEditable is still on).
3425 editorss->RemoveOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/designmode.css"));
3427 rv = editSession->RestoreJSAndPlugins(window);
3428 NS_ENSURE_SUCCESS(rv, rv);
3430 updateState = PR_TRUE;
3432 else {
3433 // contentEditable is being turned on (and designMode is off).
3434 updateState = PR_FALSE;
3437 mEditingState = newState;
3439 if (makeWindowEditable) {
3440 // Set the editor to not insert br's on return when in p
3441 // elements by default.
3442 // XXX Do we only want to do this for designMode?
3443 PRBool unused;
3444 rv = ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), PR_FALSE,
3445 NS_LITERAL_STRING("false"), &unused);
3447 if (NS_FAILED(rv)) {
3448 // Editor setup failed. Editing is not on after all.
3449 // XXX Should we reset the editable flag on nodes?
3450 editSession->TearDownEditorOnWindow(window);
3451 mEditingState = eOff;
3453 return rv;
3457 if (updateState) {
3458 mozAutoDocUpdate upd(this, UPDATE_CONTENT_STATE, PR_TRUE);
3459 NotifyEditableStateChange(this, this, !designMode);
3462 // Resync the editor's spellcheck state.
3463 if (spellRecheckAll) {
3464 nsCOMPtr<nsISelectionController> selcon;
3465 nsresult rv = editor->GetSelectionController(getter_AddRefs(selcon));
3466 NS_ENSURE_SUCCESS(rv, rv);
3468 nsCOMPtr<nsISelection> spellCheckSelection;
3469 rv = selcon->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
3470 getter_AddRefs(spellCheckSelection));
3471 if (NS_SUCCEEDED(rv)) {
3472 spellCheckSelection->RemoveAllRanges();
3475 editor->SyncRealTimeSpell();
3477 return NS_OK;
3480 NS_IMETHODIMP
3481 nsHTMLDocument::SetDesignMode(const nsAString & aDesignMode)
3483 nsresult rv = NS_OK;
3485 if (!nsContentUtils::IsCallerTrustedForWrite()) {
3486 nsCOMPtr<nsIPrincipal> subject;
3487 nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
3488 rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
3489 NS_ENSURE_SUCCESS(rv, rv);
3490 if (subject) {
3491 PRBool subsumes;
3492 rv = subject->Subsumes(NodePrincipal(), &subsumes);
3493 NS_ENSURE_SUCCESS(rv, rv);
3495 NS_ENSURE_TRUE(subsumes, NS_ERROR_DOM_PROP_ACCESS_DENIED);
3499 PRBool editableMode = HasFlag(NODE_IS_EDITABLE);
3500 if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
3501 SetEditableFlag(!editableMode);
3503 return EditingStateChanged();
3506 return NS_OK;
3509 nsresult
3510 nsHTMLDocument::GetMidasCommandManager(nsICommandManager** aCmdMgr)
3512 // initialize return value
3513 NS_ENSURE_ARG_POINTER(aCmdMgr);
3515 // check if we have it cached
3516 if (mMidasCommandManager) {
3517 NS_ADDREF(*aCmdMgr = mMidasCommandManager);
3518 return NS_OK;
3521 *aCmdMgr = nsnull;
3523 nsPIDOMWindow *window = GetWindow();
3524 if (!window)
3525 return NS_ERROR_FAILURE;
3527 nsIDocShell *docshell = window->GetDocShell();
3528 if (!docshell)
3529 return NS_ERROR_FAILURE;
3531 mMidasCommandManager = do_GetInterface(docshell);
3532 if (!mMidasCommandManager)
3533 return NS_ERROR_FAILURE;
3535 NS_ADDREF(*aCmdMgr = mMidasCommandManager);
3537 return NS_OK;
3541 struct MidasCommand {
3542 const char* incomingCommandString;
3543 const char* internalCommandString;
3544 const char* internalParamString;
3545 PRPackedBool useNewParam;
3546 PRPackedBool convertToBoolean;
3549 static const struct MidasCommand gMidasCommandTable[] = {
3550 { "bold", "cmd_bold", "", PR_TRUE, PR_FALSE },
3551 { "italic", "cmd_italic", "", PR_TRUE, PR_FALSE },
3552 { "underline", "cmd_underline", "", PR_TRUE, PR_FALSE },
3553 { "strikethrough", "cmd_strikethrough", "", PR_TRUE, PR_FALSE },
3554 { "subscript", "cmd_subscript", "", PR_TRUE, PR_FALSE },
3555 { "superscript", "cmd_superscript", "", PR_TRUE, PR_FALSE },
3556 { "cut", "cmd_cut", "", PR_TRUE, PR_FALSE },
3557 { "copy", "cmd_copy", "", PR_TRUE, PR_FALSE },
3558 { "paste", "cmd_paste", "", PR_TRUE, PR_FALSE },
3559 { "delete", "cmd_delete", "", PR_TRUE, PR_FALSE },
3560 { "selectall", "cmd_selectAll", "", PR_TRUE, PR_FALSE },
3561 { "undo", "cmd_undo", "", PR_TRUE, PR_FALSE },
3562 { "redo", "cmd_redo", "", PR_TRUE, PR_FALSE },
3563 { "indent", "cmd_indent", "", PR_TRUE, PR_FALSE },
3564 { "outdent", "cmd_outdent", "", PR_TRUE, PR_FALSE },
3565 { "backcolor", "cmd_backgroundColor", "", PR_FALSE, PR_FALSE },
3566 { "forecolor", "cmd_fontColor", "", PR_FALSE, PR_FALSE },
3567 { "hilitecolor", "cmd_highlight", "", PR_FALSE, PR_FALSE },
3568 { "fontname", "cmd_fontFace", "", PR_FALSE, PR_FALSE },
3569 { "fontsize", "cmd_fontSize", "", PR_FALSE, PR_FALSE },
3570 { "increasefontsize", "cmd_increaseFont", "", PR_FALSE, PR_FALSE },
3571 { "decreasefontsize", "cmd_decreaseFont", "", PR_FALSE, PR_FALSE },
3572 { "inserthorizontalrule", "cmd_insertHR", "", PR_TRUE, PR_FALSE },
3573 { "createlink", "cmd_insertLinkNoUI", "", PR_FALSE, PR_FALSE },
3574 { "insertimage", "cmd_insertImageNoUI", "", PR_FALSE, PR_FALSE },
3575 { "inserthtml", "cmd_insertHTML", "", PR_FALSE, PR_FALSE },
3576 { "gethtml", "cmd_getContents", "", PR_FALSE, PR_FALSE },
3577 { "justifyleft", "cmd_align", "left", PR_TRUE, PR_FALSE },
3578 { "justifyright", "cmd_align", "right", PR_TRUE, PR_FALSE },
3579 { "justifycenter", "cmd_align", "center", PR_TRUE, PR_FALSE },
3580 { "justifyfull", "cmd_align", "justify", PR_TRUE, PR_FALSE },
3581 { "removeformat", "cmd_removeStyles", "", PR_TRUE, PR_FALSE },
3582 { "unlink", "cmd_removeLinks", "", PR_TRUE, PR_FALSE },
3583 { "insertorderedlist", "cmd_ol", "", PR_TRUE, PR_FALSE },
3584 { "insertunorderedlist", "cmd_ul", "", PR_TRUE, PR_FALSE },
3585 { "insertparagraph", "cmd_paragraphState", "p", PR_TRUE, PR_FALSE },
3586 { "formatblock", "cmd_paragraphState", "", PR_FALSE, PR_FALSE },
3587 { "heading", "cmd_paragraphState", "", PR_FALSE, PR_FALSE },
3588 { "styleWithCSS", "cmd_setDocumentUseCSS", "", PR_FALSE, PR_TRUE },
3589 { "contentReadOnly", "cmd_setDocumentReadOnly", "", PR_FALSE, PR_TRUE },
3590 { "insertBrOnReturn", "cmd_insertBrOnReturn", "", PR_FALSE, PR_TRUE },
3591 { "enableObjectResizing", "cmd_enableObjectResizing", "", PR_FALSE, PR_TRUE },
3592 { "enableInlineTableEditing", "cmd_enableInlineTableEditing", "", PR_FALSE, PR_TRUE },
3593 #if 0
3594 // no editor support to remove alignments right now
3595 { "justifynone", "cmd_align", "", PR_TRUE, PR_FALSE },
3597 // the following will need special review before being turned on
3598 { "saveas", "cmd_saveAs", "", PR_TRUE, PR_FALSE },
3599 { "print", "cmd_print", "", PR_TRUE, PR_FALSE },
3600 #endif
3601 { NULL, NULL, NULL, PR_FALSE, PR_FALSE }
3604 #define MidasCommandCount ((sizeof(gMidasCommandTable) / sizeof(struct MidasCommand)) - 1)
3606 static const char* const gBlocks[] = {
3607 "ADDRESS",
3608 "BLOCKQUOTE",
3609 "DD",
3610 "DIV",
3611 "DL",
3612 "DT",
3613 "H1",
3614 "H2",
3615 "H3",
3616 "H4",
3617 "H5",
3618 "H6",
3619 "P",
3620 "PRE"
3623 static PRBool
3624 ConvertToMidasInternalCommandInner(const nsAString & inCommandID,
3625 const nsAString & inParam,
3626 nsACString& outCommandID,
3627 nsACString& outParam,
3628 PRBool& outIsBoolean,
3629 PRBool& outBooleanValue,
3630 PRBool aIgnoreParams)
3632 NS_ConvertUTF16toUTF8 convertedCommandID(inCommandID);
3634 // Hack to support old boolean commands that were backwards (see bug 301490).
3635 PRBool invertBool = PR_FALSE;
3636 if (convertedCommandID.LowerCaseEqualsLiteral("usecss")) {
3637 convertedCommandID.Assign("styleWithCSS");
3638 invertBool = PR_TRUE;
3640 else if (convertedCommandID.LowerCaseEqualsLiteral("readonly")) {
3641 convertedCommandID.Assign("contentReadOnly");
3642 invertBool = PR_TRUE;
3645 PRUint32 i;
3646 PRBool found = PR_FALSE;
3647 for (i = 0; i < MidasCommandCount; ++i) {
3648 if (convertedCommandID.Equals(gMidasCommandTable[i].incomingCommandString,
3649 nsCaseInsensitiveCStringComparator())) {
3650 found = PR_TRUE;
3651 break;
3655 if (found) {
3656 // set outCommandID (what we use internally)
3657 outCommandID.Assign(gMidasCommandTable[i].internalCommandString);
3659 // set outParam & outIsBoolean based on flags from the table
3660 outIsBoolean = gMidasCommandTable[i].convertToBoolean;
3662 if (!aIgnoreParams) {
3663 if (gMidasCommandTable[i].useNewParam) {
3664 outParam.Assign(gMidasCommandTable[i].internalParamString);
3666 else {
3667 // handle checking of param passed in
3668 if (outIsBoolean) {
3669 // if this is a boolean value and it's not explicitly false
3670 // (e.g. no value) we default to "true". For old backwards commands
3671 // we invert the check (see bug 301490).
3672 if (invertBool) {
3673 outBooleanValue = inParam.LowerCaseEqualsLiteral("false");
3675 else {
3676 outBooleanValue = !inParam.LowerCaseEqualsLiteral("false");
3678 outParam.Truncate();
3680 else {
3681 // check to see if we need to convert the parameter
3682 if (outCommandID.EqualsLiteral("cmd_paragraphState")) {
3683 const PRUnichar *start = inParam.BeginReading();
3684 const PRUnichar *end = inParam.EndReading();
3685 if (start != end && *start == '<' && *(end - 1) == '>') {
3686 ++start;
3687 --end;
3690 NS_ConvertUTF16toUTF8 convertedParam(Substring(start, end));
3691 PRUint32 j;
3692 for (j = 0; j < NS_ARRAY_LENGTH(gBlocks); ++j) {
3693 if (convertedParam.Equals(gBlocks[j],
3694 nsCaseInsensitiveCStringComparator())) {
3695 outParam.Assign(gBlocks[j]);
3696 break;
3700 return j != NS_ARRAY_LENGTH(gBlocks);
3702 else {
3703 CopyUTF16toUTF8(inParam, outParam);
3708 } // end else for useNewParam (do convert existing param)
3709 else {
3710 // reset results if the command is not found in our table
3711 outCommandID.SetLength(0);
3712 outParam.SetLength(0);
3713 outIsBoolean = PR_FALSE;
3716 return found;
3719 static PRBool
3720 ConvertToMidasInternalCommand(const nsAString & inCommandID,
3721 const nsAString & inParam,
3722 nsACString& outCommandID,
3723 nsACString& outParam,
3724 PRBool& outIsBoolean,
3725 PRBool& outBooleanValue)
3727 return ConvertToMidasInternalCommandInner(inCommandID, inParam, outCommandID,
3728 outParam, outIsBoolean,
3729 outBooleanValue, PR_FALSE);
3732 static PRBool
3733 ConvertToMidasInternalCommand(const nsAString & inCommandID,
3734 nsACString& outCommandID)
3736 nsCAutoString dummyCString;
3737 nsAutoString dummyString;
3738 PRBool dummyBool;
3739 return ConvertToMidasInternalCommandInner(inCommandID, dummyString,
3740 outCommandID, dummyCString,
3741 dummyBool, dummyBool, PR_TRUE);
3744 jsval
3745 nsHTMLDocument::sCutCopyInternal_id = JSVAL_VOID;
3746 jsval
3747 nsHTMLDocument::sPasteInternal_id = JSVAL_VOID;
3749 /* Helper function to check security of clipboard commands. If aPaste is */
3750 /* true, we check paste, else we check cutcopy */
3751 nsresult
3752 nsHTMLDocument::DoClipboardSecurityCheck(PRBool aPaste)
3754 nsresult rv = NS_ERROR_FAILURE;
3756 nsCOMPtr<nsIJSContextStack> stack =
3757 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
3759 if (stack) {
3760 JSContext *cx = nsnull;
3761 stack->Peek(&cx);
3762 if (!cx) {
3763 return NS_OK;
3766 JSAutoRequest ar(cx);
3768 NS_NAMED_LITERAL_CSTRING(classNameStr, "Clipboard");
3770 nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
3772 if (aPaste) {
3773 if (nsHTMLDocument::sPasteInternal_id == JSVAL_VOID) {
3774 nsHTMLDocument::sPasteInternal_id =
3775 STRING_TO_JSVAL(::JS_InternString(cx, "paste"));
3777 rv = secMan->CheckPropertyAccess(cx, nsnull, classNameStr.get(),
3778 nsHTMLDocument::sPasteInternal_id,
3779 nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
3780 } else {
3781 if (nsHTMLDocument::sCutCopyInternal_id == JSVAL_VOID) {
3782 nsHTMLDocument::sCutCopyInternal_id =
3783 STRING_TO_JSVAL(::JS_InternString(cx, "cutcopy"));
3785 rv = secMan->CheckPropertyAccess(cx, nsnull, classNameStr.get(),
3786 nsHTMLDocument::sCutCopyInternal_id,
3787 nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
3790 return rv;
3793 /* TODO: don't let this call do anything if the page is not done loading */
3794 /* boolean execCommand(in DOMString commandID, in boolean doShowUI,
3795 in DOMString value); */
3796 NS_IMETHODIMP
3797 nsHTMLDocument::ExecCommand(const nsAString & commandID,
3798 PRBool doShowUI,
3799 const nsAString & value,
3800 PRBool *_retval)
3802 NS_ENSURE_ARG_POINTER(_retval);
3804 // for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
3805 // this might add some ugly JS dependencies?
3807 *_retval = PR_FALSE;
3809 // if editing is not on, bail
3810 if (!IsEditingOn())
3811 return NS_ERROR_FAILURE;
3813 // if they are requesting UI from us, let's fail since we have no UI
3814 if (doShowUI)
3815 return NS_OK;
3817 nsresult rv = NS_OK;
3819 if (commandID.LowerCaseEqualsLiteral("gethtml"))
3820 return NS_ERROR_FAILURE;
3822 if (commandID.LowerCaseEqualsLiteral("cut") ||
3823 (commandID.LowerCaseEqualsLiteral("copy"))) {
3824 rv = DoClipboardSecurityCheck(PR_FALSE);
3825 } else if (commandID.LowerCaseEqualsLiteral("paste")) {
3826 rv = DoClipboardSecurityCheck(PR_TRUE);
3829 if (NS_FAILED(rv))
3830 return rv;
3832 // get command manager and dispatch command to our window if it's acceptable
3833 nsCOMPtr<nsICommandManager> cmdMgr;
3834 GetMidasCommandManager(getter_AddRefs(cmdMgr));
3835 if (!cmdMgr)
3836 return NS_ERROR_FAILURE;
3838 nsIDOMWindow *window = GetWindow();
3839 if (!window)
3840 return NS_ERROR_FAILURE;
3842 nsCAutoString cmdToDispatch, paramStr;
3843 PRBool isBool, boolVal;
3844 if (!ConvertToMidasInternalCommand(commandID, value,
3845 cmdToDispatch, paramStr, isBool, boolVal))
3846 return NS_OK;
3848 if (!isBool && paramStr.IsEmpty()) {
3849 rv = cmdMgr->DoCommand(cmdToDispatch.get(), nsnull, window);
3850 } else {
3851 // we have a command that requires a parameter, create params
3852 nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
3853 NS_COMMAND_PARAMS_CONTRACTID, &rv);
3854 if (!cmdParams)
3855 return NS_ERROR_OUT_OF_MEMORY;
3857 if (isBool)
3858 rv = cmdParams->SetBooleanValue("state_attribute", boolVal);
3859 else if (cmdToDispatch.Equals("cmd_fontFace"))
3860 rv = cmdParams->SetStringValue("state_attribute", value);
3861 else if (cmdToDispatch.Equals("cmd_insertHTML"))
3862 rv = cmdParams->SetStringValue("state_data", value);
3863 else
3864 rv = cmdParams->SetCStringValue("state_attribute", paramStr.get());
3865 if (NS_FAILED(rv))
3866 return rv;
3867 rv = cmdMgr->DoCommand(cmdToDispatch.get(), cmdParams, window);
3870 *_retval = NS_SUCCEEDED(rv);
3872 return rv;
3875 /* TODO: don't let this call do anything if the page is not done loading */
3876 /* boolean execCommandShowHelp(in DOMString commandID); */
3877 NS_IMETHODIMP
3878 nsHTMLDocument::ExecCommandShowHelp(const nsAString & commandID,
3879 PRBool *_retval)
3881 NS_ENSURE_ARG_POINTER(_retval);
3882 *_retval = PR_FALSE;
3884 // if editing is not on, bail
3885 if (!IsEditingOn())
3886 return NS_ERROR_FAILURE;
3888 return NS_ERROR_NOT_IMPLEMENTED;
3891 /* boolean queryCommandEnabled(in DOMString commandID); */
3892 NS_IMETHODIMP
3893 nsHTMLDocument::QueryCommandEnabled(const nsAString & commandID,
3894 PRBool *_retval)
3896 NS_ENSURE_ARG_POINTER(_retval);
3897 *_retval = PR_FALSE;
3899 // if editing is not on, bail
3900 if (!IsEditingOn())
3901 return NS_ERROR_FAILURE;
3903 // get command manager and dispatch command to our window if it's acceptable
3904 nsCOMPtr<nsICommandManager> cmdMgr;
3905 GetMidasCommandManager(getter_AddRefs(cmdMgr));
3906 if (!cmdMgr)
3907 return NS_ERROR_FAILURE;
3909 nsIDOMWindow *window = GetWindow();
3910 if (!window)
3911 return NS_ERROR_FAILURE;
3913 nsCAutoString cmdToDispatch, paramStr;
3914 if (!ConvertToMidasInternalCommand(commandID, cmdToDispatch))
3915 return NS_ERROR_NOT_IMPLEMENTED;
3917 return cmdMgr->IsCommandEnabled(cmdToDispatch.get(), window, _retval);
3920 /* boolean queryCommandIndeterm (in DOMString commandID); */
3921 NS_IMETHODIMP
3922 nsHTMLDocument::QueryCommandIndeterm(const nsAString & commandID,
3923 PRBool *_retval)
3925 NS_ENSURE_ARG_POINTER(_retval);
3926 *_retval = PR_FALSE;
3928 // if editing is not on, bail
3929 if (!IsEditingOn())
3930 return NS_ERROR_FAILURE;
3932 // get command manager and dispatch command to our window if it's acceptable
3933 nsCOMPtr<nsICommandManager> cmdMgr;
3934 GetMidasCommandManager(getter_AddRefs(cmdMgr));
3935 if (!cmdMgr)
3936 return NS_ERROR_FAILURE;
3938 nsIDOMWindow *window = GetWindow();
3939 if (!window)
3940 return NS_ERROR_FAILURE;
3942 nsCAutoString cmdToDispatch, paramToCheck;
3943 PRBool dummy;
3944 if (!ConvertToMidasInternalCommand(commandID, commandID,
3945 cmdToDispatch, paramToCheck, dummy, dummy))
3946 return NS_ERROR_NOT_IMPLEMENTED;
3948 nsresult rv;
3949 nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
3950 NS_COMMAND_PARAMS_CONTRACTID, &rv);
3951 NS_ENSURE_SUCCESS(rv, rv);
3953 rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, cmdParams);
3954 if (NS_FAILED(rv))
3955 return rv;
3957 // if command does not have a state_mixed value, this call fails, so we fail too,
3958 // which is what is expected
3959 rv = cmdParams->GetBooleanValue("state_mixed", _retval);
3960 return rv;
3963 /* boolean queryCommandState(in DOMString commandID); */
3964 NS_IMETHODIMP
3965 nsHTMLDocument::QueryCommandState(const nsAString & commandID, PRBool *_retval)
3967 NS_ENSURE_ARG_POINTER(_retval);
3968 *_retval = PR_FALSE;
3970 // if editing is not on, bail
3971 if (!IsEditingOn())
3972 return NS_ERROR_FAILURE;
3974 // get command manager and dispatch command to our window if it's acceptable
3975 nsCOMPtr<nsICommandManager> cmdMgr;
3976 GetMidasCommandManager(getter_AddRefs(cmdMgr));
3977 if (!cmdMgr)
3978 return NS_ERROR_FAILURE;
3980 nsIDOMWindow *window = GetWindow();
3981 if (!window)
3982 return NS_ERROR_FAILURE;
3984 nsCAutoString cmdToDispatch, paramToCheck;
3985 PRBool dummy, dummy2;
3986 if (!ConvertToMidasInternalCommand(commandID, commandID,
3987 cmdToDispatch, paramToCheck, dummy, dummy2))
3988 return NS_ERROR_NOT_IMPLEMENTED;
3990 nsresult rv;
3991 nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
3992 NS_COMMAND_PARAMS_CONTRACTID, &rv);
3993 if (!cmdParams)
3994 return NS_ERROR_OUT_OF_MEMORY;
3996 rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, cmdParams);
3997 if (NS_FAILED(rv))
3998 return rv;
4000 // handle alignment as a special case (possibly other commands too?)
4001 // Alignment is special because the external api is individual
4002 // commands but internally we use cmd_align with different
4003 // parameters. When getting the state of this command, we need to
4004 // return the boolean for this particular alignment rather than the
4005 // string of 'which alignment is this?'
4006 if (cmdToDispatch.Equals("cmd_align")) {
4007 char * actualAlignmentType = nsnull;
4008 rv = cmdParams->GetCStringValue("state_attribute", &actualAlignmentType);
4009 if (NS_SUCCEEDED(rv) && actualAlignmentType && actualAlignmentType[0]) {
4010 *_retval = paramToCheck.Equals(actualAlignmentType);
4012 if (actualAlignmentType)
4013 nsMemory::Free(actualAlignmentType);
4015 else {
4016 rv = cmdParams->GetBooleanValue("state_all", _retval);
4017 if (NS_FAILED(rv))
4018 *_retval = PR_FALSE;
4021 return rv;
4024 /* boolean queryCommandSupported(in DOMString commandID); */
4025 NS_IMETHODIMP
4026 nsHTMLDocument::QueryCommandSupported(const nsAString & commandID,
4027 PRBool *_retval)
4029 NS_ENSURE_ARG_POINTER(_retval);
4030 *_retval = PR_FALSE;
4032 // if editing is not on, bail
4033 if (!IsEditingOn())
4034 return NS_ERROR_FAILURE;
4036 return NS_ERROR_NOT_IMPLEMENTED;
4039 /* DOMString queryCommandText(in DOMString commandID); */
4040 NS_IMETHODIMP
4041 nsHTMLDocument::QueryCommandText(const nsAString & commandID,
4042 nsAString & _retval)
4044 _retval.SetLength(0);
4046 // if editing is not on, bail
4047 if (!IsEditingOn())
4048 return NS_ERROR_FAILURE;
4050 return NS_ERROR_NOT_IMPLEMENTED;
4053 /* DOMString queryCommandValue(in DOMString commandID); */
4054 NS_IMETHODIMP
4055 nsHTMLDocument::QueryCommandValue(const nsAString & commandID,
4056 nsAString &_retval)
4058 _retval.SetLength(0);
4060 // if editing is not on, bail
4061 if (!IsEditingOn())
4062 return NS_ERROR_FAILURE;
4064 // get command manager and dispatch command to our window if it's acceptable
4065 nsCOMPtr<nsICommandManager> cmdMgr;
4066 GetMidasCommandManager(getter_AddRefs(cmdMgr));
4067 if (!cmdMgr)
4068 return NS_ERROR_FAILURE;
4070 nsIDOMWindow *window = GetWindow();
4071 if (!window)
4072 return NS_ERROR_FAILURE;
4074 nsCAutoString cmdToDispatch, paramStr;
4075 if (!ConvertToMidasInternalCommand(commandID, cmdToDispatch))
4076 return NS_ERROR_NOT_IMPLEMENTED;
4078 // create params
4079 nsresult rv;
4080 nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
4081 NS_COMMAND_PARAMS_CONTRACTID, &rv);
4082 if (!cmdParams)
4083 return NS_ERROR_OUT_OF_MEMORY;
4085 // this is a special command since we are calling "DoCommand rather than
4086 // GetCommandState like the other commands
4087 if (cmdToDispatch.Equals("cmd_getContents"))
4089 rv = cmdParams->SetBooleanValue("selection_only", PR_TRUE);
4090 if (NS_FAILED(rv)) return rv;
4091 rv = cmdParams->SetCStringValue("format", "text/html");
4092 if (NS_FAILED(rv)) return rv;
4093 rv = cmdMgr->DoCommand(cmdToDispatch.get(), cmdParams, window);
4094 if (NS_FAILED(rv)) return rv;
4095 return cmdParams->GetStringValue("result", _retval);
4098 rv = cmdParams->SetCStringValue("state_attribute", paramStr.get());
4099 if (NS_FAILED(rv))
4100 return rv;
4102 rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, cmdParams);
4103 if (NS_FAILED(rv))
4104 return rv;
4106 nsXPIDLCString cStringResult;
4107 rv = cmdParams->GetCStringValue("state_attribute",
4108 getter_Copies(cStringResult));
4109 CopyUTF8toUTF16(cStringResult, _retval);
4111 return rv;
4114 #ifdef DEBUG
4115 nsresult
4116 nsHTMLDocument::CreateElem(nsIAtom *aName, nsIAtom *aPrefix,
4117 PRInt32 aNamespaceID, PRBool aDocumentDefaultType,
4118 nsIContent** aResult)
4120 NS_ASSERTION(!aDocumentDefaultType || IsXHTML() ||
4121 aNamespaceID == kNameSpaceID_None,
4122 "HTML elements in an HTML document should have "
4123 "kNamespaceID_None as their namespace ID.");
4125 if (IsXHTML() &&
4126 (aDocumentDefaultType || aNamespaceID == kNameSpaceID_XHTML)) {
4127 nsCAutoString name, lcName;
4128 aName->ToUTF8String(name);
4129 ToLowerCase(name, lcName);
4130 NS_ASSERTION(lcName.Equals(name),
4131 "aName should be lowercase, fix caller.");
4134 return nsDocument::CreateElem(aName, aPrefix, aNamespaceID,
4135 aDocumentDefaultType, aResult);
4137 #endif