Bug 1549560 - Move HTMLDocument.open/close/write/writeln to Document; r=farre
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 27 May 2019 23:03:03 +0000
changeset 475790 a9b24ae04ca3684c286b4c8504d694b2426f255c
parent 475789 0765f0fa75605655beb1eb98afc7584364efb846
child 475791 8c1c9564e9625046076f4475423795deb933fcd1
push id86475
push usereakhgari@mozilla.com
push dateMon, 27 May 2019 23:03:46 +0000
treeherderautoland@a9b24ae04ca3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfarre
bugs1549560
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1549560 - Move HTMLDocument.open/close/write/writeln to Document; r=farre Differential Revision: https://phabricator.services.mozilla.com/D32388
browser/components/places/content/places.xul
browser/components/preferences/blocklists.xul
browser/components/preferences/in-content/syncDisconnect.xul
browser/components/preferences/in-content/tests/subdialog.xul
browser/components/preferences/in-content/tests/subdialog2.xul
browser/components/preferences/permissions.xul
browser/components/preferences/sitePermissions.xul
browser/components/preferences/translation.xul
dom/base/Document.cpp
dom/base/Document.h
dom/html/nsHTMLContentSink.cpp
dom/html/nsHTMLDocument.cpp
dom/html/nsHTMLDocument.h
dom/html/nsIHTMLDocument.h
dom/webidl/Document.webidl
dom/webidl/HTMLDocument.webidl
dom/xml/XMLDocument.cpp
dom/xml/nsXMLContentSink.cpp
dom/xslt/xslt/txMozillaXMLOutput.cpp
editor/composer/nsEditingSession.cpp
parser/html/nsHtml5DocumentBuilder.cpp
testing/web-platform/meta/html/dom/interfaces.https.html.ini
toolkit/components/passwordmgr/content/passwordManager.xul
toolkit/content/widgets/findbar.js
--- a/browser/components/places/content/places.xul
+++ b/browser/components/places/content/places.xul
@@ -90,17 +90,17 @@
              oncommand="PlacesOrganizer.forward();"/>
   </commandset>
 #include ../../downloads/content/downloadsCommands.inc.xul
 #include ../../downloads/content/downloadsStrings.inc.xul
 
   <keyset id="placesOrganizerKeyset">
     <!-- Instantiation Keys -->
     <key id="placesKey_close" key="&cmd.close.key;" modifiers="accel"
-         oncommand="close();"/>
+         oncommand="window.close();"/>
 
     <!-- Command Keys -->
     <key id="placesKey_find:all"
          command="OrganizerCommand_find:all"
          key="&cmd.find.key;"
          modifiers="accel"/>
 
     <!-- Back/Forward Keys Support -->
@@ -242,17 +242,17 @@
                       accesskey="&selectAllCmd.accesskey;"/>
 
             <menuseparator id="orgCloseSeparator"/>
 
             <menuitem id="orgClose"
                       key="placesKey_close"
                       label="&file.close.label;"
                       accesskey="&file.close.accesskey;"
-                      oncommand="close();"/>
+                      oncommand="window.close();"/>
 #endif
           </menupopup>
 #ifdef XP_MACOSX
         </toolbarbutton>
         <toolbarbutton type="menu" class="tabbable"
 #else
         </menu>
         <menu accesskey="&views.accesskey;" class="menu-iconic"
--- a/browser/components/preferences/blocklists.xul
+++ b/browser/components/preferences/blocklists.xul
@@ -43,14 +43,14 @@
         <treecol id="listCol" data-l10n-id="blocklist-treehead-list" flex="80"
                  sortable="false"/>
       </treecols>
       <treechildren/>
     </tree>
   </vbox>
 
   <hbox class="actionButtons" align="right" flex="1">
-    <button oncommand="close();" icon="close"
+    <button oncommand="window.close();" icon="close"
             data-l10n-id="blocklist-button-cancel"/>
     <button id="btnApplyChanges" oncommand="gBlocklistManager.onApplyChanges();" icon="save"
             data-l10n-id="blocklist-button-ok"/>
   </hbox>
 </window>
--- a/browser/components/preferences/in-content/syncDisconnect.xul
+++ b/browser/components/preferences/in-content/syncDisconnect.xul
@@ -44,17 +44,17 @@
         </vbox>
       </hbox>
     </vbox>
 
     <vbox>
       <spacer flex="1"/>
       <hbox class="actionButtons" align="right" flex="1">
         <button id="butCancel"
-                oncommand="close(event);"
+                oncommand="window.close(event);"
                 class="syncDisconnectButton"
                 data-l10n-id="sync-disconnect-cancel"/>
         <button id="butDisconnect"
                 oncommand="gSyncDisconnectDialog.accept(event);"
                 class="syncDisconnectButton"
                 data-l10n-id="sync-disconnect-confirm-disconnect"/>
       </hbox>
     </vbox>
--- a/browser/components/preferences/in-content/tests/subdialog.xul
+++ b/browser/components/preferences/in-content/tests/subdialog.xul
@@ -17,11 +17,11 @@
   </script>
 
   <description id="desc">A sample sub-dialog for testing</description>
 
   <textbox id="textbox" value="Default text" />
 
   <separator class="thin"/>
 
-  <button oncommand="close();" icon="close" label="Close" />
+  <button oncommand="window.close();" icon="close" label="Close" />
 
 </dialog>
--- a/browser/components/preferences/in-content/tests/subdialog2.xul
+++ b/browser/components/preferences/in-content/tests/subdialog2.xul
@@ -17,11 +17,11 @@
   </script>
 
   <description id="desc">A sample sub-dialog for testing</description>
 
   <textbox id="textbox" value="Default text" />
 
   <separator class="thin"/>
 
-  <button oncommand="close();" icon="close" label="Close" />
+  <button oncommand="window.close();" icon="close" label="Close" />
 
 </dialog>
--- a/browser/components/preferences/permissions.xul
+++ b/browser/components/preferences/permissions.xul
@@ -68,14 +68,14 @@
             oncommand="gPermissionManager.onPermissionDelete();"/>
     <button id="removeAllPermissions"
             data-l10n-id="permissions-remove-all"
             icon="clear"
             oncommand="gPermissionManager.onAllPermissionsDelete();"/>
   </hbox>
   <spacer flex="1"/>
   <hbox class="actionButtons" align="right" flex="1">
-    <button oncommand="close();" icon="close"
+    <button oncommand="window.close();" icon="close"
             data-l10n-id="permissions-button-cancel" />
     <button id="btnApplyChanges" oncommand="gPermissionManager.onApplyChanges();" icon="save"
             data-l10n-id="permissions-button-ok" />
   </hbox>
 </window>
--- a/browser/components/preferences/sitePermissions.xul
+++ b/browser/components/preferences/sitePermissions.xul
@@ -69,14 +69,14 @@
   <hbox id="browserNotificationsPermissionExtensionContent"
         class="extension-controlled" align="center" hidden="true">
     <description control="disableNotificationsPermissionExtension" flex="1"/>
     <button id="disableNotificationsPermissionExtension"
             class="extension-controlled-button accessory-button"
             data-l10n-id="disable-extension"/>
   </hbox>
   <hbox class="actionButtons" align="right" flex="1">
-    <button oncommand="close();" icon="close" id="cancel"
+    <button oncommand="window.close();" icon="close" id="cancel"
             data-l10n-id="permissions-button-cancel" />
     <button id="btnApplyChanges" oncommand="gSitePermissionsManager.onApplyChanges();" icon="save"
             data-l10n-id="permissions-button-ok" />
   </hbox>
 </window>
--- a/browser/components/preferences/translation.xul
+++ b/browser/components/preferences/translation.xul
@@ -78,12 +78,12 @@
             data-l10n-id="translation-sites-button-remove"
             icon="remove"
             oncommand="gTranslationExceptions.onSiteDeleted();"/>
     <button id="removeAllSites"
             data-l10n-id="translation-sites-button-remove-all"
             icon="clear"
             oncommand="gTranslationExceptions.onAllSitesDeleted();"/>
     <spacer flex="1"/>
-    <button oncommand="close();" icon="close"
+    <button oncommand="window.close();" icon="close"
             data-l10n-id="translation-button-close"/>
   </hbox>
 </window>
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -308,22 +308,26 @@
 #include "MobileViewportManager.h"
 #include "NodeUbiReporting.h"
 #include "nsICookieService.h"
 #include "mozilla/net/ChannelEventQueue.h"
 #include "mozilla/net/RequestContextService.h"
 #include "StorageAccessPermissionRequest.h"
 #include "mozilla/dom/WindowProxyHolder.h"
 #include "ThirdPartyUtil.h"
+#include "nsHtml5Module.h"
+#include "nsHtml5Parser.h"
 
 #define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0)
 #define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1)
 #define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2)
 #define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3)
 
