Backed out 2 changesets (bug 1547835, bug 1550526) for causing EventStateManager.cpp asertion failures CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Mon, 13 May 2019 23:50:59 +0300
changeset 532477 d8a1c2fa4ea526b7185459ef414828efb8bf9841
parent 532476 5d66a70eebdbe0e8cbd592350741cfcab1420b0d
child 532478 225fb1820fe5ce1c86421cb2e41137bd6ac27da4
push id11268
push usercsabou@mozilla.com
push dateTue, 14 May 2019 15:24:22 +0000
treeherdermozilla-beta@5fb7fcd568d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1547835, 1550526
milestone68.0a1
backs out0bc1bd7285c1e06298eabf873e627596d95e73c2
9053b1f316ff269aba9b7b4bcbea0b97f9ca733c
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 1547835, bug 1550526) for causing EventStateManager.cpp asertion failures CLOSED TREE Backed out changeset 0bc1bd7285c1 (bug 1547835) Backed out changeset 9053b1f316ff (bug 1550526)
toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
toolkit/mozapps/extensions/content/aboutaddons.css
toolkit/mozapps/extensions/content/aboutaddons.html
toolkit/mozapps/extensions/content/aboutaddons.js
toolkit/mozapps/extensions/content/aboutaddonsCommon.js
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/extensions.xml
toolkit/mozapps/extensions/content/named-deck.js
toolkit/mozapps/extensions/content/updateinfo.xsl
toolkit/mozapps/extensions/internal/XPIInstall.jsm
toolkit/mozapps/extensions/jar.mn
toolkit/mozapps/extensions/test/browser/browser.ini
toolkit/mozapps/extensions/test/browser/browser_html_named_deck.js
toolkit/mozapps/extensions/test/browser/browser_html_recent_updates.js
toolkit/mozapps/extensions/test/browser/browser_html_updates.js
--- a/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
@@ -384,18 +384,16 @@ remove-addon-button = Remove
 disable-addon-button = Disable
 enable-addon-button = Enable
 expand-addon-button = More Options
 preferences-addon-button =
     { PLATFORM() ->
         [windows] Options
        *[other] Preferences
     }
-details-addon-button = Details
-release-notes-addon-button = Release Notes
 
 addons-enabled-heading = Enabled
 addons-disabled-heading = Disabled
 
 ask-to-activate-button = Ask to Activate
 always-activate-button = Always Activate
 never-activate-button = Never Activate
 
@@ -444,11 +442,8 @@ install-update-button = Update
 addon-badge-private-browsing-allowed =
     .title = Allowed in private windows
 addon-detail-private-browsing-help = When allowed, the extension will have access to your online activities while private browsing. <a data-l10n-name="learn-more">Learn more</a>
 addon-detail-private-browsing-allow = Allow
 addon-detail-private-browsing-disallow = Don’t Allow
 
 available-updates-heading = Available Updates
 recent-updates-heading = Recent Updates
-
-release-notes-loading = Loading…
-release-notes-error = Sorry, but there was an error loading the release notes.
--- a/toolkit/mozapps/extensions/content/aboutaddons.css
+++ b/toolkit/mozapps/extensions/content/aboutaddons.css
@@ -353,17 +353,8 @@ panel-item-separator[hidden] {
   color: var(--in-content-link-color-hover) !important;
   text-decoration: underline;
 }
 
 .button-link:active {
   color: var(--in-content-link-color-active) !important;
   text-decoration: none;
 }
-
-.deck-tab-group {
-  border-bottom: 1px solid var(--grey-90-a20);
-  border-top: 1px solid var(--grey-90-a20);
-  margin-top: 8px;
-  margin-inline: -16px;
-  font-size: 0;
-  line-height: 0;
-}
--- a/toolkit/mozapps/extensions/content/aboutaddons.html
+++ b/toolkit/mozapps/extensions/content/aboutaddons.html
@@ -3,17 +3,16 @@
   <head>
     <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
     <link rel="stylesheet" href="chrome://mozapps/content/extensions/aboutaddons.css">
 
     <link rel="localization" href="branding/brand.ftl">
     <link rel="localization" href="toolkit/about/aboutAddons.ftl">
     <link rel="localization" href="toolkit/about/abuseReports.ftl">
 
-    <script src="chrome://mozapps/content/extensions/named-deck.js"></script>
     <script src="chrome://mozapps/content/extensions/aboutaddonsCommon.js"></script>
     <script src="chrome://mozapps/content/extensions/message-bar.js"></script>
     <script src="chrome://mozapps/content/extensions/abuse-reports.js"></script>
     <script src="chrome://mozapps/content/extensions/aboutaddons.js"></script>
   </head>
   <body>
     <message-bar-stack id="abuse-reports-messages" reverse max-message-bar-count="3">
     </message-bar-stack>
@@ -80,89 +79,80 @@
       </div>
       <div class="disco-description-statistics">
         <five-star-rating></five-star-rating>
         <span class="disco-user-count"></span>
       </div>
     </template>
 
     <template name="addon-details">
-      <div class="deck-tab-group">
-        <named-deck-button deck="details-deck" name="details" data-l10n-id="details-addon-button"></named-deck-button>
-        <named-deck-button deck="details-deck" name="release-notes" data-l10n-id="release-notes-addon-button"></named-deck-button>
+      <div class="addon-detail-description"></div>
+      <div class="addon-detail-contribute">
+        <label data-l10n-id="detail-contributions-description"></label>
+        <button
+          class="addon-detail-contribute-button"
+          action="contribute"
+          data-l10n-id="detail-contributions-button"
+          data-l10n-attrs="accesskey">
+        </button>
+      </div>
+      <div class="addon-detail-row addon-detail-row-updates">
+        <label data-l10n-id="addon-detail-updates-label"></label>
+        <div>
+          <button class="button-link" data-l10n-id="addon-detail-update-check-label" action="update-check" hidden></button>
+          <label>
+            <input type="radio" name="autoupdate" value="1"/>
+            <span data-l10n-id="addon-detail-updates-radio-default"></span>
+          </label>
+          <label>
+            <input type="radio" name="autoupdate" value="2"/>
+            <span data-l10n-id="addon-detail-updates-radio-on"></span>
+          </label>
+          <label>
+            <input type="radio" name="autoupdate" value="0"/>
+            <span data-l10n-id="addon-detail-updates-radio-off"></span>
+          </label>
+        </div>
       </div>
