Bug 1594752, use WindowGlobalParent's documentTitle to update tab titles rather than sending messages and events between processes, r=Gijs
authorNeil Deakin <neil@mozilla.com>
Wed, 13 May 2020 19:26:10 +0000
changeset 529712 66cc44b67170c7e48411180d5785f861b04a7719
parent 529711 e781cf38f088c02d2b336aa8ec990756447683c9
child 529713 d5eab5a42b3e5a444d3f0c46b3c77c2a76d4cf77
push id37414
push usernbeleuzu@mozilla.com
push dateThu, 14 May 2020 02:40:10 +0000
treeherdermozilla-central@045d696faa87 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1594752
milestone78.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 1594752, use WindowGlobalParent's documentTitle to update tab titles rather than sending messages and events between processes, r=Gijs Fix up the browser_tab_label_during_restore.js test to wait for the right number of tab title changes, since the timing of the tab title updating has now changed. Differential Revision: https://phabricator.services.mozilla.com/D72562
browser/base/content/tabbrowser.js
browser/components/extensions/ExtensionPopups.jsm
browser/components/sessionstore/test/browser_tab_label_during_restore.js
devtools/client/responsive/browser/tunnel.js
devtools/server/actors/webbrowser.js
toolkit/content/browser-child.js
toolkit/content/widgets/browser-custom-element.js
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -52,17 +52,16 @@
           "browser.display.background_color"
         );
       }
 
       let messageManager = window.getGroupMessageManager("browsers");
       window.messageManager.addMessageListener("contextmenu", this);
 
       if (gMultiProcessBrowser) {
-        messageManager.addMessageListener("DOMTitleChanged", this);
         messageManager.addMessageListener("DOMWindowClose", this);
         messageManager.addMessageListener("Browser:Init", this);
       } else {
         this._outerWindowIDBrowserMap.set(
           this.selectedBrowser.outerWindowID,
           this.selectedBrowser
         );
       }
@@ -5118,27 +5117,16 @@
       }
     },
 
     receiveMessage(aMessage) {
       let data = aMessage.data;
       let browser = aMessage.target;
 
       switch (aMessage.name) {
-        case "DOMTitleChanged": {
-          let tab = this.getTabForBrowser(browser);
-          if (!tab || tab.hasAttribute("pending")) {
-            return undefined;
-          }
-          let titleChanged = this.setTabTitle(tab);
-          if (titleChanged && !tab.selected && !tab.hasAttribute("busy")) {
-            tab.setAttribute("titlechanged", "true");
-          }
-          break;
-        }
         case "contextmenu": {
           openContextMenu(aMessage);
           break;
         }
         case "Browser:Init": {
           let tab = this.getTabForBrowser(browser);
           if (!tab) {
             return undefined;
@@ -5283,18 +5271,16 @@
           false
         );
       }
       window.removeEventListener("sizemodechange", this);
       window.removeEventListener("occlusionstatechange", this);
       window.removeEventListener("framefocusrequested", this);
 
       if (gMultiProcessBrowser) {
-        let messageManager = window.getGroupMessageManager("browsers");
-        messageManager.removeMessageListener("DOMTitleChanged", this);
         window.messageManager.removeMessageListener("contextmenu", this);
 
         if (this._switcher) {
           this._switcher.destroy();
         }
       }
     },
 
@@ -5344,16 +5330,39 @@
           // If we don't preventDefault on the DOMWindowClose event, then
           // in the parent-process browser case, we're telling the platform
           // to close the entire window. Calling preventDefault is our way of
           // saying we took care of this close request by closing the tab.
           event.preventDefault();
         }
       });
 