+#define NS_MAX_DOCUMENT_WRITE_DEPTH 20
+
 extern bool sDisablePrefetchHTTPSPref;
 
 mozilla::LazyLogModule gPageCacheLog("PageCache");
 
 namespace mozilla {
 namespace dom {
 
 typedef nsTArray<Link*> LinkArray;
@@ -1246,20 +1250,23 @@ Document::Document(const char* aContentT
       mReportedUseCounters(false),
       mHasReportedShadowDOMUsage(false),
       mDocTreeHadAudibleMedia(false),
       mDocTreeHadPlayRevoked(false),
       mHasDelayedRefreshEvent(false),
       mLoadEventFiring(false),
       mSkipLoadEventAfterClose(false),
       mDisableCookieAccess(false),
+      mDisableDocWrite(false),
+      mTooDeepWriteRecursion(false),
       mPendingFullscreenRequests(0),
       mXMLDeclarationBits(0),
       mOnloadBlockCount(0),
       mAsyncOnloadBlockCount(0),
+      mWriteLevel(0),
       mCompatMode(eCompatibility_FullStandards),
       mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
       mAncestorIsLoading(false),
 #ifdef MOZILLA_INTERNAL_API
       mVisibilityState(dom::VisibilityState::Hidden),
 #else
       mDummy(0),
 #endif
@@ -2514,16 +2521,27 @@ void Document::CompatibilityModeChanged(
     mStyleSet->RemoveStyleSheet(StyleOrigin::UserAgent, sheet);
   } else {
     mStyleSet->AppendStyleSheet(StyleOrigin::UserAgent, sheet);
   }
   mQuirkSheetAdded = !mQuirkSheetAdded;
   ApplicableStylesChanged();
 }
 
+void Document::SetCompatibilityMode(nsCompatibility aMode) {
+  NS_ASSERTION(IsHTMLDocument() || aMode == eCompatibility_FullStandards,
+               "Bad compat mode for XHTML document!");
+
+  if (mCompatMode == aMode) {
+    return;
+  }
+  mCompatMode = aMode;
+  CompatibilityModeChanged();
+}
+
 static void WarnIfSandboxIneffective(nsIDocShell* aDocShell,
                                      uint32_t aSandboxFlags,
                                      nsIChannel* aChannel) {
   // If the document is sandboxed (via the HTML5 iframe sandbox
   // attribute) and both the allow-scripts and allow-same-origin
   // keywords are supplied, the sandboxed document can call into its
   // parent document and remove its sandboxing entirely - we print a
   // warning to the web console in this case.
@@ -6704,16 +6722,495 @@ static bool MatchAnchors(Element* aEleme
 
 nsIHTMLCollection* Document::Anchors() {
   if (!mAnchors) {
     mAnchors = new nsContentList(this, MatchAnchors, nullptr, nullptr);
   }
   return mAnchors;
 }
 
+mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> Document::Open(
+    const nsAString& aURL, const nsAString& aName, const nsAString& aFeatures,
+    bool aReplace, ErrorResult& rv) {
+  MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
+             "XOW should have caught this!");
+
+  nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
+  if (!window) {
+    rv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return nullptr;
+  }
+  nsCOMPtr<nsPIDOMWindowOuter> outer =
+      nsPIDOMWindowOuter::GetFromCurrentInner(window);
+  if (!outer) {
+    rv.Throw(NS_ERROR_NOT_INITIALIZED);
+    return nullptr;
+  }
+  RefPtr<nsGlobalWindowOuter> win = nsGlobalWindowOuter::Cast(outer);
+  nsCOMPtr<nsPIDOMWindowOuter> newWindow;
+  // XXXbz We ignore aReplace for now.
+  rv = win->OpenJS(aURL, aName, aFeatures, getter_AddRefs(newWindow));
+  if (!newWindow) {
+    return nullptr;
+  }
+  return WindowProxyHolder(newWindow->GetBrowsingContext());
+}
+
+Document* Document::Open(const Optional<nsAString>& /* unused */,
+                         const nsAString& /* unused */, ErrorResult& aError) {
+  // Implements
+  // <https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps>
+
+  MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
+             "XOW should have caught this!");
+
+  // Step 1 -- throw if we're an XML document.
+  if (!IsHTMLDocument() || mDisableDocWrite) {
+    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  // Step 2 -- throw if dynamic markup insertion should throw.
+  if (ShouldThrowOnDynamicMarkupInsertion()) {
+    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  // Step 3 -- get the entry document, so we can use it for security checks.
+  nsCOMPtr<Document> callerDoc = GetEntryDocument();
+  if (!callerDoc) {
+    // If we're called from C++ or in some other way without an originating
+    // document we can't do a document.open w/o changing the principal of the
+    // document to something like about:blank (as that's the only sane thing to
+    // do when we don't know the origin of this call), and since we can't
+    // change the principals of a document for security reasons we'll have to
+    // refuse to go ahead with this call.
+
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  // Step 4 -- make sure we're same-origin (not just same origin-domain) with
+  // the entry document.
+  if (!callerDoc->NodePrincipal()->Equals(NodePrincipal())) {
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  // Step 5 -- if we have an active parser with a nonzero script nesting level,
+  // just no-op.
+  //
+  // The mParserAborted check here is probably wrong.  Removing it is
+  // tracked in https://bugzilla.mozilla.org/show_bug.cgi?id=1475000
+  if ((mParser && mParser->HasNonzeroScriptNestingLevel()) || mParserAborted) {
+    return this;
+  }
+
+  // Step 6 -- check for open() during unload.  Per spec, this is just a check
+  // of the ignore-opens-during-unload counter, but our unload event code
+  // doesn't affect that counter yet (unlike pagehide and beforeunload, which
+  // do), so we check for unload directly.
+  if (ShouldIgnoreOpens()) {
+    return this;
+  }
+
+  nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
+  if (shell) {
+    bool inUnload;
+    shell->GetIsInUnload(&inUnload);
+    if (inUnload) {
+      return this;
+    }
+  }
+
+  // document.open() inherits the CSP from the opening document.
+  // Please create an actual copy of the CSP (do not share the same
+  // reference) otherwise appending a new policy within the opened
+  // document will be incorrectly propagated to the opening doc.
+  nsCOMPtr<nsIContentSecurityPolicy> csp = callerDoc->GetCsp();
+  if (csp) {
+    RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
+    cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
+    mCSP = cspToInherit;
+  }
+
+  // At this point we know this is a valid-enough document.open() call
+  // and not a no-op.  Increment our use counter.
+  SetDocumentAndPageUseCounter(eUseCounter_custom_DocumentOpen);
+
+  // Step 7 -- stop existing navigation of our browsing context (and all other
+  // loads it's doing) if we're the active document of our browsing context.
+  // Note that we do not want to stop anything if there is no existing
+  // navigation.
+  if (shell && IsCurrentActiveDocument() &&
+      shell->GetIsAttemptingToNavigate()) {
+    nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(shell));
+    webnav->Stop(nsIWebNavigation::STOP_NETWORK);
+
+    // The Stop call may have cancelled the onload blocker request or
+    // prevented it from getting added, so we need to make sure it gets added
+    // to the document again otherwise the document could have a non-zero
+    // onload block count without the onload blocker request being in the
+    // loadgroup.
+    EnsureOnloadBlocker();
+  }
+
+  // Step 8 -- clear event listeners out of our DOM tree
+  for (nsINode* node : ShadowIncludingTreeIterator(*this)) {
+    if (EventListenerManager* elm = node->GetExistingListenerManager()) {
+      elm->RemoveAllListeners();
+    }
+  }
+
+  // Step 9 -- clear event listeners from our window, if we have one.
+  //
+  // Note that we explicitly want the inner window, and only if we're its
+  // document.  We want to do this (per spec) even when we're not the "active
+  // document", so we can't go through GetWindow(), because it might forward to
+  // the wrong inner.
+  if (nsPIDOMWindowInner* win = GetInnerWindow()) {
+    if (win->GetExtantDoc() == this) {
+      if (EventListenerManager* elm =
+              nsGlobalWindowInner::Cast(win)->GetExistingListenerManager()) {
+        elm->RemoveAllListeners();
+      }
+    }
+  }
+
+  // If we have a parser that has a zero script nesting level, we need to
+  // properly terminate it.  We do that after we've removed all the event
+  // listeners (so termination won't trigger event listeners if it does
+  // something to the DOM), but before we remove all elements from the document
+  // (so if termination does modify the DOM in some way we will just blow it
+  // away immediately.  See the similar code in WriteCommon that handles the
+  // !IsInsertionPointDefined() case and should stay in sync with this code.
+  if (mParser) {
+    MOZ_ASSERT(!mParser->HasNonzeroScriptNestingLevel(),
+               "Why didn't we take the early return?");
+    // Make sure we don't re-enter.
+    IgnoreOpensDuringUnload ignoreOpenGuard(this);
+    mParser->Terminate();
+    MOZ_RELEASE_ASSERT(!mParser, "mParser should have been null'd out");
+  }
+
+  // Step 10 -- remove all our DOM kids without firing any mutation events.
+  {
+    // We want to ignore any recursive calls to Open() that happen while
+    // disconnecting the node tree.  The spec doesn't say to do this, but the
+    // spec also doesn't envision unload events on subframes firing while we do
+    // this, while all browsers fire them in practice.  See
+    // <https://github.com/whatwg/html/issues/4611>.
+    IgnoreOpensDuringUnload ignoreOpenGuard(this);
+    DisconnectNodeTree();
+  }
+
+  // Step 11 -- if we're the current document in our docshell, do the
+  // equivalent of pushState() with the new URL we should have.
+  if (shell && IsCurrentActiveDocument()) {
+    nsCOMPtr<nsIURI> newURI = callerDoc->GetDocumentURI();
+    if (callerDoc != this) {
+      nsCOMPtr<nsIURI> noFragmentURI;
+      nsresult rv = NS_GetURIWithoutRef(newURI, getter_AddRefs(noFragmentURI));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        aError.Throw(rv);
+        return nullptr;
+      }
+      newURI = noFragmentURI.forget();
+    }
+
+    // UpdateURLAndHistory might do various member-setting, so make sure we're
+    // holding strong refs to all the refcounted args on the stack.  We can
+    // assume that our caller is holding on to "this" already.
+    nsCOMPtr<nsIURI> currentURI = GetDocumentURI();
+    bool equalURIs;
+    nsresult rv = currentURI->Equals(newURI, &equalURIs);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aError.Throw(rv);
+      return nullptr;
+    }
+    nsCOMPtr<nsIStructuredCloneContainer> stateContainer(mStateObjectContainer);
+    rv = shell->UpdateURLAndHistory(this, newURI, stateContainer, EmptyString(),
+                                    /* aReplace = */ true, currentURI,
+                                    equalURIs);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aError.Throw(rv);
+      return nullptr;
+    }
+
+    // And use the security info of the caller document as well, since
+    // it's the thing providing our data.
+    mSecurityInfo = callerDoc->GetSecurityInfo();
+
+    // This is not mentioned in the spec, but I think that's a spec bug.  See
+    // <https://github.com/whatwg/html/issues/4299>.  In any case, since our
+    // URL may be changing away from about:blank here, we really want to unset
+    // this flag no matter what, since only about:blank can be an initial
+    // document.
+    SetIsInitialDocument(false);
+
+    // And let our docloader know that it will need to track our load event.
+    nsDocShell::Cast(shell)->SetDocumentOpenedButNotLoaded();
+  }
+
+  // Per spec nothing happens with our URI in other cases, though note
+  // <https://github.com/whatwg/html/issues/4286>.
+
+  // Note that we don't need to do anything here with base URIs per spec.
+  // That said, this might be assuming that we implement
+  // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url
+  // correctly, which we don't right now for the about:blank case.
+
+  // Step 12, but note <https://github.com/whatwg/html/issues/4292>.
+  mSkipLoadEventAfterClose = mLoadEventFiring;
+
+  // Preliminary to steps 13-16.  Set our ready state to uninitialized before
+  // we do anything else, so we can then proceed to later ready state levels.
+  SetReadyStateInternal(READYSTATE_UNINITIALIZED,
+                        /* updateTimingInformation = */ false);
+
+  // Step 13 -- set our compat mode to standards.
+  SetCompatibilityMode(eCompatibility_FullStandards);
+
+  // Step 14 -- create a new parser associated with document.  This also does
+  // step 16 implicitly.
+  mParserAborted = false;
+  RefPtr<nsHtml5Parser> parser = nsHtml5Module::NewHtml5Parser();
+  mParser = parser;
+  parser->Initialize(this, GetDocumentURI(), shell, nullptr);
+  if (mReferrerPolicySet) {
+    // CSP may have set the referrer policy, so a speculative parser should
+    // start with the new referrer policy.
+    nsHtml5TreeOpExecutor* executor = nullptr;
+    executor = static_cast<nsHtml5TreeOpExecutor*>(mParser->GetContentSink());
+    if (executor && mReferrerPolicySet) {
+      executor->SetSpeculationReferrerPolicy(
+          static_cast<net::ReferrerPolicy>(mReferrerPolicy));
+    }
+  }
+  nsresult rv = parser->StartExecutor();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return nullptr;
+  }
+
+  if (shell) {
+    // Prepare the docshell and the document viewer for the impending
+    // out-of-band document.write()
+    shell->PrepareForNewContentModel();
+
+    nsCOMPtr<nsIContentViewer> cv;
+    shell->GetContentViewer(getter_AddRefs(cv));
+    if (cv) {
+      cv->LoadStart(this);
+    }
+  }
+
+  // Step 15.
+  SetReadyStateInternal(Document::READYSTATE_LOADING,
+                        /* updateTimingInformation = */ false);
+
+  // Step 16 happened with step 14 above.
+
+  // Step 17.
+  return this;
+}
+
+void Document::Close(ErrorResult& rv) {
+  if (!IsHTMLDocument()) {
+    // No calling document.close() on XHTML!
+
+    rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  if (ShouldThrowOnDynamicMarkupInsertion()) {
+    rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  if (!mParser || !mParser->IsScriptCreated()) {
+    return;
+  }
+
+  ++mWriteLevel;
+  rv = (static_cast<nsHtml5Parser*>(mParser.get()))
+           ->Parse(EmptyString(), nullptr, true);
+  --mWriteLevel;
+
+  // Even if that Parse() call failed, do the rest of this method
+
+  // XXX Make sure that all the document.written content is
+  // reflowed.  We should remove this call once we change
+  // Document::OpenCommon() so that it completely destroys the
+  // earlier document's content and frame hierarchy.  Right now, it
+  // re-uses the earlier document's root content object and
+  // corresponding frame objects.  These re-used frame objects think
+  // that they have already been reflowed, so they drop initial
+  // reflows.  For certain cases of document.written content, like a
+  // frameset document, the dropping of the initial reflow means
+  // that we end up in document.close() without appended any reflow
+  // commands to the reflow queue and, consequently, without adding
+  // the dummy layout request to the load group.  Since the dummy
+  // layout request is not added to the load group, the onload
+  // handler of the frameset fires before the frames get reflowed
+  // and loaded.  That is the long explanation for why we need this
+  // one line of code here!
+  // XXXbz as far as I can tell this may not be needed anymore; all
+  // the testcases in bug 57636 pass without this line...  Leaving
+  // it be for now, though.  In any case, there's no reason to do
+  // this if we have no presshell, since in that case none of the
+  // above about reusing frames applies.
+  //
+  // XXXhsivonen keeping this around for bug 577508 / 253951 still :-(
+  if (GetPresShell()) {
+    FlushPendingNotifications(FlushType::Layout);
+  }
+}
+
+void Document::WriteCommon(const Sequence<nsString>& aText,
+                           bool aNewlineTerminate, mozilla::ErrorResult& rv) {
+  // Fast path the common case
+  if (aText.Length() == 1) {
+    WriteCommon(aText[0], aNewlineTerminate, rv);
+  } else {
+    // XXXbz it would be nice if we could pass all the strings to the parser
+    // without having to do all this copying and then ask it to start
+    // parsing....
+    nsString text;
+    for (uint32_t i = 0; i < aText.Length(); ++i) {
+      text.Append(aText[i]);
+    }
+    WriteCommon(text, aNewlineTerminate, rv);
+  }
+}
+
+void Document::WriteCommon(const nsAString& aText, bool aNewlineTerminate,
+                           ErrorResult& aRv) {
+  mTooDeepWriteRecursion =
+      (mWriteLevel > NS_MAX_DOCUMENT_WRITE_DEPTH || mTooDeepWriteRecursion);
+  if (NS_WARN_IF(mTooDeepWriteRecursion)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  if (!IsHTMLDocument() || mDisableDocWrite) {
+    // No calling document.write*() on XHTML!
+
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  if (ShouldThrowOnDynamicMarkupInsertion()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  if (mParserAborted) {
+    // Hixie says aborting the parser doesn't undefine the insertion point.
+    // However, since we null out mParser in that case, we track the
+    // theoretically defined insertion point using mParserAborted.
+    return;
+  }
+
+  // Implement Step 4.1 of:
+  // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
+  if (ShouldIgnoreOpens()) {
+    return;
+  }
+
+  void* key = GenerateParserKey();
+  if (mParser && !mParser->IsInsertionPointDefined()) {
+    if (mIgnoreDestructiveWritesCounter) {
+      // Instead of implying a call to document.open(), ignore the call.
+      nsContentUtils::ReportToConsole(
+          nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM Events"), this,
+          nsContentUtils::eDOM_PROPERTIES, "DocumentWriteIgnored", nullptr, 0,
+          mDocumentURI);
+      return;
+    }
+    // The spec doesn't tell us to ignore opens from here, but we need to
+    // ensure opens are ignored here.  See similar code in Open() that handles
+    // the case of an existing parser which is not currently running script and
+    // should stay in sync with this code.
+    IgnoreOpensDuringUnload ignoreOpenGuard(this);
+    mParser->Terminate();
+    MOZ_RELEASE_ASSERT(!mParser, "mParser should have been null'd out");
+  }
+
+  if (!mParser) {
+    if (mIgnoreDestructiveWritesCounter) {
+      // Instead of implying a call to document.open(), ignore the call.
+      nsContentUtils::ReportToConsole(
+          nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM Events"), this,
+          nsContentUtils::eDOM_PROPERTIES, "DocumentWriteIgnored", nullptr, 0,
+          mDocumentURI);
+      return;
+    }
+
+    Open(Optional<nsAString>(), EmptyString(), aRv);
+
+    // If Open() fails, or if it didn't create a parser (as it won't
+    // if the user chose to not discard the current document through
+    // onbeforeunload), don't write anything.
+    if (aRv.Failed() || !mParser) {
+      return;
+    }
+  }
+
+  static NS_NAMED_LITERAL_STRING(new_line, "\n");
+
+  ++mWriteLevel;
+
+  // This could be done with less code, but for performance reasons it
+  // makes sense to have the code for two separate Parse() calls here
+  // since the concatenation of strings costs more than we like. And
+  // why pay that price when we don't need to?
+  if (aNewlineTerminate) {
+    aRv = (static_cast<nsHtml5Parser*>(mParser.get()))
+              ->Parse(aText + new_line, key, false);
+  } else {
+    aRv =
+        (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(aText, key, false);
+  }
+
+  --mWriteLevel;
+
+  mTooDeepWriteRecursion = (mWriteLevel != 0 && mTooDeepWriteRecursion);
+}
+
+void Document::Write(const Sequence<nsString>& aText, ErrorResult& rv) {
+  WriteCommon(aText, false, rv);
+}
+
+void Document::Writeln(const Sequence<nsString>& aText, ErrorResult& rv) {
+  WriteCommon(aText, true, rv);
+}
+
+void* Document::GenerateParserKey(void) {
+  if (!mScriptLoader) {
+    // If we don't have a script loader, then the parser probably isn't parsing
+    // anything anyway, so just return null.
+    return nullptr;
+  }
+
+  // The script loader provides us with the currently executing script element,
+  // which is guaranteed to be unique per script.
+  nsIScriptElement* script = mScriptLoader->GetCurrentParserInsertedScript();
+  if (script && mParser && mParser->IsScriptCreated()) {
+    nsCOMPtr<nsIParser> creatorParser = script->GetCreatorParser();
+    if (creatorParser != mParser) {
+      // Make scripts that aren't inserted by the active parser of this document
+      // participate in the context of the script that document.open()ed
+      // this document.
+      return nullptr;
+    }
+  }
+  return script;
+}
+
 /* static */
 bool Document::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
                                   nsAtom* aAtom, void* aData) {
   MOZ_ASSERT(aElement, "Must have element to work with!");
 
   if (!aElement->HasName()) {
     return false;
   }
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -1276,16 +1276,32 @@ class Document : public nsINode,
 
   /**
    * Called to disable client access to cookies through the document.cookie API
    * from user JavaScript code.
    */
   void DisableCookieAccess() { mDisableCookieAccess = true; }
 
   /**
+   * Set compatibility mode for this document
+   */
+  void SetCompatibilityMode(nsCompatibility aMode);
+
+  /**
+   * Called to disable client access to document.write() API from user
+   * JavaScript code.
+   */
+  void SetDocWriteDisabled(bool aDisabled) { mDisableDocWrite = aDisabled; }
+
+  /**
+   * Whether a document.write() call is in progress.
+   */
+  bool IsWriting() const { return mWriteLevel != uint32_t(0); }
+
+  /**
    * Access HTTP header data (this may also get set from other
    * sources, like HTML META tags).
    */
   void GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const;
   void SetHeaderData(nsAtom* aheaderField, const nsAString& aData);
 
   /**
    * Create a new presentation shell that will use aContext for its
@@ -3284,16 +3300,26 @@ class Document : public nsINode,
   nsIHTMLCollection* Plugins() { return Embeds(); }
   nsIHTMLCollection* Links();
   nsIHTMLCollection* Forms();
   nsIHTMLCollection* Scripts();
   already_AddRefed<nsContentList> GetElementsByName(const nsAString& aName) {
     return GetFuncStringContentList<nsCachableElementsByNameNodeList>(
         this, MatchNameAttribute, nullptr, UseExistingNameString, aName);
   }
+  Document* Open(const mozilla::dom::Optional<nsAString>& /* unused */,
+                 const nsAString& /* unused */, mozilla::ErrorResult& aError);
+  mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> Open(
+      const nsAString& aURL, const nsAString& aName, const nsAString& aFeatures,
+      bool aReplace, mozilla::ErrorResult& rv);
+  void Close(mozilla::ErrorResult& rv);
+  void Write(const mozilla::dom::Sequence<nsString>& aText,
+             mozilla::ErrorResult& rv);
+  void Writeln(const mozilla::dom::Sequence<nsString>& aText,
+               mozilla::ErrorResult& rv);
   Nullable<WindowProxyHolder> GetDefaultView() const;
   Element* GetActiveElement();
   bool HasFocus(ErrorResult& rv) const;
   nsIHTMLCollection* Applets();
   nsIHTMLCollection* Anchors();
   TimeStamp LastFocusTime() const;
   void SetLastFocusTime(const TimeStamp& aFocusTime);
   // Event handlers are all on nsINode already
@@ -3911,16 +3937,24 @@ class Document : public nsINode,
                                        bool aUpdateCSSLoader);
 
   already_AddRefed<nsIURI> GetDomainURI();
   already_AddRefed<nsIURI> CreateInheritingURIForHost(
       const nsACString& aHostString);
   already_AddRefed<nsIURI> RegistrableDomainSuffixOfInternal(
       const nsAString& aHostSuffixString, nsIURI* aOrigHost);
 
+  void WriteCommon(const nsAString& aText, bool aNewlineTerminate,
+                   mozilla::ErrorResult& aRv);
+  // A version of WriteCommon used by WebIDL bindings
+  void WriteCommon(const mozilla::dom::Sequence<nsString>& aText,
+                   bool aNewlineTerminate, mozilla::ErrorResult& rv);
+
+  void* GenerateParserKey(void);
+
  private:
   void RecordContentBlockingLog(
       const nsACString& aOrigin, uint32_t aType, bool aBlocked,
       const Maybe<AntiTrackingCommon::StorageAccessGrantedReason>& aReason =
           Nothing(),
       const nsTArray<nsCString>& aTrackingFullHashes = nsTArray<nsCString>()) {
     mContentBlockingLog.RecordLog(aOrigin, aType, aBlocked, aReason,
                                   aTrackingFullHashes);
@@ -4383,26 +4417,39 @@ class Document : public nsINode,
   // via document.close() should skip firing the load event.  Note that this
   // flag is only relevant for HTML documents, but lives here for reasons that
   // are documented above on SkipLoadEventAfterClose().
   bool mSkipLoadEventAfterClose : 1;
 
   // When false, the .cookies property is completely disabled
   bool mDisableCookieAccess : 1;
 
+  // When false, the document.write() API is disabled.
+  bool mDisableDocWrite : 1;
+
+  // Has document.write() been called with a recursion depth higher than
+  // allowed?
+  bool mTooDeepWriteRecursion : 1;
+
   uint8_t mPendingFullscreenRequests;
 
   uint8_t mXMLDeclarationBits;
 
   // Currently active onload blockers.
   uint32_t mOnloadBlockCount;
 
   // Onload blockers which haven't been activated yet.
   uint32_t mAsyncOnloadBlockCount;
 
+  // Tracks if we are currently processing any document.write calls (either
+  // implicit or explicit). Note that if a write call writes out something which
+  // would block the parser, then mWriteLevel will be incorrect until the parser
+  // finishes processing that script.
+  uint32_t mWriteLevel;
+
   // Compatibility mode
   nsCompatibility mCompatMode;
 
   // Our readyState
   ReadyState mReadyState;
 
   // Ancestor's loading state
   bool mAncestorIsLoading;
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -655,30 +655,28 @@ nsresult HTMLContentSink::Init(Document*
 
 NS_IMETHODIMP
 HTMLContentSink::WillParse(void) { return WillParseImpl(); }
 
 NS_IMETHODIMP
 HTMLContentSink::WillBuildModel(nsDTDMode aDTDMode) {
   WillBuildModelImpl();
 
-  if (mHTMLDocument) {
-    nsCompatibility mode = eCompatibility_NavQuirks;
-    switch (aDTDMode) {
-      case eDTDMode_full_standards:
-        mode = eCompatibility_FullStandards;
-        break;
-      case eDTDMode_almost_standards:
-        mode = eCompatibility_AlmostStandards;
-        break;
-      default:
-        break;
-    }
-    mHTMLDocument->SetCompatibilityMode(mode);
+  nsCompatibility mode = eCompatibility_NavQuirks;
+  switch (aDTDMode) {
+    case eDTDMode_full_standards:
+      mode = eCompatibility_FullStandards;
+      break;
+    case eDTDMode_almost_standards:
+      mode = eCompatibility_AlmostStandards;
+      break;
+    default:
+      break;
   }
+  mDocument->SetCompatibilityMode(mode);
 
   // Notify document that the load is beginning
   mDocument->BeginLoad();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -115,18 +115,16 @@
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/Unused.h"
 #include "nsCommandParams.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-#define NS_MAX_DOCUMENT_WRITE_DEPTH 20
-
 #include "prtime.h"
 
 //#define DEBUG_charset
 
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
 // this function will return false if the command is not recognized
 // inCommandID will be converted as necessary for internal operations
@@ -166,20 +164,17 @@ nsresult NS_NewHTMLDocument(Document** a
 
   return NS_OK;
 }
 
 nsHTMLDocument::nsHTMLDocument()
     : Document("text/html"),
       mContentListHolder(nullptr),
       mNumForms(0),
-      mWriteLevel(0),
       mLoadFlags(0),
-      mTooDeepWriteRecursion(false),
-      mDisableDocWrite(false),
       mWarnedWidthHeight(false),
       mContentEditableCount(0),
       mEditingState(EditingState::eOff),
       mPendingMaybeEditingStateChanged(false),
       mHasBeenEditable(false),
       mIsPlainText(false) {
   mType = eHTML;
   mDefaultElementType = kNameSpaceID_XHTML;
@@ -746,27 +741,16 @@ void nsHTMLDocument::EndLoad() {
     SetReadyStateInternal(Document::READYSTATE_COMPLETE,
                           /* updateTimingInformation = */ false);
 
     // Reset mSkipLoadEventAfterClose just in case.
     mSkipLoadEventAfterClose = false;
   }
 }
 
-void nsHTMLDocument::SetCompatibilityMode(nsCompatibility aMode) {
-  NS_ASSERTION(IsHTMLDocument() || aMode == eCompatibility_FullStandards,
-               "Bad compat mode for XHTML document!");
-
-  if (mCompatMode == aMode) {
-    return;
-  }
-  mCompatMode = aMode;
-  CompatibilityModeChanged();
-}
-
 bool nsHTMLDocument::UseWidthDeviceWidthFallbackViewport() const {
   if (mIsPlainText) {
     // Plain text documents are simple enough that font inflation doesn't offer
     // any appreciable advantage over defaulting to "width=device-width" and
     // subsequently turning on word-wrapping.
     return true;
   }
   return Document::UseWidthDeviceWidthFallbackViewport();
@@ -796,475 +780,16 @@ bool nsHTMLDocument::IsRegistrableDomain
       RegistrableDomainSuffixOfInternal(aHostSuffixString, origURI);
   if (!newURI) {
     // Error: illegal domain
     return false;
   }
   return true;
 }
 
-mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> nsHTMLDocument::Open(
-    const nsAString& aURL, const nsAString& aName, const nsAString& aFeatures,
-    bool aReplace, ErrorResult& rv) {
-  MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
-             "XOW should have caught this!");
-
-  nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
-  if (!window) {
-    rv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return nullptr;
-  }
-  nsCOMPtr<nsPIDOMWindowOuter> outer =
-      nsPIDOMWindowOuter::GetFromCurrentInner(window);
-  if (!outer) {
-    rv.Throw(NS_ERROR_NOT_INITIALIZED);
-    return nullptr;
-  }
-  RefPtr<nsGlobalWindowOuter> win = nsGlobalWindowOuter::Cast(outer);
-  nsCOMPtr<nsPIDOMWindowOuter> newWindow;
-  // XXXbz We ignore aReplace for now.
-  rv = win->OpenJS(aURL, aName, aFeatures, getter_AddRefs(newWindow));
-  if (!newWindow) {
-    return nullptr;
-  }
-  return WindowProxyHolder(newWindow->GetBrowsingContext());
-}
-
-Document* nsHTMLDocument::Open(const Optional<nsAString>& /* unused */,
-                               const nsAString& /* unused */,
-                               ErrorResult& aError) {
-  // Implements
-  // <https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps>
-
-  MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
-             "XOW should have caught this!");
-
-  // Step 1 -- throw if we're an XML document.
-  if (!IsHTMLDocument() || mDisableDocWrite) {
-    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-
-  // Step 2 -- throw if dynamic markup insertion should throw.
-  if (ShouldThrowOnDynamicMarkupInsertion()) {
-    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-
-  // Step 3 -- get the entry document, so we can use it for security checks.
-  nsCOMPtr<Document> callerDoc = GetEntryDocument();
-  if (!callerDoc) {
-    // If we're called from C++ or in some other way without an originating
-    // document we can't do a document.open w/o changing the principal of the
-    // document to something like about:blank (as that's the only sane thing to
-    // do when we don't know the origin of this call), and since we can't
-    // change the principals of a document for security reasons we'll have to
-    // refuse to go ahead with this call.
-
-    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return nullptr;
-  }
-
-  // Step 4 -- make sure we're same-origin (not just same origin-domain) with
-  // the entry document.
-  if (!callerDoc->NodePrincipal()->Equals(NodePrincipal())) {
-    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return nullptr;
-  }
-
-  // Step 5 -- if we have an active parser with a nonzero script nesting level,
-  // just no-op.
-  //
-  // The mParserAborted check here is probably wrong.  Removing it is
-  // tracked in https://bugzilla.mozilla.org/show_bug.cgi?id=1475000
-  if ((mParser && mParser->HasNonzeroScriptNestingLevel()) || mParserAborted) {
-    return this;
-  }
-
-  // Step 6 -- check for open() during unload.  Per spec, this is just a check
-  // of the ignore-opens-during-unload counter, but our unload event code
-  // doesn't affect that counter yet (unlike pagehide and beforeunload, which
-  // do), so we check for unload directly.
-  if (ShouldIgnoreOpens()) {
-    return this;
-  }
-
-  nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
-  if (shell) {
-    bool inUnload;
-    shell->GetIsInUnload(&inUnload);
-    if (inUnload) {
-      return this;
-    }
-  }
-
-  // document.open() inherits the CSP from the opening document.
-  // Please create an actual copy of the CSP (do not share the same
-  // reference) otherwise appending a new policy within the opened
-  // document will be incorrectly propagated to the opening doc.
-  nsCOMPtr<nsIContentSecurityPolicy> csp = callerDoc->GetCsp();
-  if (csp) {
-    RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
-    cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
-    mCSP = cspToInherit;
-  }
-
-  // At this point we know this is a valid-enough document.open() call
-  // and not a no-op.  Increment our use counter.
-  SetDocumentAndPageUseCounter(eUseCounter_custom_DocumentOpen);
-
-  // Step 7 -- stop existing navigation of our browsing context (and all other
-  // loads it's doing) if we're the active document of our browsing context.
-  // Note that we do not want to stop anything if there is no existing
-  // navigation.
-  if (shell && IsCurrentActiveDocument() &&
-      shell->GetIsAttemptingToNavigate()) {
-    nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(shell));
-    webnav->Stop(nsIWebNavigation::STOP_NETWORK);
-
-    // The Stop call may have cancelled the onload blocker request or
-    // prevented it from getting added, so we need to make sure it gets added
-    // to the document again otherwise the document could have a non-zero
-    // onload block count without the onload blocker request being in the
-    // loadgroup.
-    EnsureOnloadBlocker();
-  }
-
-  // Step 8 -- clear event listeners out of our DOM tree
-  for (nsINode* node : ShadowIncludingTreeIterator(*this)) {
-    if (EventListenerManager* elm = node->GetExistingListenerManager()) {
-      elm->RemoveAllListeners();
-    }
-  }
-
-  // Step 9 -- clear event listeners from our window, if we have one.
-  //
-  // Note that we explicitly want the inner window, and only if we're its
-  // document.  We want to do this (per spec) even when we're not the "active
-  // document", so we can't go through GetWindow(), because it might forward to
-  // the wrong inner.
-  if (nsPIDOMWindowInner* win = GetInnerWindow()) {
-    if (win->GetExtantDoc() == this) {
-      if (EventListenerManager* elm =
-              nsGlobalWindowInner::Cast(win)->GetExistingListenerManager()) {
-        elm->RemoveAllListeners();
-      }
-    }
-  }
-
-  // If we have a parser that has a zero script nesting level, we need to
-  // properly terminate it.  We do that after we've removed all the event
-  // listeners (so termination won't trigger event listeners if it does
-  // something to the DOM), but before we remove all elements from the document
-  // (so if termination does modify the DOM in some way we will just blow it
-  // away immediately.  See the similar code in WriteCommon that handles the
-  // !IsInsertionPointDefined() case and should stay in sync with this code.
-  if (mParser) {
-    MOZ_ASSERT(!mParser->HasNonzeroScriptNestingLevel(),
-               "Why didn't we take the early return?");
-    // Make sure we don't re-enter.
-    IgnoreOpensDuringUnload ignoreOpenGuard(this);
-    mParser->Terminate();
-    MOZ_RELEASE_ASSERT(!mParser, "mParser should have been null'd out");
-  }
-
-  // Step 10 -- remove all our DOM kids without firing any mutation events.
-  {
-    // We want to ignore any recursive calls to Open() that happen while
-    // disconnecting the node tree.  The spec doesn't say to do this, but the
-    // spec also doesn't envision unload events on subframes firing while we do
-    // this, while all browsers fire them in practice.  See
-    // <https://github.com/whatwg/html/issues/4611>.
-    IgnoreOpensDuringUnload ignoreOpenGuard(this);
-    DisconnectNodeTree();
-  }
-
-  // Step 11 -- if we're the current document in our docshell, do the
-  // equivalent of pushState() with the new URL we should have.
-  if (shell && IsCurrentActiveDocument()) {
-    nsCOMPtr<nsIURI> newURI = callerDoc->GetDocumentURI();
-    if (callerDoc != this) {
-      nsCOMPtr<nsIURI> noFragmentURI;
-      nsresult rv = NS_GetURIWithoutRef(newURI, getter_AddRefs(noFragmentURI));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        aError.Throw(rv);
-        return nullptr;
-      }
-      newURI = noFragmentURI.forget();
-    }
-
-    // UpdateURLAndHistory might do various member-setting, so make sure we're
-    // holding strong refs to all the refcounted args on the stack.  We can
-    // assume that our caller is holding on to "this" already.
-    nsCOMPtr<nsIURI> currentURI = GetDocumentURI();
-    bool equalURIs;
-    nsresult rv = currentURI->Equals(newURI, &equalURIs);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aError.Throw(rv);
-      return nullptr;
-    }
-    nsCOMPtr<nsIStructuredCloneContainer> stateContainer(mStateObjectContainer);
-    rv = shell->UpdateURLAndHistory(this, newURI, stateContainer, EmptyString(),
-                                    /* aReplace = */ true, currentURI,
-                                    equalURIs);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aError.Throw(rv);
-      return nullptr;
-    }
-
-    // And use the security info of the caller document as well, since
-    // it's the thing providing our data.
-    mSecurityInfo = callerDoc->GetSecurityInfo();
-
-    // This is not mentioned in the spec, but I think that's a spec bug.  See
-    // <https://github.com/whatwg/html/issues/4299>.  In any case, since our
-    // URL may be changing away from about:blank here, we really want to unset
-    // this flag no matter what, since only about:blank can be an initial
-    // document.
-    SetIsInitialDocument(false);
-
-    // And let our docloader know that it will need to track our load event.
-    nsDocShell::Cast(shell)->SetDocumentOpenedButNotLoaded();
-  }
-
-  // Per spec nothing happens with our URI in other cases, though note
-  // <https://github.com/whatwg/html/issues/4286>.
-
-  // Note that we don't need to do anything here with base URIs per spec.
-  // That said, this might be assuming that we implement
-  // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url
-  // correctly, which we don't right now for the about:blank case.
-
-  // Step 12, but note <https://github.com/whatwg/html/issues/4292>.
-  mSkipLoadEventAfterClose = mLoadEventFiring;
-
-  // Preliminary to steps 13-16.  Set our ready state to uninitialized before
-  // we do anything else, so we can then proceed to later ready state levels.
-  SetReadyStateInternal(READYSTATE_UNINITIALIZED,
-                        /* updateTimingInformation = */ false);
-
-  // Step 13 -- set our compat mode to standards.
-  SetCompatibilityMode(eCompatibility_FullStandards);
-
-  // Step 14 -- create a new parser associated with document.  This also does
-  // step 16 implicitly.
-  mParserAborted = false;
-  RefPtr<nsHtml5Parser> parser = nsHtml5Module::NewHtml5Parser();
-  mParser = parser;
-  parser->Initialize(this, GetDocumentURI(), shell, nullptr);
-  if (mReferrerPolicySet) {
-    // CSP may have set the referrer policy, so a speculative parser should
-    // start with the new referrer policy.
-    nsHtml5TreeOpExecutor* executor = nullptr;
-    executor = static_cast<nsHtml5TreeOpExecutor*>(mParser->GetContentSink());
-    if (executor && mReferrerPolicySet) {
-      executor->SetSpeculationReferrerPolicy(
-          static_cast<ReferrerPolicy>(mReferrerPolicy));
-    }
-  }
-  nsresult rv = parser->StartExecutor();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aError.Throw(rv);
-    return nullptr;
-  }
-
-  if (shell) {
-    // Prepare the docshell and the document viewer for the impending
-    // out-of-band document.write()
-    shell->PrepareForNewContentModel();
-
-    nsCOMPtr<nsIContentViewer> cv;
-    shell->GetContentViewer(getter_AddRefs(cv));
-    if (cv) {
-      cv->LoadStart(this);
-    }
-  }
-
-  // Step 15.
-  SetReadyStateInternal(Document::READYSTATE_LOADING,
-                        /* updateTimingInformation = */ false);
-
-  // Step 16 happened with step 14 above.
-
-  // Step 17.
-  return this;
-}
-
-void nsHTMLDocument::Close(ErrorResult& rv) {
-  if (!IsHTMLDocument()) {
-    // No calling document.close() on XHTML!
-
-    rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-
-  if (ShouldThrowOnDynamicMarkupInsertion()) {
-    rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-
-  if (!mParser || !mParser->IsScriptCreated()) {
-    return;
-  }
-
-  ++mWriteLevel;
-  rv = (static_cast<nsHtml5Parser*>(mParser.get()))
-           ->Parse(EmptyString(), nullptr, true);
-  --mWriteLevel;
-
-  // Even if that Parse() call failed, do the rest of this method
-
-  // XXX Make sure that all the document.written content is
-  // reflowed.  We should remove this call once we change
-  // nsHTMLDocument::OpenCommon() so that it completely destroys the
-  // earlier document's content and frame hierarchy.  Right now, it
-  // re-uses the earlier document's root content object and
-  // corresponding frame objects.  These re-used frame objects think
-  // that they have already been reflowed, so they drop initial
-  // reflows.  For certain cases of document.written content, like a
-  // frameset document, the dropping of the initial reflow means
-  // that we end up in document.close() without appended any reflow
-  // commands to the reflow queue and, consequently, without adding
-  // the dummy layout request to the load group.  Since the dummy
-  // layout request is not added to the load group, the onload
-  // handler of the frameset fires before the frames get reflowed
-  // and loaded.  That is the long explanation for why we need this
-  // one line of code here!
-  // XXXbz as far as I can tell this may not be needed anymore; all
-  // the testcases in bug 57636 pass without this line...  Leaving
-  // it be for now, though.  In any case, there's no reason to do
-  // this if we have no presshell, since in that case none of the
-  // above about reusing frames applies.
-  //
-  // XXXhsivonen keeping this around for bug 577508 / 253951 still :-(
-  if (GetPresShell()) {
-    FlushPendingNotifications(FlushType::Layout);
-  }
-}
-
-void nsHTMLDocument::WriteCommon(const Sequence<nsString>& aText,
-                                 bool aNewlineTerminate,
-                                 mozilla::ErrorResult& rv) {
-  // Fast path the common case
-  if (aText.Length() == 1) {
-    WriteCommon(aText[0], aNewlineTerminate, rv);
-  } else {
-    // XXXbz it would be nice if we could pass all the strings to the parser
-    // without having to do all this copying and then ask it to start
-    // parsing....
-    nsString text;
-    for (uint32_t i = 0; i < aText.Length(); ++i) {
-      text.Append(aText[i]);
-    }
-    WriteCommon(text, aNewlineTerminate, rv);
-  }
-}
-
-void nsHTMLDocument::WriteCommon(const nsAString& aText, bool aNewlineTerminate,
-                                 ErrorResult& aRv) {
-  mTooDeepWriteRecursion =
-      (mWriteLevel > NS_MAX_DOCUMENT_WRITE_DEPTH || mTooDeepWriteRecursion);
-  if (NS_WARN_IF(mTooDeepWriteRecursion)) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return;
-  }
-
-  if (!IsHTMLDocument() || mDisableDocWrite) {
-    // No calling document.write*() on XHTML!
-
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-
-  if (ShouldThrowOnDynamicMarkupInsertion()) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
-
-  if (mParserAborted) {
-    // Hixie says aborting the parser doesn't undefine the insertion point.
-    // However, since we null out mParser in that case, we track the
-    // theoretically defined insertion point using mParserAborted.
-    return;
-  }
-
-  // Implement Step 4.1 of:
-  // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
-  if (ShouldIgnoreOpens()) {
-    return;
-  }
-
-  void* key = GenerateParserKey();
-  if (mParser && !mParser->IsInsertionPointDefined()) {
-    if (mIgnoreDestructiveWritesCounter) {
-      // Instead of implying a call to document.open(), ignore the call.
-      nsContentUtils::ReportToConsole(
-          nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM Events"), this,
-          nsContentUtils::eDOM_PROPERTIES, "DocumentWriteIgnored", nullptr, 0,
-          mDocumentURI);
-      return;
-    }
-    // The spec doesn't tell us to ignore opens from here, but we need to
-    // ensure opens are ignored here.  See similar code in Open() that handles
-    // the case of an existing parser which is not currently running script and
-    // should stay in sync with this code.
-    IgnoreOpensDuringUnload ignoreOpenGuard(this);
-    mParser->Terminate();
-    MOZ_RELEASE_ASSERT(!mParser, "mParser should have been null'd out");
-  }
-
-  if (!mParser) {
-    if (mIgnoreDestructiveWritesCounter) {
-      // Instead of implying a call to document.open(), ignore the call.
-      nsContentUtils::ReportToConsole(
-          nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM Events"), this,
-          nsContentUtils::eDOM_PROPERTIES, "DocumentWriteIgnored", nullptr, 0,
-          mDocumentURI);
-      return;
-    }
-
-    Open(Optional<nsAString>(), EmptyString(), aRv);
-
-    // If Open() fails, or if it didn't create a parser (as it won't
-    // if the user chose to not discard the current document through
-    // onbeforeunload), don't write anything.
-    if (aRv.Failed() || !mParser) {
-      return;
-    }
-  }
-
-  static NS_NAMED_LITERAL_STRING(new_line, "\n");
-
-  ++mWriteLevel;
-
-  // This could be done with less code, but for performance reasons it
-  // makes sense to have the code for two separate Parse() calls here
-  // since the concatenation of strings costs more than we like. And
-  // why pay that price when we don't need to?
-  if (aNewlineTerminate) {
-    aRv = (static_cast<nsHtml5Parser*>(mParser.get()))
-              ->Parse(aText + new_line, key, false);
-  } else {
-    aRv =
-        (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(aText, key, false);
-  }
-
-  --mWriteLevel;
-
-  mTooDeepWriteRecursion = (mWriteLevel != 0 && mTooDeepWriteRecursion);
-}
-
-void nsHTMLDocument::Write(const Sequence<nsString>& aText, ErrorResult& rv) {
-  WriteCommon(aText, false, rv);
-}
-
-void nsHTMLDocument::Writeln(const Sequence<nsString>& aText, ErrorResult& rv) {
-  WriteCommon(aText, true, rv);
-}
-
 void nsHTMLDocument::AddedForm() { ++mNumForms; }
 
 void nsHTMLDocument::RemovedForm() { --mNumForms; }
 
 int32_t nsHTMLDocument::GetNumFormsSynchronous() { return mNumForms; }
 
 void nsHTMLDocument::GetAlinkColor(nsAString& aAlinkColor) {
   aAlinkColor.Truncate();
@@ -1412,38 +937,16 @@ void nsHTMLDocument::GetSupportedNames(n
 
 // forms related stuff
 
 bool nsHTMLDocument::MatchFormControls(Element* aElement, int32_t aNamespaceID,
                                        nsAtom* aAtom, void* aData) {
   return aElement->IsNodeOfType(nsIContent::eHTML_FORM_CONTROL);
 }
 
-void* nsHTMLDocument::GenerateParserKey(void) {
-  if (!mScriptLoader) {
-    // If we don't have a script loader, then the parser probably isn't parsing
-    // anything anyway, so just return null.
-    return nullptr;
-  }
-
-  // The script loader provides us with the currently executing script element,
-  // which is guaranteed to be unique per script.
-  nsIScriptElement* script = mScriptLoader->GetCurrentParserInsertedScript();
-  if (script && mParser && mParser->IsScriptCreated()) {
-    nsCOMPtr<nsIParser> creatorParser = script->GetCreatorParser();
-    if (creatorParser != mParser) {
-      // Make scripts that aren't inserted by the active parser of this document
-      // participate in the context of the script that document.open()ed
-      // this document.
-      return nullptr;
-    }
-  }
-  return script;
-}
-
 void nsHTMLDocument::GetDesignMode(nsAString& aDesignMode) {
   if (HasFlag(NODE_IS_EDITABLE)) {
     aDesignMode.AssignLiteral("on");
   } else {
     aDesignMode.AssignLiteral("off");
   }
 }
 
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -69,20 +69,16 @@ class nsHTMLDocument : public mozilla::d
   virtual void BeginLoad() override;
   virtual void EndLoad() override;
 
  protected:
   virtual bool UseWidthDeviceWidthFallbackViewport() const override;
 
  public:
   // nsIHTMLDocument
-  virtual void SetCompatibilityMode(nsCompatibility aMode) override;
-
-  virtual bool IsWriting() override { return mWriteLevel != uint32_t(0); }
-
   virtual Element* GetUnfocusedKeyEventTarget() override;
 
   nsContentList* GetExistingForms() const { return mForms; }
 
   mozilla::dom::HTMLAllCollection* All();
 
   // Returns whether an object was found for aName.
   bool ResolveName(JSContext* aCx, const nsAString& aName,
@@ -91,19 +87,16 @@ class nsHTMLDocument : public mozilla::d
 
   virtual void AddedForm() override;
   virtual void RemovedForm() override;
   virtual int32_t GetNumFormsSynchronous() override;
   virtual void TearingDownEditor() override;
   virtual void SetIsXHTML(bool aXHTML) override {
     mType = (aXHTML ? eXHTML : eHTML);
   }
-  virtual void SetDocWriteDisabled(bool aDisabled) override {
-    mDisableDocWrite = aDisabled;
-  }
 
   nsresult ChangeContentEditableCount(nsIContent* aElement,
                                       int32_t aChange) override;
   void DeferredContentEditableCountChange(nsIContent* aElement);
 
   virtual EditingState GetEditingState() override { return mEditingState; }
 
   class nsAutoEditingState {
@@ -147,26 +140,16 @@ class nsHTMLDocument : public mozilla::d
                    JS::MutableHandle<JSObject*> aRetval,
                    mozilla::ErrorResult& rv) {
     JS::Rooted<JS::Value> v(cx);
     if ((aFound = ResolveName(cx, aName, &v, rv))) {
       aRetval.set(v.toObjectOrNull());
     }
   }
   void GetSupportedNames(nsTArray<nsString>& aNames);
-  Document* Open(const mozilla::dom::Optional<nsAString>& /* unused */,
-                 const nsAString& /* unused */, mozilla::ErrorResult& aError);
-  mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> Open(
-      const nsAString& aURL, const nsAString& aName, const nsAString& aFeatures,
-      bool aReplace, mozilla::ErrorResult& rv);
-  void Close(mozilla::ErrorResult& rv);
-  void Write(const mozilla::dom::Sequence<nsString>& aText,
-             mozilla::ErrorResult& rv);
-  void Writeln(const mozilla::dom::Sequence<nsString>& aText,
-               mozilla::ErrorResult& rv);
   void GetDesignMode(nsAString& aDesignMode);
   void SetDesignMode(const nsAString& aDesignMode,
                      nsIPrincipal& aSubjectPrincipal, mozilla::ErrorResult& rv);
   void SetDesignMode(const nsAString& aDesignMode,
                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
                      mozilla::ErrorResult& rv);
   MOZ_CAN_RUN_SCRIPT
   bool ExecCommand(const nsAString& aCommandID, bool aDoShowUI,
@@ -217,29 +200,21 @@ class nsHTMLDocument : public mozilla::d
   ~nsHTMLDocument();
 
   nsresult GetBodySize(int32_t* aWidth, int32_t* aHeight);
 
   nsIContent* MatchId(nsIContent* aContent, const nsAString& aId);
 
   static void DocumentWriteTerminationFunc(nsISupports* aRef);
 
-  void WriteCommon(const nsAString& aText, bool aNewlineTerminate,
-                   mozilla::ErrorResult& aRv);
-  // A version of WriteCommon used by WebIDL bindings
-  void WriteCommon(const mozilla::dom::Sequence<nsString>& aText,
-                   bool aNewlineTerminate, mozilla::ErrorResult& rv);
-
   /**
    * Like IsEditingOn(), but will flush as needed first.
    */
   bool IsEditingOnAfterFlush();
 
-  void* GenerateParserKey(void);
-
   // A helper class to keep nsContentList objects alive for a short period of
   // time. Note, when the final Release is called on an nsContentList object, it
   // removes itself from MutationObserver list.
   class ContentListHolder : public mozilla::Runnable {
    public:
     ContentListHolder(nsHTMLDocument* aDocument, nsContentList* aFormList,
                       nsContentList* aFormControlList)
         : mozilla::Runnable("ContentListHolder"),
@@ -285,29 +260,19 @@ class nsHTMLDocument : public mozilla::d
    * MaybeDispatchCheckKeyPressEventModelEvent() dispatches
    * "CheckKeyPressEventModel" event to check whether we should dispatch
    * keypress events in confluent model or split model.  This should be
    * called only when mEditingState is changed to eDesignMode or
    * eConentEditable at first time.
    */
   void MaybeDispatchCheckKeyPressEventModelEvent();
 
-  // Tracks if we are currently processing any document.write calls (either
-  // implicit or explicit). Note that if a write call writes out something which
-  // would block the parser, then mWriteLevel will be incorrect until the parser
-  // finishes processing that script.
-  uint32_t mWriteLevel;
-
   // Load flags of the document's channel
   uint32_t mLoadFlags;
 
-  bool mTooDeepWriteRecursion;
-
-  bool mDisableDocWrite;
-
   bool mWarnedWidthHeight;
 
   /* Midas implementation */
   nsCommandManager* GetMidasCommandManager();
 
   RefPtr<nsCommandManager> mMidasCommandManager;
 
   nsresult TurnEditingOff();
--- a/dom/html/nsIHTMLDocument.h
+++ b/dom/html/nsIHTMLDocument.h
@@ -23,21 +23,16 @@ class nsContentList;
 /**
  * HTML document extensions to Document.
  */
 class nsIHTMLDocument : public nsISupports {
  public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IHTMLDOCUMENT_IID)
 
   /**
-   * Set compatibility mode for this document
-   */
-  virtual void SetCompatibilityMode(nsCompatibility aMode) = 0;
-
-  /**
    * Called when form->BindToTree() is called so that document knows
    * immediately when a form is added
    */
   virtual void AddedForm() = 0;
   /**
    * Called when form->SetDocument() is called so that document knows
    * immediately when a form is removed
    */
@@ -45,18 +40,16 @@ class nsIHTMLDocument : public nsISuppor
   /**
    * Called to get a better count of forms than document.forms can provide
    * without calling FlushPendingNotifications (bug 138892).
    */
   // XXXbz is this still needed now that we can flush just content,
   // not the rest?
   virtual int32_t GetNumFormsSynchronous() = 0;
 
-  virtual bool IsWriting() = 0;
-
   /**
    * Should be called when an element's editable changes as a result of
    * changing its contentEditable attribute/property.
    *
    * @param aElement the element for which the contentEditable
    *                 attribute/property was changed
    * @param aChange +1 if the contentEditable attribute/property was changed to
    *                true, -1 if it was changed to false
@@ -94,15 +87,13 @@ class nsIHTMLDocument : public nsISuppor
   virtual nsresult SetEditingState(EditingState aState) = 0;
 
   /**
    * Called when this nsIHTMLDocument's editor is destroyed.
    */
   virtual void TearingDownEditor() = 0;
 
   virtual void SetIsXHTML(bool aXHTML) = 0;
-
-  virtual void SetDocWriteDisabled(bool aDisabled) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLDocument, NS_IHTMLDOCUMENT_IID)
 
 #endif /* nsIHTMLDocument_h */
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -139,21 +139,26 @@ partial interface Document {
   [SameObject] readonly attribute HTMLCollection links;
   [SameObject] readonly attribute HTMLCollection forms;
   [SameObject] readonly attribute HTMLCollection scripts;
   [Pure]
   NodeList getElementsByName(DOMString elementName);
   //(Not implemented)readonly attribute DOMElementMap cssElementMap;
 
   // dynamic markup insertion
-  //(HTML only)Document open(optional DOMString type, optional DOMString replace);
-  //(HTML only)WindowProxy open(DOMString url, DOMString name, DOMString features, optional boolean replace);
-  //(HTML only)void close();
-  //(HTML only)void write(DOMString... text);
-  //(HTML only)void writeln(DOMString... text);
+  [CEReactions, Throws]
+  Document open(optional DOMString type, optional DOMString replace = ""); // type is ignored
+  [CEReactions, Throws]
+  WindowProxy? open(DOMString url, DOMString name, DOMString features, optional boolean replace = false);
+  [CEReactions, Throws]
+  void close();
+  [CEReactions, Throws]
+  void write(DOMString... text);
+  [CEReactions, Throws]
+  void writeln(DOMString... text);
 
   // user interaction
   [Pure]
   readonly attribute WindowProxy? defaultView;
   [Throws]
   boolean hasFocus();
   //(HTML only)         attribute DOMString designMode;
   //(HTML only)boolean execCommand(DOMString commandId);
--- a/dom/webidl/HTMLDocument.webidl
+++ b/dom/webidl/HTMLDocument.webidl
@@ -5,28 +5,16 @@
  */
 
 [OverrideBuiltins]
 interface HTMLDocument : Document {
   // DOM tree accessors
   [Throws]
   getter object (DOMString name);
 
-  // dynamic markup insertion
-  [CEReactions, Throws]
-  Document open(optional DOMString type, optional DOMString replace = ""); // type is ignored
-  [CEReactions, Throws]
-  WindowProxy? open(DOMString url, DOMString name, DOMString features, optional boolean replace = false);
-  [CEReactions, Throws]
-  void close();
-  [CEReactions, Throws]
-  void write(DOMString... text);
-  [CEReactions, Throws]
-  void writeln(DOMString... text);
-
   [CEReactions, SetterThrows, SetterNeedsSubjectPrincipal]
            attribute DOMString designMode;
   [CEReactions, Throws, NeedsSubjectPrincipal]
   boolean execCommand(DOMString commandId, optional boolean showUI = false,
                       optional DOMString value = "");
   [Throws, NeedsSubjectPrincipal]
   boolean queryCommandEnabled(DOMString commandId);
   [Throws]
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -118,17 +118,17 @@ nsresult NS_NewDOMDocument(Document** aI
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (isHTML) {
     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(d);
     NS_ASSERTION(htmlDoc, "HTML Document doesn't implement nsIHTMLDocument?");
-    htmlDoc->SetCompatibilityMode(eCompatibility_FullStandards);
+    d->SetCompatibilityMode(eCompatibility_FullStandards);
     htmlDoc->SetIsXHTML(isXHTML);
   }
   d->SetLoadedAsData(aLoadedAsData);
   d->SetDocumentURI(aDocumentURI);
   // Must set the principal first, since SetBaseURI checks it.
   d->SetPrincipals(aPrincipal, aPrincipal);
   d->SetBaseURI(aBaseURI);
 
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -317,20 +317,17 @@ nsXMLContentSink::DidBuildModel(bool aTe
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXMLContentSink::OnDocumentCreated(Document* aResultDocument) {
   NS_ENSURE_ARG(aResultDocument);
 
-  nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aResultDocument);
-  if (htmlDoc) {
-    htmlDoc->SetDocWriteDisabled(true);
-  }
+  aResultDocument->SetDocWriteDisabled(true);
 
   nsCOMPtr<nsIContentViewer> contentViewer;
   mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
   if (contentViewer) {
     return contentViewer->SetDocumentInternal(aResultDocument, true);
   }
   return NS_OK;
 }
@@ -357,20 +354,17 @@ nsXMLContentSink::OnTransformDone(nsresu
   if (!mRunsToCompletion) {
     // This BlockOnload call corresponds to the UnblockOnload call in
     // nsContentSink::DropParserAndPerfHint.
     aResultDocument->BlockOnload();
     mIsBlockingOnload = true;
   }
   // Transform succeeded, or it failed and we have an error document to display.
   mDocument = aResultDocument;
-  nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
-  if (htmlDoc) {
-    htmlDoc->SetDocWriteDisabled(false);
-  }
+  aResultDocument->SetDocWriteDisabled(false);
 
   // Notify document observers that all the content has been stuck
   // into the document.
   // XXX do we need to notify for things like PIs?  Or just the
   // documentElement?
   nsIContent* rootElement = mDocument->GetRootElement();
   if (rootElement) {
     NS_ASSERTION(mDocument->ComputeIndexOf(rootElement) != -1,
--- a/dom/xslt/xslt/txMozillaXMLOutput.cpp
+++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp
@@ -807,19 +807,18 @@ nsresult txMozillaXMLOutput::createResul
 
   if (mNotifier) {
     rv = mNotifier->SetOutputDocument(mDocument);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Do this after calling OnDocumentCreated to ensure that the
   // PresShell/PresContext has been hooked up and get notified.
-  nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
-  if (htmlDoc) {
-    htmlDoc->SetCompatibilityMode(eCompatibility_FullStandards);
+  if (mDocument) {
+    mDocument->SetCompatibilityMode(eCompatibility_FullStandards);
   }
 
   // Add a doc-type if requested
   if (!mOutputFormat.mSystemId.IsEmpty()) {
     nsAutoString qName;
     if (mOutputFormat.mMethod == eHTMLOutput) {
       qName.AssignLiteral("html");
     } else {
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -627,17 +627,17 @@ nsEditingSession::OnStateChange(nsIWebPr
       if (progressIsForTargetDocument) {
         nsCOMPtr<mozIDOMWindowProxy> window;
         aWebProgress->GetDOMWindow(getter_AddRefs(window));
 
         auto* piWindow = nsPIDOMWindowOuter::From(window);
         RefPtr<Document> doc = piWindow->GetDoc();
         nsHTMLDocument* htmlDoc =
             doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr;
-        if (htmlDoc && htmlDoc->IsWriting()) {
+        if (htmlDoc && doc->IsWriting()) {
           nsAutoString designMode;
           htmlDoc->GetDesignMode(designMode);
 
           if (designMode.EqualsLiteral("on")) {
             // This notification is for data coming in through
             // document.open/write/close(), ignore it.
 
             return NS_OK;
--- a/parser/html/nsHtml5DocumentBuilder.cpp
+++ b/parser/html/nsHtml5DocumentBuilder.cpp
@@ -87,19 +87,17 @@ void nsHtml5DocumentBuilder::SetDocument
       break;
     case ALMOST_STANDARDS_MODE:
       mode = eCompatibility_AlmostStandards;
       break;
     case QUIRKS_MODE:
       mode = eCompatibility_NavQuirks;
       break;
   }
-  nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(mDocument);
-  NS_ASSERTION(htmlDocument, "Document didn't QI into HTML document.");
-  htmlDocument->SetCompatibilityMode(mode);
+  mDocument->SetCompatibilityMode(mode);
 }
 
 // nsContentSink overrides
 
 void nsHtml5DocumentBuilder::UpdateChildCounts() {
   // No-op
 }
 
--- a/testing/web-platform/meta/html/dom/interfaces.https.html.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.https.html.ini
@@ -1,25 +1,10 @@
 prefs: [dom.security.featurePolicy.enabled:true]
 [interfaces.https.html?include=(Document|Window)]
-  [Document interface: operation open(DOMString, DOMString)]
-    expected: FAIL
-
-  [Document interface: operation open(USVString, DOMString, DOMString)]
-    expected: FAIL
-
-  [Document interface: operation close()]
-    expected: FAIL
-
-  [Document interface: operation write(DOMString)]
-    expected: FAIL
-
-  [Document interface: operation writeln(DOMString)]
-    expected: FAIL
-
   [Document interface: attribute designMode]
     expected: FAIL
 
   [Document interface: operation execCommand(DOMString, boolean, DOMString)]
     expected: FAIL
 
   [Document interface: operation queryCommandEnabled(DOMString)]
     expected: FAIL
@@ -76,43 +61,16 @@ prefs: [dom.security.featurePolicy.enabl
     expected: FAIL
 
   [Document interface: iframe.contentDocument must inherit property "oncancel" with the proper type]
     expected: FAIL
 
   [Document interface: iframe.contentDocument must inherit property "onsecuritypolicyviolation" with the proper type]
     expected: FAIL
 
-  [Document interface: new Document() must inherit property "open(DOMString, DOMString)" with the proper type]
-    expected: FAIL
-
-  [Document interface: calling open(DOMString, DOMString) on new Document() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: new Document() must inherit property "open(USVString, DOMString, DOMString)" with the proper type]
-    expected: FAIL
-
-  [Document interface: calling open(USVString, DOMString, DOMString) on new Document() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: new Document() must inherit property "close()" with the proper type]
-    expected: FAIL
-
-  [Document interface: new Document() must inherit property "write(DOMString)" with the proper type]
-    expected: FAIL
-
-  [Document interface: calling write(DOMString) on new Document() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: new Document() must inherit property "writeln(DOMString)" with the proper type]
-    expected: FAIL
-
-  [Document interface: calling writeln(DOMString) on new Document() with too few arguments must throw TypeError]
-    expected: FAIL
-
   [Document interface: new Document() must inherit property "designMode" with the proper type]
     expected: FAIL
 
   [Document interface: new Document() must inherit property "execCommand(DOMString, boolean, DOMString)" with the proper type]
     expected: FAIL
 
   [Document interface: calling execCommand(DOMString, boolean, DOMString) on new Document() with too few arguments must throw TypeError]
     expected: FAIL
@@ -175,43 +133,16 @@ prefs: [dom.security.featurePolicy.enabl
     expected: FAIL
 
   [Document interface: new Document() must inherit property "oncancel" with the proper type]
     expected: FAIL
 
   [Document interface: new Document() must inherit property "onsecuritypolicyviolation" with the proper type]
     expected: FAIL
 
-  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "open(DOMString, DOMString)" with the proper type]
-    expected: FAIL
-
-  [Document interface: calling open(DOMString, DOMString) on document.implementation.createDocument(null, "", null) with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "open(USVString, DOMString, DOMString)" with the proper type]
-    expected: FAIL
-
-  [Document interface: calling open(USVString, DOMString, DOMString) on document.implementation.createDocument(null, "", null) with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "close()" with the proper type]
-    expected: FAIL
-
-  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "write(DOMString)" with the proper type]
-    expected: FAIL
-
-  [Document interface: calling write(DOMString) on document.implementation.createDocument(null, "", null) with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "writeln(DOMString)" with the proper type]
-    expected: FAIL
-
-  [Document interface: calling writeln(DOMString) on document.implementation.createDocument(null, "", null) with too few arguments must throw TypeError]
-    expected: FAIL
-
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "designMode" with the proper type]
     expected: FAIL
 
   [Document interface: document.implementation.createDocument(null, "", null) must inherit property "execCommand(DOMString, boolean, DOMString)" with the proper type]
     expected: FAIL
 
   [Document interface: calling execCommand(DOMString, boolean, DOMString) on document.implementation.createDocument(null, "", null) with too few arguments must throw TypeError]
     expected: FAIL
--- a/toolkit/components/passwordmgr/content/passwordManager.xul
+++ b/toolkit/components/passwordmgr/content/passwordManager.xul
@@ -126,14 +126,14 @@
       <button id="togglePasswords"
               oncommand="TogglePasswordVisible();"/>
     </hbox>
   </vbox>
   <hbox align="end">
     <hbox class="actionButtons" flex="1">
       <spacer flex="1"/>
 #ifndef XP_MACOSX
-      <button oncommand="close();"
+      <button oncommand="window.close();"
               data-l10n-id="close-button"/>
 #endif
     </hbox>
   </hbox>
 </window>
--- a/toolkit/content/widgets/findbar.js
+++ b/toolkit/content/widgets/findbar.js
@@ -37,17 +37,17 @@ class MozFindbar extends XULElement {
         <toolbarbutton anonid="find-case-sensitive" class="findbar-case-sensitive findbar-button tabbable" data-l10n-id="findbar-case-sensitive" oncommand="_setCaseSensitivity(this.checked ? 1 : 0);" type="checkbox" />
         <toolbarbutton anonid="find-entire-word" class="findbar-entire-word findbar-button tabbable" data-l10n-id="findbar-entire-word" oncommand="toggleEntireWord(this.checked);" type="checkbox" />
         <label anonid="match-case-status" class="findbar-find-fast" />
         <label anonid="entire-word-status" class="findbar-find-fast" />
         <label anonid="found-matches" class="findbar-find-fast found-matches" hidden="true" />
         <image anonid="find-status-icon" class="findbar-find-fast find-status-icon" />
         <description anonid="find-status" control="findbar-textbox" class="findbar-find-fast findbar-find-status" />
       </hbox>
-      <toolbarbutton anonid="find-closebutton" class="findbar-closebutton close-icon" data-l10n-id="findbar-find-button-close" oncommand="close();" />
+      <toolbarbutton anonid="find-closebutton" class="findbar-closebutton close-icon" data-l10n-id="findbar-find-button-close" oncommand="window.close();" />
     `);
   }
 
   connectedCallback() {
     // Hide the findbar immediately without animation. This prevents a flicker in the case where
     // we'll never be shown (i.e. adopting a tab that has a previously-opened-but-now-closed
     // findbar into a new window).
     this.setAttribute("noanim", "true");