Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLDocument.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim: set sw=2 ts=2 et tw=80: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Pierre Phaneuf <pp@ludusdesign.com>
00025  *   Kathleen Brade <brade@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 #include "nsICharsetAlias.h"
00041 
00042 #include "nsCOMPtr.h"
00043 #include "nsXPIDLString.h"
00044 #include "nsPrintfCString.h"
00045 #include "nsReadableUtils.h"
00046 #include "nsUnicharUtils.h"
00047 #include "nsHTMLDocument.h"
00048 #include "nsIParserFilter.h"
00049 #include "nsIHTMLContentSink.h"
00050 #include "nsIXMLContentSink.h"
00051 #include "nsHTMLParts.h"
00052 #include "nsHTMLStyleSheet.h"
00053 #include "nsHTMLAtoms.h"
00054 #include "nsLayoutAtoms.h"
00055 #include "nsIPresShell.h"
00056 #include "nsPresContext.h"
00057 #include "nsIDOMNode.h" // for Find
00058 #include "nsIDOMNodeList.h"
00059 #include "nsIDOMElement.h"
00060 #include "nsIDOMText.h"
00061 #include "nsIDOMComment.h"
00062 #include "nsIDOMDOMImplementation.h"
00063 #include "nsIDOMDocumentType.h"
00064 #include "nsPIDOMWindow.h"
00065 #include "nsIDOMHTMLFormElement.h"
00066 #include "nsDOMString.h"
00067 #include "nsIStreamListener.h"
00068 #include "nsIURI.h"
00069 #include "nsIIOService.h"
00070 #include "nsNetUtil.h"
00071 #include "nsIContentViewerContainer.h"
00072 #include "nsIContentViewer.h"
00073 #include "nsIMarkupDocumentViewer.h"
00074 #include "nsIDocShell.h"
00075 #include "nsIDocShellTreeItem.h"
00076 #include "nsIWebNavigation.h"
00077 #include "nsIBaseWindow.h"
00078 #include "nsIWebShellServices.h"
00079 #include "nsIDocumentLoader.h"
00080 #include "nsIScriptGlobalObject.h"
00081 #include "nsIScriptContext.h"
00082 #include "nsIXPConnect.h"
00083 #include "nsContentList.h"
00084 #include "nsDOMError.h"
00085 #include "nsIPrincipal.h"
00086 #include "nsIScriptSecurityManager.h"
00087 #include "nsIScrollableView.h"
00088 
00089 #include "nsNetCID.h"
00090 #include "nsIIOService.h"
00091 #include "nsICookieService.h"
00092 
00093 #include "nsIServiceManager.h"
00094 #include "nsIConsoleService.h"
00095 #include "nsIComponentManager.h"
00096 #include "nsParserCIID.h"
00097 #include "nsIDOMHTMLElement.h"
00098 #include "nsIDOMHTMLMapElement.h"
00099 #include "nsIDOMHTMLBodyElement.h"
00100 #include "nsINameSpaceManager.h"
00101 #include "nsGenericHTMLElement.h"
00102 #include "nsGenericDOMNodeList.h"
00103 #include "nsICSSLoader.h"
00104 #include "nsIHttpChannel.h"
00105 #include "nsIFile.h"
00106 #include "nsIEventListenerManager.h"
00107 #include "nsISelectElement.h"
00108 #include "nsIFrameSelection.h"
00109 #include "nsISelectionPrivate.h"//for toStringwithformat code
00110 
00111 #include "nsICharsetDetector.h"
00112 #include "nsICharsetDetectionAdaptor.h"
00113 #include "nsCharsetDetectionAdaptorCID.h"
00114 #include "nsICharsetAlias.h"
00115 #include "nsContentUtils.h"
00116 #include "nsJSUtils.h"
00117 #include "nsIDocumentCharsetInfo.h"
00118 #include "nsIDocumentEncoder.h" //for outputting selection
00119 #include "nsICharsetResolver.h"
00120 #include "nsICachingChannel.h"
00121 #include "nsICacheEntryDescriptor.h"
00122 #include "nsIJSContextStack.h"
00123 #include "nsIDocumentViewer.h"
00124 #include "nsIWyciwygChannel.h"
00125 #include "nsIScriptError.h"
00126 #include "nsArray.h"
00127 #include "nsIPrompt.h"
00128 //AHMED 12-2
00129 #include "nsBidiUtils.h"
00130 
00131 #include "nsIEditingSession.h"
00132 #include "nsIEditor.h"
00133 #include "nsNodeInfoManager.h"
00134 
00135 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
00136 
00137 #define DETECTOR_CONTRACTID_MAX 127
00138 static char g_detector_contractid[DETECTOR_CONTRACTID_MAX + 1];
00139 static PRBool gInitDetector = PR_FALSE;
00140 static PRBool gPlugDetector = PR_FALSE;
00141 
00142 #include "prmem.h"
00143 #include "prtime.h"
00144 
00145 // Find/Search Includes
00146 const PRInt32 kForward  = 0;
00147 const PRInt32 kBackward = 1;
00148 
00149 //#define DEBUG_charset
00150 
00151 
00152 #define ID_NOT_IN_DOCUMENT ((nsIContent *)1)
00153 #define NAME_NOT_VALID ((nsBaseContentList*)1)
00154 
00155 static NS_DEFINE_CID(kCookieServiceCID, NS_COOKIESERVICE_CID);
00156 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
00157 static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
00158 
00159 static PRBool
00160 IsNamedItem(nsIContent* aContent, nsIAtom *aTag, nsAString& aName);
00161 
00162 // MatchElementId is defined in nsXMLDocument.cpp
00163 nsIContent *
00164 MatchElementId(nsIContent *aContent, const nsACString& aUTF8Id, const nsAString& aId);
00165 
00166 
00167 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
00168 
00169 PRUint32       nsHTMLDocument::gWyciwygSessionCnt = 0;
00170 
00171 static int PR_CALLBACK
00172 MyPrefChangedCallback(const char*aPrefName, void* instance_data)
00173 {
00174   const nsAdoptingString& detector_name =
00175     nsContentUtils::GetLocalizedStringPref("intl.charset.detector");
00176 
00177   if (detector_name.Length() > 0) {
00178     PL_strncpy(g_detector_contractid, NS_CHARSET_DETECTOR_CONTRACTID_BASE,
00179                DETECTOR_CONTRACTID_MAX);
00180     PL_strncat(g_detector_contractid,
00181                NS_ConvertUCS2toUTF8(detector_name).get(),
00182                DETECTOR_CONTRACTID_MAX);
00183     gPlugDetector = PR_TRUE;
00184   } else {
00185     g_detector_contractid[0]=0;
00186     gPlugDetector = PR_FALSE;
00187   }
00188 
00189   return 0;
00190 }
00191 
00192 // ==================================================================
00193 // =
00194 // ==================================================================
00195 nsresult
00196 NS_NewHTMLDocument(nsIDocument** aInstancePtrResult)
00197 {
00198   nsHTMLDocument* doc = new nsHTMLDocument();
00199   NS_ENSURE_TRUE(doc, NS_ERROR_OUT_OF_MEMORY);
00200 
00201   NS_ADDREF(doc);
00202   nsresult rv = doc->Init();
00203 
00204   if (NS_FAILED(rv)) {
00205     NS_RELEASE(doc);
00206   }
00207 
00208   *aInstancePtrResult = doc;
00209 
00210   return rv;
00211 }
00212 
00213 class IdAndNameMapEntry : public PLDHashEntryHdr
00214 {
00215 public:
00216   IdAndNameMapEntry(const nsAString& aString) :
00217     mKey(aString), mIdContent(nsnull), mContentList(nsnull)
00218   {
00219   }
00220 
00221   ~IdAndNameMapEntry()
00222   {
00223     if (mContentList && mContentList != NAME_NOT_VALID) {
00224       NS_RELEASE(mContentList);
00225     }
00226   }
00227 
00228   nsString mKey;
00229   nsIContent *mIdContent;
00230   nsBaseContentList *mContentList;
00231 };
00232 
00233 
00234 PR_STATIC_CALLBACK(const void *)
00235 IdAndNameHashGetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
00236 {
00237   IdAndNameMapEntry *e = NS_STATIC_CAST(IdAndNameMapEntry *, entry);
00238 
00239   return NS_STATIC_CAST(const nsAString *, &e->mKey);
00240 }
00241 
00242 PR_STATIC_CALLBACK(PLDHashNumber)
00243 IdAndNameHashHashKey(PLDHashTable *table, const void *key)
00244 {
00245   const nsAString *str = NS_STATIC_CAST(const nsAString *, key);
00246 
00247   return HashString(*str);
00248 }
00249 
00250 PR_STATIC_CALLBACK(PRBool)
00251 IdAndNameHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry,
00252                         const void *key)
00253 {
00254   const IdAndNameMapEntry *e =
00255     NS_STATIC_CAST(const IdAndNameMapEntry *, entry);
00256   const nsAString *str = NS_STATIC_CAST(const nsAString *, key);
00257 
00258   return str->Equals(e->mKey);
00259 }
00260 
00261 PR_STATIC_CALLBACK(void)
00262 IdAndNameHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
00263 {
00264   IdAndNameMapEntry *e = NS_STATIC_CAST(IdAndNameMapEntry *, entry);
00265 
00266   // An entry is being cleared, let the entry do its own cleanup.
00267   e->~IdAndNameMapEntry();
00268 }
00269 
00270 PR_STATIC_CALLBACK(PRBool)
00271 IdAndNameHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
00272                        const void *key)
00273 {
00274   const nsAString *keyStr = NS_STATIC_CAST(const nsAString *, key);
00275 
00276   // Inititlize the entry with placement new
00277   new (entry) IdAndNameMapEntry(*keyStr);
00278   return PR_TRUE;
00279 }
00280 
00281   // NOTE! nsDocument::operator new() zeroes out all members, so don't
00282   // bother initializing members to 0.
00283 
00284 nsHTMLDocument::nsHTMLDocument()
00285   : mCompatMode(eCompatibility_NavQuirks),
00286     mTexttype(IBMBIDI_TEXTTYPE_LOGICAL),
00287     mDefaultNamespaceID(kNameSpaceID_None)
00288 {
00289 
00290   // NOTE! nsDocument::operator new() zeroes out all members, so don't
00291   // bother initializing members to 0.
00292 
00293   mDefaultElementType = kNameSpaceID_XHTML;
00294 }
00295 
00296 nsHTMLDocument::~nsHTMLDocument()
00297 {
00298   if (mIdAndNameHashTable.ops) {
00299     PL_DHashTableFinish(&mIdAndNameHashTable);
00300   }
00301 }
00302 
00303 NS_IMPL_ADDREF_INHERITED(nsHTMLDocument, nsDocument)
00304 NS_IMPL_RELEASE_INHERITED(nsHTMLDocument, nsDocument)
00305 
00306 
00307 // QueryInterface implementation for nsHTMLDocument
00308 NS_INTERFACE_MAP_BEGIN(nsHTMLDocument)
00309   NS_INTERFACE_MAP_ENTRY(nsIHTMLDocument)
00310   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLDocument)
00311   NS_INTERFACE_MAP_ENTRY(nsIDOMNSHTMLDocument)
00312   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLDocument)
00313 NS_INTERFACE_MAP_END_INHERITING(nsDocument)
00314 
00315 
00316 nsresult
00317 nsHTMLDocument::Init()
00318 {
00319   nsresult rv = nsDocument::Init();
00320   NS_ENSURE_SUCCESS(rv, rv);
00321 
00322   // Now reset the case-sensitivity of the CSSLoader, since we default
00323   // to being HTML, not XHTML.  Also, reset the compatibility mode to
00324   // match our compat mode.
00325   CSSLoader()->SetCaseSensitive(IsXHTML());
00326   CSSLoader()->SetCompatibilityMode(mCompatMode);
00327 
00328   static PLDHashTableOps hash_table_ops =
00329   {
00330     PL_DHashAllocTable,
00331     PL_DHashFreeTable,
00332     IdAndNameHashGetKey,
00333     IdAndNameHashHashKey,
00334     IdAndNameHashMatchEntry,
00335     PL_DHashMoveEntryStub,
00336     IdAndNameHashClearEntry,
00337     PL_DHashFinalizeStub,
00338     IdAndNameHashInitEntry
00339   };
00340 
00341   PRBool ok = PL_DHashTableInit(&mIdAndNameHashTable, &hash_table_ops, nsnull,
00342                                 sizeof(IdAndNameMapEntry), 16);
00343   if (!ok) {
00344     mIdAndNameHashTable.ops = nsnull;
00345 
00346     return NS_ERROR_OUT_OF_MEMORY;
00347   }
00348 
00349   PrePopulateHashTables();
00350 
00351   return NS_OK;
00352 }
00353 
00354 void
00355 nsHTMLDocument::Destroy()
00356 {
00357   InvalidateHashTables();
00358   nsDocument::Destroy();
00359 }
00360 
00361 void
00362 nsHTMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
00363 {
00364   nsDocument::Reset(aChannel, aLoadGroup);
00365 
00366   if (aChannel) {
00367     aChannel->GetLoadFlags(&mLoadFlags);
00368   }
00369 }
00370 
00371 void
00372 nsHTMLDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup)
00373 {
00374   mLoadFlags = nsIRequest::LOAD_NORMAL;
00375 
00376   nsDocument::ResetToURI(aURI, aLoadGroup);
00377 
00378   InvalidateHashTables();
00379   PrePopulateHashTables();
00380 
00381   mImages = nsnull;
00382   mApplets = nsnull;
00383   mEmbeds = nsnull;
00384   mLinks = nsnull;
00385   mAnchors = nsnull;
00386 
00387   mBodyContent = nsnull;
00388 
00389   mImageMaps.Clear();
00390   mForms = nsnull;
00391 
00392   NS_ASSERTION(!mWyciwygChannel,
00393                "nsHTMLDocument::Reset() - Wyciwyg Channel  still exists!");
00394 
00395   mWyciwygChannel = nsnull;
00396 
00397   // Make the content type default to "text/html", we are a HTML
00398   // document, after all. Once we start getting data, this may be
00399   // changed.
00400   mContentType = "text/html";
00401 }
00402 
00403 nsStyleSet::sheetType
00404 nsHTMLDocument::GetAttrSheetType()
00405 {
00406   if (IsXHTML()) {
00407     return nsDocument::GetAttrSheetType();
00408   }
00409   
00410   return nsStyleSet::eHTMLPresHintSheet;
00411 }
00412 
00413 nsresult
00414 nsHTMLDocument::CreateShell(nsPresContext* aContext,
00415                             nsIViewManager* aViewManager,
00416                             nsStyleSet* aStyleSet,
00417                             nsIPresShell** aInstancePtrResult)
00418 {
00419   return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode,
00420                        aInstancePtrResult);
00421 }
00422 
00423 // The following Try*Charset will return PR_FALSE only if the charset source
00424 // should be considered (ie. aCharsetSource < thisCharsetSource) but we failed
00425 // to get the charset from this source.
00426 
00427 PRBool
00428 nsHTMLDocument::TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV,
00429                                PRInt32& aCharsetSource, nsACString& aCharset)
00430 {
00431   if (aMarkupDV) {
00432     PRInt32 requestCharsetSource;
00433     nsresult rv = aMarkupDV->GetHintCharacterSetSource(&requestCharsetSource);
00434 
00435     if(NS_SUCCEEDED(rv) && kCharsetUninitialized != requestCharsetSource) {
00436       nsCAutoString requestCharset;
00437       rv = aMarkupDV->GetHintCharacterSet(requestCharset);
00438       aMarkupDV->SetHintCharacterSetSource((PRInt32)(kCharsetUninitialized));
00439 
00440       if(requestCharsetSource <= aCharsetSource)
00441         return PR_TRUE;
00442 
00443       if(NS_SUCCEEDED(rv)) {
00444         aCharsetSource = requestCharsetSource;
00445         aCharset = requestCharset;
00446 
00447         return PR_TRUE;
00448       }
00449     }
00450   }
00451   return PR_FALSE;
00452 }
00453 
00454 
00455 PRBool
00456 nsHTMLDocument::TryUserForcedCharset(nsIMarkupDocumentViewer* aMarkupDV,
00457                                      nsIDocumentCharsetInfo*  aDocInfo,
00458                                      PRInt32& aCharsetSource,
00459                                      nsACString& aCharset)
00460 {
00461   nsresult rv = NS_OK;
00462 
00463   if(kCharsetFromUserForced <= aCharsetSource)
00464     return PR_TRUE;
00465 
00466   nsCAutoString forceCharsetFromDocShell;
00467   if (aMarkupDV) {
00468     rv = aMarkupDV->GetForceCharacterSet(forceCharsetFromDocShell);
00469   }
00470 
00471   if(NS_SUCCEEDED(rv) && !forceCharsetFromDocShell.IsEmpty()) {
00472     aCharset = forceCharsetFromDocShell;
00473     //TODO: we should define appropriate constant for force charset
00474     aCharsetSource = kCharsetFromUserForced;
00475   } else if (aDocInfo) {
00476     nsCOMPtr<nsIAtom> csAtom;
00477     aDocInfo->GetForcedCharset(getter_AddRefs(csAtom));
00478     if (csAtom) {
00479       csAtom->ToUTF8String(aCharset);
00480       aCharsetSource = kCharsetFromUserForced;
00481       aDocInfo->SetForcedCharset(nsnull);
00482       return PR_TRUE;
00483     }
00484   }
00485 
00486   return PR_FALSE;
00487 }
00488 
00489 PRBool
00490 nsHTMLDocument::TryCacheCharset(nsICacheEntryDescriptor* aCacheDescriptor,
00491                                 PRInt32& aCharsetSource,
00492                                 nsACString& aCharset)
00493 {
00494   nsresult rv;
00495 
00496   if (kCharsetFromCache <= aCharsetSource) {
00497     return PR_TRUE;
00498   }
00499 
00500   nsXPIDLCString cachedCharset;
00501   rv = aCacheDescriptor->GetMetaDataElement("charset",
00502                                            getter_Copies(cachedCharset));
00503   if (NS_SUCCEEDED(rv) && !cachedCharset.IsEmpty())
00504   {
00505     aCharset = cachedCharset;
00506     aCharsetSource = kCharsetFromCache;
00507 
00508     return PR_TRUE;
00509   }
00510 
00511   return PR_FALSE;
00512 }
00513 
00514 PRBool
00515 nsHTMLDocument::TryBookmarkCharset(nsIDocShell* aDocShell,
00516                                    nsIChannel* aChannel,
00517                                    PRInt32& aCharsetSource,
00518                                    nsACString& aCharset)
00519 {
00520   if (kCharsetFromBookmarks <= aCharsetSource) {
00521     return PR_TRUE;
00522   }
00523 
00524   if (!aChannel) {
00525     return PR_FALSE;
00526   }
00527 
00528   nsCOMPtr<nsICharsetResolver> bookmarksResolver =
00529     do_GetService("@mozilla.org/embeddor.implemented/bookmark-charset-resolver;1");
00530 
00531   if (!bookmarksResolver) {
00532     return PR_FALSE;
00533   }
00534 
00535   PRBool wantCharset;         // ignored for now
00536   nsCAutoString charset;
00537   nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(aDocShell));
00538   nsCOMPtr<nsISupports> closure;
00539   nsresult rv = bookmarksResolver->RequestCharset(webNav,
00540                                                   aChannel,
00541                                                   &wantCharset,
00542                                                   getter_AddRefs(closure),
00543                                                   charset);
00544   // FIXME: Bug 337790
00545   NS_ASSERTION(!wantCharset, "resolved charset notification not implemented!");
00546 
00547   if (NS_SUCCEEDED(rv) && !charset.IsEmpty()) {
00548     aCharset = charset;
00549     aCharsetSource = kCharsetFromBookmarks;
00550     return PR_TRUE;
00551   }
00552 
00553   return PR_FALSE;
00554 }
00555 
00556 static PRBool
00557 CheckSameOrigin(nsIDocument* aDoc1, nsIDocument* aDoc2)
00558 {
00559   NS_PRECONDITION(aDoc1, "Null doc?");
00560   NS_PRECONDITION(aDoc2, "Null doc?");
00561 
00562   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
00563   if (!secMan) {
00564     return PR_FALSE;
00565   }
00566 
00567   nsIPrincipal* principal1 = aDoc1->GetPrincipal();
00568   nsIPrincipal* principal2 = aDoc2->GetPrincipal();
00569 
00570   if (!principal1 || !principal2) {
00571     return PR_FALSE;
00572   }
00573 
00574   return
00575     NS_SUCCEEDED(secMan->CheckSameOriginPrincipal(principal1, principal2));
00576 }
00577 
00578 PRBool
00579 nsHTMLDocument::TryParentCharset(nsIDocumentCharsetInfo*  aDocInfo,
00580                                  nsIDocument* aParentDocument,
00581                                  PRInt32& aCharsetSource,
00582                                  nsACString& aCharset)
00583 {
00584   if (aDocInfo) {
00585     PRInt32 source;
00586     nsCOMPtr<nsIAtom> csAtom;
00587     PRInt32 parentSource;
00588     aDocInfo->GetParentCharsetSource(&parentSource);
00589     if (kCharsetFromParentForced <= parentSource)
00590       source = kCharsetFromParentForced;
00591     else if (kCharsetFromHintPrevDoc == parentSource) {
00592       // Make sure that's OK
00593       if (!aParentDocument || !CheckSameOrigin(this, aParentDocument)) {
00594         return PR_FALSE;
00595       }
00596       
00597       // if parent is posted doc, set this prevent autodections
00598       // I'm not sure this makes much sense... but whatever.
00599       source = kCharsetFromHintPrevDoc;
00600     }
00601     else if (kCharsetFromCache <= parentSource) {
00602       // Make sure that's OK
00603       if (!aParentDocument || !CheckSameOrigin(this, aParentDocument)) {
00604         return PR_FALSE;
00605       }
00606 
00607       source = kCharsetFromParentFrame;
00608     }
00609     else
00610       return PR_FALSE;
00611 
00612     if (source < aCharsetSource)
00613       return PR_TRUE;
00614 
00615     aDocInfo->GetParentCharset(getter_AddRefs(csAtom));
00616     if (csAtom) {
00617       csAtom->ToUTF8String(aCharset);
00618       aCharsetSource = source;
00619       return PR_TRUE;
00620     }
00621   }
00622   return PR_FALSE;
00623 }
00624 
00625 PRBool
00626 nsHTMLDocument::UseWeakDocTypeDefault(PRInt32& aCharsetSource,
00627                                       nsACString& aCharset)
00628 {
00629   if (kCharsetFromWeakDocTypeDefault <= aCharsetSource)
00630     return PR_TRUE;
00631   // fallback value in case docshell return error
00632   aCharset.AssignLiteral("ISO-8859-1");
00633 
00634   const nsAdoptingString& defCharset =
00635     nsContentUtils::GetLocalizedStringPref("intl.charset.default");
00636 
00637   if (!defCharset.IsEmpty()) {
00638     LossyCopyUTF16toASCII(defCharset, aCharset);
00639     aCharsetSource = kCharsetFromWeakDocTypeDefault;
00640   }
00641   return PR_TRUE;
00642 }
00643 
00644 PRBool
00645 nsHTMLDocument::TryDefaultCharset( nsIMarkupDocumentViewer* aMarkupDV,
00646                                    PRInt32& aCharsetSource,
00647                                    nsACString& aCharset)
00648 {
00649   if(kCharsetFromUserDefault <= aCharsetSource)
00650     return PR_TRUE;
00651 
00652   nsCAutoString defaultCharsetFromDocShell;
00653   if (aMarkupDV) {
00654     nsresult rv =
00655       aMarkupDV->GetDefaultCharacterSet(defaultCharsetFromDocShell);
00656     if(NS_SUCCEEDED(rv)) {
00657       aCharset = defaultCharsetFromDocShell;
00658 
00659       aCharsetSource = kCharsetFromUserDefault;
00660       return PR_TRUE;
00661     }
00662   }
00663   return PR_FALSE;
00664 }
00665 
00666 void
00667 nsHTMLDocument::StartAutodetection(nsIDocShell *aDocShell, nsACString& aCharset,
00668                                    const char* aCommand)
00669 {
00670   nsCOMPtr <nsIParserFilter> cdetflt;
00671 
00672   nsresult rv_detect;
00673   if(!gInitDetector) {
00674     const nsAdoptingString& detector_name =
00675       nsContentUtils::GetLocalizedStringPref("intl.charset.detector");
00676 
00677     if(!detector_name.IsEmpty()) {
00678       PL_strncpy(g_detector_contractid, NS_CHARSET_DETECTOR_CONTRACTID_BASE,
00679                  DETECTOR_CONTRACTID_MAX);
00680       PL_strncat(g_detector_contractid,
00681                  NS_ConvertUTF16toUTF8(detector_name).get(),
00682                  DETECTOR_CONTRACTID_MAX);
00683       gPlugDetector = PR_TRUE;
00684     }
00685 
00686     nsContentUtils::RegisterPrefCallback("intl.charset.detector",
00687                                          MyPrefChangedCallback,
00688                                          nsnull);
00689 
00690     gInitDetector = PR_TRUE;
00691   }
00692 
00693   if (gPlugDetector) {
00694     nsCOMPtr <nsICharsetDetector> cdet =
00695       do_CreateInstance(g_detector_contractid, &rv_detect);
00696     if (NS_SUCCEEDED(rv_detect)) {
00697       cdetflt = do_CreateInstance(NS_CHARSET_DETECTION_ADAPTOR_CONTRACTID,
00698                                   &rv_detect);
00699 
00700       nsCOMPtr<nsICharsetDetectionAdaptor> adp = do_QueryInterface(cdetflt);
00701       if (adp) {
00702         nsCOMPtr<nsIWebShellServices> wss = do_QueryInterface(aDocShell);
00703         if (wss) {
00704           rv_detect = adp->Init(wss, cdet, this, mParser,
00705                                 PromiseFlatCString(aCharset).get(), aCommand);
00706 
00707           if (mParser)
00708             mParser->SetParserFilter(cdetflt);
00709         }
00710       }
00711     }
00712     else {
00713       // IF we cannot create the detector, don't bother to
00714       // create one next time.
00715       gPlugDetector = PR_FALSE;
00716     }
00717   }
00718 }
00719 
00720 nsresult
00721 nsHTMLDocument::StartDocumentLoad(const char* aCommand,
00722                                   nsIChannel* aChannel,
00723                                   nsILoadGroup* aLoadGroup,
00724                                   nsISupports* aContainer,
00725                                   nsIStreamListener **aDocListener,
00726                                   PRBool aReset,
00727                                   nsIContentSink* aSink)
00728 {
00729   nsCAutoString contentType;
00730   aChannel->GetContentType(contentType);
00731 
00732   if (contentType.Equals("application/xhtml+xml") &&
00733       (!aCommand || nsCRT::strcmp(aCommand, "view-source") != 0)) {
00734     // We're parsing XHTML as XML, remember that.
00735 
00736     mDefaultNamespaceID = kNameSpaceID_XHTML;
00737     mCompatMode = eCompatibility_FullStandards;
00738   }
00739 #ifdef DEBUG
00740   else {
00741     NS_ASSERTION(mDefaultNamespaceID == kNameSpaceID_None,
00742                  "Hey, someone forgot to reset mDefaultNamespaceID!!!");
00743   }
00744 #endif
00745 
00746   CSSLoader()->SetCaseSensitive(IsXHTML());
00747   CSSLoader()->SetCompatibilityMode(mCompatMode);
00748   
00749   PRBool needsParser = PR_TRUE;
00750   if (aCommand)
00751   {
00752     if (!nsCRT::strcmp(aCommand, "view delayedContentLoad")) {
00753       needsParser = PR_FALSE;
00754     }
00755   }
00756 
00757   nsCOMPtr<nsICacheEntryDescriptor> cacheDescriptor;
00758   nsresult rv = nsDocument::StartDocumentLoad(aCommand,
00759                                               aChannel, aLoadGroup,
00760                                               aContainer,
00761                                               aDocListener, aReset);
00762   if (NS_FAILED(rv)) {
00763     return rv;
00764   }
00765 
00766   // Store the security info for future use with wyciwyg channels.
00767   aChannel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
00768 
00769   nsCOMPtr<nsIURI> uri;
00770   rv = aChannel->GetURI(getter_AddRefs(uri));
00771   if (NS_FAILED(rv)) {
00772     return rv;
00773   }
00774 
00775   nsCOMPtr<nsICachingChannel> cachingChan = do_QueryInterface(aChannel);
00776   if (cachingChan) {
00777     nsCOMPtr<nsISupports> cacheToken;
00778     cachingChan->GetCacheToken(getter_AddRefs(cacheToken));
00779     if (cacheToken)
00780       cacheDescriptor = do_QueryInterface(cacheToken);
00781   }
00782 
00783   if (needsParser) {
00784     mParser = do_CreateInstance(kCParserCID, &rv);
00785     NS_ENSURE_SUCCESS(rv, rv);
00786   }
00787 
00788   NS_PRECONDITION(nsnull != aContainer, "No content viewer container");
00789 
00790   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
00791 
00792   nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
00793   docShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
00794   nsCOMPtr<nsPresContext> cx;
00795   docShell->GetPresContext(getter_AddRefs(cx));
00796   if(cx){
00797     mTexttype = GET_BIDI_OPTION_TEXTTYPE(cx->GetBidi());
00798   }
00799   // Look for the parent document.  Note that at this point we don't have our
00800   // content viewer set up yet, and therefore do not have a useful
00801   // mParentDocument.
00802 
00803   // in this block of code, if we get an error result, we return it
00804   // but if we get a null pointer, that's perfectly legal for parent
00805   // and parentContentViewer
00806   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(docShell));
00807   NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
00808 
00809   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
00810   docShellAsItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
00811 
00812   nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
00813   nsCOMPtr<nsIDocument> parentDocument;
00814   nsCOMPtr<nsIContentViewer> parentContentViewer;
00815   if (parent) {
00816     rv = parent->GetContentViewer(getter_AddRefs(parentContentViewer));
00817     NS_ENSURE_SUCCESS(rv, rv);
00818     nsCOMPtr<nsIDocumentViewer> docViewer =
00819       do_QueryInterface(parentContentViewer);
00820     if (docViewer) {
00821       docViewer->GetDocument(getter_AddRefs(parentDocument));
00822     }
00823   }
00824 
00825   //
00826   // The following logic is mirrored in nsWebShell::Embed!
00827   //
00828   nsCOMPtr<nsIMarkupDocumentViewer> muCV;
00829   PRBool muCVIsParent = PR_FALSE;
00830   nsCOMPtr<nsIContentViewer> cv;
00831   docShell->GetContentViewer(getter_AddRefs(cv));
00832   if (cv) {
00833      muCV = do_QueryInterface(cv);
00834   } else {
00835     muCV = do_QueryInterface(parentContentViewer);
00836     if (muCV) {
00837       muCVIsParent = PR_TRUE;
00838     }
00839   }
00840 
00841   nsCAutoString scheme;
00842   uri->GetScheme(scheme);
00843 
00844   nsCAutoString urlSpec;
00845   uri->GetSpec(urlSpec);
00846 #ifdef DEBUG_charset
00847   printf("Determining charset for %s\n", urlSpec.get());
00848 #endif
00849 
00850   PRInt32 charsetSource;
00851   nsCAutoString charset;
00852 
00853   if (IsXHTML()) {
00854     charsetSource = kCharsetFromDocTypeDefault;
00855     charset.AssignLiteral("UTF-8");
00856     TryChannelCharset(aChannel, charsetSource, charset);
00857   } else {
00858     charsetSource = kCharsetUninitialized;
00859 
00860     // The following charset resolving calls has implied knowledge
00861     // about charset source priority order. Each try will return true
00862     // if the source is higher or equal to the source as its name
00863     // describes. Some try call might change charset source to
00864     // multiple values, like TryHintCharset and TryParentCharset. It
00865     // should be always safe to try more sources.
00866     if (!TryUserForcedCharset(muCV, dcInfo, charsetSource, charset)) {
00867       TryHintCharset(muCV, charsetSource, charset);
00868       TryParentCharset(dcInfo, parentDocument, charsetSource, charset);
00869       if (TryChannelCharset(aChannel, charsetSource, charset)) {
00870         // Use the channel's charset (e.g., charset from HTTP
00871         // "Content-Type" header).
00872       }
00873       else if (!scheme.EqualsLiteral("about") &&          // don't try to access bookmarks for about:blank
00874                TryBookmarkCharset(docShell, aChannel, charsetSource, charset)) {
00875         // Use the bookmark's charset.
00876       }
00877       else if (cacheDescriptor && !urlSpec.IsEmpty() &&
00878                TryCacheCharset(cacheDescriptor, charsetSource, charset)) {
00879         // Use the cache's charset.
00880       }
00881       else if (TryDefaultCharset(muCV, charsetSource, charset)) {
00882         // Use the default charset.
00883         // previous document charset might be inherited as default charset.
00884       }
00885       else {
00886         // Use the weak doc type default charset
00887         UseWeakDocTypeDefault(charsetSource, charset);
00888       }
00889     }
00890 
00891     PRBool isPostPage = PR_FALSE;
00892     // check if current doc is from POST command
00893     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
00894     if (httpChannel) {
00895       nsCAutoString methodStr;
00896       rv = httpChannel->GetRequestMethod(methodStr);
00897       isPostPage = (NS_SUCCEEDED(rv) &&
00898                     methodStr.EqualsLiteral("POST"));
00899     }
00900 
00901     if (isPostPage && muCV && kCharsetFromHintPrevDoc > charsetSource) {
00902       nsCAutoString requestCharset;
00903       muCV->GetPrevDocCharacterSet(requestCharset);
00904       if (!requestCharset.IsEmpty()) {
00905         charsetSource = kCharsetFromHintPrevDoc;
00906         charset = requestCharset;
00907       }
00908     }
00909 
00910     if(kCharsetFromAutoDetection > charsetSource && !isPostPage) {
00911       StartAutodetection(docShell, charset, aCommand);
00912     }
00913 
00914     // ahmed
00915     // Check if 864 but in Implicit mode !
00916     if ((mTexttype == IBMBIDI_TEXTTYPE_LOGICAL) &&
00917         (charset.LowerCaseEqualsLiteral("ibm864"))) {
00918       charset.AssignLiteral("IBM864i");
00919     }
00920   }
00921 
00922   SetDocumentCharacterSet(charset);
00923   SetDocumentCharacterSetSource(charsetSource);
00924 
00925   // set doc charset to muCV for next document.
00926   // Don't propagate this back up to the parent document if we have one.
00927   if (muCV && !muCVIsParent)
00928     muCV->SetPrevDocCharacterSet(charset);
00929 
00930   if(cacheDescriptor) {
00931     rv = cacheDescriptor->SetMetaDataElement("charset",
00932                                              charset.get());
00933     NS_ASSERTION(NS_SUCCEEDED(rv),"cannot SetMetaDataElement");
00934   }
00935 
00936   // Set the parser as the stream listener for the document loader...
00937   if (mParser) {
00938     rv = CallQueryInterface(mParser, aDocListener);
00939     if (NS_FAILED(rv)) {
00940       return rv;
00941     }
00942 
00943 #ifdef DEBUG_charset
00944     printf(" charset = %s source %d\n",
00945           charset.get(), charsetSource);
00946 #endif
00947     mParser->SetDocumentCharset(charset, charsetSource);
00948     mParser->SetCommand(aCommand);
00949 
00950     // create the content sink
00951     nsCOMPtr<nsIContentSink> sink;
00952 
00953     if (aSink)
00954       sink = aSink;
00955     else {
00956       if (IsXHTML()) {
00957         nsCOMPtr<nsIXMLContentSink> xmlsink;
00958         rv = NS_NewXMLContentSink(getter_AddRefs(xmlsink), this, uri,
00959                                   docShell, aChannel);
00960 
00961         sink = xmlsink;
00962       } else {
00963         nsCOMPtr<nsIHTMLContentSink> htmlsink;
00964 
00965         rv = NS_NewHTMLContentSink(getter_AddRefs(htmlsink), this, uri,
00966                                    docShell, aChannel);
00967 
00968         sink = htmlsink;
00969       }
00970       NS_ENSURE_SUCCESS(rv, rv);
00971 
00972       NS_ASSERTION(sink,
00973                    "null sink with successful result from factory method");
00974     }
00975 
00976     mParser->SetContentSink(sink);
00977     // parser the content of the URI
00978     mParser->Parse(uri, nsnull, PR_FALSE, (void *)this);
00979   }
00980 
00981   return rv;
00982 }
00983 
00984 // static
00985 void
00986 nsHTMLDocument::DocumentWriteTerminationFunc(nsISupports *aRef)
00987 {
00988   nsCOMPtr<nsIArray> arr = do_QueryInterface(aRef);
00989   NS_ASSERTION(arr, "Must have array!");
00990 
00991   nsCOMPtr<nsIDocument> doc = do_QueryElementAt(arr, 0);
00992   NS_ASSERTION(doc, "Must have document!");
00993   
00994   nsCOMPtr<nsIParser> parser = do_QueryElementAt(arr, 1);
00995   NS_ASSERTION(parser, "Must have parser!");
00996 
00997   nsHTMLDocument *htmldoc = NS_STATIC_CAST(nsHTMLDocument *,
00998                                            NS_STATIC_CAST(nsIDocument*,
00999                                                           doc.get()));
01000 
01001   // Check whether htmldoc still has the same parser.  If not, it's
01002   // not for us to mess with it.
01003   if (htmldoc->mParser != parser) {
01004     return;
01005   }
01006 
01007   // If the document is in the middle of a document.write() call, this
01008   // most likely means that script on a page document.write()'d out a
01009   // script tag that did location="..." and we're right now finishing
01010   // up executing the script that was written with
01011   // document.write(). Since there's still script on the stack (the
01012   // script that called document.write()) we don't want to release the
01013   // parser now, that would cause the next document.write() call to
01014   // cancel the load that was initiated by the location="..." in the
01015   // script that was written out by document.write().
01016 
01017   if (!htmldoc->mWriteLevel && !htmldoc->mIsWriting) {
01018     // Release the document's parser so that the call to EndLoad()
01019     // doesn't just return early and set the termination function again.
01020 
01021     htmldoc->mParser = nsnull;
01022   }
01023 
01024   htmldoc->EndLoad();
01025 }
01026 
01027 void
01028 nsHTMLDocument::EndLoad()
01029 {
01030   if (mParser) {
01031     nsCOMPtr<nsIJSContextStack> stack =
01032       do_GetService("@mozilla.org/js/xpc/ContextStack;1");
01033 
01034     if (stack) {
01035       JSContext *cx = nsnull;
01036       stack->Peek(&cx);
01037 
01038       if (cx) {
01039         nsIScriptContext *scx = nsJSUtils::GetDynamicScriptContext(cx);
01040 
01041         if (scx) {
01042           // The load of the document was terminated while we're
01043           // called from within JS and we have a parser (i.e. we're in
01044           // the middle of doing document.write()). In stead of
01045           // releasing the parser and ending the document load
01046           // directly, we'll make that happen once the script is done
01047           // executing. This way subsequent document.write() calls
01048           // won't end up creating a new parser and interrupting other
01049           // loads that were started while the script was
01050           // running. I.e. this makes the following case work as
01051           // expected:
01052           //
01053           //   document.write("foo");
01054           //   location.href = "http://www.mozilla.org";
01055           //   document.write("bar");
01056 
01057           nsCOMPtr<nsIMutableArray> arr;
01058           nsresult rv = NS_NewArray(getter_AddRefs(arr));
01059           if (NS_SUCCEEDED(rv)) {
01060             rv = arr->AppendElement(NS_STATIC_CAST(nsIDocument*, this),
01061                                     PR_FALSE);
01062             if (NS_SUCCEEDED(rv)) {
01063               rv = arr->AppendElement(mParser, PR_FALSE);
01064               if (NS_SUCCEEDED(rv)) {
01065                 rv = scx->SetTerminationFunction(DocumentWriteTerminationFunc,
01066                                                  arr);
01067                 // If we fail to set the termination function, just go ahead
01068                 // and EndLoad now.  The slight bugginess involved is better
01069                 // than leaking.
01070                 if (NS_SUCCEEDED(rv)) {
01071                   return;
01072                 }
01073               }
01074             }
01075           }
01076         }
01077       }
01078     }
01079   }
01080 
01081   nsDocument::EndLoad();
01082 }
01083 
01084 NS_IMETHODIMP
01085 nsHTMLDocument::SetTitle(const nsAString& aTitle)
01086 {
01087   return nsDocument::SetTitle(aTitle);
01088 }
01089 
01090 nsresult
01091 nsHTMLDocument::AddImageMap(nsIDOMHTMLMapElement* aMap)
01092 {
01093   // XXX We should order the maps based on their order in the document.
01094   // XXX Otherwise scripts that add/remove maps with duplicate names
01095   // XXX will cause problems
01096   NS_PRECONDITION(nsnull != aMap, "null ptr");
01097   if (nsnull == aMap) {
01098     return NS_ERROR_NULL_POINTER;
01099   }
01100   if (mImageMaps.AppendObject(aMap)) {
01101     return NS_OK;
01102   }
01103   return NS_ERROR_OUT_OF_MEMORY;
01104 }
01105 
01106 void
01107 nsHTMLDocument::RemoveImageMap(nsIDOMHTMLMapElement* aMap)
01108 {
01109   NS_PRECONDITION(nsnull != aMap, "null ptr");
01110   mImageMaps.RemoveObject(aMap);
01111 }
01112 
01113 nsIDOMHTMLMapElement *
01114 nsHTMLDocument::GetImageMap(const nsAString& aMapName)
01115 {
01116   nsAutoString name;
01117   PRUint32 i, n = mImageMaps.Count();
01118   nsIDOMHTMLMapElement *firstMatch = nsnull;
01119 
01120   for (i = 0; i < n; ++i) {
01121     nsIDOMHTMLMapElement *map = mImageMaps[i];
01122     NS_ASSERTION(map, "Null map in map list!");
01123 
01124     PRBool match;
01125     nsresult rv;
01126 
01127     if (IsXHTML()) {
01128       rv = map->GetId(name);
01129 
01130       match = name.Equals(aMapName);
01131     } else {
01132       rv = map->GetName(name);
01133 
01134       match = name.Equals(aMapName, nsCaseInsensitiveStringComparator());
01135     }
01136 
01137     if (match && NS_SUCCEEDED(rv)) {
01138       // Quirk: if the first matching map is empty, remember it, but keep
01139       // searching for a non-empty one, only use it if none was found (bug 264624).
01140       if (mCompatMode == eCompatibility_NavQuirks) {
01141         nsCOMPtr<nsIDOMHTMLCollection> mapAreas;
01142         rv = map->GetAreas(getter_AddRefs(mapAreas));
01143         if (NS_SUCCEEDED(rv) && mapAreas) {
01144           PRUint32 length = 0;
01145           mapAreas->GetLength(&length);
01146           if (length == 0) {
01147             if (!firstMatch) {
01148               firstMatch = map;
01149             }
01150             continue;
01151           }
01152         }
01153       }
01154       return map;
01155     }
01156   }
01157 
01158   return firstMatch;
01159 }
01160 
01161 nsCompatibility
01162 nsHTMLDocument::GetCompatibilityMode()
01163 {
01164   return mCompatMode;
01165 }
01166 
01167 void
01168 nsHTMLDocument::SetCompatibilityMode(nsCompatibility aMode)
01169 {
01170   NS_ASSERTION(!IsXHTML() || aMode == eCompatibility_FullStandards,
01171                "Bad compat mode for XHTML document!");
01172 
01173   mCompatMode = aMode;
01174   CSSLoader()->SetCompatibilityMode(mCompatMode);
01175   nsCOMPtr<nsIPresShell> shell = (nsIPresShell*)mPresShells.SafeElementAt(0);
01176   if (shell) {
01177     nsPresContext *pc = shell->GetPresContext();
01178     if (pc) {
01179       pc->SetCompatibilityMode(mCompatMode);
01180     }
01181   }
01182 }
01183 
01184 void
01185 nsHTMLDocument::ContentAppended(nsIContent* aContainer,
01186                                 PRInt32 aNewIndexInContainer)
01187 {
01188   NS_ABORT_IF_FALSE(aContainer, "Null container!");
01189 
01190   // Register new content. That is the content numbered from
01191   // aNewIndexInContainer and upwards.
01192   PRUint32 count = aContainer->GetChildCount();
01193 
01194   for (PRUint32 i = aNewIndexInContainer; i < count; ++i) {
01195     RegisterNamedItems(aContainer->GetChildAt(i));
01196   }
01197 
01198   nsDocument::ContentAppended(aContainer, aNewIndexInContainer);
01199 }
01200 
01201 void
01202 nsHTMLDocument::ContentInserted(nsIContent* aContainer, nsIContent* aContent,
01203                                 PRInt32 aIndexInContainer)
01204 {
01205   NS_ABORT_IF_FALSE(aContent, "Null content!");
01206 
01207   nsresult rv = RegisterNamedItems(aContent);
01208 
01209   if (NS_FAILED(rv)) {
01210     return;
01211   }
01212 
01213   nsDocument::ContentInserted(aContainer, aContent, aIndexInContainer);
01214 }
01215 
01216 void
01217 nsHTMLDocument::ContentRemoved(nsIContent* aContainer, nsIContent* aContent,
01218                                PRInt32 aIndexInContainer)
01219 {
01220   NS_ABORT_IF_FALSE(aContent, "Null content!");
01221 
01222   if (aContainer == mRootContent) {
01223     // Reset mBodyContent in case we got a new body.
01224 
01225     mBodyContent = nsnull;
01226   }
01227 
01228   nsresult rv = UnregisterNamedItems(aContent);
01229 
01230   if (NS_FAILED(rv)) {
01231     return;
01232   }
01233 
01234   nsDocument::ContentRemoved(aContainer, aContent, aIndexInContainer);
01235 }
01236 
01237 void
01238 nsHTMLDocument::AttributeWillChange(nsIContent* aContent, PRInt32 aNameSpaceID,
01239                                     nsIAtom* aAttribute)
01240 {
01241   NS_ABORT_IF_FALSE(aContent, "Null content!");
01242   NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
01243 
01244   if (!IsXHTML() && aAttribute == nsHTMLAtoms::name &&
01245       aNameSpaceID == kNameSpaceID_None) {
01246     nsAutoString value;
01247 
01248     if (IsNamedItem(aContent, aContent->Tag(), value)) {
01249       nsresult rv = RemoveFromNameTable(value, aContent);
01250 
01251       if (NS_FAILED(rv)) {
01252         return;
01253       }
01254     }
01255   } else if (aAttribute == aContent->GetIDAttributeName() &&
01256              aNameSpaceID == kNameSpaceID_None) {
01257     nsresult rv = RemoveFromIdTable(aContent);
01258 
01259     if (NS_FAILED(rv)) {
01260       return;
01261     }
01262   }
01263 
01264   nsDocument::AttributeWillChange(aContent, aNameSpaceID, aAttribute);
01265 }
01266 
01267 void
01268 nsHTMLDocument::AttributeChanged(nsIContent* aContent, PRInt32 aNameSpaceID,
01269                                  nsIAtom* aAttribute, PRInt32 aModType)
01270 {
01271   NS_ABORT_IF_FALSE(aContent, "Null content!");
01272   NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
01273 
01274   if (!IsXHTML() && aAttribute == nsHTMLAtoms::name &&
01275       aNameSpaceID == kNameSpaceID_None) {
01276     nsAutoString value;
01277 
01278     if (IsNamedItem(aContent, aContent->Tag(), value)) {
01279       nsresult rv = UpdateNameTableEntry(value, aContent);
01280 
01281       if (NS_FAILED(rv)) {
01282         return;
01283       }
01284     }
01285   } else if (aAttribute == aContent->GetIDAttributeName() &&
01286              aNameSpaceID == kNameSpaceID_None) {
01287     nsAutoString value;
01288 
01289     aContent->GetAttr(aNameSpaceID,
01290                       aContent->GetIDAttributeName(),
01291                       value);
01292 
01293     if (!value.IsEmpty()) {
01294       nsresult rv = AddToIdTable(value, aContent);
01295 
01296       if (NS_FAILED(rv)) {
01297         return;
01298       }
01299     }
01300   }
01301 
01302   nsDocument::AttributeChanged(aContent, aNameSpaceID, aAttribute, aModType);
01303 }
01304 
01305 void
01306 nsHTMLDocument::FlushPendingNotifications(mozFlushType aType)
01307 {
01308   // Determine if it is safe to flush the sink notifications
01309   // by determining if it safe to flush all the presshells.
01310   if ((aType & Flush_Content) && mParser &&
01311       (!(aType & Flush_SinkNotifications) || IsSafeToFlush())) {
01312     nsCOMPtr<nsIContentSink> sink = mParser->GetContentSink();
01313     if (sink) {
01314       sink->FlushPendingNotifications(aType);
01315     }
01316   }
01317   
01318   nsDocument::FlushPendingNotifications(aType);
01319 }
01320 
01321 PRBool
01322 nsHTMLDocument::IsCaseSensitive()
01323 {
01324   return IsXHTML();
01325 }
01326 
01327 //
01328 // nsIDOMDocument interface implementation
01329 //
01330 NS_IMETHODIMP
01331 nsHTMLDocument::CreateElement(const nsAString& aTagName,
01332                               nsIDOMElement** aReturn)
01333 {
01334   *aReturn = nsnull;
01335   nsresult rv;
01336 
01337   nsAutoString tagName(aTagName);
01338 
01339   // if we are in quirks, allow surrounding '<' '>' for IE compat
01340   if (mCompatMode == eCompatibility_NavQuirks &&
01341       tagName.Length() > 2 &&
01342       tagName.First() == '<' &&
01343       tagName.Last() == '>') {
01344     tagName = Substring(tagName, 1, tagName.Length() - 2); 
01345   }
01346 
01347   rv = nsContentUtils::CheckQName(tagName, PR_FALSE);
01348   NS_ENSURE_SUCCESS(rv, rv);
01349 
01350   if (!IsXHTML()) {
01351     ToLowerCase(tagName);
01352   }
01353 
01354   nsCOMPtr<nsIAtom> name = do_GetAtom(tagName);
01355 
01356   nsCOMPtr<nsIContent> content;
01357   rv = CreateElem(name, nsnull, GetDefaultNamespaceID(), PR_TRUE,
01358                   getter_AddRefs(content));
01359   NS_ENSURE_SUCCESS(rv, rv);
01360 
01361   return CallQueryInterface(content, aReturn);
01362 }
01363 
01364 NS_IMETHODIMP
01365 nsHTMLDocument::CreateElementNS(const nsAString& aNamespaceURI,
01366                                 const nsAString& aQualifiedName,
01367                                 nsIDOMElement** aReturn)
01368 {
01369   return nsDocument::CreateElementNS(aNamespaceURI, aQualifiedName, aReturn);
01370 }
01371 
01372 NS_IMETHODIMP
01373 nsHTMLDocument::CreateProcessingInstruction(const nsAString& aTarget,
01374                                             const nsAString& aData,
01375                                             nsIDOMProcessingInstruction** aReturn)
01376 {
01377   if (IsXHTML()) {
01378     return nsDocument::CreateProcessingInstruction(aTarget, aData, aReturn);
01379   }
01380 
01381   // There are no PIs for HTML
01382   *aReturn = nsnull;
01383 
01384   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
01385 }
01386 
01387 NS_IMETHODIMP
01388 nsHTMLDocument::CreateCDATASection(const nsAString& aData,
01389                                    nsIDOMCDATASection** aReturn)
01390 {
01391   if (IsXHTML()) {
01392     return nsDocument::CreateCDATASection(aData, aReturn);
01393   }
01394 
01395   // There are no CDATASections in HTML
01396   *aReturn = nsnull;
01397 
01398   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
01399 }
01400 
01401 NS_IMETHODIMP
01402 nsHTMLDocument::CreateEntityReference(const nsAString& aName,
01403                                       nsIDOMEntityReference** aReturn)
01404 {
01405   if (IsXHTML()) {
01406     return nsDocument::CreateEntityReference(aName, aReturn);
01407   }
01408 
01409   // There are no EntityReferences in HTML
01410   *aReturn = nsnull;
01411 
01412   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
01413 }
01414 
01415 NS_IMETHODIMP
01416 nsHTMLDocument::GetDoctype(nsIDOMDocumentType** aDocumentType)
01417 {
01418   return nsDocument::GetDoctype(aDocumentType);
01419 }
01420 
01421 NS_IMETHODIMP
01422 nsHTMLDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation)
01423 {
01424   return nsDocument::GetImplementation(aImplementation);
01425 }
01426 
01427 NS_IMETHODIMP
01428 nsHTMLDocument::GetDocumentElement(nsIDOMElement** aDocumentElement)
01429 {
01430   return nsDocument::GetDocumentElement(aDocumentElement);
01431 }
01432 
01433 NS_IMETHODIMP
01434 nsHTMLDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn)
01435 {
01436   return nsDocument::CreateDocumentFragment(aReturn);
01437 }
01438 
01439 NS_IMETHODIMP
01440 nsHTMLDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn)
01441 {
01442   return nsDocument::CreateComment(aData, aReturn);
01443 }
01444 
01445 NS_IMETHODIMP
01446 nsHTMLDocument::CreateAttribute(const nsAString& aName, nsIDOMAttr** aReturn)
01447 {
01448   return nsDocument::CreateAttribute(aName, aReturn);
01449 }
01450 
01451 NS_IMETHODIMP
01452 nsHTMLDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
01453 {
01454   return nsDocument::CreateTextNode(aData, aReturn);
01455 }
01456 
01457 NS_IMETHODIMP
01458 nsHTMLDocument::GetElementsByTagName(const nsAString& aTagname,
01459                                      nsIDOMNodeList** aReturn)
01460 {
01461   nsAutoString tmp(aTagname);
01462   if (!IsXHTML()) {
01463     ToLowerCase(tmp); // HTML elements are lower case internally.
01464   }
01465   return nsDocument::GetElementsByTagName(tmp, aReturn);
01466 }
01467 
01468 NS_IMETHODIMP
01469 nsHTMLDocument::GetBaseURI(nsAString &aURI)
01470 {
01471   aURI.Truncate();
01472   nsIURI *uri = mDocumentBaseURI; // WEAK
01473 
01474   if (!uri) {
01475     uri = mDocumentURI;
01476   }
01477 
01478   if (uri) {
01479     nsCAutoString spec;
01480     uri->GetSpec(spec);
01481 
01482     CopyUTF8toUTF16(spec, aURI);
01483   }
01484 
01485   return NS_OK;
01486 }
01487 
01488 // nsIDOM3Document interface implementation
01489 NS_IMETHODIMP
01490 nsHTMLDocument::GetXmlEncoding(nsAString& aXmlEncoding)
01491 {
01492   if (IsXHTML()) {
01493     return nsDocument::GetXmlEncoding(aXmlEncoding);
01494   }
01495 
01496   SetDOMStringToNull(aXmlEncoding);
01497 
01498   return NS_OK;
01499 }
01500 
01501 NS_IMETHODIMP
01502 nsHTMLDocument::GetXmlStandalone(PRBool *aXmlStandalone)
01503 {
01504   if (IsXHTML()) {
01505     return nsDocument::GetXmlStandalone(aXmlStandalone);
01506   }
01507 
01508   *aXmlStandalone = PR_FALSE;
01509 
01510   return NS_OK;
01511 }
01512 
01513 NS_IMETHODIMP
01514 nsHTMLDocument::SetXmlStandalone(PRBool aXmlStandalone)
01515 {
01516   if (IsXHTML()) {
01517     return nsDocument::SetXmlStandalone(aXmlStandalone);
01518   }
01519 
01520   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
01521 }
01522 
01523 
01524 NS_IMETHODIMP
01525 nsHTMLDocument::GetXmlVersion(nsAString& aXmlVersion)
01526 {
01527   if (IsXHTML()) {
01528     return nsDocument::GetXmlVersion(aXmlVersion);
01529   }
01530 
01531   SetDOMStringToNull(aXmlVersion);
01532 
01533   return NS_OK;
01534 }
01535 
01536 NS_IMETHODIMP
01537 nsHTMLDocument::SetXmlVersion(const nsAString& aXmlVersion)
01538 {
01539   if (IsXHTML()) {
01540     return nsDocument::SetXmlVersion(aXmlVersion);
01541   }
01542 
01543   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
01544 }
01545 
01546 //
01547 // nsIDOMHTMLDocument interface implementation
01548 //
01549 NS_IMETHODIMP
01550 nsHTMLDocument::GetTitle(nsAString& aTitle)
01551 {
01552   return nsDocument::GetTitle(aTitle);
01553 }
01554 
01555 NS_IMETHODIMP
01556 nsHTMLDocument::GetReferrer(nsAString& aReferrer)
01557 {
01558   return nsDocument::GetReferrer(aReferrer);
01559 }
01560 
01561 void
01562 nsHTMLDocument::GetDomainURI(nsIURI **aURI)
01563 {
01564   *aURI = nsnull;
01565 
01566   nsIPrincipal *principal = GetPrincipal();
01567   if (!principal)
01568     return;
01569 
01570   principal->GetDomain(aURI);
01571   if (!*aURI) {
01572     principal->GetURI(aURI);
01573   }
01574 }
01575 
01576 
01577 NS_IMETHODIMP
01578 nsHTMLDocument::GetDomain(nsAString& aDomain)
01579 {
01580   nsCOMPtr<nsIURI> uri;
01581   GetDomainURI(getter_AddRefs(uri));
01582 
01583   if (!uri) {
01584     return NS_ERROR_FAILURE;
01585   }
01586 
01587   nsCAutoString hostName;
01588 
01589   if (NS_SUCCEEDED(uri->GetHost(hostName))) {
01590     CopyUTF8toUTF16(hostName, aDomain);
01591   } else {
01592     // If we can't get the host from the URI (e.g. about:, javascript:,
01593     // etc), just return an null string.
01594     SetDOMStringToNull(aDomain);
01595   }
01596 
01597   return NS_OK;
01598 }
01599 
01600 NS_IMETHODIMP
01601 nsHTMLDocument::SetDomain(const nsAString& aDomain)
01602 {
01603   // Check new domain - must be a superdomain of the current host
01604   // For example, a page from foo.bar.com may set domain to bar.com,
01605   // but not to ar.com, baz.com, or fi.foo.bar.com.
01606   if (aDomain.IsEmpty())
01607     return NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN;
01608   nsAutoString current;
01609   if (NS_FAILED(GetDomain(current)))
01610     return NS_ERROR_FAILURE;
01611   PRBool ok = PR_FALSE;
01612   if (current.Equals(aDomain)) {
01613     ok = PR_TRUE;
01614   } else if (aDomain.Length() < current.Length()) {
01615     nsAutoString suffix;
01616     current.Right(suffix, aDomain.Length());
01617     PRUnichar c = current.CharAt(current.Length() - aDomain.Length() - 1);
01618     if (suffix.Equals(aDomain, nsCaseInsensitiveStringComparator()) &&
01619         (c == '.'))
01620       ok = PR_TRUE;
01621   }
01622   if (!ok) {
01623     // Error: illegal domain
01624     return NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN;
01625   }
01626 
01627   // Create new URI
01628   nsCOMPtr<nsIURI> uri;
01629   GetDomainURI(getter_AddRefs(uri));
01630 
01631   if (!uri) {
01632     return NS_ERROR_FAILURE;
01633   }
01634 
01635   nsCAutoString newURIString;
01636   if (NS_FAILED(uri->GetScheme(newURIString)))
01637     return NS_ERROR_FAILURE;
01638   nsCAutoString path;
01639   if (NS_FAILED(uri->GetPath(path)))
01640     return NS_ERROR_FAILURE;
01641   newURIString.AppendLiteral("://");
01642   AppendUTF16toUTF8(aDomain, newURIString);
01643   newURIString.Append(path);
01644 
01645   nsCOMPtr<nsIURI> newURI;
01646   if (NS_FAILED(NS_NewURI(getter_AddRefs(newURI), newURIString)))
01647     return NS_ERROR_FAILURE;
01648 
01649   nsIPrincipal* principal = GetPrincipal();
01650   NS_ENSURE_STATE(principal);
01651   nsresult rv = principal->SetDomain(newURI);
01652 
01653   // Bug 13871: Frameset spoofing - note that document.domain was set
01654   if (NS_SUCCEEDED(rv)) {
01655     mDomainWasSet = PR_TRUE;
01656   }
01657 
01658   return rv;
01659 }
01660 
01661 PRBool
01662 nsHTMLDocument::WasDomainSet()
01663 {
01664   return mDomainWasSet;
01665 }
01666 
01667 NS_IMETHODIMP
01668 nsHTMLDocument::GetURL(nsAString& aURL)
01669 {
01670   nsCAutoString str;
01671 
01672   if (mDocumentURI) {
01673     mDocumentURI->GetSpec(str);
01674   }
01675 
01676   CopyUTF8toUTF16(str, aURL);
01677 
01678   return NS_OK;
01679 }
01680 
01681 NS_IMETHODIMP
01682 nsHTMLDocument::GetBody(nsIDOMHTMLElement** aBody)
01683 {
01684   NS_ENSURE_ARG_POINTER(aBody);
01685   *aBody = nsnull;
01686 
01687   nsISupports* element = nsnull;
01688   nsCOMPtr<nsIDOMNode> node;
01689 
01690   if (mBodyContent || GetBodyContent()) {
01691     // There is a body element, return that as the body.
01692     element = mBodyContent;
01693   } else {
01694     // The document is most likely a frameset document so look for the
01695     // outer most frameset element
01696 
01697     nsCOMPtr<nsIDOMNodeList> nodeList;
01698 
01699     nsresult rv;
01700     if (IsXHTML()) {
01701       rv = GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"),
01702                                   NS_LITERAL_STRING("frameset"),
01703                                   getter_AddRefs(nodeList));
01704     } else {
01705       rv = GetElementsByTagName(NS_LITERAL_STRING("frameset"),
01706                                 getter_AddRefs(nodeList));
01707     }
01708 
01709     if (nodeList) {
01710       rv |= nodeList->Item(0, getter_AddRefs(node));
01711 
01712       element = node;
01713     }
01714 
01715     NS_ENSURE_SUCCESS(rv, rv);
01716   }
01717 
01718   return element ? CallQueryInterface(element, aBody) : NS_OK;
01719 }
01720 
01721 NS_IMETHODIMP
01722 nsHTMLDocument::SetBody(nsIDOMHTMLElement* aBody)
01723 {
01724   nsCOMPtr<nsIContent> body(do_QueryInterface(aBody));
01725   nsCOMPtr<nsIDOMElement> root(do_QueryInterface(mRootContent));
01726 
01727   // The body element must be either a body tag or a frameset tag.
01728   if (!body || !root || !(body->Tag() == nsHTMLAtoms::body ||
01729                           body->Tag() == nsHTMLAtoms::frameset)) {
01730     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
01731   }
01732 
01733   nsCOMPtr<nsIDOMNode> tmp;
01734 
01735   if (mBodyContent || GetBodyContent()) {
01736     root->ReplaceChild(aBody, mBodyContent, getter_AddRefs(tmp));
01737   } else {
01738     root->AppendChild(aBody, getter_AddRefs(tmp));
01739   }
01740 
01741   mBodyContent = aBody;
01742 
01743   return PR_FALSE;
01744 }
01745 
01746 NS_IMETHODIMP
01747 nsHTMLDocument::GetImages(nsIDOMHTMLCollection** aImages)
01748 {
01749   if (!mImages) {
01750     mImages = new nsContentList(this, nsHTMLAtoms::img, mDefaultNamespaceID);
01751     if (!mImages) {
01752       return NS_ERROR_OUT_OF_MEMORY;
01753     }
01754   }
01755 
01756   *aImages = mImages;
01757   NS_ADDREF(*aImages);
01758 
01759   return NS_OK;
01760 }
01761 
01762 NS_IMETHODIMP
01763 nsHTMLDocument::GetApplets(nsIDOMHTMLCollection** aApplets)
01764 {
01765   if (!mApplets) {
01766     mApplets = new nsContentList(this, nsHTMLAtoms::applet,
01767                                  mDefaultNamespaceID);
01768     if (!mApplets) {
01769       return NS_ERROR_OUT_OF_MEMORY;
01770     }
01771   }
01772 
01773   *aApplets = mApplets;
01774   NS_ADDREF(*aApplets);
01775 
01776   return NS_OK;
01777 }
01778 
01779 PRBool
01780 nsHTMLDocument::MatchLinks(nsIContent *aContent, PRInt32 aNamespaceID,
01781                            nsIAtom* aAtom, const nsAString& aData)
01782 {
01783   nsINodeInfo *ni = aContent->GetNodeInfo();
01784   nsIDocument *doc = aContent->GetDocument();
01785 
01786   if (ni && doc) {
01787     NS_ASSERTION(aContent->IsInDoc(),
01788                  "This method should never be called on content nodes that "
01789                  "are not in a document!");
01790 #ifdef DEBUG
01791     {
01792       nsCOMPtr<nsIHTMLDocument> htmldoc =
01793         do_QueryInterface(aContent->GetCurrentDoc());
01794       NS_ASSERTION(htmldoc,
01795                    "Huh, how did this happen? This should only be used with "
01796                    "HTML documents!");
01797     }
01798 #endif
01799 
01800     PRInt32 namespaceID = aContent->GetCurrentDoc()->GetDefaultNamespaceID();
01801 
01802     if (ni->Equals(nsHTMLAtoms::a, namespaceID) ||
01803         ni->Equals(nsHTMLAtoms::area, namespaceID)) {
01804       return aContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::href);
01805     }
01806   }
01807 
01808   return PR_FALSE;
01809 }
01810 
01811 NS_IMETHODIMP
01812 nsHTMLDocument::GetLinks(nsIDOMHTMLCollection** aLinks)
01813 {
01814   if (!mLinks) {
01815     mLinks = new nsContentList(this, MatchLinks, EmptyString());
01816     if (!mLinks) {
01817       return NS_ERROR_OUT_OF_MEMORY;
01818     }
01819   }
01820 
01821   *aLinks = mLinks;
01822   NS_ADDREF(*aLinks);
01823 
01824   return NS_OK;
01825 }
01826 
01827 PRBool
01828 nsHTMLDocument::MatchAnchors(nsIContent *aContent, PRInt32 aNamespaceID,
01829                              nsIAtom* aAtom, const nsAString& aData)
01830 {
01831   nsINodeInfo *ni = aContent->GetNodeInfo();
01832   nsIDocument *doc = aContent->GetCurrentDoc();
01833   NS_WARN_IF_FALSE(doc,
01834                    "This method should not be called on content nodes that "
01835                    "are not in a document!");
01836   if (ni && doc) {
01837 #ifdef DEBUG
01838     {
01839       nsCOMPtr<nsIHTMLDocument> htmldoc =
01840         do_QueryInterface(doc);
01841       NS_ASSERTION(htmldoc,
01842                    "Huh, how did this happen? This should only be used with "
01843                    "HTML documents!");
01844     }
01845 #endif
01846 
01847     if (ni->Equals(nsHTMLAtoms::a, doc->GetDefaultNamespaceID())) {
01848       return aContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::name);
01849     }
01850   }
01851 
01852   return PR_FALSE;
01853 }
01854 
01855 NS_IMETHODIMP
01856 nsHTMLDocument::GetAnchors(nsIDOMHTMLCollection** aAnchors)
01857 {
01858   if (!mAnchors) {
01859     mAnchors = new nsContentList(this, MatchAnchors, EmptyString());
01860     if (!mAnchors) {
01861       return NS_ERROR_OUT_OF_MEMORY;
01862     }
01863   }
01864 
01865   *aAnchors = mAnchors;
01866   NS_ADDREF(*aAnchors);
01867 
01868   return NS_OK;
01869 }
01870 
01871 NS_IMETHODIMP
01872 nsHTMLDocument::GetCookie(nsAString& aCookie)
01873 {
01874   aCookie.Truncate(); // clear current cookie in case service fails;
01875                       // no cookie isn't an error condition.
01876 
01877   // not having a cookie service isn't an error
01878   nsCOMPtr<nsICookieService> service = do_GetService(kCookieServiceCID);
01879   if (service) {
01880     // Get a URI from the document principal. We use the original
01881     // codebase in case the codebase was changed by SetDomain
01882     nsCOMPtr<nsIURI> codebaseURI;
01883     nsIPrincipal* principal = GetPrincipal();
01884     NS_ENSURE_STATE(principal);
01885     principal->GetURI(getter_AddRefs(codebaseURI));
01886 
01887     if (!codebaseURI) {
01888       // Document's principal is not a codebase (may be system), so
01889       // can't set cookies
01890 
01891       return NS_OK;
01892     }
01893 
01894     nsXPIDLCString cookie;
01895     service->GetCookieString(codebaseURI, mChannel, getter_Copies(cookie));
01896     CopyASCIItoUTF16(cookie, aCookie);
01897   }
01898 
01899   return NS_OK;
01900 }
01901 
01902 NS_IMETHODIMP
01903 nsHTMLDocument::SetCookie(const nsAString& aCookie)
01904 {
01905   // not having a cookie service isn't an error
01906   nsCOMPtr<nsICookieService> service = do_GetService(kCookieServiceCID);
01907   if (service && mDocumentURI) {
01908     nsCOMPtr<nsIPrompt> prompt;
01909     nsCOMPtr<nsIDOMWindowInternal> window =
01910       do_QueryInterface(GetScriptGlobalObject());
01911     if (window) {
01912       window->GetPrompter(getter_AddRefs(prompt));
01913     }
01914 
01915     nsCOMPtr<nsIURI> codebaseURI;
01916     nsIPrincipal* principal = GetPrincipal();
01917     NS_ENSURE_STATE(principal);
01918     principal->GetURI(getter_AddRefs(codebaseURI));
01919 
01920     if (!codebaseURI) {
01921       // Document's principal is not a codebase (may be system), so
01922       // can't set cookies
01923 
01924       return NS_OK;
01925     }
01926 
01927     NS_LossyConvertUTF16toASCII cookie(aCookie);
01928     service->SetCookieString(codebaseURI, prompt, cookie.get(), mChannel);
01929   }
01930 
01931   return NS_OK;
01932 }
01933 
01934 // XXX TBI: accepting arguments to the open method.
01935 nsresult
01936 nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
01937 {
01938   if (IsXHTML()) {
01939     // No calling document.open() on XHTML
01940 
01941     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
01942   }
01943 
01944   nsresult rv = NS_OK;
01945 
01946   // If we already have a parser we ignore the document.open call.
01947   if (mParser) {
01948 
01949     return NS_OK;
01950   }
01951 
01952   if (!nsContentUtils::CanCallerAccess(NS_STATIC_CAST(nsIDOMHTMLDocument*, this))) {
01953     nsPIDOMWindow *win = GetWindow();
01954     if (win) {
01955       nsCOMPtr<nsIDOMElement> frameElement;
01956       rv = win->GetFrameElement(getter_AddRefs(frameElement));
01957       NS_ENSURE_SUCCESS(rv, rv);
01958 
01959       if (frameElement && !nsContentUtils::CanCallerAccess(frameElement)) {
01960         return NS_ERROR_DOM_SECURITY_ERR;
01961       }
01962     }
01963   }
01964 
01965   // check whether we're in the middle of unload.  If so, ignore this call.
01966   nsCOMPtr<nsIDocShell_MOZILLA_1_8_BRANCH2> shell =
01967     do_QueryReferent(mDocumentContainer);
01968   if (shell) {
01969     PRBool inUnload;
01970     shell->GetIsInUnload(&inUnload);
01971     if (inUnload) {
01972       return NS_OK;
01973     }
01974   }
01975 
01976   nsCOMPtr<nsIDocument> callerDoc =
01977     do_QueryInterface(nsContentUtils::GetDocumentFromContext());
01978 
01979   // Grab a reference to the calling documents security info (if any)
01980   // and principal as it may be lost in the call to Reset().
01981   nsCOMPtr<nsISupports> securityInfo;
01982   if (callerDoc) {
01983     securityInfo = callerDoc->GetSecurityInfo();
01984   }
01985 
01986   nsCOMPtr<nsIPrincipal> callerPrincipal;
01987   nsContentUtils::GetSecurityManager()->
01988     GetSubjectPrincipal(getter_AddRefs(callerPrincipal));
01989 
01990   // The URI for the document after this call. Get it from the calling
01991   // principal (if available), or set it to "about:blank" if no
01992   // principal is reachable.
01993   nsCOMPtr<nsIURI> uri;
01994 
01995   if (callerPrincipal) {
01996     callerPrincipal->GetURI(getter_AddRefs(uri));
01997   }
01998   if (!uri) {
01999     rv = NS_NewURI(getter_AddRefs(uri),
02000                    NS_LITERAL_CSTRING("about:blank"));
02001     NS_ENSURE_SUCCESS(rv, rv);
02002   }
02003 
02004   nsCOMPtr<nsIDocShell> docshell = do_QueryReferent(mDocumentContainer);
02005 
02006   // Stop current loads targeted at the window this document is in.
02007   if (mScriptGlobalObject && docshell) {
02008     nsCOMPtr<nsIContentViewer> cv;
02009     docshell->GetContentViewer(getter_AddRefs(cv));
02010 
02011     if (cv) {
02012       PRBool okToUnload;
02013       rv = cv->PermitUnload(&okToUnload);
02014 
02015       if (NS_SUCCEEDED(rv) && !okToUnload) {
02016         // We don't want to unload, so stop here, but don't throw an
02017         // exception.
02018         return NS_OK;
02019       }
02020     }
02021 
02022     nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(docshell));
02023     webnav->Stop(nsIWebNavigation::STOP_NETWORK);
02024   }
02025 
02026   // The open occurred after the document finished loading.
02027   // So we reset the document and create a new one.
02028   nsCOMPtr<nsIChannel> channel;
02029   nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
02030 
02031   rv = NS_NewChannel(getter_AddRefs(channel), uri, nsnull, group);
02032 
02033   if (NS_FAILED(rv)) {
02034     return rv;
02035   }
02036 
02037   // Before we reset the doc notify the globalwindow of the change.
02038 
02039   // Hold onto ourselves on the offchance that we're down to one ref
02040   nsRefPtr<nsHTMLDocument> kungFuDeathGrip(this);
02041 
02042   if (mScriptGlobalObject) {
02043     // Remember the old scope in case the call to SetNewDocument changes it.
02044     nsCOMPtr<nsIScriptGlobalObject> oldScope(do_QueryReferent(mScopeObject));
02045 
02046     // If callerPrincipal doesn't match our principal. make sure that
02047     // SetNewDocument gives us a new inner window and clears our scope.
02048     NS_ENSURE_TRUE(GetPrincipal(), NS_ERROR_OUT_OF_MEMORY);
02049     if (!callerPrincipal ||
02050         NS_FAILED(nsContentUtils::GetSecurityManager()->
02051           CheckSameOriginPrincipal(callerPrincipal, GetPrincipal()))) {
02052       SetIsInitialDocument(PR_FALSE);
02053     }      
02054 
02055     rv = mScriptGlobalObject->SetNewDocument((nsDocument *)this, nsnull,
02056                                              PR_FALSE, PR_FALSE);
02057     NS_ENSURE_SUCCESS(rv, rv);
02058 
02059     // Now make sure we're not flagged as the initial document anymore, now
02060     // that we've had stuff done to us.  From now on, if anyone tries to
02061     // document.open() us, they get a new inner window.
02062     SetIsInitialDocument(PR_FALSE);
02063 
02064     nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
02065     if (oldScope && newScope != oldScope) {
02066       nsContentUtils::ReparentContentWrappersInScope(oldScope, newScope);
02067     }
02068   }
02069 
02070   // XXX This is a nasty workaround for a scrollbar code bug
02071   // (http://bugzilla.mozilla.org/show_bug.cgi?id=55334).
02072 
02073   // Hold on to our root element
02074   nsCOMPtr<nsIContent> root(mRootContent);
02075 
02076   if (root) {
02077     PRInt32 rootIndex = mChildren.IndexOfChild(mRootContent);
02078     NS_ASSERTION(rootIndex >= 0, "Root must be in list!");
02079     
02080     PRUint32 count = root->GetChildCount();
02081 
02082     // Remove all the children from the root.
02083     while (count-- > 0) {
02084       root->RemoveChildAt(count, PR_TRUE);
02085     }
02086 
02087     count = mRootContent->GetAttrCount();
02088 
02089     // Remove all attributes from the root element
02090     while (count-- > 0) {
02091       nsCOMPtr<nsIAtom> name, prefix;
02092       PRInt32 nsid;
02093 
02094       root->GetAttrNameAt(count, &nsid, getter_AddRefs(name),
02095                           getter_AddRefs(prefix));
02096 
02097       root->UnsetAttr(nsid, name, PR_FALSE);
02098     }
02099 
02100     // Remove the root from the childlist
02101     mChildren.RemoveChildAt(rootIndex);
02102 
02103     mRootContent = nsnull;
02104   }
02105 
02106   // Call Reset(), this will now do the full reset, except removing
02107   // the root from the document, doing that confuses the scrollbar
02108   // code in mozilla since the document in the root element and all
02109   // the anonymous content (i.e. scrollbar elements) is set to
02110   // null.
02111 
02112   Reset(channel, group);
02113 
02114   if (root) {
02115     // Tear down the frames for the root element.
02116     ContentRemoved(nsnull, root, 0);
02117 
02118     // Put the root element back into the document, we don't notify
02119     // the document about this insertion since the sink will do that
02120     // for us, the sink will call InitialReflow() and that'll create
02121     // frames for the root element and the scrollbars work as expected
02122     // (since the document in the root element was never set to null)
02123 
02124     mChildren.AppendChild(root);
02125     mRootContent = root;
02126   }
02127 
02128   if (mEditingIsOn) {
02129     // Reset() blows away all event listeners in the document, and our
02130     // editor relies heavily on those. Midas is turned on, to make it
02131     // work, re-initialize it to give it a chance to add its event
02132     // listeners again.
02133 
02134     SetDesignMode(NS_LITERAL_STRING("off"));
02135     SetDesignMode(NS_LITERAL_STRING("on"));
02136   }
02137 
02138   // Zap the old title -- otherwise it would hang around until document.close()
02139   // (which might never come) if the new document doesn't explicitly set one.
02140   // Void the title to make sure that we actually respect any titles set by the
02141   // new document.
02142   SetTitle(EmptyString());
02143   mDocumentTitle.SetIsVoid(PR_TRUE);
02144 
02145   // Store the security info of the caller now that we're done
02146   // resetting the document.
02147   mSecurityInfo = securityInfo;
02148 
02149   // Restore the principal to that of the caller.
02150   mPrincipal = callerPrincipal;
02151 
02152   // Recover if we had a problem obtaining the caller principal. In
02153   // such a case we set the documents URI to be about:blank (uri is
02154   // set to that above already) and the appropriate principal will be
02155   // created as needed.
02156   if (!mPrincipal) {
02157     mDocumentURI = uri;
02158   }
02159 
02160   mParser = do_CreateInstance(kCParserCID, &rv);
02161 
02162   // This will be propagated to the parser when someone actually calls write()
02163   mContentType = aContentType;
02164 
02165   mIsWriting = 1;
02166 
02167   if (NS_SUCCEEDED(rv)) {
02168     nsCOMPtr<nsIHTMLContentSink> sink;
02169 
02170     rv = NS_NewHTMLContentSink(getter_AddRefs(sink), this, uri, docshell,
02171                                channel);
02172     NS_ENSURE_SUCCESS(rv, rv);
02173 
02174     static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID);
02175     nsCOMPtr<nsIDTD> theDTD(do_CreateInstance(kNavDTDCID));
02176     if(theDTD) {
02177       mParser->RegisterDTD(theDTD);
02178     }
02179 
02180     mParser->SetContentSink(sink);
02181   }
02182 
02183   // Prepare the docshell and the document viewer for the impending
02184   // out of band document.write()
02185   if (docshell) {
02186     docshell->PrepareForNewContentModel();
02187 
02188     // Now check whether we were opened with a "replace" argument.  If
02189     // so, we need to tell the docshell to not create a new history
02190     // entry for this load.
02191     // XXXbz we're basically duplicating the MAKE_LOAD_TYPE macro from
02192     // nsDocShell.h.  All this stuff needs better apis.
02193     PRUint32 loadType;
02194     if (aReplace) {
02195       loadType = nsIDocShell::LOAD_CMD_NORMAL |
02196         (nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY << 16);
02197     } else {
02198       // Make sure that we're doing a normal load, not whatever type
02199       // of load was previously done on this docshell.
02200       loadType = nsIDocShell::LOAD_CMD_NORMAL |
02201         (nsIWebNavigation::LOAD_FLAGS_NONE << 16);
02202     }
02203     docshell->SetLoadType(loadType);
02204     
02205     nsCOMPtr<nsIContentViewer> cv;
02206     docshell->GetContentViewer(getter_AddRefs(cv));
02207     nsCOMPtr<nsIDocumentViewer> docViewer = do_QueryInterface(cv);
02208     if (docViewer) {
02209       docViewer->LoadStart(NS_STATIC_CAST(nsIHTMLDocument *, this));
02210     }
02211   }
02212 
02213   // Add a wyciwyg channel request into the document load group
02214   NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::OpenCommon(): wyciwyg "
02215                "channel already exists!");
02216 
02217   // In case the editor is listening and will see the new channel
02218   // being added, make sure mWriteLevel is non-zero so that the editor
02219   // knows that document.open/write/close() is being called on this
02220   // document.
02221   ++mWriteLevel;
02222 
02223   CreateAndAddWyciwygChannel();
02224 
02225   --mWriteLevel;
02226 
02227   return rv;
02228 }
02229 
02230 NS_IMETHODIMP
02231 nsHTMLDocument::Open()
02232 {
02233   nsCOMPtr<nsIDOMDocument> doc;
02234   return Open(NS_LITERAL_CSTRING("text/html"), PR_FALSE, getter_AddRefs(doc));
02235 }
02236 
02237 NS_IMETHODIMP
02238 nsHTMLDocument::Open(const nsACString& aContentType, PRBool aReplace,
02239                      nsIDOMDocument** aReturn)
02240 {
02241   nsresult rv = OpenCommon(aContentType, aReplace);
02242   NS_ENSURE_SUCCESS(rv, rv);
02243 
02244   return CallQueryInterface(this, aReturn);
02245 }
02246 
02247 NS_IMETHODIMP
02248 nsHTMLDocument::Clear()
02249 {
02250   // This method has been deprecated
02251   return NS_OK;
02252 }
02253 
02254 NS_IMETHODIMP
02255 nsHTMLDocument::Close()
02256 {
02257   if (IsXHTML()) {
02258     // No calling document.close() on XHTML!
02259 
02260     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
02261   }
02262 
02263   nsresult rv = NS_OK;
02264 
02265   if (mParser && mIsWriting) {
02266     ++mWriteLevel;
02267     if (mContentType.EqualsLiteral("text/html")) {
02268       rv = mParser->Parse(NS_LITERAL_STRING("</HTML>"),
02269                           GenerateParserKey(),
02270                           mContentType, PR_FALSE,
02271                           PR_TRUE);
02272     } else {
02273       rv = mParser->Parse(EmptyString(), GenerateParserKey(),
02274                           mContentType, PR_FALSE, PR_TRUE);
02275     }
02276     --mWriteLevel;
02277     mIsWriting = 0;
02278     mParser = nsnull;
02279 
02280     // XXX Make sure that all the document.written content is
02281     // reflowed.  We should remove this call once we change
02282     // nsHTMLDocument::OpenCommon() so that it completely destroys the
02283     // earlier document's content and frame hierarchy.  Right now, it
02284     // re-uses the earlier document's root content object and
02285     // corresponding frame objects.  These re-used frame objects think
02286     // that they have already been reflowed, so they drop initial
02287     // reflows.  For certain cases of document.written content, like a
02288     // frameset document, the dropping of the initial reflow means
02289     // that we end up in document.close() without appended any reflow
02290     // commands to the reflow queue and, consequently, without adding
02291     // the dummy layout request to the load group.  Since the dummy
02292     // layout request is not added to the load group, the onload
02293     // handler of the frameset fires before the frames get reflowed
02294     // and loaded.  That is the long explanation for why we need this
02295     // one line of code here!
02296     // XXXbz as far as I can tell this may not be needed anymore; all
02297     // the testcases in bug 57636 pass without this line...  Leaving
02298     // it be for now, though.  In any case, there's no reason to do
02299     // this if we have no presshell, since in that case none of the
02300     // above about reusing frames applies.
02301     if (GetNumberOfShells() != 0) {
02302       FlushPendingNotifications(Flush_Layout);
02303     }
02304 
02305     // Remove the wyciwyg channel request from the document load group
02306     // that we added in OpenCommon().  If all other requests between
02307     // document.open() and document.close() have completed, then this
02308     // method should cause the firing of an onload event.
02309     NS_ASSERTION(mWyciwygChannel, "nsHTMLDocument::Close(): Trying to remove "
02310                  "non-existent wyciwyg channel!");
02311     RemoveWyciwygChannel();
02312     NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Close(): "
02313                  "nsIWyciwygChannel could not be removed!");
02314   }
02315 
02316   return NS_OK;
02317 }
02318 
02319 nsresult
02320 nsHTMLDocument::WriteCommon(const nsAString& aText,
02321                             PRBool aNewlineTerminate)
02322 {
02323   mTooDeepWriteRecursion =
02324     (mWriteLevel > NS_MAX_DOCUMENT_WRITE_DEPTH || mTooDeepWriteRecursion);
02325   NS_ENSURE_STATE(!mTooDeepWriteRecursion);
02326 
02327   if (IsXHTML()) {
02328     // No calling document.write*() on XHTML!
02329 
02330     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
02331   }
02332 
02333   nsresult rv = NS_OK;
02334 
02335   if (!mParser) {
02336     rv = Open();
02337 
02338     // If Open() fails, or if it didn't create a parser (as it won't
02339     // if the user chose to not discard the current document through
02340     // onbeforeunload), don't write anything.
02341     if (NS_FAILED(rv) || !mParser) {
02342       return rv;
02343     }
02344   }
02345 
02346   static NS_NAMED_LITERAL_STRING(new_line, "\n");
02347 
02348   // Save the data in cache
02349   if (mWyciwygChannel) {
02350     if (!aText.IsEmpty()) {
02351       mWyciwygChannel->WriteToCacheEntry(aText);
02352     }
02353 
02354     if (aNewlineTerminate) {
02355       mWyciwygChannel->WriteToCacheEntry(new_line);
02356     }
02357   }
02358 
02359   ++mWriteLevel;
02360 
02361   // This could be done with less code, but for performance reasons it
02362   // makes sense to have the code for two separate Parse() calls here
02363   // since the concatenation of strings costs more than we like. And
02364   // why pay that price when we don't need to?
02365   if (aNewlineTerminate) {
02366     rv = mParser->Parse(aText + new_line,
02367                         GenerateParserKey(),
02368                         mContentType, PR_FALSE,
02369                         (!mIsWriting || (mWriteLevel > 1)));
02370   } else {
02371     rv = mParser->Parse(aText,
02372                         GenerateParserKey(),
02373                         mContentType, PR_FALSE,
02374                         (!mIsWriting || (mWriteLevel > 1)));
02375   }
02376 
02377   --mWriteLevel;
02378 
02379   mTooDeepWriteRecursion = (mWriteLevel != 0 && mTooDeepWriteRecursion);
02380 
02381   return rv;
02382 }
02383 
02384 NS_IMETHODIMP
02385 nsHTMLDocument::Write(const nsAString& aText)
02386 {
02387   return WriteCommon(aText, PR_FALSE);
02388 }
02389 
02390 NS_IMETHODIMP
02391 nsHTMLDocument::Writeln(const nsAString& aText)
02392 {
02393   return WriteCommon(aText, PR_TRUE);
02394 }
02395 
02396 nsresult
02397 nsHTMLDocument::ScriptWriteCommon(PRBool aNewlineTerminate)
02398 {
02399   nsCOMPtr<nsIXPCNativeCallContext> ncc;
02400 
02401   nsresult rv = nsContentUtils::XPConnect()->
02402     GetCurrentNativeCallContext(getter_AddRefs(ncc));
02403   NS_ENSURE_SUCCESS(rv, rv);
02404 
02405   if (ncc) {
02406     // We're called from JS, concatenate the extra arguments into
02407     // string_buffer
02408     PRUint32 i, argc;
02409 
02410     ncc->GetArgc(&argc);
02411 
02412     JSContext *cx = nsnull;
02413     rv = ncc->GetJSContext(&cx);
02414     NS_ENSURE_SUCCESS(rv, rv);
02415 
02416     jsval *argv = nsnull;
02417     ncc->GetArgvPtr(&argv);
02418     NS_ENSURE_TRUE(argv, NS_ERROR_UNEXPECTED);
02419 
02420     if (argc == 1) {
02421       JSString *jsstr = JS_ValueToString(cx, argv[0]);
02422       NS_ENSURE_TRUE(jsstr, NS_ERROR_OUT_OF_MEMORY);
02423 
02424       nsDependentString str(NS_REINTERPRET_CAST(const PRUnichar *,
02425                                               ::JS_GetStringChars(jsstr)),
02426                           ::JS_GetStringLength(jsstr));
02427 
02428       return WriteCommon(str, aNewlineTerminate);
02429     }
02430 
02431     if (argc > 1) {
02432       nsAutoString string_buffer;
02433 
02434       for (i = 0; i < argc; ++i) {
02435         JSString *str = JS_ValueToString(cx, argv[i]);
02436         NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
02437 
02438         string_buffer.Append(NS_REINTERPRET_CAST(const PRUnichar *,
02439                                                  ::JS_GetStringChars(str)),
02440                              ::JS_GetStringLength(str));
02441       }
02442 
02443       return WriteCommon(string_buffer, aNewlineTerminate);
02444     }
02445   }
02446 
02447   // No arguments...
02448   return WriteCommon(EmptyString(), aNewlineTerminate);
02449 }
02450 
02451 NS_IMETHODIMP
02452 nsHTMLDocument::Write()
02453 {
02454   return ScriptWriteCommon(PR_FALSE);
02455 }
02456 
02457 NS_IMETHODIMP
02458 nsHTMLDocument::Writeln()
02459 {
02460   return ScriptWriteCommon(PR_TRUE);
02461 }
02462 
02463 NS_IMETHODIMP
02464 nsHTMLDocument::GetElementById(const nsAString& aElementId,
02465                                nsIDOMElement** aReturn)
02466 {
02467   NS_ENSURE_ARG_POINTER(aReturn);
02468   *aReturn = nsnull;
02469 
02470   IdAndNameMapEntry *entry = nsnull;
02471   nsIContent *e = nsnull;
02472   
02473   // We don't want to use the hash table at all after the document has been
02474   // destroyed, since the hash table doesn't own the objects that are placed
02475   // into it, and so they have an uncertain lifetime after the document's
02476   // released them.
02477   if (!mIsGoingAway) {
02478     // We don't have to flush before we do the initial hashtable lookup, since if
02479     // the id is already in the hashtable it couldn't have been removed without
02480     // us being notified (all removals notify immediately, as far as I can tell).
02481     // So do the lookup first.
02482     entry =
02483       NS_STATIC_CAST(IdAndNameMapEntry *,
02484                      PL_DHashTableOperate(&mIdAndNameHashTable, &aElementId,
02485                                         PL_DHASH_ADD));
02486     NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
02487 
02488     e = entry->mIdContent;
02489 
02490     if (e == ID_NOT_IN_DOCUMENT) {
02491       // Now we have to flush.  It could be that we have a cached "not in
02492       // document" but more content has been added to the document since.  Note
02493       // that we have to flush notifications, so that the entry will get updated
02494       // properly.
02495     
02496       // Make sure to stash away the current generation so we can check whether
02497       // the table changes when we flush.
02498       PRUint32 generation = mIdAndNameHashTable.generation;
02499   
02500       FlushPendingNotifications(Flush_ContentAndNotify);
02501 
02502       if (generation != mIdAndNameHashTable.generation) {
02503         // Table changed, so the entry pointer is no longer valid; look up the
02504         // entry again, adding if necessary (the adding may be necessary in case
02505         // the flush actually deleted entries).
02506         entry =
02507           NS_STATIC_CAST(IdAndNameMapEntry *,
02508                          PL_DHashTableOperate(&mIdAndNameHashTable, &aElementId,
02509                                             PL_DHASH_ADD));
02510         NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
02511       }
02512 
02513       // We could now have a new entry, or the entry could have been
02514       // updated, so update e to point to the current entry's
02515       // mIdContent.
02516       e = entry->mIdContent;
02517     }
02518 
02519     if (e == ID_NOT_IN_DOCUMENT) {
02520       // We've looked for this id before and we didn't find it, so it
02521       // won't be in the document now either (since the
02522       // mIdAndNameHashTable is live for entries in the table)
02523 
02524       return NS_OK;
02525     }
02526   
02527   }
02528 
02529   if (!e) {
02530     NS_WARN_IF_FALSE(!aElementId.IsEmpty(),
02531                      "getElementById(\"\") called, fix caller?");
02532 
02533     if (mRootContent && !aElementId.IsEmpty()) {
02534       e = MatchElementId(mRootContent, NS_ConvertUCS2toUTF8(aElementId),
02535                          aElementId);
02536     }
02537 
02538     if (!e) {
02539       // There is no element with the given id in the document, cache
02540       // the fact that it's not in the document
02541       if (entry) {
02542         entry->mIdContent = ID_NOT_IN_DOCUMENT;
02543       }
02544 
02545       return NS_OK;
02546     }
02547 
02548     // We found an element with a matching id, store that in the hash
02549     if (entry) {
02550       entry->mIdContent = e;
02551     }
02552   }
02553 
02554   return CallQueryInterface(e, aReturn);
02555 }
02556 
02557 NS_IMETHODIMP
02558 nsHTMLDocument::ImportNode(nsIDOMNode* aImportedNode,
02559                            PRBool aDeep,
02560                            nsIDOMNode** aReturn)
02561 {
02562   return nsDocument::ImportNode(aImportedNode, aDeep, aReturn);
02563 }
02564 
02565 NS_IMETHODIMP
02566 nsHTMLDocument::CreateAttributeNS(const nsAString& aNamespaceURI,
02567                                   const nsAString& aQualifiedName,
02568                                   nsIDOMAttr** aReturn)
02569 {
02570   return nsDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, aReturn);
02571 }
02572 
02573 NS_IMETHODIMP
02574 nsHTMLDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
02575                                        const nsAString& aLocalName,
02576                                        nsIDOMNodeList** aReturn)
02577 {
02578   nsAutoString tmp(aLocalName);
02579 
02580   if (!IsXHTML()) {
02581     ToLowerCase(tmp); // HTML elements are lower case internally.
02582   }
02583 
02584   return nsDocument::GetElementsByTagNameNS(aNamespaceURI, tmp, aReturn);
02585 }
02586 
02587 PRBool
02588 nsHTMLDocument::MatchNameAttribute(nsIContent* aContent, PRInt32 aNamespaceID,
02589                                    nsIAtom* aAtom, const nsAString& aData)
02590 {
02591   NS_PRECONDITION(aContent, "Must have content node to work with!");
02592   
02593   // Getting attrs is expensive, so use HasAttr() first.
02594   if (!aContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::name)) {
02595     return PR_FALSE;
02596   }
02597 
02598   nsAutoString value;
02599   nsresult rv = aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::name, value);
02600 
02601   return NS_SUCCEEDED(rv) && value.Equals(aData);
02602 }
02603 
02604 NS_IMETHODIMP
02605 nsHTMLDocument::GetElementsByName(const nsAString& aElementName,
02606                                   nsIDOMNodeList** aReturn)
02607 {
02608   nsContentList* elements = new nsContentList(this,
02609                                               MatchNameAttribute,
02610                                               aElementName);
02611   NS_ENSURE_TRUE(elements, NS_ERROR_OUT_OF_MEMORY);
02612 
02613   *aReturn = elements;
02614   NS_ADDREF(*aReturn);
02615 
02616   return NS_OK;
02617 }
02618 
02619 void
02620 nsHTMLDocument::AddedForm()
02621 {
02622   ++mNumForms;
02623 }
02624 
02625 void
02626 nsHTMLDocument::RemovedForm()
02627 {
02628   --mNumForms;
02629 }
02630 
02631 PRInt32
02632 nsHTMLDocument::GetNumFormsSynchronous()
02633 {
02634   return mNumForms;
02635 }
02636 
02637 nsresult
02638 nsHTMLDocument::GetPixelDimensions(nsIPresShell* aShell,
02639                                    PRInt32* aWidth,
02640                                    PRInt32* aHeight)
02641 {
02642   *aWidth = *aHeight = 0;
02643 
02644   FlushPendingNotifications(Flush_Layout);
02645 
02646   // Find the <body> element: this is what we'll want to use for the
02647   // document's width and height values.
02648   if (!mBodyContent && !GetBodyContent()) {
02649     return NS_OK;
02650   }
02651 
02652   nsCOMPtr<nsIContent> body = do_QueryInterface(mBodyContent);
02653 
02654   // Now grab its frame
02655   nsIFrame* frame;
02656   nsresult rv = aShell->GetPrimaryFrameFor(body, &frame);
02657   if (NS_SUCCEEDED(rv) && frame) {
02658     nsSize                    size;
02659     nsIView* view = frame->GetView();
02660 
02661     // If we have a view check if it's scrollable. If not,
02662     // just use the view size itself
02663     if (view) {
02664       nsIScrollableView* scrollableView = view->ToScrollableView();
02665 
02666       if (scrollableView) {
02667         scrollableView->GetScrolledView(view);
02668       }
02669 
02670       nsRect r = view->GetBounds();
02671       size.height = r.height;
02672       size.width = r.width;
02673     }
02674     // If we don't have a view, use the frame size
02675     else {
02676       size = frame->GetSize();
02677     }
02678 
02679     // Convert from twips to pixels
02680     nsPresContext *context = aShell->GetPresContext();
02681     if (context) {
02682       float scale;
02683       scale = context->TwipsToPixels();
02684 
02685       *aWidth = NSTwipsToIntPixels(size.width, scale);
02686       *aHeight = NSTwipsToIntPixels(size.height, scale);
02687     }
02688   }
02689 
02690   return NS_OK;
02691 }
02692 
02693 NS_IMETHODIMP
02694 nsHTMLDocument::GetWidth(PRInt32* aWidth)
02695 {
02696   NS_ENSURE_ARG_POINTER(aWidth);
02697   *aWidth = 0;
02698 
02699   // We make the assumption that the first presentation shell
02700   // is the one for which we need information.
02701   // Since GetPixelDimensions flushes and flushing can destroy
02702   // our shell, hold a strong ref to it.
02703   nsCOMPtr<nsIPresShell> shell = GetShellAt(0);
02704   if (!shell) {
02705     return NS_OK;
02706   }
02707 
02708   PRInt32 dummy;
02709 
02710   // GetPixelDimensions() does the flushing for us, no need to flush
02711   // here too
02712   return GetPixelDimensions(shell, aWidth, &dummy);
02713 }
02714 
02715 NS_IMETHODIMP
02716 nsHTMLDocument::GetHeight(PRInt32* aHeight)
02717 {
02718   NS_ENSURE_ARG_POINTER(aHeight);
02719   *aHeight = 0;
02720 
02721   // We make the assumption that the first presentation shell
02722   // is the one for which we need information.
02723   // Since GetPixelDimensions flushes and flushing can destroy
02724   // our shell, hold a strong ref to it.
02725   nsCOMPtr<nsIPresShell> shell = GetShellAt(0);
02726   if (!shell) {
02727     return NS_OK;
02728   }
02729 
02730   PRInt32 dummy;
02731 
02732   // GetPixelDimensions() does the flushing for us, no need to flush
02733   // here too
02734   return GetPixelDimensions(shell, &dummy, aHeight);
02735 }
02736 
02737 NS_IMETHODIMP
02738 nsHTMLDocument::GetAlinkColor(nsAString& aAlinkColor)
02739 {
02740   aAlinkColor.Truncate();
02741 
02742   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02743   GetBodyElement(getter_AddRefs(body));
02744 
02745   if (body) {
02746     body->GetALink(aAlinkColor);
02747   } else if (mAttrStyleSheet) {
02748     nscolor color;
02749     nsresult rv = mAttrStyleSheet->GetActiveLinkColor(color);
02750     if (NS_SUCCEEDED(rv)) {
02751       NS_RGBToHex(color, aAlinkColor);
02752     }
02753   }
02754 
02755   return NS_OK;
02756 }
02757 
02758 NS_IMETHODIMP
02759 nsHTMLDocument::SetAlinkColor(const nsAString& aAlinkColor)
02760 {
02761   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02762   GetBodyElement(getter_AddRefs(body));
02763 
02764   if (body) {
02765     body->SetALink(aAlinkColor);
02766   } else if (mAttrStyleSheet) {
02767     nsAttrValue value;
02768     if (value.ParseColor(aAlinkColor, this)) {
02769       nscolor color;
02770       value.GetColorValue(color);
02771       mAttrStyleSheet->SetActiveLinkColor(color);
02772     }
02773   }
02774 
02775   return NS_OK;
02776 }
02777 
02778 NS_IMETHODIMP
02779 nsHTMLDocument::GetLinkColor(nsAString& aLinkColor)
02780 {
02781   aLinkColor.Truncate();
02782 
02783   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02784   GetBodyElement(getter_AddRefs(body));
02785 
02786   if (body) {
02787     body->GetLink(aLinkColor);
02788   } else if (mAttrStyleSheet) {
02789     nscolor color;
02790     nsresult rv = mAttrStyleSheet->GetLinkColor(color);
02791     if (NS_SUCCEEDED(rv)) {
02792       NS_RGBToHex(color, aLinkColor);
02793     }
02794   }
02795 
02796   return NS_OK;
02797 }
02798 
02799 NS_IMETHODIMP
02800 nsHTMLDocument::SetLinkColor(const nsAString& aLinkColor)
02801 {
02802   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02803   GetBodyElement(getter_AddRefs(body));
02804 
02805   if (body) {
02806     body->SetLink(aLinkColor);
02807   } else if (mAttrStyleSheet) {
02808     nsAttrValue value;
02809     if (value.ParseColor(aLinkColor, this)) {
02810       nscolor color;
02811       value.GetColorValue(color);
02812       mAttrStyleSheet->SetLinkColor(color);
02813     }
02814   }
02815 
02816   return NS_OK;
02817 }
02818 
02819 NS_IMETHODIMP
02820 nsHTMLDocument::GetVlinkColor(nsAString& aVlinkColor)
02821 {
02822   aVlinkColor.Truncate();
02823 
02824   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02825   GetBodyElement(getter_AddRefs(body));
02826 
02827   if (body) {
02828     body->GetVLink(aVlinkColor);
02829   } else if (mAttrStyleSheet) {
02830     nscolor color;
02831     nsresult rv = mAttrStyleSheet->GetVisitedLinkColor(color);
02832     if (NS_SUCCEEDED(rv)) {
02833       NS_RGBToHex(color, aVlinkColor);
02834     }
02835   }
02836 
02837   return NS_OK;
02838 }
02839 
02840 NS_IMETHODIMP
02841 nsHTMLDocument::SetVlinkColor(const nsAString& aVlinkColor)
02842 {
02843   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02844   GetBodyElement(getter_AddRefs(body));
02845 
02846   if (body) {
02847     body->SetVLink(aVlinkColor);
02848   } else if (mAttrStyleSheet) {
02849     nsAttrValue value;
02850     if (value.ParseColor(aVlinkColor, this)) {
02851       nscolor color;
02852       value.GetColorValue(color);
02853       mAttrStyleSheet->SetVisitedLinkColor(color);
02854     }
02855   }
02856 
02857   return NS_OK;
02858 }
02859 
02860 NS_IMETHODIMP
02861 nsHTMLDocument::GetBgColor(nsAString& aBgColor)
02862 {
02863   aBgColor.Truncate();
02864 
02865   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02866   GetBodyElement(getter_AddRefs(body));
02867 
02868   if (body) {
02869     body->GetBgColor(aBgColor);
02870   }
02871 
02872   return NS_OK;
02873 }
02874 
02875 NS_IMETHODIMP
02876 nsHTMLDocument::SetBgColor(const nsAString& aBgColor)
02877 {
02878   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02879   GetBodyElement(getter_AddRefs(body));
02880 
02881   if (body) {
02882     body->SetBgColor(aBgColor);
02883   }
02884   // XXXldb And otherwise?
02885 
02886   return NS_OK;
02887 }
02888 
02889 NS_IMETHODIMP
02890 nsHTMLDocument::GetFgColor(nsAString& aFgColor)
02891 {
02892   aFgColor.Truncate();
02893 
02894   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02895   GetBodyElement(getter_AddRefs(body));
02896 
02897   if (body) {
02898     body->GetText(aFgColor);
02899   }
02900 
02901   return NS_OK;
02902 }
02903 
02904 NS_IMETHODIMP
02905 nsHTMLDocument::SetFgColor(const nsAString& aFgColor)
02906 {
02907   nsCOMPtr<nsIDOMHTMLBodyElement> body;
02908   GetBodyElement(getter_AddRefs(body));
02909 
02910   if (body) {
02911     body->SetText(aFgColor);
02912   }
02913   // XXXldb And otherwise?
02914 
02915   return NS_OK;
02916 }
02917 
02918 
02919 NS_IMETHODIMP
02920 nsHTMLDocument::GetEmbeds(nsIDOMHTMLCollection** aEmbeds)
02921 {
02922   if (!mEmbeds) {
02923     mEmbeds = new nsContentList(this, nsHTMLAtoms::embed, mDefaultNamespaceID);
02924     if (!mEmbeds) {
02925       return NS_ERROR_OUT_OF_MEMORY;
02926     }
02927   }
02928 
02929   *aEmbeds = mEmbeds;
02930   NS_ADDREF(*aEmbeds);
02931 
02932   return NS_OK;
02933 }
02934 
02935 NS_IMETHODIMP
02936 nsHTMLDocument::GetSelection(nsAString& aReturn)
02937 {
02938   aReturn.Truncate();
02939 
02940   nsCOMPtr<nsIConsoleService> consoleService
02941     (do_GetService("@mozilla.org/consoleservice;1"));
02942 
02943   if (consoleService) {
02944     consoleService->LogStringMessage(NS_LITERAL_STRING("Deprecated method document.getSelection() called.  Please use window.getSelection() instead.").get());
02945   }
02946 
02947   nsIDOMWindow *window = GetWindow();
02948   NS_ENSURE_TRUE(window, NS_OK);
02949 
02950   nsCOMPtr<nsISelection> selection;
02951   nsresult rv = window->GetSelection(getter_AddRefs(selection));
02952   NS_ENSURE_TRUE(selection && NS_SUCCEEDED(rv), rv);
02953 
02954   nsXPIDLString str;
02955 
02956   rv = selection->ToString(getter_Copies(str));
02957 
02958   aReturn.Assign(str);
02959 
02960   return rv;
02961 }
02962 
02963 static void
02964 ReportUseOfDeprecatedMethod(nsHTMLDocument* aDoc, const char* aWarning)
02965 {
02966   nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
02967                                   aWarning,
02968                                   nsnull, 0,
02969                                   NS_STATIC_CAST(nsIDocument*, aDoc)->
02970                                     GetDocumentURI(),
02971                                   EmptyString(), 0, 0,
02972                                   nsIScriptError::warningFlag,
02973                                   "DOM Events");
02974 }
02975 
02976 NS_IMETHODIMP
02977 nsHTMLDocument::CaptureEvents(PRInt32 aEventFlags)
02978 {
02979   ReportUseOfDeprecatedMethod(this, "UseOfCaptureEventsWarning");
02980 
02981   nsIEventListenerManager *manager;
02982 
02983   if (NS_OK == GetListenerManager(&manager)) {
02984     manager->CaptureEvent(aEventFlags);
02985     NS_RELEASE(manager);
02986     return NS_OK;
02987   }
02988 
02989   return NS_ERROR_FAILURE;
02990 }
02991 
02992 NS_IMETHODIMP
02993 nsHTMLDocument::ReleaseEvents(PRInt32 aEventFlags)
02994 {
02995   ReportUseOfDeprecatedMethod(this, "UseOfReleaseEventsWarning");
02996 
02997   nsIEventListenerManager *manager;
02998 
02999   if (NS_OK == GetListenerManager(&manager)) {
03000     manager->ReleaseEvent(aEventFlags);
03001     NS_RELEASE(manager);
03002     return NS_OK;
03003   }
03004 
03005   return NS_ERROR_FAILURE;
03006 }
03007 
03008 NS_IMETHODIMP
03009 nsHTMLDocument::RouteEvent(nsIDOMEvent* aEvt)
03010 {
03011   ReportUseOfDeprecatedMethod(this, "UseOfRouteEventWarning");
03012   return NS_OK;
03013 }
03014 
03015 // readonly attribute DOMString compatMode;
03016 // Returns "BackCompat" if we are in quirks mode, "CSS1Compat" if we are
03017 // in almost standards or full standards mode. See bug 105640.  This was
03018 // implemented to match MSIE's compatMode property
03019 NS_IMETHODIMP
03020 nsHTMLDocument::GetCompatMode(nsAString& aCompatMode)
03021 {
03022   NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
03023                mCompatMode == eCompatibility_AlmostStandards ||
03024                mCompatMode == eCompatibility_FullStandards,
03025                "mCompatMode is neither quirks nor strict for this document");
03026 
03027   if (mCompatMode == eCompatibility_NavQuirks) {
03028     aCompatMode.AssignLiteral("BackCompat");
03029   } else {
03030     aCompatMode.AssignLiteral("CSS1Compat");
03031   }
03032 
03033   return NS_OK;
03034 }
03035 
03036 // Mapped to document.embeds for NS4 compatibility
03037 NS_IMETHODIMP
03038 nsHTMLDocument::GetPlugins(nsIDOMHTMLCollection** aPlugins)
03039 {
03040   *aPlugins = nsnull;
03041 
03042   return GetEmbeds(aPlugins);
03043 }
03044 
03045 PR_STATIC_CALLBACK(PLDHashOperator)
03046 IdAndNameMapEntryRemoveCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
03047                                 PRUint32 number, void *arg)
03048 {
03049   return PL_DHASH_REMOVE;
03050 }
03051 
03052 
03053 void
03054 nsHTMLDocument::InvalidateHashTables()
03055 {
03056   PL_DHashTableEnumerate(&mIdAndNameHashTable, IdAndNameMapEntryRemoveCallback,
03057                          nsnull);
03058 }
03059 
03060 static nsresult
03061 ReserveNameInHash(const nsAString& aName, PLDHashTable *aHash)
03062 {
03063   IdAndNameMapEntry *entry =
03064     NS_STATIC_CAST(IdAndNameMapEntry *,
03065                    PL_DHashTableOperate(aHash, &aName, PL_DHASH_ADD));
03066 
03067   NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
03068 
03069   entry->mContentList = NAME_NOT_VALID;
03070 
03071   return NS_OK;
03072 }
03073 
03074 // Pre-fill the name hash with names that are likely to be resolved in
03075 // this document to avoid walking the tree looking for elements with
03076 // these names.
03077 
03078 nsresult
03079 nsHTMLDocument::PrePopulateHashTables()
03080 {
03081   nsresult rv = NS_OK;
03082 
03083   rv = ReserveNameInHash(NS_LITERAL_STRING("write"), &mIdAndNameHashTable);
03084   NS_ENSURE_SUCCESS(rv, rv);
03085 
03086   rv = ReserveNameInHash(NS_LITERAL_STRING("writeln"), &mIdAndNameHashTable);
03087   NS_ENSURE_SUCCESS(rv, rv);
03088 
03089   rv = ReserveNameInHash(NS_LITERAL_STRING("open"), &mIdAndNameHashTable);
03090   NS_ENSURE_SUCCESS(rv, rv);
03091 
03092   rv = ReserveNameInHash(NS_LITERAL_STRING("close"), &mIdAndNameHashTable);
03093   NS_ENSURE_SUCCESS(rv, rv);
03094 
03095   rv = ReserveNameInHash(NS_LITERAL_STRING("forms"), &mIdAndNameHashTable);
03096   NS_ENSURE_SUCCESS(rv, rv);
03097 
03098   rv = ReserveNameInHash(NS_LITERAL_STRING("elements"), &mIdAndNameHashTable);
03099   NS_ENSURE_SUCCESS(rv, rv);
03100 
03101   rv = ReserveNameInHash(NS_LITERAL_STRING("characterSet"),
03102                           &mIdAndNameHashTable);
03103   NS_ENSURE_SUCCESS(rv, rv);
03104 
03105   rv = ReserveNameInHash(NS_LITERAL_STRING("nodeType"), &mIdAndNameHashTable);
03106   NS_ENSURE_SUCCESS(rv, rv);
03107 
03108   rv = ReserveNameInHash(NS_LITERAL_STRING("parentNode"),
03109                           &mIdAndNameHashTable);
03110   NS_ENSURE_SUCCESS(rv, rv);
03111 
03112   rv = ReserveNameInHash(NS_LITERAL_STRING("cookie"), &mIdAndNameHashTable);
03113   NS_ENSURE_SUCCESS(rv, rv);
03114 
03115   return rv;
03116 }
03117 
03118 static PRBool
03119 IsNamedItem(nsIContent* aContent, nsIAtom *aTag, nsAString& aName)
03120 {
03121   // Only the content types reflected in Level 0 with a NAME
03122   // attribute are registered. Images, layers and forms always get
03123   // reflected up to the document. Applets and embeds only go
03124   // to the closest container (which could be a form).
03125   if (aTag == nsHTMLAtoms::img    ||
03126       aTag == nsHTMLAtoms::form   ||
03127       aTag == nsHTMLAtoms::applet ||
03128       aTag == nsHTMLAtoms::embed  ||
03129       aTag == nsHTMLAtoms::object) {
03130     aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::name, aName);
03131 
03132     if (!aName.IsEmpty()) {
03133       return PR_TRUE;
03134     }
03135   }
03136 
03137   return PR_FALSE;
03138 }
03139 
03140 nsresult
03141 nsHTMLDocument::UpdateNameTableEntry(const nsAString& aName,
03142                                      nsIContent *aContent)
03143 {
03144   NS_ASSERTION(!IsXHTML(), "Don't call me on an XHTML document!!!");
03145 
03146   IdAndNameMapEntry *entry =
03147     NS_STATIC_CAST(IdAndNameMapEntry *,
03148                    PL_DHashTableOperate(&mIdAndNameHashTable, &aName,
03149                                         PL_DHASH_LOOKUP));
03150 
03151   if (PL_DHASH_ENTRY_IS_FREE(entry)) {
03152     return NS_OK;
03153   }
03154 
03155   nsBaseContentList *list = entry->mContentList;
03156 
03157   if (!list || list == NAME_NOT_VALID) {
03158     return NS_OK;
03159   }
03160 
03161   // NOTE: this indexof is absolutely needed, since we don't flush
03162   // content notifications when we do document.foo resolution.  So
03163   // aContent may be in our list already and just now getting notified
03164   // for!
03165   if (list->IndexOf(aContent, PR_FALSE) < 0) {
03166     list->AppendElement(aContent);
03167   }
03168 
03169   return NS_OK;
03170 }
03171 
03172 nsresult
03173 nsHTMLDocument::AddToIdTable(const nsAString& aId, nsIContent *aContent)
03174 {
03175   if (mIsGoingAway) {
03176     return NS_OK;
03177   }
03178 
03179   IdAndNameMapEntry *entry =
03180     NS_STATIC_CAST(IdAndNameMapEntry *,
03181                    PL_DHashTableOperate(&mIdAndNameHashTable, &aId,
03182                                         PL_DHASH_ADD));
03183   NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
03184 
03185   const nsIContent *e = entry->mIdContent;
03186 
03187   if (!e || e == ID_NOT_IN_DOCUMENT) {
03188     entry->mIdContent = aContent;
03189   }
03190 
03191   return NS_OK;
03192 }
03193 
03194 nsresult
03195 nsHTMLDocument::UpdateIdTableEntry(const nsAString& aId, nsIContent *aContent)
03196 {
03197   if (mIsGoingAway) {
03198     return NS_OK;
03199   }
03200 
03201   IdAndNameMapEntry *entry =
03202     NS_STATIC_CAST(IdAndNameMapEntry *,
03203                    PL_DHashTableOperate(&mIdAndNameHashTable, &aId,
03204                                         PL_DHASH_LOOKUP));
03205 
03206   if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
03207     entry->mIdContent = aContent;
03208   }
03209 
03210   return NS_OK;
03211 }
03212 
03213 nsresult
03214 nsHTMLDocument::RemoveFromNameTable(const nsAString& aName,
03215                                     nsIContent *aContent)
03216 {
03217   NS_ASSERTION(!IsXHTML(), "Don't call me on an XHTML document!!!");
03218 
03219   IdAndNameMapEntry *entry =
03220     NS_STATIC_CAST(IdAndNameMapEntry *,
03221                    PL_DHashTableOperate(&mIdAndNameHashTable, &aName,
03222                                         PL_DHASH_LOOKUP));
03223 
03224   if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mContentList &&
03225       entry->mContentList != NAME_NOT_VALID) {
03226     entry->mContentList->RemoveElement(aContent);
03227   }
03228 
03229   return NS_OK;
03230 }
03231 
03232 nsresult
03233 nsHTMLDocument::RemoveFromIdTable(nsIContent *aContent)
03234 {
03235   nsIAtom* idAttr = aContent->GetIDAttributeName();
03236   
03237   if (!idAttr || !aContent->HasAttr(kNameSpaceID_None, idAttr)) {
03238     return NS_OK;
03239   }
03240 
03241   nsAutoString value;
03242   aContent->GetAttr(kNameSpaceID_None, idAttr, value);
03243 
03244   if (value.IsEmpty()) {
03245     return NS_OK;
03246   }
03247 
03248   IdAndNameMapEntry *entry =
03249     NS_STATIC_CAST(IdAndNameMapEntry *,
03250                    PL_DHashTableOperate(&mIdAndNameHashTable,
03251                                         NS_STATIC_CAST(const nsAString *,
03252                                                        &value),
03253                                         PL_DHASH_LOOKUP));
03254 
03255   if (PL_DHASH_ENTRY_IS_FREE(entry) || entry->mIdContent != aContent) {
03256     return NS_OK;
03257   }
03258 
03259   PL_DHashTableRawRemove(&mIdAndNameHashTable, entry);
03260 
03261   return NS_OK;
03262 }
03263 
03264 nsresult
03265 nsHTMLDocument::UnregisterNamedItems(nsIContent *aContent)
03266 {
03267   if (aContent->IsContentOfType(nsIContent::eTEXT)) {
03268     // Text nodes are not named items nor can they have children.
03269     return NS_OK;
03270   }
03271 
03272   nsAutoString value;
03273   nsresult rv = NS_OK;
03274 
03275   if (!IsXHTML() && IsNamedItem(aContent, aContent->Tag(), value)) {
03276     rv = RemoveFromNameTable(value, aContent);
03277 
03278     if (NS_FAILED(rv)) {
03279       return rv;
03280     }
03281   }
03282 
03283   rv = RemoveFromIdTable(aContent);
03284 
03285   if (NS_FAILED(rv)) {
03286     return rv;
03287   }
03288 
03289   PRUint32 i, count = aContent->GetChildCount();
03290 
03291   for (i = 0; i < count; ++i) {
03292     UnregisterNamedItems(aContent->GetChildAt(i));
03293   }
03294 
03295   return NS_OK;
03296 }
03297 
03298 nsresult
03299 nsHTMLDocument::RegisterNamedItems(nsIContent *aContent)
03300 {
03301   if (aContent->IsContentOfType(nsIContent::eTEXT)) {
03302     // Text nodes are not named items nor can they have children.
03303     return NS_OK;
03304   }
03305 
03306   nsAutoString value;
03307 
03308   if (!IsXHTML() && IsNamedItem(aContent, aContent->Tag(), value)) {
03309     UpdateNameTableEntry(value, aContent);
03310   }
03311 
03312   nsIAtom* idAttr = aContent->GetIDAttributeName();
03313   if (idAttr) {
03314     aContent->GetAttr(kNameSpaceID_None, idAttr, value);
03315     if (!value.IsEmpty()) {
03316       nsresult rv = UpdateIdTableEntry(value, aContent);
03317       
03318       if (NS_FAILED(rv)) {
03319         return rv;
03320       }
03321     }
03322   }
03323 
03324   PRUint32 i, count = aContent->GetChildCount();
03325 
03326   for (i = 0; i < count; ++i) {
03327     RegisterNamedItems(aContent->GetChildAt(i));
03328   }
03329 
03330   return NS_OK;
03331 }
03332 
03333 static void
03334 FindNamedItems(const nsAString& aName, nsIContent *aContent,
03335                IdAndNameMapEntry& aEntry, PRBool aIsXHTML)
03336 {
03337   NS_ASSERTION(aEntry.mContentList,
03338                "Entry w/o content list passed to FindNamedItems()!");
03339   NS_ASSERTION(aEntry.mContentList != NAME_NOT_VALID,
03340                "Entry that should never have a list passed to FindNamedItems()!");
03341 
03342   if (aContent->IsContentOfType(nsIContent::eTEXT)) {
03343     // Text nodes are not named items nor can they have children.
03344     return;
03345   }
03346 
03347   nsAutoString value;
03348 
03349   if (!aIsXHTML && IsNamedItem(aContent, aContent->Tag(), value) &&
03350       value.Equals(aName)) {
03351     aEntry.mContentList->AppendElement(aContent);
03352   }
03353 
03354   if (!aEntry.mIdContent) {
03355     nsIAtom* idAttr = aContent->GetIDAttributeName();
03356     if (idAttr) {
03357       aContent->GetAttr(kNameSpaceID_None, idAttr, value);
03358 
03359       if (value.Equals(aName)) {
03360         aEntry.mIdContent = aContent;
03361       }
03362     }
03363   }
03364 
03365   PRUint32 i, count = aContent->GetChildCount();
03366 
03367   for (i = 0; i < count; ++i) {
03368     FindNamedItems(aName, aContent->GetChildAt(i), aEntry, aIsXHTML);
03369   }
03370 }
03371 
03372 nsresult
03373 nsHTMLDocument::ResolveName(const nsAString& aName,
03374                             nsIDOMHTMLFormElement *aForm,
03375                             nsISupports **aResult)
03376 {
03377   *aResult = nsnull;
03378 
03379   if (IsXHTML() || mIsGoingAway) {
03380     // We don't dynamically resolve names on XHTML documents.
03381     // We also don't want to add a new cache item to a destroyed document
03382 
03383     return NS_OK;
03384   }
03385 
03386   // We have built a table and cache the named items. The table will
03387   // be updated as content is added and removed.
03388 
03389   IdAndNameMapEntry *entry =
03390     NS_STATIC_CAST(IdAndNameMapEntry *,
03391                    PL_DHashTableOperate(&mIdAndNameHashTable, &aName,
03392                                         PL_DHASH_ADD));
03393   NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
03394 
03395   if (entry->mContentList == NAME_NOT_VALID) {
03396     // There won't be any named items by this name -- it's reserved
03397     return NS_OK;
03398   }
03399 
03400   // Now we know we _might_ have items.  Before looking at
03401   // entry->mContentList, make sure to flush out content (see
03402   // bug 69826).
03403   // This is a perf killer while the document is loading!
03404 
03405   // Make sure to stash away the current generation so we can check whether the
03406   // table changes when we flush.
03407   PRUint32 generation = mIdAndNameHashTable.generation;
03408   
03409   // If we already have an entry->mContentList, we need to flush out
03410   // notifications too, so that it will get updated properly.
03411   FlushPendingNotifications(entry->mContentList ?
03412                               Flush_ContentAndNotify : Flush_Content);
03413 
03414   if (generation != mIdAndNameHashTable.generation) {
03415     // Table changed, so the entry pointer is no longer valid; look up the
03416     // entry again, adding if necessary (the adding may be necessary in case
03417     // the flush actually deleted entries).
03418     entry =
03419       NS_STATIC_CAST(IdAndNameMapEntry *,
03420                      PL_DHashTableOperate(&mIdAndNameHashTable, &aName,
03421                                           PL_DHASH_ADD));
03422     NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
03423   }
03424     
03425 
03426   nsBaseContentList *list = entry->mContentList;
03427 
03428   if (!list) {
03429 #ifdef DEBUG_jst
03430     {
03431       printf ("nsHTMLDocument name cache miss for name '%s'\n",
03432               NS_ConvertUCS2toUTF8(aName).get());
03433     }
03434 #endif
03435 
03436     list = new nsBaseContentList();
03437     NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
03438 
03439     entry->mContentList = list;
03440     NS_ADDREF(entry->mContentList);
03441 
03442     if (mRootContent && !aName.IsEmpty()) {
03443       // We'll never get here if !IsXHTML(), so we can just pass
03444       // PR_FALSE to FindNamedItems().
03445       FindNamedItems(aName, mRootContent, *entry, PR_FALSE);
03446     }
03447   }
03448 
03449   PRUint32 length;
03450   list->GetLength(&length);
03451 
03452   if (length > 0) {
03453     if (length == 1) {
03454       // Only one element in the list, return the element instead of
03455       // returning the list
03456 
03457       nsCOMPtr<nsIDOMNode> node;
03458 
03459       list->Item(0, getter_AddRefs(node));
03460 
03461       nsCOMPtr<nsIContent> ourContent(do_QueryInterface(node));
03462       if (aForm && ourContent &&
03463           !nsContentUtils::BelongsInForm(aForm, ourContent)) {
03464         // This is not the content you are looking for
03465         node = nsnull;
03466       }
03467 
03468       *aResult = node;
03469       NS_IF_ADDREF(*aResult);
03470 
03471       return NS_OK;
03472     }
03473 
03474     // The list contains more than one element, return the whole
03475     // list, unless...
03476 
03477     if (aForm) {
03478       // ... we're called from a form, in that case we create a
03479       // nsFormContentList which will filter out the elements in the
03480       // list that don't belong to aForm
03481 
03482       nsFormContentList *fc_list = new nsFormContentList(aForm, *list);
03483       NS_ENSURE_TRUE(fc_list, NS_ERROR_OUT_OF_MEMORY);
03484 
03485       PRUint32 len;
03486       fc_list->GetLength(&len);
03487 
03488       if (len < 2) {
03489         // After the nsFormContentList is done filtering there's either
03490         // nothing or one element in the list.  Return that element, or null
03491         // if there's no element in the list.
03492 
03493         nsCOMPtr<nsIDOMNode> node;
03494 
03495         fc_list->Item(0, getter_AddRefs(node));
03496 
03497         NS_IF_ADDREF(*aResult = node);
03498 
03499         delete fc_list;
03500 
03501         return NS_OK;
03502       }
03503 
03504       list = fc_list;
03505     }
03506 
03507     return CallQueryInterface(list, aResult);
03508   }
03509 
03510   // No named items were found, see if there's one registerd by id for
03511   // aName. If we get this far, FindNamedItems() will have been called
03512   // for aName, so we're guaranteed that if there is an element with
03513   // the id aName, it'll be in entry->mIdContent.
03514 
03515   nsIContent *e = entry->mIdContent;
03516 
03517   if (e && e != ID_NOT_IN_DOCUMENT && e->IsContentOfType(nsIContent::eHTML)) {
03518     nsIAtom *tag = e->Tag();
03519 
03520     if ((tag == nsHTMLAtoms::embed  ||
03521          tag == nsHTMLAtoms::img    ||
03522          tag == nsHTMLAtoms::object ||
03523          tag == nsHTMLAtoms::applet) &&
03524         (!aForm || nsContentUtils::BelongsInForm(aForm, e))) {
03525       NS_ADDREF(*aResult = e);
03526     }
03527   }
03528 
03529   return NS_OK;
03530 }
03531 
03532 //----------------------------
03533 
03534 PRBool
03535 nsHTMLDocument::GetBodyContent()
03536 {
03537   if (!mRootContent) {
03538     return PR_FALSE;
03539   }
03540 
03541   PRUint32 i, child_count = mRootContent->GetChildCount();
03542 
03543   for (i = 0; i < child_count; ++i) {
03544     nsIContent *child = mRootContent->GetChildAt(i);
03545     NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
03546 
03547     if (child->IsContentOfType(nsIContent::eHTML) &&
03548         child->GetNodeInfo()->Equals(nsHTMLAtoms::body,
03549                                      mDefaultNamespaceID)) {
03550       mBodyContent = do_QueryInterface(child);
03551 
03552       return PR_TRUE;
03553     }
03554   }
03555 
03556   return PR_FALSE;
03557 }
03558 
03559 void
03560 nsHTMLDocument::GetBodyElement(nsIDOMHTMLBodyElement** aBody)
03561 {
03562   *aBody = nsnull;
03563 
03564   if (!mBodyContent && !GetBodyContent()) {
03565     // No body in this document.
03566 
03567     return;
03568   }
03569 
03570   CallQueryInterface(mBodyContent, aBody);
03571 }
03572 
03573 // forms related stuff
03574 
03575 NS_IMETHODIMP
03576 nsHTMLDocument::GetForms(nsIDOMHTMLCollection** aForms)
03577 {
03578   nsContentList *forms = nsHTMLDocument::GetForms();
03579   if (!forms)
03580     return NS_ERROR_OUT_OF_MEMORY;
03581 
03582   NS_ADDREF(*aForms = forms);
03583   return NS_OK;
03584 }
03585 
03586 nsContentList*
03587 nsHTMLDocument::GetForms()
03588 {
03589   if (!mForms)
03590     mForms = new nsContentList(this, nsHTMLAtoms::form, mDefaultNamespaceID);
03591 
03592   return mForms;
03593 }
03594 
03595 
03596 nsresult
03597 nsHTMLDocument::CreateAndAddWyciwygChannel(void)
03598 {
03599   nsresult rv = NS_OK;
03600   nsCAutoString url, originalSpec;
03601 
03602   mDocumentURI->GetSpec(originalSpec);
03603 
03604   // Generate the wyciwyg url
03605   url = NS_LITERAL_CSTRING("wyciwyg://")
03606       + nsPrintfCString("%d", gWyciwygSessionCnt++)
03607       + NS_LITERAL_CSTRING("/")
03608       + originalSpec;
03609 
03610   nsCOMPtr<nsIURI> wcwgURI;
03611   NS_NewURI(getter_AddRefs(wcwgURI), url);
03612 
03613   // Create the nsIWyciwygChannel to store out-of-band
03614   // document.write() script to cache
03615   nsCOMPtr<nsIChannel> channel;
03616   // Create a wyciwyg Channel
03617   rv = NS_NewChannel(getter_AddRefs(channel), wcwgURI);
03618   NS_ENSURE_SUCCESS(rv, rv);
03619 
03620   mWyciwygChannel = do_QueryInterface(channel);
03621 
03622   mWyciwygChannel->SetSecurityInfo(mSecurityInfo);
03623 
03624   // Inherit load flags from the original document's channel
03625   channel->SetLoadFlags(mLoadFlags);
03626 
03627   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
03628 
03629   // Use the Parent document's loadgroup to trigger load notifications
03630   if (loadGroup && channel) {
03631     rv = channel->SetLoadGroup(loadGroup);
03632     NS_ENSURE_SUCCESS(rv, rv);
03633 
03634     nsLoadFlags loadFlags = 0;
03635     channel->GetLoadFlags(&loadFlags);
03636     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
03637     channel->SetLoadFlags(loadFlags);
03638 
03639     channel->SetOriginalURI(wcwgURI);
03640 
03641     rv = loadGroup->AddRequest(mWyciwygChannel, nsnull);
03642     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to add request to load group.");
03643   }
03644 
03645   return rv;
03646 }
03647 
03648 nsresult
03649 nsHTMLDocument::RemoveWyciwygChannel(void)
03650 {
03651   nsresult rv = NS_OK;
03652 
03653   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
03654 
03655   // note there can be a write request without a load group if
03656   // this is a synchronously constructed about:blank document
03657   if (loadGroup && mWyciwygChannel) {
03658     mWyciwygChannel->CloseCacheEntry(NS_OK);
03659     rv = loadGroup->RemoveRequest(mWyciwygChannel, nsnull, NS_OK);
03660     NS_ENSURE_SUCCESS(rv, rv);
03661   }
03662 
03663   mWyciwygChannel = nsnull;
03664 
03665   return rv;
03666 }
03667 
03668 void *
03669 nsHTMLDocument::GenerateParserKey(void)
03670 {
03671   // The script loader provides us with the currently executing script element,
03672   // which is guaranteed to be unique per script.
03673   nsCOMPtr<nsIScriptElement> key;
03674   mScriptLoader->GetCurrentScript(getter_AddRefs(key));
03675 
03676   return key;
03677 }
03678 
03679 /* attribute DOMString designMode; */
03680 NS_IMETHODIMP
03681 nsHTMLDocument::GetDesignMode(nsAString & aDesignMode)
03682 {
03683   if (mEditingIsOn) {
03684     aDesignMode.AssignLiteral("on");
03685   }
03686   else {
03687     aDesignMode.AssignLiteral("off");
03688   }
03689   return NS_OK;
03690 }
03691 
03692 NS_IMETHODIMP
03693 nsHTMLDocument::SetDesignMode(const nsAString & aDesignMode)
03694 {
03695   // get editing session
03696   if (!mScriptGlobalObject)
03697     return NS_ERROR_FAILURE;
03698 
03699   nsIDocShell *docshell = mScriptGlobalObject->GetDocShell();
03700   if (!docshell)
03701     return NS_ERROR_FAILURE;
03702 
03703   nsresult rv = NS_OK;
03704   if (!nsContentUtils::IsCallerTrustedForWrite()) {
03705     // test if the above works if document.domain is set for Midas document
03706     // (www.netscape.com --> netscape.com)
03707 
03708     nsIPrincipal *principal = GetPrincipal();
03709     if (!principal)
03710       return NS_ERROR_FAILURE;
03711     nsCOMPtr<nsIPrincipal> subject;
03712     nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
03713     rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
03714     NS_ENSURE_SUCCESS(rv, rv);
03715     if (subject) {
03716        rv = secMan->CheckSameOriginPrincipal(subject, principal);
03717        NS_ENSURE_SUCCESS(rv, rv);
03718     }
03719   }
03720 
03721   nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell);
03722   if (!editSession)
03723     return NS_ERROR_FAILURE;
03724 
03725   nsIDOMWindow *window = GetWindow();
03726   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
03727 
03728   if (aDesignMode.LowerCaseEqualsLiteral("on") && !mEditingIsOn) {
03729     rv = editSession->MakeWindowEditable(window, "html", PR_FALSE);
03730 
03731     if (NS_SUCCEEDED(rv)) {
03732       // now that we've successfully created the editor, we can
03733       // reset our flag
03734       mEditingIsOn = PR_TRUE;
03735 
03736       // Set the editor to not insert br's on return when in p
03737       // elements by default.
03738       PRBool unused;
03739       rv = ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), PR_FALSE,
03740                        NS_LITERAL_STRING("false"), &unused);
03741 
03742       if (NS_FAILED(rv)) {
03743         // Editor setup failed. Editing is is not on after all.
03744 
03745         editSession->TearDownEditorOnWindow(window);
03746 
03747         mEditingIsOn = PR_FALSE;
03748       } else {
03749         // Resync the editor's spellcheck state, since when the editor was
03750         // created it asked us whether designMode was on, and we told it no.
03751         // Note that reporting "yes" (by setting mEditingIsOn true before
03752         // calling MakeWindowEditable()) exposed several crash bugs (see bugs
03753         // 348497, 348981).
03754         nsCOMPtr<nsIEditor> editor;
03755         rv = editSession->GetEditorForWindow(window, getter_AddRefs(editor));
03756         nsCOMPtr<nsIEditor_MOZILLA_1_8_BRANCH> editor_1_8 =
03757           do_QueryInterface(editor);
03758         if (NS_SUCCEEDED(rv) && editor_1_8) {
03759           editor_1_8->SyncRealTimeSpell();
03760         }
03761       }
03762     }
03763   } else if (aDesignMode.LowerCaseEqualsLiteral("off") && mEditingIsOn) {
03764     // turn editing off
03765     rv = editSession->TearDownEditorOnWindow(window);
03766 
03767     if (NS_SUCCEEDED(rv)) {
03768       mEditingIsOn = PR_FALSE;
03769     }
03770   }
03771 
03772   return rv;
03773 }
03774 
03775 nsresult
03776 nsHTMLDocument::GetMidasCommandManager(nsICommandManager** aCmdMgr)
03777 {
03778   // initialize return value
03779   NS_ENSURE_ARG_POINTER(aCmdMgr);
03780 
03781   // check if we have it cached
03782   if (mMidasCommandManager) {
03783     NS_ADDREF(*aCmdMgr = mMidasCommandManager);
03784     return NS_OK;
03785   }
03786 
03787   *aCmdMgr = nsnull;
03788   if (!mScriptGlobalObject)
03789     return NS_ERROR_FAILURE;
03790 
03791   nsIDocShell *docshell = mScriptGlobalObject->GetDocShell();
03792   if (!docshell)
03793     return NS_ERROR_FAILURE;
03794 
03795   mMidasCommandManager = do_GetInterface(docshell);
03796   if (!mMidasCommandManager)
03797     return NS_ERROR_FAILURE;
03798 
03799   NS_ADDREF(*aCmdMgr = mMidasCommandManager);
03800 
03801   return NS_OK;
03802 }
03803 
03804 
03805 struct MidasCommand {
03806   const char*  incomingCommandString;
03807   const char*  internalCommandString;
03808   const char*  internalParamString;
03809   PRPackedBool useNewParam;
03810   PRPackedBool convertToBoolean;
03811 };
03812 
03813 static const struct MidasCommand gMidasCommandTable[] = {
03814   { "bold",          "cmd_bold",            "", PR_TRUE,  PR_FALSE },
03815   { "italic",        "cmd_italic",          "", PR_TRUE,  PR_FALSE },
03816   { "underline",     "cmd_underline",       "", PR_TRUE,  PR_FALSE },
03817   { "strikethrough", "cmd_strikethrough",   "", PR_TRUE,  PR_FALSE },
03818   { "subscript",     "cmd_subscript",       "", PR_TRUE,  PR_FALSE },
03819   { "superscript",   "cmd_superscript",     "", PR_TRUE,  PR_FALSE },
03820   { "cut",           "cmd_cut",             "", PR_TRUE,  PR_FALSE },
03821   { "copy",          "cmd_copy",            "", PR_TRUE,  PR_FALSE },
03822   { "paste",         "cmd_paste",           "", PR_TRUE,  PR_FALSE },
03823   { "delete",        "cmd_delete",          "", PR_TRUE,  PR_FALSE },
03824   { "selectall",     "cmd_selectAll",       "", PR_TRUE,  PR_FALSE },
03825   { "undo",          "cmd_undo",            "", PR_TRUE,  PR_FALSE },
03826   { "redo",          "cmd_redo",            "", PR_TRUE,  PR_FALSE },
03827   { "indent",        "cmd_indent",          "", PR_TRUE,  PR_FALSE },
03828   { "outdent",       "cmd_outdent",         "", PR_TRUE,  PR_FALSE },
03829   { "backcolor",     "cmd_backgroundColor", "", PR_FALSE, PR_FALSE },
03830   { "forecolor",     "cmd_fontColor",       "", PR_FALSE, PR_FALSE },
03831   { "hilitecolor",   "cmd_highlight",       "", PR_FALSE, PR_FALSE },
03832   { "fontname",      "cmd_fontFace",        "", PR_FALSE, PR_FALSE },
03833   { "fontsize",      "cmd_fontSize",        "", PR_FALSE, PR_FALSE },
03834   { "increasefontsize", "cmd_increaseFont", "", PR_FALSE, PR_FALSE },
03835   { "decreasefontsize", "cmd_decreaseFont", "", PR_FALSE, PR_FALSE },
03836   { "inserthorizontalrule", "cmd_insertHR", "", PR_TRUE,  PR_FALSE },
03837   { "createlink",    "cmd_insertLinkNoUI",  "", PR_FALSE, PR_FALSE },
03838   { "insertimage",   "cmd_insertImageNoUI", "", PR_FALSE, PR_FALSE },
03839   { "inserthtml",    "cmd_insertHTML",      "", PR_FALSE, PR_FALSE },
03840   { "gethtml",       "cmd_getContents",     "", PR_FALSE, PR_FALSE },
03841   { "justifyleft",   "cmd_align",       "left", PR_TRUE,  PR_FALSE },
03842   { "justifyright",  "cmd_align",      "right", PR_TRUE,  PR_FALSE },
03843   { "justifycenter", "cmd_align",     "center", PR_TRUE,  PR_FALSE },
03844   { "justifyfull",   "cmd_align",    "justify", PR_TRUE,  PR_FALSE },
03845   { "removeformat",  "cmd_removeStyles",    "", PR_TRUE,  PR_FALSE },
03846   { "unlink",        "cmd_removeLinks",     "", PR_TRUE,  PR_FALSE },
03847   { "insertorderedlist",   "cmd_ol",        "", PR_TRUE,  PR_FALSE },
03848   { "insertunorderedlist", "cmd_ul",        "", PR_TRUE,  PR_FALSE },
03849   { "insertparagraph", "cmd_paragraphState", "p", PR_TRUE, PR_FALSE },
03850   { "formatblock",   "cmd_paragraphState",  "", PR_FALSE, PR_FALSE },
03851   { "heading",       "cmd_paragraphState",  "", PR_FALSE, PR_FALSE },
03852   { "styleWithCSS",  "cmd_setDocumentUseCSS", "", PR_FALSE, PR_TRUE },
03853   { "contentReadOnly", "cmd_setDocumentReadOnly", "", PR_FALSE, PR_TRUE },
03854   { "insertBrOnReturn", "cmd_insertBrOnReturn", "", PR_FALSE, PR_TRUE },
03855   { "enableObjectResizing", "cmd_enableObjectResizing", "", PR_FALSE, PR_TRUE },
03856   { "enableInlineTableEditing", "cmd_enableInlineTableEditing", "", PR_FALSE, PR_TRUE },
03857 #if 0
03858 // no editor support to remove alignments right now
03859   { "justifynone",   "cmd_align",           "", PR_TRUE,  PR_FALSE },
03860 
03861 // the following will need special review before being turned on
03862   { "saveas",        "cmd_saveAs",          "", PR_TRUE,  PR_FALSE },
03863   { "print",         "cmd_print",           "", PR_TRUE,  PR_FALSE },
03864 #endif
03865   { NULL, NULL, NULL, PR_FALSE, PR_FALSE }
03866 };
03867 
03868 #define MidasCommandCount ((sizeof(gMidasCommandTable) / sizeof(struct MidasCommand)) - 1)
03869 
03870 struct MidasParam {
03871   const char*  incomingParamString;
03872   const char*  internalParamString;
03873 };
03874 
03875 static const struct MidasParam gMidasParamTable[] = {
03876   { "<P>",                "P" },
03877   { "<H1>",               "H1" },
03878   { "<H2>",               "H2" },
03879   { "<H3>",               "H3" },
03880   { "<H4>",               "H4" },
03881   { "<H5>",               "H5" },
03882   { "<H6>",               "H6" },
03883   { "<PRE>",              "PRE" },
03884   { "<ADDRESS>",          "ADDRESS" },
03885   { NULL, NULL }
03886 };
03887 
03888 #define MidasParamCount ((sizeof(gMidasParamTable) / sizeof(struct MidasParam)) - 1)
03889 
03890 // this function will return false if the command is not recognized
03891 // inCommandID will be converted as necessary for internal operations
03892 // inParam will be converted as necessary for internal operations
03893 // outParam will be Empty if no parameter is needed or if returning a boolean
03894 // outIsBoolean will determine whether to send param as a boolean or string
03895 // outBooleanParam will not be set unless outIsBoolean
03896 PRBool
03897 nsHTMLDocument::ConvertToMidasInternalCommand(const nsAString & inCommandID,
03898                                               const nsAString & inParam,
03899                                               nsACString& outCommandID,
03900                                               nsACString& outParam,
03901                                               PRBool& outIsBoolean,
03902                                               PRBool& outBooleanValue)
03903 {
03904   NS_ConvertUTF16toUTF8 convertedCommandID(inCommandID);
03905 
03906   // Hack to support old boolean commands that were backwards (see bug 301490).
03907   PRBool invertBool = PR_FALSE;
03908   if (convertedCommandID.LowerCaseEqualsLiteral("usecss")) {
03909     convertedCommandID.Assign("styleWithCSS");
03910     invertBool = PR_TRUE;
03911   }
03912   else if (convertedCommandID.LowerCaseEqualsLiteral("readonly")) {
03913     convertedCommandID.Assign("contentReadOnly");
03914     invertBool = PR_TRUE;
03915   }
03916 
03917   PRUint32 i;
03918   PRBool found = PR_FALSE;
03919   for (i = 0; i < MidasCommandCount; ++i) {
03920     if (convertedCommandID.Equals(gMidasCommandTable[i].incomingCommandString,
03921                                   nsCaseInsensitiveCStringComparator())) {
03922       found = PR_TRUE;
03923       break;
03924     }
03925   }
03926 
03927   if (found) {
03928     // set outCommandID (what we use internally)
03929     outCommandID.Assign(gMidasCommandTable[i].internalCommandString);
03930 
03931     // set outParam & outIsBoolean based on flags from the table
03932     outIsBoolean = gMidasCommandTable[i].convertToBoolean;
03933 
03934     if (gMidasCommandTable[i].useNewParam) {
03935       outParam.Assign(gMidasCommandTable[i].internalParamString);
03936     }
03937     else {
03938       // handle checking of param passed in
03939       if (outIsBoolean) {
03940         // if this is a boolean value and it's not explicitly false
03941         // (e.g. no value) we default to "true". For old backwards commands
03942         // we invert the check (see bug 301490).
03943         if (invertBool) {
03944           outBooleanValue = inParam.LowerCaseEqualsLiteral("false");
03945         }
03946         else {
03947           outBooleanValue = !inParam.LowerCaseEqualsLiteral("false");
03948         }
03949         outParam.Truncate();
03950       }
03951       else {
03952         NS_ConvertUCS2toUTF8 convertedParam(inParam);
03953 
03954         // check to see if we need to convert the parameter
03955         PRUint32 j;
03956         for (j = 0; j < MidasParamCount; ++j) {
03957           if (convertedParam.Equals(gMidasParamTable[j].incomingParamString,
03958                                     nsCaseInsensitiveCStringComparator())) {
03959             outParam.Assign(gMidasParamTable[j].internalParamString);
03960             break;
03961           }
03962         }
03963 
03964         // if we didn't convert the parameter, just
03965         // pass through the parameter that was passed to us
03966         if (j == MidasParamCount)
03967           outParam.Assign(convertedParam);
03968       }
03969     }
03970   } // end else for useNewParam (do convert existing param)
03971   else {
03972     // reset results if the command is not found in our table
03973     outCommandID.SetLength(0);
03974     outParam.SetLength(0);
03975     outIsBoolean = PR_FALSE;
03976   }
03977 
03978   return found;
03979 }
03980 
03981 jsval
03982 nsHTMLDocument::sCutCopyInternal_id = JSVAL_VOID;
03983 jsval
03984 nsHTMLDocument::sPasteInternal_id = JSVAL_VOID;
03985 
03986 /* Helper function to check security of clipboard commands. If aPaste is */
03987 /* true, we check paste, else we check cutcopy */
03988 nsresult
03989 nsHTMLDocument::DoClipboardSecurityCheck(PRBool aPaste)
03990 {
03991   nsresult rv = NS_ERROR_FAILURE;
03992 
03993   nsCOMPtr<nsIJSContextStack> stack =
03994     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
03995 
03996   if (stack) {
03997     JSContext *cx = nsnull;
03998     stack->Peek(&cx);
03999 
04000     NS_NAMED_LITERAL_CSTRING(classNameStr, "Clipboard");
04001 
04002     nsIScriptSecurityManager *secMan =
04003       nsContentUtils::GetSecurityManager();
04004 
04005     if (aPaste) {
04006       if (nsHTMLDocument::sPasteInternal_id == JSVAL_VOID) {
04007         nsHTMLDocument::sPasteInternal_id =
04008           STRING_TO_JSVAL(::JS_InternString(cx, "paste"));
04009       }
04010       rv = secMan->CheckPropertyAccess(cx, nsnull, classNameStr.get(),
04011                                        nsHTMLDocument::sPasteInternal_id,
04012                                        nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
04013     } else {
04014       if (nsHTMLDocument::sCutCopyInternal_id == JSVAL_VOID) {
04015         nsHTMLDocument::sCutCopyInternal_id =
04016           STRING_TO_JSVAL(::JS_InternString(cx, "cutcopy"));
04017       }
04018       rv = secMan->CheckPropertyAccess(cx, nsnull, classNameStr.get(),
04019                                        nsHTMLDocument::sCutCopyInternal_id,
04020                                        nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
04021     }
04022   }
04023   return rv;
04024 }
04025 
04026 /* TODO: don't let this call do anything if the page is not done loading */
04027 /* boolean execCommand(in DOMString commandID, in boolean doShowUI,
04028                                                in DOMString value); */
04029 NS_IMETHODIMP
04030 nsHTMLDocument::ExecCommand(const nsAString & commandID,
04031                             PRBool doShowUI,
04032                             const nsAString & value,
04033                             PRBool *_retval)
04034 {
04035   NS_ENSURE_ARG_POINTER(_retval);
04036 
04037   //  for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
04038   //  this might add some ugly JS dependencies?
04039 
04040   *_retval = PR_FALSE;
04041 
04042   // if editing is not on, bail
04043   if (!mEditingIsOn)
04044     return NS_ERROR_FAILURE;
04045 
04046   // if they are requesting UI from us, let's fail since we have no UI
04047   if (doShowUI)
04048     return NS_ERROR_NOT_IMPLEMENTED;
04049 
04050   nsresult rv = NS_OK;
04051 
04052   if (commandID.LowerCaseEqualsLiteral("gethtml"))
04053     return NS_ERROR_FAILURE;
04054 
04055   if (commandID.LowerCaseEqualsLiteral("cut") ||
04056       (commandID.LowerCaseEqualsLiteral("copy"))) {
04057     rv = DoClipboardSecurityCheck(PR_FALSE);
04058   } else if (commandID.LowerCaseEqualsLiteral("paste")) {
04059     rv = DoClipboardSecurityCheck(PR_TRUE);
04060   }
04061 
04062   if (NS_FAILED(rv))
04063     return rv;
04064 
04065   // get command manager and dispatch command to our window if it's acceptable
04066   nsCOMPtr<nsICommandManager> cmdMgr;
04067   GetMidasCommandManager(getter_AddRefs(cmdMgr));
04068   if (!cmdMgr)
04069     return NS_ERROR_FAILURE;
04070 
04071   nsIDOMWindow *window = GetWindow();
04072   if (!window)
04073     return NS_ERROR_FAILURE;
04074 
04075   nsCAutoString cmdToDispatch, paramStr;
04076   PRBool isBool, boolVal;
04077   if (!ConvertToMidasInternalCommand(commandID, value,
04078                                      cmdToDispatch, paramStr, isBool, boolVal))
04079     return NS_ERROR_NOT_IMPLEMENTED;
04080 
04081   if (!isBool && paramStr.IsEmpty()) {
04082     rv = cmdMgr->DoCommand(cmdToDispatch.get(), nsnull, window);
04083   } else {
04084     // we have a command that requires a parameter, create params
04085     nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
04086                                             NS_COMMAND_PARAMS_CONTRACTID, &rv);
04087     if (!cmdParams)
04088       return NS_ERROR_OUT_OF_MEMORY;
04089 
04090     if (isBool)
04091       rv = cmdParams->SetBooleanValue("state_attribute", boolVal);
04092     else if (cmdToDispatch.Equals("cmd_fontFace"))
04093       rv = cmdParams->SetStringValue("state_attribute", value);
04094     else if (cmdToDispatch.Equals("cmd_insertHTML"))
04095       rv = cmdParams->SetStringValue("state_data", value);
04096     else
04097       rv = cmdParams->SetCStringValue("state_attribute", paramStr.get());
04098     if (NS_FAILED(rv))
04099       return rv;
04100     rv = cmdMgr->DoCommand(cmdToDispatch.get(), cmdParams, window);
04101   }
04102 
04103   *_retval = NS_SUCCEEDED(rv);
04104 
04105   return rv;
04106 }
04107 
04108 /* TODO: don't let this call do anything if the page is not done loading */
04109 /* boolean execCommandShowHelp(in DOMString commandID); */
04110 NS_IMETHODIMP
04111 nsHTMLDocument::ExecCommandShowHelp(const nsAString & commandID,
04112                                     PRBool *_retval)
04113 {
04114   NS_ENSURE_ARG_POINTER(_retval);
04115   *_retval = PR_FALSE;
04116 
04117   // if editing is not on, bail
04118   if (!mEditingIsOn)
04119     return NS_ERROR_FAILURE;
04120 
04121   return NS_ERROR_NOT_IMPLEMENTED;
04122 }
04123 
04124 /* boolean queryCommandEnabled(in DOMString commandID); */
04125 NS_IMETHODIMP
04126 nsHTMLDocument::QueryCommandEnabled(const nsAString & commandID,
04127                                     PRBool *_retval)
04128 {
04129   NS_ENSURE_ARG_POINTER(_retval);
04130   *_retval = PR_FALSE;
04131 
04132   // if editing is not on, bail
04133   if (!mEditingIsOn)
04134     return NS_ERROR_FAILURE;
04135 
04136   // get command manager and dispatch command to our window if it's acceptable
04137   nsCOMPtr<nsICommandManager> cmdMgr;
04138   GetMidasCommandManager(getter_AddRefs(cmdMgr));
04139   if (!cmdMgr)
04140     return NS_ERROR_FAILURE;
04141 
04142   nsIDOMWindow *window = GetWindow();
04143   if (!window)
04144     return NS_ERROR_FAILURE;
04145 
04146   nsCAutoString cmdToDispatch, paramStr;
04147   PRBool isBool, boolVal;
04148   if (!ConvertToMidasInternalCommand(commandID, commandID,
04149                                      cmdToDispatch, paramStr, isBool, boolVal))
04150     return NS_ERROR_NOT_IMPLEMENTED;
04151 
04152   return cmdMgr->IsCommandEnabled(cmdToDispatch.get(), window, _retval);
04153 }
04154 
04155 /* boolean queryCommandIndeterm (in DOMString commandID); */
04156 NS_IMETHODIMP
04157 nsHTMLDocument::QueryCommandIndeterm(const nsAString & commandID,
04158                                      PRBool *_retval)
04159 {
04160   NS_ENSURE_ARG_POINTER(_retval);
04161   *_retval = PR_FALSE;
04162 
04163   // if editing is not on, bail
04164   if (!mEditingIsOn)
04165     return NS_ERROR_FAILURE;
04166 
04167   // get command manager and dispatch command to our window if it's acceptable
04168   nsCOMPtr<nsICommandManager> cmdMgr;
04169   GetMidasCommandManager(getter_AddRefs(cmdMgr));
04170   if (!cmdMgr)
04171     return NS_ERROR_FAILURE;
04172 
04173   nsIDOMWindow *window = GetWindow();
04174   if (!window)
04175     return NS_ERROR_FAILURE;
04176 
04177   nsCAutoString cmdToDispatch, paramToCheck;
04178   PRBool dummy;
04179   if (!ConvertToMidasInternalCommand(commandID, commandID,
04180                                      cmdToDispatch, paramToCheck, dummy, dummy))
04181     return NS_ERROR_NOT_IMPLEMENTED;
04182 
04183   nsresult rv;
04184   nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
04185                                            NS_COMMAND_PARAMS_CONTRACTID, &rv);
04186   NS_ENSURE_SUCCESS(rv, rv);
04187 
04188   rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, cmdParams);
04189   if (NS_FAILED(rv))
04190     return rv;
04191 
04192   // if command does not have a state_mixed value, this call fails, so we fail too,
04193   // which is what is expected
04194   rv = cmdParams->GetBooleanValue("state_mixed", _retval);
04195   return rv;
04196 }
04197 
04198 /* boolean queryCommandState(in DOMString commandID); */
04199 NS_IMETHODIMP
04200 nsHTMLDocument::QueryCommandState(const nsAString & commandID, PRBool *_retval)
04201 {
04202   NS_ENSURE_ARG_POINTER(_retval);
04203   *_retval = PR_FALSE;
04204 
04205   // if editing is not on, bail
04206   if (!mEditingIsOn)
04207     return NS_ERROR_FAILURE;
04208 
04209   // get command manager and dispatch command to our window if it's acceptable
04210   nsCOMPtr<nsICommandManager> cmdMgr;
04211   GetMidasCommandManager(getter_AddRefs(cmdMgr));
04212   if (!cmdMgr)
04213     return NS_ERROR_FAILURE;
04214 
04215   nsIDOMWindow *window = GetWindow();
04216   if (!window)
04217     return NS_ERROR_FAILURE;
04218 
04219   nsCAutoString cmdToDispatch, paramToCheck;
04220   PRBool dummy, dummy2;
04221   if (!ConvertToMidasInternalCommand(commandID, commandID,
04222                                      cmdToDispatch, paramToCheck, dummy, dummy2))
04223     return NS_ERROR_NOT_IMPLEMENTED;
04224 
04225   nsresult rv;
04226   nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
04227                                            NS_COMMAND_PARAMS_CONTRACTID, &rv);
04228   if (!cmdParams)
04229     return NS_ERROR_OUT_OF_MEMORY;
04230 
04231   rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, cmdParams);
04232   if (NS_FAILED(rv))
04233     return rv;
04234 
04235   // handle alignment as a special case (possibly other commands too?)
04236   // Alignment is special because the external api is individual
04237   // commands but internally we use cmd_align with different
04238   // parameters.  When getting the state of this command, we need to
04239   // return the boolean for this particular alignment rather than the
04240   // string of 'which alignment is this?'
04241   if (cmdToDispatch.Equals("cmd_align")) {
04242     char * actualAlignmentType = nsnull;
04243     rv = cmdParams->GetCStringValue("state_attribute", &actualAlignmentType);
04244     if (NS_SUCCEEDED(rv) && actualAlignmentType && actualAlignmentType[0]) {
04245       *_retval = paramToCheck.Equals(actualAlignmentType);
04246     }
04247     if (actualAlignmentType)
04248       nsMemory::Free(actualAlignmentType);
04249   }
04250   else {
04251     rv = cmdParams->GetBooleanValue("state_all", _retval);
04252     if (NS_FAILED(rv))
04253       *_retval = PR_FALSE;
04254   }
04255 
04256   return rv;
04257 }
04258 
04259 /* boolean queryCommandSupported(in DOMString commandID); */
04260 NS_IMETHODIMP
04261 nsHTMLDocument::QueryCommandSupported(const nsAString & commandID,
04262                                       PRBool *_retval)
04263 {
04264   NS_ENSURE_ARG_POINTER(_retval);
04265   *_retval = PR_FALSE;
04266 
04267   // if editing is not on, bail
04268   if (!mEditingIsOn)
04269     return NS_ERROR_FAILURE;
04270 
04271   return NS_ERROR_NOT_IMPLEMENTED;
04272 }
04273 
04274 /* DOMString queryCommandText(in DOMString commandID); */
04275 NS_IMETHODIMP
04276 nsHTMLDocument::QueryCommandText(const nsAString & commandID,
04277                                  nsAString & _retval)
04278 {
04279   _retval.SetLength(0);
04280 
04281   // if editing is not on, bail
04282   if (!mEditingIsOn)
04283     return NS_ERROR_FAILURE;
04284 
04285   return NS_ERROR_NOT_IMPLEMENTED;
04286 }
04287 
04288 /* DOMString queryCommandValue(in DOMString commandID); */
04289 NS_IMETHODIMP
04290 nsHTMLDocument::QueryCommandValue(const nsAString & commandID,
04291                                   nsAString &_retval)
04292 {
04293   _retval.SetLength(0);
04294 
04295   // if editing is not on, bail
04296   if (!mEditingIsOn)
04297     return NS_ERROR_FAILURE;
04298 
04299   // get command manager and dispatch command to our window if it's acceptable
04300   nsCOMPtr<nsICommandManager> cmdMgr;
04301   GetMidasCommandManager(getter_AddRefs(cmdMgr));
04302   if (!cmdMgr)
04303     return NS_ERROR_FAILURE;
04304 
04305   nsIDOMWindow *window = GetWindow();
04306   if (!window)
04307     return NS_ERROR_FAILURE;
04308 
04309   nsCAutoString cmdToDispatch, paramStr;
04310   PRBool isBool, boolVal;
04311   if (!ConvertToMidasInternalCommand(commandID, commandID,
04312                                      cmdToDispatch, paramStr, isBool, boolVal))
04313     return NS_ERROR_NOT_IMPLEMENTED;
04314 
04315   // create params
04316   nsresult rv;
04317   nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
04318                                            NS_COMMAND_PARAMS_CONTRACTID, &rv);
04319   if (!cmdParams)
04320     return NS_ERROR_OUT_OF_MEMORY;
04321 
04322   // this is a special command since we are calling "DoCommand rather than
04323   // GetCommandState like the other commands
04324   if (cmdToDispatch.Equals("cmd_getContents"))
04325   {
04326     rv = cmdParams->SetBooleanValue("selection_only", PR_TRUE);
04327     if (NS_FAILED(rv)) return rv;
04328     rv = cmdParams->SetCStringValue("format", "text/html");
04329     if (NS_FAILED(rv)) return rv;
04330     rv = cmdMgr->DoCommand(cmdToDispatch.get(), cmdParams, window);
04331     if (NS_FAILED(rv)) return rv;
04332     return cmdParams->GetStringValue("result", _retval);
04333   }
04334 
04335   rv = cmdParams->SetCStringValue("state_attribute", paramStr.get());
04336   if (NS_FAILED(rv))
04337     return rv;
04338 
04339   rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, cmdParams);
04340   if (NS_FAILED(rv))
04341     return rv;
04342 
04343   nsXPIDLCString cStringResult;
04344   rv = cmdParams->GetCStringValue("state_attribute",
04345                                   getter_Copies(cStringResult));
04346   CopyUTF8toUTF16(cStringResult, _retval);
04347 
04348   return rv;
04349 }
04350 
04351 #ifdef DEBUG
04352 nsresult
04353 nsHTMLDocument::CreateElem(nsIAtom *aName, nsIAtom *aPrefix,
04354                            PRInt32 aNamespaceID, PRBool aDocumentDefaultType,
04355                            nsIContent** aResult)
04356 {
04357   NS_ASSERTION(!aDocumentDefaultType || IsXHTML() ||
04358                aNamespaceID == kNameSpaceID_None,
04359                "HTML elements in an HTML document should have "
04360                "kNamespaceID_None as their namespace ID.");
04361 
04362   if (IsXHTML() &&
04363       (aDocumentDefaultType || aNamespaceID == kNameSpaceID_XHTML)) {
04364     nsCAutoString name, lcName;
04365     aName->ToUTF8String(name);
04366     ToLowerCase(name, lcName);
04367     NS_ASSERTION(lcName.Equals(name),
04368                  "aName should be lowercase, fix caller.");
04369   }
04370 
04371   return nsDocument::CreateElem(aName, aPrefix, aNamespaceID,
04372                                 aDocumentDefaultType, aResult);
04373 }
04374 #endif