+      this.addEventListener("pagetitlechanged", event => {
+        let browser = event.target;
+        let tab = this.getTabForBrowser(browser);
+        if (!tab || tab.hasAttribute("pending")) {
+          return;
+        }
+
+        // Ignore empty title changes on internal pages. This prevents the title
+        // from changing while Fluent is populating the (initially-empty) title
+        // element.
+        if (
+          !browser.contentTitle &&
+          browser.contentPrincipal.isSystemPrincipal
+        ) {
+          return;
+        }
+
+        let titleChanged = this.setTabTitle(tab);
+        if (titleChanged && !tab.selected && !tab.hasAttribute("busy")) {
+          tab.setAttribute("titlechanged", "true");
+        }
+      });
+
       this.addEventListener(
         "DOMWillOpenModalDialog",
         event => {
           if (!event.isTrusted) {
             return;
           }
 
           let targetIsWindow = event.target instanceof Window;
@@ -5431,57 +5440,16 @@
           }
 
           // If permissions/origins dictate so, bring tab to the front.
           this.selectedTab = tabForEvent;
         },
         true
       );
 
-      this.addEventListener("DOMTitleChanged", event => {
-        if (!event.isTrusted) {
-          return;
-        }
-
-        var contentWin = event.target.defaultView;
-        if (contentWin != contentWin.top) {
-          return;
-        }
-
-        let browser = contentWin.docShell.chromeEventHandler;
-        var tab = this.getTabForBrowser(browser);
-        if (!tab || tab.hasAttribute("pending")) {
-          return;
-        }
-
-        if (!browser.docShell) {
-          return;
-        }
-        // Ensure `docShell.document` (an nsIWebNavigation idl prop) is there:
-        browser.docShell.QueryInterface(Ci.nsIWebNavigation);
-        if (event.target != browser.docShell.document) {
-          return;
-        }
-
-        // Ignore empty title changes on internal pages. This prevents the title
-        // from changing while Fluent is populating the (initially-empty) title
-        // element.
-        if (
-          !browser.contentTitle &&
-          browser.contentPrincipal.isSystemPrincipal
-        ) {
-          return;
-        }
-
-        var titleChanged = this.setTabTitle(tab);
-        if (titleChanged && !tab.selected && !tab.hasAttribute("busy")) {
-          tab.setAttribute("titlechanged", "true");
-        }
-      });
-
       let onTabCrashed = event => {
         if (!event.isTrusted || !event.isTopFrame) {
           return;
         }
 
         let browser = event.originalTarget;
 
         // Preloaded browsers do not actually have any tabs. If one crashes,
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -167,23 +167,23 @@ class BasePopup {
   }
 
   destroyBrowser(browser, finalize = false) {
     let mm = browser.messageManager;
     // If the browser has already been removed from the document, because the
     // popup was closed externally, there will be no message manager here, so
     // just replace our receiveMessage method with a stub.
     if (mm) {
-      mm.removeMessageListener("DOMTitleChanged", this);
       mm.removeMessageListener("Extension:BrowserBackgroundChanged", this);
       mm.removeMessageListener("Extension:BrowserContentLoaded", this);
       mm.removeMessageListener("Extension:BrowserResized", this);
     } else if (finalize) {
       this.receiveMessage = () => {};
     }
+    browser.removeEventListener("pagetitlechanged", this);
     browser.removeEventListener("DOMWindowClose", this);
   }
 
   // Returns the name of the event fired on `viewNode` when the popup is being
   // destroyed. This must be implemented by every subclass.
   get DESTROY_EVENT() {
     throw new Error("Not implemented");
   }
@@ -206,20 +206,16 @@ class BasePopup {
     while (panel && panel.localName != "panel") {
       panel = panel.parentNode;
     }
     return panel;
   }
 
   receiveMessage({ name, data }) {
     switch (name) {
-      case "DOMTitleChanged":
-        this.viewNode.setAttribute("aria-label", this.browser.contentTitle);
-        break;
-
       case "Extension:BrowserBackgroundChanged":
         this.setBackground(data.background);
         break;
 
       case "Extension:BrowserContentLoaded":
         this.browserLoadedDeferred.resolve();
         break;
 
@@ -255,16 +251,20 @@ class BasePopup {
               );
             })
             .catch(() => {
               // If the panel closes too fast an exception is raised here and tests will fail.
             });
         }
         break;
 
