Bug 1455885: Inline and make document casts fatally assert. r?bz draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sat, 21 Apr 2018 18:42:31 +0200
changeset 787405 1e146f9b6c531b30e3513b5882f7e9dbc52f03e4
parent 787142 26e53729a10976f52e75efa44e17b5e054969fec
child 787406 3ad6dd36573a889f7540a2ad51095b52f4c139be
push id107721
push userbmo:emilio@crisal.io
push dateTue, 24 Apr 2018 20:07:40 +0000
reviewersbz
bugs1455885
milestone61.0a1
Bug 1455885: Inline and make document casts fatally assert. r?bz For consistency with AsElement / AsContent / AsDocumentFragment, etc. MozReview-Commit-ID: 8GSj8R9hLBe
accessible/generic/Accessible.cpp
dom/base/nsDocument.cpp
dom/base/nsFrameLoader.cpp
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsIDocument.h
dom/html/moz.build
dom/html/nsHTMLDocument.h
dom/svg/SVGDocument.h
dom/webauthn/WebAuthnManager.cpp
dom/webauthn/WebAuthnManager.h
dom/webauthn/WebAuthnUtil.cpp
dom/xbl/nsXBLBinding.cpp
dom/xul/XULDocument.h
dom/xul/nsXULContentSink.cpp
dom/xul/nsXULContentUtils.cpp
dom/xul/nsXULElement.cpp
editor/composer/nsEditingSession.cpp
editor/libeditor/HTMLEditor.cpp
layout/base/nsPresContext.cpp
layout/xul/nsXULPopupManager.cpp
xpfe/appshell/nsXULWindow.cpp
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1749,19 +1749,20 @@ Accessible::RelationByType(RelationType 
           if (form) {
             nsCOMPtr<nsIContent> formContent =
               do_QueryInterface(form->GetDefaultSubmitElement());
             return Relation(mDoc, formContent);
           }
         }
       } else {
         // In XUL, use first <button default="true" .../> in the document
-        dom::XULDocument* xulDoc = mContent->OwnerDoc()->AsXULDocument();
+        nsIDocument* doc = mContent->OwnerDoc();
         nsCOMPtr<nsIDOMXULButtonElement> buttonEl;
-        if (xulDoc) {
+        if (doc->IsXULDocument()) {
+          dom::XULDocument* xulDoc = doc->AsXULDocument();
           nsCOMPtr<nsINodeList> possibleDefaultButtons =
             xulDoc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
                                            NS_LITERAL_STRING("true"));
           if (possibleDefaultButtons) {
             uint32_t length = possibleDefaultButtons->Length();
             // Check for button in list of default="true" elements
             for (uint32_t count = 0; count < length && !buttonEl; count ++) {
               buttonEl = do_QueryInterface(possibleDefaultButtons->Item(count));
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3519,21 +3519,20 @@ nsIDocument::GetActiveElement()
 {
   // Get the focused element.
   Element* focusedElement = GetRetargetedFocusedElement();
   if (focusedElement) {
     return focusedElement;
   }
 
   // No focused element anywhere in this document.  Try to get the BODY.
-  RefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument();
-  if (htmlDoc) {
+  if (IsHTMLOrXHTML()) {
     // Because of IE compatibility, return null when html document doesn't have
     // a body.
-    return htmlDoc->GetBody();
+    return AsHTMLDocument()->GetBody();
   }
 
   // If we couldn't get a BODY, return the root element.
   return GetDocumentElement();
 }
 
 Element*
 nsIDocument::GetCurrentScript()
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -838,17 +838,18 @@ nsFrameLoader::Show(int32_t marginWidth,
 
   // Trigger editor re-initialization if midas is turned on in the
   // sub-document. This shouldn't be necessary, but given the way our
   // editor works, it is. See
   // https://bugzilla.mozilla.org/show_bug.cgi?id=284245
   presShell = mDocShell->GetPresShell();
   if (presShell) {
     nsIDocument* doc = presShell->GetDocument();
-    nsHTMLDocument* htmlDoc = doc ? doc->AsHTMLDocument() : nullptr;
+    nsHTMLDocument* htmlDoc =
+      doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr;
 
     if (htmlDoc) {
       nsAutoString designMode;
       htmlDoc->GetDesignMode(designMode);
 
       if (designMode.EqualsLiteral("on")) {
         // Hold on to the editor object to let the document reattach to the
         // same editor object, instead of creating a new one.
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -5229,20 +5229,18 @@ nsGlobalWindowInner::FireOfflineStatusEv
   if (mWasOffline) {
     name.AssignLiteral("offline");
   } else {
     name.AssignLiteral("online");
   }
   // The event is fired at the body element, or if there is no body element,
   // at the document.
   nsCOMPtr<EventTarget> eventTarget = mDoc.get();
-  nsHTMLDocument* htmlDoc = mDoc->AsHTMLDocument();
-  if (htmlDoc) {
-    Element* body = htmlDoc->GetBody();
-    if (body) {
+  if (mDoc->IsHTMLOrXHTML()) {
+    if (Element* body = mDoc->AsHTMLDocument()->GetBody()) {
       eventTarget = body;
     }
   } else {
     Element* documentElement = mDoc->GetDocumentElement();
     if (documentElement) {
       eventTarget = documentElement;
     }
   }
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -6294,24 +6294,26 @@ nsGlobalWindowOuter::UpdateCommands(cons
   }
 
   nsPIDOMWindowOuter *rootWindow = GetPrivateRoot();
   if (!rootWindow) {
     return;
   }
 
   nsIDocument* doc = rootWindow->GetExtantDoc();
-  XULDocument* xulDoc = doc ? doc->AsXULDocument() : nullptr;
   // See if we contain a XUL document.
+  if (!doc || !doc->IsXULDocument()) {
+    return;
+  }
   // selectionchange action is only used for mozbrowser, not for XUL. So we bypass
   // XUL command dispatch if anAction is "selectionchange".
-  if (xulDoc && !anAction.EqualsLiteral("selectionchange")) {
+  if (!anAction.EqualsLiteral("selectionchange")) {
     // Retrieve the command dispatcher and call updateCommands on it.
     nsIDOMXULCommandDispatcher* xulCommandDispatcher =
-      xulDoc->GetCommandDispatcher();
+      doc->AsXULDocument()->GetCommandDispatcher();
     if (xulCommandDispatcher) {
       nsContentUtils::AddScriptRunner(new CommandDispatcher(xulCommandDispatcher,
                                                             anAction));
     }
   }
 }
 
 Selection*
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -3343,19 +3343,33 @@ public:
   already_AddRefed<nsIURI> GetMozDocumentURIIfNotForErrorPages();
 
   mozilla::dom::Promise* GetDocumentReadyForIdle(mozilla::ErrorResult& aRv);
 
   // ParentNode
   nsIHTMLCollection* Children();
   uint32_t ChildElementCount();
 
-  virtual nsHTMLDocument* AsHTMLDocument() { return nullptr; }
-  virtual mozilla::dom::SVGDocument* AsSVGDocument() { return nullptr; }
-  virtual mozilla::dom::XULDocument* AsXULDocument() { return nullptr; }
+  /**
+   * Asserts IsHTMLDocument, and can't return null.
+   * Defined inline in nsHTMLDocument.h
+   */
+  inline nsHTMLDocument* AsHTMLDocument();
+
+  /**
+   * Asserts IsSVGDocument, and can't return null.
+   * Defined inline in SVGDocument.h
+   */
+  inline mozilla::dom::SVGDocument* AsSVGDocument();
+
+  /**
+   * Asserts IsXULDocument, and can't return null.
+   * Defined inline in XULDocument.h
+   */
+  inline mozilla::dom::XULDocument* AsXULDocument();
 
   /*
    * Given a node, get a weak reference to it and append that reference to
    * mBlockedTrackingNodes. Can be used later on to look up a node in it.
    * (e.g., by the UI)
    */
   void AddBlockedTrackingNode(nsINode *node)
   {
--- a/dom/html/moz.build
+++ b/dom/html/moz.build
@@ -29,16 +29,17 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'content_html'
 
 EXPORTS += [
     'nsGenericHTMLElement.h',
     'nsGenericHTMLFrameElement.h',
     'nsHTMLDNSPrefetch.h',
+    'nsHTMLDocument.h',
     'nsIConstraintValidation.h',
     'nsIForm.h',
     'nsIFormControl.h',
     'nsIFormProcessor.h',
     'nsIHTMLCollection.h',
     'nsIHTMLDocument.h',
     'nsIRadioGroupContainer.h',
     'nsIRadioVisitor.h',
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -10,17 +10,16 @@
 #include "nsDocument.h"
 #include "nsIHTMLDocument.h"
 #include "nsIHTMLCollection.h"
 #include "nsIScriptElement.h"
 #include "nsTArray.h"
 
 #include "PLDHashTable.h"
 #include "nsIHttpChannel.h"
-#include "nsHTMLStyleSheet.h"
 #include "nsThreadUtils.h"
 #include "nsICommandManager.h"
 #include "mozilla/dom/HTMLSharedElement.h"
 #include "mozilla/dom/BindingDeclarations.h"
 
 class nsIURI;
 class nsIDocShell;
 class nsICachingChannel;
@@ -220,18 +219,16 @@ public:
   void CaptureEvents();
   void ReleaseEvents();
   // We're picking up GetLocation from Document
   already_AddRefed<mozilla::dom::Location> GetLocation() const
   {
     return nsIDocument::GetLocation();
   }
 
-  virtual nsHTMLDocument* AsHTMLDocument() override { return this; }
-
   static bool MatchFormControls(Element* aElement, int32_t aNamespaceID,
                                 nsAtom* aAtom, void* aData);
 
   void GetFormsAndFormControls(nsContentList** aFormList,
                                nsContentList** aFormControlList);
 protected:
   ~nsHTMLDocument();
 
@@ -362,13 +359,20 @@ protected:
 
   /**
    * Temporary flag that is set in EndUpdate() to ignore
    * MaybeEditingStateChanged() script runners from a nested scope.
    */
   bool mPendingMaybeEditingStateChanged;
 };
 
+inline nsHTMLDocument*
+nsIDocument::AsHTMLDocument()
+{
+  MOZ_ASSERT(IsHTMLOrXHTML());
+  return static_cast<nsHTMLDocument*>(this);
+}
+
 #define NS_HTML_DOCUMENT_INTERFACE_TABLE_BEGIN(_class)                        \
     NS_DOCUMENT_INTERFACE_TABLE_BEGIN(_class)                                 \
     NS_INTERFACE_TABLE_ENTRY(_class, nsIHTMLDocument)
 
 #endif /* nsHTMLDocument_h___ */
--- a/dom/svg/SVGDocument.h
+++ b/dom/svg/SVGDocument.h
@@ -44,9 +44,16 @@ private:
   void EnsureNonSVGUserAgentStyleSheetsLoaded();
 
   bool mHasLoadedNonSVGUserAgentStyleSheets;
 };
 
 } // namespace dom
 } // namespace mozilla
 
+inline mozilla::dom::SVGDocument*
+nsIDocument::AsSVGDocument()
+{
+  MOZ_ASSERT(IsSVGDocument());
+  return static_cast<mozilla::dom::SVGDocument*>(this);
+}
+
 #endif // mozilla_dom_SVGDocument_h
--- a/dom/webauthn/WebAuthnManager.cpp
+++ b/dom/webauthn/WebAuthnManager.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "hasht.h"
+#include "nsHTMLDocument.h"
 #include "nsICryptoHash.h"
 #include "nsNetCID.h"
 #include "nsThreadUtils.h"
 #include "WebAuthnCoseIdentifiers.h"
 #include "mozilla/dom/AuthenticatorAttestationResponse.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PWebAuthnTransaction.h"
 #include "mozilla/dom/U2FUtil.h"
@@ -131,20 +132,16 @@ RelaxSameOrigin(nsPIDOMWindowInner* aPar
   if (NS_FAILED(uri->GetAsciiHost(originHost))) {
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsIDocument> document = aParent->GetDoc();
   if (!document || !document->IsHTMLDocument()) {
     return NS_ERROR_FAILURE;
   }
   nsHTMLDocument* html = document->AsHTMLDocument();
-  if (NS_WARN_IF(!html)) {
-    return NS_ERROR_FAILURE;
-  }
-
   if (!html->IsRegistrableDomainSuffixOfOrEqualTo(aInputRpId, originHost)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   aRelaxedRpId.Assign(NS_ConvertUTF16toUTF8(aInputRpId));
   return NS_OK;
 }
 
--- a/dom/webauthn/WebAuthnManager.h
+++ b/dom/webauthn/WebAuthnManager.h
@@ -36,23 +36,16 @@
  * - On return of successful transaction information from parent process, turn
  *   information into DOM object format required by spec, and resolve promise
  *   (by running the Finish* functions of WebAuthnManager). On cancellation
  *   request from parent, reject promise with corresponding error code. Either
  *   outcome will also close the IPC channel.
  *
  */
 
-// Forward decl because of nsHTMLDocument.h's complex dependency on /layout/style
-class nsHTMLDocument {
-public:
-  bool IsRegistrableDomainSuffixOfOrEqualTo(const nsAString& aHostSuffixString,
-                                            const nsACString& aOrigHost);
-};
-
 namespace mozilla {
 namespace dom {
 
 class WebAuthnTransaction
 {
 public:
   WebAuthnTransaction(const RefPtr<Promise>& aPromise,
                       const nsTArray<uint8_t>& aRpIdHash,
--- a/dom/webauthn/WebAuthnUtil.cpp
+++ b/dom/webauthn/WebAuthnUtil.cpp
@@ -73,21 +73,18 @@ EvaluateAppID(nsPIDOMWindowInner* aParen
 
   // Run the HTML5 algorithm to relax the same-origin policy, copied from W3C
   // Web Authentication. See Bug 1244959 comment #8 for context on why we are
   // doing this instead of implementing the external-fetch FacetID logic.
   nsCOMPtr<nsIDocument> document = aParent->GetDoc();
   if (!document || !document->IsHTMLDocument()) {
     return false;
   }
+
   nsHTMLDocument* html = document->AsHTMLDocument();
-  if (NS_WARN_IF(!html)) {
-    return false;
-  }
-
   // Use the base domain as the facet for evaluation. This lets this algorithm
   // relax the whole eTLD+1.
   nsCOMPtr<nsIEffectiveTLDService> tldService =
     do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   if (!tldService) {
     return false;
   }
 
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -221,42 +221,41 @@ nsXBLBinding::BindAnonymousContent(nsICo
       child->UnbindFromTree();
       return;
     }
 
 #ifdef MOZ_XUL
     // To make XUL templates work (and other goodies that happen when
     // an element is added to a XUL document), we need to notify the
     // XUL document using its special API.
-    XULDocument* xuldoc = doc ? doc->AsXULDocument() : nullptr;
-    if (xuldoc) {
-      xuldoc->AddSubtreeToDocument(child);
+    if (doc && doc->IsXULDocument()) {
+      doc->AsXULDocument()->AddSubtreeToDocument(child);
     }
 #endif
   }
 }
 
 void
 nsXBLBinding::UnbindAnonymousContent(nsIDocument* aDocument,
                                      nsIContent* aAnonParent,
                                      bool aNullParent)
 {
   nsAutoScriptBlocker scriptBlocker;
   // Hold a strong ref while doing this, just in case.
   nsCOMPtr<nsIContent> anonParent = aAnonParent;
 #ifdef MOZ_XUL
-  XULDocument* xuldoc = aDocument ? aDocument->AsXULDocument() : nullptr;
+  const bool isXULDocument = aDocument && aDocument->IsXULDocument();
 #endif
   for (nsIContent* child = aAnonParent->GetFirstChild();
        child;
        child = child->GetNextSibling()) {
     child->UnbindFromTree(true, aNullParent);
 #ifdef MOZ_XUL
-    if (xuldoc) {
-      xuldoc->RemoveSubtreeFromDocument(child);
+    if (isXULDocument) {
+      aDocument->AsXULDocument()->RemoveSubtreeFromDocument(child);
     }
 #endif
   }
 }
 
 void
 nsXBLBinding::SetBoundElement(Element* aElement)
 {
--- a/dom/xul/XULDocument.h
+++ b/dom/xul/XULDocument.h
@@ -78,20 +78,16 @@ public:
                                        nsIStreamListener **aDocListener,
                                        bool aReset = true,
                                        nsIContentSink* aSink = nullptr) override;
 
     virtual void SetContentType(const nsAString& aContentType) override;
 
     virtual void EndLoad() override;
 
-    virtual XULDocument* AsXULDocument() override {
-        return this;
-    }
-
     // nsIMutationObserver interface
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
     NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
 
     /**
      * Notify the XUL document that a subtree has been added
@@ -717,9 +713,16 @@ protected:
 private:
     // helpers
 
 };
 
 } // namespace dom
 } // namespace mozilla
 
+inline mozilla::dom::XULDocument*
+nsIDocument::AsXULDocument()
+{
+  MOZ_ASSERT(IsXULDocument());
+  return static_cast<mozilla::dom::XULDocument*>(this);
+}
+
 #endif // mozilla_dom_XULDocument_h
--- a/dom/xul/nsXULContentSink.cpp
+++ b/dom/xul/nsXULContentSink.cpp
@@ -668,18 +668,19 @@ XULContentSinkImpl::ReportError(const ch
   mTextLength = 0;
 
   // return leaving the document empty if we're asked to not add a <parsererror> root node
   nsCOMPtr<nsIDocument> idoc = do_QueryReferent(mDocument);
   if (idoc && idoc->SuppressParserErrorElement()) {
     return NS_OK;
   };
 
-  XULDocument* doc = idoc ? idoc->AsXULDocument() : nullptr;
-  if (doc && !doc->OnDocumentParserError()) {
+  if (idoc &&
+      idoc->IsXULDocument() &&
+      !idoc->AsXULDocument()->OnDocumentParserError()) {
     // The overlay was broken.  Don't add a messy element to the master doc.
     return NS_OK;
   }
 
   const char16_t* noAtts[] = { 0, 0 };
 
   NS_NAMED_LITERAL_STRING(errorNs,
                           "http://www.mozilla.org/newlayout/xml/parsererror.xml");
--- a/dom/xul/nsXULContentUtils.cpp
+++ b/dom/xul/nsXULContentUtils.cpp
@@ -116,21 +116,21 @@ nsXULContentUtils::SetCommandUpdater(nsI
         return NS_ERROR_NULL_POINTER;
 
     NS_PRECONDITION(aElement != nullptr, "null ptr");
     if (! aElement)
         return NS_ERROR_NULL_POINTER;
 
     nsresult rv;
 
-    XULDocument* xuldoc = aDocument->AsXULDocument();
-    NS_ASSERTION(xuldoc != nullptr, "not a xul document");
-    if (! xuldoc)
+    NS_ASSERTION(aDocument->IsXULDocument(), "not a xul document");
+    if (! aDocument->IsXULDocument())
         return NS_ERROR_UNEXPECTED;
 
+    XULDocument* xuldoc = aDocument->AsXULDocument();
     nsCOMPtr<nsIDOMXULCommandDispatcher> dispatcher =
         xuldoc->GetCommandDispatcher();
     NS_ASSERTION(dispatcher != nullptr, "no dispatcher");
     if (! dispatcher)
         return NS_ERROR_UNEXPECTED;
 
     nsAutoString events;
     aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::events, events);
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1115,26 +1115,24 @@ nsXULElement::AfterSetAttr(int32_t aName
                 } else if (aName == nsGkAtoms::drawintitlebar) {
                     SetDrawsInTitlebar(
                         aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
                 } else if (aName == nsGkAtoms::drawtitle) {
                     SetDrawsTitle(
                         aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
                 } else if (aName == nsGkAtoms::localedir) {
                     // if the localedir changed on the root element, reset the document direction
-                    XULDocument* xuldoc = document->AsXULDocument();
-                    if (xuldoc) {
-                        xuldoc->ResetDocumentDirection();
+                    if (document->IsXULDocument()) {
+                        document->AsXULDocument()->ResetDocumentDirection();
                     }
                 } else if (aName == nsGkAtoms::lwtheme ||
                          aName == nsGkAtoms::lwthemetextcolor) {
                     // if the lwtheme changed, make sure to reset the document lwtheme cache
-                    XULDocument* xuldoc = document->AsXULDocument();
-                    if (xuldoc) {
-                        xuldoc->ResetDocumentLWTheme();
+                    if (document->IsXULDocument()) {
+                        document->AsXULDocument()->ResetDocumentLWTheme();
                         UpdateBrightTitlebarForeground(document);
                     }
                 } else if (aName == nsGkAtoms::brighttitlebarforeground) {
                     UpdateBrightTitlebarForeground(document);
                 }
             }
 
             if (aName == nsGkAtoms::src && document) {
@@ -1148,26 +1146,24 @@ nsXULElement::AfterSetAttr(int32_t aName
                     ResetChromeMargins();
                 }
             }
 
             nsIDocument* doc = GetUncomposedDoc();
             if (doc && doc->GetRootElement() == this) {
                 if (aName == nsGkAtoms::localedir) {
                     // if the localedir changed on the root element, reset the document direction
-                    XULDocument* xuldoc = doc->AsXULDocument();
-                    if (xuldoc) {
-                        xuldoc->ResetDocumentDirection();
+                    if (doc->IsXULDocument()) {
+                        doc->AsXULDocument()->ResetDocumentDirection();
                     }
                 } else if ((aName == nsGkAtoms::lwtheme ||
                             aName == nsGkAtoms::lwthemetextcolor)) {
                     // if the lwtheme changed, make sure to restyle appropriately
-                    XULDocument* xuldoc = doc->AsXULDocument();
-                    if (xuldoc) {
-                        xuldoc->ResetDocumentLWTheme();
+                    if (doc->IsXULDocument()) {
+                        doc->AsXULDocument()->ResetDocumentLWTheme();
                         UpdateBrightTitlebarForeground(doc);
                     }
                 } else if (aName == nsGkAtoms::brighttitlebarforeground) {
                     UpdateBrightTitlebarForeground(doc);
                 } else if (aName == nsGkAtoms::drawintitlebar) {
                     SetDrawsInTitlebar(false);
                 } else if (aName == nsGkAtoms::drawtitle) {
                     SetDrawsTitle(false);
@@ -1231,23 +1227,23 @@ nsXULElement::ParseAttribute(int32_t aNa
     }
 
     return true;
 }
 
 void
 nsXULElement::RemoveBroadcaster(const nsAString & broadcasterId)
 {
-    XULDocument* xuldoc = OwnerDoc()->AsXULDocument();
-    if (xuldoc) {
-        Element* broadcaster = xuldoc->GetElementById(broadcasterId);
-        if (broadcaster) {
-            xuldoc->RemoveBroadcastListenerFor(*broadcaster, *this,
-                                               NS_LITERAL_STRING("*"));
-        }
+    nsIDocument* doc = OwnerDoc();
+    if (!doc->IsXULDocument()) {
+      return;
+    }
+    if (Element* broadcaster = doc->GetElementById(broadcasterId)) {
+        doc->AsXULDocument()->RemoveBroadcastListenerFor(
+           *broadcaster, *this, NS_LITERAL_STRING("*"));
     }
 }
 
 void
 nsXULElement::DestroyContent()
 {
     nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     if (slots) {
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -659,17 +659,18 @@ nsEditingSession::OnStateChange(nsIWebPr
         IsProgressForTargetDocument(aWebProgress);
 
       if (progressIsForTargetDocument) {
         nsCOMPtr<mozIDOMWindowProxy> window;
         aWebProgress->GetDOMWindow(getter_AddRefs(window));
 
         auto* piWindow = nsPIDOMWindowOuter::From(window);
         nsCOMPtr<nsIDocument> doc = piWindow->GetDoc();
-        nsHTMLDocument* htmlDoc = doc ? doc->AsHTMLDocument() : nullptr;
+        nsHTMLDocument* htmlDoc = doc && doc->IsHTMLDocument()
+          ? doc->AsHTMLDocument() : nullptr;
         if (htmlDoc && htmlDoc->IsWriting()) {
           nsAutoString designMode;
           htmlDoc->GetDesignMode(designMode);
 
           if (designMode.EqualsLiteral("on")) {
             // This notification is for data coming in through
             // document.open/write/close(), ignore it.
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -5126,12 +5126,15 @@ HTMLEditor::GetEditorRoot()
 
 nsHTMLDocument*
 HTMLEditor::GetHTMLDocument() const
 {
   nsIDocument* doc = GetDocument();
   if (NS_WARN_IF(!doc)) {
     return nullptr;
   }
+  if (!doc->IsHTMLOrXHTML()) {
+    return nullptr;
+  }
   return doc->AsHTMLDocument();
 }
 
 } // namespace mozilla
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1458,23 +1458,21 @@ GetPropagatedScrollbarStylesForViewport(
     return docElement;
   }
 
   // Don't look in the BODY for non-HTML documents or HTML documents
   // with non-HTML roots.
   // XXX this should be earlier; we shouldn't even look at the document root
   // for non-HTML documents. Fix this once we support explicit CSS styling
   // of the viewport
-  // XXX what about XHTML?
-  nsHTMLDocument* htmlDoc = document->AsHTMLDocument();
-  if (!htmlDoc || !docElement->IsHTMLElement()) {
+  if (!document->IsHTMLOrXHTML() || !docElement->IsHTMLElement()) {
     return nullptr;
   }
 
-  Element* bodyElement = htmlDoc->GetBodyElement();
+  Element* bodyElement = document->AsHTMLDocument()->GetBodyElement();
   if (!bodyElement) {
     // No body, nothing to do here.
     return nullptr;
   }
 
   MOZ_ASSERT(bodyElement->IsHTMLElement(nsGkAtoms::body),
              "GetBodyElement returned something bogus");
 
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -1972,20 +1972,19 @@ nsXULPopupManager::UpdateMenuItems(nsICo
   // command attribute. If so, then several attributes must potentially be updated.
 
   nsCOMPtr<nsIDocument> document = aPopup->GetUncomposedDoc();
   if (!document) {
     return;
   }
 
   // When a menu is opened, make sure that command updating is unlocked first.
-  XULDocument* xulDoc = document->AsXULDocument();
-  if (xulDoc) {
+  if (document->IsXULDocument()) {
     nsCOMPtr<nsIDOMXULCommandDispatcher> xulCommandDispatcher =
-      xulDoc->GetCommandDispatcher();
+      document->AsXULDocument()->GetCommandDispatcher();
     if (xulCommandDispatcher) {
       xulCommandDispatcher->Unlock();
     }
   }
 
   for (nsCOMPtr<nsIContent> grandChild = aPopup->GetFirstChild();
        grandChild;
        grandChild = grandChild->GetNextSibling()) {
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -1639,17 +1639,18 @@ NS_IMETHODIMP nsXULWindow::SavePersisten
     }
   }
 
   nsAutoString                sizeString;
   nsAutoString                windowElementId;
 
   // fetch docShellElement's ID and XUL owner document
   RefPtr<dom::XULDocument> ownerXULDoc =
-    docShellElement->OwnerDoc()->AsXULDocument();
+    docShellElement->OwnerDoc()->IsXULDocument()
+      ? docShellElement->OwnerDoc()->AsXULDocument() : nullptr;
   if (docShellElement->IsXULElement()) {
     docShellElement->GetId(windowElementId);
   }
 
   bool shouldPersist = !isFullscreen && ownerXULDoc;
   ErrorResult rv;
   // (only for size elements which are persisted)
   if ((mPersistentAttributesDirty & PAD_POSITION) && gotRestoredBounds) {