content/html/document/src/nsHTMLDocument.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set sw=2 ts=2 et tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsHTMLDocument.h"
     9 #include "mozilla/DebugOnly.h"
    10 #include "mozilla/dom/HTMLAllCollection.h"
    11 #include "nsCOMPtr.h"
    12 #include "nsGlobalWindow.h"
    13 #include "nsXPIDLString.h"
    14 #include "nsPrintfCString.h"
    15 #include "nsReadableUtils.h"
    16 #include "nsUnicharUtils.h"
    17 #include "nsIHTMLContentSink.h"
    18 #include "nsIXMLContentSink.h"
    19 #include "nsHTMLParts.h"
    20 #include "nsHTMLStyleSheet.h"
    21 #include "nsGkAtoms.h"
    22 #include "nsIPresShell.h"
    23 #include "nsPresContext.h"
    24 #include "nsIDOMNode.h" // for Find
    25 #include "nsIDOMNodeList.h"
    26 #include "nsIDOMElement.h"
    27 #include "nsPIDOMWindow.h"
    28 #include "nsDOMString.h"
    29 #include "nsIStreamListener.h"
    30 #include "nsIURI.h"
    31 #include "nsIIOService.h"
    32 #include "nsNetUtil.h"
    33 #include "nsIContentViewerContainer.h"
    34 #include "nsIContentViewer.h"
    35 #include "nsIMarkupDocumentViewer.h"
    36 #include "nsDocShell.h"
    37 #include "nsDocShellLoadTypes.h"
    38 #include "nsIWebNavigation.h"
    39 #include "nsIBaseWindow.h"
    40 #include "nsIWebShellServices.h"
    41 #include "nsIScriptContext.h"
    42 #include "nsIXPConnect.h"
    43 #include "nsContentList.h"
    44 #include "nsError.h"
    45 #include "nsIPrincipal.h"
    46 #include "nsJSPrincipals.h"
    47 #include "nsIScriptSecurityManager.h"
    48 #include "nsAttrName.h"
    49 #include "nsNodeUtils.h"
    51 #include "nsNetCID.h"
    52 #include "nsICookieService.h"
    54 #include "nsIServiceManager.h"
    55 #include "nsIConsoleService.h"
    56 #include "nsIComponentManager.h"
    57 #include "nsParserCIID.h"
    58 #include "nsIDOMHTMLElement.h"
    59 #include "nsIDOMHTMLHeadElement.h"
    60 #include "nsNameSpaceManager.h"
    61 #include "nsGenericHTMLElement.h"
    62 #include "mozilla/css/Loader.h"
    63 #include "nsIHttpChannel.h"
    64 #include "nsIFile.h"
    65 #include "nsFrameSelection.h"
    66 #include "nsISelectionPrivate.h"//for toStringwithformat code
    68 #include "nsContentUtils.h"
    69 #include "nsJSUtils.h"
    70 #include "nsIDocumentInlines.h"
    71 #include "nsIDocumentEncoder.h" //for outputting selection
    72 #include "nsICachingChannel.h"
    73 #include "nsIContentViewer.h"
    74 #include "nsIWyciwygChannel.h"
    75 #include "nsIScriptElement.h"
    76 #include "nsIScriptError.h"
    77 #include "nsIMutableArray.h"
    78 #include "nsArrayUtils.h"
    79 #include "nsIEffectiveTLDService.h"
    81 //AHMED 12-2
    82 #include "nsBidiUtils.h"
    84 #include "mozilla/dom/EncodingUtils.h"
    85 #include "mozilla/dom/FallbackEncoding.h"
    86 #include "nsIEditingSession.h"
    87 #include "nsIEditor.h"
    88 #include "nsNodeInfoManager.h"
    89 #include "nsIPlaintextEditor.h"
    90 #include "nsIHTMLEditor.h"
    91 #include "nsIEditorStyleSheets.h"
    92 #include "nsIInlineSpellChecker.h"
    93 #include "nsRange.h"
    94 #include "mozAutoDocUpdate.h"
    95 #include "nsCCUncollectableMarker.h"
    96 #include "nsHtml5Module.h"
    97 #include "prprf.h"
    98 #include "mozilla/dom/Element.h"
    99 #include "mozilla/Preferences.h"
   100 #include "nsMimeTypes.h"
   101 #include "nsIRequest.h"
   102 #include "nsHtml5TreeOpExecutor.h"
   103 #include "nsHtml5Parser.h"
   104 #include "nsIDOMJSWindow.h"
   105 #include "nsSandboxFlags.h"
   106 #include "nsIImageDocument.h"
   107 #include "mozilla/dom/HTMLBodyElement.h"
   108 #include "mozilla/dom/HTMLDocumentBinding.h"
   109 #include "nsCharsetSource.h"
   110 #include "nsIStringBundle.h"
   111 #include "nsDOMClassInfo.h"
   113 using namespace mozilla;
   114 using namespace mozilla::dom;
   116 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
   118 #include "prtime.h"
   120 //#define DEBUG_charset
   122 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
   124 uint32_t       nsHTMLDocument::gWyciwygSessionCnt = 0;
   126 // this function will return false if the command is not recognized
   127 // inCommandID will be converted as necessary for internal operations
   128 // inParam will be converted as necessary for internal operations
   129 // outParam will be Empty if no parameter is needed or if returning a boolean
   130 // outIsBoolean will determine whether to send param as a boolean or string
   131 // outBooleanParam will not be set unless outIsBoolean
   132 static bool ConvertToMidasInternalCommand(const nsAString & inCommandID,
   133                                             const nsAString & inParam,
   134                                             nsACString& outCommandID,
   135                                             nsACString& outParam,
   136                                             bool& isBoolean,
   137                                             bool& boolValue);
   139 static bool ConvertToMidasInternalCommand(const nsAString & inCommandID,
   140                                             nsACString& outCommandID);
   142 // ==================================================================
   143 // =
   144 // ==================================================================
   145 static nsresult
   146 RemoveFromAgentSheets(nsCOMArray<nsIStyleSheet> &aAgentSheets, const nsAString& url)
   147 {
   148   nsCOMPtr<nsIURI> uri;
   149   nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
   150   NS_ENSURE_SUCCESS(rv, rv);
   152   for (int32_t i = aAgentSheets.Count() - 1; i >= 0; --i) {
   153     nsIStyleSheet* sheet = aAgentSheets[i];
   154     nsIURI* sheetURI = sheet->GetSheetURI();
   156     bool equals = false;
   157     uri->Equals(sheetURI, &equals);
   158     if (equals) {
   159       aAgentSheets.RemoveObjectAt(i);
   160     }
   161   }
   163   return NS_OK;
   164 }
   166 nsresult
   167 NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData)
   168 {
   169   nsRefPtr<nsHTMLDocument> doc = new nsHTMLDocument();
   171   nsresult rv = doc->Init();
   173   if (NS_FAILED(rv)) {
   174     *aInstancePtrResult = nullptr;
   175     return rv;
   176   }
   178   doc->SetLoadedAsData(aLoadedAsData);
   179   doc.forget(aInstancePtrResult);
   181   return NS_OK;
   182 }
   184   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   185   // bother initializing members to 0.
   187 nsHTMLDocument::nsHTMLDocument()
   188   : nsDocument("text/html")
   189 {
   190   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   191   // bother initializing members to 0.
   193   mIsRegularHTML = true;
   194   mDefaultElementType = kNameSpaceID_XHTML;
   195   mCompatMode = eCompatibility_NavQuirks;
   196 }
   198 nsHTMLDocument::~nsHTMLDocument()
   199 {
   200 }
   202 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsHTMLDocument, nsDocument,
   203                                    mAll,
   204                                    mImages,
   205                                    mApplets,
   206                                    mEmbeds,
   207                                    mLinks,
   208                                    mAnchors,
   209                                    mScripts,
   210                                    mForms,
   211                                    mFormControls,
   212                                    mWyciwygChannel,
   213                                    mMidasCommandManager)
   215 NS_IMPL_ADDREF_INHERITED(nsHTMLDocument, nsDocument)
   216 NS_IMPL_RELEASE_INHERITED(nsHTMLDocument, nsDocument)
   218 // QueryInterface implementation for nsHTMLDocument
   219 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLDocument)
   220   NS_INTERFACE_TABLE_INHERITED(nsHTMLDocument, nsIHTMLDocument,
   221                                nsIDOMHTMLDocument)
   222 NS_INTERFACE_TABLE_TAIL_INHERITING(nsDocument)
   224 JSObject*
   225 nsHTMLDocument::WrapNode(JSContext* aCx)
   226 {
   227   return HTMLDocumentBinding::Wrap(aCx, this);
   228 }
   230 nsresult
   231 nsHTMLDocument::Init()
   232 {
   233   nsresult rv = nsDocument::Init();
   234   NS_ENSURE_SUCCESS(rv, rv);
   236   // Now reset the compatibility mode of the CSSLoader
   237   // to match our compat mode.
   238   CSSLoader()->SetCompatibilityMode(mCompatMode);
   240   return NS_OK;
   241 }
   244 void
   245 nsHTMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
   246 {
   247   nsDocument::Reset(aChannel, aLoadGroup);
   249   if (aChannel) {
   250     aChannel->GetLoadFlags(&mLoadFlags);
   251   }
   252 }
   254 void
   255 nsHTMLDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
   256                            nsIPrincipal* aPrincipal)
   257 {
   258   mLoadFlags = nsIRequest::LOAD_NORMAL;
   260   nsDocument::ResetToURI(aURI, aLoadGroup, aPrincipal);
   262   mImages = nullptr;
   263   mApplets = nullptr;
   264   mEmbeds = nullptr;
   265   mLinks = nullptr;
   266   mAnchors = nullptr;
   267   mScripts = nullptr;
   269   mForms = nullptr;
   271   NS_ASSERTION(!mWyciwygChannel,
   272                "nsHTMLDocument::Reset() - Wyciwyg Channel  still exists!");
   274   mWyciwygChannel = nullptr;
   276   // Make the content type default to "text/html", we are a HTML
   277   // document, after all. Once we start getting data, this may be
   278   // changed.
   279   SetContentTypeInternal(nsDependentCString("text/html"));
   280 }
   282 already_AddRefed<nsIPresShell>
   283 nsHTMLDocument::CreateShell(nsPresContext* aContext,
   284                             nsViewManager* aViewManager,
   285                             nsStyleSet* aStyleSet)
   286 {
   287   return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode);
   288 }
   290 void
   291 nsHTMLDocument::TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV,
   292                                int32_t& aCharsetSource, nsACString& aCharset)
   293 {
   294   if (aMarkupDV) {
   295     int32_t requestCharsetSource;
   296     nsresult rv = aMarkupDV->GetHintCharacterSetSource(&requestCharsetSource);
   298     if(NS_SUCCEEDED(rv) && kCharsetUninitialized != requestCharsetSource) {
   299       nsAutoCString requestCharset;
   300       rv = aMarkupDV->GetHintCharacterSet(requestCharset);
   301       aMarkupDV->SetHintCharacterSetSource((int32_t)(kCharsetUninitialized));
   303       if(requestCharsetSource <= aCharsetSource)
   304         return;
   306       if(NS_SUCCEEDED(rv) && EncodingUtils::IsAsciiCompatible(requestCharset)) {
   307         aCharsetSource = requestCharsetSource;
   308         aCharset = requestCharset;
   310         return;
   311       }
   312     }
   313   }
   314   return;
   315 }
   318 void
   319 nsHTMLDocument::TryUserForcedCharset(nsIMarkupDocumentViewer* aMarkupDV,
   320                                      nsIDocShell*  aDocShell,
   321                                      int32_t& aCharsetSource,
   322                                      nsACString& aCharset)
   323 {
   324   nsresult rv = NS_OK;
   326   if(kCharsetFromUserForced <= aCharsetSource)
   327     return;
   329   // mCharacterSet not updated yet for channel, so check aCharset, too.
   330   if (WillIgnoreCharsetOverride() || !EncodingUtils::IsAsciiCompatible(aCharset)) {
   331     return;
   332   }
   334   nsAutoCString forceCharsetFromDocShell;
   335   if (aMarkupDV) {
   336     // XXX mailnews-only
   337     rv = aMarkupDV->GetForceCharacterSet(forceCharsetFromDocShell);
   338   }
   340   if(NS_SUCCEEDED(rv) &&
   341      !forceCharsetFromDocShell.IsEmpty() &&
   342      EncodingUtils::IsAsciiCompatible(forceCharsetFromDocShell)) {
   343     aCharset = forceCharsetFromDocShell;
   344     aCharsetSource = kCharsetFromUserForced;
   345     return;
   346   }
   348   if (aDocShell) {
   349     // This is the Character Encoding menu code path in Firefox
   350     nsAutoCString charset;
   351     rv = aDocShell->GetForcedCharset(charset);
   353     if (NS_SUCCEEDED(rv) && !charset.IsEmpty()) {
   354       if (!EncodingUtils::IsAsciiCompatible(charset)) {
   355         return;
   356       }
   357       aCharset = charset;
   358       aCharsetSource = kCharsetFromUserForced;
   359       aDocShell->SetForcedCharset(NS_LITERAL_CSTRING(""));
   360     }
   361   }
   362 }
   364 void
   365 nsHTMLDocument::TryCacheCharset(nsICachingChannel* aCachingChannel,
   366                                 int32_t& aCharsetSource,
   367                                 nsACString& aCharset)
   368 {
   369   nsresult rv;
   371   if (kCharsetFromCache <= aCharsetSource) {
   372     return;
   373   }
   375   nsCString cachedCharset;
   376   rv = aCachingChannel->GetCacheTokenCachedCharset(cachedCharset);
   377   // Check EncodingUtils::IsAsciiCompatible() even in the cache case, because the value
   378   // might be stale and in the case of a stale charset that is not a rough
   379   // ASCII superset, the parser has no way to recover.
   380   if (NS_SUCCEEDED(rv) &&
   381       !cachedCharset.IsEmpty() &&
   382       EncodingUtils::IsAsciiCompatible(cachedCharset))
   383   {
   384     aCharset = cachedCharset;
   385     aCharsetSource = kCharsetFromCache;
   386   }
   387 }
   389 void
   390 nsHTMLDocument::TryParentCharset(nsIDocShell*  aDocShell,
   391                                  int32_t& aCharsetSource,
   392                                  nsACString& aCharset)
   393 {
   394   if (!aDocShell) {
   395     return;
   396   }
   397   if (aCharsetSource >= kCharsetFromParentForced) {
   398     return;
   399   }
   401   int32_t parentSource;
   402   nsAutoCString parentCharset;
   403   nsCOMPtr<nsIPrincipal> parentPrincipal;
   404   aDocShell->GetParentCharset(parentCharset,
   405                               &parentSource,
   406                               getter_AddRefs(parentPrincipal));
   407   if (parentCharset.IsEmpty()) {
   408     return;
   409   }
   410   if (kCharsetFromParentForced == parentSource ||
   411       kCharsetFromUserForced == parentSource) {
   412     if (WillIgnoreCharsetOverride() ||
   413         !EncodingUtils::IsAsciiCompatible(aCharset) || // if channel said UTF-16
   414         !EncodingUtils::IsAsciiCompatible(parentCharset)) {
   415       return;
   416     }
   417     aCharset.Assign(parentCharset);
   418     aCharsetSource = kCharsetFromParentForced;
   419     return;
   420   }
   422   if (aCharsetSource >= kCharsetFromParentFrame) {
   423     return;
   424   }
   426   if (kCharsetFromCache <= parentSource) {
   427     // Make sure that's OK
   428     if (!NodePrincipal()->Equals(parentPrincipal) ||
   429         !EncodingUtils::IsAsciiCompatible(parentCharset)) {
   430       return;
   431     }
   433     aCharset.Assign(parentCharset);
   434     aCharsetSource = kCharsetFromParentFrame;
   435   }
   436 }
   438 void
   439 nsHTMLDocument::TryTLD(int32_t& aCharsetSource, nsACString& aCharset)
   440 {
   441   if (aCharsetSource >= kCharsetFromTopLevelDomain) {
   442     return;
   443   }
   444   if (!FallbackEncoding::sGuessFallbackFromTopLevelDomain) {
   445     return;
   446   }
   447   if (!mDocumentURI) {
   448     return;
   449   }
   450   nsAutoCString host;
   451   mDocumentURI->GetAsciiHost(host);
   452   if (host.IsEmpty()) {
   453     return;
   454   }
   455   // First let's see if the host is DNS-absolute and ends with a dot and
   456   // get rid of that one.
   457   if (host.Last() == '.') {
   458     host.SetLength(host.Length() - 1);
   459     if (host.IsEmpty()) {
   460       return;
   461     }
   462   }
   463   // If we still have a dot, the host is weird, so let's continue only
   464   // if we have something other than a dot now.
   465   if (host.Last() == '.') {
   466     return;
   467   }
   468   int32_t index = host.RFindChar('.');
   469   if (index == kNotFound) {
   470     // We have an intranet host, Gecko-internal URL or an IPv6 address.
   471     return;
   472   }
   473   // Since the string didn't end with a dot and we found a dot,
   474   // there is at least one character between the dot and the end of
   475   // the string, so taking the substring below is safe.
   476   nsAutoCString tld;
   477   ToLowerCase(Substring(host, index + 1, host.Length() - (index + 1)), tld);
   478   // Reject generic TLDs and country TLDs that need more research
   479   if (!FallbackEncoding::IsParticipatingTopLevelDomain(tld)) {
   480     return;
   481   }
   482   // Check if we have an IPv4 address
   483   bool seenNonDigit = false;
   484   for (size_t i = 0; i < tld.Length(); ++i) {
   485     char c = tld.CharAt(i);
   486     if (c < '0' || c > '9') {
   487       seenNonDigit = true;
   488       break;
   489     }
   490   }
   491   if (!seenNonDigit) {
   492     return;
   493   }
   494   aCharsetSource = kCharsetFromTopLevelDomain;
   495   FallbackEncoding::FromTopLevelDomain(tld, aCharset);
   496 }
   498 void
   499 nsHTMLDocument::TryFallback(int32_t& aCharsetSource, nsACString& aCharset)
   500 {
   501   if (kCharsetFromFallback <= aCharsetSource)
   502     return;
   504   aCharsetSource = kCharsetFromFallback;
   505   FallbackEncoding::FromLocale(aCharset);
   506 }
   508 void
   509 nsHTMLDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
   510 {
   511   nsDocument::SetDocumentCharacterSet(aCharSetID);
   512   // Make sure to stash this charset on our channel as needed if it's a wyciwyg
   513   // channel.
   514   nsCOMPtr<nsIWyciwygChannel> wyciwygChannel = do_QueryInterface(mChannel);
   515   if (wyciwygChannel) {
   516     wyciwygChannel->SetCharsetAndSource(GetDocumentCharacterSetSource(),
   517                                         aCharSetID);
   518   }
   519 }
   521 nsresult
   522 nsHTMLDocument::StartDocumentLoad(const char* aCommand,
   523                                   nsIChannel* aChannel,
   524                                   nsILoadGroup* aLoadGroup,
   525                                   nsISupports* aContainer,
   526                                   nsIStreamListener **aDocListener,
   527                                   bool aReset,
   528                                   nsIContentSink* aSink)
   529 {
   530   if (!aCommand) {
   531     MOZ_ASSERT(false, "Command is mandatory");
   532     return NS_ERROR_INVALID_POINTER;
   533   }
   534   if (aSink) {
   535     MOZ_ASSERT(false, "Got a sink override. Should not happen for HTML doc.");
   536     return NS_ERROR_INVALID_ARG;
   537   }
   538   if (!mIsRegularHTML) {
   539     MOZ_ASSERT(false, "Must not set HTML doc to XHTML mode before load start.");
   540     return NS_ERROR_DOM_INVALID_STATE_ERR;
   541   }
   543   nsAutoCString contentType;
   544   aChannel->GetContentType(contentType);
   546   bool view = !strcmp(aCommand, "view") ||
   547               !strcmp(aCommand, "external-resource");
   548   bool viewSource = !strcmp(aCommand, "view-source");
   549   bool asData = !strcmp(aCommand, kLoadAsData);
   550   if(!(view || viewSource || asData)) {
   551     MOZ_ASSERT(false, "Bad parser command");
   552     return NS_ERROR_INVALID_ARG;
   553   }
   555   bool html = contentType.EqualsLiteral(TEXT_HTML);
   556   bool xhtml = !html && contentType.EqualsLiteral(APPLICATION_XHTML_XML);
   557   bool plainText = !html && !xhtml && nsContentUtils::IsPlainTextType(contentType);
   558   if (!(html || xhtml || plainText || viewSource)) {
   559     MOZ_ASSERT(false, "Channel with bad content type.");
   560     return NS_ERROR_INVALID_ARG;
   561   }
   563   bool loadAsHtml5 = true;
   565   if (!viewSource && xhtml) {
   566       // We're parsing XHTML as XML, remember that.
   567       mIsRegularHTML = false;
   568       mCompatMode = eCompatibility_FullStandards;
   569       loadAsHtml5 = false;
   570   }
   572   // TODO: Proper about:blank treatment is bug 543435
   573   if (loadAsHtml5 && view) {
   574     // mDocumentURI hasn't been set, yet, so get the URI from the channel
   575     nsCOMPtr<nsIURI> uri;
   576     aChannel->GetOriginalURI(getter_AddRefs(uri));
   577     // Adapted from nsDocShell:
   578     // GetSpec can be expensive for some URIs, so check the scheme first.
   579     bool isAbout = false;
   580     if (uri && NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) {
   581       nsAutoCString str;
   582       uri->GetSpec(str);
   583       if (str.EqualsLiteral("about:blank")) {
   584         loadAsHtml5 = false;    
   585       }
   586     }
   587   }
   589   CSSLoader()->SetCompatibilityMode(mCompatMode);
   591   nsresult rv = nsDocument::StartDocumentLoad(aCommand,
   592                                               aChannel, aLoadGroup,
   593                                               aContainer,
   594                                               aDocListener, aReset);
   595   if (NS_FAILED(rv)) {
   596     return rv;
   597   }
   599   // Store the security info for future use with wyciwyg channels.
   600   aChannel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
   602   nsCOMPtr<nsIURI> uri;
   603   rv = aChannel->GetURI(getter_AddRefs(uri));
   604   if (NS_FAILED(rv)) {
   605     return rv;
   606   }
   608   nsCOMPtr<nsICachingChannel> cachingChan = do_QueryInterface(aChannel);
   610   if (loadAsHtml5) {
   611     mParser = nsHtml5Module::NewHtml5Parser();
   612     if (plainText) {
   613       if (viewSource) {
   614         mParser->MarkAsNotScriptCreated("view-source-plain");
   615       } else {
   616         mParser->MarkAsNotScriptCreated("plain-text");
   617       }
   618     } else if (viewSource && !html) {
   619       mParser->MarkAsNotScriptCreated("view-source-xml");
   620     } else {
   621       mParser->MarkAsNotScriptCreated(aCommand);
   622     }
   623   } else {
   624     mParser = do_CreateInstance(kCParserCID, &rv);
   625     NS_ENSURE_SUCCESS(rv, rv);
   626   }
   628   // Look for the parent document.  Note that at this point we don't have our
   629   // content viewer set up yet, and therefore do not have a useful
   630   // mParentDocument.
   632   // in this block of code, if we get an error result, we return it
   633   // but if we get a null pointer, that's perfectly legal for parent
   634   // and parentContentViewer
   635   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
   636   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
   637   if (docShell) {
   638     docShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
   639   }
   641   nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
   642   nsCOMPtr<nsIContentViewer> parentContentViewer;
   643   if (parent) {
   644     rv = parent->GetContentViewer(getter_AddRefs(parentContentViewer));
   645     NS_ENSURE_SUCCESS(rv, rv);
   646   }
   648   nsCOMPtr<nsIMarkupDocumentViewer> muCV;
   649   nsCOMPtr<nsIContentViewer> cv;
   650   if (docShell) {
   651     docShell->GetContentViewer(getter_AddRefs(cv));
   652   }
   653   if (cv) {
   654      muCV = do_QueryInterface(cv);
   655   } else {
   656     muCV = do_QueryInterface(parentContentViewer);
   657   }
   659   nsAutoCString urlSpec;
   660   uri->GetSpec(urlSpec);
   661 #ifdef DEBUG_charset
   662   printf("Determining charset for %s\n", urlSpec.get());
   663 #endif
   665   // These are the charset source and charset for our document
   666   int32_t charsetSource;
   667   nsAutoCString charset;
   669   // These are the charset source and charset for the parser.  This can differ
   670   // from that for the document if the channel is a wyciwyg channel.
   671   int32_t parserCharsetSource;
   672   nsAutoCString parserCharset;
   674   nsCOMPtr<nsIWyciwygChannel> wyciwygChannel;
   676   // For error reporting
   677   nsHtml5TreeOpExecutor* executor = nullptr;
   678   if (loadAsHtml5) {
   679     executor = static_cast<nsHtml5TreeOpExecutor*> (mParser->GetContentSink());
   680   }
   682   if (!IsHTML() || !docShell) { // no docshell for text/html XHR
   683     charsetSource = IsHTML() ? kCharsetFromFallback
   684                              : kCharsetFromDocTypeDefault;
   685     charset.AssignLiteral("UTF-8");
   686     TryChannelCharset(aChannel, charsetSource, charset, executor);
   687     parserCharsetSource = charsetSource;
   688     parserCharset = charset;
   689   } else {
   690     NS_ASSERTION(docShell, "Unexpected null value");
   692     charsetSource = kCharsetUninitialized;
   693     wyciwygChannel = do_QueryInterface(aChannel);
   695     // The following will try to get the character encoding from various
   696     // sources. Each Try* function will return early if the source is already
   697     // at least as large as any of the sources it might look at.  Some of
   698     // these functions (like TryHintCharset and TryParentCharset) can set
   699     // charsetSource to various values depending on where the charset they
   700     // end up finding originally comes from.
   702     // Don't actually get the charset from the channel if this is a
   703     // wyciwyg channel; it'll always be UTF-16
   704     if (!wyciwygChannel) {
   705       // Otherwise, try the channel's charset (e.g., charset from HTTP
   706       // "Content-Type" header) first. This way, we get to reject overrides in
   707       // TryParentCharset and TryUserForcedCharset if the channel said UTF-16.
   708       // This is to avoid socially engineered XSS by adding user-supplied
   709       // content to a UTF-16 site such that the byte have a dangerous
   710       // interpretation as ASCII and the user can be lured to using the
   711       // charset menu.
   712       TryChannelCharset(aChannel, charsetSource, charset, executor);
   713     }
   715     TryUserForcedCharset(muCV, docShell, charsetSource, charset);
   717     TryHintCharset(muCV, charsetSource, charset); // XXX mailnews-only
   718     TryParentCharset(docShell, charsetSource, charset);
   720     if (cachingChan && !urlSpec.IsEmpty()) {
   721       TryCacheCharset(cachingChan, charsetSource, charset);
   722     }
   724     TryTLD(charsetSource, charset);
   725     TryFallback(charsetSource, charset);
   727     if (wyciwygChannel) {
   728       // We know for sure that the parser needs to be using UTF16.
   729       parserCharset = "UTF-16";
   730       parserCharsetSource = charsetSource < kCharsetFromChannel ?
   731         kCharsetFromChannel : charsetSource;
   733       nsAutoCString cachedCharset;
   734       int32_t cachedSource;
   735       rv = wyciwygChannel->GetCharsetAndSource(&cachedSource, cachedCharset);
   736       if (NS_SUCCEEDED(rv)) {
   737         if (cachedSource > charsetSource) {
   738           charsetSource = cachedSource;
   739           charset = cachedCharset;
   740         }
   741       } else {
   742         // Don't propagate this error.
   743         rv = NS_OK;
   744       }
   746     } else {
   747       parserCharset = charset;
   748       parserCharsetSource = charsetSource;
   749     }
   750   }
   752   SetDocumentCharacterSetSource(charsetSource);
   753   SetDocumentCharacterSet(charset);
   755   if (cachingChan) {
   756     NS_ASSERTION(charset == parserCharset,
   757                  "How did those end up different here?  wyciwyg channels are "
   758                  "not nsICachingChannel");
   759     rv = cachingChan->SetCacheTokenCachedCharset(charset);
   760     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "cannot SetMetaDataElement");
   761     rv = NS_OK; // don't propagate error
   762   }
   764   // Set the parser as the stream listener for the document loader...
   765   rv = NS_OK;
   766   nsCOMPtr<nsIStreamListener> listener = mParser->GetStreamListener();
   767   listener.forget(aDocListener);
   769 #ifdef DEBUG_charset
   770   printf(" charset = %s source %d\n",
   771         charset.get(), charsetSource);
   772 #endif
   773   mParser->SetDocumentCharset(parserCharset, parserCharsetSource);
   774   mParser->SetCommand(aCommand);
   776   if (!IsHTML()) {
   777     MOZ_ASSERT(!loadAsHtml5);
   778     nsCOMPtr<nsIXMLContentSink> xmlsink;
   779     NS_NewXMLContentSink(getter_AddRefs(xmlsink), this, uri,
   780                          docShell, aChannel);
   781     mParser->SetContentSink(xmlsink);
   782   } else {
   783     if (loadAsHtml5) {
   784       nsHtml5Module::Initialize(mParser, this, uri, docShell, aChannel);
   785     } else {
   786       // about:blank *only*
   787       nsCOMPtr<nsIHTMLContentSink> htmlsink;
   788       NS_NewHTMLContentSink(getter_AddRefs(htmlsink), this, uri,
   789                             docShell, aChannel);
   790       mParser->SetContentSink(htmlsink);
   791     }
   792   }
   794   if (plainText && !nsContentUtils::IsChildOfSameType(this) &&
   795       Preferences::GetBool("plain_text.wrap_long_lines")) {
   796     nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
   797     NS_ASSERTION(NS_SUCCEEDED(rv) && bundleService, "The bundle service could not be loaded");
   798     nsCOMPtr<nsIStringBundle> bundle;
   799     rv = bundleService->CreateBundle("chrome://global/locale/browser.properties",
   800                                      getter_AddRefs(bundle));
   801     NS_ASSERTION(NS_SUCCEEDED(rv) && bundle, "chrome://global/locale/browser.properties could not be loaded");
   802     nsXPIDLString title;
   803     if (bundle) {
   804       bundle->GetStringFromName(MOZ_UTF16("plainText.wordWrap"), getter_Copies(title));
   805     }
   806     SetSelectedStyleSheetSet(title);
   807   }
   809   // parser the content of the URI
   810   mParser->Parse(uri, nullptr, (void *)this);
   812   return rv;
   813 }
   815 void
   816 nsHTMLDocument::StopDocumentLoad()
   817 {
   818   BlockOnload();
   820   // Remove the wyciwyg channel request from the document load group
   821   // that we added in Open() if Open() was called on this doc.
   822   RemoveWyciwygChannel();
   823   NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::StopDocumentLoad(): "
   824                "nsIWyciwygChannel could not be removed!");
   826   nsDocument::StopDocumentLoad();
   827   UnblockOnload(false);
   828   return;
   829 }
   831 void
   832 nsHTMLDocument::BeginLoad()
   833 {
   834   if (IsEditingOn()) {
   835     // Reset() blows away all event listeners in the document, and our
   836     // editor relies heavily on those. Midas is turned on, to make it
   837     // work, re-initialize it to give it a chance to add its event
   838     // listeners again.
   840     TurnEditingOff();
   841     EditingStateChanged();
   842   }
   843   nsDocument::BeginLoad();
   844 }
   846 void
   847 nsHTMLDocument::EndLoad()
   848 {
   849   bool turnOnEditing =
   850     mParser && (HasFlag(NODE_IS_EDITABLE) || mContentEditableCount > 0);
   851   // Note: nsDocument::EndLoad nulls out mParser.
   852   nsDocument::EndLoad();
   853   if (turnOnEditing) {
   854     EditingStateChanged();
   855   }
   856 }
   858 void
   859 nsHTMLDocument::SetCompatibilityMode(nsCompatibility aMode)
   860 {
   861   NS_ASSERTION(IsHTML() || aMode == eCompatibility_FullStandards,
   862                "Bad compat mode for XHTML document!");
   864   mCompatMode = aMode;
   865   CSSLoader()->SetCompatibilityMode(mCompatMode);
   866   nsCOMPtr<nsIPresShell> shell = GetShell();
   867   if (shell) {
   868     nsPresContext *pc = shell->GetPresContext();
   869     if (pc) {
   870       pc->CompatibilityModeChanged();
   871     }
   872   }
   873 }
   875 //
   876 // nsIDOMHTMLDocument interface implementation
   877 //
   878 already_AddRefed<nsIURI>
   879 nsHTMLDocument::GetDomainURI()
   880 {
   881   nsIPrincipal* principal = NodePrincipal();
   883   nsCOMPtr<nsIURI> uri;
   884   principal->GetDomain(getter_AddRefs(uri));
   885   if (uri) {
   886     return uri.forget();
   887   }
   889   principal->GetURI(getter_AddRefs(uri));
   890   return uri.forget();
   891 }
   894 NS_IMETHODIMP
   895 nsHTMLDocument::GetDomain(nsAString& aDomain)
   896 {
   897   ErrorResult rv;
   898   GetDomain(aDomain, rv);
   899   return rv.ErrorCode();
   900 }
   902 void
   903 nsHTMLDocument::GetDomain(nsAString& aDomain, ErrorResult& rv)
   904 {
   905   nsCOMPtr<nsIURI> uri = GetDomainURI();
   907   if (!uri) {
   908     rv.Throw(NS_ERROR_FAILURE);
   909     return;
   910   }
   912   nsAutoCString hostName;
   914   if (NS_SUCCEEDED(uri->GetHost(hostName))) {
   915     CopyUTF8toUTF16(hostName, aDomain);
   916   } else {
   917     // If we can't get the host from the URI (e.g. about:, javascript:,
   918     // etc), just return an null string.
   919     SetDOMStringToNull(aDomain);
   920   }
   921 }
   923 NS_IMETHODIMP
   924 nsHTMLDocument::SetDomain(const nsAString& aDomain)
   925 {
   926   ErrorResult rv;
   927   SetDomain(aDomain, rv);
   928   return rv.ErrorCode();
   929 }
   931 void
   932 nsHTMLDocument::SetDomain(const nsAString& aDomain, ErrorResult& rv)
   933 {
   934   if (mSandboxFlags & SANDBOXED_DOMAIN) {
   935     // We're sandboxed; disallow setting domain
   936     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   937     return;
   938   }
   940   if (aDomain.IsEmpty()) {
   941     rv.Throw(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN);
   942     return;
   943   }
   945   // Create new URI
   946   nsCOMPtr<nsIURI> uri = GetDomainURI();
   948   if (!uri) {
   949     rv.Throw(NS_ERROR_FAILURE);
   950     return;
   951   }
   953   nsAutoCString newURIString;
   954   if (NS_FAILED(uri->GetScheme(newURIString))) {
   955     rv.Throw(NS_ERROR_FAILURE);
   956     return;
   957   }
   958   nsAutoCString path;
   959   if (NS_FAILED(uri->GetPath(path))) {
   960     rv.Throw(NS_ERROR_FAILURE);
   961     return;
   962   }
   963   newURIString.AppendLiteral("://");
   964   AppendUTF16toUTF8(aDomain, newURIString);
   965   newURIString.Append(path);
   967   nsCOMPtr<nsIURI> newURI;
   968   if (NS_FAILED(NS_NewURI(getter_AddRefs(newURI), newURIString))) {
   969     rv.Throw(NS_ERROR_FAILURE);
   970     return;
   971   }
   973   // Check new domain - must be a superdomain of the current host
   974   // For example, a page from foo.bar.com may set domain to bar.com,
   975   // but not to ar.com, baz.com, or fi.foo.bar.com.
   976   nsAutoCString current, domain;
   977   if (NS_FAILED(uri->GetAsciiHost(current)))
   978     current.Truncate();
   979   if (NS_FAILED(newURI->GetAsciiHost(domain)))
   980     domain.Truncate();
   982   bool ok = current.Equals(domain);
   983   if (current.Length() > domain.Length() &&
   984       StringEndsWith(current, domain) &&
   985       current.CharAt(current.Length() - domain.Length() - 1) == '.') {
   986     // We're golden if the new domain is the current page's base domain or a
   987     // subdomain of it.
   988     nsCOMPtr<nsIEffectiveTLDService> tldService =
   989       do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   990     if (!tldService) {
   991       rv.Throw(NS_ERROR_NOT_AVAILABLE);
   992       return;
   993     }
   995     nsAutoCString currentBaseDomain;
   996     ok = NS_SUCCEEDED(tldService->GetBaseDomain(uri, 0, currentBaseDomain));
   997     NS_ASSERTION(StringEndsWith(domain, currentBaseDomain) ==
   998                  (domain.Length() >= currentBaseDomain.Length()),
   999                  "uh-oh!  slight optimization wasn't valid somehow!");
  1000     ok = ok && domain.Length() >= currentBaseDomain.Length();
  1002   if (!ok) {
  1003     // Error: illegal domain
  1004     rv.Throw(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN);
  1005     return;
  1008   rv = NodePrincipal()->SetDomain(newURI);
  1011 nsGenericHTMLElement*
  1012 nsHTMLDocument::GetBody()
  1014   Element* html = GetHtmlElement();
  1015   if (!html) {
  1016     return nullptr;
  1019   for (nsIContent* child = html->GetFirstChild();
  1020        child;
  1021        child = child->GetNextSibling()) {
  1022     if (child->IsHTML(nsGkAtoms::body) || child->IsHTML(nsGkAtoms::frameset)) {
  1023       return static_cast<nsGenericHTMLElement*>(child);
  1027   return nullptr;
  1030 NS_IMETHODIMP
  1031 nsHTMLDocument::GetBody(nsIDOMHTMLElement** aBody)
  1033   *aBody = nullptr;
  1035   nsIContent *body = GetBody();
  1037   return body ? CallQueryInterface(body, aBody) : NS_OK;
  1040 NS_IMETHODIMP
  1041 nsHTMLDocument::SetBody(nsIDOMHTMLElement* aBody)
  1043   nsCOMPtr<nsIContent> newBody = do_QueryInterface(aBody);
  1044   MOZ_ASSERT(!newBody || newBody->IsHTML(),
  1045              "How could we be an nsIContent but not actually HTML here?");
  1046   ErrorResult rv;
  1047   SetBody(static_cast<nsGenericHTMLElement*>(newBody.get()), rv);
  1048   return rv.ErrorCode();
  1051 void
  1052 nsHTMLDocument::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv)
  1054   Element* root = GetRootElement();
  1056   // The body element must be either a body tag or a frameset tag. And we must
  1057   // have a html root tag, otherwise GetBody will not return the newly set
  1058   // body.
  1059   if (!newBody || !(newBody->Tag() == nsGkAtoms::body ||
  1060                     newBody->Tag() == nsGkAtoms::frameset) ||
  1061       !root || !root->IsHTML() ||
  1062       root->Tag() != nsGkAtoms::html) {
  1063     rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  1064     return;
  1067   // Use DOM methods so that we pass through the appropriate security checks.
  1068   nsCOMPtr<Element> currentBody = GetBodyElement();
  1069   if (currentBody) {
  1070     root->ReplaceChild(*newBody, *currentBody, rv);
  1071   } else {
  1072     root->AppendChild(*newBody, rv);
  1076 NS_IMETHODIMP
  1077 nsHTMLDocument::GetHead(nsIDOMHTMLHeadElement** aHead)
  1079   *aHead = nullptr;
  1081   Element* head = GetHeadElement();
  1083   return head ? CallQueryInterface(head, aHead) : NS_OK;
  1086 NS_IMETHODIMP
  1087 nsHTMLDocument::GetImages(nsIDOMHTMLCollection** aImages)
  1089   NS_ADDREF(*aImages = Images());
  1090   return NS_OK;
  1093 nsIHTMLCollection*
  1094 nsHTMLDocument::Images()
  1096   if (!mImages) {
  1097     mImages = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::img, nsGkAtoms::img);
  1099   return mImages;
  1102 NS_IMETHODIMP
  1103 nsHTMLDocument::GetApplets(nsIDOMHTMLCollection** aApplets)
  1105   NS_ADDREF(*aApplets = Applets());
  1106   return NS_OK;
  1109 nsIHTMLCollection*
  1110 nsHTMLDocument::Applets()
  1112   if (!mApplets) {
  1113     mApplets = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::applet, nsGkAtoms::applet);
  1115   return mApplets;
  1118 bool
  1119 nsHTMLDocument::MatchLinks(nsIContent *aContent, int32_t aNamespaceID,
  1120                            nsIAtom* aAtom, void* aData)
  1122   nsIDocument* doc = aContent->GetCurrentDoc();
  1124   if (doc) {
  1125     NS_ASSERTION(aContent->IsInDoc(),
  1126                  "This method should never be called on content nodes that "
  1127                  "are not in a document!");
  1128 #ifdef DEBUG
  1130       nsCOMPtr<nsIHTMLDocument> htmldoc =
  1131         do_QueryInterface(aContent->GetCurrentDoc());
  1132       NS_ASSERTION(htmldoc,
  1133                    "Huh, how did this happen? This should only be used with "
  1134                    "HTML documents!");
  1136 #endif
  1138     nsINodeInfo *ni = aContent->NodeInfo();
  1140     nsIAtom *localName = ni->NameAtom();
  1141     if (ni->NamespaceID() == kNameSpaceID_XHTML &&
  1142         (localName == nsGkAtoms::a || localName == nsGkAtoms::area)) {
  1143       return aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
  1147   return false;
  1150 NS_IMETHODIMP
  1151 nsHTMLDocument::GetLinks(nsIDOMHTMLCollection** aLinks)
  1153   NS_ADDREF(*aLinks = Links());
  1154   return NS_OK;
  1157 nsIHTMLCollection*
  1158 nsHTMLDocument::Links()
  1160   if (!mLinks) {
  1161     mLinks = new nsContentList(this, MatchLinks, nullptr, nullptr);
  1163   return mLinks;
  1166 bool
  1167 nsHTMLDocument::MatchAnchors(nsIContent *aContent, int32_t aNamespaceID,
  1168                              nsIAtom* aAtom, void* aData)
  1170   NS_ASSERTION(aContent->IsInDoc(),
  1171                "This method should never be called on content nodes that "
  1172                "are not in a document!");
  1173 #ifdef DEBUG
  1175     nsCOMPtr<nsIHTMLDocument> htmldoc =
  1176       do_QueryInterface(aContent->GetCurrentDoc());
  1177     NS_ASSERTION(htmldoc,
  1178                  "Huh, how did this happen? This should only be used with "
  1179                  "HTML documents!");
  1181 #endif
  1183   if (aContent->NodeInfo()->Equals(nsGkAtoms::a, kNameSpaceID_XHTML)) {
  1184     return aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::name);
  1187   return false;
  1190 NS_IMETHODIMP
  1191 nsHTMLDocument::GetAnchors(nsIDOMHTMLCollection** aAnchors)
  1193   NS_ADDREF(*aAnchors = Anchors());
  1194   return NS_OK;
  1197 nsIHTMLCollection*
  1198 nsHTMLDocument::Anchors()
  1200   if (!mAnchors) {
  1201     mAnchors = new nsContentList(this, MatchAnchors, nullptr, nullptr);
  1203   return mAnchors;
  1206 NS_IMETHODIMP
  1207 nsHTMLDocument::GetScripts(nsIDOMHTMLCollection** aScripts)
  1209   NS_ADDREF(*aScripts = Scripts());
  1210   return NS_OK;
  1213 nsIHTMLCollection*
  1214 nsHTMLDocument::Scripts()
  1216   if (!mScripts) {
  1217     mScripts = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::script, nsGkAtoms::script);
  1219   return mScripts;
  1222 NS_IMETHODIMP
  1223 nsHTMLDocument::GetCookie(nsAString& aCookie)
  1225   ErrorResult rv;
  1226   GetCookie(aCookie, rv);
  1227   return rv.ErrorCode();
  1230 void
  1231 nsHTMLDocument::GetCookie(nsAString& aCookie, ErrorResult& rv)
  1233   aCookie.Truncate(); // clear current cookie in case service fails;
  1234                       // no cookie isn't an error condition.
  1236   if (mDisableCookieAccess) {
  1237     return;
  1240   // If the document's sandboxed origin flag is set, access to read cookies
  1241   // is prohibited.
  1242   if (mSandboxFlags & SANDBOXED_ORIGIN) {
  1243     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1244     return;
  1247   // not having a cookie service isn't an error
  1248   nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
  1249   if (service) {
  1250     // Get a URI from the document principal. We use the original
  1251     // codebase in case the codebase was changed by SetDomain
  1252     nsCOMPtr<nsIURI> codebaseURI;
  1253     NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
  1255     if (!codebaseURI) {
  1256       // Document's principal is not a codebase (may be system), so
  1257       // can't set cookies
  1259       return;
  1262     nsXPIDLCString cookie;
  1263     service->GetCookieString(codebaseURI, mChannel, getter_Copies(cookie));
  1264     // CopyUTF8toUTF16 doesn't handle error
  1265     // because it assumes that the input is valid.
  1266     nsContentUtils::ConvertStringFromEncoding(NS_LITERAL_CSTRING("UTF-8"),
  1267                                               cookie, aCookie);
  1271 NS_IMETHODIMP
  1272 nsHTMLDocument::SetCookie(const nsAString& aCookie)
  1274   ErrorResult rv;
  1275   SetCookie(aCookie, rv);
  1276   return rv.ErrorCode();
  1279 void
  1280 nsHTMLDocument::SetCookie(const nsAString& aCookie, ErrorResult& rv)
  1282   if (mDisableCookieAccess) {
  1283     return;
  1286   // If the document's sandboxed origin flag is set, access to write cookies
  1287   // is prohibited.
  1288   if (mSandboxFlags & SANDBOXED_ORIGIN) {
  1289     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1290     return;
  1293   // not having a cookie service isn't an error
  1294   nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
  1295   if (service && mDocumentURI) {
  1296     // The for getting the URI matches nsNavigator::GetCookieEnabled
  1297     nsCOMPtr<nsIURI> codebaseURI;
  1298     NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
  1300     if (!codebaseURI) {
  1301       // Document's principal is not a codebase (may be system), so
  1302       // can't set cookies
  1304       return;
  1307     NS_ConvertUTF16toUTF8 cookie(aCookie);
  1308     service->SetCookieString(codebaseURI, nullptr, cookie.get(), mChannel);
  1312 NS_IMETHODIMP
  1313 nsHTMLDocument::Open(const nsAString& aContentTypeOrUrl,
  1314                      const nsAString& aReplaceOrName,
  1315                      const nsAString& aFeatures,
  1316                      JSContext* cx, uint8_t aOptionalArgCount,
  1317                      nsISupports** aReturn)
  1319   // When called with 3 or more arguments, document.open() calls window.open().
  1320   if (aOptionalArgCount > 2) {
  1321     ErrorResult rv;
  1322     *aReturn = Open(cx, aContentTypeOrUrl, aReplaceOrName, aFeatures,
  1323                     false, rv).take();
  1324     return rv.ErrorCode();
  1327   nsString type;
  1328   if (aOptionalArgCount > 0) {
  1329     type = aContentTypeOrUrl;
  1330   } else {
  1331     type.AssignLiteral("text/html");
  1333   nsString replace;
  1334   if (aOptionalArgCount > 1) {
  1335     replace = aReplaceOrName;
  1337   ErrorResult rv;
  1338   *aReturn = Open(cx, type, replace, rv).take();
  1339   return rv.ErrorCode();
  1342 already_AddRefed<nsIDOMWindow>
  1343 nsHTMLDocument::Open(JSContext* /* unused */,
  1344                      const nsAString& aURL,
  1345                      const nsAString& aName,
  1346                      const nsAString& aFeatures,
  1347                      bool aReplace,
  1348                      ErrorResult& rv)
  1350   NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
  1351                "XOW should have caught this!");
  1353   nsCOMPtr<nsIDOMWindow> window = GetInnerWindow();
  1354   if (!window) {
  1355     rv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
  1356     return nullptr;
  1358   nsCOMPtr<nsIDOMJSWindow> win = do_QueryInterface(window);
  1359   nsCOMPtr<nsIDOMWindow> newWindow;
  1360   // XXXbz We ignore aReplace for now.
  1361   rv = win->OpenJS(aURL, aName, aFeatures, getter_AddRefs(newWindow));
  1362   return newWindow.forget();
  1365 already_AddRefed<nsIDocument>
  1366 nsHTMLDocument::Open(JSContext* cx,
  1367                      const nsAString& aType,
  1368                      const nsAString& aReplace,
  1369                      ErrorResult& rv)
  1371   NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
  1372                "XOW should have caught this!");
  1373   if (!IsHTML() || mDisableDocWrite) {
  1374     // No calling document.open() on XHTML
  1375     rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1376     return nullptr;
  1379   nsAutoCString contentType;
  1380   contentType.AssignLiteral("text/html");
  1382   nsAutoString type;
  1383   nsContentUtils::ASCIIToLower(aType, type);
  1384   nsAutoCString actualType, dummy;
  1385   NS_ParseContentType(NS_ConvertUTF16toUTF8(type), actualType, dummy);
  1386   if (!actualType.EqualsLiteral("text/html") &&
  1387       !type.EqualsLiteral("replace")) {
  1388     contentType.AssignLiteral("text/plain");
  1391   // If we already have a parser we ignore the document.open call.
  1392   if (mParser || mParserAborted) {
  1393     // The WHATWG spec says: "If the document has an active parser that isn't
  1394     // a script-created parser, and the insertion point associated with that
  1395     // parser's input stream is not undefined (that is, it does point to
  1396     // somewhere in the input stream), then the method does nothing. Abort
  1397     // these steps and return the Document object on which the method was
  1398     // invoked."
  1399     // Note that aborting a parser leaves the parser "active" with its
  1400     // insertion point "not undefined". We track this using mParserAborted,
  1401     // because aborting a parser nulls out mParser.
  1402     nsCOMPtr<nsIDocument> ret = this;
  1403     return ret.forget();
  1406   // No calling document.open() without a script global object
  1407   if (!mScriptGlobalObject) {
  1408     nsCOMPtr<nsIDocument> ret = this;
  1409     return ret.forget();
  1412   nsPIDOMWindow* outer = GetWindow();
  1413   if (!outer || (GetInnerWindow() != outer->GetCurrentInnerWindow())) {
  1414     nsCOMPtr<nsIDocument> ret = this;
  1415     return ret.forget();
  1418   // check whether we're in the middle of unload.  If so, ignore this call.
  1419   nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
  1420   if (!shell) {
  1421     // We won't be able to create a parser anyway.
  1422     nsCOMPtr<nsIDocument> ret = this;
  1423     return ret.forget();
  1426   bool inUnload;
  1427   shell->GetIsInUnload(&inUnload);
  1428   if (inUnload) {
  1429     nsCOMPtr<nsIDocument> ret = this;
  1430     return ret.forget();
  1433   // Note: We want to use GetDocumentFromContext here because this document
  1434   // should inherit the security information of the document that's opening us,
  1435   // (since if it's secure, then it's presumably trusted).
  1436   nsCOMPtr<nsIDocument> callerDoc = nsContentUtils::GetDocumentFromContext();
  1437   if (!callerDoc) {
  1438     // If we're called from C++ or in some other way without an originating
  1439     // document we can't do a document.open w/o changing the principal of the
  1440     // document to something like about:blank (as that's the only sane thing to
  1441     // do when we don't know the origin of this call), and since we can't
  1442     // change the principals of a document for security reasons we'll have to
  1443     // refuse to go ahead with this call.
  1445     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1446     return nullptr;
  1449   // Grab a reference to the calling documents security info (if any)
  1450   // and URIs as they may be lost in the call to Reset().
  1451   nsCOMPtr<nsISupports> securityInfo = callerDoc->GetSecurityInfo();
  1452   nsCOMPtr<nsIURI> uri = callerDoc->GetDocumentURI();
  1453   nsCOMPtr<nsIURI> baseURI = callerDoc->GetBaseURI();
  1454   nsCOMPtr<nsIPrincipal> callerPrincipal = callerDoc->NodePrincipal();
  1455   nsCOMPtr<nsIChannel> callerChannel = callerDoc->GetChannel();
  1457   // We're called from script. Make sure the script is from the same
  1458   // origin, not just that the caller can access the document. This is
  1459   // needed to keep document principals from ever changing, which is
  1460   // needed because of the way we use our XOW code, and is a sane
  1461   // thing to do anyways.
  1463   bool equals = false;
  1464   if (NS_FAILED(callerPrincipal->Equals(NodePrincipal(), &equals)) ||
  1465       !equals) {
  1467 #ifdef DEBUG
  1468     nsCOMPtr<nsIURI> callerDocURI = callerDoc->GetDocumentURI();
  1469     nsCOMPtr<nsIURI> thisURI = nsIDocument::GetDocumentURI();
  1470     nsAutoCString callerSpec;
  1471     nsAutoCString thisSpec;
  1472     if (callerDocURI) {
  1473       callerDocURI->GetSpec(callerSpec);
  1475     if (thisURI) {
  1476       thisURI->GetSpec(thisSpec);
  1478     printf("nsHTMLDocument::Open callerDoc %s this %s\n", callerSpec.get(), thisSpec.get());
  1479 #endif
  1481     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1482     return nullptr;
  1485   // Stop current loads targeted at the window this document is in.
  1486   if (mScriptGlobalObject) {
  1487     nsCOMPtr<nsIContentViewer> cv;
  1488     shell->GetContentViewer(getter_AddRefs(cv));
  1490     if (cv) {
  1491       bool okToUnload;
  1492       if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) && !okToUnload) {
  1493         // We don't want to unload, so stop here, but don't throw an
  1494         // exception.
  1495         nsCOMPtr<nsIDocument> ret = this;
  1496         return ret.forget();
  1500     nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(shell));
  1501     webnav->Stop(nsIWebNavigation::STOP_NETWORK);
  1503     // The Stop call may have cancelled the onload blocker request or prevented
  1504     // it from getting added, so we need to make sure it gets added to the
  1505     // document again otherwise the document could have a non-zero onload block
  1506     // count without the onload blocker request being in the loadgroup.
  1507     EnsureOnloadBlocker();
  1510   // The open occurred after the document finished loading.
  1511   // So we reset the document and create a new one.
  1512   nsCOMPtr<nsIChannel> channel;
  1513   nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
  1515   rv = NS_NewChannel(getter_AddRefs(channel), uri, nullptr, group);
  1517   if (rv.Failed()) {
  1518     return nullptr;
  1521   // We can't depend on channels implementing property bags, so do our
  1522   // base URI manually after reset.
  1524   // Set the caller principal, if any, on the channel so that we'll
  1525   // make sure to use it when we reset.
  1526   rv = channel->SetOwner(callerPrincipal);
  1527   if (rv.Failed()) {
  1528     return nullptr;
  1531   if (callerChannel) {
  1532     nsLoadFlags callerLoadFlags;
  1533     rv = callerChannel->GetLoadFlags(&callerLoadFlags);
  1534     if (rv.Failed()) {
  1535       return nullptr;
  1538     nsLoadFlags loadFlags;
  1539     rv = channel->GetLoadFlags(&loadFlags);
  1540     if (rv.Failed()) {
  1541       return nullptr;
  1544     loadFlags |= callerLoadFlags & nsIRequest::INHIBIT_PERSISTENT_CACHING;
  1546     rv = channel->SetLoadFlags(loadFlags);
  1547     if (rv.Failed()) {
  1548       return nullptr;
  1551     // If the user has allowed mixed content on the rootDoc, then we should propogate it
  1552     // down to the new document channel.
  1553     bool rootHasSecureConnection = false;
  1554     bool allowMixedContent = false;
  1555     bool isDocShellRoot = false;
  1556     nsresult rvalue = shell->GetAllowMixedContentAndConnectionData(&rootHasSecureConnection, &allowMixedContent, &isDocShellRoot);
  1557     if (NS_SUCCEEDED(rvalue) && allowMixedContent && isDocShellRoot) {
  1558        shell->SetMixedContentChannel(channel);
  1562   // Before we reset the doc notify the globalwindow of the change,
  1563   // but only if we still have a window (i.e. our window object the
  1564   // current inner window in our outer window).
  1566   // Hold onto ourselves on the offchance that we're down to one ref
  1567   nsCOMPtr<nsIDocument> kungFuDeathGrip = this;
  1569   nsPIDOMWindow *window = GetInnerWindow();
  1570   if (window) {
  1571     // Remember the old scope in case the call to SetNewDocument changes it.
  1572     nsCOMPtr<nsIScriptGlobalObject> oldScope(do_QueryReferent(mScopeObject));
  1574 #ifdef DEBUG
  1575     bool willReparent = mWillReparent;
  1576     mWillReparent = true;
  1577 #endif
  1579     // Should this pass true for aForceReuseInnerWindow?
  1580     rv = window->SetNewDocument(this, nullptr, false);
  1581     if (rv.Failed()) {
  1582       return nullptr;
  1585 #ifdef DEBUG
  1586     mWillReparent = willReparent;
  1587 #endif
  1589     // Now make sure we're not flagged as the initial document anymore, now
  1590     // that we've had stuff done to us.  From now on, if anyone tries to
  1591     // document.open() us, they get a new inner window.
  1592     SetIsInitialDocument(false);
  1594     nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
  1595     JS::Rooted<JSObject*> wrapper(cx, GetWrapper());
  1596     if (oldScope && newScope != oldScope && wrapper) {
  1597       JSAutoCompartment ac(cx, wrapper);
  1598       rv = mozilla::dom::ReparentWrapper(cx, wrapper);
  1599       if (rv.Failed()) {
  1600         return nullptr;
  1602       nsIXPConnect *xpc = nsContentUtils::XPConnect();
  1603       rv = xpc->RescueOrphansInScope(cx, oldScope->GetGlobalJSObject());
  1604       if (rv.Failed()) {
  1605         return nullptr;
  1610   mDidDocumentOpen = true;
  1612   // Call Reset(), this will now do the full reset
  1613   Reset(channel, group);
  1614   if (baseURI) {
  1615     mDocumentBaseURI = baseURI;
  1618   // Store the security info of the caller now that we're done
  1619   // resetting the document.
  1620   mSecurityInfo = securityInfo;
  1622   mParserAborted = false;
  1623   mParser = nsHtml5Module::NewHtml5Parser();
  1624   nsHtml5Module::Initialize(mParser, this, uri, shell, channel);
  1626   // This will be propagated to the parser when someone actually calls write()
  1627   SetContentTypeInternal(contentType);
  1629   // Prepare the docshell and the document viewer for the impending
  1630   // out of band document.write()
  1631   shell->PrepareForNewContentModel();
  1633   // Now check whether we were opened with a "replace" argument.  If
  1634   // so, we need to tell the docshell to not create a new history
  1635   // entry for this load. Otherwise, make sure that we're doing a normal load,
  1636   // not whatever type of load was previously done on this docshell.
  1637   shell->SetLoadType(aReplace.LowerCaseEqualsLiteral("replace") ?
  1638                        LOAD_NORMAL_REPLACE : LOAD_NORMAL);
  1640   nsCOMPtr<nsIContentViewer> cv;
  1641   shell->GetContentViewer(getter_AddRefs(cv));
  1642   if (cv) {
  1643     cv->LoadStart(this);
  1646   // Add a wyciwyg channel request into the document load group
  1647   NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Open(): wyciwyg "
  1648                "channel already exists!");
  1650   // In case the editor is listening and will see the new channel
  1651   // being added, make sure mWriteLevel is non-zero so that the editor
  1652   // knows that document.open/write/close() is being called on this
  1653   // document.
  1654   ++mWriteLevel;
  1656   CreateAndAddWyciwygChannel();
  1658   --mWriteLevel;
  1660   SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
  1662   // After changing everything around, make sure that the principal on the
  1663   // document's compartment exactly matches NodePrincipal().
  1664   DebugOnly<JSObject*> wrapper = GetWrapperPreserveColor();
  1665   MOZ_ASSERT_IF(wrapper,
  1666                 JS_GetCompartmentPrincipals(js::GetObjectCompartment(wrapper)) ==
  1667                 nsJSPrincipals::get(NodePrincipal()));
  1669   return kungFuDeathGrip.forget();
  1672 NS_IMETHODIMP
  1673 nsHTMLDocument::Clear()
  1675   // This method has been deprecated
  1676   return NS_OK;
  1679 NS_IMETHODIMP
  1680 nsHTMLDocument::Close()
  1682   ErrorResult rv;
  1683   Close(rv);
  1684   return rv.ErrorCode();
  1687 void
  1688 nsHTMLDocument::Close(ErrorResult& rv)
  1690   if (!IsHTML()) {
  1691     // No calling document.close() on XHTML!
  1693     rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1694     return;
  1697   if (!mParser || !mParser->IsScriptCreated()) {
  1698     return;
  1701   ++mWriteLevel;
  1702   rv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
  1703     EmptyString(), nullptr, GetContentTypeInternal(), true);
  1704   --mWriteLevel;
  1706   // Even if that Parse() call failed, do the rest of this method
  1708   // XXX Make sure that all the document.written content is
  1709   // reflowed.  We should remove this call once we change
  1710   // nsHTMLDocument::OpenCommon() so that it completely destroys the
  1711   // earlier document's content and frame hierarchy.  Right now, it
  1712   // re-uses the earlier document's root content object and
  1713   // corresponding frame objects.  These re-used frame objects think
  1714   // that they have already been reflowed, so they drop initial
  1715   // reflows.  For certain cases of document.written content, like a
  1716   // frameset document, the dropping of the initial reflow means
  1717   // that we end up in document.close() without appended any reflow
  1718   // commands to the reflow queue and, consequently, without adding
  1719   // the dummy layout request to the load group.  Since the dummy
  1720   // layout request is not added to the load group, the onload
  1721   // handler of the frameset fires before the frames get reflowed
  1722   // and loaded.  That is the long explanation for why we need this
  1723   // one line of code here!
  1724   // XXXbz as far as I can tell this may not be needed anymore; all
  1725   // the testcases in bug 57636 pass without this line...  Leaving
  1726   // it be for now, though.  In any case, there's no reason to do
  1727   // this if we have no presshell, since in that case none of the
  1728   // above about reusing frames applies.
  1729   //
  1730   // XXXhsivonen keeping this around for bug 577508 / 253951 still :-(
  1731   if (GetShell()) {
  1732     FlushPendingNotifications(Flush_Layout);
  1735   // Removing the wyciwygChannel here is wrong when document.close() is
  1736   // called from within the document itself. However, legacy requires the
  1737   // channel to be removed here. Otherwise, the load event never fires.
  1738   NS_ASSERTION(mWyciwygChannel, "nsHTMLDocument::Close(): Trying to remove "
  1739                "nonexistent wyciwyg channel!");
  1740   RemoveWyciwygChannel();
  1741   NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Close(): "
  1742                "nsIWyciwygChannel could not be removed!");
  1745 void
  1746 nsHTMLDocument::WriteCommon(JSContext *cx,
  1747                             const Sequence<nsString>& aText,
  1748                             bool aNewlineTerminate,
  1749                             mozilla::ErrorResult& rv)
  1751   // Fast path the common case
  1752   if (aText.Length() == 1) {
  1753     rv = WriteCommon(cx, aText[0], aNewlineTerminate);
  1754   } else {
  1755     // XXXbz it would be nice if we could pass all the strings to the parser
  1756     // without having to do all this copying and then ask it to start
  1757     // parsing....
  1758     nsString text;
  1759     for (uint32_t i = 0; i < aText.Length(); ++i) {
  1760       text.Append(aText[i]);
  1762     rv = WriteCommon(cx, text, aNewlineTerminate);
  1766 nsresult
  1767 nsHTMLDocument::WriteCommon(JSContext *cx,
  1768                             const nsAString& aText,
  1769                             bool aNewlineTerminate)
  1771   mTooDeepWriteRecursion =
  1772     (mWriteLevel > NS_MAX_DOCUMENT_WRITE_DEPTH || mTooDeepWriteRecursion);
  1773   NS_ENSURE_STATE(!mTooDeepWriteRecursion);
  1775   if (!IsHTML() || mDisableDocWrite) {
  1776     // No calling document.write*() on XHTML!
  1778     return NS_ERROR_DOM_INVALID_STATE_ERR;
  1781   if (mParserAborted) {
  1782     // Hixie says aborting the parser doesn't undefine the insertion point.
  1783     // However, since we null out mParser in that case, we track the
  1784     // theoretically defined insertion point using mParserAborted.
  1785     return NS_OK;
  1788   nsresult rv = NS_OK;
  1790   void *key = GenerateParserKey();
  1791   if (mParser && !mParser->IsInsertionPointDefined()) {
  1792     if (mExternalScriptsBeingEvaluated) {
  1793       // Instead of implying a call to document.open(), ignore the call.
  1794       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  1795                                       NS_LITERAL_CSTRING("DOM Events"), this,
  1796                                       nsContentUtils::eDOM_PROPERTIES,
  1797                                       "DocumentWriteIgnored",
  1798                                       nullptr, 0,
  1799                                       mDocumentURI);
  1800       return NS_OK;
  1802     mParser->Terminate();
  1803     NS_ASSERTION(!mParser, "mParser should have been null'd out");
  1806   if (!mParser) {
  1807     if (mExternalScriptsBeingEvaluated) {
  1808       // Instead of implying a call to document.open(), ignore the call.
  1809       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  1810                                       NS_LITERAL_CSTRING("DOM Events"), this,
  1811                                       nsContentUtils::eDOM_PROPERTIES,
  1812                                       "DocumentWriteIgnored",
  1813                                       nullptr, 0,
  1814                                       mDocumentURI);
  1815       return NS_OK;
  1817     nsCOMPtr<nsISupports> ignored;
  1818     rv = Open(NS_LITERAL_STRING("text/html"), EmptyString(), EmptyString(), cx,
  1819               1, getter_AddRefs(ignored));
  1821     // If Open() fails, or if it didn't create a parser (as it won't
  1822     // if the user chose to not discard the current document through
  1823     // onbeforeunload), don't write anything.
  1824     if (NS_FAILED(rv) || !mParser) {
  1825       return rv;
  1827     NS_ABORT_IF_FALSE(!JS_IsExceptionPending(cx),
  1828                       "Open() succeeded but JS exception is pending");
  1831   static NS_NAMED_LITERAL_STRING(new_line, "\n");
  1833   // Save the data in cache if the write isn't from within the doc
  1834   if (mWyciwygChannel && !key) {
  1835     if (!aText.IsEmpty()) {
  1836       mWyciwygChannel->WriteToCacheEntry(aText);
  1839     if (aNewlineTerminate) {
  1840       mWyciwygChannel->WriteToCacheEntry(new_line);
  1844   ++mWriteLevel;
  1846   // This could be done with less code, but for performance reasons it
  1847   // makes sense to have the code for two separate Parse() calls here
  1848   // since the concatenation of strings costs more than we like. And
  1849   // why pay that price when we don't need to?
  1850   if (aNewlineTerminate) {
  1851     rv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
  1852       aText + new_line, key, GetContentTypeInternal(), false);
  1853   } else {
  1854     rv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
  1855       aText, key, GetContentTypeInternal(), false);
  1858   --mWriteLevel;
  1860   mTooDeepWriteRecursion = (mWriteLevel != 0 && mTooDeepWriteRecursion);
  1862   return rv;
  1865 NS_IMETHODIMP
  1866 nsHTMLDocument::Write(const nsAString& aText, JSContext *cx)
  1868   return WriteCommon(cx, aText, false);
  1871 void
  1872 nsHTMLDocument::Write(JSContext* cx, const Sequence<nsString>& aText,
  1873                       ErrorResult& rv)
  1875   WriteCommon(cx, aText, false, rv);
  1878 NS_IMETHODIMP
  1879 nsHTMLDocument::Writeln(const nsAString& aText, JSContext *cx)
  1881   return WriteCommon(cx, aText, true);
  1884 void
  1885 nsHTMLDocument::Writeln(JSContext* cx, const Sequence<nsString>& aText,
  1886                         ErrorResult& rv)
  1888   WriteCommon(cx, aText, true, rv);
  1891 bool
  1892 nsHTMLDocument::MatchNameAttribute(nsIContent* aContent, int32_t aNamespaceID,
  1893                                    nsIAtom* aAtom, void* aData)
  1895   NS_PRECONDITION(aContent, "Must have content node to work with!");
  1896   nsString* elementName = static_cast<nsString*>(aData);
  1897   return
  1898     aContent->GetNameSpaceID() == kNameSpaceID_XHTML &&
  1899     aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
  1900                           *elementName, eCaseMatters);
  1903 /* static */
  1904 void*
  1905 nsHTMLDocument::UseExistingNameString(nsINode* aRootNode, const nsString* aName)
  1907   return const_cast<nsString*>(aName);
  1910 NS_IMETHODIMP
  1911 nsHTMLDocument::GetElementsByName(const nsAString& aElementName,
  1912                                   nsIDOMNodeList** aReturn)
  1914   *aReturn = GetElementsByName(aElementName).take();
  1915   return NS_OK;
  1918 static bool MatchItems(nsIContent* aContent, int32_t aNameSpaceID, 
  1919                        nsIAtom* aAtom, void* aData)
  1921   if (!(aContent->IsElement() && aContent->AsElement()->IsHTML())) {
  1922     return false;
  1925   nsGenericHTMLElement* elem = static_cast<nsGenericHTMLElement*>(aContent);
  1926   if (!elem->HasAttr(kNameSpaceID_None, nsGkAtoms::itemscope) ||
  1927       elem->HasAttr(kNameSpaceID_None, nsGkAtoms::itemprop)) {
  1928     return false;
  1931   nsTArray<nsCOMPtr<nsIAtom> >* tokens = static_cast<nsTArray<nsCOMPtr<nsIAtom> >*>(aData);
  1932   if (tokens->IsEmpty()) {
  1933     return true;
  1936   const nsAttrValue* attr = elem->GetParsedAttr(nsGkAtoms::itemtype);
  1937   if (!attr)
  1938     return false;
  1940   for (uint32_t i = 0; i < tokens->Length(); i++) {
  1941     if (!attr->Contains(tokens->ElementAt(i), eCaseMatters)) {
  1942       return false;
  1945   return true;
  1948 static void DestroyTokens(void* aData)
  1950   nsTArray<nsCOMPtr<nsIAtom> >* tokens = static_cast<nsTArray<nsCOMPtr<nsIAtom> >*>(aData);
  1951   delete tokens;
  1954 static void* CreateTokens(nsINode* aRootNode, const nsString* types)
  1956   nsTArray<nsCOMPtr<nsIAtom> >* tokens = new nsTArray<nsCOMPtr<nsIAtom> >();
  1957   nsAString::const_iterator iter, end;
  1958   types->BeginReading(iter);
  1959   types->EndReading(end);
  1961   // skip initial whitespace
  1962   while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
  1963     ++iter;
  1966   // parse the tokens
  1967   while (iter != end) {
  1968     nsAString::const_iterator start(iter);
  1970     do {
  1971       ++iter;
  1972     } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
  1974     nsCOMPtr<nsIAtom> token = do_GetAtom(Substring(start, iter));
  1975     tokens->AppendElement(token);
  1977     // skip whitespace
  1978     while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
  1979       ++iter;
  1982   return tokens;
  1985 NS_IMETHODIMP
  1986 nsHTMLDocument::GetItems(const nsAString& types, nsIDOMNodeList** aReturn)
  1988   *aReturn = GetItems(types).take();
  1989   return NS_OK;
  1992 already_AddRefed<nsINodeList>
  1993 nsHTMLDocument::GetItems(const nsAString& aTypeNames)
  1995   return NS_GetFuncStringNodeList(this, MatchItems, DestroyTokens, CreateTokens,
  1996                                   aTypeNames);
  1999 void
  2000 nsHTMLDocument::AddedForm()
  2002   ++mNumForms;
  2005 void
  2006 nsHTMLDocument::RemovedForm()
  2008   --mNumForms;
  2011 int32_t
  2012 nsHTMLDocument::GetNumFormsSynchronous()
  2014   return mNumForms;
  2017 NS_IMETHODIMP
  2018 nsHTMLDocument::GetAlinkColor(nsAString& aAlinkColor)
  2020   aAlinkColor.Truncate();
  2022   HTMLBodyElement* body = GetBodyElement();
  2023   if (body) {
  2024     body->GetALink(aAlinkColor);
  2027   return NS_OK;
  2030 NS_IMETHODIMP
  2031 nsHTMLDocument::SetAlinkColor(const nsAString& aAlinkColor)
  2033   HTMLBodyElement* body = GetBodyElement();
  2034   if (body) {
  2035     body->SetALink(aAlinkColor);
  2038   return NS_OK;
  2041 NS_IMETHODIMP
  2042 nsHTMLDocument::GetLinkColor(nsAString& aLinkColor)
  2044   aLinkColor.Truncate();
  2046   HTMLBodyElement* body = GetBodyElement();
  2047   if (body) {
  2048     body->GetLink(aLinkColor);
  2051   return NS_OK;
  2054 NS_IMETHODIMP
  2055 nsHTMLDocument::SetLinkColor(const nsAString& aLinkColor)
  2057   HTMLBodyElement* body = GetBodyElement();
  2058   if (body) {
  2059     body->SetLink(aLinkColor);
  2062   return NS_OK;
  2065 NS_IMETHODIMP
  2066 nsHTMLDocument::GetVlinkColor(nsAString& aVlinkColor)
  2068   aVlinkColor.Truncate();
  2070   HTMLBodyElement* body = GetBodyElement();
  2071   if (body) {
  2072     body->GetVLink(aVlinkColor);
  2075   return NS_OK;
  2078 NS_IMETHODIMP
  2079 nsHTMLDocument::SetVlinkColor(const nsAString& aVlinkColor)
  2081   HTMLBodyElement* body = GetBodyElement();
  2082   if (body) {
  2083     body->SetVLink(aVlinkColor);
  2086   return NS_OK;
  2089 NS_IMETHODIMP
  2090 nsHTMLDocument::GetBgColor(nsAString& aBgColor)
  2092   aBgColor.Truncate();
  2094   HTMLBodyElement* body = GetBodyElement();
  2095   if (body) {
  2096     body->GetBgColor(aBgColor);
  2099   return NS_OK;
  2102 NS_IMETHODIMP
  2103 nsHTMLDocument::SetBgColor(const nsAString& aBgColor)
  2105   HTMLBodyElement* body = GetBodyElement();
  2106   if (body) {
  2107     body->SetBgColor(aBgColor);
  2110   return NS_OK;
  2113 NS_IMETHODIMP
  2114 nsHTMLDocument::GetFgColor(nsAString& aFgColor)
  2116   aFgColor.Truncate();
  2118   HTMLBodyElement* body = GetBodyElement();
  2119   if (body) {
  2120     body->GetText(aFgColor);
  2123   return NS_OK;
  2126 NS_IMETHODIMP
  2127 nsHTMLDocument::SetFgColor(const nsAString& aFgColor)
  2129   HTMLBodyElement* body = GetBodyElement();
  2130   if (body) {
  2131     body->SetText(aFgColor);
  2134   return NS_OK;
  2138 NS_IMETHODIMP
  2139 nsHTMLDocument::GetEmbeds(nsIDOMHTMLCollection** aEmbeds)
  2141   NS_ADDREF(*aEmbeds = Embeds());
  2142   return NS_OK;
  2145 nsIHTMLCollection*
  2146 nsHTMLDocument::Embeds()
  2148   if (!mEmbeds) {
  2149     mEmbeds = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::embed, nsGkAtoms::embed);
  2151   return mEmbeds;
  2154 NS_IMETHODIMP
  2155 nsHTMLDocument::GetSelection(nsISelection** aReturn)
  2157   ErrorResult rv;
  2158   NS_IF_ADDREF(*aReturn = GetSelection(rv));
  2159   return rv.ErrorCode();
  2162 Selection*
  2163 nsHTMLDocument::GetSelection(ErrorResult& aRv)
  2165   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetScopeObject());
  2166   if (!window) {
  2167     return nullptr;
  2170   NS_ASSERTION(window->IsInnerWindow(), "Should have inner window here!");
  2171   if (!window->IsCurrentInnerWindow()) {
  2172     return nullptr;
  2175   return static_cast<nsGlobalWindow*>(window.get())->GetSelection(aRv);
  2178 NS_IMETHODIMP
  2179 nsHTMLDocument::CaptureEvents()
  2181   WarnOnceAbout(nsIDocument::eUseOfCaptureEvents);
  2182   return NS_OK;
  2185 NS_IMETHODIMP
  2186 nsHTMLDocument::ReleaseEvents()
  2188   WarnOnceAbout(nsIDocument::eUseOfReleaseEvents);
  2189   return NS_OK;
  2192 // Mapped to document.embeds for NS4 compatibility
  2193 NS_IMETHODIMP
  2194 nsHTMLDocument::GetPlugins(nsIDOMHTMLCollection** aPlugins)
  2196   *aPlugins = nullptr;
  2198   return GetEmbeds(aPlugins);
  2201 nsIHTMLCollection*
  2202 nsHTMLDocument::Plugins()
  2204   return Embeds();
  2207 nsISupports*
  2208 nsHTMLDocument::ResolveName(const nsAString& aName, nsWrapperCache **aCache)
  2210   nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aName);
  2211   if (!entry) {
  2212     *aCache = nullptr;
  2213     return nullptr;
  2216   nsBaseContentList *list = entry->GetNameContentList();
  2217   uint32_t length = list ? list->Length() : 0;
  2219   if (length > 0) {
  2220     if (length == 1) {
  2221       // Only one element in the list, return the element instead of returning
  2222       // the list.
  2223       nsIContent *node = list->Item(0);
  2224       *aCache = node;
  2225       return node;
  2228     // The list contains more than one element, return the whole list.
  2229     *aCache = list;
  2230     return list;
  2233   // No named items were found, see if there's one registerd by id for aName.
  2234   Element *e = entry->GetIdElement();
  2236   if (e && nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(e)) {
  2237     *aCache = e;
  2238     return e;
  2241   *aCache = nullptr;
  2242   return nullptr;
  2245 void
  2246 nsHTMLDocument::NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
  2247                             JS::MutableHandle<JSObject*> aRetval,
  2248                             ErrorResult& rv)
  2250   nsWrapperCache* cache;
  2251   nsISupports* supp = ResolveName(aName, &cache);
  2252   if (!supp) {
  2253     aFound = false;
  2254     aRetval.set(nullptr);
  2255     return;
  2258   JS::Rooted<JS::Value> val(cx);
  2259   // XXXbz Should we call the (slightly misnamed, really) WrapNativeParent
  2260   // here?
  2261   if (!dom::WrapObject(cx, supp, cache, nullptr, &val)) {
  2262     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  2263     return;
  2265   aFound = true;
  2266   aRetval.set(&val.toObject());
  2269 bool
  2270 nsHTMLDocument::NameIsEnumerable(const nsAString& aName)
  2272   return true;
  2275 static PLDHashOperator
  2276 IdentifierMapEntryAddNames(nsIdentifierMapEntry* aEntry, void* aArg)
  2278   nsTArray<nsString>* aNames = static_cast<nsTArray<nsString>*>(aArg);
  2279   if (aEntry->HasNameElement() ||
  2280       aEntry->HasIdElementExposedAsHTMLDocumentProperty()) {
  2281     aNames->AppendElement(aEntry->GetKey());
  2283   return PL_DHASH_NEXT;
  2286 void
  2287 nsHTMLDocument::GetSupportedNames(unsigned, nsTArray<nsString>& aNames)
  2289   mIdentifierMap.EnumerateEntries(IdentifierMapEntryAddNames, &aNames);
  2292 //----------------------------
  2294 // forms related stuff
  2296 NS_IMETHODIMP
  2297 nsHTMLDocument::GetForms(nsIDOMHTMLCollection** aForms)
  2299   NS_ADDREF(*aForms = nsHTMLDocument::GetForms());
  2300   return NS_OK;
  2303 nsContentList*
  2304 nsHTMLDocument::GetForms()
  2306   if (!mForms) {
  2307     mForms = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::form, nsGkAtoms::form);
  2310   return mForms;
  2313 static bool MatchFormControls(nsIContent* aContent, int32_t aNamespaceID,
  2314                                 nsIAtom* aAtom, void* aData)
  2316   return aContent->IsNodeOfType(nsIContent::eHTML_FORM_CONTROL);
  2319 nsContentList*
  2320 nsHTMLDocument::GetFormControls()
  2322   if (!mFormControls) {
  2323     mFormControls = new nsContentList(this, MatchFormControls, nullptr, nullptr);
  2326   return mFormControls;
  2329 nsresult
  2330 nsHTMLDocument::CreateAndAddWyciwygChannel(void)
  2332   nsresult rv = NS_OK;
  2333   nsAutoCString url, originalSpec;
  2335   mDocumentURI->GetSpec(originalSpec);
  2337   // Generate the wyciwyg url
  2338   url = NS_LITERAL_CSTRING("wyciwyg://")
  2339       + nsPrintfCString("%d", gWyciwygSessionCnt++)
  2340       + NS_LITERAL_CSTRING("/")
  2341       + originalSpec;
  2343   nsCOMPtr<nsIURI> wcwgURI;
  2344   NS_NewURI(getter_AddRefs(wcwgURI), url);
  2346   // Create the nsIWyciwygChannel to store out-of-band
  2347   // document.write() script to cache
  2348   nsCOMPtr<nsIChannel> channel;
  2349   // Create a wyciwyg Channel
  2350   rv = NS_NewChannel(getter_AddRefs(channel), wcwgURI);
  2351   NS_ENSURE_SUCCESS(rv, rv);
  2353   mWyciwygChannel = do_QueryInterface(channel);
  2355   mWyciwygChannel->SetSecurityInfo(mSecurityInfo);
  2357   // Note: we want to treat this like a "previous document" hint so that,
  2358   // e.g. a <meta> tag in the document.write content can override it.
  2359   SetDocumentCharacterSetSource(kCharsetFromHintPrevDoc);
  2360   mWyciwygChannel->SetCharsetAndSource(kCharsetFromHintPrevDoc,
  2361                                        GetDocumentCharacterSet());
  2363   // Use our new principal
  2364   channel->SetOwner(NodePrincipal());
  2366   // Inherit load flags from the original document's channel
  2367   channel->SetLoadFlags(mLoadFlags);
  2369   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
  2371   // Use the Parent document's loadgroup to trigger load notifications
  2372   if (loadGroup && channel) {
  2373     rv = channel->SetLoadGroup(loadGroup);
  2374     NS_ENSURE_SUCCESS(rv, rv);
  2376     nsLoadFlags loadFlags = 0;
  2377     channel->GetLoadFlags(&loadFlags);
  2378     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
  2379     channel->SetLoadFlags(loadFlags);
  2381     channel->SetOriginalURI(wcwgURI);
  2383     rv = loadGroup->AddRequest(mWyciwygChannel, nullptr);
  2384     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to add request to load group.");
  2387   return rv;
  2390 nsresult
  2391 nsHTMLDocument::RemoveWyciwygChannel(void)
  2393   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
  2395   // note there can be a write request without a load group if
  2396   // this is a synchronously constructed about:blank document
  2397   if (loadGroup && mWyciwygChannel) {
  2398     mWyciwygChannel->CloseCacheEntry(NS_OK);
  2399     loadGroup->RemoveRequest(mWyciwygChannel, nullptr, NS_OK);
  2402   mWyciwygChannel = nullptr;
  2404   return NS_OK;
  2407 void *
  2408 nsHTMLDocument::GenerateParserKey(void)
  2410   if (!mScriptLoader) {
  2411     // If we don't have a script loader, then the parser probably isn't parsing
  2412     // anything anyway, so just return null.
  2413     return nullptr;
  2416   // The script loader provides us with the currently executing script element,
  2417   // which is guaranteed to be unique per script.
  2418   nsIScriptElement* script = mScriptLoader->GetCurrentParserInsertedScript();
  2419   if (script && mParser && mParser->IsScriptCreated()) {
  2420     nsCOMPtr<nsIParser> creatorParser = script->GetCreatorParser();
  2421     if (creatorParser != mParser) {
  2422       // Make scripts that aren't inserted by the active parser of this document
  2423       // participate in the context of the script that document.open()ed
  2424       // this document.
  2425       return nullptr;
  2428   return script;
  2431 /* attribute DOMString designMode; */
  2432 NS_IMETHODIMP
  2433 nsHTMLDocument::GetDesignMode(nsAString & aDesignMode)
  2435   if (HasFlag(NODE_IS_EDITABLE)) {
  2436     aDesignMode.AssignLiteral("on");
  2438   else {
  2439     aDesignMode.AssignLiteral("off");
  2441   return NS_OK;
  2444 void
  2445 nsHTMLDocument::MaybeEditingStateChanged()
  2447   if (!mPendingMaybeEditingStateChanged &&
  2448       mUpdateNestLevel == 0 && (mContentEditableCount > 0) != IsEditingOn()) {
  2449     if (nsContentUtils::IsSafeToRunScript()) {
  2450       EditingStateChanged();
  2451     } else if (!mInDestructor) {
  2452       nsContentUtils::AddScriptRunner(
  2453         NS_NewRunnableMethod(this, &nsHTMLDocument::MaybeEditingStateChanged));
  2458 void
  2459 nsHTMLDocument::EndUpdate(nsUpdateType aUpdateType)
  2461   const bool reset = !mPendingMaybeEditingStateChanged;
  2462   mPendingMaybeEditingStateChanged = true;
  2463   nsDocument::EndUpdate(aUpdateType);
  2464   if (reset) {
  2465     mPendingMaybeEditingStateChanged = false;
  2467   MaybeEditingStateChanged();
  2471 // Helper class, used below in ChangeContentEditableCount().
  2472 class DeferredContentEditableCountChangeEvent : public nsRunnable
  2474 public:
  2475   DeferredContentEditableCountChangeEvent(nsHTMLDocument *aDoc,
  2476                                           nsIContent *aElement)
  2477     : mDoc(aDoc)
  2478     , mElement(aElement)
  2482   NS_IMETHOD Run() {
  2483     if (mElement && mElement->OwnerDoc() == mDoc) {
  2484       mDoc->DeferredContentEditableCountChange(mElement);
  2486     return NS_OK;
  2489 private:
  2490   nsRefPtr<nsHTMLDocument> mDoc;
  2491   nsCOMPtr<nsIContent> mElement;
  2492 };
  2494 nsresult
  2495 nsHTMLDocument::ChangeContentEditableCount(nsIContent *aElement,
  2496                                            int32_t aChange)
  2498   NS_ASSERTION(int32_t(mContentEditableCount) + aChange >= 0,
  2499                "Trying to decrement too much.");
  2501   mContentEditableCount += aChange;
  2503   nsContentUtils::AddScriptRunner(
  2504     new DeferredContentEditableCountChangeEvent(this, aElement));
  2506   return NS_OK;
  2509 void
  2510 nsHTMLDocument::DeferredContentEditableCountChange(nsIContent *aElement)
  2512   if (mParser ||
  2513       (mUpdateNestLevel > 0 && (mContentEditableCount > 0) != IsEditingOn())) {
  2514     return;
  2517   EditingState oldState = mEditingState;
  2519   nsresult rv = EditingStateChanged();
  2520   NS_ENSURE_SUCCESS_VOID(rv);
  2522   if (oldState == mEditingState && mEditingState == eContentEditable) {
  2523     // We just changed the contentEditable state of a node, we need to reset
  2524     // the spellchecking state of that node.
  2525     nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
  2526     if (node) {
  2527       nsPIDOMWindow *window = GetWindow();
  2528       if (!window)
  2529         return;
  2531       nsIDocShell *docshell = window->GetDocShell();
  2532       if (!docshell)
  2533         return;
  2535       nsCOMPtr<nsIEditor> editor;
  2536       docshell->GetEditor(getter_AddRefs(editor));
  2537       if (editor) {
  2538         nsRefPtr<nsRange> range = new nsRange(aElement);
  2539         rv = range->SelectNode(node);
  2540         if (NS_FAILED(rv)) {
  2541           // The node might be detached from the document at this point,
  2542           // which would cause this call to fail.  In this case, we can
  2543           // safely ignore the contenteditable count change.
  2544           return;
  2547         nsCOMPtr<nsIInlineSpellChecker> spellChecker;
  2548         rv = editor->GetInlineSpellChecker(false,
  2549                                            getter_AddRefs(spellChecker));
  2550         NS_ENSURE_SUCCESS_VOID(rv);
  2552         if (spellChecker) {
  2553           rv = spellChecker->SpellCheckRange(range);
  2560 HTMLAllCollection*
  2561 nsHTMLDocument::All()
  2563   if (!mAll) {
  2564     mAll = new HTMLAllCollection(this);
  2566   return mAll;
  2569 void
  2570 nsHTMLDocument::GetAll(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval,
  2571                        ErrorResult& aRv)
  2573   aRetval.set(All()->GetObject(aCx, aRv));
  2576 static void
  2577 NotifyEditableStateChange(nsINode *aNode, nsIDocument *aDocument)
  2579   for (nsIContent* child = aNode->GetFirstChild();
  2580        child;
  2581        child = child->GetNextSibling()) {
  2582     if (child->IsElement()) {
  2583       child->AsElement()->UpdateState(true);
  2585     NotifyEditableStateChange(child, aDocument);
  2589 void
  2590 nsHTMLDocument::TearingDownEditor(nsIEditor *aEditor)
  2592   if (IsEditingOn()) {
  2593     EditingState oldState = mEditingState;
  2594     mEditingState = eTearingDown;
  2596     nsCOMPtr<nsIPresShell> presShell = GetShell();
  2597     if (!presShell)
  2598       return;
  2600     nsCOMArray<nsIStyleSheet> agentSheets;
  2601     presShell->GetAgentStyleSheets(agentSheets);
  2603     RemoveFromAgentSheets(agentSheets, NS_LITERAL_STRING("resource://gre/res/contenteditable.css"));
  2604     if (oldState == eDesignMode)
  2605       RemoveFromAgentSheets(agentSheets, NS_LITERAL_STRING("resource://gre/res/designmode.css"));
  2607     presShell->SetAgentStyleSheets(agentSheets);
  2609     presShell->ReconstructStyleData();
  2613 nsresult
  2614 nsHTMLDocument::TurnEditingOff()
  2616   NS_ASSERTION(mEditingState != eOff, "Editing is already off.");
  2618   nsPIDOMWindow *window = GetWindow();
  2619   if (!window)
  2620     return NS_ERROR_FAILURE;
  2622   nsIDocShell *docshell = window->GetDocShell();
  2623   if (!docshell)
  2624     return NS_ERROR_FAILURE;
  2626   nsresult rv;
  2627   nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell, &rv);
  2628   NS_ENSURE_SUCCESS(rv, rv);
  2630   // turn editing off
  2631   rv = editSession->TearDownEditorOnWindow(window);
  2632   NS_ENSURE_SUCCESS(rv, rv);
  2634   mEditingState = eOff;
  2636   return NS_OK;
  2639 static bool HasPresShell(nsPIDOMWindow *aWindow)
  2641   nsIDocShell *docShell = aWindow->GetDocShell();
  2642   if (!docShell)
  2643     return false;
  2644   return docShell->GetPresShell() != nullptr;
  2647 nsresult
  2648 nsHTMLDocument::SetEditingState(EditingState aState)
  2650   mEditingState = aState;
  2651   return NS_OK;
  2654 nsresult
  2655 nsHTMLDocument::EditingStateChanged()
  2657   if (mRemovedFromDocShell) {
  2658     return NS_OK;
  2661   if (mEditingState == eSettingUp || mEditingState == eTearingDown) {
  2662     // XXX We shouldn't recurse.
  2663     return NS_OK;
  2666   bool designMode = HasFlag(NODE_IS_EDITABLE);
  2667   EditingState newState = designMode ? eDesignMode :
  2668                           (mContentEditableCount > 0 ? eContentEditable : eOff);
  2669   if (mEditingState == newState) {
  2670     // No changes in editing mode.
  2671     return NS_OK;
  2674   if (newState == eOff) {
  2675     // Editing is being turned off.
  2676     nsAutoScriptBlocker scriptBlocker;
  2677     NotifyEditableStateChange(this, this);
  2678     return TurnEditingOff();
  2681   // Flush out style changes on our _parent_ document, if any, so that
  2682   // our check for a presshell won't get stale information.
  2683   if (mParentDocument) {
  2684     mParentDocument->FlushPendingNotifications(Flush_Style);
  2687   // get editing session, make sure this is a strong reference so the
  2688   // window can't get deleted during the rest of this call.
  2689   nsCOMPtr<nsPIDOMWindow> window = GetWindow();
  2690   if (!window)
  2691     return NS_ERROR_FAILURE;
  2693   nsIDocShell *docshell = window->GetDocShell();
  2694   if (!docshell)
  2695     return NS_ERROR_FAILURE;
  2697   nsresult rv;
  2698   nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell, &rv);
  2699   NS_ENSURE_SUCCESS(rv, rv);
  2701   nsCOMPtr<nsIEditor> existingEditor;
  2702   editSession->GetEditorForWindow(window, getter_AddRefs(existingEditor));
  2703   if (existingEditor) {
  2704     // We might already have an editor if it was set up for mail, let's see
  2705     // if this is actually the case.
  2706     nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(existingEditor);
  2707     NS_ABORT_IF_FALSE(htmlEditor, "If we have an editor, it must be an HTML editor");
  2708     uint32_t flags = 0;
  2709     existingEditor->GetFlags(&flags);
  2710     if (flags & nsIPlaintextEditor::eEditorMailMask) {
  2711       // We already have a mail editor, then we should not attempt to create
  2712       // another one.
  2713       return NS_OK;
  2717   if (!HasPresShell(window)) {
  2718     // We should not make the window editable or setup its editor.
  2719     // It's probably style=display:none.
  2720     return NS_OK;
  2723   bool makeWindowEditable = mEditingState == eOff;
  2724   bool updateState = false;
  2725   bool spellRecheckAll = false;
  2726   nsCOMPtr<nsIEditor> editor;
  2729     EditingState oldState = mEditingState;
  2730     nsAutoEditingState push(this, eSettingUp);
  2732     if (makeWindowEditable) {
  2733       // Editing is being turned on (through designMode or contentEditable)
  2734       // Turn on editor.
  2735       // XXX This can cause flushing which can change the editing state, so make
  2736       //     sure to avoid recursing.
  2737       rv = editSession->MakeWindowEditable(window, "html", false, false,
  2738                                            true);
  2739       NS_ENSURE_SUCCESS(rv, rv);
  2742     // XXX Need to call TearDownEditorOnWindow for all failures.
  2743     docshell->GetEditor(getter_AddRefs(editor));
  2744     if (!editor)
  2745       return NS_ERROR_FAILURE;
  2747     nsCOMPtr<nsIPresShell> presShell = GetShell();
  2748     NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
  2750     // If we're entering the design mode, put the selection at the beginning of
  2751     // the document for compatibility reasons.
  2752     if (designMode && oldState == eOff) {
  2753       editor->BeginningOfDocument();
  2756     nsCOMArray<nsIStyleSheet> agentSheets;
  2757     rv = presShell->GetAgentStyleSheets(agentSheets);
  2758     NS_ENSURE_SUCCESS(rv, rv);
  2760     nsCOMPtr<nsIURI> uri;
  2761     rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("resource://gre/res/contenteditable.css"));
  2762     NS_ENSURE_SUCCESS(rv, rv);
  2764     nsRefPtr<nsCSSStyleSheet> sheet;
  2765     rv = LoadChromeSheetSync(uri, true, getter_AddRefs(sheet));
  2766     NS_ENSURE_TRUE(sheet, rv);
  2768     bool result = agentSheets.AppendObject(sheet);
  2769     NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
  2771     // Should we update the editable state of all the nodes in the document? We
  2772     // need to do this when the designMode value changes, as that overrides
  2773     // specific states on the elements.
  2774     if (designMode) {
  2775       // designMode is being turned on (overrides contentEditable).
  2776       rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("resource://gre/res/designmode.css"));
  2777       NS_ENSURE_SUCCESS(rv, rv);
  2779       rv = LoadChromeSheetSync(uri, true, getter_AddRefs(sheet));
  2780       NS_ENSURE_TRUE(sheet, rv);
  2782       result = agentSheets.AppendObject(sheet);
  2783       NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
  2785       updateState = true;
  2786       spellRecheckAll = oldState == eContentEditable;
  2788     else if (oldState == eDesignMode) {
  2789       // designMode is being turned off (contentEditable is still on).
  2790       RemoveFromAgentSheets(agentSheets, NS_LITERAL_STRING("resource://gre/res/designmode.css"));
  2792       updateState = true;
  2795     rv = presShell->SetAgentStyleSheets(agentSheets);
  2796     NS_ENSURE_SUCCESS(rv, rv);
  2798     presShell->ReconstructStyleData();
  2801   mEditingState = newState;
  2803   if (makeWindowEditable) {
  2804     // Set the editor to not insert br's on return when in p
  2805     // elements by default.
  2806     // XXX Do we only want to do this for designMode?
  2807     bool unused;
  2808     rv = ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), false,
  2809                      NS_LITERAL_STRING("false"), &unused);
  2811     if (NS_FAILED(rv)) {
  2812       // Editor setup failed. Editing is not on after all.
  2813       // XXX Should we reset the editable flag on nodes?
  2814       editSession->TearDownEditorOnWindow(window);
  2815       mEditingState = eOff;
  2817       return rv;
  2821   if (updateState) {
  2822     nsAutoScriptBlocker scriptBlocker;
  2823     NotifyEditableStateChange(this, this);
  2826   // Resync the editor's spellcheck state.
  2827   if (spellRecheckAll) {
  2828     nsCOMPtr<nsISelectionController> selcon;
  2829     nsresult rv = editor->GetSelectionController(getter_AddRefs(selcon));
  2830     NS_ENSURE_SUCCESS(rv, rv); 
  2832     nsCOMPtr<nsISelection> spellCheckSelection;
  2833     rv = selcon->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
  2834                               getter_AddRefs(spellCheckSelection));
  2835     if (NS_SUCCEEDED(rv)) {
  2836       spellCheckSelection->RemoveAllRanges();
  2839   editor->SyncRealTimeSpell();
  2841   return NS_OK;
  2844 NS_IMETHODIMP
  2845 nsHTMLDocument::SetDesignMode(const nsAString & aDesignMode)
  2847   ErrorResult rv;
  2848   SetDesignMode(aDesignMode, rv);
  2849   return rv.ErrorCode();
  2852 void
  2853 nsHTMLDocument::SetDesignMode(const nsAString& aDesignMode, ErrorResult& rv)
  2855   if (!nsContentUtils::IsCallerChrome()) {
  2856     nsCOMPtr<nsIPrincipal> subject;
  2857     nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
  2858     rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
  2859     if (rv.Failed()) {
  2860       return;
  2862     if (subject) {
  2863       bool subsumes;
  2864       rv = subject->Subsumes(NodePrincipal(), &subsumes);
  2865       if (rv.Failed()) {
  2866         return;
  2869       if (!subsumes) {
  2870         rv.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED);
  2871         return;
  2876   bool editableMode = HasFlag(NODE_IS_EDITABLE);
  2877   if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
  2878     SetEditableFlag(!editableMode);
  2880     rv = EditingStateChanged();
  2884 nsresult
  2885 nsHTMLDocument::GetMidasCommandManager(nsICommandManager** aCmdMgr)
  2887   // initialize return value
  2888   NS_ENSURE_ARG_POINTER(aCmdMgr);
  2890   // check if we have it cached
  2891   if (mMidasCommandManager) {
  2892     NS_ADDREF(*aCmdMgr = mMidasCommandManager);
  2893     return NS_OK;
  2896   *aCmdMgr = nullptr;
  2898   nsPIDOMWindow *window = GetWindow();
  2899   if (!window)
  2900     return NS_ERROR_FAILURE;
  2902   nsIDocShell *docshell = window->GetDocShell();
  2903   if (!docshell)
  2904     return NS_ERROR_FAILURE;
  2906   mMidasCommandManager = do_GetInterface(docshell);
  2907   if (!mMidasCommandManager)
  2908     return NS_ERROR_FAILURE;
  2910   NS_ADDREF(*aCmdMgr = mMidasCommandManager);
  2912   return NS_OK;
  2916 struct MidasCommand {
  2917   const char*  incomingCommandString;
  2918   const char*  internalCommandString;
  2919   const char*  internalParamString;
  2920   bool useNewParam;
  2921   bool convertToBoolean;
  2922 };
  2924 static const struct MidasCommand gMidasCommandTable[] = {
  2925   { "bold",          "cmd_bold",            "", true,  false },
  2926   { "italic",        "cmd_italic",          "", true,  false },
  2927   { "underline",     "cmd_underline",       "", true,  false },
  2928   { "strikethrough", "cmd_strikethrough",   "", true,  false },
  2929   { "subscript",     "cmd_subscript",       "", true,  false },
  2930   { "superscript",   "cmd_superscript",     "", true,  false },
  2931   { "cut",           "cmd_cut",             "", true,  false },
  2932   { "copy",          "cmd_copy",            "", true,  false },
  2933   { "paste",         "cmd_paste",           "", true,  false },
  2934   { "delete",        "cmd_deleteCharBackward", "", true,  false },
  2935   { "forwarddelete", "cmd_deleteCharForward", "", true,  false },
  2936   { "selectall",     "cmd_selectAll",       "", true,  false },
  2937   { "undo",          "cmd_undo",            "", true,  false },
  2938   { "redo",          "cmd_redo",            "", true,  false },
  2939   { "indent",        "cmd_indent",          "", true,  false },
  2940   { "outdent",       "cmd_outdent",         "", true,  false },
  2941   { "backcolor",     "cmd_highlight",       "", false, false },
  2942   { "forecolor",     "cmd_fontColor",       "", false, false },
  2943   { "hilitecolor",   "cmd_highlight",       "", false, false },
  2944   { "fontname",      "cmd_fontFace",        "", false, false },
  2945   { "fontsize",      "cmd_fontSize",        "", false, false },
  2946   { "increasefontsize", "cmd_increaseFont", "", false, false },
  2947   { "decreasefontsize", "cmd_decreaseFont", "", false, false },
  2948   { "inserthorizontalrule", "cmd_insertHR", "", true,  false },
  2949   { "createlink",    "cmd_insertLinkNoUI",  "", false, false },
  2950   { "insertimage",   "cmd_insertImageNoUI", "", false, false },
  2951   { "inserthtml",    "cmd_insertHTML",      "", false, false },
  2952   { "inserttext",    "cmd_insertText",      "", false, false },
  2953   { "gethtml",       "cmd_getContents",     "", false, false },
  2954   { "justifyleft",   "cmd_align",       "left", true,  false },
  2955   { "justifyright",  "cmd_align",      "right", true,  false },
  2956   { "justifycenter", "cmd_align",     "center", true,  false },
  2957   { "justifyfull",   "cmd_align",    "justify", true,  false },
  2958   { "removeformat",  "cmd_removeStyles",    "", true,  false },
  2959   { "unlink",        "cmd_removeLinks",     "", true,  false },
  2960   { "insertorderedlist",   "cmd_ol",        "", true,  false },
  2961   { "insertunorderedlist", "cmd_ul",        "", true,  false },
  2962   { "insertparagraph", "cmd_paragraphState", "p", true,  false },
  2963   { "formatblock",   "cmd_paragraphState",  "", false, false },
  2964   { "heading",       "cmd_paragraphState",  "", false, false },
  2965   { "styleWithCSS",  "cmd_setDocumentUseCSS", "", false, true },
  2966   { "contentReadOnly", "cmd_setDocumentReadOnly", "", false, true },
  2967   { "insertBrOnReturn", "cmd_insertBrOnReturn", "", false, true },
  2968   { "enableObjectResizing", "cmd_enableObjectResizing", "", false, true },
  2969   { "enableInlineTableEditing", "cmd_enableInlineTableEditing", "", false, true },
  2970 #if 0
  2971 // no editor support to remove alignments right now
  2972   { "justifynone",   "cmd_align",           "", true,  false },
  2974 // the following will need special review before being turned on
  2975   { "saveas",        "cmd_saveAs",          "", true,  false },
  2976   { "print",         "cmd_print",           "", true,  false },
  2977 #endif
  2978   { nullptr, nullptr, nullptr, false, false }
  2979 };
  2981 #define MidasCommandCount ((sizeof(gMidasCommandTable) / sizeof(struct MidasCommand)) - 1)
  2983 static const char* const gBlocks[] = {
  2984   "ADDRESS",
  2985   "BLOCKQUOTE",
  2986   "DD",
  2987   "DIV",
  2988   "DL",
  2989   "DT",
  2990   "H1",
  2991   "H2",
  2992   "H3",
  2993   "H4",
  2994   "H5",
  2995   "H6",
  2996   "P",
  2997   "PRE"
  2998 };
  3000 static bool
  3001 ConvertToMidasInternalCommandInner(const nsAString& inCommandID,
  3002                                    const nsAString& inParam,
  3003                                    nsACString& outCommandID,
  3004                                    nsACString& outParam,
  3005                                    bool& outIsBoolean,
  3006                                    bool& outBooleanValue,
  3007                                    bool aIgnoreParams)
  3009   NS_ConvertUTF16toUTF8 convertedCommandID(inCommandID);
  3011   // Hack to support old boolean commands that were backwards (see bug 301490).
  3012   bool invertBool = false;
  3013   if (convertedCommandID.LowerCaseEqualsLiteral("usecss")) {
  3014     convertedCommandID.Assign("styleWithCSS");
  3015     invertBool = true;
  3016   } else if (convertedCommandID.LowerCaseEqualsLiteral("readonly")) {
  3017     convertedCommandID.Assign("contentReadOnly");
  3018     invertBool = true;
  3021   uint32_t i;
  3022   bool found = false;
  3023   for (i = 0; i < MidasCommandCount; ++i) {
  3024     if (convertedCommandID.Equals(gMidasCommandTable[i].incomingCommandString,
  3025                                   nsCaseInsensitiveCStringComparator())) {
  3026       found = true;
  3027       break;
  3031   if (!found) {
  3032     // reset results if the command is not found in our table
  3033     outCommandID.SetLength(0);
  3034     outParam.SetLength(0);
  3035     outIsBoolean = false;
  3036     return false;
  3039   // set outCommandID (what we use internally)
  3040   outCommandID.Assign(gMidasCommandTable[i].internalCommandString);
  3042   // set outParam & outIsBoolean based on flags from the table
  3043   outIsBoolean = gMidasCommandTable[i].convertToBoolean;
  3045   if (aIgnoreParams) {
  3046     // No further work to do
  3047     return true;
  3050   if (gMidasCommandTable[i].useNewParam) {
  3051     // Just have to copy it, no checking
  3052     outParam.Assign(gMidasCommandTable[i].internalParamString);
  3053     return true;
  3056   // handle checking of param passed in
  3057   if (outIsBoolean) {
  3058     // If this is a boolean value and it's not explicitly false (e.g. no value)
  3059     // we default to "true". For old backwards commands we invert the check (see
  3060     // bug 301490).
  3061     if (invertBool) {
  3062       outBooleanValue = inParam.LowerCaseEqualsLiteral("false");
  3063     } else {
  3064       outBooleanValue = !inParam.LowerCaseEqualsLiteral("false");
  3066     outParam.Truncate();
  3068     return true;
  3071   // String parameter -- see if we need to convert it (necessary for
  3072   // cmd_paragraphState and cmd_fontSize)
  3073   if (outCommandID.EqualsLiteral("cmd_paragraphState")) {
  3074     const char16_t* start = inParam.BeginReading();
  3075     const char16_t* end = inParam.EndReading();
  3076     if (start != end && *start == '<' && *(end - 1) == '>') {
  3077       ++start;
  3078       --end;
  3081     NS_ConvertUTF16toUTF8 convertedParam(Substring(start, end));
  3082     uint32_t j;
  3083     for (j = 0; j < ArrayLength(gBlocks); ++j) {
  3084       if (convertedParam.Equals(gBlocks[j],
  3085                                 nsCaseInsensitiveCStringComparator())) {
  3086         outParam.Assign(gBlocks[j]);
  3087         break;
  3091     if (j == ArrayLength(gBlocks)) {
  3092       outParam.Truncate();
  3094   } else if (outCommandID.EqualsLiteral("cmd_fontSize")) {
  3095     // Per editing spec as of April 23, 2012, we need to reject the value if
  3096     // it's not a valid floating-point number surrounded by optional whitespace.
  3097     // Otherwise, we parse it as a legacy font size.  For now, we just parse as
  3098     // a legacy font size regardless (matching WebKit) -- bug 747879.
  3099     outParam.Truncate();
  3100     int32_t size = nsContentUtils::ParseLegacyFontSize(inParam);
  3101     if (size) {
  3102       outParam.AppendInt(size);
  3104   } else {
  3105     CopyUTF16toUTF8(inParam, outParam);
  3108   return true;
  3111 static bool
  3112 ConvertToMidasInternalCommand(const nsAString & inCommandID,
  3113                               const nsAString & inParam,
  3114                               nsACString& outCommandID,
  3115                               nsACString& outParam,
  3116                               bool& outIsBoolean,
  3117                               bool& outBooleanValue)
  3119   return ConvertToMidasInternalCommandInner(inCommandID, inParam, outCommandID,
  3120                                             outParam, outIsBoolean,
  3121                                             outBooleanValue, false);
  3124 static bool
  3125 ConvertToMidasInternalCommand(const nsAString & inCommandID,
  3126                               nsACString& outCommandID)
  3128   nsAutoCString dummyCString;
  3129   nsAutoString dummyString;
  3130   bool dummyBool;
  3131   return ConvertToMidasInternalCommandInner(inCommandID, dummyString,
  3132                                             outCommandID, dummyCString,
  3133                                             dummyBool, dummyBool, true);
  3136 /* TODO: don't let this call do anything if the page is not done loading */
  3137 /* boolean execCommand(in DOMString commandID, in boolean doShowUI,
  3138                                                in DOMString value); */
  3139 NS_IMETHODIMP
  3140 nsHTMLDocument::ExecCommand(const nsAString& commandID,
  3141                             bool doShowUI,
  3142                             const nsAString& value,
  3143                             bool* _retval)
  3145   ErrorResult rv;
  3146   *_retval = ExecCommand(commandID, doShowUI, value, rv);
  3147   return rv.ErrorCode();
  3150 bool
  3151 nsHTMLDocument::ExecCommand(const nsAString& commandID,
  3152                             bool doShowUI,
  3153                             const nsAString& value,
  3154                             ErrorResult& rv)
  3156   //  for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
  3157   //  this might add some ugly JS dependencies?
  3159   nsAutoCString cmdToDispatch, paramStr;
  3160   bool isBool, boolVal;
  3161   if (!ConvertToMidasInternalCommand(commandID, value,
  3162                                      cmdToDispatch, paramStr,
  3163                                      isBool, boolVal)) {
  3164     return false;
  3167   // if editing is not on, bail
  3168   if (!IsEditingOnAfterFlush()) {
  3169     rv.Throw(NS_ERROR_FAILURE);
  3170     return false;
  3173   // if they are requesting UI from us, let's fail since we have no UI
  3174   if (doShowUI) {
  3175     return false;
  3178   if (commandID.LowerCaseEqualsLiteral("gethtml")) {
  3179     rv.Throw(NS_ERROR_FAILURE);
  3180     return false;
  3183   bool restricted = commandID.LowerCaseEqualsLiteral("cut") ||
  3184                     commandID.LowerCaseEqualsLiteral("copy")||
  3185                     commandID.LowerCaseEqualsLiteral("paste");
  3186   if (restricted && !nsContentUtils::IsCallerChrome()) {
  3187     rv = NS_ERROR_DOM_SECURITY_ERR;
  3188     return false;
  3191   // get command manager and dispatch command to our window if it's acceptable
  3192   nsCOMPtr<nsICommandManager> cmdMgr;
  3193   GetMidasCommandManager(getter_AddRefs(cmdMgr));
  3194   if (!cmdMgr) {
  3195     rv.Throw(NS_ERROR_FAILURE);
  3196     return false;
  3199   nsIDOMWindow* window = GetWindow();
  3200   if (!window) {
  3201     rv.Throw(NS_ERROR_FAILURE);
  3202     return false;
  3205   if ((cmdToDispatch.EqualsLiteral("cmd_fontSize") ||
  3206        cmdToDispatch.EqualsLiteral("cmd_insertImageNoUI") ||
  3207        cmdToDispatch.EqualsLiteral("cmd_insertLinkNoUI") ||
  3208        cmdToDispatch.EqualsLiteral("cmd_paragraphState")) &&
  3209       paramStr.IsEmpty()) {
  3210     // Invalid value, return false
  3211     return false;
  3214   // Return false for disabled commands (bug 760052)
  3215   bool enabled = false;
  3216   cmdMgr->IsCommandEnabled(cmdToDispatch.get(), window, &enabled);
  3217   if (!enabled) {
  3218     return false;
  3221   if (!isBool && paramStr.IsEmpty()) {
  3222     rv = cmdMgr->DoCommand(cmdToDispatch.get(), nullptr, window);
  3223   } else {
  3224     // we have a command that requires a parameter, create params
  3225     nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
  3226                                             NS_COMMAND_PARAMS_CONTRACTID);
  3227     if (!cmdParams) {
  3228       rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  3229       return false;
  3232     if (isBool) {
  3233       rv = cmdParams->SetBooleanValue("state_attribute", boolVal);
  3234     } else if (cmdToDispatch.EqualsLiteral("cmd_fontFace")) {
  3235       rv = cmdParams->SetStringValue("state_attribute", value);
  3236     } else if (cmdToDispatch.EqualsLiteral("cmd_insertHTML") ||
  3237                cmdToDispatch.EqualsLiteral("cmd_insertText")) {
  3238       rv = cmdParams->SetStringValue("state_data", value);
  3239     } else {
  3240       rv = cmdParams->SetCStringValue("state_attribute", paramStr.get());
  3242     if (rv.Failed()) {
  3243       return false;
  3245     rv = cmdMgr->DoCommand(cmdToDispatch.get(), cmdParams, window);
  3248   return !rv.Failed();
  3251 /* boolean queryCommandEnabled(in DOMString commandID); */
  3252 NS_IMETHODIMP
  3253 nsHTMLDocument::QueryCommandEnabled(const nsAString& commandID,
  3254                                     bool* _retval)
  3256   ErrorResult rv;
  3257   *_retval = QueryCommandEnabled(commandID, rv);
  3258   return rv.ErrorCode();
  3261 bool
  3262 nsHTMLDocument::QueryCommandEnabled(const nsAString& commandID, ErrorResult& rv)
  3264   nsAutoCString cmdToDispatch;
  3265   if (!ConvertToMidasInternalCommand(commandID, cmdToDispatch)) {
  3266     return false;
  3269   // if editing is not on, bail
  3270   if (!IsEditingOnAfterFlush()) {
  3271     rv.Throw(NS_ERROR_FAILURE);
  3272     return false;
  3275   // get command manager and dispatch command to our window if it's acceptable
  3276   nsCOMPtr<nsICommandManager> cmdMgr;
  3277   GetMidasCommandManager(getter_AddRefs(cmdMgr));
  3278   if (!cmdMgr) {
  3279     rv.Throw(NS_ERROR_FAILURE);
  3280     return false;
  3283   nsIDOMWindow* window = GetWindow();
  3284   if (!window) {
  3285     rv.Throw(NS_ERROR_FAILURE);
  3286     return false;
  3289   bool retval;
  3290   rv = cmdMgr->IsCommandEnabled(cmdToDispatch.get(), window, &retval);
  3291   return retval;
  3294 /* boolean queryCommandIndeterm (in DOMString commandID); */
  3295 NS_IMETHODIMP
  3296 nsHTMLDocument::QueryCommandIndeterm(const nsAString & commandID,
  3297                                      bool *_retval)
  3299   ErrorResult rv;
  3300   *_retval = QueryCommandIndeterm(commandID, rv);
  3301   return rv.ErrorCode();
  3304 bool
  3305 nsHTMLDocument::QueryCommandIndeterm(const nsAString& commandID, ErrorResult& rv)
  3307   nsAutoCString cmdToDispatch;
  3308   if (!ConvertToMidasInternalCommand(commandID, cmdToDispatch)) {
  3309     return false;
  3312   // if editing is not on, bail
  3313   if (!IsEditingOnAfterFlush()) {
  3314     rv.Throw(NS_ERROR_FAILURE);
  3315     return false;
  3318   // get command manager and dispatch command to our window if it's acceptable
  3319   nsCOMPtr<nsICommandManager> cmdMgr;
  3320   GetMidasCommandManager(getter_AddRefs(cmdMgr));
  3321   if (!cmdMgr) {
  3322     rv.Throw(NS_ERROR_FAILURE);
  3323     return false;
  3326   nsIDOMWindow* window = GetWindow();
  3327   if (!window) {
  3328     rv.Throw(NS_ERROR_FAILURE);
  3329     return false;
  3332   nsresult res;
  3333   nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
  3334                                            NS_COMMAND_PARAMS_CONTRACTID, &res);
  3335   if (NS_FAILED(res)) {
  3336     rv.Throw(res);
  3337     return false;
  3340   rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, cmdParams);
  3341   if (rv.Failed()) {
  3342     return false;
  3345   // If command does not have a state_mixed value, this call fails and sets
  3346   // retval to false.  This is fine -- we want to return false in that case
  3347   // anyway (bug 738385), so we just don't throw regardless.
  3348   bool retval = false;
  3349   cmdParams->GetBooleanValue("state_mixed", &retval);
  3350   return retval;
  3353 /* boolean queryCommandState(in DOMString commandID); */
  3354 NS_IMETHODIMP
  3355 nsHTMLDocument::QueryCommandState(const nsAString & commandID, bool *_retval)
  3357   ErrorResult rv;
  3358   *_retval = QueryCommandState(commandID, rv);
  3359   return rv.ErrorCode();
  3362 bool
  3363 nsHTMLDocument::QueryCommandState(const nsAString& commandID, ErrorResult& rv)
  3365   nsAutoCString cmdToDispatch, paramToCheck;
  3366   bool dummy, dummy2;
  3367   if (!ConvertToMidasInternalCommand(commandID, commandID,
  3368                                      cmdToDispatch, paramToCheck,
  3369                                      dummy, dummy2)) {
  3370     return false;
  3373   // if editing is not on, bail
  3374   if (!IsEditingOnAfterFlush()) {
  3375     rv.Throw(NS_ERROR_FAILURE);
  3376     return false;
  3379   // get command manager and dispatch command to our window if it's acceptable
  3380   nsCOMPtr<nsICommandManager> cmdMgr;
  3381   GetMidasCommandManager(getter_AddRefs(cmdMgr));
  3382   if (!cmdMgr) {
  3383     rv.Throw(NS_ERROR_FAILURE);
  3384     return false;
  3387   nsIDOMWindow* window = GetWindow();
  3388   if (!window) {
  3389     rv.Throw(NS_ERROR_FAILURE);
  3390     return false;
  3393   if (commandID.LowerCaseEqualsLiteral("usecss")) {
  3394     // Per spec, state is supported for styleWithCSS but not useCSS, so we just
  3395     // return false always.
  3396     return false;
  3399   nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
  3400                                            NS_COMMAND_PARAMS_CONTRACTID);
  3401   if (!cmdParams) {
  3402     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  3403     return false;
  3406   rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, cmdParams);
  3407   if (rv.Failed()) {
  3408     return false;
  3411   // handle alignment as a special case (possibly other commands too?)
  3412   // Alignment is special because the external api is individual
  3413   // commands but internally we use cmd_align with different
  3414   // parameters.  When getting the state of this command, we need to
  3415   // return the boolean for this particular alignment rather than the
  3416   // string of 'which alignment is this?'
  3417   if (cmdToDispatch.EqualsLiteral("cmd_align")) {
  3418     char * actualAlignmentType = nullptr;
  3419     rv = cmdParams->GetCStringValue("state_attribute", &actualAlignmentType);
  3420     bool retval = false;
  3421     if (!rv.Failed() && actualAlignmentType && actualAlignmentType[0]) {
  3422       retval = paramToCheck.Equals(actualAlignmentType);
  3424     if (actualAlignmentType) {
  3425       nsMemory::Free(actualAlignmentType);
  3427     return retval;
  3430   // If command does not have a state_all value, this call fails and sets
  3431   // retval to false.  This is fine -- we want to return false in that case
  3432   // anyway (bug 738385), so we just succeed and return false regardless.
  3433   bool retval = false;
  3434   cmdParams->GetBooleanValue("state_all", &retval);
  3435   return retval;
  3438 /* boolean queryCommandSupported(in DOMString commandID); */
  3439 NS_IMETHODIMP
  3440 nsHTMLDocument::QueryCommandSupported(const nsAString & commandID,
  3441                                       bool *_retval)
  3443   *_retval = QueryCommandSupported(commandID);
  3444   return NS_OK;
  3447 bool
  3448 nsHTMLDocument::QueryCommandSupported(const nsAString& commandID)
  3450   // commandID is supported if it can be converted to a Midas command
  3451   nsAutoCString cmdToDispatch;
  3452   return ConvertToMidasInternalCommand(commandID, cmdToDispatch);
  3455 /* DOMString queryCommandValue(in DOMString commandID); */
  3456 NS_IMETHODIMP
  3457 nsHTMLDocument::QueryCommandValue(const nsAString & commandID,
  3458                                   nsAString &_retval)
  3460   ErrorResult rv;
  3461   QueryCommandValue(commandID, _retval, rv);
  3462   return rv.ErrorCode();
  3465 void
  3466 nsHTMLDocument::QueryCommandValue(const nsAString& commandID,
  3467                                   nsAString& aValue,
  3468                                   ErrorResult& rv)
  3470   aValue.Truncate();
  3472   nsAutoCString cmdToDispatch, paramStr;
  3473   if (!ConvertToMidasInternalCommand(commandID, cmdToDispatch)) {
  3474     // Return empty string
  3475     return;
  3478   // if editing is not on, bail
  3479   if (!IsEditingOnAfterFlush()) {
  3480     rv.Throw(NS_ERROR_FAILURE);
  3481     return;
  3484   // get command manager and dispatch command to our window if it's acceptable
  3485   nsCOMPtr<nsICommandManager> cmdMgr;
  3486   GetMidasCommandManager(getter_AddRefs(cmdMgr));
  3487   if (!cmdMgr) {
  3488     rv.Throw(NS_ERROR_FAILURE);
  3489     return;
  3492   nsIDOMWindow* window = GetWindow();
  3493   if (!window) {
  3494     rv.Throw(NS_ERROR_FAILURE);
  3495     return;
  3498   // create params
  3499   nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
  3500                                            NS_COMMAND_PARAMS_CONTRACTID);
  3501   if (!cmdParams) {
  3502     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  3503     return;
  3506   // this is a special command since we are calling DoCommand rather than
  3507   // GetCommandState like the other commands
  3508   if (cmdToDispatch.EqualsLiteral("cmd_getContents")) {
  3509     rv = cmdParams->SetBooleanValue("selection_only", true);
  3510     if (rv.Failed()) {
  3511       return;
  3513     rv = cmdParams->SetCStringValue("format", "text/html");
  3514     if (rv.Failed()) {
  3515       return;
  3517     rv = cmdMgr->DoCommand(cmdToDispatch.get(), cmdParams, window);
  3518     if (rv.Failed()) {
  3519       return;
  3521     rv = cmdParams->GetStringValue("result", aValue);
  3522     return;
  3525   rv = cmdParams->SetCStringValue("state_attribute", paramStr.get());
  3526   if (rv.Failed()) {
  3527     return;
  3530   rv = cmdMgr->GetCommandState(cmdToDispatch.get(), window, cmdParams);
  3531   if (rv.Failed()) {
  3532     return;
  3535   // If command does not have a state_attribute value, this call fails, and
  3536   // aValue will wind up being the empty string.  This is fine -- we want to
  3537   // return "" in that case anyway (bug 738385), so we just return NS_OK
  3538   // regardless.
  3539   nsXPIDLCString cStringResult;
  3540   cmdParams->GetCStringValue("state_attribute",
  3541                              getter_Copies(cStringResult));
  3542   CopyUTF8toUTF16(cStringResult, aValue);
  3545 nsresult
  3546 nsHTMLDocument::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
  3548   NS_ASSERTION(aNodeInfo->NodeInfoManager() == mNodeInfoManager,
  3549                "Can't import this document into another document!");
  3551   nsRefPtr<nsHTMLDocument> clone = new nsHTMLDocument();
  3552   nsresult rv = CloneDocHelper(clone.get());
  3553   NS_ENSURE_SUCCESS(rv, rv);
  3555   // State from nsHTMLDocument
  3556   clone->mLoadFlags = mLoadFlags;
  3558   return CallQueryInterface(clone.get(), aResult);
  3561 bool
  3562 nsHTMLDocument::IsEditingOnAfterFlush()
  3564   nsIDocument* doc = GetParentDocument();
  3565   if (doc) {
  3566     // Make sure frames are up to date, since that can affect whether
  3567     // we're editable.
  3568     doc->FlushPendingNotifications(Flush_Frames);
  3571   return IsEditingOn();
  3574 void
  3575 nsHTMLDocument::RemovedFromDocShell()
  3577   mEditingState = eOff;
  3578   nsDocument::RemovedFromDocShell();
  3581 /* virtual */ void
  3582 nsHTMLDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
  3584   nsDocument::DocAddSizeOfExcludingThis(aWindowSizes);
  3586   // Measurement of the following members may be added later if DMD finds it is
  3587   // worthwhile:
  3588   // - mImages
  3589   // - mApplets
  3590   // - mEmbeds
  3591   // - mLinks
  3592   // - mAnchors
  3593   // - mScripts
  3594   // - mForms
  3595   // - mFormControls
  3596   // - mWyciwygChannel
  3597   // - mMidasCommandManager
  3600 bool
  3601 nsHTMLDocument::WillIgnoreCharsetOverride()
  3603   if (!mIsRegularHTML) {
  3604     return true;
  3606   if (mCharacterSetSource == kCharsetFromByteOrderMark) {
  3607     return true;
  3609   if (!EncodingUtils::IsAsciiCompatible(mCharacterSet)) {
  3610     return true;
  3612   nsCOMPtr<nsIWyciwygChannel> wyciwyg = do_QueryInterface(mChannel);
  3613   if (wyciwyg) {
  3614     return true;
  3616   nsIURI* uri = GetOriginalURI();
  3617   if (uri) {
  3618     bool schemeIs = false;
  3619     uri->SchemeIs("about", &schemeIs);
  3620     if (schemeIs) {
  3621       return true;
  3623     bool isResource;
  3624     nsresult rv = NS_URIChainHasFlags(uri,
  3625                                       nsIProtocolHandler::URI_IS_UI_RESOURCE,
  3626                                       &isResource);
  3627     if (NS_FAILED(rv) || isResource) {
  3628       return true;
  3631   return false;

mercurial