+      case "pagetitlechanged":
+        this.viewNode.setAttribute("aria-label", this.browser.contentTitle);
+        break;
+
       case "DOMWindowClose":
         this.closePopup();
         break;
     }
   }
 
   createBrowser(viewNode, popupURL = null) {
     let document = viewNode.ownerDocument;
@@ -319,20 +319,20 @@ class BasePopup {
       // that, but we should get rid of it in the long term.
       browser.contentWindow; // eslint-disable-line no-unused-expressions
     }
 
     ExtensionParent.apiManager.emit("extension-browser-inserted", browser);
 
     let setupBrowser = browser => {
       let mm = browser.messageManager;
-      mm.addMessageListener("DOMTitleChanged", this);
       mm.addMessageListener("Extension:BrowserBackgroundChanged", this);
       mm.addMessageListener("Extension:BrowserContentLoaded", this);
       mm.addMessageListener("Extension:BrowserResized", this);
+      browser.addEventListener("pagetitlechanged", this);
       browser.addEventListener("DOMWindowClose", this);
       return browser;
     };
 
     if (!popupURL) {
       // For remote browsers, we can't do any setup until the frame loader is
       // created. Non-remote browsers get a message manager immediately, so
       // there's no need to wait for the load event.
--- a/browser/components/sessionstore/test/browser_tab_label_during_restore.js
+++ b/browser/components/sessionstore/test/browser_tab_label_during_restore.js
@@ -25,17 +25,21 @@ add_task(async function() {
   function observeLabelChanges(tab, expectedLabels) {
     let seenLabels = [tab.label];
     function TabAttrModifiedListener(event) {
       if (event.detail.changed.some(attr => attr == "label")) {
         seenLabels.push(tab.label);
       }
     }
     tab.addEventListener("TabAttrModified", TabAttrModifiedListener);
-    return () => {
+    return async () => {
+      await BrowserTestUtils.waitForCondition(
+        () => seenLabels.length == expectedLabels.length,
+        "saw " + seenLabels.length + " TabAttrModified events"
+      );
       tab.removeEventListener("TabAttrModified", TabAttrModifiedListener);
       is(
         JSON.stringify(seenLabels),
         JSON.stringify(expectedLabels || []),
         "observed tab label changes"
       );
     };
   }
@@ -90,57 +94,57 @@ add_task(async function() {
   browserLoadedPromise = BrowserTestUtils.browserLoaded(
     tab2.linkedBrowser,
     false,
     ABOUT_ROBOTS_URI
   );
   gBrowser.selectedTab = tab2;
   await browserLoadedPromise;
   ok(!tab2.hasAttribute("pending"), "second tab isn't pending anymore");
+  await finishObservingLabelChanges();
   ok(
     document.title.startsWith(ABOUT_ROBOTS_TITLE),
     "title bar displays content title"
   );
-  finishObservingLabelChanges();
 
   info("selecting the third tab");
   finishObservingLabelChanges = observeLabelChanges(tab3, [
     "example.com/",
     REMOTE_TITLE,
   ]);
   browserLoadedPromise = BrowserTestUtils.browserLoaded(
     tab3.linkedBrowser,
     false,
     REMOTE_URL
   );
   gBrowser.selectedTab = tab3;
   await browserLoadedPromise;
   ok(!tab3.hasAttribute("pending"), "third tab isn't pending anymore");
+  await finishObservingLabelChanges();
   ok(
     document.title.startsWith(REMOTE_TITLE),
     "title bar displays content title"
   );
-  finishObservingLabelChanges();
 
   info("selecting the fourth tab");
   finishObservingLabelChanges = observeLabelChanges(tab4, [NO_TITLE_URL]);
   browserLoadedPromise = BrowserTestUtils.browserLoaded(
     tab4.linkedBrowser,
     false,
     NO_TITLE_URL
   );
   gBrowser.selectedTab = tab4;
   await browserLoadedPromise;
   ok(!tab4.hasAttribute("pending"), "fourth tab isn't pending anymore");
+  await finishObservingLabelChanges();
   is(
     document.title,
     document.getElementById("bundle_brand").getString("brandFullName"),
     "title bar doesn't display content title since page doesn't have one"
   );
-  finishObservingLabelChanges();
 
   info("restoring the modified browser state");
   gBrowser.selectedTab = tab3;
   await TabStateFlusher.flushWindow(window);
   await promiseBrowserState(SessionStore.getBrowserState());
   [tab1, tab2, tab3, tab4, tab5] = gBrowser.tabs;
   is(tab3, gBrowser.selectedTab, "third tab is selected after restoring");
   ok(
@@ -167,12 +171,12 @@ add_task(async function() {
   );
   gBrowser.selectedTab = tab1;
   ok(
     document.title.startsWith(REMOTE_TITLE),
     "title bar displays content title"
   );
   await tabContentRestored;
   ok(!tab1.hasAttribute("pending"), "first tab isn't pending anymore");
-  finishObservingLabelChanges();
+  await finishObservingLabelChanges();
 
   await promiseBrowserState(BACKUP_STATE);
 });
--- a/devtools/client/responsive/browser/tunnel.js
+++ b/devtools/client/responsive/browser/tunnel.js
@@ -24,17 +24,16 @@ function debug(msg) {
 /**
  * Properties swapped between browsers by browser.js's `swapDocShells`.
  */
 const SWAPPED_BROWSER_STATE = [
   "_remoteFinder",
   "_securityUI",
   "_documentURI",
   "_documentContentType",
-  "_contentTitle",
   "_characterSet",
   "_contentPrincipal",
   "_isSyntheticDocument",
   "_innerWindowID",
 ];
 
 /**
  * Various parts of the Firefox code base expect to access properties on the browser
@@ -97,17 +96,16 @@ function tunnelToInnerBrowser(outer, inn
         inner._documentContentType = outer._documentContentType;
       }
     },
 
     onLocationChange: (webProgress, request, location, flags) => {
       if (webProgress?.isTopLevel) {
         inner._documentURI = outer._documentURI;
         inner._documentContentType = outer._documentContentType;
-        inner._contentTitle = outer._contentTitle;
         inner._characterSet = outer._characterSet;
         inner._contentPrincipal = outer._contentPrincipal;
         inner._isSyntheticDocument = outer._isSyntheticDocument;
         inner._innerWindowID = outer._innerWindowID;
         inner._remoteWebNavigation._currentURI =
           outer._remoteWebNavigation._currentURI;
       }
     },
@@ -452,17 +450,16 @@ MessageManagerTunnel.prototype = {
     "SessionStore:restoreHistory",
     "SessionStore:restoreTabContent",
   ],
 
   INNER_TO_OUTER_MESSAGES: [
     // Messages sent to browser.js
     "PageStyle:StyleSheets",
     // Messages sent to browser.js
-    "DOMTitleChanged",
     "InPermitUnload",
     "PermitUnload",
     // Messages sent to SessionStore.jsm
     "SessionStore:update",
     // Messages sent to BrowserTestUtils.jsm
     "browser-test-utils:loadEvent",
   ],
 
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -46,21 +46,16 @@ loader.lazyRequireGetter(
   "devtools/server/actors/process",
   true
 );
 loader.lazyImporter(
   this,
   "AddonManager",
   "resource://gre/modules/AddonManager.jsm"
 );
-loader.lazyImporter(
-  this,
-  "AppConstants",
-  "resource://gre/modules/AppConstants.jsm"
-);
 
 /**
  * Browser-specific actors.
  */
 
 /**
  * Retrieve the window type of the top-level window |window|.
  */
@@ -173,25 +168,16 @@ exports.createRootActor = function creat
  * linked browser's content window objects do).
  *
  * However, while we could thus assume that each tab stays with the XUL window
  * it belonged to when it was created, I'm not sure this is behavior one should
  * rely upon. When a XUL window is closed, we take the less efficient, more
  * conservative approach of simply searching the entire table for actors that
  * belong to the closing XUL window, rather than trying to somehow track which
  * XUL window each tab belongs to.
- *
- * - Title changes:
- *
- * For tabs living in the child process, we listen for DOMTitleChange message
- * via the top-level window's message manager.
- * But as these messages aren't sent for tabs loaded in the parent process,
- * we also listen for TabAttrModified event, which is fired only on Firefox
- * desktop.
- * Also, we listen DOMTitleChange event on Android document.
  */
 function BrowserTabList(connection) {
   this._connection = connection;
 
   /*
    * The XUL document of a tabbed browser window has "tab" elements, whose
    * 'linkedBrowser' JavaScript properties are "browser" elements; those
    * browsers' 'contentWindow' properties are wrappers on the tabs' content
@@ -226,17 +212,17 @@ function BrowserTabList(connection) {
    * True if we've been iterated over since we last called our onListChanged
    * hook.
    */
   this._mustNotify = false;
 
   /* True if we're testing, and should throw if consistency checks fail. */
   this._testing = false;
 
-  this._onAndroidDocumentEvent = this._onAndroidDocumentEvent.bind(this);
+  this._onPageTitleChangedEvent = this._onPageTitleChangedEvent.bind(this);
 }
 
 BrowserTabList.prototype.constructor = BrowserTabList;
 
 BrowserTabList.prototype.destroy = function() {
   this._actorByBrowser.clear();
   this.onListChanged = null;
 };
@@ -482,43 +468,24 @@ BrowserTabList.prototype._checkListening
    * only way to find out about tabs that come and go when top-level windows
    * are opened and closed.
    */
   this._listenToMediatorIf(
     (this._onListChanged && this._mustNotify) || this._actorByBrowser.size > 0
   );
 
   /*
-   * We also listen for title changed from the child process.
-   * This allows listening for title changes from OOP tabs.
-   * OOP tabs are running browser-child.js frame script which sends DOMTitleChanged
-   * events through the message manager.
+   * We also listen for title changed events on the browser.
    */
-  this._listenForMessagesIf(
+  this._listenForEventsIf(
     this._onListChanged && this._mustNotify,
     "_listeningForTitleChange",
-    ["DOMTitleChanged"]
+    ["pagetitlechanged"],
+    this._onPageTitleChangedEvent
   );
-
-  /*
-   * We also listen for title changed event on Android document.
-   * Android document events are used for single process Gecko View and Firefox for
-   * Android. They do no execute browser-child.js because of single process, instead
-   * DOMTitleChanged events are emitted on the top level document.
-   * Also, Multi process Gecko View is not covered by here since that receives title
-   * updates via DOMTitleChanged messages.
-   */
-  if (AppConstants.platform === "android") {
-    this._listenForEventsIf(
-      this._onListChanged && this._mustNotify,
-      "_listeningForAndroidDocument",
-      ["DOMTitleChanged"],
-      this._onAndroidDocumentEvent
-    );
-  }
 };
 
 /*
  * Add or remove event listeners for all XUL windows.
  *
  * @param shouldListen boolean
  *    True if we should add event handlers; false if we should remove them.
  * @param guard string
@@ -542,73 +509,29 @@ BrowserTabList.prototype._listenForEvent
         win[op](name, listener, false);
       }
     }
     this[guard] = shouldListen;
   }
 };
 
 /*
- * Add or remove message listeners for all XUL windows.
- *
- * @param shouldListen boolean
- *    True if we should add message listeners; false if we should remove them.
- * @param guard string
- *    The name of a guard property of 'this', indicating whether we're
- *    already listening for those messages.
- * @param messageNames array of strings
- *    An array of message names.
+ * Event listener for pagetitlechanged event.
  */
-BrowserTabList.prototype._listenForMessagesIf = function(
-  shouldListen,
-  guard,
-  messageNames
-) {
-  if (!shouldListen !== !this[guard]) {
-    const op = shouldListen ? "addMessageListener" : "removeMessageListener";
-    for (const win of Services.wm.getEnumerator(
-      DevToolsServer.chromeWindowType
-    )) {
-      for (const name of messageNames) {
-        win.messageManager[op](name, this);
-      }
-    }
-    this[guard] = shouldListen;
-  }
-};
-
-/*
- * This function assumes to be used as a event listener for Android document.
- */
-BrowserTabList.prototype._onAndroidDocumentEvent = function(event) {
+BrowserTabList.prototype._onPageTitleChangedEvent = function(event) {
   switch (event.type) {
-    case "DOMTitleChanged": {
+    case "pagetitlechanged": {
       const window = event.currentTarget.ownerGlobal;
       this._onDOMTitleChanged(window.browser);
       break;
     }
   }
 };
 
 /**
- * Implement nsIMessageListener.
- */
-BrowserTabList.prototype.receiveMessage = DevToolsUtils.makeInfallible(function(
-  message
-) {
-  const browser = message.target;
-  switch (message.name) {
-    case "DOMTitleChanged": {
-      this._onDOMTitleChanged(browser);
-      break;
-    }
-  }
-});
-
-/**
  * Handle "DOMTitleChanged" event.
  */
 BrowserTabList.prototype._onDOMTitleChanged = DevToolsUtils.makeInfallible(
   function(browser) {
     const actor = this._actorByBrowser.get(browser);
     if (actor) {
       this._notifyListChanged();
       this._checkListening();
@@ -618,18 +541,17 @@ BrowserTabList.prototype._onDOMTitleChan
 
 /**
  * Implement nsIDOMEventListener.
  */
 BrowserTabList.prototype.handleEvent = DevToolsUtils.makeInfallible(function(
   event
 ) {
   // If event target has `linkedBrowser`, the event target can be assumed <tab> element.
-  // Else (in Android case), because event target is assumed <browser> element,
-  // use the target as it is.
+  // Else, event target is assumed <browser> element, use the target as it is.
   const browser = event.target.linkedBrowser || event.target;
   switch (event.type) {
     case "TabOpen":
     case "TabSelect": {
       /* Don't create a new actor; iterate will take care of that. Just notify. */
       this._notifyListChanged();
       this._checkListening();
       break;
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -21,42 +21,16 @@ try {
 }
 
 // This message is used to measure content process startup performance in Talos
 // tests.
 sendAsyncMessage("Content:BrowserChildReady", {
   time: Services.telemetry.msSystemNow(),
 });
 
-addEventListener(
-  "DOMTitleChanged",
-  function(aEvent) {
-    if (
-      !aEvent.isTrusted ||
-      // Check that we haven't been closed (DOM code dispatches this event
-      // asynchronously).
-      content.closed
-    ) {
-      return;
-    }
-    // Ensure `docShell.document` (an nsIWebNavigation idl prop) is there:
-    docShell.QueryInterface(Ci.nsIWebNavigation);
-    if (
-      // Check that the document whose title changed is the toplevel document,
-      // rather than a subframe, and check that it is still the current
-      // document in the docshell - we may have started loading another one.
-      docShell.document != aEvent.target
-    ) {
-      return;
-    }
-    sendAsyncMessage("DOMTitleChanged", { title: content.document.title });
-  },
-  false
-);
-
 // This is here for now until we find a better way of forcing an about:blank load
 // with a particular principal that doesn't involve the message manager. We can't
 // do this with JS Window Actors for now because JS Window Actors are tied to the
 // document principals themselves, so forcing the load with a new principal is
 // self-destructive in that case.
 addMessageListener("BrowserElement:CreateAboutBlank", message => {
   if (!content.document || content.document.documentURI != "about:blank") {
     throw new Error("Can't create a content viewer unless on about:blank");
--- a/toolkit/content/widgets/browser-custom-element.js
+++ b/toolkit/content/widgets/browser-custom-element.js
@@ -326,18 +326,16 @@
       this._innerWindowID = null;
 
       this._lastSearchString = null;
 
       this._remoteWebNavigation = null;
 
       this._remoteWebProgress = null;
 
-      this._contentTitle = "";
-
       this._characterSet = "";
 
       this._mayEnableCharacterEncodingMenu = null;
 
       this._charsetAutodetected = false;
 
       this._contentPrincipal = null;
 
@@ -725,17 +723,17 @@
     }
 
     get sessionHistory() {
       return this.webNavigation.sessionHistory;
     }
 
     get contentTitle() {
       return this.isRemoteBrowser
-        ? this._contentTitle
+        ? this.browsingContext.currentWindowGlobal?.documentTitle
         : this.contentDocument.title;
     }
 
     set characterSet(val) {
       if (this.isRemoteBrowser) {
         this.sendMessageToActor(
           "UpdateCharacterSet",
           { value: val },
@@ -1215,17 +1213,16 @@
           aboutBlank,
           this.loadContext
         );
         // CSP for about:blank is null; if we ever change _contentPrincipal above,
         // we should re-evaluate the CSP here.
         this._csp = null;
 
         this.messageManager.addMessageListener("Browser:Init", this);
-        this.messageManager.addMessageListener("DOMTitleChanged", this);
 
         let jsm = "resource://gre/modules/RemoteWebProgress.jsm";
         let { RemoteWebProgressManager } = ChromeUtils.import(jsm, {});
 
         let oldManager = this._remoteWebProgressManager;
         this._remoteWebProgressManager = new RemoteWebProgressManager(this);
         if (oldManager) {
           // We're transitioning from one remote type to another. This means that
@@ -1341,19 +1338,16 @@
 
     receiveMessage(aMessage) {
       if (this.isRemoteBrowser) {
         const data = aMessage.data;
         switch (aMessage.name) {
           case "Browser:Init":
             this._outerWindowID = data.outerWindowID;
             break;
-          case "DOMTitleChanged":
-            this._contentTitle = data.title;
-            break;
           default:
             break;
         }
       }
     }
 
     updateSecurityUIForSecurityChange(aSecurityInfo, aState, aIsSecureContext) {
       if (this.isRemoteBrowser && this.messageManager) {
@@ -1417,17 +1411,16 @@
         }
 
         if (aContentType != null) {
           this._documentContentType = aContentType;
         }
 
         this._remoteWebNavigation._currentURI = aLocation;
         this._documentURI = aDocumentURI;
-        this._contentTitle = aTitle;
         this._contentPrincipal = aContentPrincipal;
         this._contentStoragePrincipal = aContentStoragePrincipal;
         this._csp = aCSP;
         this._referrerInfo = aReferrerInfo;
         this._isSyntheticDocument = aIsSynthetic;
         this._innerWindowID = aInnerWindowID;
         this._contentRequestContextID = aHaveRequestContextID
           ? aRequestContextID
@@ -1812,17 +1805,16 @@
           ...[
             "_remoteWebNavigation",
             "_remoteWebProgressManager",
             "_remoteWebProgress",
             "_remoteFinder",
             "_securityUI",
             "_documentURI",
             "_documentContentType",
-            "_contentTitle",
             "_characterSet",
             "_mayEnableCharacterEncodingMenu",
             "_charsetAutodetected",
             "_contentPrincipal",
             "_contentStoragePrincipal",
             "_isSyntheticDocument",
             "_innerWindowID",
           ]