-      <named-deck id="details-deck">
-        <section name="details">
-          <div class="addon-detail-description"></div>
-          <div class="addon-detail-contribute">
-            <label data-l10n-id="detail-contributions-description"></label>
-            <button
-              class="addon-detail-contribute-button"
-              action="contribute"
-              data-l10n-id="detail-contributions-button"
-              data-l10n-attrs="accesskey">
-            </button>
-          </div>
-          <div class="addon-detail-row addon-detail-row-updates">
-            <label data-l10n-id="addon-detail-updates-label"></label>
-            <div>
-              <button class="button-link" data-l10n-id="addon-detail-update-check-label" action="update-check" hidden></button>
-              <label>
-                <input type="radio" name="autoupdate" value="1"/>
-                <span data-l10n-id="addon-detail-updates-radio-default"></span>
-              </label>
-              <label>
-                <input type="radio" name="autoupdate" value="2"/>
-                <span data-l10n-id="addon-detail-updates-radio-on"></span>
-              </label>
-              <label>
-                <input type="radio" name="autoupdate" value="0"/>
-                <span data-l10n-id="addon-detail-updates-radio-off"></span>
-              </label>
-            </div>
-          </div>
-          <div class="addon-detail-row addon-detail-row-has-help addon-detail-row-private-browsing">
-            <label data-l10n-id="detail-private-browsing-label"></label>
-            <div>
-              <label>
-                <input type="radio" name="private-browsing" value="1"/>
-                <span data-l10n-id="addon-detail-private-browsing-allow"></span>
-              </label>
-              <label>
-                <input type="radio" name="private-browsing" value="0"/>
-                <span data-l10n-id="addon-detail-private-browsing-disallow"></span>
-              </label>
-            </div>
-          </div>
-          <div class="addon-detail-row-help" data-l10n-id="addon-detail-private-browsing-help">
-            <a target="_blank" data-l10n-name="learn-more"></a>
-          </div>
-          <div class="addon-detail-row addon-detail-row-author">
-            <label data-l10n-id="addon-detail-author-label"></label>
-          </div>
-          <div class="addon-detail-row addon-detail-row-version">
-            <label data-l10n-id="addon-detail-version-label"></label>
-          </div>
-          <div class="addon-detail-row addon-detail-row-lastUpdated">
-            <label data-l10n-id="addon-detail-last-updated-label"></label>
-          </div>
-          <div class="addon-detail-row addon-detail-row-homepage">
-            <label data-l10n-id="addon-detail-homepage-label"></label>
-            <a target="_blank"></a>
-          </div>
-          <div class="addon-detail-row addon-detail-row-rating">
-            <label data-l10n-id="addon-detail-rating-label"></label>
-            <div class="addon-detail-rating">
-              <five-star-rating></five-star-rating>
-              <a target="_blank"></a>
-            </div>
-          </div>
-        </section>
-        <update-release-notes name="release-notes"></update-release-notes>
-      </named-deck>
+      <div class="addon-detail-row addon-detail-row-has-help addon-detail-row-private-browsing">
+        <label data-l10n-id="detail-private-browsing-label"></label>
+        <div>
+          <label>
+            <input type="radio" name="private-browsing" value="1"/>
+            <span data-l10n-id="addon-detail-private-browsing-allow"></span>
+          </label>
+          <label>
+            <input type="radio" name="private-browsing" value="0"/>
+            <span data-l10n-id="addon-detail-private-browsing-disallow"></span>
+          </label>
+        </div>
+      </div>
+      <div class="addon-detail-row-help" data-l10n-id="addon-detail-private-browsing-help">
+        <a target="_blank" data-l10n-name="learn-more"></a>
+      </div>
+      <div class="addon-detail-row addon-detail-row-author">
+        <label data-l10n-id="addon-detail-author-label"></label>
+      </div>
+      <div class="addon-detail-row addon-detail-row-version">
+        <label data-l10n-id="addon-detail-version-label"></label>
+      </div>
+      <div class="addon-detail-row addon-detail-row-lastUpdated">
+        <label data-l10n-id="addon-detail-last-updated-label"></label>
+      </div>
+      <div class="addon-detail-row addon-detail-row-homepage">
+        <label data-l10n-id="addon-detail-homepage-label"></label>
+        <a target="_blank"></a>
+      </div>
+      <div class="addon-detail-row addon-detail-row-rating">
+        <label data-l10n-id="addon-detail-rating-label"></label>
+        <div class="addon-detail-rating">
+          <five-star-rating></five-star-rating>
+          <a target="_blank"></a>
+        </div>
+      </div>
     </template>
 
     <template name="five-star-rating">
       <link rel="stylesheet" href="chrome://mozapps/content/extensions/rating-star.css">
       <span class="rating-star"></span>
       <span class="rating-star"></span>
       <span class="rating-star"></span>
       <span class="rating-star"></span>
--- a/toolkit/mozapps/extensions/content/aboutaddons.js
+++ b/toolkit/mozapps/extensions/content/aboutaddons.js
@@ -679,131 +679,30 @@ class FiveStarRating extends HTMLElement
     }
     document.l10n.setAttributes(this, "five-star-rating", {
       rating: this.rating,
     });
   }
 }
 customElements.define("five-star-rating", FiveStarRating);
 
-class UpdateReleaseNotes extends HTMLElement {
-  connectedCallback() {
-    this.addEventListener("click", this);
-  }
-
-  disconnectedCallback() {
-    this.removeEventListener("click", this);
-  }
-
-  handleEvent(e) {
-    // We used to strip links, but ParserUtils.parseFragment() leaves them in,
-    // so just make sure we open them using the null principal in a new tab.
-    if (e.type == "click" && e.target.localName == "a" && e.target.href) {
-      e.preventDefault();
-      e.stopPropagation();
-      windowRoot.ownerGlobal.openWebLinkIn(e.target.href, "tab");
-    }
-  }
-
-  async loadForUri(uri) {
-    // Don't try to load for the same update a second time.
-    if (this.uri == uri.spec) {
-      this.dispatchEvent(new CustomEvent("release-notes-cached"));
-      return;
-    }
-    this.uri = uri.spec;
-
-    // Can't load the release notes without a URL to load.
-    if (!uri) {
-      this.setErrorMessage();
-      this.dispatchEvent(new CustomEvent("release-notes-error"));
-      return;
-    }
-
-    // Set the loading message before hitting the network.
-    this.setLoadingMessage();
-    this.dispatchEvent(new CustomEvent("release-notes-loading"));
-
-    try {
-      // loadReleaseNotes with fetch and sanitize the release notes.
-      let fragment = await loadReleaseNotes(uri);
-      this.textContent = "";
-      this.appendChild(fragment);
-      this.dispatchEvent(new CustomEvent("release-notes-loaded"));
-    } catch (e) {
-      this.setErrorMessage();
-      this.dispatchEvent(new CustomEvent("release-notes-error"));
-    }
-  }
-
-  setMessage(id) {
-    this.textContent = "";
-    let message = document.createElement("p");
-    document.l10n.setAttributes(message, id);
-    this.appendChild(message);
-  }
-
-  setLoadingMessage() {
-    this.setMessage("release-notes-loading");
-  }
-
-  setErrorMessage() {
-    this.setMessage("release-notes-error");
-  }
-}
-customElements.define("update-release-notes", UpdateReleaseNotes);
-
-
 class AddonDetails extends HTMLElement {
   connectedCallback() {
     if (this.children.length == 0) {
       this.render();
     }
-    this.deck.addEventListener("view-changed", this);
-  }
-
-  disconnectedCallback() {
-    this.deck.removeEventListener("view-changed", this);
-  }
-
-  handleEvent(e) {
-    if (e.type == "view-changed" && e.target == this.deck) {
-      if (this.deck.selectedViewName == "release-notes") {
-        let releaseNotes = this.querySelector("update-release-notes");
-        let uri = this.releaseNotesUri;
-        if (uri) {
-          releaseNotes.loadForUri(uri);
-        }
-      }
-    }
-  }
-
-  get releaseNotesUri() {
-    return this.addon.updateInstall ?
-      this.addon.updateInstall.releaseNotesURI : this.addon.releaseNotesURI;
   }
 
   setAddon(addon) {
     this.addon = addon;
   }
 
   update() {
     let {addon} = this;
 
-    // Hide tab buttons that won't have any content.
-    let getButtonByName =
-      name => this.tabGroup.querySelector(`[name="${name}"]`);
-    let notesBtn = getButtonByName("release-notes");
-    notesBtn.hidden = !this.releaseNotesUri;
-
-    // Hide the tab group if "details" is the only visible button.
-    this.tabGroup.hidden = Array.from(this.tabGroup.children).every(button => {
-      return button.name == "details" || button.hidden;
-    });
-
     // Show the update check button if necessary. The button might not exist if
     // the add-on doesn't support updates.
     let updateButton = this.querySelector('[action="update-check"]');
     if (updateButton) {
       updateButton.hidden =
         this.addon.updateInstall || AddonManager.shouldAutoUpdate(this.addon);
     }
 
@@ -818,19 +717,16 @@ class AddonDetails extends HTMLElement {
     let {addon} = this;
     if (!addon) {
       throw new Error("addon-details must be initialized by setAddon");
     }
 
     this.textContent = "";
     this.appendChild(importTemplate("addon-details"));
 
-    this.deck = this.querySelector("named-deck");
-    this.tabGroup = this.querySelector(".deck-tab-group");
-
     // Full description.
     let description = this.querySelector(".addon-detail-description");
     if (addon.getFullDescription) {
       description.appendChild(addon.getFullDescription(document));
     } else if (addon.fullDescription) {
       description.appendChild(nl2br(addon.fullDescription));
     }
 
--- a/toolkit/mozapps/extensions/content/aboutaddonsCommon.js
+++ b/toolkit/mozapps/extensions/content/aboutaddonsCommon.js
@@ -1,17 +1,16 @@
 /* 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/. */
 /* eslint max-len: ["error", 80] */
 
 "use strict";
 
-/* exported attachUpdateHandler, getBrowserElement, loadReleaseNotes,
- * openOptionsInTab */
+/* exported attachUpdateHandler, getBrowserElement, openOptionsInTab */
 
 var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(
   this, "WEBEXT_PERMISSION_PROMPTS",
   "extensions.webextPermissionPrompts", false);
 
@@ -61,39 +60,16 @@ function attachUpdateHandler(install) {
           },
         },
       };
       Services.obs.notifyObservers(subject, "webextension-permission-prompt");
     });
   };
 }
 
