Backed out 2 changesets (bug 1518252) for frequent clipboard failures on browser_jsterm_middle_click_paste.
authorCosmin Sabou <csabou@mozilla.com>
Thu, 24 Jan 2019 02:50:54 +0200
changeset 515216 9aae2b5ef219907ff9647488ce19ccb755f96908
parent 515215 4634649d3189fc3d34fec2058758f978de5f8a5b
child 515217 ac16e1408d672d6a94d5410b135af06ce30484a1
child 515231 3e5f4a8daaee1b9ccad2fe88666a3cfadffb2a78
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1518252
milestone66.0a1
backs outb6f80451898e0878e3299e5ea949f9c81dfce770
06114c66901fd20d3c2177d51307ac5045827fcc
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
Backed out 2 changesets (bug 1518252) for frequent clipboard failures on browser_jsterm_middle_click_paste. Backed out changeset b6f80451898e (bug 1518252) Backed out changeset 06114c66901f (bug 1518252)
dom/base/Document.cpp
dom/base/Document.h
dom/base/nsContentSink.cpp
dom/xml/nsXMLContentSink.cpp
dom/xml/nsXMLContentSink.h
dom/xul/XULDocument.cpp
dom/xul/XULDocument.h
intl/l10n/DocumentL10n.cpp
intl/l10n/DocumentL10n.h
intl/l10n/test/dom/test_docl10n.html
intl/l10n/test/dom/test_docl10n.xhtml
intl/l10n/test/dom/test_docl10n.xul
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
parser/htmlparser/nsIContentSink.h
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -1334,18 +1334,17 @@ Document::Document(const char* aContentT
       mScrollAnchorAdjustmentCount(0),
       mBoxObjectTable(nullptr),
       mCurrentOrientationAngle(0),
       mCurrentOrientationType(OrientationType::Portrait_primary),
       mServoRestyleRootDirtyBits(0),
       mThrowOnDynamicMarkupInsertionCounter(0),
       mIgnoreOpensDuringUnloadCounter(0),
       mDocLWTheme(Doc_Theme_Uninitialized),
-      mSavedResolution(1.0f),
-      mPendingInitialTranslation(false) {
+      mSavedResolution(1.0f) {
   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
 
   SetIsInDocument();
   SetIsConnected(true);
 
   if (StaticPrefs::layout_css_use_counters_enabled()) {
     mStyleUseCounters.reset(Servo_UseCounters_Create());
   }
@@ -3127,18 +3126,16 @@ void Document::LocalizationLinkAdded(Ele
     InitializeLocalization(resourceIds);
     mDocumentL10n->TriggerInitialDocumentTranslation();
   } else {
     // Otherwise, we're still parsing the document.
     // In that case, add it to the pending list. This list
     // will be resolved once the end of l10n resource
     // container is reached.
     mL10nResources.AppendElement(href);
-
-    mPendingInitialTranslation = true;
   }
 }
 
 void Document::LocalizationLinkRemoved(Element* aLinkElement) {
   if (!PrincipalAllowsL10n(NodePrincipal())) {
     return;
   }
 
@@ -3171,40 +3168,21 @@ void Document::LocalizationLinkRemoved(E
 void Document::OnL10nResourceContainerParsed() {
   if (!mL10nResources.IsEmpty()) {
     InitializeLocalization(mL10nResources);
     mL10nResources.Clear();
   }
 }
 
 void Document::TriggerInitialDocumentTranslation() {
-  // Let's call it again, in case the resource
-  // container has not been closed, and only
-  // now we're closing the document.
-  OnL10nResourceContainerParsed();
-
   if (mDocumentL10n) {
     mDocumentL10n->TriggerInitialDocumentTranslation();
   }
 }
 
-void Document::InitialDocumentTranslationCompleted() {
-  mPendingInitialTranslation = false;
-
-  nsCOMPtr<nsIContentSink> sink;
-  if (mParser) {
-    sink = mParser->GetContentSink();
-  } else {
-    sink = do_QueryReferent(mWeakSink);
-  }
-  if (sink) {
-    sink->InitialDocumentTranslationCompleted();
-  }
-}
-
 bool Document::IsWebAnimationsEnabled(JSContext* aCx, JSObject* /*unused*/) {
   MOZ_ASSERT(NS_IsMainThread());
 
   return nsContentUtils::IsSystemCaller(aCx) ||
          nsContentUtils::AnimationsAPICoreEnabled();
 }
 
 bool Document::IsWebAnimationsEnabled(CallerType aCallerType) {
@@ -8158,16 +8136,17 @@ void Document::SetReadyStateInternal(Rea
   if (READYSTATE_INTERACTIVE == rs) {
     if (nsContentUtils::IsSystemPrincipal(NodePrincipal())) {
       Element* root = GetRootElement();
       if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozpersist)) {
         mXULPersist = new XULPersist(this);
         mXULPersist->Init();
       }
     }
+    TriggerInitialDocumentTranslation();
   }
 
   RecordNavigationTiming(rs);
 
   RefPtr<AsyncEventDispatcher> asyncDispatcher =
       new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
                                CanBubble::eNo, ChromeOnlyDispatch::eNo);
   asyncDispatcher->RunDOMEventWhenSafe();
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3420,42 +3420,31 @@ class Document : public nsINode,
   void LocalizationLinkAdded(Element* aLinkElement);
 
   /**
    * This method should be called when a link element
    * with rel="localization" is being removed.
    */
   void LocalizationLinkRemoved(Element* aLinkElement);
 
-  /**
-   * This method should be called as soon as the
+ protected:
+  /**
+   * This method should be collect as soon as the
    * parsing of the document is completed.
    *
-   * In HTML/XHTML this happens when we finish parsing
-   * the document element.
+   * In HTML this happens when readyState becomes
+   * `interactive`.
    * In XUL it happens at `DoneWalking`, during
    * `MozBeforeInitialXULLayout`.
    *
    * It triggers the initial translation of the
    * document.
    */
   void TriggerInitialDocumentTranslation();
 
-  /**
-   * This method is called when the initial translation
-   * of the document is completed.
-   *
-   * It unblocks the layout.
-   *
-   * This method is virtual so that XULDocument can
-   * override it.
-   */
-  virtual void InitialDocumentTranslationCompleted();
-
- protected:
   RefPtr<mozilla::dom::DocumentL10n> mDocumentL10n;
 
  private:
   void InitializeLocalization(nsTArray<nsString>& aResourceIds);
 
   void ParseWidthAndHeightInMetaViewport(const nsAString& aWidthString,
                                          const nsAString& aHeightString,
                                          const nsAString& aScaleString);
@@ -4519,23 +4508,19 @@ class Document : public nsINode,
 
   // document lightweight theme for use with :-moz-lwtheme,
   // :-moz-lwtheme-brighttext and :-moz-lwtheme-darktext
   DocumentTheme mDocLWTheme;
 
   // Pres shell resolution saved before entering fullscreen mode.
   float mSavedResolution;
 
-  bool mPendingInitialTranslation;
-
  public:
   // Needs to be public because the bindings code pokes at it.
   js::ExpandoAndGeneration mExpandoAndGeneration;
-
-  bool HasPendingInitialTranslation() { return mPendingInitialTranslation; }
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Document, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -1165,19 +1165,18 @@ void nsContentSink::StartLayout(bool aIg
 
   if (mLayoutStarted) {
     // Nothing to do here
     return;
   }
 
   mDeferredLayoutStart = true;
 
-  if (!aIgnorePendingSheets &&
-      (WaitForPendingSheets() || mDocument->HasPendingInitialTranslation())) {
-    // Bail out; we'll start layout when the sheets and l10n load
+  if (!aIgnorePendingSheets && WaitForPendingSheets()) {
+    // Bail out; we'll start layout when the sheets load
     return;
   }
 
   mDeferredLayoutStart = false;
 
   // Notify on all our content.  If none of our presshells have started layout
   // yet it'll be a no-op except for updating our data structures, a la
   // UpdateChildCounts() (because we don't want to double-notify on whatever we
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -1068,18 +1068,16 @@ nsresult nsXMLContentSink::HandleEndElem
     mCurrentHead = nullptr;
   }
 
   if (mDocElement == content) {
     // XXXbz for roots that don't want to be appended on open, we
     // probably need to deal here.... (and stop appending them on open).
     mState = eXMLContentSinkState_InEpilog;
 
-    mDocument->TriggerInitialDocumentTranslation();
-
     // We might have had no occasion to start layout yet.  Do so now.
     MaybeStartLayout(false);
   }
 
   DidAddContent();
 
   if (content->IsSVGElement(nsGkAtoms::svg)) {
     FlushTags();
@@ -1404,20 +1402,16 @@ nsresult nsXMLContentSink::AddText(const
     mTextLength += amount;
     offset += amount;
     aLength -= amount;
   }
 
   return NS_OK;
 }
 
-void nsXMLContentSink::InitialDocumentTranslationCompleted() {
-  StartLayout(false);
-}
-
 void nsXMLContentSink::FlushPendingNotifications(FlushType aType) {
   // Only flush tags if we're not doing the notification ourselves
   // (since we aren't reentrant)
   if (!mInNotification) {
     if (mIsDocumentObserver) {
       // Only flush if we're still a document observer (so that our child
       // counts should be correct).
       if (aType >= FlushType::ContentAndNotify) {
--- a/dom/xml/nsXMLContentSink.h
+++ b/dom/xml/nsXMLContentSink.h
@@ -62,17 +62,16 @@ class nsXMLContentSink : public nsConten
 
   // nsIContentSink
   NS_IMETHOD WillParse(void) override;
   NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) override;
   NS_IMETHOD DidBuildModel(bool aTerminated) override;
   NS_IMETHOD WillInterrupt(void) override;
   NS_IMETHOD WillResume(void) override;
   NS_IMETHOD SetParser(nsParserBase* aParser) override;
-  virtual void InitialDocumentTranslationCompleted() override;
   virtual void FlushPendingNotifications(mozilla::FlushType aType) override;
   virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding) override;
   virtual nsISupports* GetTarget() override;
   virtual bool IsScriptExecuting() override;
   virtual void ContinueInterruptedParsingAsync() override;
 
   // nsITransformObserver
   NS_IMETHOD OnDocumentCreated(
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -444,21 +444,16 @@ void XULDocument::AddElementToDocumentPo
 
   if (aElement->IsXULElement(nsGkAtoms::link)) {
     LocalizationLinkAdded(aElement);
   } else if (aElement->IsXULElement(nsGkAtoms::linkset)) {
     OnL10nResourceContainerParsed();
   }
 }
 
-void XULDocument::InitialDocumentTranslationCompleted() {
-  mPendingInitialTranslation = false;
-  MaybeDoneWalking();
-}
-
 void XULDocument::AddSubtreeToDocument(nsIContent* aContent) {
   MOZ_ASSERT(aContent->GetComposedDoc() == this, "Element not in doc!");
 
   // If the content is not in the document, it must be in a shadow tree.
   //
   // The shadow root itself takes care of maintaining the ID tables and such,
   // and there's no use case for localization links in shadow trees, or at
   // least they don't work in regular HTML documents either as of today so...
@@ -958,56 +953,53 @@ nsresult XULDocument::ResumeWalk() {
 
   // If we get here, there is nothing left for us to walk. The content
   // model is built and ready for layout.
 
   mXULPersist = new XULPersist(this);
   mXULPersist->Init();
 
   mStillWalking = false;
-  return MaybeDoneWalking();
-}
-
-nsresult XULDocument::MaybeDoneWalking() {
-  if (mPendingSheets > 0 || mStillWalking) {
-    return NS_OK;
+  if (mPendingSheets == 0) {
+    rv = DoneWalking();
   }
-
-  if (mPendingInitialTranslation) {
-    TriggerInitialDocumentTranslation();
-    return NS_OK;
-  }
-
-  return DoneWalking();
+  return rv;
 }
 
 nsresult XULDocument::DoneWalking() {
   MOZ_ASSERT(mPendingSheets == 0, "there are sheets to be loaded");
   MOZ_ASSERT(!mStillWalking, "walk not done");
-  MOZ_ASSERT(!mPendingInitialTranslation, "translation pending");
 
   // XXXldb This is where we should really be setting the chromehidden
   // attribute.
 
   if (!mDocumentLoaded) {
     // Make sure we don't reenter here from StartLayout().  Note that
     // setting mDocumentLoaded to true here means that if StartLayout()
     // causes ResumeWalk() to be reentered, we'll take the other branch of
     // the |if (!mDocumentLoaded)| check above and since
     // mInitialLayoutComplete will be false will follow the else branch
     // there too.  See the big comment there for how such reentry can
     // happen.
     mDocumentLoaded = true;
 
     NotifyPossibleTitleChange(false);
 
+    // For performance reasons, we want to trigger the DocumentL10n's
+    // `TriggerInitialDocumentTranslation` within the same microtask that will
+    // be created for a `MozBeforeInitialXULLayout` event listener.
+    AddEventListener(NS_LITERAL_STRING("MozBeforeInitialXULLayout"),
+                     mDocumentL10n, true, false);
+
     nsContentUtils::DispatchTrustedEvent(
         this, ToSupports(this), NS_LITERAL_STRING("MozBeforeInitialXULLayout"),
         CanBubble::eYes, Cancelable::eNo);
 
+    RemoveEventListener(NS_LITERAL_STRING("MozBeforeInitialXULLayout"),
+                        mDocumentL10n, true);
 
     // Before starting layout, check whether we're a toplevel chrome
     // window.  If we are, setup some state so that we don't have to restyle
     // the whole tree after StartLayout.
     if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
       // We're the chrome document!
       win->BeforeStartLayout();
     }
@@ -1042,17 +1034,19 @@ NS_IMETHODIMP
 XULDocument::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
                               nsresult aStatus) {
   if (!aWasDeferred) {
     // Don't care about when alternate sheets finish loading
     MOZ_ASSERT(mPendingSheets > 0, "Unexpected StyleSheetLoaded notification");
 
     --mPendingSheets;
 
-    return MaybeDoneWalking();
+    if (!mStillWalking && mPendingSheets == 0) {
+      return DoneWalking();
+    }
   }
 
   return NS_OK;
 }
 
 void XULDocument::EndUpdate() { XMLDocument::EndUpdate(); }
 
 nsresult XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto,
--- a/dom/xul/XULDocument.h
+++ b/dom/xul/XULDocument.h
@@ -71,18 +71,16 @@ class XULDocument final : public XMLDocu
                                      nsIStreamListener** aDocListener,
                                      bool aReset = true,
                                      nsIContentSink* aSink = nullptr) override;
 
   virtual void SetContentType(const nsAString& aContentType) override;
 
   virtual void EndLoad() override;
 
-  virtual void InitialDocumentTranslationCompleted() override;
-
   // nsIMutationObserver interface
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   /**
    * Notify the XUL document that a subtree has been added
    */
@@ -293,25 +291,17 @@ class XULDocument final : public XMLDocu
 
   /**
    * Resume (or initiate) an interrupted (or newly prepared)
    * prototype walk.
    */
   nsresult ResumeWalk();
 
   /**
-   * Called at the end of ResumeWalk(), from StyleSheetLoaded(),
-   * and from DocumentL10n.
-   * If walking, stylesheets and l10n are not blocking, it
-   * will trigger `DoneWalking()`.
-   */
-  nsresult MaybeDoneWalking();
-
-  /**
-   * Called from `MaybeDoneWalking()`.
+   * Called at the end of ResumeWalk() and from StyleSheetLoaded().
    * Expects that both the prototype document walk is complete and
    * all referenced stylesheets finished loading.
    */
   nsresult DoneWalking();
 
   class CachedChromeStreamListener : public nsIStreamListener {
    protected:
     RefPtr<XULDocument> mDocument;
--- a/intl/l10n/DocumentL10n.cpp
+++ b/intl/l10n/DocumentL10n.cpp
@@ -3,16 +3,17 @@
 /* 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 "js/JSON.h"
 #include "mozilla/dom/DocumentL10n.h"
 #include "mozilla/dom/DocumentL10nBinding.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/Event.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "nsQueryObject.h"
 #include "nsISupports.h"
 #include "nsContentUtils.h"
 #include "xpcprivate.h"
 
 namespace mozilla {
@@ -54,22 +55,28 @@ void PromiseResolver::RejectedCallback(J
   StackScopedClone(cx, options, sourceScope, &value);
 
   mPromise->MaybeReject(cx, value);
   mPromise = nullptr;
 }
 
 PromiseResolver::~PromiseResolver() { mPromise = nullptr; }
 
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentL10n)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DocumentL10n)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DocumentL10n)
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DocumentL10n, mDocument, mDOMLocalization,
                                       mReady)
 
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DocumentL10n, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DocumentL10n, Release)
-
 DocumentL10n::DocumentL10n(Document* aDocument)
     : mDocument(aDocument), mState(DocumentL10nState::Initialized) {}
 
 DocumentL10n::~DocumentL10n() {}
 
 bool DocumentL10n::Init(nsTArray<nsString>& aResourceIds) {
   nsCOMPtr<mozIDOMLocalization> domL10n =
       do_CreateInstance("@mozilla.org/intl/domlocalization;1");
@@ -128,16 +135,29 @@ already_AddRefed<Promise> DocumentL10n::
     return nullptr;
   }
 
   RefPtr<PromiseResolver> resolver = new PromiseResolver(docPromise);
   aInnerPromise->AppendNativeHandler(resolver);
   return docPromise.forget();
 }
 
+NS_IMETHODIMP
+DocumentL10n::HandleEvent(Event* aEvent) {
+#ifdef DEBUG
+  nsAutoString eventType;
+  aEvent->GetType(eventType);
+  MOZ_ASSERT(eventType.EqualsLiteral("MozBeforeInitialXULLayout"));
+#endif
+
+  TriggerInitialDocumentTranslation();
+
+  return NS_OK;
+}
+
 uint32_t DocumentL10n::AddResourceIds(nsTArray<nsString>& aResourceIds) {
   uint32_t ret = 0;
   mDOMLocalization->AddResourceIds(aResourceIds, false, &ret);
   return ret;
 }
 
 uint32_t DocumentL10n::RemoveResourceIds(nsTArray<nsString>& aResourceIds) {
   // We need to guard against a scenario where the
@@ -287,34 +307,30 @@ already_AddRefed<Promise> DocumentL10n::
   return MaybeWrapPromise(promise);
 }
 
 class L10nReadyHandler final : public PromiseNativeHandler {
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(L10nReadyHandler)
 
-  explicit L10nReadyHandler(Promise* aPromise, Document* aDocument)
-      : mPromise(aPromise), mDocument(aDocument) {}
+  explicit L10nReadyHandler(Promise* aPromise) : mPromise(aPromise) {}
 
   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
-    mDocument->InitialDocumentTranslationCompleted();
     mPromise->MaybeResolveWithUndefined();
   }
 
   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
-    mDocument->InitialDocumentTranslationCompleted();
     mPromise->MaybeRejectWithUndefined();
   }
 
  private:
   ~L10nReadyHandler() = default;
 
   RefPtr<Promise> mPromise;
-  RefPtr<Document> mDocument;
 };
 
 NS_IMPL_CYCLE_COLLECTION(L10nReadyHandler, mPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(L10nReadyHandler)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
@@ -330,17 +346,16 @@ void DocumentL10n::TriggerInitialDocumen
 
   Element* elem = mDocument->GetDocumentElement();
   if (elem) {
     mDOMLocalization->ConnectRoot(elem);
   }
 
   RefPtr<Promise> promise;
   mDOMLocalization->TranslateRoots(getter_AddRefs(promise));
-  RefPtr<PromiseNativeHandler> l10nReadyHandler =
-      new L10nReadyHandler(mReady, mDocument);
+  RefPtr<PromiseNativeHandler> l10nReadyHandler = new L10nReadyHandler(mReady);
   promise->AppendNativeHandler(l10nReadyHandler);
 }
 
 Promise* DocumentL10n::Ready() { return mReady; }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/intl/l10n/DocumentL10n.h
+++ b/intl/l10n/DocumentL10n.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_DocumentL10n_h
 #define mozilla_dom_DocumentL10n_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "nsIDOMEventListener.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "mozilla/dom/Document.h"
 #include "nsINode.h"
 #include "mozIDOMLocalization.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 
@@ -47,20 +48,21 @@ enum class DocumentL10nState { Initializ
  *
  * The document will initialize it lazily when a link with a localization
  * resource is added to the document.
  *
  * Once initialized, DocumentL10n relays all API methods to an
  * instance of mozIDOMLocalization and maintains a single promise
  * which gets resolved the first time the document gets translated.
  */
-class DocumentL10n final : public nsWrapperCache {
+class DocumentL10n final : public nsIDOMEventListener, public nsWrapperCache {
  public:
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DocumentL10n)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DocumentL10n)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DocumentL10n)
+  NS_DECL_NSIDOMEVENTLISTENER
 
  public:
   explicit DocumentL10n(Document* aDocument);
   bool Init(nsTArray<nsString>& aResourceIds);
 
  protected:
   virtual ~DocumentL10n();
 
--- a/intl/l10n/test/dom/test_docl10n.html
+++ b/intl/l10n/test/dom/test_docl10n.html
@@ -8,46 +8,44 @@
   <link rel="localization" href="crashreporter/aboutcrashes.ftl"/>
   <script type="application/javascript">
   "use strict";
   SimpleTest.waitForExplicitFinish();
 
   document.addEventListener("DOMContentLoaded", async function() {
     await document.l10n.ready;
 
-    // Test for initial localization applied.
     let desc = document.getElementById("main-desc");
     is(desc.textContent.length > 0, true);
 
-    // Test for manual value formatting.
+    // Test for manual value formatting
     let msg = await document.l10n.formatValue("id-heading");
     is(msg.length > 0, true);
 
-    // Test for mutations applied.
-    let verifyL10n = () => {
-      if (label.textContent.length > 0) {
-        window.removeEventListener("MozAfterPaint", verifyL10n);
-        SimpleTest.finish();
-      }
-    };
-    window.addEventListener("MozAfterPaint", verifyL10n);
-
     let label = document.getElementById("label1");
     document.l10n.setAttributes(
       label,
       "date-crashed-heading",
       {
         name: "John",
       }
     );
 
     // Test for l10n.getAttributes
     let l10nArgs = document.l10n.getAttributes(label);
     is(l10nArgs.id, "date-crashed-heading");
     is(l10nArgs.args.name, "John");
+
+    let verifyL10n = () => {
+      if (label.textContent.length > 0) {
+        window.removeEventListener("MozAfterPaint", verifyL10n);
+        SimpleTest.finish();
+      }
+    };
+    window.addEventListener("MozAfterPaint", verifyL10n);
   }, { once: true});
   </script>
 </head>
 <body>
   <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1>
 
   <p id="label1"></p>
 </body>
--- a/intl/l10n/test/dom/test_docl10n.xhtml
+++ b/intl/l10n/test/dom/test_docl10n.xhtml
@@ -14,46 +14,44 @@
   <link rel="localization" href="crashreporter/aboutcrashes.ftl"/>
   <script type="application/javascript">
   "use strict";
   SimpleTest.waitForExplicitFinish();
 
   document.addEventListener("DOMContentLoaded", async function() {
     await document.l10n.ready;
 
-    // Test for initial localization applied.
     let desc = document.getElementById("main-desc");
     is(desc.textContent.length > 0, true);
 
     // Test for manual value formatting
     let msg = await document.l10n.formatValue("id-heading");
     is(msg.length > 0, true);
 
-    // Test for mutations applied.
-    let verifyL10n = () => {
-      if (label.textContent.length > 0) {
-        window.removeEventListener("MozAfterPaint", verifyL10n);
-        SimpleTest.finish();
-      }
-    };
-    window.addEventListener("MozAfterPaint", verifyL10n);
-
     let label = document.getElementById("label1");
     document.l10n.setAttributes(
       label,
       "date-crashed-heading",
       {
         name: "John",
       }
     );
 
     // Test for l10n.getAttributes
     let l10nArgs = document.l10n.getAttributes(label);
     is(l10nArgs.id, "date-crashed-heading");
     is(l10nArgs.args.name, "John");
+
+    let verifyL10n = () => {
+      if (label.textContent.length > 0) {
+        window.removeEventListener("MozAfterPaint", verifyL10n);
+        SimpleTest.finish();
+      }
+    };
+    window.addEventListener("MozAfterPaint", verifyL10n);
   }, { once: true});
   </script>
 </head>
 <body>
   <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1>
 
   <p id="label1" />
 </body>
--- a/intl/l10n/test/dom/test_docl10n.xul
+++ b/intl/l10n/test/dom/test_docl10n.xul
@@ -16,46 +16,44 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
   <script type="application/javascript">
   <![CDATA[
   SimpleTest.waitForExplicitFinish();
 
   document.addEventListener("DOMContentLoaded", async function() {
     await document.l10n.ready;
 
-    // Test for initial localization applied.
     let desc = document.getElementById("main-desc");
     is(desc.textContent.length > 0, true);
 
     // Test for manual value formatting
     let msg = await document.l10n.formatValue("id-heading");
     is(msg.length > 0, true);
 
-    // Test for mutations applied.
-    let verifyL10n = () => {
-      if (label.textContent.length > 0) {
-        window.removeEventListener("MozAfterPaint", verifyL10n);
-        SimpleTest.finish();
-      }
-    };
-    window.addEventListener("MozAfterPaint", verifyL10n);
-
     let label = document.getElementById("label1");
     document.l10n.setAttributes(
       label,
       "date-crashed-heading",
       {
         name: "John"
       }
     );
 
     // Test for l10n.getAttributes
     let l10nArgs = document.l10n.getAttributes(label);
     is(l10nArgs.id, "date-crashed-heading");
     is(l10nArgs.args.name, "John");
+
+    let verifyL10n = () => {
+      if (label.textContent.length > 0) {
+        window.removeEventListener("MozAfterPaint", verifyL10n);
+        SimpleTest.finish();
+      }
+    };
+    window.addEventListener("MozAfterPaint", verifyL10n);
   }, { once: true});
   ]]>
   </script>
 
   <description id="main-desc" data-l10n-id="crash-reports-title"/>
 
   <label id="label1" />
 </window>
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -165,17 +165,16 @@ nsHtml5TreeOpExecutor::DidBuildModel(boo
     // likely cause us to crash, or at best waste a lot of time as we
     // are just going to tear it down anyway.
     bool destroying = true;
     if (mDocShell) {
       mDocShell->IsBeingDestroyed(&destroying);
     }
 
     if (!destroying) {
-      mDocument->TriggerInitialDocumentTranslation();
       nsContentSink::StartLayout(false);
     }
   }
 
   ScrollToRef();
   mDocument->RemoveObserver(this);
   if (!mParser) {
     // DidBuildModelImpl may cause mParser to be nulled out
@@ -224,20 +223,16 @@ nsHtml5TreeOpExecutor::WillResume() {
 }
 
 NS_IMETHODIMP
 nsHtml5TreeOpExecutor::SetParser(nsParserBase* aParser) {
   mParser = aParser;
   return NS_OK;
 }
 
-void nsHtml5TreeOpExecutor::InitialDocumentTranslationCompleted() {
-  nsContentSink::StartLayout(false);
-}
-
 void nsHtml5TreeOpExecutor::FlushPendingNotifications(FlushType aType) {
   if (aType >= FlushType::EnsurePresShellInitAndFrames) {
     // Bug 577508 / 253951
     nsContentSink::StartLayout(true);
   }
 }
 
 nsISupports* nsHtml5TreeOpExecutor::GetTarget() {
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -131,18 +131,16 @@ class nsHtml5TreeOpExecutor final
    */
   NS_IMETHOD WillInterrupt() override;
 
   /**
    * Unimplemented. For interface compat only.
    */
   NS_IMETHOD WillResume() override;
 
-  virtual void InitialDocumentTranslationCompleted() override;
-
   /**
    * Sets the parser.
    */
   NS_IMETHOD SetParser(nsParserBase* aParser) override;
 
   /**
    * No-op for backwards compat.
    */
--- a/parser/htmlparser/nsIContentSink.h
+++ b/parser/htmlparser/nsIContentSink.h
@@ -125,15 +125,13 @@ class nsIContentSink : public nsISupport
    * parsing for.
    */
   virtual bool IsScriptExecuting() { return false; }
 
   /**
    * Posts a runnable that continues parsing.
    */
   virtual void ContinueInterruptedParsingAsync() {}
-
-  virtual void InitialDocumentTranslationCompleted() {}
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentSink, NS_ICONTENT_SINK_IID)
 
 #endif /* nsIContentSink_h___ */