-async function loadReleaseNotes(uri) {
-  const res = await fetch(uri.spec, {credentials: "omit"});
-
-  if (!res.ok) {
-    throw new Error("Error loading release notes");
-  }
-
-  // Load the content.
-  const text = await res.text();
-
-  // Setup the content sanitizer.
-  const ParserUtils = Cc["@mozilla.org/parserutils;1"]
-    .getService(Ci.nsIParserUtils);
-  const flags =
-    ParserUtils.SanitizerDropMedia |
-    ParserUtils.SanitizerDropNonCSSPresentation |
-    ParserUtils.SanitizerDropForms;
-
-  // Sanitize and parse the content to a fragment.
-  const context = document.createElement("div");
-  return ParserUtils.parseFragment(text, flags, false, uri, context);
-}
-
 function openOptionsInTab(optionsURL) {
   let mainWindow = window.windowRoot.ownerGlobal;
   if ("switchToTabHavingURI" in mainWindow) {
     mainWindow.switchToTabHavingURI(optionsURL, true, {
       relatedToCurrent: true,
       triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
     });
     return true;
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -2,17 +2,17 @@
  * 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/. */
 
 "use strict";
 
 /* import-globals-from ../../../content/contentAreaUtils.js */
 /* import-globals-from aboutaddonsCommon.js */
 /* globals ProcessingInstruction */
-/* exported gBrowser, loadView */
+/* exported UPDATES_RELEASENOTES_TRANSFORMFILE, XMLURI_PARSE_ERROR, loadView, gBrowser */
 
 const {DeferredTask} = ChromeUtils.import("resource://gre/modules/DeferredTask.jsm");
 const {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
 const {AddonRepository} = ChromeUtils.import("resource://gre/modules/addons/AddonRepository.jsm");
 const {AddonSettings} = ChromeUtils.import("resource://gre/modules/addons/AddonSettings.jsm");
 
 ChromeUtils.defineModuleGetter(this, "AMTelemetry",
                                "resource://gre/modules/AddonManager.jsm");
@@ -54,16 +54,19 @@ const PREF_GETADDONS_CACHE_ID_ENABLED = 
 const PREF_UI_TYPE_HIDDEN = "extensions.ui.%TYPE%.hidden";
 const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory";
 const PREF_LEGACY_EXCEPTIONS = "extensions.legacy.exceptions";
 const PREF_LEGACY_ENABLED = "extensions.legacy.enabled";
 
 const LOADING_MSG_DELAY = 100;
 
 const UPDATES_RECENT_TIMESPAN = 2 * 24 * 3600000; // 2 days (in milliseconds)
+const UPDATES_RELEASENOTES_TRANSFORMFILE = "chrome://mozapps/content/extensions/updateinfo.xsl";
+
+const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
 
 var gViewDefault = "addons://discover/";
 
 XPCOMUtils.defineLazyGetter(this, "extensionStylesheets", () => {
   const {ExtensionParent} = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
   return ExtensionParent.extensionStylesheets;
 });
 
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -1177,34 +1177,79 @@
             this.dispatchEvent(event);
           };
 
           if (!aURI || this._relNotesLoaded) {
             sendToggleEvent();
             return;
           }
 
+          var relNotesData = null, transformData = null;
+
           this._relNotesLoaded = true;
           this._relNotesLoading.hidden = false;
           this._relNotesError.hidden = true;
 
-          loadReleaseNotes(aURI).then(fragment => {
+          let showRelNotes = () => {
+            if (!relNotesData || !transformData)
+              return;
+
             this._relNotesLoading.hidden = true;
+
+            var processor = new XSLTProcessor();
+            processor.flags |= XSLTProcessor.DISABLE_ALL_LOADS;
+
+            processor.importStylesheet(transformData);
+            var fragment = processor.transformToFragment(relNotesData, document);
             this._relNotes.appendChild(fragment);
             if (this.hasAttribute("show-relnotes")) {
               var container = this._relNotesContainer;
               container.style.height = container.scrollHeight + "px";
             }
             sendToggleEvent();
-          }, () => {
+          };
+
+          let handleError = () => {
+            dataReq.abort();
+            styleReq.abort();
             this._relNotesLoading.hidden = true;
             this._relNotesError.hidden = false;
             this._relNotesLoaded = false; // allow loading to be re-tried
             sendToggleEvent();
-          });
+          };
+
+          function handleResponse(aEvent) {
+            var req = aEvent.target;
+            var ct = req.getResponseHeader("content-type");
+            if ((!ct || !ct.includes("text/html")) &&
+                req.responseXML &&
+                req.responseXML.documentElement.namespaceURI != XMLURI_PARSE_ERROR) {
+              if (req == dataReq)
+                relNotesData = req.responseXML;
+              else
+                transformData = req.responseXML;
+              showRelNotes();
+            } else {
+              handleError();
+            }
+          }
+
+          var dataReq = new XMLHttpRequest({mozAnon: true});
+          dataReq.open("GET", aURI.spec, true);
+          dataReq.responseType = "document";
+          dataReq.addEventListener("load", handleResponse);
+          dataReq.addEventListener("error", handleError);
+          dataReq.send(null);
+
+          var styleReq = new XMLHttpRequest({mozAnon: true});
+          styleReq.open("GET", UPDATES_RELEASENOTES_TRANSFORMFILE, true);
+          styleReq.responseType = "document";
+          styleReq.addEventListener("load", handleResponse);
+          styleReq.addEventListener("error", handleError);
+          styleReq.send(null);
         ]]></body>
       </method>
 
       <method name="toggleReleaseNotes">
         <body><![CDATA[
           if (this.hasAttribute("show-relnotes")) {
             this._relNotesContainer.style.height = "0px";
             this.removeAttribute("show-relnotes");
@@ -1467,22 +1512,16 @@
 
     <handlers>
       <handler event="click" button="0"><![CDATA[
         if (!["button", "checkbox", "menulist", "menuitem"].includes(event.originalTarget.localName) &&
             !event.originalTarget.classList.contains("text-link") &&
             // Treat the relnotes container as embedded text instead of a click target.
             !event.originalTarget.closest(".relnotes-container")) {
           this.showInDetailView();
-        } else if (event.originalTarget.localName == "a" &&
-                   event.originalTarget.closest(".relnotes-container") &&
-                   event.originalTarget.href) {
-          event.preventDefault();
-          event.stopPropagation();
-          openURL(event.originalTarget.href);
         }
       ]]></handler>
     </handlers>
   </binding>
 
 
   <!-- Addon - uninstalled - An uninstalled addon that can be re-installed. -->
   <binding id="addon-uninstalled"
deleted file mode 100644
--- a/toolkit/mozapps/extensions/content/named-deck.js
+++ /dev/null
@@ -1,221 +0,0 @@
-/* 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/. */
-/* eslint max-len: ["error", 80] */
-
-"use strict";
-
-/**
- * This element is for use with the <named-deck> element. Set the target
- * <named-deck>'s ID in the "deck" attribute and the button's selected state
- * will reflect the deck's state. When the button is clicked, it will set the
- * view in the <named-deck> to the button's "name" attribute.
- *
- * NOTE: This does not observe changes to the "deck" or "name" attributes, so
- * changing them likely won't work properly.
- *
- * <named-deck-button deck="pet-deck" name="dogs">Dogs</named-deck-button>
- * <named-deck id="pet-deck">
- *   <p name="cats">I like cats.</p>
- *   <p name="dogs">I like dogs.</p>
- * </named-deck>
- *
- * let btn = document.querySelector("named-deck-button");
- * let deck = document.querySelector("named-deck");
- * deck.selectedViewName == "cats";
- * btn.selected == false; // Selected was pulled from the related deck.
- * btn.click();
- * deck.selectedViewName == "dogs";
- * btn.selected == true; // Selected updated when view changed.
- */
-class NamedDeckButton extends HTMLElement {
-  constructor() {
-    super();
-    this.attachShadow({mode: "open"});
-    // Include styles inline to avoid a FOUC.
-    let style = document.createElement("style");
-    style.textContent = `
-      button {
-        -moz-appearance: none;
-        border: none;
-        border-top: 2px solid transparent;
-        border-bottom: 2px solid transparent;
-        background: white;
-        font-size: 14px;
-        line-height: 20px;
-        padding: 4px 16px;
-        color: var(--grey-90);
-      }
-
-      button:hover {
-        background-color: var(--grey-90-a10);
-        border-top-color: var(--grey-90-a20);
-      }
-
-      button:hover:active {
-        background-color: var(--grey-90-a20);
-      }
-
-      :host([selected]) button {
-        border-top-color: var(--blue-60);
-        color: var(--blue-60);
-      }
-    `;
-    this.shadowRoot.appendChild(style);
-
-    let button = document.createElement("button");
-    button.appendChild(document.createElement("slot"));
-    this.shadowRoot.appendChild(button);
-
-    this.addEventListener("click", this);
-  }
-
-  connectedCallback() {
-    this.setSelectedFromDeck();
-    document.addEventListener("view-changed", this, {capture: true});
-  }
-
-  disconnectedCallback() {
-    document.removeEventListener("view-changed", this, {capture: true});
-  }
-
-  get deckId() {
-    return this.getAttribute("deck");
-  }
-
-  set deckId(val) {
-    this.setAttribute("deck", val);
-  }
-
-  get deck() {
-    return document.getElementById(this.deckId);
-  }
-
-  handleEvent(e) {
-    if (e.type == "view-changed" && e.target.id == this.deckId) {
-      this.setSelectedFromDeck();
-    } else if (e.type == "click") {
-      let {deck} = this;
-      if (deck) {
-        deck.selectedViewName = this.name;
-      }
-    }
-  }
-
-  get name() {
-    return this.getAttribute("name");
-  }
-
-  get selected() {
-    return this.hasAttribute("selected");
-  }
-
-  set selected(val) {
-    this.toggleAttribute("selected", !!val);
-  }
-
-  setSelectedFromDeck() {
-    let {deck} = this;
-    this.selected = deck && deck.selectedViewName == this.name;
-  }
-}
-customElements.define("named-deck-button", NamedDeckButton);
-
-/**
- * A deck that is indexed by the "name" attribute of its children. The
- * <named-deck-button> element is a companion element that can update its state
- * and change the view of a <named-deck>.
- *
- * When the deck is connected it will set the first child as the selected view
- * if a view is not already selected.
- *
- * The deck is implemented using a named slot. Setting a slot directly on a
- * child element of the deck is not supported.
- *
- * You can get or set the selected view by name with the `selectedViewName`
- * property or by setting the "selected-view" attribute.
- *
- * <named-deck>
- *   <section name="cats">Some info about cats.</section>
- *   <section name="dogs">Some dog stuff.</section>
- * </named-deck>
- *
- * let deck = document.querySelector("named-deck");
- * deck.selectedViewName == "cats"; // Cat info is shown.
- * deck.selectedViewName = "dogs";
- * deck.selectedViewName == "dogs"; // Dog stuff is shown.
- * deck.setAttribute("selected-view", "cats");
- * deck.selectedViewName == "cats"; // Cat info is shown.
- */
-class NamedDeck extends HTMLElement {
-  static get observedAttributes() {
-    return ["selected-view"];
-  }
-
-  constructor() {
-    super();
-    this.attachShadow({mode: "open"});
-
-    // Create a slot for the visible content.
-    let selectedSlot = document.createElement("slot");
-    selectedSlot.setAttribute("name", "selected");
-    this.shadowRoot.appendChild(selectedSlot);
-
-    this.observer = new MutationObserver(() => {
-      this._setSelectedViewAttributes();
-    });
-  }
-
-  connectedCallback() {
-    if (this.selectedViewName) {
-      // Make sure the selected view is shown.
-      this._setSelectedViewAttributes();
-    } else {
-      // If there's no selected view, default to the first.
-      let firstView = this.firstElementChild;
-      if (firstView) {
-        // This will trigger showing the first view.
-        this.selectedViewName = firstView.getAttribute("name");
-      }
-    }
-    this.observer.observe(this, {childList: true});
-  }
-
-  disconnectedCallback() {
-    this.observer.disconnect();
-  }
-
-  attributeChangedCallback(attr, oldVal, newVal) {
-    if (attr == "selected-view" && oldVal != newVal) {
-      // Update the slot attribute on the views.
-      this._setSelectedViewAttributes();
-
-      // Notify that the selected view changed.
-      this.dispatchEvent(new CustomEvent("view-changed"));
-    }
-  }
-
-  get selectedViewName() {
-    return this.getAttribute("selected-view");
-  }
-
-  set selectedViewName(name) {
-    this.setAttribute("selected-view", name);
-  }
-
-  /**
-   * Set the slot attribute on all of the views to ensure only the selected view
-   * is shown.
-   */
-  _setSelectedViewAttributes() {
-    let {selectedViewName} = this;
-    for (let view of this.children) {
-      if (view.getAttribute("name") == selectedViewName) {
-        view.slot = "selected";
-      } else {
-        view.slot = "";
-      }
-    }
-  }
-}
-customElements.define("named-deck", NamedDeck);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/updateinfo.xsl
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+
+<xsl:stylesheet version="1.0" xmlns:xhtml="http://www.w3.org/1999/xhtml"
+                              xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+  <!-- Any elements not otherwise specified will be stripped but the contents
+       will be displayed. All attributes are stripped from copied elements. -->
+
+  <!-- Block these elements and their contents -->
+  <xsl:template match="xhtml:head|xhtml:script|xhtml:style">
+  </xsl:template>
+
+  <!-- Allowable styling elements -->
+  <xsl:template match="xhtml:b|xhtml:i|xhtml:em|xhtml:strong|xhtml:u|xhtml:q|xhtml:sub|xhtml:sup|xhtml:code">
+    <xsl:copy><xsl:apply-templates/></xsl:copy>
+  </xsl:template>
+
+  <!-- Allowable block formatting elements -->
+  <xsl:template match="xhtml:h1|xhtml:h2|xhtml:h3|xhtml:p|xhtml:div|xhtml:blockquote|xhtml:pre">
+    <xsl:copy><xsl:apply-templates/></xsl:copy>
+  </xsl:template>
+
+  <!-- Allowable list formatting elements -->
+  <xsl:template match="xhtml:ul|xhtml:ol|xhtml:li|xhtml:dl|xhtml:dt|xhtml:dd">
+    <xsl:copy><xsl:apply-templates/></xsl:copy>
+  </xsl:template>
+
+  <!-- These elements are copied and their contents dropped -->
+  <xsl:template match="xhtml:br|xhtml:hr">
+    <xsl:copy/>
+  </xsl:template>
+
+  <!-- The root document -->
+  <xsl:template match="/">
+    <xhtml:body><xsl:apply-templates/></xhtml:body>
+  </xsl:template>
+  
+</xsl:stylesheet>
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -1095,18 +1095,16 @@ class AddonInstall {
    *        Optional icons for the add-on
    * @param {string} [options.version]
    *        An optional version for the add-on
    * @param {Object?} [options.telemetryInfo]
    *        An optional object which provides details about the installation source
    *        included in the addon manager telemetry events.
    * @param {boolean} [options.isUserRequestedUpdate]
    *        An optional boolean, true if the install object is related to a user triggered update.
-   * @param {nsIURL} [options.releaseNotesURI]
-   *        An optional nsIURL that release notes where release notes can be retrieved.
    * @param {function(string) : Promise<void>} [options.promptHandler]
    *        A callback to prompt the user before installing.
    */
   constructor(installLocation, url, options = {}) {
     this.wrapper = new AddonInstallWrapper(this);
     this.location = installLocation;
     this.sourceURI = url;
 
@@ -1115,17 +1113,17 @@ class AddonInstall {
       this.originalHash = {
         algorithm: hashSplit[0],
         data: hashSplit[1],
       };
     }
     this.hash = this.originalHash;
     this.existingAddon = options.existingAddon || null;
     this.promptHandler = options.promptHandler || (() => Promise.resolve());
-    this.releaseNotesURI = options.releaseNotesURI || null;
+    this.releaseNotesURI = null;
 
     this._startupPromise = null;
 
     this._installPromise = new Promise(resolve => {
       this._resolveInstallPromise = resolve;
     });
     // Ignore uncaught rejections for this promise, since they're
     // handled by install listeners.
@@ -2283,30 +2281,29 @@ function createUpdate(aCallback, aAddon,
       existingAddon: aAddon,
       name: aAddon.selectedLocale.name,
       type: aAddon.type,
       icons: aAddon.icons,
       version: aUpdate.version,
       isUserRequestedUpdate: isUserRequested,
     };
 
-    try {
-      if (aUpdate.updateInfoURL)
-        opts.releaseNotesURI = Services.io.newURI(escapeAddonURI(aAddon, aUpdate.updateInfoURL));
-    } catch (e) {
-      // If the releaseNotesURI cannot be parsed then just ignore it.
-    }
-
     let install;
     if (url instanceof Ci.nsIFileURL) {
       install = new LocalAddonInstall(aAddon.location, url, opts);
       await install.init();
     } else {
       install = new DownloadAddonInstall(aAddon.location, url, opts);
     }
+    try {
+      if (aUpdate.updateInfoURL)
+        install.releaseNotesURI = Services.io.newURI(escapeAddonURI(aAddon, aUpdate.updateInfoURL));
+    } catch (e) {
+      // If the releaseNotesURI cannot be parsed then just ignore it.
+    }
 
     aCallback(install);
   })();
 }
 
 // Maps instances of AddonInstall to AddonInstallWrapper
 const wrapperMap = new WeakMap();
 let installFor = wrapper => wrapperMap.get(wrapper);
--- a/toolkit/mozapps/extensions/jar.mn
+++ b/toolkit/mozapps/extensions/jar.mn
@@ -7,29 +7,29 @@ toolkit.jar:
   content/mozapps/extensions/shortcuts.html                     (content/shortcuts.html)
   content/mozapps/extensions/shortcuts.css                      (content/shortcuts.css)
   content/mozapps/extensions/shortcuts.js                       (content/shortcuts.js)
 #ifndef MOZ_FENNEC
 * content/mozapps/extensions/extensions.xul                     (content/extensions.xul)
   content/mozapps/extensions/extensions.css                     (content/extensions.css)
   content/mozapps/extensions/extensions.js                      (content/extensions.js)
 * content/mozapps/extensions/extensions.xml                     (content/extensions.xml)
+  content/mozapps/extensions/updateinfo.xsl                     (content/updateinfo.xsl)
   content/mozapps/extensions/blocklist.xul                      (content/blocklist.xul)
   content/mozapps/extensions/blocklist.js                       (content/blocklist.js)
   content/mozapps/extensions/pluginPrefs.xul                    (content/pluginPrefs.xul)
   content/mozapps/extensions/pluginPrefs.js                     (content/pluginPrefs.js)
   content/mozapps/extensions/OpenH264-license.txt               (content/OpenH264-license.txt)
   content/mozapps/extensions/aboutaddons.html                   (content/aboutaddons.html)
   content/mozapps/extensions/aboutaddons.js                     (content/aboutaddons.js)
   content/mozapps/extensions/aboutaddonsCommon.js               (content/aboutaddonsCommon.js)
   content/mozapps/extensions/aboutaddons.css                    (content/aboutaddons.css)
   content/mozapps/extensions/abuse-reports.js                   (content/abuse-reports.js)
   content/mozapps/extensions/abuse-report-frame.html            (content/abuse-report-frame.html)
   content/mozapps/extensions/abuse-report-frame.js              (content/abuse-report-frame.js)
   content/mozapps/extensions/abuse-report-panel.css             (content/abuse-report-panel.css)
   content/mozapps/extensions/abuse-report-panel.js              (content/abuse-report-panel.js)
   content/mozapps/extensions/message-bar.css                    (content/message-bar.css)
   content/mozapps/extensions/message-bar.js                     (content/message-bar.js)
-  content/mozapps/extensions/named-deck.js                      (content/named-deck.js)
   content/mozapps/extensions/panel-list.css                     (content/panel-list.css)
   content/mozapps/extensions/panel-item.css                     (content/panel-item.css)
   content/mozapps/extensions/rating-star.css                    (content/rating-star.css)
 #endif
--- a/toolkit/mozapps/extensions/test/browser/browser.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser.ini
@@ -80,17 +80,16 @@ skip-if = true # Bug 1449071 - Frequent 
 skip-if = os == 'linux' && !debug # Bug 1398766
 [browser_html_abuse_report.js]
 [browser_html_detail_view.js]
 [browser_html_discover_view.js]
 [browser_html_discover_view_clientid.js]
 [browser_html_discover_view_prefs.js]
 [browser_html_list_view.js]
 [browser_html_message_bar.js]
-[browser_html_named_deck.js]
 [browser_html_options_ui_in_tab.js]
 [browser_html_plugins.js]
 skip-if = (os == 'win' && processor == 'aarch64') # aarch64 has no plugin support, bug 1525174 and 1547495
 [browser_html_recent_updates.js]
 [browser_html_updates.js]
 [browser_inlinesettings_browser.js]
 skip-if = os == 'mac' || os == 'linux' # Bug 1483347
 [browser_installssl.js]
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/browser/browser_html_named_deck.js
+++ /dev/null
@@ -1,187 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-/* eslint max-len: ["error", 80] */
-
-"use strict";
-
-add_task(async function enableHtmlViews() {
-  await SpecialPowers.pushPrefEnv({
-    set: [["extensions.htmlaboutaddons.enabled", true]],
-  });
-});
-
-const DEFAULT_SECTION_NAMES = ["one", "two", "three"];
-
-function makeButton({doc, name, deckId}) {
-  let button = doc.createElement("named-deck-button");
-  button.setAttribute("name", name);
-  button.deckId = deckId;
-  button.textContent = name.toUpperCase();
-  return button;
-}
-
-function makeSection({doc, name}) {
-  let view = doc.createElement("section");
-  view.setAttribute("name", name);
-  view.textContent = name + name;
-  return view;
-}
-
-function addSection({name, deck, buttons}) {
-  let doc = deck.ownerDocument;
-  let button = makeButton({doc, name, deckId: deck.id});
-  buttons.appendChild(button);
-  let view = makeSection({doc, name});
-  deck.appendChild(view);
-  return {button, view};
-}
-
-async function runTests({deck, buttons}) {
-  const selectedSlot = deck.shadowRoot.querySelector('slot[name="selected"]');
-  const getButtonByName = name => buttons.querySelector(`[name="${name}"]`);
-
-  function checkState(name, count, empty = false) {
-    // Check that the right view is selected.
-    is(deck.selectedViewName, name, "The right view is selected");
-
-    // Verify there's one element in the slot.
-    let slottedEls = selectedSlot.assignedElements();
-    if (empty) {
-      is(slottedEls.length, 0, "The deck is empty");
-    } else {
-      is(slottedEls.length, 1, "There's one visible view");
-      is(slottedEls[0].getAttribute("name"), name,
-        "The correct view is in the slot");
-    }
-
-    // Check that the hidden properties are set.
-    let sections = deck.querySelectorAll("section");
-    is(sections.length, count, "There are the right number of sections");
-    for (let section of sections) {
-      let sectionName = section.getAttribute("name");
-      if (sectionName == name) {
-        is(section.slot, "selected", `${sectionName} is visible`);
-      } else {
-        is(section.slot, "", `${sectionName} is hidden`);
-      }
-    }
-
-    // Check the right button is selected.
-    is(buttons.children.length, count, "There are the right number of buttons");
-    for (let button of buttons.children) {
-      let buttonName = button.getAttribute("name");
-      let selected = buttonName == name;
-      is(button.hasAttribute("selected"), selected,
-         `${buttonName} is ${selected ? "selected" : "not selected"}`);
-    }
-  }
-
-  // Check that the first view is selected by default.
-  checkState("one", 3);
-
-  // Switch to the third view.
-  info("Switch to section three");
-  getButtonByName("three").click();
-  checkState("three", 3);
-
-  // Add a new section, nothing changes.
-  info("Add section last");
-  let last = addSection({name: "last", deck, buttons});
-  checkState("three", 4);
-
-  // We can switch to the new section.
-  last.button.click();
-  info("Switch to section last");
-  checkState("last", 4);
-
-  info("Switch view with selectedViewName");
-  let shown = BrowserTestUtils.waitForEvent(deck, "view-changed");
-  deck.selectedViewName = "two";
-  await shown;
-  checkState("two", 4);
-
-  info("Switch back to the last view to test removing selected view");
-  shown = BrowserTestUtils.waitForEvent(deck, "view-changed");
-  deck.setAttribute("selected-view", "last");
-  await shown;
-  checkState("last", 4);
-
-  // Removing the selected section leaves the selected slot empty.
-  info("Remove section last");
-  last.button.remove();
-  last.view.remove();
-
-  info("Should not have any selected views");
-  checkState("last", 3, true);
-
-  // Setting a missing view will give a "view-changed" event.
-  info("Set view to a missing name");
-  let hidden = BrowserTestUtils.waitForEvent(deck, "view-changed");
-  deck.selectedViewName = "missing";
-  await hidden;
-  checkState("missing", 3, true);
-
-  // Adding the view won't trigger "view-changed", but the view will slotted.
-  info("Add the missing view, it should be shown");
-  shown = BrowserTestUtils.waitForEvent(selectedSlot, "slotchange");
-  let viewChangedEvent = false;
-  let viewChangedFn = () => { viewChangedEvent = true; };
-  deck.addEventListener("view-changed", viewChangedFn);
-  addSection({name: "missing", deck, buttons});
-  await shown;
-  deck.removeEventListener("view-changed", viewChangedFn);
-  ok(!viewChangedEvent, "The view-changed event didn't fire");
-  checkState("missing", 4);
-}
-
-async function setup({doc, beAsync, first}) {
-  const deckId = `${first}-first-${beAsync}`;
-
-  // Make the deck and buttons.
-  const deck = doc.createElement("named-deck");
-  deck.id = deckId;
-  for (let name of DEFAULT_SECTION_NAMES) {
-    deck.appendChild(makeSection({doc, name}));
-  }
-  const buttons = doc.createElement("div");
-  for (let name of DEFAULT_SECTION_NAMES) {
-    buttons.appendChild(makeButton({doc, name, deckId}));
-  }
-
-  let ordered;
-  if (first == "deck") {
-    ordered = [deck, buttons];
-  } else if (first == "buttons") {
-    ordered = [buttons, deck];
-  } else {
-    throw new Error("Invalid order");
-  }
-
-  // Insert them in the specified order, possibly async.
-  doc.body.appendChild(ordered.shift());
-  if (beAsync) {
-    await new Promise(resolve => requestAnimationFrame(resolve));
-  }
-  doc.body.appendChild(ordered.shift());
-
-  return {deck, buttons};
-}
-
-add_task(async function testNamedDeckAndButtons() {
-  const win = await loadInitialView("extension");
-  const doc = win.document;
-
-  // Check adding the deck first.
-  dump("Running deck first tests synchronously");
-  await runTests(await setup({doc, beAsync: false, first: "deck"}));
-  dump("Running deck first tests asynchronously");
-  await runTests(await setup({doc, beAsync: true, first: "deck"}));
-
-  // Check adding the buttons first.
-  dump("Running buttons first tests synchronously");
-  await runTests(await setup({doc, beAsync: false, first: "buttons"}));
-  dump("Running buttons first tests asynchronously");
-  await runTests(await setup({doc, beAsync: true, first: "buttons"}));
-
-  await closeView(win);
-});
--- a/toolkit/mozapps/extensions/test/browser/browser_html_recent_updates.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_html_recent_updates.js
@@ -28,17 +28,16 @@ add_task(async function enableHtmlViews(
     type: "extension",
     updateDate: dateHoursAgo(9),
   }, {
     id: "addon-today-1@mochi.test",
     name: "Updated today",
     creator: {name: "The creator"},
     version: "3.1",
     type: "extension",
-    releaseNotesURI: "http://example.com/notes.txt",
     updateDate: dateHoursAgo(1),
   }, {
     id: "addon-yesterday-1@mochi.test",
     name: "Updated yesterday one",
     creator: {name: "The creator"},
     version: "3.3",
     type: "extension",
     updateDate: dateHoursAgo(15),
@@ -86,38 +85,16 @@ add_task(async function testRecentUpdate
 
   // Verify that the add-ons are in the right order.
   Assert.deepEqual(addonsInOrder(), [
     "addon-today-1@mochi.test", "addon-today-2@mochi.test",
     "addon-today-3@mochi.test", "addon-yesterday-1@mochi.test",
     "addon-yesterday-2@mochi.test",
   ], "The add-ons are in the right order");
 
-  info("Check that release notes are shown on the details page");
-  let card = list.querySelector(
-    'addon-card[addon-id="addon-today-1@mochi.test"]');
-  loaded = waitForViewLoad(win);
-  card.querySelector('[action="expand"]').click();
-  await loaded;
-
-  card = doc.querySelector("addon-card");
-  ok(card.expanded, "The card is expanded");
-  ok(!card.details.tabGroup.hidden, "The tabs are shown");
-  ok(!card.details.tabGroup.querySelector('[name="release-notes"]').hidden,
-     "The release notes button is shown");
-
-  info("Go back to the recent updates view");
-  loaded = waitForViewLoad(win);
-  managerDoc.getElementById("utils-viewUpdates").doCommand();
-  await loaded;
-
-  // Find the list again.
-  list = doc.querySelector("addon-list");
-
-  info("Install a new add-on, it should be first in the list");
   // Install a new extension.
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       name: "New extension",
       applications: {gecko: {id: "new@mochi.test"}},
     },
     useAddonManager: "temporary",
   });
@@ -128,17 +105,17 @@ add_task(async function testRecentUpdate
   // The new extension should now be at the top of the list.
   Assert.deepEqual(addonsInOrder(), [
     "new@mochi.test", "addon-today-1@mochi.test", "addon-today-2@mochi.test",
     "addon-today-3@mochi.test", "addon-yesterday-1@mochi.test",
     "addon-yesterday-2@mochi.test",
   ], "The new add-on went to the top");
 
   // Open the detail view for the new add-on.
-  card = list.querySelector('addon-card[addon-id="new@mochi.test"]');
+  let card = list.querySelector('addon-card[addon-id="new@mochi.test"]');
   loaded = waitForViewLoad(win);
   card.querySelector('[action="expand"]').click();
   await loaded;
 
   is(win.managerWindow.gCategories.selected, "addons://list/extension",
      "The extensions category is selected");
 
   await closeView(win);
--- a/toolkit/mozapps/extensions/test/browser/browser_html_updates.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_html_updates.js
@@ -102,17 +102,17 @@ add_task(async function testChangeAutoUp
   updated = BrowserTestUtils.waitForEvent(card, "update");
   AddonManager.autoUpdateDefault = true;
   await updated;
 
   await closeView(win);
   await extension.unload();
 });
 
-async function setupExtensionWithUpdate(id, {releaseNotes} = {}) {
+async function setupExtensionWithUpdate(id) {
   await SpecialPowers.pushPrefEnv({
     set: [["extensions.checkUpdateSecurity", false]],
   });
 
   let server = AddonTestUtils.createHttpServer();
   let serverHost = `http://localhost:${server.identity.primaryPort}`;
   let updatesPath = `/ext-updates-${id}.json`;
 
@@ -127,43 +127,24 @@ async function setupExtensionWithUpdate(
   };
 
   let updateXpi = AddonTestUtils.createTempWebExtensionFile({
     manifest: {
       ...baseManifest,
       version: "2",
     },
   });
-
-  let releaseNotesExtra = {};
-  if (releaseNotes) {
-    let notesPath = "/notes.txt";
-    server.registerPathHandler(notesPath, (request, response) => {
-      if (releaseNotes == "ERROR") {
-        response.setStatusLine(null, 404, "Not Found");
-      } else {
-        response.setStatusLine(null, 200, "OK");
-        response.write(releaseNotes);
-      }
-      response.processAsync();
-      response.finish();
-    });
-    releaseNotesExtra.update_info_url = serverHost + notesPath;
-  }
-
   let xpiFilename = `/update-${id}.xpi`;
   server.registerFile(xpiFilename, updateXpi);
   AddonTestUtils.registerJSON(server, updatesPath, {
     addons: {
       [id]: {
-        updates: [{
-          version: "2",
-          update_link: serverHost + xpiFilename,
-          ...releaseNotesExtra,
-        }],
+        updates: [
+          {version: "2", update_link: serverHost + xpiFilename},
+        ],
       },
     },
   });
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       ...baseManifest,
       version: "1",
@@ -198,36 +179,27 @@ function checkForUpdate(card, expected) 
 function installUpdate(card, expected) {
   // Install the update.
   let updateInstalled = BrowserTestUtils.waitForEvent(card, expected);
   let updated = BrowserTestUtils.waitForEvent(card, "update");
   card.querySelector('panel-item[action="install-update"]').click();
   return Promise.all([updateInstalled, updated]);
 }
 
-function assertUpdateState({
-  card, shown, expanded = true, releaseNotes = false,
-}) {
+function assertUpdateState({card, shown, expanded = true}) {
   let menuButton = card.querySelector(".more-options-button");
   ok(menuButton.classList.contains("more-options-button-badged") == shown,
      "The menu button is badged");
   let installButton = card.querySelector('panel-item[action="install-update"]');
   ok(installButton.hidden != shown,
      `The install button is ${shown ? "hidden" : "shown"}`);
   if (expanded) {
     let updateCheckButton = card.querySelector('button[action="update-check"]');
     ok(updateCheckButton.hidden == shown,
       `The update check button is ${shown ? "hidden" : "shown"}`);
-
-    let {tabGroup} = card.details;
-    is(tabGroup.hidden, !releaseNotes,
-       `The tab group is ${releaseNotes ? "shown" : "hidden"}`);
-    let notesBtn = tabGroup.querySelector('[name="release-notes"]');
-    is(notesBtn.hidden, !releaseNotes,
-       `The release notes button is ${releaseNotes ? "shown" : "hidden"}`);
   }
 }
 
 add_task(async function testUpdateAvailable() {
   let id = "update@mochi.test";
   let extension = await setupExtensionWithUpdate(id);
 
   let win = await loadInitialView("extension");
@@ -259,163 +231,16 @@ add_task(async function testUpdateAvaila
 
   // Check for updates again, there shouldn't be an update.
   await checkForUpdate(card, "no-update");
 
   await closeView(win);
   await extension.unload();
 });
 
-add_task(async function testReleaseNotesLoad() {
-  let id = "update-with-notes@mochi.test";
-  let extension = await setupExtensionWithUpdate(id, {
-    releaseNotes: `
-      <html xmlns="http://www.w3.org/1999/xhtml">
-        <head><link rel="stylesheet" href="remove-me.css"/></head>
-        <body>
-          <script src="no-scripts.js"></script>
-          <h1>My release notes</h1>
-          <img src="http://example.com/tracker.png"/>
-          <ul>
-            <li onclick="alert('hi')">A thing</li>
-          </ul>
-          <a href="http://example.com/">Go somewhere</a>
-        </body>
-      </html>
-    `,
-  });
-
-  let win = await loadInitialView("extension");
-  let doc = win.document;
-
-  await loadDetailView(win, id);
-
-  let card = doc.querySelector("addon-card");
-  let {deck, tabGroup} = card.details;
-
-  // Disable updates and then check.
-  disableAutoUpdates(card);
-  await checkForUpdate(card, "update-found");
-
-  // There should now be an update.
-  assertUpdateState({card, shown: true, releaseNotes: true});
-
-  info("Check release notes");
-  let notesBtn = tabGroup.querySelector('[name="release-notes"]');
-  let notes = card.querySelector("update-release-notes");
-  let loading = BrowserTestUtils.waitForEvent(notes, "release-notes-loading");
-  let loaded = BrowserTestUtils.waitForEvent(notes, "release-notes-loaded");
-  notesBtn.click();
-  await loading;
-  is(doc.l10n.getAttributes(notes.firstElementChild).id,
-     "release-notes-loading", "The loading message is shown");
-  await loaded;
-  info("Checking HTML release notes");
-  let [h1, ul, a] = notes.children;
-  is(h1.tagName, "H1", "There's a heading");
-  is(h1.textContent, "My release notes", "The heading has content");
-  is(ul.tagName, "UL", "There's a list");
-  is(ul.children.length, 1, "There's one item in the list");
-  let [li] = ul.children;
-  is(li.tagName, "LI", "There's a list item");
-  is(li.textContent, "A thing", "The text is set");
-  ok(!li.hasAttribute("onclick"), "The onclick was removed");
-  ok(!notes.querySelector("link"), "The link tag was removed");
-  ok(!notes.querySelector("script"), "The script tag was removed");
-  is(a.textContent, "Go somewhere", "The link text is preserved");
-  is(a.href, "http://example.com/", "The link href is preserved");
-
-  info("Verify the link opened in a new tab");
-  let tabOpened = BrowserTestUtils.waitForNewTab(gBrowser, a.href);
-  a.click();
-  let tab = await tabOpened;
-  BrowserTestUtils.removeTab(tab);
-
-  let originalContent = notes.innerHTML;
-
-  info("Switch away and back to release notes");
-  // Load details view.
-  let detailsBtn = tabGroup.firstElementChild;
-  let viewChanged = BrowserTestUtils.waitForEvent(deck, "view-changed");
-  detailsBtn.click();
-  await viewChanged;
-
-  // Load release notes again, verify they weren't loaded.
-  viewChanged = BrowserTestUtils.waitForEvent(deck, "view-changed");
-  let notesCached = BrowserTestUtils.waitForEvent(
-    notes, "release-notes-cached");
-  notesBtn.click();
-  await viewChanged;
-  await notesCached;
-  is(notes.innerHTML, originalContent, "The content didn't change");
-
-  info("Install the update to clean it up");
-  await installUpdate(card, "update-installed");
-
-  // There's no more update but release notes are still shown.
-  assertUpdateState({card, shown: false, releaseNotes: true});
-
-  await closeView(win);
-  await extension.unload();
-});
-
-add_task(async function testReleaseNotesError() {
-  let id = "update-with-notes-error@mochi.test";
-  let extension = await setupExtensionWithUpdate(id, {releaseNotes: "ERROR"});
-
-  let win = await loadInitialView("extension");
-  let doc = win.document;
-
-  await loadDetailView(win, id);
-
-  let card = doc.querySelector("addon-card");
-  let {deck, tabGroup} = card.details;
-
-  // Disable updates and then check.
-  disableAutoUpdates(card);
-  await checkForUpdate(card, "update-found");
-
-  // There should now be an update.
-  assertUpdateState({card, shown: true, releaseNotes: true});
-
-  info("Check release notes");
-  let notesBtn = tabGroup.querySelector('[name="release-notes"]');
-  let notes = card.querySelector("update-release-notes");
-  let loading = BrowserTestUtils.waitForEvent(notes, "release-notes-loading");
-  let errored = BrowserTestUtils.waitForEvent(notes, "release-notes-error");
-  notesBtn.click();
-  await loading;
-  is(doc.l10n.getAttributes(notes.firstElementChild).id,
-     "release-notes-loading", "The loading message is shown");
-  await errored;
-  is(doc.l10n.getAttributes(notes.firstElementChild).id,
-     "release-notes-error", "The error message is shown");
-
-  info("Switch away and back to release notes");
-  // Load details view.
-  let detailsBtn = tabGroup.firstElementChild;
-  let viewChanged = BrowserTestUtils.waitForEvent(deck, "view-changed");
-  detailsBtn.click();
-  await viewChanged;
-
-  // Load release notes again, verify they weren't loaded.
-  viewChanged = BrowserTestUtils.waitForEvent(deck, "view-changed");
-  let notesCached = BrowserTestUtils.waitForEvent(
-    notes, "release-notes-cached");
-  notesBtn.click();
-  await viewChanged;
-  await notesCached;
-
-  info("Install the update to clean it up");
-  await installUpdate(card, "update-installed");
-
-  await closeView(win);
-  await extension.unload();
-});
-
 add_task(async function testUpdateCancelled() {
   let id = "update@mochi.test";
   let extension = await setupExtensionWithUpdate(id);
 
   let win = await loadInitialView("extension");
   let doc = win.document;
 
   await loadDetailView(win, "update@mochi.test");