merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 11 Jul 2016 16:21:15 +0200
changeset 344518 214884d507ee369c1cf14edb26527c4f9a97bf48
parent 344479 f9cae1b84fe26ada12aaa1ef08173948a6b54e83 (current diff)
parent 344517 e78cec726586d5f19f7219ed3c2e864da9ae194d (diff)
child 344520 1a53b603ba56cd26ed5c7f10407dd8335143d9df
child 344540 2de28b975423d18acbc965d6f29ae396c2f1bef3
child 344545 42dcbbe5f18d3f50ec1af613f12bc9cd11dfaf0b
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
214884d507ee / 50.0a1 / 20160711143747 / files
nightly linux64
214884d507ee / 50.0a1 / 20160711143754 / files
nightly mac
214884d507ee / 50.0a1 / 20160711143735 / files
nightly win32
214884d507ee / 50.0a1 / 20160711143737 / files
nightly win64
214884d507ee / 50.0a1 / 20160711143757 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/components/safebrowsing/content/test/browser_forbidden.js
dom/grid/test/chrome/test_grid_repeats.html
dom/grid/test/chrome/test_grid_state.html
toolkit/components/telemetry/Histograms.json
toolkit/content/nsDragAndDrop.js
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -40,17 +40,18 @@ enum Interfaces
 class ProxyAccessible
 {
 public:
 
   ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
                   DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces) :
      mParent(aParent), mDoc(aDoc), mWrapper(0), mID(aID), mRole(aRole),
      mOuterDoc(false), mIsDoc(false),
-     mHasValue(aInterfaces & Interfaces::VALUE)
+     mHasValue(aInterfaces & Interfaces::VALUE),
+     mIsHyperLink(aInterfaces & Interfaces::HYPERLINK)
   {
     MOZ_COUNT_CTOR(ProxyAccessible);
   }
   ~ProxyAccessible()
   {
     MOZ_COUNT_DTOR(ProxyAccessible);
     MOZ_ASSERT(!mWrapper);
   }
@@ -415,35 +416,37 @@ public:
    * Return true if this proxy is a DocAccessibleParent.
    */
   bool IsDoc() const { return mIsDoc; }
   DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; }
 
 protected:
   explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc) :
     mParent(nullptr), mDoc(aThisAsDoc), mWrapper(0), mID(0),
-    mRole(roles::DOCUMENT), mOuterDoc(false), mIsDoc(true), mHasValue(false)
+    mRole(roles::DOCUMENT), mOuterDoc(false), mIsDoc(true), mHasValue(false),
+    mIsHyperLink(false)
   { MOZ_COUNT_CTOR(ProxyAccessible); }
 
 protected:
   ProxyAccessible* mParent;
 
 private:
   nsTArray<ProxyAccessible*> mChildren;
   DocAccessibleParent* mDoc;
   uintptr_t mWrapper;
   uint64_t mID;
 protected:
   // XXX DocAccessibleParent gets to change this to change the role of
   // documents.
-  role mRole : 29;
+  role mRole : 28;
 private:
   bool mOuterDoc : 1;
 
 public:
   const bool mIsDoc: 1;
   const bool mHasValue: 1;
+  const bool mIsHyperLink: 1;
 };
 
 }
 }
 
 #endif
--- a/accessible/xpcom/xpcAccessibleDocument.cpp
+++ b/accessible/xpcom/xpcAccessibleDocument.cpp
@@ -212,16 +212,20 @@ xpcAccessibleDocument::GetXPCAccessible(
     return acc;
   }
 
   // XXX support exposing optional interfaces.
   uint8_t interfaces = 0;
   if (aProxy->mHasValue) {
     interfaces |= eValue;
   }
+  
+  if (aProxy->mIsHyperLink) {
+    interfaces |= eHyperLink;
+  }
 
   acc = new xpcAccessibleGeneric(aProxy, interfaces);
   mCache.Put(aProxy, acc);
 
   return acc;
 }
 
 void
--- a/b2g/locales/en-US/chrome/overrides/appstrings.properties
+++ b/b2g/locales/en-US/chrome/overrides/appstrings.properties
@@ -27,14 +27,13 @@ externalProtocolTitle=External Protocol 
 externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
 #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
 malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
 unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
 deceptiveBlocked=This web page at %S has been reported as a deceptive site and has been blocked based on your security preferences.
-forbiddenBlocked=The site at %S has been blocked by your browser configuration.
 cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
 corruptedContentErrorv2=The site at %S has experienced a network protocol violation that cannot be repaired.
 remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox.
 sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol.
 weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website.
--- a/browser/base/content/blockedSite.xhtml
+++ b/browser/base/content/blockedSite.xhtml
@@ -89,19 +89,16 @@
             error = "malware";
             break;
           case "deceptiveBlocked" :
             error = "phishing";
             break;
           case "unwantedBlocked" :
             error = "unwanted";
             break;
-          case "forbiddenBlocked" :
-            error = "forbidden";
-            break;
           default:
             return;
         }
 
         var el;
 
         if (error !== "malware") {
           el = document.getElementById("errorTitleText_malware");
@@ -125,34 +122,16 @@
           el = document.getElementById("errorTitleText_unwanted");
           el.parentNode.removeChild(el);
           el = document.getElementById("errorShortDescText_unwanted");
           el.parentNode.removeChild(el);
           el = document.getElementById("errorLongDescText_unwanted");
           el.parentNode.removeChild(el);
         }
 
-        if (error !== "forbidden") {
-          el = document.getElementById("errorTitleText_forbidden");
-          el.parentNode.removeChild(el);
-          el = document.getElementById("errorShortDescText_forbidden");
-          el.parentNode.removeChild(el);
-          el = document.getElementById("whyForbiddenButton");
-          el.parentNode.removeChild(el);
-        } else {
-          el = document.getElementById("ignoreWarningButton");
-          el.parentNode.removeChild(el);
-          el = document.getElementById("reportButton");
-          el.parentNode.removeChild(el);
-
-          // Remove red style: A "forbidden site" does not warrant the same level
-          // of anxiety as a security concern.
-          document.documentElement.className = "";
-        }
-
         // Set sitename
         document.getElementById(error + "_sitename").textContent = getHostString();
         document.title = document.getElementById("errorTitleText_" + error)
                                  .innerHTML;
 
         if (!getOverride()) {
           var btn = document.getElementById("ignoreWarningButton");
           if (btn) {
@@ -170,43 +149,40 @@
   <body dir="&locale.dir;">
     <div id="errorPageContainer" class="container">
 
       <!-- Error Title -->
       <div id="errorTitle" class="title">
         <h1 class="title-text" id="errorTitleText_phishing">&safeb.blocked.phishingPage.title2;</h1>
         <h1 class="title-text" id="errorTitleText_malware">&safeb.blocked.malwarePage.title;</h1>
         <h1 class="title-text" id="errorTitleText_unwanted">&safeb.blocked.unwantedPage.title;</h1>
-        <h1 class="title-text" id="errorTitleText_forbidden">&safeb.blocked.forbiddenPage.title2;</h1>
       </div>
 
       <div id="errorLongContent">
 
         <!-- Short Description -->
         <div id="errorShortDesc">
           <p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc2;</p>
           <p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p>
           <p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc;</p>
-          <p id="errorShortDescText_forbidden">&safeb.blocked.forbiddenPage.shortDesc2;</p>
         </div>
 
         <!-- Long Description -->
         <div id="errorLongDesc">
           <p id="errorLongDescText_phishing">&safeb.blocked.phishingPage.longDesc2;</p>
           <p id="errorLongDescText_malware">&safeb.blocked.malwarePage.longDesc;</p>
           <p id="errorLongDescText_unwanted">&safeb.blocked.unwantedPage.longDesc;</p>
         </div>
 
         <!-- Action buttons -->
         <div id="buttons" class="button-container">
           <!-- Commands handled in browser.js -->
           <button id="getMeOutButton" class="primary">&safeb.palm.accept.label;</button>
           <div class="button-spacer"></div>
           <button id="reportButton">&safeb.palm.reportPage.label;</button>
-          <button id="whyForbiddenButton">&safeb.palm.whyForbidden.label;</button>
         </div>
       </div>
       <div id="ignoreWarning">
         <button id="ignoreWarningButton">&safeb.palm.decline.label;</button>
       </div>
     </div>
     <!--
     - Note: It is important to run the script this way, instead of using
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2950,22 +2950,16 @@ var BrowserOnClick = {
       case "ignoreWarningButton":
         if (gPrefService.getBoolPref("browser.safebrowsing.allowOverride")) {
           if (sendTelemetry) {
             secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
           }
           this.ignoreWarningButton(reason);
         }
         break;
-
-      case "whyForbiddenButton":
-        // This is the "Why is this site blocked" button for family friendly browsing
-        // for Fennec. There's no desktop focused support page yet.
-        gBrowser.loadURI("https://support.mozilla.org/kb/controlledaccess");
-        break;
     }
   },
 
   /**
    * This functions prevents navigation from happening directly through the <a>
    * link in about:newtab (which is loaded in the parent and therefore would load
    * the next page also in the parent) and instructs the browser to open the url
    * in the current tab which will make it update the remoteness of the tab.
@@ -3022,18 +3016,16 @@ var BrowserOnClick = {
         callback: function() {
           openUILinkIn(gSafeBrowsing.getReportURL('PhishMistake'), 'tab');
         }
       };
     } else if (reason === 'unwanted') {
       title = gNavigatorBundle.getString("safebrowsing.reportedUnwantedSite");
       // There is no button for reporting errors since Google doesn't currently
       // provide a URL endpoint for these reports.
-    } else {
-      return; // no notifications for forbidden sites
     }
 
     let notificationBox = gBrowser.getNotificationBox();
     let value = "blocked-badware-page";
 
     let previousNotification = notificationBox.getNotificationWithValue(value);
     if (previousNotification) {
       notificationBox.removeNotification(previousNotification);
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -559,18 +559,16 @@ var ClickEventHandler = {
   },
 
   onAboutBlocked: function (targetElement, ownerDoc) {
     var reason = 'phishing';
     if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
       reason = 'malware';
     } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
       reason = 'unwanted';
-    } else if (/e=forbiddenBlocked/.test(ownerDoc.documentURI)) {
-      reason = 'forbidden';
     }
     sendAsyncMessage("Browser:SiteBlockedError", {
       location: ownerDoc.location.href,
       reason: reason,
       elementId: targetElement.getAttribute("id"),
       isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView)
     });
   },
--- a/browser/base/content/test/general/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/general/browser_misused_characters_in_strings.js
@@ -40,20 +40,16 @@ let gWhitelist = [{
     file: "phishing-afterload-warning-message.dtd",
     key: "safeb.blocked.unwantedPage.shortDesc",
     type: "single-quote"
   }, {
     file: "phishing-afterload-warning-message.dtd",
     key: "safeb.blocked.phishingPage.shortDesc2",
     type: "single-quote"
   }, {
-    file: "phishing-afterload-warning-message.dtd",
-    key: "safeb.blocked.forbiddenPage.shortDesc2",
-    type: "single-quote"
-  }, {
     file: "mathfont.properties",
     key: "operator.\\u002E\\u002E\\u002E.postfix",
     type: "ellipsis"
   }, {
     file: "layout_errors.properties",
     key: "ImageMapRectBoundsError",
     type: "double-quote"
   }, {
--- a/browser/components/safebrowsing/content/test/browser.ini
+++ b/browser/components/safebrowsing/content/test/browser.ini
@@ -1,9 +1,8 @@
 [DEFAULT]
 support-files = head.js
 
-[browser_forbidden.js]
 [browser_bug400731.js]
 [browser_bug415846.js]
 # Disabled on Mac because of its bizarre special-and-unique snowflake of a help menu.
 skip-if = os == "mac" || e10s # e10s: Bug 1248632
 [browser_whitelisted.js]
deleted file mode 100644
--- a/browser/components/safebrowsing/content/test/browser_forbidden.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Ensure that pages in the forbidden list are blocked. */
-
-const PREF_FORBIDDEN_ENABLED = "browser.safebrowsing.forbiddenURIs.enabled";
-const BENIGN_PAGE = "http://example.com/";
-const FORBIDDEN_PAGE = "http://www.itisatrap.org/firefox/forbidden.html";
-var tabbrowser = null;
-
-registerCleanupFunction(function() {
-  tabbrowser = null;
-  Services.prefs.clearUserPref(PREF_FORBIDDEN_ENABLED);
-  while (gBrowser.tabs.length > 1) {
-    gBrowser.removeCurrentTab();
-  }
-});
-
-function testBenignPage(window) {
-  info("Non-forbidden content must not be blocked");
-  var getmeout_button = window.document.getElementById("getMeOutButton");
-  var ignorewarning_button = window.document.getElementById("ignoreWarningButton");
-  ok(!getmeout_button, "GetMeOut button not present");
-  ok(!ignorewarning_button, "IgnoreWarning button not present");
-}
-
-function testForbiddenPage(window) {
-  info("Forbidden content must be blocked");
-  ok(true, "about:blocked was shown");
-}
-
-add_task(function* testNormalBrowsing() {
-  tabbrowser = gBrowser;
-  let tab = tabbrowser.selectedTab = tabbrowser.addTab();
-
-  info("Load a test page that's not forbidden");
-  yield promiseTabLoadEvent(tab, BENIGN_PAGE, "load");
-  testBenignPage(tab.ownerDocument.defaultView);
-
-  info("Load a test page that is forbidden");
-  yield promiseTabLoadEvent(tab, FORBIDDEN_PAGE, "AboutBlockedLoaded");
-  testForbiddenPage(tab.ownerDocument.defaultView);
-});
--- a/browser/components/safebrowsing/content/test/head.js
+++ b/browser/components/safebrowsing/content/test/head.js
@@ -59,14 +59,12 @@ function promiseTabLoadEvent(tab, url, e
     BrowserTestUtils.loadURI(tab.linkedBrowser, url);
 
   // Promise.all rejects if either promise rejects (i.e. if we time out) and
   // if our loaded promise resolves before the timeout, then we resolve the
   // timeout promise as well, causing the all promise to resolve.
   return Promise.all([deferred.promise, loaded]);
 }
 
-Services.prefs.setCharPref("urlclassifier.forbiddenTable", "test-forbid-simple");
 Services.prefs.setCharPref("urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple");
 Services.prefs.setCharPref("urlclassifier.phishTable", "test-phish-simple");
 Services.prefs.setCharPref("urlclassifier.blockedTable", "test-block-simple");
-Services.prefs.setBoolPref("browser.safebrowsing.forbiddenURIs.enabled", true);
 SafeBrowsing.init();
--- a/browser/locales/en-US/chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd
+++ b/browser/locales/en-US/chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd
@@ -9,29 +9,24 @@
   shown. -->
 <!ENTITY safeb.palm.notdeceptive.label "This isn’t a deceptive site…">
 <!-- Localization note (safeb.palm.notdeceptive.accesskey) - Because
   safeb.palm.notdeceptive.label and reportDeceptiveSiteMenu.title from
   report-phishing.dtd are never shown at the same time, the same accesskey can
   be used for them. -->
 <!ENTITY safeb.palm.notdeceptive.accesskey "d">
 <!ENTITY safeb.palm.reportPage.label "Why was this page blocked?">
-<!ENTITY safeb.palm.whyForbidden.label "Why was this page blocked?">
 
 <!ENTITY safeb.blocked.malwarePage.title "Reported Attack Page!">
 <!-- Localization note (safeb.blocked.malwarePage.shortDesc) - Please don't translate the contents of the <span id="malware_sitename"/> tag.  It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
 <!ENTITY safeb.blocked.malwarePage.shortDesc "This web page at <span id='malware_sitename'/> has been reported as an attack page and has been blocked based on your security preferences.">
 <!ENTITY safeb.blocked.malwarePage.longDesc "<p>Attack pages try to install programs that steal private information, use your computer to attack others, or damage your system.</p><p>Some attack pages intentionally distribute harmful software, but many are compromised without the knowledge or permission of their owners.</p>">
 
 <!ENTITY safeb.blocked.unwantedPage.title "Reported Unwanted Software Page!">
 <!-- Localization note (safeb.blocked.unwantedPage.shortDesc) - Please don't translate the contents of the <span id="unwanted_sitename"/> tag.  It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
 <!ENTITY safeb.blocked.unwantedPage.shortDesc "This web page at <span id='unwanted_sitename'/> has been reported to contain unwanted software and has been blocked based on your security preferences.">
 <!ENTITY safeb.blocked.unwantedPage.longDesc "<p>Unwanted software pages try to install software that can be deceptive and affect your system in unexpected ways.</p>">
 
 <!ENTITY safeb.blocked.phishingPage.title2 "Deceptive Site!">
 <!-- Localization note (safeb.blocked.phishingPage.shortDesc2) - Please don't translate the contents of the <span id="phishing_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
 <!ENTITY safeb.blocked.phishingPage.shortDesc2 "This web page at <span id='phishing_sitename'/> has been reported as a deceptive site and has been blocked based on your security preferences.">
 <!ENTITY safeb.blocked.phishingPage.longDesc2 "<p>Deceptive sites are designed to trick you into doing something dangerous, like installing software, or revealing your personal information, like passwords, phone numbers or credit cards.</p><p>Entering any information on this web page may result in identity theft or other fraud.</p>">
 
-<!ENTITY safeb.blocked.forbiddenPage.title2 "Blocked Site">
-<!-- Localization note (safeb.blocked.forbiddenPage.shortDesc2) - Please don't translate the contents of the <span id="forbidden_sitename"/> tag.  It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
-<!ENTITY safeb.blocked.forbiddenPage.shortDesc2 "The Web page at <span id='forbidden_sitename'/> has been blocked by your admin profile.">
-
--- a/browser/locales/en-US/chrome/overrides/appstrings.properties
+++ b/browser/locales/en-US/chrome/overrides/appstrings.properties
@@ -28,17 +28,16 @@ externalProtocolTitle=External Protocol 
 externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
 #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
 malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
 unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
 deceptiveBlocked=This web page at %S has been reported as a deceptive site and has been blocked based on your security preferences.
-forbiddenBlocked=The site at %S has been blocked by your browser configuration.
 cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
 corruptedContentErrorv2=The site at %S has experienced a network protocol violation that cannot be repaired.
 remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox.
 ## LOCALIZATION NOTE (sslv3Used) - Do not translate "%S".
 sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol.
 ## LOCALIZATION NOTE (weakCryptoUsed) - Do not translate "%S".
 weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website.
 inadequateSecurityError=The website tried to negotiate an inadequate level of security.
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4981,18 +4981,17 @@ nsDocShell::DisplayLoadError(nsresult aE
         }
 
       } else {
         error.AssignLiteral("nssFailure2");
       }
     }
   } else if (NS_ERROR_PHISHING_URI == aError ||
              NS_ERROR_MALWARE_URI == aError ||
-             NS_ERROR_UNWANTED_URI == aError ||
-             NS_ERROR_FORBIDDEN_URI == aError) {
+             NS_ERROR_UNWANTED_URI == aError) {
     nsAutoCString host;
     aURI->GetHost(host);
     CopyUTF8toUTF16(host, formatStrs[0]);
     formatStrCount = 1;
 
     // Malware and phishing detectors may want to use an alternate error
     // page, but if the pref's not set, we'll fall back on the standard page
     nsAdoptingCString alternateErrorPage =
@@ -5013,18 +5012,16 @@ nsDocShell::DisplayLoadError(nsresult aE
       error.AssignLiteral("malwareBlocked");
       bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME
                            : nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP;
     } else if (NS_ERROR_UNWANTED_URI == aError) {
       sendTelemetry = true;
       error.AssignLiteral("unwantedBlocked");
       bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_FRAME
                            : nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_TOP;
-    } else if (NS_ERROR_FORBIDDEN_URI == aError) {
-      error.AssignLiteral("forbiddenBlocked");
     }
 
     if (sendTelemetry && errorPage.EqualsIgnoreCase("blocked")) {
       Telemetry::Accumulate(Telemetry::SECURITY_UI, bucketId);
     }
 
     cssClass.AssignLiteral("blacklist");
   } else if (NS_ERROR_CONTENT_CRASHED == aError) {
@@ -7871,17 +7868,16 @@ nsDocShell::EndPageLoad(nsIWebProgress* 
                aStatus == NS_ERROR_REDIRECT_LOOP ||
                aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
                aStatus == NS_ERROR_NET_INTERRUPT ||
                aStatus == NS_ERROR_NET_RESET ||
                aStatus == NS_ERROR_OFFLINE ||
                aStatus == NS_ERROR_MALWARE_URI ||
                aStatus == NS_ERROR_PHISHING_URI ||
                aStatus == NS_ERROR_UNWANTED_URI ||
-               aStatus == NS_ERROR_FORBIDDEN_URI ||
                aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
                aStatus == NS_ERROR_REMOTE_XUL ||
                aStatus == NS_ERROR_INTERCEPTION_FAILED ||
                aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
                NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
       // Errors to be shown for any frame
       DisplayLoadError(aStatus, url, nullptr, aChannel);
     } else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3287,25 +3287,26 @@ void
 Element::MozRequestPointerLock()
 {
   OwnerDoc()->RequestPointerLock(this);
 }
 
 void
 Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult)
 {
-  nsIFrame* frame = GetPrimaryFrame();
-  if (frame && (frame->GetType() == nsGkAtoms::gridContainerFrame)) {
-    // If primary frame is a nsGridContainerFrame, all the next frames
-    // in flow will also be nsGridContainerFrame.
-    for (; frame != nullptr; frame = frame->GetNextInFlow()) {
-      aResult.AppendElement(
-        new Grid(this, static_cast<nsGridContainerFrame*>(frame))
-      );
-    }
+  nsGridContainerFrame* frame =
+    nsGridContainerFrame::GetGridFrameWithComputedInfo(GetPrimaryFrame());
+
+  // If we get a nsGridContainerFrame from the prior call,
+  // all the next-in-flow frames will also be nsGridContainerFrames.
+  while (frame) {
+    aResult.AppendElement(
+      new Grid(this, frame)
+    );
+    frame = static_cast<nsGridContainerFrame*>(frame->GetNextInFlow());
   }
 }
 
 already_AddRefed<Animation>
 Element::Animate(JSContext* aContext,
                  JS::Handle<JSObject*> aKeyframes,
                  const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
                  ErrorResult& aError)
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5578,95 +5578,150 @@ nsContentUtils::GetCurrentJSContextForTh
   MOZ_ASSERT(IsInitialized());
   if (MOZ_LIKELY(NS_IsMainThread())) {
     return GetCurrentJSContext();
   } else {
     return workers::GetCurrentThreadJSContext();
   }
 }
 
-/* static */
+template<typename StringType, typename CharType>
 void
-nsContentUtils::ASCIIToLower(nsAString& aStr)
-{
-  char16_t* iter = aStr.BeginWriting();
-  char16_t* end = aStr.EndWriting();
+_ASCIIToLowerInSitu(StringType& aStr)
+{
+  CharType* iter = aStr.BeginWriting();
+  CharType* end = aStr.EndWriting();
   MOZ_ASSERT(iter && end);
 
   while (iter != end) {
-    char16_t c = *iter;
+    CharType c = *iter;
     if (c >= 'A' && c <= 'Z') {
       *iter = c + ('a' - 'A');
     }
     ++iter;
   }
 }
 
 /* static */
 void
-nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest)
+nsContentUtils::ASCIIToLower(nsAString& aStr)
+{
+  return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToLower(nsACString& aStr)
+{
+  return _ASCIIToLowerInSitu<nsACString, char>(aStr);
+}
+
+template<typename StringType, typename CharType>
+void
+_ASCIIToLowerCopy(const StringType& aSource, StringType& aDest)
 {
   uint32_t len = aSource.Length();
   aDest.SetLength(len);
   MOZ_ASSERT(aDest.Length() == len);
 
-  char16_t* dest = aDest.BeginWriting();
+  CharType* dest = aDest.BeginWriting();
   MOZ_ASSERT(dest);
 
-  const char16_t* iter = aSource.BeginReading();
-  const char16_t* end = aSource.EndReading();
+  const CharType* iter = aSource.BeginReading();
+  const CharType* end = aSource.EndReading();
   while (iter != end) {
-    char16_t c = *iter;
+    CharType c = *iter;
     *dest = (c >= 'A' && c <= 'Z') ?
        c + ('a' - 'A') : c;
     ++iter;
     ++dest;
   }
 }
 
 /* static */
 void
-nsContentUtils::ASCIIToUpper(nsAString& aStr)
-{
-  char16_t* iter = aStr.BeginWriting();
-  char16_t* end = aStr.EndWriting();
+nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
+  return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToLower(const nsACString& aSource, nsACString& aDest) {
+  return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
+}
+
+
+template<typename StringType, typename CharType>
+void
+_ASCIIToUpperInSitu(StringType& aStr)
+{
+  CharType* iter = aStr.BeginWriting();
+  CharType* end = aStr.EndWriting();
   MOZ_ASSERT(iter && end);
 
   while (iter != end) {
-    char16_t c = *iter;
+    CharType c = *iter;
     if (c >= 'a' && c <= 'z') {
       *iter = c + ('A' - 'a');
     }
     ++iter;
   }
 }
 
 /* static */
 void
-nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest)
+nsContentUtils::ASCIIToUpper(nsAString& aStr)
+{
+  return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToUpper(nsACString& aStr)
+{
+  return _ASCIIToUpperInSitu<nsACString, char>(aStr);
+}
+
+template<typename StringType, typename CharType>
+void
+_ASCIIToUpperCopy(const StringType& aSource, StringType& aDest)
 {
   uint32_t len = aSource.Length();
   aDest.SetLength(len);
   MOZ_ASSERT(aDest.Length() == len);
 
-  char16_t* dest = aDest.BeginWriting();
+  CharType* dest = aDest.BeginWriting();
   MOZ_ASSERT(dest);
 
-  const char16_t* iter = aSource.BeginReading();
-  const char16_t* end = aSource.EndReading();
+  const CharType* iter = aSource.BeginReading();
+  const CharType* end = aSource.EndReading();
   while (iter != end) {
-    char16_t c = *iter;
+    CharType c = *iter;
     *dest = (c >= 'a' && c <= 'z') ?
       c + ('A' - 'a') : c;
     ++iter;
     ++dest;
   }
 }
 
 /* static */
+void
+nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest)
+{
+  return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToUpper(const nsACString& aSource, nsACString& aDest)
+{
+  return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
+}
+
+/* static */
 bool
 nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
                                       const nsAString& aStr2)
 {
   uint32_t len = aStr1.Length();
   if (len != aStr2.Length()) {
     return false;
   }
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1744,23 +1744,27 @@ public:
    */
   static bool EqualsIgnoreASCIICase(const nsAString& aStr1,
                                     const nsAString& aStr2);
 
   /**
    * Convert ASCII A-Z to a-z.
    */
   static void ASCIIToLower(nsAString& aStr);
+  static void ASCIIToLower(nsACString& aStr);
   static void ASCIIToLower(const nsAString& aSource, nsAString& aDest);
+  static void ASCIIToLower(const nsACString& aSource, nsACString& aDest);
 
   /**
    * Convert ASCII a-z to A-Z.
    */
   static void ASCIIToUpper(nsAString& aStr);
+  static void ASCIIToUpper(nsACString& aStr);
   static void ASCIIToUpper(const nsAString& aSource, nsAString& aDest);
+  static void ASCIIToUpper(const nsACString& aSource, nsACString& aDest);
 
   /**
    * Return whether aStr contains an ASCII uppercase character.
    */
   static bool StringContainsASCIIUpper(const nsAString& aStr);
 
   // Returns NS_OK for same origin, error (NS_ERROR_DOM_BAD_URI) if not.
   static nsresult CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel);
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -35,27 +35,29 @@ support-files =
 [test_pointerevent_pointerenter-manual.html]
   support-files = pointerevent_pointerenter-manual.html
 [test_pointerevent_pointerleave_after_pointercancel_touch-manual.html]
   support-files = pointerevent_pointerleave_after_pointercancel_touch-manual.html
 [test_pointerevent_pointerleave_after_pointerup_nohover-manual.html]
   support-files = pointerevent_pointerleave_after_pointerup_nohover-manual.html
 [test_pointerevent_pointerleave_descendant_over-manual.html]
   support-files = pointerevent_pointerleave_descendant_over-manual.html
+  skip-if = (os == 'win') || (os == 'linux') # see bug1267310
 [test_pointerevent_pointerleave_descendants-manual.html]
   support-files = pointerevent_pointerleave_descendants-manual.html
 [test_pointerevent_pointerleave_does_not_bubble-manual.html]
   support-files = pointerevent_pointerleave_does_not_bubble-manual.html
 [test_pointerevent_pointerleave_mouse-manual.html]
   support-files = pointerevent_pointerleave_mouse-manual.html
 [test_pointerevent_pointerleave_pen-manual.html]
   support-files = pointerevent_pointerleave_pen-manual.html
   disabled = should be investigated
 [test_pointerevent_pointerleave_touch-manual.html]
   support-files = pointerevent_pointerleave_touch-manual.html
+  skip-if = (os == 'win') || (os == 'linux') # see bug1267311
 [test_pointerevent_pointermove-manual.html]
   support-files = pointerevent_pointermove-manual.html
 [test_pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html]
   support-files = pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html
 [test_pointerevent_pointermove_pointertype-manual.html]
   support-files = pointerevent_pointermove_pointertype-manual.html
 [test_pointerevent_pointerout-manual.html]
   support-files = pointerevent_pointerout-manual.html
@@ -89,16 +91,17 @@ support-files =
 [test_pointerevent_releasepointercapture_onpointercancel_touch-manual.html]
   support-files = pointerevent_releasepointercapture_onpointercancel_touch-manual.html
 [test_pointerevent_releasepointercapture_onpointerup_mouse-manual.html]
   support-files = pointerevent_releasepointercapture_onpointerup_mouse-manual.html
 [test_pointerevent_setpointercapture_disconnected-manual.html]
   support-files = pointerevent_setpointercapture_disconnected-manual.html
 [test_pointerevent_setpointercapture_inactive_button_mouse-manual.html]
   support-files = pointerevent_setpointercapture_inactive_button_mouse-manual.html
+  skip-if = (os == 'win') || (os == 'linux') # see bug1270903
 [test_pointerevent_setpointercapture_invalid_pointerid-manual.html]
   support-files = pointerevent_setpointercapture_invalid_pointerid-manual.html
 [test_pointerevent_setpointercapture_relatedtarget-manual.html]
   support-files = pointerevent_setpointercapture_relatedtarget-manual.html
 [test_pointerevent_touch-action-auto-css_touch-manual.html]
   support-files = pointerevent_touch-action-auto-css_touch-manual.html
   disabled = disabled
 [test_pointerevent_touch-action-button-test_touch-manual.html]
--- a/dom/grid/Grid.cpp
+++ b/dom/grid/Grid.cpp
@@ -25,23 +25,29 @@ Grid::Grid(nsISupports* aParent,
            nsGridContainerFrame* aFrame)
   : mParent(do_QueryInterface(aParent))
   , mRows(new GridDimension(this))
   , mCols(new GridDimension(this))
 {
   MOZ_ASSERT(aFrame,
     "Should never be instantiated with a null nsGridContainerFrame");
 
-  const ComputedGridTrackInfo* rowTrackInfo = aFrame->GetComputedTemplateRows();
+  const ComputedGridTrackInfo* rowTrackInfo =
+    aFrame->GetComputedTemplateRows();
+  const ComputedGridLineInfo* rowLineInfo =
+    aFrame->GetComputedTemplateRowLines();
   mRows->SetTrackInfo(rowTrackInfo);
-  mRows->SetLineInfo(rowTrackInfo);
+  mRows->SetLineInfo(rowTrackInfo, rowLineInfo);
 
-  const ComputedGridTrackInfo* colTrackInfo = aFrame->GetComputedTemplateColumns();
-  mCols->SetTrackInfo(colTrackInfo);
-  mCols->SetLineInfo(colTrackInfo);
+  const ComputedGridTrackInfo* columnTrackInfo =
+    aFrame->GetComputedTemplateColumns();
+  const ComputedGridLineInfo* columnLineInfo =
+    aFrame->GetComputedTemplateColumnLines();
+  mCols->SetTrackInfo(columnTrackInfo);
+  mCols->SetLineInfo(columnTrackInfo, columnLineInfo);
 }
 
 Grid::~Grid()
 {
 }
 
 JSObject*
 Grid::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
--- a/dom/grid/GridDimension.cpp
+++ b/dom/grid/GridDimension.cpp
@@ -55,15 +55,16 @@ GridDimension::Tracks() const
 
 void
 GridDimension::SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo)
 {
   mTracks->SetTrackInfo(aTrackInfo);
 }
 
 void
-GridDimension::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo)
+GridDimension::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
+                           const ComputedGridLineInfo* aLineInfo)
 {
-  mLines->SetLineInfo(aTrackInfo);
+  mLines->SetLineInfo(aTrackInfo, aLineInfo);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/grid/GridDimension.h
+++ b/dom/grid/GridDimension.h
@@ -37,17 +37,18 @@ public:
   {
     return mParent;
   }
 
   GridLines* Lines() const;
   GridTracks* Tracks() const;
 
   void SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo);
-  void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo);
+  void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
+                   const ComputedGridLineInfo* aLineInfo);
 
 protected:
   RefPtr<Grid> mParent;
   RefPtr<GridLines> mLines;
   RefPtr<GridTracks> mTracks;
 };
 
 } // namespace dom
--- a/dom/grid/GridLines.cpp
+++ b/dom/grid/GridLines.cpp
@@ -58,17 +58,18 @@ GridLines::IndexedGetter(uint32_t aIndex
   aFound = aIndex < mLines.Length();
   if (!aFound) {
     return nullptr;
   }
   return mLines[aIndex];
 }
 
 void
-GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo)
+GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
+                       const ComputedGridLineInfo* aLineInfo)
 {
   mLines.Clear();
 
   if (!aTrackInfo) {
     return;
   }
 
   uint32_t trackCount = aTrackInfo->mEndFragmentTrack -
@@ -84,22 +85,28 @@ GridLines::SetLineInfo(const ComputedGri
          i < aTrackInfo->mEndFragmentTrack + 1;
          i++) {
       startOfNextTrack = (i < aTrackInfo->mEndFragmentTrack) ?
                          aTrackInfo->mPositions[i] :
                          endOfLastTrack;
 
       GridLine* line = new GridLine(this);
       mLines.AppendElement(line);
+
+      nsTArray<nsString> lineNames;
+      if (aLineInfo) {
+        lineNames = aLineInfo->mNames.SafeElementAt(i, nsTArray<nsString>());
+      }
+
       line->SetLineValues(
         nsPresContext::AppUnitsToDoubleCSSPixels(endOfLastTrack),
         nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack -
                                                  endOfLastTrack),
         i + 1,
-        nsTArray<nsString>()
+        lineNames
       );
 
       if (i < aTrackInfo->mEndFragmentTrack) {
         endOfLastTrack = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i];
       }
     }
   }
 }
--- a/dom/grid/GridLines.h
+++ b/dom/grid/GridLines.h
@@ -34,17 +34,18 @@ public:
   {
     return mParent;
   }
 
   uint32_t Length() const;
   GridLine* Item(uint32_t aIndex);
   GridLine* IndexedGetter(uint32_t aIndex, bool& aFound);
 
-  void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo);
+  void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
+                   const ComputedGridLineInfo* aLineInfo);
 
 protected:
   RefPtr<GridDimension> mParent;
   nsTArray<RefPtr<GridLine>> mLines;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/grid/test/chrome.ini
+++ b/dom/grid/test/chrome.ini
@@ -1,3 +1,5 @@
+[chrome/test_grid_fragmentation.html]
+[chrome/test_grid_lines.html]
 [chrome/test_grid_object.html]
-[chrome/test_grid_state.html]
-[chrome/test_grid_repeats.html]
+[chrome/test_grid_track_state.html]
+[chrome/test_grid_tracks.html]
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_fragmentation.html
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: Fragmentation of height:auto grid, not top-of-page</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1144096">
+  <link rel="help" href="https://drafts.csswg.org/css-grid/#pagination">
+  <link rel="match" href="grid-fragmentation-001-ref.html">
+
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <style type="text/css">
+html,body {
+    color:black; background-color:white; font-size:16px; padding:0; margin:0;
+}
+body { overflow:hidden; }
+
+.columns {
+  position:relative;
+     -moz-columns: 5;
+      -ms-columns: 5;
+  -webkit-columns: 5;
+          columns: 5;
+     -moz-column-fill: auto;
+      -ms-column-fill: auto;
+  -webkit-column-fill: auto;
+          column-fill: auto;
+  border: 2px dashed;
+  margin-bottom: 5px;
+}
+
+.grid {
+  display: grid;
+  grid-template-columns: 30px 30px 30px;
+  grid-auto-rows: 50px;
+  grid-gap: 12px;
+  border:5px solid;
+  align-content: start;
+}
+span { background:lime; border:1px solid black; }
+x { display:block; height:20px; }
+
+</style>
+
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+	var wrapper = document.getElementById("wrapper");
+	var fragments = wrapper.getGridFragments();
+	
+	// test fragments of the grid
+	is(fragments.length, 2, "Grid is split into two fragments.");
+	
+	if (fragments.length == 2) {
+		var grid0 = fragments[0];
+		var grid1 = fragments[1];
+	
+		// test that both fragments have one row track and two lines
+		is(grid0.rows.tracks.length, 1, "Fragment 0 has one row track.");
+		is(grid0.rows.lines.length, 2, "Fragment 0 has two row lines.");
+		is(grid1.rows.tracks.length, 1, "Fragment 1 has one row track.");
+		is(grid1.rows.lines.length, 2, "Fragment 1 has two row lines.");
+	}
+	
+	SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+<div class="columns" style="height: 100px/*fragmentainer ends in the last row*/">
+<div style="padding-top:10px; background:grey">
+<div id="wrapper" class="grid">
+<span style="grid-row:span 2"><x></x></span>
+<span style="height:60px; background:cyan"><x></x></span>
+<span style="align-self:end; background:pink"><x></x></span>
+<span style="grid-row:1; height:60px"><x></x></span>
+</div></div></div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_lines.html
@@ -0,0 +1,117 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+body {
+	margin: 40px;
+}
+.wrapper {
+	display: grid;
+	width: 400px;
+	grid-gap: 10px;
+	grid-template-columns: 50px [first] repeat(3, [divider] 100px) [last];
+	grid-template-rows: [top1] repeat(1, [top2] 50px) [bot];
+	background-color: #f00;
+}
+.box {
+	background-color: #444;
+	color: #fff;
+}
+</style>
+
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+	var wrapper = document.getElementById("wrapper");
+	var grid = wrapper.getGridFragments()[0];
+	
+	// test property existence
+	isnot(typeof(grid.cols.lines), "undefined", "Grid.cols.lines property exists.");
+	
+	if (typeof(grid.cols.lines) != "undefined") {
+		// test column line count
+		is(grid.cols.lines.length, 5,
+			"Grid.cols.lines property has length that matches grid-template-columns."
+		);
+	
+		if (grid.cols.lines.length == 5) {
+			// test column line position
+			is(grid.cols.lines[1].start, 50, "Grid column line 2 position is as expected.");
+
+			// test column line width
+			is(grid.cols.lines[1].breadth, 10, "Grid column line 2 width is as expected.");
+
+			// test column line number
+			is(grid.cols.lines[3].number, 4, "Grid column line 4 number is as expected.");
+
+			// test column line names
+			is(grid.cols.lines[0].names.length, 0, "Grid column line 1 has no names.");
+			
+			is(grid.cols.lines[1].names.length, 2, "Grid column line 2 has 2 names.");
+			isnot(grid.cols.lines[1].names.indexOf("first"), -1,
+				"Grid column line 2 has the name 'first'."
+			);
+			isnot(grid.cols.lines[1].names.indexOf("divider"), -1,
+				"Grid column line 2 has the name 'divider'."
+			);
+			
+			is(grid.cols.lines[4].names.length, 1, "Grid column line 5 has 1 name.");
+			isnot(grid.cols.lines[4].names.indexOf("last"), -1,
+				"Grid column line 5 has the name 'last'."
+			);
+		}
+	}
+	
+	// test property existence
+	isnot(typeof(grid.rows.lines), "undefined", "Grid.rows.lines property exists.");
+	
+	if (typeof(grid.rows.lines) != "undefined") {
+		// test column line count
+		is(grid.rows.lines.length, 3,
+			"Grid.rows.lines property has length that matches grid-template-rows."
+		);
+	
+		if (grid.rows.lines.length == 3) {
+			// test row line names
+			is(grid.rows.lines[0].names.length, 2, "Grid row line 1 has 2 names.");
+			isnot(grid.rows.lines[0].names.indexOf("top1"), -1,
+				"Grid row line 1 has the name 'top1'."
+			);
+			isnot(grid.rows.lines[0].names.indexOf("top2"), -1,
+				"Grid row line 1 has the name 'top2'."
+			);
+			
+			is(grid.rows.lines[1].names.length, 1, "Grid row line 2 has 1 name.");
+			isnot(grid.rows.lines[1].names.indexOf("bot"), -1,
+				"Grid row line 2 has the name 'bot'."
+			);
+			
+			is(grid.rows.lines[2].names.length, 0, "Grid row line 3 has no names.");
+		}
+	}
+	
+	SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+	<div id="wrapper" class="wrapper">
+		<div id="boxA" class="box a">A</div>
+		<div id="boxB" class="box b">B</div>
+		<div id="boxC" class="box c">C</div>
+		<div class="box d">D</div>
+		<div class="box e">E</div>
+		<div class="box f">F</div>
+		<div class="box g">G</div>
+		<div class="box h">H</div>
+	</div>
+	
+</body>
+</html>
--- a/dom/grid/test/chrome/test_grid_object.html
+++ b/dom/grid/test/chrome/test_grid_object.html
@@ -24,68 +24,45 @@ body {
 <script>
 'use strict';
 
 SimpleTest.waitForExplicitFinish();
 
 function runTests() {
 	var wrapper = document.getElementById("wrapper");
 	var boxA = document.getElementById("boxA");
-	var boxB = document.getElementById("boxB");
-	var boxC = document.getElementById("boxC");
-
+	
 	// test function existence
 	is(typeof(wrapper.getGridFragments), "function",
 		"getGridFragments function exists."
 	);
 	
 	// test that display:grid elements have grids and display:block elements don't	
 	is(boxA.getGridFragments().length, 0, "No grid on display:block styled objects.");
 	is(wrapper.getGridFragments().length, 1,
 		"One grid on an un-fragmented display:grid styled object."
 	);
 	
-	var grid = wrapper.getGridFragments()[0];
-	
-	// test column and row track counts
-	is(grid.cols.tracks.length, 4,
-		"Grid.cols.tracks property has length that matches grid-template-columns."
-	);
-	is(grid.rows.tracks.length, 2,
-		"Grid.rows.tracks property has length that matches content."
-	);
-	
-	// test column track position
-	is(grid.cols.tracks[1].start, 110, "Grid column track 1 position is as expected.");
-	
-	// test column track width
-	is(grid.cols.tracks[0].breadth, boxA.offsetWidth,
-		"Grid column track width (fixed size) matches item width."
-	);
-	is(grid.cols.tracks[1].breadth, boxB.offsetWidth,
-		"Grid column track width (flexible size) matches item width."
-	);
-	is(grid.cols.tracks[1].breadth, grid.cols.tracks[2].breadth,
-		"Grid column track widths with equal proportion flexible size actually are same size."
-	);
-	
-	// test explicit / implicit tracks
-	is(grid.cols.tracks[0].type, "explicit", "Grid column track 0 is explicit.");
-	is(grid.rows.tracks[0].type, "implicit", "Grid row track 0 is implicit.");
+	if (wrapper.getGridFragments().length == 1) {
+		var grid = wrapper.getGridFragments()[0];
+		
+		isnot(typeof(grid.cols), "undefined", "Grid.cols property exists.");
+		isnot(typeof(grid.rows), "undefined", "Grid.rows property exists.");
+	}
 	
 	SimpleTest.finish();
 }
 </script>
 </head>
 <body onLoad="runTests();">
 
 	<div id="wrapper" class="wrapper">
 		<div id="boxA" class="box a">A</div>
-		<div id="boxB" class="box b">B</div>
-		<div id="boxC" class="box c">C</div>
+		<div class="box b">B</div>
+		<div class="box c">C</div>
 		<div class="box d">D</div>
 		<div class="box e">E</div>
 		<div class="box f">F</div>
 		<div class="box g">G</div>
 		<div class="box h">H</div>
 	</div>
 	
 </body>
deleted file mode 100644
--- a/dom/grid/test/chrome/test_grid_repeats.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<meta charset="utf-8">
-<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-<style>
-body {
-	margin: 40px;
-}
-.wrapper {
-	display: grid;
-	width: 600px;
-	grid-gap: 10px;
-	grid-template-columns: repeat(2, 100px) repeat(auto-fill, 50px);
-	background-color: #f00;
-}
-.box {
-	background-color: #444;
-	color: #fff;
-}
-
-</style>
-
-<script>
-'use strict';
-
-SimpleTest.waitForExplicitFinish();
-
-function runTests() {
-	var wrapper = document.getElementById("wrapper");
-	var grid = wrapper.getGridFragments()[0];
-	
-	// test state of tracks
-	is(grid.cols.tracks[1].state, "static", "Grid column track 1 is marked as static.");
-	is(grid.cols.tracks[2].state, "repeat", "Grid column track 2 is marked as repeat.");
-	
-	SimpleTest.finish();
-}
-</script>
-</head>
-<body onLoad="runTests();">
-
-	<div id="wrapper" class="wrapper">
-		<div id="boxA" class="box a">A</div>
-		<div class="box b">B</div>
-		<div class="box c">C</div>
-		<div class="box d">D</div>
-		<div class="box e">E</div>
-	</div>
-	
-</body>
-</html>
deleted file mode 100644
--- a/dom/grid/test/chrome/test_grid_state.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<meta charset="utf-8">
-<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-<style>
-body {
-	margin: 40px;
-}
-.wrapper {
-	display: grid;
-	width: 600px;
-	grid-gap: 0px;
-	grid-template-columns: 50px repeat(auto-fit, 100px);
-	background-color: #f00;
-}
-.box {
-	background-color: #444;
-	color: #fff;
-}
-.a {
-	grid-column: 3;
-}
-</style>
-
-<script>
-'use strict';
-
-SimpleTest.waitForExplicitFinish();
-
-function runTests() {
-	var wrapper = document.getElementById("wrapper");
-	var grid = wrapper.getGridFragments()[0];
-	
-	// test count after removal
-	is(grid.cols.tracks.length, 2, "Grid column track array compensates for removed auto-fit columns.");
-	
-	// test state of tracks
-	is(grid.cols.tracks[0].state, "static", "Grid column track 0 is marked as static.");
-	is(grid.cols.tracks[1].state, "repeat", "Grid column track 1 is marked as repeat.");
-	
-	SimpleTest.finish();
-}
-</script>
-</head>
-<body onLoad="runTests();">
-
-	<div id="wrapper" class="wrapper">
-		<div id="boxA" class="box a">A</div>
-	</div>
-	
-</body>
-</html>
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_track_state.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+body {
+	margin: 40px;
+}
+.wrapper {
+	display: grid;
+	width: 600px;
+	grid-gap: 0px;
+	grid-template-columns: 50px repeat(auto-fit, 100px);
+	background-color: #f00;
+}
+.wrapper2 {
+	display: grid;
+	width: 600px;
+	grid-gap: 0px;
+	grid-template-columns: repeat(2, 100px) repeat(auto-fill, 50px);
+	background-color: #f00;
+}
+.box {
+	background-color: #444;
+	color: #fff;
+}
+.a {
+	grid-column: 3;
+}
+</style>
+
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+	var wrapper = document.getElementById("wrapper");
+	var grid = wrapper.getGridFragments()[0];
+	
+	// test auto-fit count after removal
+	is(grid.cols.tracks.length, 2, "Grid column track array compensates for removed auto-fit columns.");
+	
+	var wrapper2 = document.getElementById("wrapper2");
+	var grid2 = wrapper2.getGridFragments()[0];
+	
+	// test auto-fill count of tracks
+	is(grid2.cols.tracks.length, 10, "Grid column track array respects auto-fill columns.");
+	
+	if (grid2.cols.tracks.length == 10) {
+		// test for static and repeat
+		is(grid2.cols.tracks[1].state, "static", "Grid column track 2 is marked as static.");
+		is(grid2.cols.tracks[2].state, "repeat", "Grid column track 3 is marked as repeat.");
+	}
+	
+	SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+	<div id="wrapper" class="wrapper">
+		<div id="boxA" class="box a">A</div>
+	</div>
+	
+	<div id="wrapper2" class="wrapper2">
+		<div id="boxB" class="box b">B</div>
+	</div>
+	
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_tracks.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+body {
+	margin: 40px;
+}
+.wrapper {
+	display: grid;
+	width: 400px;
+	grid-gap: 10px;
+	grid-template-columns: 100px 1fr 1fr 100px;
+	background-color: #f00;
+}
+.box {
+	background-color: #444;
+	color: #fff;
+}
+</style>
+
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+	var wrapper = document.getElementById("wrapper");
+	var grid = wrapper.getGridFragments()[0];
+	var boxA = document.getElementById("boxA");
+	var boxB = document.getElementById("boxB");
+	
+	// test property existence
+	isnot(typeof(grid.cols.tracks), "undefined", "Grid.cols.tracks property exists.");
+	isnot(typeof(grid.rows.tracks), "undefined", "Grid.rows.tracks property exists.");
+	
+	if ((typeof(grid.cols.tracks) != "undefined") &&
+		(typeof(grid.rows.tracks) != "undefined")) {
+		// test column and row track counts
+		is(grid.cols.tracks.length, 4,
+			"Grid.cols.tracks property has length that matches grid-template-columns."
+		);
+		is(grid.rows.tracks.length, 2,
+			"Grid.rows.tracks property has length that matches content."
+		);
+	
+		if((grid.cols.tracks.length == 4) &&
+		   (grid.rows.tracks.length == 2)) {
+			// test column track position
+			is(grid.cols.tracks[1].start, 110, "Grid column track 2 position is as expected.");
+	
+			// test column track width
+			is(grid.cols.tracks[0].breadth, boxA.offsetWidth,
+				"Grid column track width (fixed size) matches item width."
+			);
+			is(grid.cols.tracks[1].breadth, boxB.offsetWidth,
+				"Grid column track width (flexible size) matches item width."
+			);
+			is(grid.cols.tracks[1].breadth, grid.cols.tracks[2].breadth,
+				"Grid column track widths with equal proportion flexible size actually are same size."
+			);
+		}
+	}
+	
+	SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+	<div id="wrapper" class="wrapper">
+		<div id="boxA" class="box a">A</div>
+		<div id="boxB" class="box b">B</div>
+		<div class="box c">C</div>
+		<div class="box d">D</div>
+		<div class="box e">E</div>
+		<div class="box f">F</div>
+		<div class="box g">G</div>
+		<div class="box h">H</div>
+	</div>
+	
+</body>
+</html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -207,16 +207,19 @@ static const nsAttrValue::EnumTable* kIn
 const Decimal HTMLInputElement::kStepScaleFactorDate = Decimal(86400000);
 const Decimal HTMLInputElement::kStepScaleFactorNumberRange = Decimal(1);
 const Decimal HTMLInputElement::kStepScaleFactorTime = Decimal(1000);
 const Decimal HTMLInputElement::kDefaultStepBase = Decimal(0);
 const Decimal HTMLInputElement::kDefaultStep = Decimal(1);
 const Decimal HTMLInputElement::kDefaultStepTime = Decimal(60);
 const Decimal HTMLInputElement::kStepAny = Decimal(0);
 
+const double HTMLInputElement::kMaximumYear = 275760;
+const double HTMLInputElement::kMinimumYear = 1;
+
 #define NS_INPUT_ELEMENT_STATE_IID                 \
 { /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */       \
   0xdc3b3d14,                                      \
   0x23e2,                                          \
   0x4479,                                          \
   {0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \
 }
 
@@ -942,17 +945,18 @@ GetDOMFileOrDirectoryPath(const OwningFi
 
 } // namespace
 
 /* static */
 bool
 HTMLInputElement::ValueAsDateEnabled(JSContext* cx, JSObject* obj)
 {
   return Preferences::GetBool("dom.experimental_forms", false) ||
-    Preferences::GetBool("dom.forms.datepicker", false);
+    Preferences::GetBool("dom.forms.datepicker", false) ||
+    Preferences::GetBool("dom.forms.datetime", false);
 }
 
 NS_IMETHODIMP
 HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
 {
   mInput->PickerClosed();
 
   if (aResult == nsIFilePicker::returnCancel) {
@@ -2217,16 +2221,22 @@ HTMLInputElement::IsValueEmpty() const
 
 void
 HTMLInputElement::ClearFiles(bool aSetValueChanged)
 {
   nsTArray<OwningFileOrDirectory> data;
   SetFilesOrDirectories(data, aSetValueChanged);
 }
 
+int32_t
+HTMLInputElement::MonthsSinceJan1970(uint32_t aYear, uint32_t aMonth) const
+{
+  return (aYear - 1970) * 12 + aMonth - 1;
+}
+
 /* static */ Decimal
 HTMLInputElement::StringToDecimal(const nsAString& aValue)
 {
   if (!IsASCII(aValue)) {
     return Decimal::nan();
   }
   NS_LossyConvertUTF16toASCII asciiString(aValue);
   std::string stdString = asciiString.get();
@@ -2268,16 +2278,36 @@ HTMLInputElement::ConvertStringToNumber(
     case NS_FORM_INPUT_TIME:
       uint32_t milliseconds;
       if (!ParseTime(aValue, &milliseconds)) {
         return false;
       }
 
       aResultValue = Decimal(int32_t(milliseconds));
       return true;
+    case NS_FORM_INPUT_MONTH:
+      {
+        uint32_t year, month;
+        if (!ParseMonth(aValue, &year, &month)) {
+          return false;
+        }
+
+        // Maximum valid month is 275760-09.
+        if (year < kMinimumYear || year > kMaximumYear) {
+          return false;
+        }
+
+        if (year == kMaximumYear && month > 9) {
+          return false;
+        }
+
+        int32_t months = MonthsSinceJan1970(year, month);
+        aResultValue = Decimal(int32_t(months));
+        return true;
+      }
     default:
       MOZ_ASSERT(false, "Unrecognized input type");
       return false;
   }
 }
 
 Decimal
 HTMLInputElement::GetValueAsDecimal() const
@@ -2485,27 +2515,48 @@ HTMLInputElement::ConvertNumberToString(
           aResultString.AppendPrintf("%02d:%02d:%02d",
                                      hours, minutes, seconds);
         } else {
           aResultString.AppendPrintf("%02d:%02d", hours, minutes);
         }
 
         return true;
       }
+    case NS_FORM_INPUT_MONTH:
+      {
+        aValue = aValue.floor();
+
+        double month = NS_floorModulo(aValue, Decimal(12)).toDouble();
+        month = (month < 0 ? month + 12 : month);
+
+        double year = 1970 + (aValue.toDouble() - month) / 12;
+
+        // Maximum valid month is 275760-09.
+        if (year < kMinimumYear || year > kMaximumYear) {
+          return false;
+        }
+
+        if (year == kMaximumYear && month > 8) {
+          return false;
+        }
+
+        aResultString.AppendPrintf("%04.0f-%02.0f", year, month + 1);
+        return true;
+      }
     default:
       MOZ_ASSERT(false, "Unrecognized input type");
       return false;
   }
 }
 
 
 Nullable<Date>
 HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
 {
-  if (mType != NS_FORM_INPUT_DATE && mType != NS_FORM_INPUT_TIME) {
+  if (!IsDateTimeInputType(mType)) {
     return Nullable<Date>();
   }
 
   switch (mType) {
     case NS_FORM_INPUT_DATE:
     {
       uint32_t year, month, day;
       nsAutoString value;
@@ -2527,37 +2578,66 @@ HTMLInputElement::GetValueAsDate(ErrorRe
       }
 
       JS::ClippedTime time = JS::TimeClip(millisecond);
       MOZ_ASSERT(time.toDouble() == millisecond,
                  "HTML times are restricted to the day after the epoch and "
                  "never clip");
       return Nullable<Date>(Date(time));
     }
+    case NS_FORM_INPUT_MONTH:
+    {
+      uint32_t year, month;
+      nsAutoString value;
+      GetValueInternal(value);
+      if (!ParseMonth(value, &year, &month)) {
+        return Nullable<Date>();
+      }
+
+      JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, 1));
+      return Nullable<Date>(Date(time));
+    }
   }
 
   MOZ_ASSERT(false, "Unrecognized input type");
   aRv.Throw(NS_ERROR_UNEXPECTED);
   return Nullable<Date>();
 }
 
 void
 HTMLInputElement::SetValueAsDate(Nullable<Date> aDate, ErrorResult& aRv)
 {
-  if (mType != NS_FORM_INPUT_DATE && mType != NS_FORM_INPUT_TIME) {
+  if (!IsDateTimeInputType(mType)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (aDate.IsNull() || aDate.Value().IsUndefined()) {
     aRv = SetValue(EmptyString());
     return;
   }
 
-  SetValue(Decimal::fromDouble(aDate.Value().TimeStamp().toDouble()));
+  double milliseconds = aDate.Value().TimeStamp().toDouble();
+
+  if (mType != NS_FORM_INPUT_MONTH) {
+    SetValue(Decimal::fromDouble(milliseconds));
+    return;
+  }
+
+  // type=month expects the value to be number of months.
+  double year = JS::YearFromTime(milliseconds);
+  double month = JS::MonthFromTime(milliseconds);
+
+  if (IsNaN(year) || IsNaN(month)) {
+    SetValue(EmptyString());
+    return;
+  }
+
+  int32_t months = MonthsSinceJan1970(year, month + 1);
+  SetValue(Decimal(int32_t(months)));
 }
 
 NS_IMETHODIMP
 HTMLInputElement::GetValueAsNumber(double* aValueAsNumber)
 {
   *aValueAsNumber = ValueAsNumber();
   return NS_OK;
 }
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -1031,21 +1031,17 @@ protected:
   /**
    * Returns if stepDown and stepUp methods apply for the current type.
    */
   bool DoStepDownStepUpApply() const { return DoesStepApply(); }
 
   /**
    * Returns if valueAsNumber attribute applies for the current type.
    */
-  bool DoesValueAsNumberApply() const
-  {
-    // TODO: this is temporary until bug 888324 is fixed.
-    return DoesMinMaxApply() && mType != NS_FORM_INPUT_MONTH;
-  }
+  bool DoesValueAsNumberApply() const { return DoesMinMaxApply(); }
 
   /**
    * Returns if autocomplete attribute applies for the current type.
    */
   bool DoesAutocompleteApply() const;
 
   /**
    * Returns if the maxlength attribute applies for the current type.
@@ -1201,16 +1197,22 @@ protected:
                  uint32_t* aDay) const;
 
   /**
    * This methods returns the number of days in a given month, for a given year.
    */
   uint32_t NumberOfDaysInMonth(uint32_t aMonth, uint32_t aYear) const;
 
   /**
+   * This methods returns the number of months between January 1970 and the
+   * given year and month.
+   */
+  int32_t MonthsSinceJan1970(uint32_t aYear, uint32_t aMonth) const;
+
+  /**
    * Returns whether aValue is a valid time as described by HTML specifications:
    * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#valid-time-string
    *
    * @param aValue the string to be tested.
    * @return Whether the string is a valid time per HTML specifications.
    */
   bool IsValidTime(const nsAString& aValue) const;
 
@@ -1433,16 +1435,21 @@ protected:
 
   // Default step used when there is no specified step.
   static const Decimal kDefaultStep;
   static const Decimal kDefaultStepTime;
 
   // Float value returned by GetStep() when the step attribute is set to 'any'.
   static const Decimal kStepAny;
 
+  // Maximum year limited by ECMAScript date object range, year <= 275760.
+  static const double kMaximumYear;
+  // Minimum year limited by HTML standard, year >= 1.
+  static const double kMinimumYear;
+
   /**
    * The type of this input (<input type=...>) as an integer.
    * @see nsIFormControl.h (specifically NS_FORM_INPUT_*)
    */
   uint8_t                  mType;
   nsContentUtils::AutocompleteAttrState mAutocompleteAttrState;
   bool                     mDisabledChanged     : 1;
   bool                     mValueChanged        : 1;
--- a/dom/html/test/forms/test_valueAsDate_pref.html
+++ b/dom/html/test/forms/test_valueAsDate_pref.html
@@ -7,41 +7,43 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug 874640</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 874640 **/
   var states = [
-        //dom.experimental_forms, dom.forms.datepicker, expectedValueAsDate
-	[ 'true', 'true', 'true' ],
-	[ 'true', 'false', 'true' ],
-	[ 'false', 'true', 'true' ],
-	[ 'false', 'false', 'false' ],
+  // dom.experimental_forms, dom.forms.datepicker, dom.forms.datetime, expectedValueAsDate
+  [ 'true', 'true', 'true', 'true' ],
+  [ 'true', 'false', 'false', 'true' ],
+  [ 'false', 'true', 'false', 'true' ],
+  [ 'false', 'false', 'true', 'true' ],
+  [ 'false', 'false', 'false', 'false' ],
         'end'
   ];
 
   SimpleTest.waitForExplicitFinish();
 
   function runTest(iframe) {
     var state = states.shift();
 
     if (state == 'end') {
       SimpleTest.finish();
       return;
     }
 
     SpecialPowers.pushPrefEnv({"set":[
       ["dom.experimental_forms", state[0] === 'true'],
-      ["dom.forms.datepicker", state[1] === 'true']]},
+      ["dom.forms.datepicker", state[1] === 'true'],
+      ["dom.forms.datetime", state[2] === 'true']]},
       function() {
         iframe.src = 'data:text/html,<script>' +
                  'parent.is("valueAsDate" in document.createElement("input"), ' +
-                 state[2] + ', "valueAsDate presence state should be ' + state[2] + '");' +
+                 state[3] + ', "valueAsDate presence state should be ' + state[3] + '");' +
                  '<\/script>'
      });
   }
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=874640">Mozilla Bug 874640</a>
--- a/dom/html/test/forms/test_valueasdate_attribute.html
+++ b/dom/html/test/forms/test_valueasdate_attribute.html
@@ -40,22 +40,22 @@ var validTypes =
   ["image", false],
   ["reset", false],
   ["button", false],
   ["number", false],
   ["range", false],
   ["date", true],
   ["time", true],
   ["color", false],
+  ["month", true],
 ];
 
 var todoTypes =
 [
   ["datetime", true],
-  ["month", true],
   ["week", true],
   ["datetime-local", true],
 ];
 
 function checkAvailability()
 {
 
   for (data of validTypes) {
@@ -190,17 +190,16 @@ function checkDateGet()
          "valueAsDate should return an invalid Date object "  +
          "when the element value is not a valid date");
     } else {
       is(element.valueAsDate, null,
          "valueAsDate should return null "  +
          "when the element value is not a valid date");
     }
   }
-
 }
 
 function checkDateSet()
 {
   var testData =
   [
     [ 1342051200000,     "2012-07-12" ],
     [ 0,                 "1970-01-01" ],
@@ -363,18 +362,22 @@ function checkWithBustedPrototype()
     is(element.valueAsDate.getUTCDate, Date.prototype.getUTCDate,
        "prototype is the same");
     is(element.valueAsDate.getTime, Date.prototype.getTime,
        "prototype is the same");
     is(element.valueAsDate.setUTCFullYear, Date.prototype.setUTCFullYear,
        "prototype is the same");
 
     // However the Date should have the correct information.
-    var witnessDate = new Date(element.valueAsNumber);
-    is(element.valueAsDate.valueOf(), witnessDate.valueOf(), "correct Date");
+    // Skip type=month for now, since .valueAsNumber returns number of months
+    // and not milliseconds.
+    if (type != "month") {
+      var witnessDate = new Date(element.valueAsNumber);
+      is(element.valueAsDate.valueOf(), witnessDate.valueOf(), "correct Date");
+    }
 
     // Same test as above but using NaN instead of {}.
 
     Date.prototype.getUTCFullYear = function() { return NaN; };
     Date.prototype.getUTCMonth = function() { return NaN; };
     Date.prototype.getUTCDate = function() { return NaN; };
     Date.prototype.getTime = function() { return NaN; };
     Date.prototype.setUTCFullYear = function(y,m,d) { };
@@ -391,35 +394,128 @@ function checkWithBustedPrototype()
     is(element.valueAsDate.getUTCDate, Date.prototype.getUTCDate,
        "prototype is the same");
     is(element.valueAsDate.getTime, Date.prototype.getTime,
        "prototype is the same");
     is(element.valueAsDate.setUTCFullYear, Date.prototype.setUTCFullYear,
        "prototype is the same");
 
     // However the Date should have the correct information.
-    var witnessDate = new Date(element.valueAsNumber);
-    is(element.valueAsDate.valueOf(), witnessDate.valueOf(), "correct Date");
+    // Skip type=month for now, since .valueAsNumber returns number of months
+    // and not milliseconds.
+    if (type != "month") {
+      var witnessDate = new Date(element.valueAsNumber);
+      is(element.valueAsDate.valueOf(), witnessDate.valueOf(), "correct Date");
+    }
 
     Date.prototype.getUTCFullYear = backupPrototype.getUTCFullYear;
     Date.prototype.getUTCMonth = backupPrototype.getUTCMonth;
     Date.prototype.getUTCDate = backupPrototype.getUTCDate;
     Date.prototype.getTime = backupPrototype.getTime;
     Date.prototype.setUTCFullYear = backupPrototype.setUTCFullYear;
   }
 }
 
+function checkMonthGet()
+{
+  var validData =
+  [
+    [ "2016-07",   1467331200000    ],
+    [ "1970-01",   0                ],
+    [ "1970-02",   2678400000       ],
+    [ "1969-12",   -2678400000      ],
+    [ "0001-01",   -62135596800000  ],
+    [ "275760-09", 8639998963200000 ],
+  ];
+
+  var invalidData =
+  [
+    [ "invalidmonth" ],
+    [ "0000-01"      ],
+    [ "2016-00"      ],
+    [ "123-01"       ],
+    [ "2017-13"      ],
+    [ ""             ],
+    // This month is valid for the input element, but is out of
+    // the date object range. In this case, on getting valueAsDate,
+    // a Date object will be created, but it will have a NaN internal value,
+    // and will return the string "Invalid Date".
+    [ "275760-10", true ],
+  ];
+
+  element.type = "month";
+  for (data of validData) {
+    element.value = data[0];
+    is(element.valueAsDate.valueOf(), data[1],
+       "valueAsDate should return the " +
+       "valid date object representing this month");
+  }
+
+  for (data of invalidData) {
+    element.value = data[0];
+    if (data[1]) {
+      is(String(element.valueAsDate), "Invalid Date",
+         "valueAsDate should return an invalid Date object "  +
+         "when the element value is not a valid month");
+    } else {
+      is(element.valueAsDate, null,
+         "valueAsDate should return null "  +
+         "when the element value is not a valid month");
+    }
+  }
+}
+
+function checkMonthSet()
+{
+  var testData =
+  [
+    [ 1342051200000,      "2012-07" ],
+    [ 0,                  "1970-01" ],
+    // Maximum valid month (limited by the ecma date object range).
+    [ 8640000000000000,   "275760-09" ],
+    // Minimum valid month (limited by the input element minimum valid value).
+    [ -62135596800000 ,   "0001-01" ],
+    [ 1330473600000,      "2012-02" ],
+    [ 1298851200000,      "2011-02" ],
+    // "Values must be truncated to valid months"
+    [ 42.1234,            "1970-01" ],
+    [ 123.123456789123,   "1970-01" ],
+    [ 1e-1,               "1970-01" ],
+    [ 1298851200010,      "2011-02" ],
+    [ -1,                 "1969-12" ],
+    [ -86400000,          "1969-12" ],
+    [ 86400000,           "1970-01" ],
+    // Negative years, this is out of range for the input element,
+    // the corresponding month string is the empty string
+    [ -62135596800001,    "" ],
+  ];
+
+  element.type = "month";
+  for (data of testData) {
+    element.valueAsDate = new Date(data[0]);
+    is(element.value, data[1], "valueAsDate should set the value to "
+                                + data[1]);
+    element.valueAsDate = new testFrame.Date(data[0]);
+    is(element.value, data[1], "valueAsDate with other-global date should " +
+                               "set the value to " + data[1]);
+  }
+}
+
 checkAvailability();
 checkGarbageValues();
 checkWithBustedPrototype();
 
 // Test <input type='date'>.
 checkDateGet();
 checkDateSet();
 
 // Test <input type='time'>.
 checkTimeGet();
 checkTimeSet();
 
+// Test <input type='month'>.
+checkMonthGet();
+checkMonthSet();
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/html/test/forms/test_valueasnumber_attribute.html
+++ b/dom/html/test/forms/test_valueasnumber_attribute.html
@@ -39,18 +39,17 @@ function checkAvailability()
     ["image", false],
     ["reset", false],
     ["button", false],
     ["number", true],
     ["range", true],
     ["date", true],
     ["time", true],
     ["color", false],
-    // TODO: temporary set to false until bug 888324 is fixed.
-    ["month", false],
+    ["month", true],
   ];
 
   var todoList =
   [
     ["datetime", true],
     ["week", true],
     ["datetime-local", true],
   ];
@@ -481,16 +480,113 @@ function checkTimeSet()
     if (!test.throw) {
       test.throw = false;
     }
 
     is(caught, test.throw, "the test throwing status should be " + test.throw);
   }
 }
 
+function checkMonthGet()
+{
+  dump("checkMonthGet\n");
+  var validData =
+  [
+    [ "2016-07", 558       ],
+    [ "1970-01", 0         ],
+    [ "1969-12", -1        ],
+    [ "0001-01", -23628    ],
+    [ "10000-12", 96371    ],
+    [ "275760-09", 3285488 ],
+  ];
+
+  var invalidData =
+  [
+    "invalidmonth",
+    "0000-01",
+    "2000-00",
+    "2012-13",
+    // Out of range.
+    "275760-10",
+  ];
+
+  var element = document.createElement('input');
+  element.type = "month";
+  for (data of validData) {
+    element.value = data[0];
+    is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
+       "integer value representing this month");
+  }
+
+  for (data of invalidData) {
+    element.value = data;
+    ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN "  +
+       "when the element value is not a valid month");
+  }
+}
+
+function checkMonthSet()
+{
+  var testData =
+  [
+    [ 558,               "2016-07"   ],
+    [ 0,                 "1970-01"   ],
+    [ -1,                "1969-12"   ],
+    [ 96371,             "10000-12"  ],
+    [ 12,                "1971-01"   ],
+    [ -12,               "1969-01"   ],
+    // Maximum valid month (limited by the ecma date object range)
+    [ 3285488,           "275760-09" ],
+    // Minimum valid month (limited by the input element minimum valid value)
+    [ -23628,            "0001-01"   ],
+    // "Values must be truncated to valid months"
+    [ 0.3,               "1970-01"   ],
+    [ -1.1,              "1969-11"   ],
+    [ 1e2,               "1978-05"   ],
+    [ 1e-1,              "1970-01"   ],
+    // Invalid numbers.
+    // Those are implicitly converted to numbers
+    [ "",                "1970-01"   ],
+    [ true,              "1970-02"   ],
+    [ false,             "1970-01"   ],
+    [ null,              "1970-01"   ],
+    // Those are converted to NaN, the corresponding month string is the empty string
+    [ "invalidmonth",    ""          ],
+    [ NaN,               ""          ],
+    [ undefined,         ""          ],
+    // Out of range, the corresponding month string is the empty string
+    [ -23629,            ""          ],
+    [ 3285489,           ""          ],
+    // Infinity will keep the current value and throw (so we need to set a current value)
+    [ 558,               "2016-07"   ],
+    [ Infinity,          "2016-07", true ],
+    [ -Infinity,         "2016-07", true ],
+  ];
+
+  var element = document.createElement('input');
+  element.type = "month";
+  for (data of testData) {
+    var caught = false;
+
+    try {
+      element.valueAsNumber = data[0];
+      is(element.value, data[1], "valueAsNumber should set the value to " + data[1]);
+    } catch(e) {
+      caught = true;
+    }
+
+    if (data[2]) {
+      ok(caught, "valueAsNumber should have trhown");
+      is(element.value, data[1], "the value should not have changed");
+    } else {
+      ok(!caught, "valueAsNumber should not have thrown");
+    }
+  }
+}
+
 checkAvailability();
 
 // <input type='number'> test
 checkNumberGet();
 checkNumberSet();
 
 // <input type='range'> test
 checkRangeGet();
@@ -499,12 +595,16 @@ checkRangeSet();
 // <input type='date'> test
 checkDateGet();
 checkDateSet();
 
 // <input type='time'> test
 checkTimeGet();
 checkTimeSet();
 
+// <input type='month'> test
+checkMonthGet();
+checkMonthSet();
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -1867,16 +1867,30 @@ BackgroundDatabaseChild::RecvInvalidate(
 
   if (mDatabase) {
     mDatabase->Invalidate();
   }
 
   return true;
 }
 
+bool
+BackgroundDatabaseChild::RecvCloseAfterInvalidationComplete()
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  if (mDatabase) {
+    mDatabase->DispatchTrustedEvent(nsDependentString(kCloseEventType));
+  }
+
+  return true;
+}
+
 /*******************************************************************************
  * BackgroundDatabaseRequestChild
  ******************************************************************************/
 
 BackgroundDatabaseRequestChild::BackgroundDatabaseRequestChild(
                                                          IDBDatabase* aDatabase,
                                                          IDBRequest* aRequest)
   : BackgroundRequestChildBase(aRequest)
--- a/dom/indexedDB/ActorsChild.h
+++ b/dom/indexedDB/ActorsChild.h
@@ -414,16 +414,19 @@ private:
   virtual bool
   RecvVersionChange(const uint64_t& aOldVersion,
                     const NullableVersion& aNewVersion)
                     override;
 
   virtual bool
   RecvInvalidate() override;
 
+  virtual bool
+  RecvCloseAfterInvalidationComplete() override;
+
   bool
   SendDeleteMe() = delete;
 };
 
 class BackgroundDatabaseRequestChild final
   : public BackgroundRequestChildBase
   , public PBackgroundIDBDatabaseRequestChild
 {
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -13806,16 +13806,24 @@ Database::ConnectionClosedCallback()
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(mClosed);
   MOZ_ASSERT(!mTransactions.Count());
   MOZ_ASSERT(!mActiveMutableFileCount);
 
   mDirectoryLock = nullptr;
 
   CleanupMetadata();
+
+  if (IsInvalidated() && IsActorAlive()) {
+    // Step 3 and 4 of "5.2 Closing a Database":
+    // 1. Wait for all transactions to complete.
+    // 2. Fire a close event if forced flag is set, i.e., IsInvalidated() in our
+    //    implementation.
+    Unused << SendCloseAfterInvalidationComplete();
+  }
 }
 
 void
 Database::CleanupMetadata()
 {
   AssertIsOnBackgroundThread();
 
   if (!mMetadataCleanedUp) {
@@ -14932,17 +14940,17 @@ TransactionBase::Invalidate()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread);
 
   if (!mInvalidated) {
     mInvalidated = true;
     mInvalidatedOnAnyThread = true;
 
-    Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ true);
+    Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, /* aForce */ false);
   }
 }
 
 PBackgroundIDBRequestParent*
 TransactionBase::AllocRequest(const RequestParams& aParams, bool aTrustParams)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aParams.type() != RequestParams::T__None);
@@ -16181,20 +16189,16 @@ Cursor::Start(const OpenCursorParams& aP
     mType == OpenCursorParams::TObjectStoreOpenCursorParams ?
       aParams.get_ObjectStoreOpenCursorParams().optionalKeyRange() :
     mType == OpenCursorParams::TObjectStoreOpenKeyCursorParams ?
       aParams.get_ObjectStoreOpenKeyCursorParams().optionalKeyRange() :
     mType == OpenCursorParams::TIndexOpenCursorParams ?
       aParams.get_IndexOpenCursorParams().optionalKeyRange() :
       aParams.get_IndexOpenKeyCursorParams().optionalKeyRange();
 
-  if (mTransaction->IsInvalidated()) {
-    return true;
-  }
-
   RefPtr<OpenOp> openOp = new OpenOp(this, optionalKeyRange);
 
   if (NS_WARN_IF(!openOp->Init(mTransaction))) {
     openOp->Cleanup();
     return false;
   }
 
   openOp->DispatchToConnectionPool();
@@ -16344,20 +16348,16 @@ Cursor::RecvContinue(const CursorRequest
     return false;
   }
 
   if (NS_WARN_IF(mTransaction->mCommitOrAbortReceived)) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
-  if (mTransaction->IsInvalidated()) {
-    return true;
-  }
-
   RefPtr<ContinueOp> continueOp = new ContinueOp(this, aParams, aKey);
   if (NS_WARN_IF(!continueOp->Init(mTransaction))) {
     continueOp->Cleanup();
     return false;
   }
 
   continueOp->DispatchToConnectionPool();
   mCurrentlyRunningOp = continueOp;
@@ -22739,22 +22739,19 @@ TransactionDatabaseOperationBase::RunOnC
   MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
 
   PROFILER_LABEL("IndexedDB",
                  "TransactionDatabaseOperationBase::RunOnConnectionThread",
                  js::ProfileEntry::Category::STORAGE);
 
   // There are several cases where we don't actually have to to any work here.
 
-  if (mTransactionIsAborted) {
-    // This transaction is already set to be aborted.
+  if (mTransactionIsAborted || mTransaction->IsInvalidatedOnAnyThread()) {
+    // This transaction is already set to be aborted or invalidated.
     mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
-  } else if (mTransaction->IsInvalidatedOnAnyThread()) {
-    // This transaction is being invalidated.
-    mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   } else if (!OperationMayProceed()) {
     // The operation was canceled in some way, likely because the child process
     // has crashed.
     IDB_REPORT_INTERNAL_ERR();
     mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   } else {
     Database* database = mTransaction->GetDatabase();
     MOZ_ASSERT(database);
@@ -22815,19 +22812,17 @@ TransactionDatabaseOperationBase::RunOnO
 
   if (NS_WARN_IF(IsActorDestroyed())) {
     // Don't send any notifications if the actor was destroyed already.
     if (NS_SUCCEEDED(mResultCode)) {
       IDB_REPORT_INTERNAL_ERR();
       mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
   } else {
-    if (mTransaction->IsInvalidated()) {
-      mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-    } else if (mTransaction->IsAborted()) {
+    if (mTransaction->IsInvalidated() || mTransaction->IsAborted()) {
       // Aborted transactions always see their requests fail with ABORT_ERR,
       // even if the request succeeded or failed with another error.
       mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
     } else if (NS_SUCCEEDED(mResultCode)) {
       // This may release the IPDL reference.
       mResultCode = SendSuccessResult();
     }
 
@@ -26736,16 +26731,24 @@ CursorOpBase::SendFailureResult(nsresult
   MOZ_ASSERT(NS_FAILED(aResultCode));
   MOZ_ASSERT(mCursor);
   MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this);
   MOZ_ASSERT(!mResponseSent);
 
   if (!IsActorDestroyed()) {
     mResponse = ClampResultCode(aResultCode);
 
+    // This is an expected race when the transaction is invalidated after
+    // data is retrieved from database. We clear the retrieved files to prevent
+    // the assertion failure in SendResponseInternal when mResponse.type() is
+    // CursorResponse::Tnsresult.
+    if (Transaction()->IsInvalidated() && !mFiles.IsEmpty()) {
+      mFiles.Clear();
+    }
+
     mCursor->SendResponseInternal(mResponse, mFiles);
   }
 
 #ifdef DEBUG
   mResponseSent = true;
 #endif
   return false;
 }
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -868,17 +868,17 @@ IDBDatabase::AbortTransactions(bool aSho
               transactionsThatNeedWarning.AppendElement(transaction);
               break;
 
             default:
               MOZ_CRASH("Unknown mode!");
           }
         }
 
-        transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+        transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
       }
 
       static const char kWarningMessage[] =
         "IndexedDBTransactionAbortNavigation";
 
       for (IDBTransaction* transaction : transactionsThatNeedWarning) {
         MOZ_ASSERT(transaction);
 
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -243,16 +243,17 @@ public:
               const StringOrStringSequence& aStoreNames,
               IDBTransactionMode aMode,
               IDBTransaction** aTransaction);
 
   StorageType
   Storage() const;
 
   IMPL_EVENT_HANDLER(abort)
+  IMPL_EVENT_HANDLER(close)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(versionchange)
 
   already_AddRefed<IDBRequest>
   CreateMutableFile(JSContext* aCx,
                     const nsAString& aName,
                     const Optional<nsAString>& aType,
                     ErrorResult& aRv);
--- a/dom/indexedDB/IDBEvents.cpp
+++ b/dom/indexedDB/IDBEvents.cpp
@@ -21,16 +21,17 @@ namespace indexedDB {
 
 const char16_t* kAbortEventType = MOZ_UTF16("abort");
 const char16_t* kBlockedEventType = MOZ_UTF16("blocked");
 const char16_t* kCompleteEventType = MOZ_UTF16("complete");
 const char16_t* kErrorEventType = MOZ_UTF16("error");
 const char16_t* kSuccessEventType = MOZ_UTF16("success");
 const char16_t* kUpgradeNeededEventType = MOZ_UTF16("upgradeneeded");
 const char16_t* kVersionChangeEventType = MOZ_UTF16("versionchange");
+const char16_t* kCloseEventType = MOZ_UTF16("close");
 
 already_AddRefed<nsIDOMEvent>
 CreateGenericEvent(EventTarget* aOwner,
                    const nsDependentString& aType,
                    Bubbles aBubbles,
                    Cancelable aCancelable)
 {
   RefPtr<Event> event = new Event(aOwner, nullptr, nullptr);
--- a/dom/indexedDB/IDBEvents.h
+++ b/dom/indexedDB/IDBEvents.h
@@ -42,16 +42,17 @@ enum Cancelable {
 
 extern const char16_t* kAbortEventType;
 extern const char16_t* kBlockedEventType;
 extern const char16_t* kCompleteEventType;
 extern const char16_t* kErrorEventType;
 extern const char16_t* kSuccessEventType;
 extern const char16_t* kUpgradeNeededEventType;
 extern const char16_t* kVersionChangeEventType;
+extern const char16_t* kCloseEventType;
 
 already_AddRefed<nsIDOMEvent>
 CreateGenericEvent(EventTarget* aOwner,
                    const nsDependentString& aType,
                    Bubbles aBubbles,
                    Cancelable aCancelable);
 
 } // namespace indexedDB
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -371,17 +371,17 @@ IDBTransaction::OnNewRequest()
 void
 IDBTransaction::OnRequestFinished(bool aActorDestroyedNormally)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mPendingRequestCount);
 
   --mPendingRequestCount;
 
-  if (!mPendingRequestCount && !mDatabase->IsInvalidated()) {
+  if (!mPendingRequestCount) {
     mReadyState = COMMITTING;
 
     if (aActorDestroyedNormally) {
       if (NS_SUCCEEDED(mAbortCode)) {
         SendCommit();
       } else {
         SendAbort(mAbortCode);
       }
@@ -636,25 +636,16 @@ IDBTransaction::AbortInternal(nsresult a
   MOZ_ASSERT(!IsCommittingOrDone());
 
   RefPtr<DOMError> error = aError;
 
   const bool isVersionChange = mMode == VERSION_CHANGE;
   const bool isInvalidated = mDatabase->IsInvalidated();
   bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
 
-  if (isInvalidated) {
-#ifdef DEBUG
-    mSentCommitOrAbort = true;
-#endif
-    // Increment the serial number counter here to account for the aborted
-    // transaction and keep the parent in sync.
-    IDBRequest::NextSerialNumber();
-  }
-
   mAbortCode = aAbortCode;
   mReadyState = DONE;
   mError = error.forget();
 
   if (isVersionChange) {
     // If a version change transaction is aborted, we must revert the world
     // back to its previous state unless we're being invalidated after the
     // transaction already completed.
--- a/dom/indexedDB/PBackgroundIDBDatabase.ipdl
+++ b/dom/indexedDB/PBackgroundIDBDatabase.ipdl
@@ -67,16 +67,18 @@ parent:
 
 child:
   async __delete__();
 
   async VersionChange(uint64_t oldVersion, NullableVersion newVersion);
 
   async Invalidate();
 
+  async CloseAfterInvalidationComplete();
+
   async PBackgroundIDBVersionChangeTransaction(uint64_t currentVersion,
                                                uint64_t requestedVersion,
                                                int64_t nextObjectStoreId,
                                                int64_t nextIndexId);
 
   async PBackgroundMutableFile(nsString name, nsString type);
 };
 
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -152,16 +152,22 @@ function testHarnessSteps() {
           ok(true, "Worker finished");
           nextTestHarnessStep();
           break;
 
         case "expectUncaughtException":
           worker._expectingUncaughtException = message.expecting;
           break;
 
+        case "clearAllDatabases":
+          clearAllDatabases(function(){
+            worker.postMessage({ op: "clearAllDatabasesDone" });
+          });
+          break;
+
         default:
           ok(false,
              "Received a bad message from worker: " + JSON.stringify(message));
           nextTestHarnessStep();
       }
     };
 
     URL.revokeObjectURL(workerScriptURL);
@@ -506,16 +512,22 @@ function workerScript() {
   };
 
   self._expectingUncaughtException = false;
   self.expectUncaughtException = function(_expecting_) {
     self._expectingUncaughtException = !!_expecting_;
     self.postMessage({ op: "expectUncaughtException", expecting: !!_expecting_ });
   };
 
+  self._clearAllDatabasesCallback = undefined;
+  self.clearAllDatabases = function(_callback_) {
+    self._clearAllDatabasesCallback = _callback_;
+    self.postMessage({ op: "clearAllDatabases" });
+  }
+
   self.onerror = function(_message_, _file_, _line_) {
     if (self._expectingUncaughtException) {
       self._expectingUncaughtException = false;
       ok(true, "Worker: expected exception [" + _file_ + ":" + _line_ + "]: '" +
          _message_ + "'");
       return;
     }
     ok(false,
@@ -537,16 +549,23 @@ function workerScript() {
 
       case "start":
         executeSoon(function() {
           info("Worker: starting tests");
           testGenerator.next();
         });
         break;
 
+      case "clearAllDatabasesDone":
+        info("Worker: all databases are cleared");
+        if (self._clearAllDatabasesCallback) {
+          self._clearAllDatabasesCallback();
+        }
+        break;
+
       default:
         throw new Error("Received a bad message from parent: " +
                         JSON.stringify(message));
     }
   };
 
   self.postMessage({ op: "ready" });
 }
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -31,16 +31,17 @@ support-files =
   unit/test_count.js
   unit/test_create_index.js
   unit/test_create_index_with_integer_keys.js
   unit/test_create_locale_aware_index.js
   unit/test_create_objectStore.js
   unit/test_cursor_mutation.js
   unit/test_cursor_update_updates_indexes.js
   unit/test_cursors.js
+  unit/test_database_onclose.js
   unit/test_deleteDatabase.js
   unit/test_deleteDatabase_interactions.js
   unit/test_deleteDatabase_onblocked.js
   unit/test_deleteDatabase_onblocked_duringVersionChange.js
   unit/test_event_source.js
   unit/test_filehandle_append_read_data.js
   unit/test_getAll.js
   unit/test_globalObjects_ipc.js
@@ -164,16 +165,18 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_create_objectStore.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_cursor_mutation.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_cursor_update_updates_indexes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_cursors.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
+[test_database_onclose.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_deleteDatabase.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_deleteDatabase_interactions.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_deleteDatabase_onblocked.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_deleteDatabase_onblocked_duringVersionChange.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_database_onclose.html
@@ -0,0 +1,19 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database DeleteDatabase Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7" src="unit/test_database_onclose.js"></script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_database_close_without_onclose.js
@@ -0,0 +1,49 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = this.window ? window.location.pathname :
+                             "test_database_close_without_onclose.js";
+
+  const checkpointSleepTimeSec = 10;
+
+  let openRequest = indexedDB.open(name, 1);
+  openRequest.onerror = errorHandler;
+  openRequest.onsuccess = unexpectedSuccessHandler;
+  openRequest.onupgradeneeded = grabEventAndContinueHandler;
+
+  ok(openRequest instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
+
+  let event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+
+  let db = event.target.result;
+  db.createObjectStore("store");
+
+  openRequest.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "success", "Expect a success event");
+  is(event.target, openRequest, "Event has right target");
+  ok(event.target.result instanceof IDBDatabase, "Result should be a database");
+  is(db.objectStoreNames.length, 1, "Expect an objectStore here");
+
+  db.onclose = errorHandler;
+
+  db.close();
+  setTimeout(continueToNextStepSync, checkpointSleepTimeSec * 1000);
+  yield undefined;
+
+  ok(true, "The close event should not be fired after closed normally!");
+
+  finishTest();
+  yield undefined;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_database_onclose.js
@@ -0,0 +1,245 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  function testInvalidStateError(aDb, aTxn) {
+    try {
+      info("The db shall become invalid after closed.");
+      aDb.transaction("store");
+      ok(false, "InvalidStateError shall be thrown.");
+    } catch (e) {
+      ok(e instanceof DOMException, "got a database exception");
+      is(e.name, "InvalidStateError", "correct error");
+    }
+
+    try {
+      info("The txn shall become invalid after closed.");
+      aTxn.objectStore("store");
+      ok(false, "InvalidStateError shall be thrown.");
+    } catch (e) {
+      ok(e instanceof DOMException, "got a database exception");
+      is(e.name, "InvalidStateError", "correct error");
+    }
+  }
+
+  const name = this.window ? window.location.pathname :
+                             "test_database_onclose.js";
+
+  info("#1: Verifying IDBDatabase.onclose after cleared by the agent.");
+  let openRequest = indexedDB.open(name, 1);
+  openRequest.onerror = errorHandler;
+  openRequest.onsuccess = unexpectedSuccessHandler;
+  openRequest.onupgradeneeded = grabEventAndContinueHandler;
+
+  ok(openRequest instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
+
+  let event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+
+  let db = event.target.result;
+  db.createObjectStore("store");
+
+  openRequest.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "success", "Expect a success event");
+  is(event.target, openRequest, "Event has right target");
+  ok(event.target.result instanceof IDBDatabase, "Result should be a database");
+  is(db.objectStoreNames.length, 1, "Expect an objectStore here");
+
+  let txn = db.transaction("store", "readwrite");
+  let objectStore = txn.objectStore("store");
+
+  clearAllDatabases(continueToNextStep);
+
+  db.onclose = grabEventAndContinueHandler;
+  event = yield undefined;
+  is(event.type, "close", "Expect a close event");
+  is(event.target, db, "Correct target");
+
+  info("Wait for callback of clearAllDatabases().");
+  yield undefined;
+
+  testInvalidStateError(db, txn);
+
+  info("#2: Verifying IDBDatabase.onclose && IDBTransaction.onerror " +
+  		 "in *write* operation after cleared by the agent.");
+  openRequest = indexedDB.open(name, 1);
+  openRequest.onerror = errorHandler;
+  openRequest.onsuccess = unexpectedSuccessHandler;
+  openRequest.onupgradeneeded = grabEventAndContinueHandler;
+
+  ok(openRequest instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
+
+  event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+
+  db = event.target.result;
+  db.createObjectStore("store");
+
+  openRequest.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "success", "Expect a success event");
+  is(event.target, openRequest, "Event has right target");
+  ok(event.target.result instanceof IDBDatabase, "Result should be a database");
+  is(db.objectStoreNames.length, 1, "Expect an objectStore here");
+
+  txn = db.transaction("store", "readwrite");
+  objectStore = txn.objectStore("store");
+
+  let objectId = 0;
+  while(true) {
+    let addRequest = objectStore.add({foo: "foo"}, objectId);
+    addRequest.onerror = function(event) {
+      info("addRequest.onerror, objectId: " + objectId);
+      txn.onerror = grabEventAndContinueHandler;
+      testGenerator.send(true);
+    }
+    addRequest.onsuccess = function() {
+      testGenerator.send(false);
+    }
+
+    if (objectId == 0) {
+      clearAllDatabases(() => {
+        info("clearAllDatabases is done.");
+        continueToNextStep();
+      });
+    }
+
+    objectId++;
+
+    let aborted = yield undefined;
+    if (aborted) {
+      break;
+    }
+  }
+
+  event = yield undefined;
+  is(event.type, "error", "Got an error event");
+  is(event.target.error.name, "AbortError", "Expected AbortError was thrown.");
+  event.preventDefault();
+
+  txn.onabort = grabEventAndContinueHandler;
+  event = yield undefined;
+  is(event.type, "abort", "Got an abort event");
+  is(event.target.error.name, "AbortError", "Expected AbortError was thrown.");
+
+  db.onclose = grabEventAndContinueHandler;
+  event = yield undefined;
+  is(event.type, "close", "Expect a close event");
+  is(event.target, db, "Correct target");
+  testInvalidStateError(db, txn);
+
+  info("Wait for the callback of clearAllDatabases().");
+  yield undefined;
+
+  info("#3: Verifying IDBDatabase.onclose && IDBTransaction.onerror " +
+  "in *read* operation after cleared by the agent.");
+  openRequest = indexedDB.open(name, 1);
+  openRequest.onerror = errorHandler;
+  openRequest.onsuccess = unexpectedSuccessHandler;
+  openRequest.onupgradeneeded = grabEventAndContinueHandler;
+
+  ok(openRequest instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
+
+  event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
+  ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
+
+  db = event.target.result;
+  objectStore =
+    db.createObjectStore("store", { keyPath: "id", autoIncrement: true });
+  // The number of read records varies between 1~2000 before the db is cleared
+  // during testing.
+  let numberOfObjects = 3000;
+  objectId = 0;
+  while(true) {
+    let addRequest = objectStore.add({foo: "foo"});
+    addRequest.onsuccess = function() {
+      objectId++;
+      testGenerator.send(objectId == numberOfObjects);
+    }
+    addRequest.onerror = errorHandler;
+
+    let done = yield undefined;
+    if (done) {
+      break;
+    }
+  }
+
+  openRequest.onsuccess = grabEventAndContinueHandler;
+
+  event = yield undefined;
+
+  is(event.type, "success", "Expect a success event");
+  is(event.target, openRequest, "Event has right target");
+  ok(event.target.result instanceof IDBDatabase, "Result should be a database");
+  is(db.objectStoreNames.length, 1, "Expect an objectStore here");
+
+  txn = db.transaction("store");
+  objectStore = txn.objectStore("store");
+
+  let numberOfReadObjects = 0;
+  let readRequest = objectStore.openCursor();
+  readRequest.onerror = function(event) {
+    info("readRequest.onerror, numberOfReadObjects: " + numberOfReadObjects);
+    testGenerator.send(true);
+  }
+  readRequest.onsuccess = function(event) {
+    let cursor = event.target.result;
+    if (cursor) {
+      numberOfReadObjects++;
+      event.target.result.continue();
+    } else {
+      info("Cursor is invalid, numberOfReadObjects: " + numberOfReadObjects);
+      todo(false, "All records are iterated before database is cleared!");
+      testGenerator.send(false);
+    }
+  }
+
+  clearAllDatabases(() => {
+    info("clearAllDatabases is done.");
+    continueToNextStep();
+  });
+
+  readRequestError = yield undefined;
+  if (readRequestError) {
+    txn.onerror = grabEventAndContinueHandler;
+
+    event = yield undefined;
+    is(event.type, "error", "Got an error event");
+    is(event.target.error.name, "AbortError", "Expected AbortError was thrown.");
+    event.preventDefault();
+
+    txn.onabort = grabEventAndContinueHandler;
+    event = yield undefined;
+    is(event.type, "abort", "Got an abort event");
+    is(event.target.error.name, "AbortError", "Expected AbortError was thrown.");
+
+    db.onclose = grabEventAndContinueHandler;
+    event = yield undefined;
+    is(event.type, "close", "Expect a close event");
+    is(event.target, db, "Correct target");
+
+    testInvalidStateError(db, txn);
+  }
+
+  info("Wait for the callback of clearAllDatabases().");
+  yield undefined;
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini
+++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini
@@ -27,16 +27,18 @@ support-files =
   storagePersistentUpgrade_profile.zip
   xpcshell-shared.ini
 
 [include:xpcshell-shared.ini]
 
 [test_blob_file_backed.js]
 [test_bug1056939.js]
 [test_cleanup_transaction.js]
+[test_database_close_without_onclose.js]
+[test_database_onclose.js]
 [test_defaultStorageUpgrade.js]
 [test_idbSubdirUpgrade.js]
 [test_globalObjects_ipc.js]
 skip-if = toolkit == 'android'
 [test_idle_maintenance.js]
 [test_invalidate.js]
 # disabled for the moment.
 skip-if = true
--- a/dom/locales/en-US/chrome/appstrings.properties
+++ b/dom/locales/en-US/chrome/appstrings.properties
@@ -27,15 +27,14 @@ externalProtocolTitle=External Protocol 
 externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
 #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
 malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
 unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
 deceptiveBlocked=This web page at %S has been reported as a deceptive site and has been blocked based on your security preferences.
-forbiddenBlocked=The site at %S has been blocked by your browser configuration.
 cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
 corruptedContentErrorv2=The site at %S has experienced a network protocol violation that cannot be repaired.
 remoteXUL=This page uses an unsupported technology that is no longer available by default.
 sslv3Used=The safety of your data on %S could not be guaranteed because it uses SSLv3, a broken security protocol.
 weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, the connection to this website has not been established.
 inadequateSecurityError=The website tried to negotiate an inadequate level of security.
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -362,18 +362,20 @@ AudioStream::OpenCubeb(cubeb_stream_para
   }
 
   cubeb_stream* stream = nullptr;
   if (cubeb_stream_init(cubebContext, &stream, "AudioStream",
                         nullptr, nullptr, nullptr, &aParams,
                         CubebUtils::GetCubebLatency(),
                         DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
     mCubebStream.reset(stream);
+    CubebUtils::ReportCubebBackendUsed();
   } else {
     NS_WARNING(nsPrintfCString("AudioStream::OpenCubeb() %p failed to init cubeb", this).get());
+    CubebUtils::ReportCubebStreamInitFailure(aIsFirst);
     return NS_ERROR_FAILURE;
   }
 
   TimeDuration timeDelta = TimeStamp::Now() - aStartTime;
   LOG("creation time %sfirst: %u ms", aIsFirst ? "" : "not ",
       (uint32_t) timeDelta.ToMilliseconds());
   Telemetry::Accumulate(aIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS :
       Telemetry::AUDIOSTREAM_LATER_OPEN_MS, timeDelta.ToMilliseconds());
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -7,16 +7,17 @@
 #include <stdint.h>
 #include <algorithm>
 #include "nsIStringBundle.h"
 #include "nsDebug.h"
 #include "nsString.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
 #include "nsThreadUtils.h"
 #include "CubebUtils.h"
 #include "nsAutoRef.h"
 #include "prdtoa.h"
 
 #define PREF_VOLUME_SCALE "media.volume_scale"
 #define PREF_CUBEB_LATENCY "media.cubeb_latency_ms"
 
@@ -25,20 +26,44 @@ namespace mozilla {
 namespace {
 
 // This mutex protects the variables below.
 StaticMutex sMutex;
 cubeb* sCubebContext;
 double sVolumeScale;
 uint32_t sCubebLatency;
 bool sCubebLatencyPrefSet;
+bool sAudioStreamInitEverSucceeded = false;
 StaticAutoPtr<char> sBrandName;
 
 const char kBrandBundleURL[]      = "chrome://branding/locale/brand.properties";
 
+const char* AUDIOSTREAM_BACKEND_ID_STR[] = {
+  "jack",
+  "pulse",
+  "alsa",
+  "audiounit",
+  "audioqueue",
+  "wasapi",
+  "winmm",
+  "directsound",
+  "sndio",
+  "opensl",
+  "audiotrack",
+  "kai"
+};
+/* Index for failures to create an audio stream the first time. */
+const int CUBEB_BACKEND_INIT_FAILURE_FIRST =
+  ArrayLength(AUDIOSTREAM_BACKEND_ID_STR);
+/* Index for failures to create an audio stream after the first time */
+const int CUBEB_BACKEND_INIT_FAILURE_OTHER = CUBEB_BACKEND_INIT_FAILURE_FIRST + 1;
+/* Index for an unknown backend. */
+const int CUBEB_BACKEND_UNKNOWN = CUBEB_BACKEND_INIT_FAILURE_FIRST + 2;
+
+
 // Prefered samplerate, in Hz (characteristic of the hardware, mixer, platform,
 // and API used).
 //
 // sMutex protects *initialization* of this, which must be performed from each
 // thread before fetching, after which it is safe to fetch without holding the
 // mutex because it is only written once per process execution (by the first
 // initialization to complete).  Since the init must have been called on a
 // given thread before fetching the value, it's guaranteed (via the mutex) that
@@ -145,16 +170,49 @@ cubeb* GetCubebContextUnlocked()
   NS_WARN_IF_FALSE(sBrandName, "Could not get brandName?");
 
   DebugOnly<int> rv = cubeb_init(&sCubebContext, sBrandName);
   NS_WARN_IF_FALSE(rv == CUBEB_OK, "Could not get a cubeb context.");
 
   return sCubebContext;
 }
 
+void ReportCubebBackendUsed()
+{
+  StaticMutexAutoLock lock(sMutex);
+
+  sAudioStreamInitEverSucceeded = true;
+
+  bool foundBackend = false;
+  for (uint32_t i = 0; i < ArrayLength(AUDIOSTREAM_BACKEND_ID_STR); i++) {
+    if (!strcmp(cubeb_get_backend_id(sCubebContext), AUDIOSTREAM_BACKEND_ID_STR[i])) {
+      Telemetry::Accumulate(Telemetry::AUDIOSTREAM_BACKEND_USED, i);
+      foundBackend = true;
+    }
+  }
+  if (!foundBackend) {
+    Telemetry::Accumulate(Telemetry::AUDIOSTREAM_BACKEND_USED,
+                          CUBEB_BACKEND_UNKNOWN);
+  }
+}
+
+void ReportCubebStreamInitFailure(bool aIsFirst)
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (!aIsFirst && !sAudioStreamInitEverSucceeded) {
+    // This machine has no audio hardware, or it's in really bad shape, don't
+    // send this info, since we want CUBEB_BACKEND_INIT_FAILURE_OTHER to detect
+    // failures to open multiple streams in a process over time.
+    return;
+  }
+  Telemetry::Accumulate(Telemetry::AUDIOSTREAM_BACKEND_USED,
+                        aIsFirst ? CUBEB_BACKEND_INIT_FAILURE_FIRST
+                                 : CUBEB_BACKEND_INIT_FAILURE_OTHER);
+}
+
 uint32_t GetCubebLatency()
 {
   StaticMutexAutoLock lock(sMutex);
   return sCubebLatency;
 }
 
 bool CubebLatencyPrefSet()
 {
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -34,16 +34,18 @@ void InitPreferredSampleRate();
 // Get the aforementioned sample rate. Thread safe.
 uint32_t PreferredSampleRate();
 
 void PrefChanged(const char* aPref, void* aClosure);
 double GetVolumeScale();
 bool GetFirstStream();
 cubeb* GetCubebContext();
 cubeb* GetCubebContextUnlocked();
+void ReportCubebStreamInitFailure(bool aIsFirstStream);
+void ReportCubebBackendUsed();
 uint32_t GetCubebLatency();
 bool CubebLatencyPrefSet();
 #if defined(__ANDROID__) && defined(MOZ_B2G)
 cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel);
 #endif
 
 } // namespace CubebUtils
 } // namespace mozilla
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -555,16 +555,17 @@ AudioCallbackDriver::~AudioCallbackDrive
 }
 
 void
 AudioCallbackDriver::Init()
 {
   cubeb_stream_params output;
   cubeb_stream_params input;
   uint32_t latency;
+  bool firstStream = CubebUtils::GetFirstStream();
 
   MOZ_ASSERT(!NS_IsMainThread(),
       "This is blocking and should never run on the main thread.");
 
   mSampleRate = output.rate = CubebUtils::PreferredSampleRate();
 
 #if defined(__ANDROID__)
 #if defined(MOZ_B2G)
@@ -624,21 +625,23 @@ AudioCallbackDriver::Init()
                           mGraphImpl->mInputWanted ? &input : nullptr,
                           output_id,
                           mGraphImpl->mOutputWanted ? &output : nullptr, latency,
                           DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
       mAudioStream.own(stream);
       DebugOnly<int> rv = cubeb_stream_set_volume(mAudioStream, CubebUtils::GetVolumeScale());
       NS_WARN_IF_FALSE(rv == CUBEB_OK,
           "Could not set the audio stream volume in GraphDriver.cpp");
+      CubebUtils::ReportCubebBackendUsed();
     } else {
 #ifdef MOZ_WEBRTC
       StaticMutexAutoUnlock unlock(AudioInputCubeb::Mutex());
 #endif
       NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling back to a SystemClockDriver");
+      CubebUtils::ReportCubebStreamInitFailure(firstStream);
       // Fall back to a driver using a normal thread.
       MonitorAutoLock lock(GraphImpl()->GetMonitor());
       SetNextDriver(new SystemClockDriver(GraphImpl()));
       NextDriver()->SetGraphTime(this, mIterationStart, mIterationEnd);
       mGraphImpl->SetCurrentDriver(NextDriver());
       NextDriver()->Start();
       return;
     }
--- a/dom/media/mediasink/AudioSinkWrapper.cpp
+++ b/dom/media/mediasink/AudioSinkWrapper.cpp
@@ -117,27 +117,30 @@ AudioSinkWrapper::SetVolume(double aVolu
     mAudioSink->SetVolume(aVolume);
   }
 }
 
 void
 AudioSinkWrapper::SetPlaybackRate(double aPlaybackRate)
 {
   AssertOwnerThread();
-  mParams.mPlaybackRate = aPlaybackRate;
   if (!mAudioEnded) {
     // Pass the playback rate to the audio sink. The underlying AudioStream
     // will handle playback rate changes and report correct audio position.
     mAudioSink->SetPlaybackRate(aPlaybackRate);
   } else if (!mPlayStartTime.IsNull()) {
     // Adjust playback duration and start time when we are still playing.
     TimeStamp now = TimeStamp::Now();
     mPlayDuration = GetVideoPosition(now);
     mPlayStartTime = now;
   }
+  // mParams.mPlaybackRate affects GetVideoPosition(). It should be updated
+  // after the calls to GetVideoPosition();
+  mParams.mPlaybackRate = aPlaybackRate;
+
   // Do nothing when not playing. Changes in playback rate will be taken into
   // account by GetVideoPosition().
 }
 
 void
 AudioSinkWrapper::SetPreservesPitch(bool aPreservesPitch)
 {
   AssertOwnerThread();
--- a/dom/push/PushSubscriptionOptions.cpp
+++ b/dom/push/PushSubscriptionOptions.cpp
@@ -1,34 +1,56 @@
 /* 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 "mozilla/dom/PushSubscriptionOptions.h"
 
 #include "mozilla/dom/PushSubscriptionOptionsBinding.h"
+#include "mozilla/HoldDropJSObjects.h"
 
 namespace mozilla {
 namespace dom {
 
 PushSubscriptionOptions::PushSubscriptionOptions(nsIGlobalObject* aGlobal,
-                                                 nsTArray<uint8_t>&& aAppServerKey)
+                                                 nsTArray<uint8_t>&& aRawAppServerKey)
   : mGlobal(aGlobal)
-  , mAppServerKey(Move(aAppServerKey))
+  , mRawAppServerKey(Move(aRawAppServerKey))
+  , mAppServerKey(nullptr)
 {
   // There's only one global on a worker, so we don't need to pass a global
   // object to the constructor.
   MOZ_ASSERT_IF(NS_IsMainThread(), mGlobal);
+  mozilla::HoldJSObjects(this);
+}
+
+PushSubscriptionOptions::~PushSubscriptionOptions()
+{
+  mAppServerKey = nullptr;
+  mozilla::DropJSObjects(this);
 }
 
-PushSubscriptionOptions::~PushSubscriptionOptions() {}
+NS_IMPL_CYCLE_COLLECTION_CLASS(PushSubscriptionOptions)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PushSubscriptionOptions)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  tmp->mAppServerKey = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PushSubscriptionOptions)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(PushSubscriptionOptions)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAppServerKey)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscriptionOptions, mGlobal)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscriptionOptions)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscriptionOptions)
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscriptionOptions)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 JSObject*
 PushSubscriptionOptions::WrapObject(JSContext* aCx,
                                     JS::Handle<JSObject*> aGivenProto)
@@ -36,13 +58,23 @@ PushSubscriptionOptions::WrapObject(JSCo
   return PushSubscriptionOptionsBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 PushSubscriptionOptions::GetApplicationServerKey(JSContext* aCx,
                                                  JS::MutableHandle<JSObject*> aKey,
                                                  ErrorResult& aRv)
 {
-  PushUtil::CopyArrayToArrayBuffer(aCx, mAppServerKey, aKey, aRv);
+  if (!mRawAppServerKey.IsEmpty() && !mAppServerKey) {
+    JS::Rooted<JSObject*> appServerKey(aCx);
+    PushUtil::CopyArrayToArrayBuffer(aCx, mRawAppServerKey, &appServerKey, aRv);
+    if (aRv.Failed()) {
+      return;
+    }
+    MOZ_ASSERT(appServerKey);
+    mAppServerKey = appServerKey;
+    JS::ExposeObjectToActiveJS(mAppServerKey);
+  }
+  aKey.set(mAppServerKey);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/push/PushSubscriptionOptions.h
+++ b/dom/push/PushSubscriptionOptions.h
@@ -21,17 +21,17 @@ namespace dom {
 class PushSubscriptionOptions final : public nsISupports
                                     , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushSubscriptionOptions)
 
   PushSubscriptionOptions(nsIGlobalObject* aGlobal,
-                          nsTArray<uint8_t>&& aAppServerKey);
+                          nsTArray<uint8_t>&& aRawAppServerKey);
 
   nsIGlobalObject*
   GetParentObject() const
   {
     return mGlobal;
   }
 
   JSObject*
@@ -41,15 +41,16 @@ public:
   GetApplicationServerKey(JSContext* aCx,
                           JS::MutableHandle<JSObject*> aKey,
                           ErrorResult& aRv);
 
 private:
   ~PushSubscriptionOptions();
 
   nsCOMPtr<nsIGlobalObject> mGlobal;
-  nsTArray<uint8_t> mAppServerKey;
+  nsTArray<uint8_t> mRawAppServerKey;
+  JS::Heap<JSObject*> mAppServerKey;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PushSubscriptionOptions_h
--- a/dom/push/test/test_register.html
+++ b/dom/push/test/test_register.html
@@ -61,16 +61,18 @@ http://creativecommons.org/licenses/publ
   add_task(function* checkPermissionState() {
     var state = yield registration.pushManager.permissionState();
     is(state, "granted", "permissionState() should resolve to granted.");
   });
 
   var pushSubscription;
   add_task(function* subscribe() {
     pushSubscription = yield registration.pushManager.subscribe();
+    is(pushSubscription.options.applicationServerKey, null,
+       "Subscription should not have an app server key");
   });
 
   add_task(function* resubscribe() {
     var data = yield sendRequestToWorker({
       type: "resubscribe",
       endpoint: pushSubscription.endpoint,
     });
     pushSubscription = yield registration.pushManager.getSubscription();
--- a/dom/push/test/test_register_key.html
+++ b/dom/push/test/test_register_key.html
@@ -106,16 +106,19 @@ http://creativecommons.org/licenses/publ
   });
 
   add_task(function* validKey() {
     var pushSubscription = yield registration.pushManager.subscribe({
       applicationServerKey: yield generateKey(),
     });
     is(pushSubscription.endpoint, "https://example.com/push/1",
       "Wrong endpoint for subscription with key");
+    is(pushSubscription.options.applicationServerKey,
+       pushSubscription.options.applicationServerKey,
+       "App server key getter should return the same object");
   });
 
   add_task(function* retrieveKey() {
     var pushSubscription = yield registration.pushManager.getSubscription();
     is(pushSubscription.endpoint, "https://example.com/push/subWithKey",
       "Got wrong endpoint for subscription with key");
     isDeeply(
       new Uint8Array(pushSubscription.options.applicationServerKey),
--- a/dom/webidl/IDBDatabase.webidl
+++ b/dom/webidl/IDBDatabase.webidl
@@ -25,16 +25,17 @@ interface IDBDatabase : EventTarget {
 
     [Throws]
     IDBTransaction transaction ((DOMString or sequence<DOMString>) storeNames,
                                 optional IDBTransactionMode mode = "readonly");
 
     void           close ();
 
                 attribute EventHandler       onabort;
+                attribute EventHandler       onclose;
                 attribute EventHandler       onerror;
                 attribute EventHandler       onversionchange;
 };
 
 partial interface IDBDatabase {
     [Func="mozilla::dom::IndexedDatabaseManager::ExperimentalFeaturesEnabled"]
     readonly    attribute StorageType        storage;
 
--- a/dom/webidl/PushSubscriptionOptions.webidl
+++ b/dom/webidl/PushSubscriptionOptions.webidl
@@ -5,11 +5,11 @@
 *
 * The origin of this IDL file is
 * https://w3c.github.io/push-api/
 */
 
 [Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled"]
 interface PushSubscriptionOptions
 {
-  [Throws]
+  [SameObject, Throws]
   readonly attribute ArrayBuffer? applicationServerKey;
 };
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -90,57 +90,36 @@ namespace dom {
 // once doubling reaches this threshold
 #define XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH (32*1024*1024)
 // start at 32k to avoid lots of doubling right at the start
 #define XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE (32*1024)
 // the maximum Content-Length that we'll preallocate.  1GB.  Must fit
 // in an int32_t!
 #define XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE (1*1024*1024*1024LL)
 
-#define LOAD_STR "load"
-#define ERROR_STR "error"
-#define ABORT_STR "abort"
-#define TIMEOUT_STR "timeout"
-#define LOADSTART_STR "loadstart"
-#define PROGRESS_STR "progress"
-#define READYSTATE_STR "readystatechange"
-#define LOADEND_STR "loadend"
+namespace {
+  const nsLiteralString ProgressEventTypeStrings[] = {
+    NS_LITERAL_STRING("loadstart"),
+    NS_LITERAL_STRING("progress"),
+    NS_LITERAL_STRING("error"),
+    NS_LITERAL_STRING("abort"),
+    NS_LITERAL_STRING("timeout"),
+    NS_LITERAL_STRING("load"),
+    NS_LITERAL_STRING("loadend")
+  };
+  static_assert(MOZ_ARRAY_LENGTH(ProgressEventTypeStrings) ==
+                  size_t(XMLHttpRequestMainThread::ProgressEventType::ENUM_MAX),
+                "Mismatched lengths for ProgressEventTypeStrings and ProgressEventType enums");
+
+  const nsString kLiteralString_readystatechange = NS_LITERAL_STRING("readystatechange");
+  const nsString kLiteralString_xmlhttprequest = NS_LITERAL_STRING("xmlhttprequest");
+  const nsString kLiteralString_DOMContentLoaded = NS_LITERAL_STRING("DOMContentLoaded");
+}
 
 // CIDs
-
-// State
-#define XML_HTTP_REQUEST_UNSENT           (1 << 0) // 0 UNSENT
-#define XML_HTTP_REQUEST_OPENED           (1 << 1) // 1 OPENED
-#define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
-#define XML_HTTP_REQUEST_LOADING          (1 << 3) // 3 LOADING
-#define XML_HTTP_REQUEST_DONE             (1 << 4) // 4 DONE
-#define XML_HTTP_REQUEST_SENT             (1 << 5) // Internal, corresponds to
-                                                   // "OPENED and the send()
-                                                   // flag is set" in spec
-                                                   // terms.
-// The above states are mutually exclusive, change with ChangeState() only.
-// The states below can be combined.
-#define XML_HTTP_REQUEST_ABORTED        (1 << 7)  // Internal
-#define XML_HTTP_REQUEST_ASYNC          (1 << 8)  // Internal
-#define XML_HTTP_REQUEST_PARSEBODY      (1 << 9)  // Internal
-#define XML_HTTP_REQUEST_SYNCLOOPING    (1 << 10) // Internal
-#define XML_HTTP_REQUEST_BACKGROUND     (1 << 13) // Internal
-#define XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND (1 << 14) // Internal
-#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 15) // Internal
-#define XML_HTTP_REQUEST_TIMED_OUT (1 << 16) // Internal
-#define XML_HTTP_REQUEST_DELETED (1 << 17) // Internal
-
-#define XML_HTTP_REQUEST_LOADSTATES         \
-  (XML_HTTP_REQUEST_UNSENT |                \
-   XML_HTTP_REQUEST_OPENED |                \
-   XML_HTTP_REQUEST_HEADERS_RECEIVED |      \
-   XML_HTTP_REQUEST_LOADING |               \
-   XML_HTTP_REQUEST_DONE |                  \
-   XML_HTTP_REQUEST_SENT)
-
 #define NS_BADCERTHANDLER_CONTRACTID \
   "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
 
 NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
 
 class nsResumeTimeoutsEvent : public Runnable
@@ -176,17 +155,21 @@ static void AddLoadFlags(nsIRequest *req
 
 bool
 XMLHttpRequestMainThread::sDontWarnAboutSyncXHR = false;
 
 XMLHttpRequestMainThread::XMLHttpRequestMainThread()
   : mResponseBodyDecodedPos(0),
     mResponseType(XMLHttpRequestResponseType::_empty),
     mRequestObserver(nullptr),
-    mState(XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC),
+    mState(State::unsent),
+    mFlagSynchronous(false), mFlagAborted(false), mFlagParseBody(false),
+    mFlagSyncLooping(false), mFlagBackgroundRequest(false),
+    mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
+    mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     mProgressSinceLastProgressEvent(false),
     mRequestSentTime(0), mTimeoutMilliseconds(0),
     mErrorLoad(false), mWaitingForOnStopRequest(false),
     mProgressTimerIsActive(false),
     mIsHtml(false),
     mWarnAboutSyncHtml(false),
     mLoadLengthComputable(false), mLoadTotal(0),
@@ -198,25 +181,25 @@ XMLHttpRequestMainThread::XMLHttpRequest
     mResultArrayBuffer(nullptr),
     mIsMappedArrayBuffer(false),
     mXPCOMifier(nullptr)
 {
 }
 
 XMLHttpRequestMainThread::~XMLHttpRequestMainThread()
 {
-  mState |= XML_HTTP_REQUEST_DELETED;
-
-  if (mState & (XML_HTTP_REQUEST_SENT |
-                XML_HTTP_REQUEST_LOADING)) {
+  mFlagDeleted = true;
+
+  if ((mState == State::opened && mFlagSend) ||
+      mState == State::loading) {
     Abort();
   }
 
-  MOZ_ASSERT(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
-  mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
+  MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
+  mFlagSyncLooping = false;
 
   mResultJSON.setUndefined();
   mResultArrayBuffer = nullptr;
   mozilla::DropJSObjects(this);
 }
 
 void
 XMLHttpRequestMainThread::RootJSResultObjects()
@@ -492,17 +475,17 @@ XMLHttpRequestMainThread::GetResponseXML
       mResponseType != XMLHttpRequestResponseType::Document) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
   if (mWarnAboutSyncHtml) {
     mWarnAboutSyncHtml = false;
     LogMessage("HTMLSyncXHRWarning", GetOwner());
   }
-  if (!(XML_HTTP_REQUEST_DONE & mState)) {
+  if (mState != State::done) {
     return nullptr;
   }
   return mResponseXML;
 }
 
 /*
  * This piece copied from XMLDocument, we try to get the charset
  * from HTTP headers.
@@ -608,17 +591,17 @@ XMLHttpRequestMainThread::GetResponseTex
   }
 
   if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text &&
       !mInLoadProgressEvent) {
     aResponseText.SetIsVoid(true);
     return;
   }
 
-  if (!(mState & (XML_HTTP_REQUEST_DONE | XML_HTTP_REQUEST_LOADING))) {
+  if (mState != State::loading && mState != State::done) {
     return;
   }
 
   // We only decode text lazily if we're also parsing to a doc.
   // Also, if we've decoded all current data already, then no need to decode
   // more.
   if (!mResponseXML ||
       mResponseBodyDecodedPos == mResponseBody.Length()) {
@@ -638,17 +621,17 @@ XMLHttpRequestMainThread::GetResponseTex
   aRv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
                              mResponseBody.Length() - mResponseBodyDecodedPos);
   if (aRv.Failed()) {
     return;
   }
 
   mResponseBodyDecodedPos = mResponseBody.Length();
 
-  if (mState & XML_HTTP_REQUEST_DONE) {
+  if (mState == State::done) {
     // Free memory buffer which we no longer need
     mResponseBody.Truncate();
     mResponseBodyDecodedPos = 0;
   }
 
   aResponseText = mResponseText;
 }
 
@@ -726,30 +709,29 @@ NS_IMETHODIMP XMLHttpRequestMainThread::
 }
 
 void
 XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseType,
                                           ErrorResult& aRv)
 {
   // If the state is LOADING or DONE raise an INVALID_STATE_ERR exception
   // and terminate these steps.
-  if ((mState & (XML_HTTP_REQUEST_LOADING | XML_HTTP_REQUEST_DONE))) {
+  if (mState == State::loading || mState == State::done) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // sync request is not allowed setting responseType in window context
-  if (HasOrHasHadOwner() &&
-      !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
+  if (HasOrHasHadOwner() && mState != State::unsent && mFlagSynchronous) {
     LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
 
-  if (!(mState & XML_HTTP_REQUEST_ASYNC) &&
+  if (mFlagSynchronous &&
       (aResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
        aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Set the responseType attribute's value to the given value.
   mResponseType = aResponseType;
@@ -783,17 +765,17 @@ XMLHttpRequestMainThread::GetResponse(JS
     }
     return;
   }
 
   case XMLHttpRequestResponseType::Arraybuffer:
   case XMLHttpRequestResponseType::Moz_chunked_arraybuffer:
   {
     if (!(mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
-          mState & XML_HTTP_REQUEST_DONE) &&
+          mState == State::done) &&
         !(mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer &&
           mInLoadProgressEvent)) {
       aResponse.setNull();
       return;
     }
 
     if (!mResultArrayBuffer) {
       RootJSResultObjects();
@@ -806,17 +788,17 @@ XMLHttpRequestMainThread::GetResponse(JS
     }
     JS::ExposeObjectToActiveJS(mResultArrayBuffer);
     aResponse.setObject(*mResultArrayBuffer);
     return;
   }
   case XMLHttpRequestResponseType::Blob:
   case XMLHttpRequestResponseType::Moz_blob:
   {
-    if (!(mState & XML_HTTP_REQUEST_DONE)) {
+    if (mState != State::done) {
       if (mResponseType != XMLHttpRequestResponseType::Moz_blob) {
         aResponse.setNull();
         return;
       }
 
       if (!mResponseBlob) {
         CreatePartialBlob(aRv);
       }
@@ -827,27 +809,27 @@ XMLHttpRequestMainThread::GetResponse(JS
       return;
     }
 
     GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse);
     return;
   }
   case XMLHttpRequestResponseType::Document:
   {
-    if (!(mState & XML_HTTP_REQUEST_DONE) || !mResponseXML) {
+    if (!mResponseXML || mState != State::done) {
       aResponse.setNull();
       return;
     }
 
     aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
     return;
   }
   case XMLHttpRequestResponseType::Json:
   {
-    if (!(mState & XML_HTTP_REQUEST_DONE)) {
+    if (mState != State::done) {
       aResponse.setNull();
       return;
     }
 
     if (mResultJSON.isUndefined()) {
       aRv = CreateResponseParsedJSON(aCx);
       mResponseText.Truncate();
       if (aRv.Failed()) {
@@ -1016,65 +998,64 @@ XMLHttpRequestMainThread::GetStatusText(
   if (httpChannel) {
     httpChannel->GetResponseStatusText(aStatusText);
   } else {
     aStatusText.AssignLiteral("OK");
   }
 }
 
 void
-XMLHttpRequestMainThread::CloseRequestWithError(const nsAString& aType,
-                                                const uint32_t aFlag)
+XMLHttpRequestMainThread::CloseRequestWithError(const ProgressEventType aType)
 {
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
   }
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
   uint32_t responseLength = mResponseBody.Length();
   ResetResponse();
-  mState |= aFlag;
 
   // If we're in the destructor, don't risk dispatching an event.
-  if (mState & XML_HTTP_REQUEST_DELETED) {
-    mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
+  if (mFlagDeleted) {
+    mFlagSyncLooping = false;
     return;
   }
 
-  if (!(mState & (XML_HTTP_REQUEST_UNSENT |
-                  XML_HTTP_REQUEST_OPENED |
-                  XML_HTTP_REQUEST_DONE))) {
-    ChangeState(XML_HTTP_REQUEST_DONE, true);
-
-    if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
+  if (mState != State::unsent &&
+      !(mState == State::opened && !mFlagSend) &&
+      mState != State::done) {
+    ChangeState(State::done, true);
+
+    if (!mFlagSyncLooping) {
       DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength,
                             mLoadTotal);
       if (mUpload && !mUploadComplete) {
         mUploadComplete = true;
         DispatchProgressEvent(mUpload, aType, true, mUploadTransferred,
                               mUploadTotal);
       }
     }
   }
 
   // The ChangeState call above calls onreadystatechange handlers which
   // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
   // the abort state bit. If this occurs we're not uninitialized (bug 361773).
-  if (mState & XML_HTTP_REQUEST_ABORTED) {
-    ChangeState(XML_HTTP_REQUEST_UNSENT, false);  // IE seems to do it
+  if (mFlagAborted) {
+    ChangeState(State::unsent, false);  // IE seems to do it
   }
 
-  mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
+  mFlagSyncLooping = false;
 }
 
 void
 XMLHttpRequestMainThread::Abort(ErrorResult& arv)
 {
-  CloseRequestWithError(NS_LITERAL_STRING(ABORT_STR), XML_HTTP_REQUEST_ABORTED);
+  mFlagAborted = true;
+  CloseRequestWithError(ProgressEventType::abort);
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SlowAbort()
 {
   Abort();
   return NS_OK;
 }
@@ -1147,18 +1128,17 @@ XMLHttpRequestMainThread::GetAllResponse
 void
 XMLHttpRequestMainThread::GetAllResponseHeaders(nsACString& aResponseHeaders,
                                                 ErrorResult& aRv)
 {
   aResponseHeaders.Truncate();
 
   // If the state is UNSENT or OPENED,
   // return the empty string and terminate these steps.
-  if (mState & (XML_HTTP_REQUEST_UNSENT |
-                XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
+  if (mState == State::unsent || mState == State::opened) {
     return;
   }
 
   if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
     RefPtr<nsHeaderVisitor> visitor =
       new nsHeaderVisitor(*this, WrapNotNull(httpChannel));
     if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
       aResponseHeaders = visitor->Headers();
@@ -1212,18 +1192,17 @@ XMLHttpRequestMainThread::GetResponseHea
 {
   _retval.SetIsVoid(true);
 
   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
 
   if (!httpChannel) {
     // If the state is UNSENT or OPENED,
     // return null and terminate these steps.
-    if (mState & (XML_HTTP_REQUEST_UNSENT |
-                  XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
+    if (mState == State::unsent || mState == State::opened) {
       return;
     }
 
     // Even non-http channels supply content type and content length.
     // Remember we don't leak header information from denied cross-site
     // requests.
     nsresult status;
     if (!mChannel ||
@@ -1270,17 +1249,17 @@ XMLHttpRequestMainThread::GetResponseHea
     _retval.SetIsVoid(true);
     aRv.SuppressException();
   }
 }
 
 already_AddRefed<nsILoadGroup>
 XMLHttpRequestMainThread::GetLoadGroup() const
 {
-  if (mState & XML_HTTP_REQUEST_BACKGROUND) {
+  if (mFlagBackgroundRequest) {
     return nullptr;
   }
 
   if (mLoadGroup) {
     nsCOMPtr<nsILoadGroup> ref = mLoadGroup;
     return ref.forget();
   }
 
@@ -1288,64 +1267,58 @@ XMLHttpRequestMainThread::GetLoadGroup()
   if (doc) {
     return doc->GetDocumentLoadGroup();
   }
 
   return nullptr;
 }
 
 nsresult
-XMLHttpRequestMainThread::CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent)
+XMLHttpRequestMainThread::FireReadystatechangeEvent()
 {
   RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
-  event.forget(aDOMEvent);
-
-  (*aDOMEvent)->InitEvent(NS_LITERAL_STRING(READYSTATE_STR),
-                          false, false);
-
+  event->InitEvent(kLiteralString_readystatechange, false, false);
   // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
-  (*aDOMEvent)->SetTrusted(true);
-
+  event->SetTrusted(true);
+  DispatchDOMEvent(nullptr, event, nullptr, nullptr);
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
-                                                const nsAString& aType,
+                                                const ProgressEventType aType,
                                                 bool aLengthComputable,
                                                 int64_t aLoaded, int64_t aTotal)
 {
   NS_ASSERTION(aTarget, "null target");
-  NS_ASSERTION(!aType.IsEmpty(), "missing event type");
 
   if (NS_FAILED(CheckInnerWindowCorrectness()) ||
       (!AllowUploadProgress() && aTarget == mUpload)) {
     return;
   }
 
-  bool dispatchLoadend = aType.EqualsLiteral(LOAD_STR) ||
-                         aType.EqualsLiteral(ERROR_STR) ||
-                         aType.EqualsLiteral(TIMEOUT_STR) ||
-                         aType.EqualsLiteral(ABORT_STR);
-
   ProgressEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
   init.mLengthComputable = aLengthComputable;
   init.mLoaded = aLoaded;
   init.mTotal = (aTotal == -1) ? 0 : aTotal;
 
+  const nsAString& typeString = ProgressEventTypeStrings[(uint8_t)aType];
   RefPtr<ProgressEvent> event =
-    ProgressEvent::Constructor(aTarget, aType, init);
+    ProgressEvent::Constructor(aTarget, typeString, init);
   event->SetTrusted(true);
 
   aTarget->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 
-  if (dispatchLoadend) {
-    DispatchProgressEvent(aTarget, NS_LITERAL_STRING(LOADEND_STR),
+  // If we're sending a load, error, timeout or abort event, then
+  // also dispatch the subsequent loadend event.
+  if (aType == ProgressEventType::load || aType == ProgressEventType::error ||
+      aType == ProgressEventType::timeout || aType == ProgressEventType::abort) {
+    DispatchProgressEvent(aTarget, ProgressEventType::loadend,
                           aLengthComputable, aLoaded, aTotal);
   }
 }
 
 already_AddRefed<nsIHttpChannel>
 XMLHttpRequestMainThread::GetCurrentHttpChannel()
 {
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
@@ -1421,38 +1394,35 @@ XMLHttpRequestMainThread::Open(const nsA
     if (mResponseType != XMLHttpRequestResponseType::_empty) {
       LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     }
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
   nsCOMPtr<nsIURI> uri;
 
-  if (mState & (XML_HTTP_REQUEST_OPENED |
-                XML_HTTP_REQUEST_HEADERS_RECEIVED |
-                XML_HTTP_REQUEST_LOADING |
-                XML_HTTP_REQUEST_SENT)) {
+  if (mState == State::opened || mState == State::headers_received ||
+      mState == State::loading) {
     // IE aborts as well
     Abort();
 
     // XXX We should probably send a warning to the JS console
     //     that load was aborted and event listeners were cleared
     //     since this looks like a situation that could happen
     //     by accident and you could spend a lot of time wondering
     //     why things didn't work.
   }
 
-  // Unset any pre-existing aborted and timed-out states.
-  mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
-
-  if (async) {
-    mState |= XML_HTTP_REQUEST_ASYNC;
-  } else {
-    mState &= ~XML_HTTP_REQUEST_ASYNC;
-  }
+  mFlagSend = false;
+
+  // Unset any pre-existing aborted and timed-out flags.
+  mFlagAborted = false;
+  mFlagTimedOut = false;
+
+  mFlagSynchronous = !async;
 
   nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
   if (!doc) {
     // This could be because we're no longer current or because we're in some
     // non-window context...
     nsresult rv = CheckInnerWindowCorrectness();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return NS_ERROR_DOM_INVALID_STATE_ERR;
@@ -1548,31 +1518,31 @@ XMLHttpRequestMainThread::Open(const nsA
                        nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
                        loadGroup,
                        nullptr,   // aCallbacks
                        loadFlags);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mState &= ~XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
+  mFlagHadUploadListenersOnSend = false;
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   if (httpChannel) {
     rv = httpChannel->SetRequestMethod(method);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set the initiator type
     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
     if (timedChannel) {
-      timedChannel->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
+      timedChannel->SetInitiatorType(kLiteralString_xmlhttprequest);
     }
   }
 
-  ChangeState(XML_HTTP_REQUEST_OPENED);
+  ChangeState(State::opened);
 
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::PopulateNetworkInterfaceId()
 {
   if (mNetworkInterfaceId.IsEmpty()) {
@@ -1636,17 +1606,17 @@ XMLHttpRequestMainThread::StreamReaderFu
              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Text ||
              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Json ||
              xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_chunked_text) {
     NS_ASSERTION(!xmlHttpRequest->mResponseXML,
                  "We shouldn't be parsing a doc here");
     xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
   }
 
-  if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
+  if (xmlHttpRequest->mFlagParseBody) {
     // Give the same data to the parser.
 
     // We need to wrap the data in a new lightweight stream and pass that
     // to the parser, because calling ReadSegments() recursively on the same
     // stream is not supported.
     nsCOMPtr<nsIInputStream> copyStream;
     rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
 
@@ -1655,17 +1625,17 @@ XMLHttpRequestMainThread::StreamReaderFu
       nsresult parsingResult = xmlHttpRequest->mXMLParserStreamListener
                                   ->OnDataAvailable(xmlHttpRequest->mChannel,
                                                     xmlHttpRequest->mContext,
                                                     copyStream, toOffset, count);
 
       // No use to continue parsing if we failed here, but we
       // should still finish reading the stream
       if (NS_FAILED(parsingResult)) {
-        xmlHttpRequest->mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+        xmlHttpRequest->mFlagParseBody = false;
       }
     }
   }
 
   if (NS_SUCCEEDED(rv)) {
     *writeCount = count;
   } else {
     *writeCount = 0;
@@ -1725,23 +1695,23 @@ XMLHttpRequestMainThread::OnDataAvailabl
   if (cancelable) {
     // We don't have to read from the local file for the blob response
     ErrorResult error;
     mDataAvailable = mDOMBlob->GetSize(error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
 
-    ChangeState(XML_HTTP_REQUEST_LOADING);
+    ChangeState(State::loading);
     return request->Cancel(NS_OK);
   }
 
   mDataAvailable += totalRead;
 
-  ChangeState(XML_HTTP_REQUEST_LOADING);
+  ChangeState(State::loading);
 
   MaybeDispatchProgressEvents(false);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
@@ -1756,59 +1726,59 @@ XMLHttpRequestMainThread::OnStartRequest
   }
 
   if (request != mChannel) {
     // Can this still happen?
     return NS_OK;
   }
 
   // Don't do anything if we have been aborted
-  if (mState & XML_HTTP_REQUEST_UNSENT)
+  if (mState == State::unsent) {
     return NS_OK;
-
-  /* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT.  See bug 361773.
+  }
+
+  /* Apparently, Abort() should set State::unsent.  See bug 361773.
      XHR2 spec says this is correct. */
-  if (mState & XML_HTTP_REQUEST_ABORTED) {
+  if (mFlagAborted) {
     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
 
     return NS_ERROR_UNEXPECTED;
   }
 
   // Don't do anything if we have timed out.
-  if (mState & XML_HTTP_REQUEST_TIMED_OUT) {
+  if (mFlagTimedOut) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
 
   nsresult status;
   request->GetStatus(&status);
   mErrorLoad = mErrorLoad || NS_FAILED(status);
 
-  if (mUpload && !mUploadComplete && !mErrorLoad &&
-      (mState & XML_HTTP_REQUEST_ASYNC)) {
+  if (mUpload && !mUploadComplete && !mErrorLoad && !mFlagSynchronous) {
     if (mProgressTimerIsActive) {
       mProgressTimerIsActive = false;
       mProgressNotifier->Cancel();
     }
     if (mUploadTransferred < mUploadTotal) {
       mUploadTransferred = mUploadTotal;
       mProgressSinceLastProgressEvent = true;
       mUploadLengthComputable = true;
       MaybeDispatchProgressEvents(true);
     }
     mUploadComplete = true;
-    DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOAD_STR),
+    DispatchProgressEvent(mUpload, ProgressEventType::load,
                           true, mUploadTotal, mUploadTotal);
   }
 
   mContext = ctxt;
-  mState |= XML_HTTP_REQUEST_PARSEBODY;
-  ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
+  mFlagParseBody = true;
+  ChangeState(State::headers_received);
 
   ResetResponse();
 
   if (!mOverrideMimeType.IsEmpty()) {
     channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
   }
 
   DetectCharset();
@@ -1885,38 +1855,38 @@ XMLHttpRequestMainThread::OnStartRequest
 
     if ((mResponseType == XMLHttpRequestResponseType::Document) &&
         type.EqualsLiteral("text/html")) {
       // HTML parsing is only supported for responseType == "document" to
       // avoid running the parser and, worse, populating responseXML for
       // legacy users of XHR who use responseType == "" for retrieving the
       // responseText of text/html resources. This legacy case is so common
       // that it's not useful to emit a warning about it.
-      if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
+      if (mFlagSynchronous) {
         // We don't make cool new features available in the bad synchronous
         // mode. The synchronous mode is for legacy only.
         mWarnAboutSyncHtml = true;
-        mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+        mFlagParseBody = false;
       } else {
         mIsHtml = true;
       }
     } else if (!(type.EqualsLiteral("text/xml") ||
                  type.EqualsLiteral("application/xml") ||
                  type.RFind("+xml", true, -1, 4) != kNotFound)) {
       // Follow https://xhr.spec.whatwg.org/
       // If final MIME type is not null, text/html, text/xml, application/xml,
       // or does not end in +xml, return null.
-      mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+      mFlagParseBody = false;
     }
   } else {
     // The request failed, so we shouldn't be parsing anyway
-    mState &= ~XML_HTTP_REQUEST_PARSEBODY;
+    mFlagParseBody = false;
   }
 
-  if (mState & XML_HTTP_REQUEST_PARSEBODY) {
+  if (mFlagParseBody) {
     nsCOMPtr<nsIURI> baseURI, docURI;
     rv = mChannel->GetURI(getter_AddRefs(docURI));
     NS_ENSURE_SUCCESS(rv, rv);
     baseURI = docURI;
 
     nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
     nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
     if (doc) {
@@ -1978,18 +1948,17 @@ XMLHttpRequestMainThread::OnStartRequest
 
     mXMLParserStreamListener = listener;
     rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // We won't get any progress events anyway if we didn't have progress
   // events when starting the request - so maybe no need to start timer here.
-  if (NS_SUCCEEDED(rv) &&
-      (mState & XML_HTTP_REQUEST_ASYNC) &&
+  if (NS_SUCCEEDED(rv) && !mFlagSynchronous &&
       HasListenersFor(nsGkAtoms::onprogress)) {
     StartProgressEventTimer();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -2008,26 +1977,25 @@ XMLHttpRequestMainThread::OnStopRequest(
   if (mRequestObserver) {
     NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
     mFirstStartRequestSeen = false;
     mRequestObserver->OnStopRequest(request, ctxt, status);
   }
 
   // make sure to notify the listener if we were aborted
   // XXX in fact, why don't we do the cleanup below in this case??
-  // XML_HTTP_REQUEST_UNSENT is for abort calls.  See OnStartRequest above.
-  if ((mState & XML_HTTP_REQUEST_UNSENT) ||
-      (mState & XML_HTTP_REQUEST_TIMED_OUT)) {
+  // State::unsent is for abort calls.  See OnStartRequest above.
+  if (mState == State::unsent || mFlagTimedOut) {
     if (mXMLParserStreamListener)
       (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
     return NS_OK;
   }
 
   // Is this good enough here?
-  if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
+  if (mXMLParserStreamListener && mFlagParseBody) {
     mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
   }
 
   mXMLParserStreamListener = nullptr;
   mContext = nullptr;
 
   // If we're received data since the last progress event, make sure to fire
   // an event for it, except in the HTML case, defer the last progress event
@@ -2081,46 +2049,45 @@ XMLHttpRequestMainThread::OnStopRequest(
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
 
   channel->SetNotificationCallbacks(nullptr);
   mNotificationCallbacks = nullptr;
   mChannelEventSink = nullptr;
   mProgressEventSink = nullptr;
 
-  mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
+  mFlagSyncLooping = false;
 
   if (NS_FAILED(status)) {
     // This can happen if the server is unreachable. Other possible
     // reasons are that the user leaves the page or hits the ESC key.
 
     mErrorLoad = true;
     mResponseXML = nullptr;
   }
 
   // If we're uninitialized at this point, we encountered an error
   // earlier and listeners have already been notified. Also we do
   // not want to do this if we already completed.
-  if (mState & (XML_HTTP_REQUEST_UNSENT |
-                XML_HTTP_REQUEST_DONE)) {
+  if (mState == State::unsent || mState == State::done) {
     return NS_OK;
   }
 
   if (!mResponseXML) {
     ChangeStateToDone();
     return NS_OK;
   }
   if (mIsHtml) {
-    NS_ASSERTION(!(mState & XML_HTTP_REQUEST_SYNCLOOPING),
+    NS_ASSERTION(!mFlagSyncLooping,
       "We weren't supposed to support HTML parsing with XHR!");
     nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mResponseXML);
     EventListenerManager* manager =
       eventTarget->GetOrCreateListenerManager();
     manager->AddEventListenerByType(new nsXHRParseEndListener(this),
-                                    NS_LITERAL_STRING("DOMContentLoaded"),
+                                    kLiteralString_DOMContentLoaded,
                                     TrustedEventsAtSystemGroupBubble());
     return NS_OK;
   }
   // We might have been sent non-XML data. If that was the case,
   // we should null out the document member. The idea in this
   // check here is that if there is no document element it is not
   // an XML document. We might need a fancier check...
   if (!mResponseXML->GetRootElement()) {
@@ -2134,30 +2101,31 @@ void
 XMLHttpRequestMainThread::ChangeStateToDone()
 {
   if (mIsHtml) {
     // In the HTML case, this has to be deferred, because the parser doesn't
     // do it's job synchronously.
     MaybeDispatchProgressEvents(true);
   }
 
-  ChangeState(XML_HTTP_REQUEST_DONE, true);
+  ChangeState(State::done, true);
+
+  mFlagSend = false;
+
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
 
-  NS_NAMED_LITERAL_STRING(errorStr, ERROR_STR);
-  NS_NAMED_LITERAL_STRING(loadStr, LOAD_STR);
   DispatchProgressEvent(this,
-                        mErrorLoad ? errorStr : loadStr,
+                        mErrorLoad ? ProgressEventType::error : ProgressEventType::load,
                         !mErrorLoad,
                         mLoadTransferred,
                         mErrorLoad ? 0 : mLoadTransferred);
   if (mErrorLoad && mUpload && !mUploadComplete) {
-    DispatchProgressEvent(mUpload, errorStr, true,
+    DispatchProgressEvent(mUpload, ProgressEventType::error, true,
                           mUploadTransferred, mUploadTotal);
   }
 
   if (mErrorLoad) {
     // By nulling out channel here we make it so that Send() can test
     // for that and throw. Also calling the various status
     // methods/members will not throw.
     // This matches what IE does.
@@ -2451,23 +2419,19 @@ XMLHttpRequestMainThread::Send(nsIVarian
 {
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
   PopulateNetworkInterfaceId();
 
   nsresult rv = CheckInnerWindowCorrectness();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Return error if we're already processing a request
-  if (XML_HTTP_REQUEST_SENT & mState) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
-  }
-
-  // Make sure we've been opened
-  if (!mChannel || !(XML_HTTP_REQUEST_OPENED & mState)) {
+  if (mState != State::opened || // Step 1
+      mFlagSend || // Step 2
+      !mChannel) { // Gecko-specific
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
   // in turn keeps STOP button from becoming active.  If the consumer passed in
   // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
   // necko won't generate any progress notifications.
   if (HasListenersFor(nsGkAtoms::onprogress) ||
@@ -2646,18 +2610,17 @@ XMLHttpRequestMainThread::Send(nsIVarian
         // Reset the method to its original value
         httpChannel->SetRequestMethod(method);
       }
     }
   }
 
   ResetResponse();
 
-  if (!IsSystemXHR() && !mIsAnon &&
-      (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS)) {
+  if (!IsSystemXHR() && !mIsAnon && mFlagACwithCredentials) {
     // This is quite sad. We have to create the channel in .open(), since the
     // chrome-only xhr.channel API depends on that. However .withCredentials
     // can be modified after, so we don't know what to set the
     // SEC_COOKIES_INCLUDE flag to when the channel is
     // created. So set it here using a hacky internal API.
 
     // Not doing this for system XHR uses since those don't use CORS.
     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
@@ -2721,24 +2684,24 @@ XMLHttpRequestMainThread::Send(nsIVarian
   }
 
   // We're about to send the request.  Start our timeout.
   mRequestSentTime = PR_Now();
   StartTimeoutTimer();
 
   // Check if we should enabled cross-origin upload listeners.
   if (mUpload && mUpload->HasListeners()) {
-    mState |= XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
+    mFlagHadUploadListenersOnSend = true;
   }
 
   // Set up the preflight if needed
   if (!IsSystemXHR()) {
     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
     loadInfo->SetCorsPreflightInfo(mCORSUnsafeHeaders,
-                                   mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
+                                   mFlagHadUploadListenersOnSend);
   }
 
   mIsMappedArrayBuffer = false;
   if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
       Preferences::GetBool("dom.mapped_arraybuffer.enabled", true)) {
     nsCOMPtr<nsIURI> uri;
     nsAutoCString scheme;
 
@@ -2777,43 +2740,47 @@ XMLHttpRequestMainThread::Send(nsIVarian
     mChannel->SetNotificationCallbacks(mNotificationCallbacks);
     mChannel = nullptr;
 
     return rv;
   }
 
   mWaitingForOnStopRequest = true;
 
+  // Step 8
+  mFlagSend = true;
+
   // If we're synchronous, spin an event loop here and wait
-  if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
-    mState |= XML_HTTP_REQUEST_SYNCLOOPING;
+  if (mFlagSynchronous) {
+    mFlagSyncLooping = true;
 
     nsCOMPtr<nsIDocument> suspendedDoc;
     nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
     if (GetOwner()) {
       if (nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetOwner()->GetOuterWindow()->GetTop()) {
         if (nsCOMPtr<nsPIDOMWindowInner> topInner = topWindow->GetCurrentInnerWindow()) {
           suspendedDoc = topWindow->GetExtantDoc();
           if (suspendedDoc) {
             suspendedDoc->SuppressEventHandling(nsIDocument::eEvents);
           }
           topWindow->SuspendTimeouts(1, false);
           resumeTimeoutRunnable = new nsResumeTimeoutsEvent(topInner);
         }
       }
     }
 
-    ChangeState(XML_HTTP_REQUEST_SENT);
+    if (mProgressNotifier) {
+      mProgressTimerIsActive = false;
+      mProgressNotifier->Cancel();
+    }
 
     {
       nsAutoSyncOperation sync(suspendedDoc);
-      // Note, calling ChangeState may have cleared
-      // XML_HTTP_REQUEST_SYNCLOOPING flag.
       nsIThread *thread = NS_GetCurrentThread();
-      while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
+      while (mFlagSyncLooping) {
         if (!NS_ProcessNextEvent(thread)) {
           rv = NS_ERROR_UNEXPECTED;
           break;
         }
       }
     }
 
     if (suspendedDoc) {
@@ -2824,42 +2791,46 @@ XMLHttpRequestMainThread::Send(nsIVarian
     if (resumeTimeoutRunnable) {
       NS_DispatchToCurrentThread(resumeTimeoutRunnable);
     }
   } else {
     // Now that we've successfully opened the channel, we can change state.  Note
     // that this needs to come after the AsyncOpen() and rv check, because this
     // can run script that would try to restart this request, and that could end
     // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
-    ChangeState(XML_HTTP_REQUEST_SENT);
+    if (mProgressNotifier) {
+      mProgressTimerIsActive = false;
+      mProgressNotifier->Cancel();
+    }
+
     if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
       StartProgressEventTimer();
     }
-    DispatchProgressEvent(this, NS_LITERAL_STRING(LOADSTART_STR), false,
+    DispatchProgressEvent(this, ProgressEventType::loadstart, false,
                           0, 0);
     if (mUpload && !mUploadComplete) {
-      DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOADSTART_STR), true,
+      DispatchProgressEvent(mUpload, ProgressEventType::loadstart, true,
                             0, mUploadTotal);
     }
   }
 
   if (!mChannel) {
     return NS_ERROR_FAILURE;
   }
 
   return rv;
 }
 
 // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SetRequestHeader(const nsACString& header,
                                            const nsACString& value)
 {
-  // Step 1 and 2
-  if (!(mState & XML_HTTP_REQUEST_OPENED)) {
+  // Steps 1 and 2
+  if (mState != State::opened || mFlagSend) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
   NS_ASSERTION(mChannel, "mChannel must be valid if we're OPENED.");
 
   // Step 3
   // Make sure we don't store an invalid header name in mCORSUnsafeHeaders
   if (!NS_IsValidHTTPToken(header)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
@@ -2958,18 +2929,17 @@ XMLHttpRequestMainThread::SetTimeout(uin
   ErrorResult rv;
   SetTimeout(aTimeout, rv);
   return rv.StealNSResult();
 }
 
 void
 XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
 {
-  if (!(mState & (XML_HTTP_REQUEST_ASYNC | XML_HTTP_REQUEST_UNSENT)) &&
-      HasOrHasHadOwner()) {
+  if (mFlagSynchronous && mState != State::unsent && HasOrHasHadOwner()) {
     /* Timeout is not supported for synchronous requests with an owning window,
        per XHR2 spec. */
     LogMessage("TimeoutSyncXHRWarning", GetOwner());
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
 
   mTimeoutMilliseconds = aTimeout;
@@ -2978,17 +2948,17 @@ XMLHttpRequestMainThread::SetTimeout(uin
   }
 }
 
 void
 XMLHttpRequestMainThread::StartTimeoutTimer()
 {
   MOZ_ASSERT(mRequestSentTime,
              "StartTimeoutTimer mustn't be called before the request was sent!");
-  if (mState & XML_HTTP_REQUEST_DONE) {
+  if (mState == State::done) {
     // do nothing!
     return;
   }
 
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
 
@@ -3014,35 +2984,36 @@ XMLHttpRequestMainThread::GetReadyState(
   *aState = ReadyState();
   return NS_OK;
 }
 
 uint16_t
 XMLHttpRequestMainThread::ReadyState() const
 {
   // Translate some of our internal states for external consumers
-  if (mState & XML_HTTP_REQUEST_UNSENT) {
-    return UNSENT;
-  }
-  if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
-    return OPENED;
+  switch(mState) {
+    case State::unsent:
+      return UNSENT;
+    case State::opened:
+      return OPENED;
+    case State::headers_received:
+      return HEADERS_RECEIVED;
+    case State::loading:
+      return LOADING;
+    case State::done:
+      return DONE;
+    default:
+      MOZ_CRASH("Unknown state");
   }
-  if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
-    return HEADERS_RECEIVED;
-  }
-  if (mState & XML_HTTP_REQUEST_LOADING) {
-    return LOADING;
-  }
-  MOZ_ASSERT(mState & XML_HTTP_REQUEST_DONE);
-  return DONE;
+  return 0;
 }
 
 void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
 {
-  if ((mState & XML_HTTP_REQUEST_LOADING) || (mState & XML_HTTP_REQUEST_DONE)) {
+  if (mState == State::loading || mState == State::done) {
     ResetResponse();
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   mOverrideMimeType = aMimeType;
 }
 
@@ -3059,36 +3030,32 @@ XMLHttpRequestMainThread::GetMozBackgrou
 {
   *_retval = MozBackgroundRequest();
   return NS_OK;
 }
 
 bool
 XMLHttpRequestMainThread::MozBackgroundRequest() const
 {
-  return !!(mState & XML_HTTP_REQUEST_BACKGROUND);
+  return mFlagBackgroundRequest;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest)
 {
   if (!IsSystemXHR()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
+  if (mState != State::unsent) {
     // Can't change this while we're in the middle of something.
      return NS_ERROR_IN_PROGRESS;
   }
 
-  if (aMozBackgroundRequest) {
-    mState |= XML_HTTP_REQUEST_BACKGROUND;
-  } else {
-    mState &= ~XML_HTTP_REQUEST_BACKGROUND;
-  }
+  mFlagBackgroundRequest = aMozBackgroundRequest;
 
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest,
                                                   ErrorResult& aRv)
 {
@@ -3101,75 +3068,59 @@ XMLHttpRequestMainThread::GetWithCredent
 {
   *_retval = WithCredentials();
   return NS_OK;
 }
 
 bool
 XMLHttpRequestMainThread::WithCredentials() const
 {
-  return !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
+  return mFlagACwithCredentials;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials)
 {
   ErrorResult rv;
   SetWithCredentials(aWithCredentials, rv);
   return rv.StealNSResult();
 }
 
 void
 XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
 {
   // Return error if we're already processing a request.  Note that we can't use
   // ReadyState() here, because it can't differentiate between "opened" and
   // "sent", so we use mState directly.
-  if ((!(mState & XML_HTTP_REQUEST_UNSENT) &&
-       !(mState & XML_HTTP_REQUEST_OPENED)) ||
-      mIsAnon) {
+  if ((mState != State::unsent && mState != State::opened) ||
+      mFlagSend || mIsAnon) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  if (aWithCredentials) {
-    mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
-  } else {
-    mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
-  }
+  mFlagACwithCredentials = aWithCredentials;
 }
 
 nsresult
-XMLHttpRequestMainThread::ChangeState(uint32_t aState, bool aBroadcast)
+XMLHttpRequestMainThread::ChangeState(State aState, bool aBroadcast)
 {
-  // If we are setting one of the mutually exclusive states,
-  // unset those state bits first.
-  if (aState & XML_HTTP_REQUEST_LOADSTATES) {
-    mState &= ~XML_HTTP_REQUEST_LOADSTATES;
-  }
-  mState |= aState;
+  mState = aState;
   nsresult rv = NS_OK;
 
   if (mProgressNotifier &&
-      !(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
+      aState != State::headers_received && aState != State::loading) {
     mProgressTimerIsActive = false;
     mProgressNotifier->Cancel();
   }
 
-  if ((aState & XML_HTTP_REQUEST_LOADSTATES) &&  // Broadcast load states only
-      aState != XML_HTTP_REQUEST_SENT && // And not internal ones
-      aBroadcast &&
-      (mState & XML_HTTP_REQUEST_ASYNC ||
-       aState & XML_HTTP_REQUEST_OPENED ||
-       aState & XML_HTTP_REQUEST_DONE)) {
-    nsCOMPtr<nsIDOMEvent> event;
-    rv = CreateReadystatechangeEvent(getter_AddRefs(event));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+
+  if (aBroadcast && (!mFlagSynchronous ||
+                     aState == State::opened ||
+                     aState == State::done)) {
+    rv = FireReadystatechangeEvent();
   }
 
   return rv;
 }
 
 /////////////////////////////////////////////////////
 // nsIChannelEventSink methods:
 //
@@ -3246,38 +3197,37 @@ XMLHttpRequestMainThread::MaybeDispatchP
   if (aFinalProgress && mProgressTimerIsActive) {
     mProgressTimerIsActive = false;
     mProgressNotifier->Cancel();
   }
 
   if (mProgressTimerIsActive ||
       !mProgressSinceLastProgressEvent ||
       mErrorLoad ||
-      !(mState & XML_HTTP_REQUEST_ASYNC)) {
+      mFlagSynchronous) {
     return;
   }
 
   if (!aFinalProgress) {
     StartProgressEventTimer();
   }
 
-  // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
-  // XML_HTTP_REQUEST_SENT
-  if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) {
+  // We're in the upload phase while our state is State::opened.
+  if (mState == State::opened) {
     if (mUpload && !mUploadComplete) {
-      DispatchProgressEvent(mUpload, NS_LITERAL_STRING(PROGRESS_STR),
+      DispatchProgressEvent(mUpload, ProgressEventType::progress,
                             mUploadLengthComputable, mUploadTransferred,
                             mUploadTotal);
     }
   } else {
     if (aFinalProgress) {
       mLoadTotal = mLoadTransferred;
     }
     mInLoadProgressEvent = true;
-    DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR),
+    DispatchProgressEvent(this, ProgressEventType::progress,
                           mLoadLengthComputable, mLoadTransferred,
                           mLoadTotal);
     mInLoadProgressEvent = false;
     if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
         mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
       mResponseBody.Truncate();
       mResponseText.Truncate();
       mResultArrayBuffer = nullptr;
@@ -3286,19 +3236,18 @@ XMLHttpRequestMainThread::MaybeDispatchP
   }
 
   mProgressSinceLastProgressEvent = false;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::OnProgress(nsIRequest *aRequest, nsISupports *aContext, int64_t aProgress, int64_t aProgressMax)
 {
-  // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
-  // XML_HTTP_REQUEST_SENT
-  bool upload = !!((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState);
+  // We're in the upload phase while our state is State::opened.
+  bool upload = mState == State::opened;
   // When uploading, OnProgress reports also headers in aProgress and aProgressMax.
   // So, try to remove the headers, if possible.
   bool lengthComputable = (aProgressMax != -1);
   if (upload) {
     int64_t loaded = aProgress;
     if (lengthComputable) {
       int64_t headerSize = aProgressMax - mUploadTotal;
       loaded -= headerSize;
@@ -3333,17 +3282,17 @@ XMLHttpRequestMainThread::OnStatus(nsIRe
 
   return NS_OK;
 }
 
 bool
 XMLHttpRequestMainThread::AllowUploadProgress()
 {
   return !IsCrossSiteCORSRequest() ||
-    (mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
+         mFlagHadUploadListenersOnSend;
 }
 
 /////////////////////////////////////////////////////
 // nsIInterfaceRequestor methods:
 //
 NS_IMETHODIMP
 XMLHttpRequestMainThread::GetInterface(const nsIID & aIID, void **aResult)
 {
@@ -3368,17 +3317,17 @@ XMLHttpRequestMainThread::GetInterface(c
   if (mNotificationCallbacks) {
     rv = mNotificationCallbacks->GetInterface(aIID, aResult);
     if (NS_SUCCEEDED(rv)) {
       NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
       return rv;
     }
   }
 
-  if (mState & XML_HTTP_REQUEST_BACKGROUND) {
+  if (mFlagBackgroundRequest) {
     nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
 
     // Ignore failure to get component, we may not have all its dependencies
     // available
     if (NS_SUCCEEDED(rv)) {
       rv = badCertHandler->GetInterface(aIID, aResult);
       if (NS_SUCCEEDED(rv))
         return rv;
@@ -3470,24 +3419,24 @@ XMLHttpRequestMainThread::GetMozSystem(b
 {
   *aSystem = MozSystem();
   return NS_OK;
 }
 
 void
 XMLHttpRequestMainThread::HandleTimeoutCallback()
 {
-  if (mState & XML_HTTP_REQUEST_DONE) {
+  if (mState == State::done) {
     NS_NOTREACHED("XMLHttpRequestMainThread::HandleTimeoutCallback with completed request");
     // do nothing!
     return;
   }
 
-  CloseRequestWithError(NS_LITERAL_STRING(TIMEOUT_STR),
-                        XML_HTTP_REQUEST_TIMED_OUT);
+  mFlagTimedOut = true;
+  CloseRequestWithError(ProgressEventType::timeout);
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::Notify(nsITimer* aTimer)
 {
   if (mProgressNotifier == aTimer) {
     HandleProgressTimerCallback();
     return NS_OK;
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_XMLHttpRequestMainThread_h
 #define mozilla_dom_XMLHttpRequestMainThread_h
 
+#include <bitset>
 #include "nsAutoPtr.h"
 #include "nsIXMLHttpRequest.h"
 #include "nsISupportsUtils.h"
 #include "nsIURI.h"
 #include "nsIHttpChannel.h"
 #include "nsIDocument.h"
 #include "nsIStreamListener.h"
 #include "nsWeakReference.h"
@@ -122,16 +123,27 @@ class XMLHttpRequestMainThread final : p
                                        public nsSupportsWeakReference,
                                        public nsITimerCallback,
                                        public nsISizeOfEventTarget
 {
   friend class nsXHRParseEndListener;
   friend class nsXMLHttpRequestXPCOMifier;
 
 public:
+  enum class ProgressEventType : uint8_t {
+    loadstart,
+    progress,
+    error,
+    abort,
+    timeout,
+    load,
+    loadend,
+    ENUM_MAX
+  };
+
   XMLHttpRequestMainThread();
 
   void Construct(nsIPrincipal* aPrincipal,
                  nsIGlobalObject* aGlobalObject,
                  nsIURI* aBaseURI = nullptr,
                  nsILoadGroup* aLoadGroup = nullptr)
   {
     MOZ_ASSERT(aPrincipal);
@@ -503,21 +515,21 @@ public:
   }
 
   // We need a GetInterface callable from JS for chrome JS
   virtual void
   GetInterface(JSContext* aCx, nsIJSID* aIID,
                JS::MutableHandle<JS::Value> aRetval,
                ErrorResult& aRv) override;
 
-  // This creates a trusted readystatechange event, which is not cancelable and
+  // This fires a trusted readystatechange event, which is not cancelable and
   // doesn't bubble.
-  nsresult CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent);
+  nsresult FireReadystatechangeEvent();
   void DispatchProgressEvent(DOMEventTargetHelper* aTarget,
-                             const nsAString& aType,
+                             const ProgressEventType aType,
                              bool aLengthComputable,
                              int64_t aLoaded, int64_t aTotal);
 
   // Dispatch the "progress" event on the XHR or XHR.upload object if we've
   // received data since the last "progress" event. Also dispatches
   // "uploadprogress" as needed.
   void MaybeDispatchProgressEvents(bool aFinalProgress);
 
@@ -542,30 +554,40 @@ public:
   {
     sDontWarnAboutSyncXHR = aVal;
   }
   static bool DontWarnAboutSyncXHR()
   {
     return sDontWarnAboutSyncXHR;
   }
 protected:
+  // XHR states are meant to mirror the XHR2 spec:
+  //   https://xhr.spec.whatwg.org/#states
+  enum class State : uint8_t {
+    unsent,           // object has been constructed.
+    opened,           // open() has been successfully invoked.
+    headers_received, // redirects followed and response headers received.
+    loading,          // response body is being received.
+    done,             // data transfer concluded, whether success or error.
+  };
+
   nsresult DetectCharset();
   nsresult AppendToResponseText(const char * aBuffer, uint32_t aBufferLen);
   static NS_METHOD StreamReaderFunc(nsIInputStream* in,
                 void* closure,
                 const char* fromRawSegment,
                 uint32_t toOffset,
                 uint32_t count,
                 uint32_t *writeCount);
   nsresult CreateResponseParsedJSON(JSContext* aCx);
   void CreatePartialBlob(ErrorResult& aRv);
   bool CreateDOMBlob(nsIRequest *request);
   // Change the state of the object with this. The broadcast argument
   // determines if the onreadystatechange listener should be called.
-  nsresult ChangeState(uint32_t aState, bool aBroadcast = true);
+  nsresult ChangeState(State aState, bool aBroadcast = true);
   already_AddRefed<nsILoadGroup> GetLoadGroup() const;
   nsIURI *GetBaseURI();
 
   already_AddRefed<nsIHttpChannel> GetCurrentHttpChannel();
   already_AddRefed<nsIJARChannel> GetCurrentJARChannel();
 
   bool IsSystemXHR() const;
 
@@ -659,17 +681,33 @@ protected:
   nsCOMPtr<nsIChannelEventSink> mChannelEventSink;
   nsCOMPtr<nsIProgressEventSink> mProgressEventSink;
 
   nsIRequestObserver* mRequestObserver;
 
   nsCOMPtr<nsIURI> mBaseURI;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
 
-  uint32_t mState;
+  State mState;
+
+  bool mFlagSynchronous;
+  bool mFlagAborted;
+  bool mFlagParseBody;
+  bool mFlagSyncLooping;
+  bool mFlagBackgroundRequest;
+  bool mFlagHadUploadListenersOnSend;
+  bool mFlagACwithCredentials;
+  bool mFlagTimedOut;
+  bool mFlagDeleted;
+
+  // The XHR2 spec's send() flag. Set when the XHR begins uploading, until it
+  // finishes downloading (or an error/abort has occurred during either phase).
+  // Used to guard against the user trying to alter headers/etc when it's too
+  // late, and ensure the XHR only handles one in-flight request at once.
+  bool mFlagSend;
 
   RefPtr<XMLHttpRequestUpload> mUpload;
   int64_t mUploadTransferred;
   int64_t mUploadTotal;
   bool mUploadLengthComputable;
   bool mUploadComplete;
   bool mProgressSinceLastProgressEvent;
 
@@ -709,20 +747,18 @@ protected:
   // that this request is associated with.
   nsCString mNetworkInterfaceId;
 
   /**
    * Close the XMLHttpRequest's channels and dispatch appropriate progress
    * events.
    *
    * @param aType The progress event type.
-   * @param aFlag A XML_HTTP_REQUEST_* state flag defined in
-   *              XMLHttpRequestMainthread.cpp.
    */
-  void CloseRequestWithError(const nsAString& aType, const uint32_t aFlag);
+  void CloseRequestWithError(const ProgressEventType aType);
 
   bool mFirstStartRequestSeen;
   bool mInLoadProgressEvent;
 
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIChannel> mNewRedirectChannel;
 
   JS::Heap<JS::Value> mResultJSON;
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -872,22 +872,19 @@ js::num_valueOf(JSContext* cx, unsigned 
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsNumber, num_valueOf_impl>(cx, args);
 }
 
 static const unsigned MAX_PRECISION = 100;
 
 static bool
-ComputePrecisionInRange(JSContext* cx, int minPrecision, int maxPrecision, HandleValue v,
+ComputePrecisionInRange(JSContext* cx, int minPrecision, int maxPrecision, double prec,
                         int* precision)
 {
-    double prec;
-    if (!ToInteger(cx, v, &prec))
-        return false;
     if (minPrecision <= prec && prec <= maxPrecision) {
         *precision = int(prec);
         return true;
     }
 
     ToCStringBuf cbuf;
     if (char* numStr = NumberToCString(cx, &cbuf, prec, 10))
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PRECISION_RANGE, numStr);
@@ -918,75 +915,154 @@ MOZ_ALWAYS_INLINE bool
 num_toFixed_impl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(IsNumber(args.thisv()));
 
     int precision;
     if (args.length() == 0) {
         precision = 0;
     } else {
-        if (!ComputePrecisionInRange(cx, -20, MAX_PRECISION, args[0], &precision))
+        double prec = 0;
+        if (!ToInteger(cx, args[0], &prec))
+            return false;
+
+        if (!ComputePrecisionInRange(cx, -20, MAX_PRECISION, prec, &precision))
             return false;
     }
 
     return DToStrResult(cx, Extract(args.thisv()), DTOSTR_FIXED, precision, args);
 }
 
 static bool
 num_toFixed(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsNumber, num_toFixed_impl>(cx, args);
 }
 
+// ES 2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f 20.1.3.2.
 MOZ_ALWAYS_INLINE bool
 num_toExponential_impl(JSContext* cx, const CallArgs& args)
 {
+    // Step 1.
     MOZ_ASSERT(IsNumber(args.thisv()));
+    double d = Extract(args.thisv());
 
-    JSDToStrMode mode;
-    int precision;
-    if (!args.hasDefined(0)) {
-        mode = DTOSTR_STANDARD_EXPONENTIAL;
-        precision = 0;
-    } else {
+    // Step 2.
+    double prec = 0;
+    JSDToStrMode mode = DTOSTR_STANDARD_EXPONENTIAL;
+    if (args.hasDefined(0)) {
         mode = DTOSTR_EXPONENTIAL;
-        if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, args[0], &precision))
+        if (!ToInteger(cx, args[0], &prec))
             return false;
     }
 
-    return DToStrResult(cx, Extract(args.thisv()), mode, precision + 1, args);
+    // Step 3.
+    MOZ_ASSERT_IF(!args.hasDefined(0), prec == 0);
+
+    // Step 4.
+    if (mozilla::IsNaN(d)) {
+        args.rval().setString(cx->names().NaN);
+        return true;
+    }
+
+    // Steps 5-7.
+    if (mozilla::IsInfinite(d)) {
+        if (d > 0) {
+            args.rval().setString(cx->names().Infinity);
+            return true;
+        }
+
+        StringBuffer sb(cx);
+        if (!sb.append("-"))
+            return false;
+
+        if (!sb.append(cx->names().Infinity))
+            return false;
+
+        JSString* str = sb.finishString();
+        if (!str)
+            return false;
+
+        args.rval().setString(str);
+        return true;
+    }
+
+    // Steps 8-15.
+    int precision = 0;
+    if (mode == DTOSTR_EXPONENTIAL) {
+        if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision))
+            return false;
+    }
+
+    return DToStrResult(cx, d, mode, precision + 1, args);
 }
 
 static bool
 num_toExponential(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsNumber, num_toExponential_impl>(cx, args);
 }
 
+// ES 2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f 20.1.3.5.
 MOZ_ALWAYS_INLINE bool
 num_toPrecision_impl(JSContext* cx, const CallArgs& args)
 {
+    // Step 1.
     MOZ_ASSERT(IsNumber(args.thisv()));
-
     double d = Extract(args.thisv());
 
+    // Step 2.
     if (!args.hasDefined(0)) {
         JSString* str = NumberToStringWithBase<CanGC>(cx, d, 10);
         if (!str) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
         args.rval().setString(str);
         return true;
     }
 
-    int precision;
-    if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, args[0], &precision))
+    // Step 3.
+    double prec = 0;
+    if (!ToInteger(cx, args[0], &prec))
+        return false;
+
+    // Step 4.
+    if (mozilla::IsNaN(d)) {
+        args.rval().setString(cx->names().NaN);
+        return true;
+    }
+
+    // Steps 5-7.
+    if (mozilla::IsInfinite(d)) {
+        if (d > 0) {
+            args.rval().setString(cx->names().Infinity);
+            return true;
+        }
+
+        StringBuffer sb(cx);
+        if (!sb.append("-"))
+            return false;
+
+        if (!sb.append(cx->names().Infinity))
+            return false;
+
+        JSString* str = sb.finishString();
+        if (!str)
+            return false;
+
+        args.rval().setString(str);
+        return true;
+    }
+
+    // Steps 8-14.
+    int precision = 0;
+    if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, prec, &precision))
         return false;
 
     return DToStrResult(cx, d, DTOSTR_PRECISION, precision, args);
 }
 
 static bool
 num_toPrecision(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/tests/ecma_3/Number/15.7.4.6-1.js
+++ b/js/src/tests/ecma_3/Number/15.7.4.6-1.js
@@ -1,9 +1,8 @@
-// |reftest| fails
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 /*
  * Date: 2001-07-15
  *
--- a/js/src/tests/ecma_3/Number/15.7.4.7-1.js
+++ b/js/src/tests/ecma_3/Number/15.7.4.7-1.js
@@ -1,9 +1,8 @@
-// |reftest| fails
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 /*
  * Date: 2001-07-15
  *
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Number/20.1.3.2-toExponential.js
@@ -0,0 +1,44 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+
+var BUGNUMBER = 818617;
+var summary = "ECMAScript 2017 Draft ECMA-262 Section 20.1.3.2: Number.prototype.toExponential";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+// With NaN, fractionDigits out of range.
+assertEq(Number.prototype.toExponential.call(NaN, 555), 'NaN');
+
+// With NaN fractionDigits in range.
+assertEq(Number.prototype.toExponential.call(NaN, 5), 'NaN');
+
+// With Infinity, fractionDigits out of range.
+assertEq(Number.prototype.toExponential.call(Infinity, 555), 'Infinity');
+
+// With Infinity, fractionDigits in range.
+assertEq(Number.prototype.toExponential.call(Infinity, 5), 'Infinity');
+
+// With -Infinity, fractionDigits out of range.
+assertEq(Number.prototype.toExponential.call(-Infinity, 555), '-Infinity');
+
+// With -Infinity, fractionDigits in range.
+assertEq(Number.prototype.toExponential.call(-Infinity, 5), '-Infinity');
+
+// With NaN, function assigning a value.
+let x = 10;
+assertEq(Number.prototype.toExponential.call(NaN, { valueOf() { x = 20; return 1; } }), 'NaN');
+assertEq(x, 20);
+
+// With NaN, function throwing an exception.
+assertThrows(() => Number.prototype.toExponential.call(NaN, { valueOf() { throw "hello"; } }));
+
+if (typeof reportCompare === "function") {
+  reportCompare(true, true);
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Number/20.1.3.2-toPrecision.js
@@ -0,0 +1,44 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+
+var BUGNUMBER = 818617;
+var summary = "ECMAScript 2017 Draft ECMA-262 Section 20.1.3.5: Number.prototype.toPrecision";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+// With NaN, fractionDigits out of range.
+assertEq(Number.prototype.toPrecision.call(NaN, 555), 'NaN');
+
+// With NaN, fractionDigits in range.
+assertEq(Number.prototype.toPrecision.call(NaN, 5), 'NaN');
+
+// With Infinity, fractionDigits out of range.
+assertEq(Number.prototype.toPrecision.call(Infinity, 555), 'Infinity');
+
+// With Infinity, fractionDigits in range.
+assertEq(Number.prototype.toPrecision.call(Infinity, 5), 'Infinity');
+
+// With -Infinity, fractionDigits out of range.
+assertEq(Number.prototype.toPrecision.call(-Infinity, 555), '-Infinity');
+
+// With -Infinity, fractionDigits in range.
+assertEq(Number.prototype.toPrecision.call(-Infinity, 5), '-Infinity');
+
+// With NaN, function assigning a value.
+let x = 10;
+assertEq(Number.prototype.toPrecision.call(NaN, { valueOf() { x = 20; return 1; } }), 'NaN');
+assertEq(x, 20);
+
+// With NaN, function throwing an exception.
+assertThrows(() => Number.prototype.toPrecision.call(NaN, { valueOf() { throw "hello"; } }));
+
+if (typeof reportCompare === "function") {
+  reportCompare(true, true);
+}
+
--- a/js/src/threading/posix/ConditionVariable.cpp
+++ b/js/src/threading/posix/ConditionVariable.cpp
@@ -18,18 +18,18 @@
 #include "threading/posix/MutexPlatformData.h"
 
 using mozilla::CheckedInt;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 
 static const long NanoSecPerSec = 1000000000;
 
-// Android has the clock functions, but not pthread_condattr_setclock.
-#if defined(HAVE_CLOCK_MONOTONIC) && !defined(__ANDROID__)
+// Android & macOS 10.12 has the clock functions, but not pthread_condattr_setclock.
+#if defined(HAVE_CLOCK_MONOTONIC) && !defined(__ANDROID__) && !defined(__APPLE__)
 # define USE_CLOCK_API
 #endif
 
 #ifdef USE_CLOCK_API
 // The C++ specification defines std::condition_variable::wait_for in terms of
 // std::chrono::steady_clock, which is closest to CLOCK_MONOTONIC.
 static const clockid_t WhichClock = CLOCK_MONOTONIC;
 
--- a/layout/generic/MathMLTextRunFactory.h
+++ b/layout/generic/MathMLTextRunFactory.h
@@ -1,42 +1,42 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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/. */
 
 #ifndef MATHMLTEXTRUNFACTORY_H_
 #define MATHMLTEXTRUNFACTORY_H_
 
-#include "nsAutoPtr.h"
+#include "mozilla/UniquePtr.h"
 #include "nsTextRunTransformations.h"
 
 /**
  * Builds textruns that render their text with MathML specific renderings.
  */
 class MathMLTextRunFactory : public nsTransformingTextRunFactory {
 public:
-  MathMLTextRunFactory(nsTransformingTextRunFactory* aInnerTransformingTextRunFactory,
+  MathMLTextRunFactory(UniquePtr<nsTransformingTextRunFactory> aInnerTransformingTextRunFactory,
                        uint32_t aFlags, uint8_t aSSTYScriptLevel,
                        float aFontInflation)
-    : mInnerTransformingTextRunFactory(aInnerTransformingTextRunFactory),
+    : mInnerTransformingTextRunFactory(Move(aInnerTransformingTextRunFactory)),
       mFlags(aFlags),
       mFontInflation(aFontInflation),
       mSSTYScriptLevel(aSSTYScriptLevel) {}
 
   virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
                               mozilla::gfx::DrawTarget* aRefDrawTarget,
                               gfxMissingFontRecorder* aMFR) override;
   enum {
     // Style effects which may override single character <mi> behaviour
     MATH_FONT_STYLING_NORMAL   = 0x1, // fontstyle="normal" has been set.
     MATH_FONT_WEIGHT_BOLD      = 0x2, // fontweight="bold" has been set.
     MATH_FONT_FEATURE_DTLS     = 0x4, // font feature dtls should be set
   };
 
 protected:
-  nsAutoPtr<nsTransformingTextRunFactory> mInnerTransformingTextRunFactory;
+  UniquePtr<nsTransformingTextRunFactory> mInnerTransformingTextRunFactory;
   uint32_t mFlags;
   float mFontInflation;
   uint8_t mSSTYScriptLevel;
 };
 
 #endif /*MATHMLTEXTRUNFACTORY_H_*/
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -8,16 +8,17 @@
  * rendering object for CSS display:block, inline-block, and list-item
  * boxes, also used for various anonymous boxes
  */
 
 #include "nsBlockFrame.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
 
 #include "nsCOMPtr.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "nsBlockReflowContext.h"
 #include "nsBlockReflowState.h"
 #include "nsBulletFrame.h"
 #include "nsFontMetrics.h"
 #include "nsLineBox.h"
@@ -32,17 +33,16 @@
 #include "nsGkAtoms.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #include "mozilla/Snprintf.h"
 #include "nsFloatManager.h"
 #include "prenv.h"
 #include "plstr.h"
 #include "nsError.h"
-#include "nsAutoPtr.h"
 #include "nsIScrollableFrame.h"
 #include <algorithm>
 #ifdef ACCESSIBILITY
 #include "nsIDOMHTMLDocument.h"
 #endif
 #include "nsLayoutUtils.h"
 #include "nsDisplayList.h"
 #include "nsCSSAnonBoxes.h"
@@ -4754,17 +4754,17 @@ nsBlockFrame::DrainOverflowLines()
 
   // Now append our own overflow lines.
   return DrainSelfOverflowList() || didFindOverflow;
 }
 
 bool
 nsBlockFrame::DrainSelfOverflowList()
 {
-  nsAutoPtr<FrameLines> ourOverflowLines(RemoveOverflowLines());
+  UniquePtr<FrameLines> ourOverflowLines(RemoveOverflowLines());
   if (!ourOverflowLines) {
     return false;
   }
 
   // No need to reparent frames in our own overflow lines/oofs, because they're
   // already ours. But we should put overflow floats back in mFloats.
   // (explicit scope to remove the OOF list before VerifyOverflowSituation)
   {
@@ -6530,17 +6530,17 @@ nsBlockFrame::BuildDisplayList(nsDisplay
       if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
          BuildDisplayListForChild(aBuilder, f, aDirtyRect, aLists);
     }
   }
 
   aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
 
   // Prepare for text-overflow processing.
-  nsAutoPtr<TextOverflow> textOverflow(
+  UniquePtr<TextOverflow> textOverflow(
     TextOverflow::WillProcessLines(aBuilder, this));
 
   // We'll collect our lines' display items here, & then append this to aLists.
   nsDisplayListCollection linesDisplayListCollection;
 
   // Don't use the line cursor if we might have a descendant placeholder ...
   // it might skip lines that contain placeholders but don't themselves
   // intersect with the dirty area.
@@ -6559,30 +6559,30 @@ nsBlockFrame::BuildDisplayList(nsDisplay
       nsRect lineArea = line->GetVisualOverflowArea();
       if (!lineArea.IsEmpty()) {
         // Because we have a cursor, the combinedArea.ys are non-decreasing.
         // Once we've passed aDirtyRect.YMost(), we can never see it again.
         if (lineArea.y >= aDirtyRect.YMost()) {
           break;
         }
         DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
-                    linesDisplayListCollection, this, textOverflow);
+                    linesDisplayListCollection, this, textOverflow.get());
       }
     }
   } else {
     bool nonDecreasingYs = true;
     int32_t lineCount = 0;
     nscoord lastY = INT32_MIN;
     nscoord lastYMost = INT32_MIN;
     for (line_iterator line = begin_lines();
          line != line_end;
          ++line) {
       nsRect lineArea = line->GetVisualOverflowArea();
       DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
-                  linesDisplayListCollection, this, textOverflow);
+                  linesDisplayListCollection, this, textOverflow.get());
       if (!lineArea.IsEmpty()) {
         if (lineArea.y < lastY
             || lineArea.YMost() < lastYMost) {
           nonDecreasingYs = false;
         }
         lastY = lineArea.y;
         lastYMost = lineArea.YMost();
       }
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -2,17 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 
 /* This Source Code is subject to the terms of the Mozilla Public License
  * version 2.0 (the "License"). You can obtain a copy of the License at
  * http://mozilla.org/MPL/2.0/. */
 
 /* rendering object for CSS "display: flex" */
 
-#include "nsAutoPtr.h"
+#include "mozilla/UniquePtr.h"
 #include "nsFlexContainerFrame.h"
 #include "nsContentUtils.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsDisplayList.h"
 #include "nsIFrameInlines.h"
 #include "nsLayoutUtils.h"
 #include "nsPlaceholderFrame.h"
 #include "nsPresContext.h"
@@ -1172,17 +1172,17 @@ IsOrderLEQ(nsIFrame* aFrame1,
 
 bool
 nsFlexContainerFrame::IsHorizontal()
 {
   const FlexboxAxisTracker axisTracker(this, GetWritingMode());
   return axisTracker.IsMainAxisHorizontal();
 }
 
-FlexItem*
+UniquePtr<FlexItem>
 nsFlexContainerFrame::GenerateFlexItemForChild(
   nsPresContext* aPresContext,
   nsIFrame*      aChildFrame,
   const nsHTMLReflowState& aParentReflowState,
   const FlexboxAxisTracker& aAxisTracker)
 {
   // Create temporary reflow state just for sizing -- to get hypothetical
   // main-size and the computed values of min / max main-size property.
@@ -1282,22 +1282,22 @@ nsFlexContainerFrame::GenerateFlexItemFo
         tentativeCrossSize = std::max(tentativeCrossSize, widgetCrossMinSize);
       }
       crossMinSize = std::max(crossMinSize, widgetCrossMinSize);
       crossMaxSize = std::max(crossMaxSize, widgetCrossMinSize);
     }
   }
 
   // Construct the flex item!
-  FlexItem* item = new FlexItem(childRS,
-                                flexGrow, flexShrink, flexBaseSize,
-                                mainMinSize, mainMaxSize,
-                                tentativeCrossSize,
-                                crossMinSize, crossMaxSize,
-                                aAxisTracker);
+  auto item = MakeUnique<FlexItem>(childRS,
+                                   flexGrow, flexShrink, flexBaseSize,
+                                   mainMinSize, mainMaxSize,
+                                   tentativeCrossSize,
+                                   crossMinSize, crossMaxSize,
+                                   aAxisTracker);
 
   // If we're inflexible, we can just freeze to our hypothetical main-size
   // up-front. Similarly, if we're a fixed-size widget, we only have one
   // valid size, so we freeze to keep ourselves from flexing.
   if (isFixedSizeWidget || (flexGrow == 0.0f && flexShrink == 0.0f)) {
     item->Freeze();
   }
 
@@ -3426,23 +3426,23 @@ nsFlexContainerFrame::GenerateFlexLines(
 
   for (nsIFrame* childFrame : mFrames) {
     // Honor "page-break-before", if we're multi-line and this line isn't empty:
     if (!isSingleLine && !curLine->IsEmpty() &&
         childFrame->StyleDisplay()->mBreakBefore) {
       curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
     }
 
-    nsAutoPtr<FlexItem> item;
+    UniquePtr<FlexItem> item;
     if (nextStrutIdx < aStruts.Length() &&
         aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
 
       // Use the simplified "strut" FlexItem constructor:
-      item = new FlexItem(childFrame, aStruts[nextStrutIdx].mStrutCrossSize,
-                          aReflowState.GetWritingMode());
+      item = MakeUnique<FlexItem>(childFrame, aStruts[nextStrutIdx].mStrutCrossSize,
+                                  aReflowState.GetWritingMode());
       nextStrutIdx++;
     } else {
       item = GenerateFlexItemForChild(aPresContext, childFrame,
                                       aReflowState, aAxisTracker);
     }
 
     nscoord itemInnerHypotheticalMainSize = item->GetMainSize();
     nscoord itemOuterHypotheticalMainSize =
@@ -3455,17 +3455,17 @@ nsFlexContainerFrame::GenerateFlexLines(
         !curLine->IsEmpty() && // No need to wrap at start of a line.
         wrapThreshold < (curLine->GetTotalOuterHypotheticalMainSize() +
                          itemOuterHypotheticalMainSize)) {
       curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
     }
 
     // Add item to current flex line (and update the line's bookkeeping about
     // how large its items collectively are).
-    curLine->AddItem(item.forget(), shouldInsertAtFront,
+    curLine->AddItem(item.release(), shouldInsertAtFront,
                      itemInnerHypotheticalMainSize,
                      itemOuterHypotheticalMainSize);
 
     // Honor "page-break-after", if we're multi-line and have more children:
     if (!isSingleLine && childFrame->GetNextSibling() &&
         childFrame->StyleDisplay()->mBreakAfter) {
       curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
     }
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -6,16 +6,17 @@
  * http://mozilla.org/MPL/2.0/. */
 
 /* rendering object for CSS "display: flex" and "display: -webkit-box" */
 
 #ifndef nsFlexContainerFrame_h___
 #define nsFlexContainerFrame_h___
 
 #include "nsContainerFrame.h"
+#include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 template <class T> class LinkedList;
 class LogicalPoint;
 } // namespace mozilla
 
 nsContainerFrame* NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
                                            nsStyleContext* aContext);
@@ -131,17 +132,17 @@ protected:
    * FlexItem's lifetime.
    *
    * Before returning, this method also processes the FlexItem to resolve its
    * flex basis (including e.g. auto-height) as well as to resolve
    * "min-height:auto", via ResolveAutoFlexBasisAndMinSize(). (Basically, the
    * returned FlexItem will be ready to participate in the "Resolve the
    * Flexible Lengths" step of the Flex Layout Algorithm.)
    */
-  FlexItem* GenerateFlexItemForChild(nsPresContext* aPresContext,
+  mozilla::UniquePtr<FlexItem> GenerateFlexItemForChild(nsPresContext* aPresContext,
                                      nsIFrame* aChildFrame,
                                      const nsHTMLReflowState& aParentReflowState,
                                      const FlexboxAxisTracker& aAxisTracker);
 
   /**
    * This method performs a "measuring" reflow to get the content height of
    * aFlexItem.Frame() (treating it as if it had auto-height), & returns the
    * resulting height.
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -317,16 +317,19 @@ FRAME_STATE_GROUP(GridContainer, nsGridC
 FRAME_STATE_BIT(GridContainer, 20, NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
 
 // True iff some first-in-flow in-flow children were pushed.
 // Note that those child frames may have been removed without this bit
 // being updated for performance reasons, so code shouldn't depend on
 // actually finding any pushed items when this bit is set.
 FRAME_STATE_BIT(GridContainer, 21, NS_STATE_GRID_DID_PUSH_ITEMS)
 
+// True iff computed grid values should be generated on the next reflow
+FRAME_STATE_BIT(GridContainer, 22, NS_STATE_GRID_GENERATE_COMPUTED_VALUES)
+
 // == Frame state bits that apply to SVG frames ===============================
 
 FRAME_STATE_GROUP(SVG, nsISVGChildFrame)
 FRAME_STATE_GROUP(SVG, nsSVGContainerFrame)
 
 FRAME_STATE_BIT(SVG, 20, NS_STATE_IS_OUTER_SVG)
 
 // If this bit is set, we are a <clipPath> element or descendant.
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -1561,16 +1561,62 @@ struct nsGridContainerFrame::Tracks
   {
     MOZ_ASSERT(mCanResolveLineRangeSize);
     MOZ_ASSERT(aRange.Extent() > 0, "grid items cover at least one track");
     nscoord pos, size;
     aRange.ToPositionAndLength(mSizes, &pos, &size);
     return size;
   }
 
+  nsTArray<nsString> GetLineNamesAtIndex(
+    const nsStyleGridTemplate& aGridTemplate,
+    const TrackSizingFunctions& aFunctions,
+    uint32_t aIndex)
+  {
+    nsTArray<nsString> lineNames;
+
+    bool hasRepeatAuto = aGridTemplate.HasRepeatAuto();
+    const nsTArray<nsTArray<nsString>>& lineNameLists(
+        aGridTemplate.mLineNameLists);
+
+    if (!hasRepeatAuto) {
+      if (aIndex < lineNameLists.Length()) {
+        lineNames.AppendElements(lineNameLists[aIndex]);
+      }
+    } else {
+      const uint32_t repeatTrackCount = aFunctions.NumRepeatTracks();
+      const uint32_t repeatAutoStart = aGridTemplate.mRepeatAutoIndex;
+      const uint32_t repeatAutoEnd = (repeatAutoStart + repeatTrackCount);
+      const int32_t repeatEndDelta = int32_t(repeatTrackCount - 1);
+
+      if (aIndex < repeatAutoEnd && aIndex >= repeatAutoStart) {
+        lineNames.AppendElements(aGridTemplate.mRepeatAutoLineNameListBefore);
+      } else if (aIndex <= repeatAutoEnd && aIndex > repeatAutoStart) {
+        lineNames.AppendElements(aGridTemplate.mRepeatAutoLineNameListAfter);
+      } else if (aIndex <= repeatAutoStart) {
+        if (aIndex < lineNameLists.Length()) {
+          lineNames.AppendElements(lineNameLists[aIndex]);
+        }
+        if (aIndex == repeatAutoEnd) {
+          uint32_t i = aIndex + 1;
+          if (i < lineNameLists.Length()) {
+            lineNames.AppendElements(lineNameLists[i]);
+          }
+        }
+      } else if (aIndex >= repeatAutoEnd) {
+        uint32_t i = aIndex - repeatEndDelta;
+        if (i < lineNameLists.Length()) {
+          lineNames.AppendElements(lineNameLists[i]);
+        }
+      }
+    }
+
+    return lineNames;
+  }
+
 #ifdef DEBUG
   void Dump() const
   {
     for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
       printf("  %d: ", i);
       mSizes[i].Dump();
       printf("\n");
     }
@@ -1608,16 +1654,17 @@ struct nsGridContainerFrame::SharedGridD
   Tracks mRows;
   struct RowData {
     nscoord mBase; // the original track size
     nscoord mGap;  // the original gap before a track
   };
   nsTArray<RowData> mOriginalRowData;
   nsTArray<GridItemInfo> mGridItems;
   nsTArray<GridItemInfo> mAbsPosItems;
+  bool mGenerateComputedGridInfo;
 
   /**
    * Only set on the first-in-flow.  Continuations will Initialize() their
    * GridReflowState from it.
    */
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(Prop, SharedGridData)
 };
 
@@ -1727,16 +1774,21 @@ struct MOZ_STACK_CLASS nsGridContainerFr
       for (auto& itemInfo : mSharedGridData->mAbsPosItems) {
         if (itemInfo.mFrame == childFirstInFlow) {
           mAbsPosItems.AppendElement(GridItemInfo(f, itemInfo.mArea));
           break;
         }
       }
       MOZ_ASSERT(mAbsPosItems.Length() == len + 1, "can't find GridItemInfo");
     }
+
+    // Copy in the computed grid info state bit
+    if (mSharedGridData->mGenerateComputedGridInfo) {
+      aGridContainerFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
+    }
   }
 
   /**
    * Calculate our track sizes.
    */
   void CalculateTrackSizes(const Grid&        aGrid,
                            const LogicalSize& aContentBox,
                            IntrinsicISizeType aConstraint);
@@ -5574,95 +5626,147 @@ nsGridContainerFrame::Reflow(nsPresConte
       NS_FRAME_SET_OVERFLOW_INCOMPLETE(aStatus);
       aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
     }
     bSize = 0;
     desiredSize.BSize(wm) = bSize + bp.BStartEnd(wm);
     aDesiredSize.SetSize(wm, desiredSize);
   }
 
-  // Now that we know column and row sizes and positions, set
-  // the ComputedGridTrackInfo.
-
-  // FIXME bug 1229180: Instead of doing this on every reflow, we should only
-  // set these properties if they are needed.
-  uint32_t colTrackCount = gridReflowState.mCols.mSizes.Length();
-  nsTArray<nscoord> colTrackPositions(colTrackCount);
-  nsTArray<nscoord> colTrackSizes(colTrackCount);
-  nsTArray<uint32_t> colTrackStates(colTrackCount);
-  uint32_t col = 0;
-  for (const TrackSize& sz : gridReflowState.mCols.mSizes) {
-    colTrackPositions.AppendElement(sz.mPosition);
-    colTrackSizes.AppendElement(sz.mBase);
-    bool isRepeat = ((col >= gridReflowState.mColFunctions.mRepeatAutoStart) &&
-                     (col < gridReflowState.mColFunctions.mRepeatAutoEnd));
-    colTrackStates.AppendElement(
+  if (HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES)) {
+    // This state bit will never be cleared, since reflow can be called
+    // multiple times in fragmented grids, and it's challenging to scope
+    // the bit to only that sequence of calls. This is relatively harmless
+    // since this bit is only set by accessing a ChromeOnly property, and
+    // therefore can't unduly slow down normal web browsing.
+
+    // Now that we know column and row sizes and positions, set
+    // the ComputedGridTrackInfo and related properties
+
+    uint32_t colTrackCount = gridReflowState.mCols.mSizes.Length();
+    nsTArray<nscoord> colTrackPositions(colTrackCount);
+    nsTArray<nscoord> colTrackSizes(colTrackCount);
+    nsTArray<uint32_t> colTrackStates(colTrackCount);
+    uint32_t col = 0;
+    for (const TrackSize& sz : gridReflowState.mCols.mSizes) {
+      colTrackPositions.AppendElement(sz.mPosition);
+      colTrackSizes.AppendElement(sz.mBase);
+      bool isRepeat = ((col >= gridReflowState.mColFunctions.mRepeatAutoStart) &&
+                       (col < gridReflowState.mColFunctions.mRepeatAutoEnd));
+      colTrackStates.AppendElement(
+          isRepeat ?
+          (uint32_t)mozilla::dom::GridTrackState::Repeat :
+          (uint32_t)mozilla::dom::GridTrackState::Static
+      );
+
+      col++;
+    }
+    ComputedGridTrackInfo* colInfo = new ComputedGridTrackInfo(
+      gridReflowState.mColFunctions.mExplicitGridOffset,
+      gridReflowState.mColFunctions.NumExplicitTracks(),
+      0,
+      col,
+      Move(colTrackPositions),
+      Move(colTrackSizes),
+      Move(colTrackStates));
+    Properties().Set(GridColTrackInfo(), colInfo);
+
+    uint32_t rowTrackCount = gridReflowState.mRows.mSizes.Length();
+    nsTArray<nscoord> rowTrackPositions(rowTrackCount);
+    nsTArray<nscoord> rowTrackSizes(rowTrackCount);
+    nsTArray<uint32_t> rowTrackStates(rowTrackCount);
+    uint32_t row = 0;
+    for (const TrackSize& sz : gridReflowState.mRows.mSizes) {
+      rowTrackPositions.AppendElement(sz.mPosition);
+      rowTrackSizes.AppendElement(sz.mBase);
+      bool isRepeat = ((row >= gridReflowState.mRowFunctions.mRepeatAutoStart) &&
+                       (row < gridReflowState.mRowFunctions.mRepeatAutoEnd));
+      rowTrackStates.AppendElement(
         isRepeat ?
         (uint32_t)mozilla::dom::GridTrackState::Repeat :
         (uint32_t)mozilla::dom::GridTrackState::Static
-    );
-
-    col++;
-  }
-  ComputedGridTrackInfo* colInfo = new ComputedGridTrackInfo(
-    gridReflowState.mColFunctions.mExplicitGridOffset,
-    gridReflowState.mColFunctions.NumExplicitTracks(),
-    0,
-    col,
-    Move(colTrackPositions),
-    Move(colTrackSizes),
-    Move(colTrackStates));
-  Properties().Set(GridColTrackInfo(), colInfo);
-
-  uint32_t rowTrackCount = gridReflowState.mRows.mSizes.Length();
-  nsTArray<nscoord> rowTrackPositions(rowTrackCount);
-  nsTArray<nscoord> rowTrackSizes(rowTrackCount);
-  nsTArray<uint32_t> rowTrackStates(rowTrackCount);
-  uint32_t row = 0;
-  for (const TrackSize& sz : gridReflowState.mRows.mSizes) {
-    rowTrackPositions.AppendElement(sz.mPosition);
-    rowTrackSizes.AppendElement(sz.mBase);
-    bool isRepeat = ((row >= gridReflowState.mRowFunctions.mRepeatAutoStart) &&
-                     (row < gridReflowState.mRowFunctions.mRepeatAutoEnd));
-    rowTrackStates.AppendElement(
-      isRepeat ?
-      (uint32_t)mozilla::dom::GridTrackState::Repeat :
-      (uint32_t)mozilla::dom::GridTrackState::Static
-    );
-
-    row++;
-  }
-  // Row info has to accomodate fragmentation of the grid, which may happen in
-  // later calls to Reflow. For now, presume that no more fragmentation will
-  // occur.
-  ComputedGridTrackInfo* rowInfo = new ComputedGridTrackInfo(
-    gridReflowState.mRowFunctions.mExplicitGridOffset,
-    gridReflowState.mRowFunctions.NumExplicitTracks(),
-    gridReflowState.mStartRow,
-    row,
-    Move(rowTrackPositions),
-    Move(rowTrackSizes),
-    Move(rowTrackStates));
-  Properties().Set(GridRowTrackInfo(), rowInfo);
-
-  if (prevInFlow) {
-    // This frame is fragmenting rows from a previous frame, so patch up
-    // the prior GridRowTrackInfo with a new end row.
-
-    ComputedGridTrackInfo* priorRowInfo =
-      prevInFlow->Properties().Get(GridRowTrackInfo());
-    ComputedGridTrackInfo* revisedPriorRowInfo = new ComputedGridTrackInfo(
-      priorRowInfo->mNumLeadingImplicitTracks,
-      priorRowInfo->mNumExplicitTracks,
-      priorRowInfo->mStartFragmentTrack,
+      );
+
+      row++;
+    }
+    // Row info has to accomodate fragmentation of the grid, which may happen in
+    // later calls to Reflow. For now, presume that no more fragmentation will
+    // occur.
+    ComputedGridTrackInfo* rowInfo = new ComputedGridTrackInfo(
+      gridReflowState.mRowFunctions.mExplicitGridOffset,
+      gridReflowState.mRowFunctions.NumExplicitTracks(),
       gridReflowState.mStartRow,
-      Move(priorRowInfo->mPositions),
-      Move(priorRowInfo->mSizes),
-      Move(priorRowInfo->mStates));
-    prevInFlow->Properties().Set(GridRowTrackInfo(), revisedPriorRowInfo);
+      row,
+      Move(rowTrackPositions),
+      Move(rowTrackSizes),
+      Move(rowTrackStates));
+    Properties().Set(GridRowTrackInfo(), rowInfo);
+
+    if (prevInFlow) {
+      // This frame is fragmenting rows from a previous frame, so patch up
+      // the prior GridRowTrackInfo with a new end row.
+
+      // FIXME: This can be streamlined and/or removed when bug 1151204 lands.
+
+      ComputedGridTrackInfo* priorRowInfo =
+        prevInFlow->Properties().Get(GridRowTrackInfo());
+
+      // Adjust track positions based on the first track in this fragment.
+      if (priorRowInfo->mPositions.Length() >
+          priorRowInfo->mStartFragmentTrack) {
+        nscoord delta =
+          priorRowInfo->mPositions[priorRowInfo->mStartFragmentTrack];
+        for (nscoord& pos : priorRowInfo->mPositions) {
+          pos -= delta;
+        }
+      }
+
+      ComputedGridTrackInfo* revisedPriorRowInfo = new ComputedGridTrackInfo(
+        priorRowInfo->mNumLeadingImplicitTracks,
+        priorRowInfo->mNumExplicitTracks,
+        priorRowInfo->mStartFragmentTrack,
+        gridReflowState.mStartRow,
+        Move(priorRowInfo->mPositions),
+        Move(priorRowInfo->mSizes),
+        Move(priorRowInfo->mStates));
+      prevInFlow->Properties().Set(GridRowTrackInfo(), revisedPriorRowInfo);
+    }
+
+    // Generate the line info properties. We need to provide the number of
+    // repeat tracks produced in the reflow.
+
+    // Generate column lines first.
+    uint32_t capacity = gridReflowState.mColFunctions.NumRepeatTracks() +
+                        gridReflowState.mCols.mSizes.Length();
+    nsTArray<nsTArray<nsString>> columnLineNames(capacity);
+    for (col = 0; col <= gridReflowState.mCols.mSizes.Length(); col++) {
+      columnLineNames.AppendElement(
+        gridReflowState.mCols.GetLineNamesAtIndex(
+          gridReflowState.mGridStyle->mGridTemplateColumns,
+          gridReflowState.mColFunctions,
+          col));
+    }
+    ComputedGridLineInfo* columnLineInfo = new ComputedGridLineInfo(
+      Move(columnLineNames));
+    Properties().Set(GridColumnLineInfo(), columnLineInfo);
+
+    // Generate row lines next.
+    capacity = gridReflowState.mRowFunctions.NumRepeatTracks() +
+               gridReflowState.mRows.mSizes.Length();
+    nsTArray<nsTArray<nsString>> rowLineNames(capacity);
+    for (row = 0; row <= gridReflowState.mRows.mSizes.Length(); row++) {
+      rowLineNames.AppendElement(
+        gridReflowState.mRows.GetLineNamesAtIndex(
+          gridReflowState.mGridStyle->mGridTemplateRows,
+          gridReflowState.mRowFunctions,
+          row));
+    }
+    ComputedGridLineInfo* rowLineInfo = new ComputedGridLineInfo(
+      Move(rowLineNames));
+    Properties().Set(GridRowLineInfo(), rowLineInfo);
   }
 
   if (!prevInFlow) {
     SharedGridData* sharedGridData = Properties().Get(SharedGridData::Prop());
     if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
       if (!sharedGridData) {
         sharedGridData = new SharedGridData;
         Properties().Set(SharedGridData::Prop(), sharedGridData);
@@ -5691,16 +5795,19 @@ nsGridContainerFrame::Reflow(nsPresConte
       sharedGridData->mRows.mBaselineSubtreeAlign[0] =
         gridReflowState.mRows.mBaselineSubtreeAlign[0];
       sharedGridData->mRows.mBaselineSubtreeAlign[1] =
         gridReflowState.mRows.mBaselineSubtreeAlign[1];
       sharedGridData->mGridItems.Clear();
       sharedGridData->mGridItems.SwapElements(gridReflowState.mGridItems);
       sharedGridData->mAbsPosItems.Clear();
       sharedGridData->mAbsPosItems.SwapElements(gridReflowState.mAbsPosItems);
+
+      sharedGridData->mGenerateComputedGridInfo =
+          HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
     } else if (sharedGridData && !GetNextInFlow()) {
       Properties().Delete(SharedGridData::Prop());
     }
   }
 
   FinishAndStoreOverflow(&aDesiredSize);
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
 }
@@ -5986,8 +6093,62 @@ nsGridContainerFrame::TrackSize::Dump() 
     printf("frozen ");
   }
   if (mState & eBreakBefore) {
     printf("break-before ");
   }
 }
 
 #endif // DEBUG
+
+nsGridContainerFrame*
+nsGridContainerFrame::GetGridFrameWithComputedInfo(nsIFrame* aFrame)
+{
+  // Prepare a lambda function that we may need to call multiple times.
+  auto GetGridContainerFrame = [](nsIFrame *aFrame) {
+    // Return the aFrame's content insertion frame, iff it is
+    // a grid container.
+    nsGridContainerFrame* gridFrame = nullptr;
+
+    if (aFrame) {
+      nsIFrame* contentFrame = aFrame->GetContentInsertionFrame();
+      if (contentFrame &&
+          (contentFrame->GetType() == nsGkAtoms::gridContainerFrame)) {
+        gridFrame = static_cast<nsGridContainerFrame*>(contentFrame);
+      }
+    }
+    return gridFrame;
+  };
+
+  nsGridContainerFrame* gridFrame = GetGridContainerFrame(aFrame);
+  if (gridFrame) {
+    // if any of our properties are missing, generate them
+    bool reflowNeeded = (!gridFrame->Properties().Has(GridColTrackInfo()) ||
+                         !gridFrame->Properties().Has(GridRowTrackInfo()) ||
+                         !gridFrame->Properties().Has(GridColumnLineInfo()) ||
+                         !gridFrame->Properties().Has(GridRowLineInfo()));
+
+    if (reflowNeeded) {
+      // Trigger a reflow that generates additional grid property data.
+      nsIPresShell* shell = gridFrame->PresContext()->PresShell();
+      gridFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
+      shell->FrameNeedsReflow(gridFrame,
+                              nsIPresShell::eResize,
+                              NS_FRAME_IS_DIRTY);
+      shell->FlushPendingNotifications(Flush_Layout);
+
+      // Since the reflow may have side effects, get the grid frame again.
+      gridFrame = GetGridContainerFrame(aFrame);
+
+      // Assert the grid properties are present
+      MOZ_ASSERT(!gridFrame ||
+                  gridFrame->Properties().Has(GridColTrackInfo()));
+      MOZ_ASSERT(!gridFrame ||
+                  gridFrame->Properties().Has(GridRowTrackInfo()));
+      MOZ_ASSERT(!gridFrame ||
+                  gridFrame->Properties().Has(GridColumnLineInfo()));
+      MOZ_ASSERT(!gridFrame ||
+                  gridFrame->Properties().Has(GridRowLineInfo()));
+    }
+  }
+
+  return gridFrame;
+}
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -46,25 +46,34 @@ struct ComputedGridTrackInfo
   uint32_t mNumLeadingImplicitTracks;
   uint32_t mNumExplicitTracks;
   uint32_t mStartFragmentTrack;
   uint32_t mEndFragmentTrack;
   nsTArray<nscoord> mPositions;
   nsTArray<nscoord> mSizes;
   nsTArray<uint32_t> mStates;
 };
+
+struct ComputedGridLineInfo
+{
+  explicit ComputedGridLineInfo(nsTArray<nsTArray<nsString>>&& aNames)
+    : mNames(aNames)
+  {}
+  nsTArray<nsTArray<nsString>> mNames;
+};
 } // namespace mozilla
 
 class nsGridContainerFrame final : public nsContainerFrame
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
   NS_DECL_QUERYFRAME_TARGET(nsGridContainerFrame)
   NS_DECL_QUERYFRAME
   typedef mozilla::ComputedGridTrackInfo ComputedGridTrackInfo;
+  typedef mozilla::ComputedGridLineInfo ComputedGridLineInfo;
 
   // nsIFrame overrides
   void Reflow(nsPresContext*           aPresContext,
               nsHTMLReflowMetrics&     aDesiredSize,
               const nsHTMLReflowState& aReflowState,
               nsReflowStatus&          aStatus) override;
   nscoord GetMinISize(nsRenderingContext* aRenderingContext) override;
   nscoord GetPrefISize(nsRenderingContext* aRenderingContext) override;
@@ -100,28 +109,60 @@ public:
    * Return the containing block for aChild which MUST be an abs.pos. child
    * of a grid container.  This is just a helper method for
    * nsAbsoluteContainingBlock::Reflow - it's not meant to be used elsewhere.
    */
   static const nsRect& GridItemCB(nsIFrame* aChild);
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridItemContainingBlockRect, nsRect)
 
+  /**
+   * These properties are created by a call to
+   * nsGridContainerFrame::GetGridFrameWithComputedInfo, typically from
+   * Element::GetGridFragments.
+   */
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColTrackInfo, ComputedGridTrackInfo)
   const ComputedGridTrackInfo* GetComputedTemplateColumns()
   {
-    return Properties().Get(GridColTrackInfo());
+    const ComputedGridTrackInfo* info = Properties().Get(GridColTrackInfo());
+    MOZ_ASSERT(info, "Property generation wasn't requested.");
+    return info;
   }
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowTrackInfo, ComputedGridTrackInfo)
   const ComputedGridTrackInfo* GetComputedTemplateRows()
   {
-    return Properties().Get(GridRowTrackInfo());
+    const ComputedGridTrackInfo* info = Properties().Get(GridRowTrackInfo());
+    MOZ_ASSERT(info, "Property generation wasn't requested.");
+    return info;
+  }
+
+  NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridColumnLineInfo, ComputedGridLineInfo)
+  const ComputedGridLineInfo* GetComputedTemplateColumnLines()
+  {
+    const ComputedGridLineInfo* info = Properties().Get(GridColumnLineInfo());
+    MOZ_ASSERT(info, "Property generation wasn't requested.");
+    return info;
   }
 
+  NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowLineInfo, ComputedGridLineInfo)
+  const ComputedGridLineInfo* GetComputedTemplateRowLines()
+  {
+    const ComputedGridLineInfo* info = Properties().Get(GridRowLineInfo());
+    MOZ_ASSERT(info, "Property generation wasn't requested.");
+    return info;
+  }
+
+  /**
+   * Return a containing grid frame, and ensure it has computed grid info
+   * @return nullptr if aFrame has no grid container, or frame was destroyed
+   * @note this might destroy layout/style data since it may flush layout
+   */
+  static nsGridContainerFrame* GetGridFrameWithComputedInfo(nsIFrame* aFrame);
+
   struct TrackSize;
   struct GridItemInfo;
   struct GridReflowState;
 protected:
   static const uint32_t kAutoLine;
   // The maximum line number, in the zero-based translated grid.
   static const uint32_t kTranslatedMaxLine;
   typedef mozilla::LogicalPoint LogicalPoint;
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -1532,19 +1532,19 @@ nsPluginFrame::BuildLayer(nsDisplayListB
 
     if (aBuilder->IsPaintingToWindow() &&
         aBuilder->GetWidgetLayerManager() &&
         aBuilder->GetWidgetLayerManager()->AsClientLayerManager() &&
         mInstanceOwner->UseAsyncRendering())
     {
       RefPtr<ClientLayerManager> lm = aBuilder->GetWidgetLayerManager()->AsClientLayerManager();
       if (!mDidCompositeObserver || !mDidCompositeObserver->IsValid(lm)) {
-        mDidCompositeObserver = new PluginFrameDidCompositeObserver(mInstanceOwner, lm);
+        mDidCompositeObserver = MakeUnique<PluginFrameDidCompositeObserver>(mInstanceOwner, lm);
       }
-      lm->AddDidCompositeObserver(mDidCompositeObserver);
+      lm->AddDidCompositeObserver(mDidCompositeObserver.get());
     }
 #ifdef MOZ_WIDGET_ANDROID
   } else if (aItem->GetType() == nsDisplayItem::TYPE_PLUGIN_VIDEO) {
     nsDisplayPluginVideo* videoItem = reinterpret_cast<nsDisplayPluginVideo*>(aItem);
     nsNPAPIPluginInstance::VideoInfo* videoInfo = videoItem->VideoInfo();
 
     RefPtr<ImageContainer> container = mInstanceOwner->GetImageContainerForVideo(videoInfo);
     if (!container)
--- a/layout/generic/nsPluginFrame.h
+++ b/layout/generic/nsPluginFrame.h
@@ -5,17 +5,17 @@
 
 /* rendering objects for replaced elements implemented by a plugin */
 
 #ifndef nsPluginFrame_h___
 #define nsPluginFrame_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
-#include "nsAutoPtr.h"
+#include "mozilla/UniquePtr.h"
 #include "nsIObjectFrame.h"
 #include "nsFrame.h"
 #include "nsRegion.h"
 #include "nsDisplayList.h"
 #include "nsIReflowCallback.h"
 #include "Units.h"
 
 #ifdef XP_WIN
@@ -331,17 +331,17 @@ private:
   bool mReflowCallbackPosted;
 
   // We keep this reference to ensure we can always unregister the
   // plugins we register on the root PresContext.
   // This is only non-null while we have a plugin registered for geometry
   // updates.
   RefPtr<nsRootPresContext> mRootPresContextRegisteredWith;
 
-  nsAutoPtr<PluginFrameDidCompositeObserver> mDidCompositeObserver;
+  mozilla::UniquePtr<PluginFrameDidCompositeObserver> mDidCompositeObserver;
 
   // Tracks windowed plugin visibility during scroll operations. See
   // SetScrollVisibility.
   bool mIsHiddenDueToScroll;
 };
 
 class nsDisplayPlugin : public nsDisplayItem {
 public:
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/IntegerRange.h"
+#include "mozilla/unused.h"
 
 #include "nsCOMPtr.h"
 #include "nsBlockFrame.h"
 #include "nsFontMetrics.h"
 #include "nsSplittableFrame.h"
 #include "nsLineLayout.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
@@ -66,22 +67,22 @@
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 
 #include <algorithm>
 #include <limits>
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
-#include "nsAutoPtr.h"
 
 #include "nsPrintfCString.h"
 
 #include "gfxContext.h"
 
+#include "mozilla/UniquePtr.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/LookAndFeel.h"
 
 #include "GeckoProfiler.h"
 
 #ifdef DEBUG
 #undef NOISY_REFLOW
 #undef NOISY_TRIM
@@ -190,17 +191,17 @@ private:
 
 /**
  * This property is set on text frames with TEXT_IN_TEXTRUN_USER_DATA set that
  * have potentially-animated glyphs.
  * The only reason this list is in a property is to automatically destroy the
  * list when the frame is deleted, unregistering the observers.
  */
 NS_DECLARE_FRAME_PROPERTY_DELETABLE(TextFrameGlyphObservers,
-                                    nsTArray<nsAutoPtr<GlyphObserver>>)
+                                    nsTArray<UniquePtr<GlyphObserver>>)
 
 static const nsFrameState TEXT_REFLOW_FLAGS =
    TEXT_FIRST_LETTER |
    TEXT_START_OF_LINE |
    TEXT_END_OF_LINE |
    TEXT_HYPHEN_BREAK |
    TEXT_TRIMMED_TRAILING_WHITESPACE |
    TEXT_JUSTIFICATION_ENABLED |
@@ -733,18 +734,18 @@ IsAllWhitespace(const nsTextFragment* aF
 static void
 CreateObserverForAnimatedGlyphs(nsTextFrame* aFrame, const nsTArray<gfxFont*>& aFonts)
 {
   if (!(aFrame->GetStateBits() & TEXT_IN_TEXTRUN_USER_DATA)) {
     // Maybe the textrun was created for uninflated text.
     return;
   }
 
-  nsTArray<nsAutoPtr<GlyphObserver> >* observers =
-    new nsTArray<nsAutoPtr<GlyphObserver> >();
+  nsTArray<UniquePtr<GlyphObserver>>* observers =
+    new nsTArray<UniquePtr<GlyphObserver>>();
   for (uint32_t i = 0, count = aFonts.Length(); i < count; ++i) {
     observers->AppendElement(new GlyphObserver(aFonts[i], aFrame));
   }
   aFrame->Properties().Set(TextFrameGlyphObservers(), observers);
   // We are lazy and don't try to remove a property value that might be
   // obsolete due to style changes or font selection changes. That is
   // likely to be rarely needed, and we don't want to eat the overhead of
   // doing it for the overwhelmingly common case of no property existing.
@@ -950,17 +951,17 @@ public:
     gfxTextRun*  mTextRun;
     DrawTarget*  mDrawTarget;
     uint32_t     mOffsetIntoTextRun;
   };
 
 private:
   AutoTArray<MappedFlow,10>   mMappedFlows;
   AutoTArray<nsTextFrame*,50> mLineBreakBeforeFrames;
-  AutoTArray<nsAutoPtr<BreakSink>,10> mBreakSinks;
+  AutoTArray<UniquePtr<BreakSink>,10> mBreakSinks;
   AutoTArray<UniquePtr<gfxTextRun>,5> mTextRunsToDelete;
   nsLineBreaker                 mLineBreaker;
   gfxTextRun*                   mCurrentFramesAllSameTextRun;
   DrawTarget*                   mDrawTarget;
   nsIFrame*                     mLineContainer;
   nsTextFrame*                  mLastFrame;
   // The common ancestor of the current frame and the previous leaf frame
   // on the line, or null if there was no previous leaf frame.
@@ -2109,25 +2110,25 @@ BuildTextRunsScanner::BuildTextRunForFra
             iter.ConvertOriginalToSkipped(textBreakPoints[i]));
   }
   if (mStartOfLine) {
     nsTextFrameUtils::AppendLineBreakOffset(&textBreakPointsAfterTransform,
                                             transformedLength);
   }
 
   // Setup factory chain
-  nsAutoPtr<nsTransformingTextRunFactory> transformingFactory;
+  UniquePtr<nsTransformingTextRunFactory> transformingFactory;
   if (anyTextTransformStyle) {
     transformingFactory =
-      new nsCaseTransformTextRunFactory(transformingFactory.forget());
+      MakeUnique<nsCaseTransformTextRunFactory>(Move(transformingFactory));
   }
   if (anyMathMLStyling) {
     transformingFactory =
-      new MathMLTextRunFactory(transformingFactory.forget(), mathFlags,
-                               sstyScriptLevel, fontInflation);
+      MakeUnique<MathMLTextRunFactory>(Move(transformingFactory), mathFlags,
+                                       sstyScriptLevel, fontInflation);
   }
   nsTArray<RefPtr<nsTransformedCharStyle>> styles;
   if (transformingFactory) {
     iter.SetOriginalOffset(0);
     for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
       MappedFlow* mappedFlow = &mMappedFlows[i];
       nsTextFrame* f;
       nsStyleContext* sc = nullptr;
@@ -2167,32 +2168,36 @@ BuildTextRunsScanner::BuildTextRunForFra
   if (mDoubleByteText) {
     const char16_t* text = static_cast<const char16_t*>(textPtr);
     if (transformingFactory) {
       textRun = transformingFactory->MakeTextRun(text, transformedLength,
                                                  &params, fontGroup, textFlags,
                                                  Move(styles), true);
       if (textRun) {
         // ownership of the factory has passed to the textrun
-        transformingFactory.forget();
+        // TODO: bug 1285316: clean up ownership transfer from the factory to
+        // the textrun
+        Unused << transformingFactory.release();
       }
     } else {
       textRun = fontGroup->MakeTextRun(text, transformedLength, &params,
                                        textFlags, mMissingFonts);
     }
   } else {
     const uint8_t* text = static_cast<const uint8_t*>(textPtr);
     textFlags |= gfxFontGroup::TEXT_IS_8BIT;
     if (transformingFactory) {
       textRun = transformingFactory->MakeTextRun(text, transformedLength,
                                                  &params, fontGroup, textFlags,
                                                  Move(styles), true);
       if (textRun) {
         // ownership of the factory has passed to the textrun
-        transformingFactory.forget();
+        // TODO: bug 1285316: clean up ownership transfer from the factory to
+        // the textrun
+        Unused << transformingFactory.release();
       }
     } else {
       textRun = fontGroup->MakeTextRun(text, transformedLength, &params,
                                        textFlags, mMissingFonts);
     }
   }
   if (!textRun) {
     DestroyUserData(userDataToDestroy);
@@ -2384,18 +2389,18 @@ BuildTextRunsScanner::SetupBreakSinksFor
 
   for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
     MappedFlow* mappedFlow = &mMappedFlows[i];
     uint32_t offset = iter.GetSkippedOffset();
     gfxSkipCharsIterator iterNext = iter;
     iterNext.AdvanceOriginal(mappedFlow->GetContentEnd() -
             mappedFlow->mStartFrame->GetContentOffset());
 
-    nsAutoPtr<BreakSink>* breakSink =
-      mBreakSinks.AppendElement(new BreakSink(aTextRun, mDrawTarget, offset));
+    UniquePtr<BreakSink>* breakSink =
+      mBreakSinks.AppendElement(MakeUnique<BreakSink>(aTextRun, mDrawTarget, offset));
     if (!breakSink || !*breakSink)
       return;
 
     uint32_t length = iterNext.GetSkippedOffset() - offset;
     uint32_t flags = 0;
     nsIFrame* initialBreakController = mappedFlow->mAncestorControllingInitialBreak;
     if (!initialBreakController) {
       initialBreakController = mLineContainer;
--- a/layout/generic/nsTextRunTransformations.h
+++ b/layout/generic/nsTextRunTransformations.h
@@ -3,18 +3,18 @@
  * 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/. */
 
 #ifndef NSTEXTRUNTRANSFORMATIONS_H_
 #define NSTEXTRUNTRANSFORMATIONS_H_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
 #include "gfxTextRun.h"
-#include "nsAutoPtr.h"
 #include "nsStyleContext.h"
 
 class nsTransformedTextRun;
 
 struct nsTransformedCharStyle final {
   NS_INLINE_DECL_REFCOUNTING(nsTransformedCharStyle)
 
   explicit nsTransformedCharStyle(nsStyleContext* aContext)
@@ -73,19 +73,19 @@ class nsCaseTransformTextRunFactory : pu
 public:
   // We could add an optimization here so that when there is no inner
   // factory, no title-case conversion, and no upper-casing of SZLIG, we override
   // MakeTextRun (after making it virtual in the superclass) and have it
   // just convert the string to uppercase or lowercase and create the textrun
   // via the fontgroup.
   
   // Takes ownership of aInnerTransformTextRunFactory
-  explicit nsCaseTransformTextRunFactory(nsTransformingTextRunFactory* aInnerTransformingTextRunFactory,
+  explicit nsCaseTransformTextRunFactory(UniquePtr<nsTransformingTextRunFactory> aInnerTransformingTextRunFactory,
                                          bool aAllUppercase = false)
-    : mInnerTransformingTextRunFactory(aInnerTransformingTextRunFactory),
+    : mInnerTransformingTextRunFactory(Move(aInnerTransformingTextRunFactory)),
       mAllUppercase(aAllUppercase) {}
 
   virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
                               mozilla::gfx::DrawTarget* aRefDrawTarget,
                               gfxMissingFontRecorder* aMFR) override;
 
   // Perform a transformation on the given string, writing the result into
   // aConvertedString. If aAllUppercase is true, the transform is (global)
@@ -103,18 +103,18 @@ public:
                               const nsIAtom* aLanguage,
                               nsTArray<bool>& aCharsToMergeArray,
                               nsTArray<bool>& aDeletedCharsArray,
                               nsTransformedTextRun* aTextRun = nullptr,
                               nsTArray<uint8_t>* aCanBreakBeforeArray = nullptr,
                               nsTArray<RefPtr<nsTransformedCharStyle>>* aStyleArray = nullptr);
 
 protected:
-  nsAutoPtr<nsTransformingTextRunFactory> mInnerTransformingTextRunFactory;
-  bool                                    mAllUppercase;
+  mozilla::UniquePtr<nsTransformingTextRunFactory> mInnerTransformingTextRunFactory;
+  bool mAllUppercase;
 };
 
 /**
  * So that we can reshape as necessary, we store enough information
  * to fully rebuild the textrun contents.
  */
 class nsTransformedTextRun final : public gfxTextRun {
 public:
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2727,39 +2727,41 @@ nsComputedDOMStyle::DoGetGridAutoRows()
   return GetGridTrackSize(StylePosition()->mGridAutoRowsMin,
                           StylePosition()->mGridAutoRowsMax);
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetGridTemplateColumns()
 {
   const ComputedGridTrackInfo* info = nullptr;
-  if (mInnerFrame) {
-    nsIFrame* gridContainerCandidate = mInnerFrame->GetContentInsertionFrame();
-    if (gridContainerCandidate &&
-        gridContainerCandidate->GetType() == nsGkAtoms::gridContainerFrame) {
-      info = static_cast<nsGridContainerFrame*>(gridContainerCandidate)->
-        GetComputedTemplateColumns();
-    }
-  }
+
+  nsGridContainerFrame* gridFrame =
+    nsGridContainerFrame::GetGridFrameWithComputedInfo(
+      mContent->GetPrimaryFrame());
+
+  if (gridFrame) {
+    info = gridFrame->GetComputedTemplateColumns();
+  }
+
   return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns, info);
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetGridTemplateRows()
 {
   const ComputedGridTrackInfo* info = nullptr;
-  if (mInnerFrame) {
-    nsIFrame* gridContainerCandidate = mInnerFrame->GetContentInsertionFrame();
-    if (gridContainerCandidate &&
-        gridContainerCandidate->GetType() == nsGkAtoms::gridContainerFrame) {
-      info = static_cast<nsGridContainerFrame*>(gridContainerCandidate)->
-        GetComputedTemplateRows();
-     }
-  }
+
+  nsGridContainerFrame* gridFrame =
+    nsGridContainerFrame::GetGridFrameWithComputedInfo(
+      mContent->GetPrimaryFrame());
+
+  if (gridFrame) {
+    info = gridFrame->GetComputedTemplateRows();
+  }
+
   return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows, info);
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::GetGridLine(const nsStyleGridLine& aGridLine)
 {
   if (aGridLine.IsAuto()) {
     RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
--- a/mobile/android/chrome/content/blockedSite.xhtml
+++ b/mobile/android/chrome/content/blockedSite.xhtml
@@ -90,19 +90,16 @@
             error = "malware";
             break;
           case "deceptiveBlocked" :
             error = "phishing";
             break;
           case "unwantedBlocked" :
             error = "unwanted";
             break;
-          case "forbiddenBlocked" :
-            error = "forbidden";
-            break;
           default:
             return;
         }
 
         var el;
 
         if (error !== "malware") {
           el = document.getElementById("errorTitleText_malware");
@@ -126,33 +123,16 @@
           el = document.getElementById("errorTitleText_unwanted");
           el.parentNode.removeChild(el);
           el = document.getElementById("errorShortDescText_unwanted");
           el.parentNode.removeChild(el);
           el = document.getElementById("errorLongDescText_unwanted");
           el.parentNode.removeChild(el);
         }
 
-        if (error !== "forbidden") {
-          el = document.getElementById("errorTitleText_forbidden");
-          el.parentNode.removeChild(el);
-          el = document.getElementById("errorShortDescText_forbidden");
-          el.parentNode.removeChild(el);
-          el = document.getElementById("whyForbiddenButton");
-          el.parentNode.removeChild(el);
-        } else {
-          el = document.getElementById("ignoreWarningButton");
-          el.parentNode.removeChild(el);
-          el = document.getElementById("reportButton");
-          el.parentNode.removeChild(el);
-
-          // Remove red style: A "forbidden site" does not warrant the same level of anxiety as a security concern.
-          document.getElementById("errorPage").className = "";
-        }
-
         if (!getOverride()) {
           var btn = document.getElementById("ignoreWarningButton");
           if (btn) {
             btn.parentNode.removeChild(btn);
           }
         }
 
         // Set sitename
@@ -171,42 +151,39 @@
 
     <div id="errorPageContainer">
 
       <!-- Error Title -->
       <div id="errorTitle">
         <h1 id="errorTitleText_phishing" class="errorTitleText">&safeb.blocked.phishingPage.title3;</h1>
         <h1 id="errorTitleText_malware" class="errorTitleText">&safeb.blocked.malwarePage.title;</h1>
         <h1 id="errorTitleText_unwanted" class="errorTitleText">&safeb.blocked.unwantedPage.title;</h1>
-        <h1 id="errorTitleText_forbidden" class="errorTitleText">&safeb.blocked.forbiddenPage.title2;</h1>
       </div>
 
       <div id="errorLongContent">
       
         <!-- Short Description -->
         <div id="errorShortDesc">
           <p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc3;</p>
           <p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p>
           <p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc;</p>
-          <p id="errorShortDescText_forbidden">&safeb.blocked.forbiddenPage.shortDesc2;</p>
         </div>
 
         <!-- Long Description -->
         <div id="errorLongDesc">
           <p id="errorLongDescText_phishing">&safeb.blocked.phishingPage.longDesc3;</p>
           <p id="errorLongDescText_malware">&safeb.blocked.malwarePage.longDesc;</p>
           <p id="errorLongDescText_unwanted">&safeb.blocked.unwantedPage.longDesc;</p>
         </div>
         
         <!-- Action buttons -->
         <div id="buttons">
           <!-- Commands handled in browser.js -->
           <button id="getMeOutButton">&safeb.palm.accept.label;</button>
           <button id="reportButton">&safeb.palm.reportPage.label;</button>
-          <button id="whyForbiddenButton">&safeb.palm.whyForbidden.label;</button>
         </div>
       </div>
       <div id="ignoreWarning">
         <button id="ignoreWarningButton">&safeb.palm.decline.label;</button>
       </div>
     </div>
     <!--
     - Note: It is important to run the script this way, instead of using
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5252,19 +5252,16 @@ var ErrorPageEventHandler = {
             // Allow users to override and continue through to the site,
             let webNav = BrowserApp.selectedBrowser.docShell.QueryInterface(Ci.nsIWebNavigation);
             let location = BrowserApp.selectedBrowser.contentWindow.location;
             webNav.loadURI(location, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, null, null, null);
 
             // ....but add a notify bar as a reminder, so that they don't lose
             // track after, e.g., tab switching.
             NativeWindow.doorhanger.show(Strings.browser.GetStringFromName("safeBrowsingDoorhanger"), "safebrowsing-warning", [], BrowserApp.selectedTab.id);
-          } else if (target == errorDoc.getElementById("whyForbiddenButton")) {
-            // This is the "Why is this site blocked" button for family friendly browsing.
-            BrowserApp.selectedBrowser.loadURI("https://support.mozilla.org/kb/controlledaccess");
           }
         }
         break;
       }
     }
   }
 };
 
--- a/mobile/android/locales/en-US/chrome/phishing.dtd
+++ b/mobile/android/locales/en-US/chrome/phishing.dtd
@@ -1,27 +1,23 @@
 <!-- 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/. -->
 
 <!ENTITY safeb.palm.accept.label "Get me out of here!">
 <!ENTITY safeb.palm.decline.label "Ignore this warning">
 <!ENTITY safeb.palm.reportPage.label "Why was this page blocked?">
-<!ENTITY safeb.palm.whyForbidden.label "Why was this page blocked?">
 
 <!ENTITY safeb.blocked.malwarePage.title "Reported Attack Page!">
 <!-- Localization note (safeb.blocked.malware.shortDesc) - Please don't translate the contents of the <span id="malware_sitename"/> tag.  It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
 <!ENTITY safeb.blocked.malwarePage.shortDesc "This web page at <span id='malware_sitename'/> has been reported as an attack page and has been blocked based on your security preferences.">
 <!ENTITY safeb.blocked.malwarePage.longDesc "<p>Attack pages try to install programs that steal private information, use your computer to attack others, or damage your system.</p><p>Some attack pages intentionally distribute harmful software, but many are compromised without the knowledge or permission of their owners.</p>">
 
 <!ENTITY safeb.blocked.phishingPage.title3 "Deceptive Site!">
 <!-- Localization note (safeb.blocked.phishingPage.shortDesc3) - Please don't translate the contents of the <span id="phishing_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
 <!ENTITY safeb.blocked.phishingPage.shortDesc3 "This web page at <span id='phishing_sitename'/> has been reported as a deceptive site and has been blocked based on your security preferences.">
 <!ENTITY safeb.blocked.phishingPage.longDesc3 "<p>Deceptive sites are designed to trick you into doing something dangerous, like installing software, or revealing your personal information, like passwords, phone numbers or credit cards.</p><p>Entering any information on this web page may result in identity theft or other fraud.</p>">
 
 <!ENTITY safeb.blocked.unwantedPage.title "Reported Unwanted Software Site!">
 <!-- Localization note (safeb.blocked.unwanted.shortDesc) - Please don't translate the contents of the <span id="unwanted_sitename"/> tag.  It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
 <!ENTITY safeb.blocked.unwantedPage.shortDesc "This web page at <span id='unwanted_sitename'/> has been reported to contain unwanted software and has been blocked based on your security preferences.">
 <!ENTITY safeb.blocked.unwantedPage.longDesc "Unwanted software pages try to install software that can be deceptive and affect your system in unexpected ways.">
 
-<!ENTITY safeb.blocked.forbiddenPage.title2 "Blocked Site">
-<!-- Localization note (safeb.blocked.forbiddenPage.shortDesc) - Please don't translate the contents of the <span id="forbidden_sitename"/> tag.  It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
-<!ENTITY safeb.blocked.forbiddenPage.shortDesc2 "The Web page at <span id='forbidden_sitename'/> has been blocked by your admin profile.">
--- a/mobile/locales/en-US/overrides/appstrings.properties
+++ b/mobile/locales/en-US/overrides/appstrings.properties
@@ -28,15 +28,14 @@ externalProtocolTitle=External Protocol 
 externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
 #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
 malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
 deceptiveBlocked=This web page at %S has been reported as a deceptive site and has been blocked based on your security preferences.
 unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
-forbiddenBlocked=The site at %S has been blocked by your browser configuration.
 cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
 corruptedContentErrorv2=The site at %S has experienced a network protocol violation that cannot be repaired.
 remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox.
 sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol.
 weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website.
 inadequateSecurityError=The website tried to negotiate an inadequate level of security.
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5061,17 +5061,17 @@ pref("urlclassifier.downloadBlockTable",
  // Only download the whitelist on Windows, since the whitelist is
  // only useful for suppressing remote lookups for signed binaries which we can
  // only verify on Windows (Bug 974579). Other platforms always do remote lookups.
 pref("urlclassifier.downloadAllowTable", "goog-downloadwhite-digest256");
 #else
 pref("urlclassifier.downloadAllowTable", "");
 #endif
 
-pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-forbid-simple,goog-downloadwhite-digest256,mozstd-track-digest256,mozstd-trackwhite-digest256,mozfull-track-digest256,test-block-simple,mozplugin-block-digest256,mozplugin2-block-digest256");
+pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple,goog-downloadwhite-digest256,mozstd-track-digest256,mozstd-trackwhite-digest256,mozfull-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256");
 
 // The table and update/gethash URLs for Safebrowsing phishing and malware
 // checks.
 pref("urlclassifier.trackingTable", "test-track-simple,mozstd-track-digest256");
 pref("urlclassifier.trackingWhitelistTable", "test-trackwhite-simple,mozstd-trackwhite-digest256");
 
 // The number of random entries to send with a gethash request.
 pref("urlclassifier.gethashnoise", 4);
@@ -5110,20 +5110,16 @@ pref("browser.safebrowsing.provider.goog
 pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2&key=%GOOGLE_API_KEY%");
 pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2");
 pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
 
 pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url=");
 pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url=");
 pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url=");
 
-// The table and global pref for blocking access to sites forbidden by policy
-pref("browser.safebrowsing.forbiddenURIs.enabled", false);
-pref("urlclassifier.forbiddenTable", "test-forbid-simple");
-
 // The table and global pref for blocking plugin content
 pref("browser.safebrowsing.blockedURIs.enabled", false);
 pref("urlclassifier.blockedTable", "test-block-simple,mozplugin-block-digest256");
 
 // The protocol version we communicate with mozilla server.
 pref("browser.safebrowsing.provider.mozilla.pver", "2.2");
 pref("browser.safebrowsing.provider.mozilla.lists", "mozstd-track-digest256,mozstd-trackwhite-digest256,mozfull-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256");
 pref("browser.safebrowsing.provider.mozilla.updateURL", "https://shavar.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2");
--- a/python/gdbpp/gdbpp/smartptr.py
+++ b/python/gdbpp/gdbpp/smartptr.py
@@ -19,19 +19,19 @@ class weak_ptr_printer(object):
 
         ref_type = proxy.dynamic_type
         weak_ptr = proxy.cast(ref_type).dereference()['mReferent']
         if not weak_ptr:
             return '[(%s) %s]' % (weak_ptr.type, weak_ptr)
 
         return '[(%s) %s]' % (weak_ptr.dynamic_type, weak_ptr)
 
-@GeckoPrettyPrinter('nsAutoPtr', 'nsAutoPtr<.*>')
-@GeckoPrettyPrinter('nsCOMPtr', 'nsCOMPtr<.*>')
-@GeckoPrettyPrinter('RefPtr', 'RefPtr<.*>')
+@GeckoPrettyPrinter('nsAutoPtr', '^nsAutoPtr<.*>$')
+@GeckoPrettyPrinter('nsCOMPtr', '^nsCOMPtr<.*>$')
+@GeckoPrettyPrinter('RefPtr', '^RefPtr<.*>$')
 class smartptr_printer(object):
     def __init__(self, value):
         self.value = value['mRawPtr']
 
     def to_string(self):
         if not self.value:
             type_name = str(self.value.type)
         else:
--- a/python/gdbpp/gdbpp/tarray.py
+++ b/python/gdbpp/gdbpp/tarray.py
@@ -3,17 +3,20 @@
 # 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/.
 
 import gdb
 import itertools
 from gdbpp import GeckoPrettyPrinter
 
-@GeckoPrettyPrinter('TArray', '.*TArray<.*>$')
+@GeckoPrettyPrinter('InfallibleTArray', '^InfallibleTArray<.*>$')
+@GeckoPrettyPrinter('FallibleTArray', '^FallibleTArray<.*>$')
+@GeckoPrettyPrinter('AutoTArray', '^AutoTArray<.*>$')
+@GeckoPrettyPrinter('nsTArray', '^nsTArray<.*>$')
 class tarray_printer(object):
     def __init__(self, value):
         self.value = value
         self.elem_type = value.type.template_argument(0)
 
     def children(self):
         length = self.value['mHdr'].dereference()['mLength']
         data = self.value['mHdr'] + 1
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -506,16 +506,17 @@ public:
     switch (sysno) {
 #ifdef DESKTOP
       // Filesystem syscalls that need more work to determine who's
       // using them, if they need to be, and what we intend to about it.
     case __NR_mkdir:
     case __NR_rmdir:
     case __NR_getcwd:
     CASES_FOR_statfs:
+    CASES_FOR_fstatfs:
     case __NR_chmod:
     case __NR_rename:
     case __NR_symlink:
     case __NR_quotactl:
     case __NR_utimes:
     case __NR_unlink:
     case __NR_fchown:
     case __NR_fchmod:
@@ -601,43 +602,53 @@ public:
       return Allow();
 
 #ifdef DESKTOP
     case __NR_pipe2:
       return Allow();
 
     CASES_FOR_getrlimit:
     case __NR_clock_getres:
-    case __NR_getresuid:
-    case __NR_getresgid:
+    CASES_FOR_getresuid:
+    CASES_FOR_getresgid:
       return Allow();
 
     case __NR_umask:
     case __NR_kill:
     case __NR_wait4:
 #ifdef __NR_arch_prctl
     case __NR_arch_prctl:
 #endif
       return Allow();
 
     case __NR_eventfd2:
     case __NR_inotify_init1:
     case __NR_inotify_add_watch:
     case __NR_inotify_rm_watch:
       return Allow();
 
+#ifdef __NR_memfd_create
+    case __NR_memfd_create:
+      return Allow();
+#endif
+
 #ifdef __NR_rt_tgsigqueueinfo
       // Only allow to send signals within the process.
     case __NR_rt_tgsigqueueinfo: {
       Arg<pid_t> tgid(0);
       return If(tgid == getpid(), Allow())
         .Else(InvalidSyscall());
     }
 #endif
 
+#ifdef __NR_semget
+    case __NR_semget:
+      return Allow();
+#endif
+
 #endif // DESKTOP
 
 #ifdef __NR_getrandom
     case __NR_getrandom:
       return Allow();
 #endif
 
       // nsSystemInfo uses uname (and we cache an instance, so
--- a/security/sandbox/linux/SandboxFilterUtil.h
+++ b/security/sandbox/linux/SandboxFilterUtil.h
@@ -74,45 +74,47 @@ public:
 #define CASES_FOR_mmap   case __NR_mmap
 #endif
 
 #ifdef __NR_getuid32
 #define CASES_FOR_getuid   case __NR_getuid32
 #define CASES_FOR_getgid   case __NR_getgid32
 #define CASES_FOR_geteuid   case __NR_geteuid32
 #define CASES_FOR_getegid   case __NR_getegid32
-#define CASES_FOR_getresuid   case __NR_getresuid32
-#define CASES_FOR_getresgid   case __NR_getresgid32
+#define CASES_FOR_getresuid   case __NR_getresuid32: case __NR_getresuid
+#define CASES_FOR_getresgid   case __NR_getresgid32: case __NR_getresgid
 // The set*id syscalls are omitted; we'll probably never need to allow them.
 #else
 #define CASES_FOR_getuid   case __NR_getuid
 #define CASES_FOR_getgid   case __NR_getgid
 #define CASES_FOR_geteuid   case __NR_geteuid
 #define CASES_FOR_getegid   case __NR_getegid
 #define CASES_FOR_getresuid   case __NR_getresuid
 #define CASES_FOR_getresgid   case __NR_getresgid
 #endif
 
 #ifdef __NR_stat64
 #define CASES_FOR_stat   case __NR_stat64
 #define CASES_FOR_lstat   case __NR_lstat64
 #define CASES_FOR_fstat   case __NR_fstat64
 #define CASES_FOR_fstatat   case __NR_fstatat64
 #define CASES_FOR_statfs   case __NR_statfs64: case __NR_statfs
+#define CASES_FOR_fstatfs   case __NR_fstatfs64: case __NR_fstatfs
 #define CASES_FOR_fcntl   case __NR_fcntl64
 // We're using the 32-bit version on 32-bit desktop for some reason.
 #define CASES_FOR_getdents   case __NR_getdents64: case __NR_getdents
 // FIXME: we might not need the compat cases for these on non-Android:
 #define CASES_FOR_lseek   case __NR_lseek: case __NR__llseek
 #define CASES_FOR_ftruncate   case __NR_ftruncate: case __NR_ftruncate64
 #else
 #define CASES_FOR_stat   case __NR_stat
 #define CASES_FOR_lstat   case __NR_lstat
 #define CASES_FOR_fstatat   case __NR_newfstatat
 #define CASES_FOR_fstat   case __NR_fstat
+#define CASES_FOR_fstatfs   case __NR_fstatfs
 #define CASES_FOR_statfs   case __NR_statfs
 #define CASES_FOR_fcntl   case __NR_fcntl
 #define CASES_FOR_getdents   case __NR_getdents
 #define CASES_FOR_lseek   case __NR_lseek
 #define CASES_FOR_ftruncate   case __NR_ftruncate
 #endif
 
 #ifdef __NR_sigprocmask
--- a/testing/web-platform/tests/IndexedDB/interfaces.idl
+++ b/testing/web-platform/tests/IndexedDB/interfaces.idl
@@ -78,16 +78,17 @@ interface IDBDatabase : EventTarget {
     readonly    attribute DOMString          name;
     readonly    attribute unsigned long long version;
     readonly    attribute DOMStringList      objectStoreNames;
     IDBObjectStore createObjectStore (DOMString name, optional IDBObjectStoreParameters optionalParameters);
     void           deleteObjectStore (DOMString name);
     IDBTransaction transaction ((DOMString or sequence<DOMString>) storeNames, optional IDBTransactionMode mode = "readonly");
     void           close ();
                 attribute EventHandler       onabort;
+                attribute EventHandler       onclose;
                 attribute EventHandler       onerror;
                 attribute EventHandler       onversionchange;
 };
 
 interface IDBObjectStore {
     attribute DOMString name;
     readonly    attribute any            keyPath;
     readonly    attribute DOMStringList  indexNames;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -97,16 +97,25 @@
   },
   "AUDIOSTREAM_LATER_OPEN_MS": {
     "expires_in_version": "50",
     "kind": "exponential",
     "high": 10000,
     "n_buckets": 50,
     "description": "The length of time (in milliseconds) for the subsequent opens of AudioStream."
   },
+  "AUDIOSTREAM_BACKEND_USED": {
+    "alert_emails": ["padenot@mozilla.com", "kinetik@flim.org"],
+    "bug_numbers": [1280630],
+    "expires_in_version": "55",
+    "kind": "enumerated",
+    "n_values": 16,
+    "description": "The operating system audio back-end used when successfully opening an audio stream, or whether the failure occurred on the first try or not <https://dxr.mozilla.org/mozilla-central/search?q=AUDIOSTREAM_BACKEND_ID_STR>",
+    "releaseChannelCollection": "opt-out"
+  },
   "BACKGROUNDFILESAVER_THREAD_COUNT": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 21,
     "description": "Maximum number of concurrent threads reached during a given download session"
   },
   "BATTERY_STATUS_COUNT": {
     "alert_emails": ["cpeterson@mozilla.com"],
--- a/toolkit/components/url-classifier/SafeBrowsing.jsm
+++ b/toolkit/components/url-classifier/SafeBrowsing.jsm
@@ -48,17 +48,16 @@ function getLists(prefName) {
 
 // These may be a comma-separated lists of tables.
 const phishingLists = getLists("urlclassifier.phishTable");
 const malwareLists = getLists("urlclassifier.malwareTable");
 const downloadBlockLists = getLists("urlclassifier.downloadBlockTable");
 const downloadAllowLists = getLists("urlclassifier.downloadAllowTable");
 const trackingProtectionLists = getLists("urlclassifier.trackingTable");
 const trackingProtectionWhitelists = getLists("urlclassifier.trackingWhitelistTable");
-const forbiddenLists = getLists("urlclassifier.forbiddenTable");
 const blockedLists = getLists("urlclassifier.blockedTable");
 
 this.SafeBrowsing = {
 
   init: function() {
     if (this.initialized) {
       log("Already initialized");
       return;
@@ -105,30 +104,26 @@ this.SafeBrowsing = {
       this.registerTableWithURLs(downloadAllowLists[i]);
     }
     for (let i = 0; i < trackingProtectionLists.length; ++i) {
       this.registerTableWithURLs(trackingProtectionLists[i]);
     }
     for (let i = 0; i < trackingProtectionWhitelists.length; ++i) {
       this.registerTableWithURLs(trackingProtectionWhitelists[i]);
     }
-    for (let i = 0; i < forbiddenLists.length; ++i) {
-      this.registerTableWithURLs(forbiddenLists[i]);
-    }
     for (let i = 0; i < blockedLists.length; ++i) {
       this.registerTableWithURLs(blockedLists[i]);
     }
   },
 
 
   initialized:      false,
   phishingEnabled:  false,
   malwareEnabled:   false,
   trackingEnabled:  false,
-  forbiddenEnabled: false,
   blockedEnabled:   false,
 
   updateURL:             null,
   gethashURL:            null,
 
   reportURL:             null,
 
   getReportURL: function(kind, URI) {
@@ -165,17 +160,16 @@ this.SafeBrowsing = {
 
   readPrefs: function() {
     log("reading prefs");
 
     this.debug = Services.prefs.getBoolPref("browser.safebrowsing.debug");
     this.phishingEnabled = Services.prefs.getBoolPref("browser.safebrowsing.phishing.enabled");
     this.malwareEnabled = Services.prefs.getBoolPref("browser.safebrowsing.malware.enabled");
     this.trackingEnabled = Services.prefs.getBoolPref("privacy.trackingprotection.enabled") || Services.prefs.getBoolPref("privacy.trackingprotection.pbmode.enabled");
-    this.forbiddenEnabled = Services.prefs.getBoolPref("browser.safebrowsing.forbiddenURIs.enabled");
     this.blockedEnabled = Services.prefs.getBoolPref("browser.safebrowsing.blockedURIs.enabled");
     this.updateProviderURLs();
     this.registerTables();
 
     // XXX The listManager backend gets confused if this is called before the
     // lists are registered. So only call it here when a pref changes, and not
     // when doing initialization. I expect to refactor this later, so pardon the hack.
     if (this.initialized) {
@@ -243,18 +237,17 @@ this.SafeBrowsing = {
         log("Update URL given but no lists managed for provider: " + provider);
       }
     }, this);
   },
 
   controlUpdateChecking: function() {
     log("phishingEnabled:", this.phishingEnabled, "malwareEnabled:",
         this.malwareEnabled, "trackingEnabled:", this.trackingEnabled,
-       "forbiddenEnabled:", this.forbiddenEnabled, "blockedEnabled:",
-        this.blockedEnabled);
+        "blockedEnabled:", this.blockedEnabled);
 
     let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
                       getService(Ci.nsIUrlListManager);
 
     for (let i = 0; i < phishingLists.length; ++i) {
       if (this.phishingEnabled) {
         listManager.enableUpdate(phishingLists[i]);
       } else {
@@ -291,23 +284,16 @@ this.SafeBrowsing = {
     }
     for (let i = 0; i < trackingProtectionWhitelists.length; ++i) {
       if (this.trackingEnabled) {
         listManager.enableUpdate(trackingProtectionWhitelists[i]);
       } else {
         listManager.disableUpdate(trackingProtectionWhitelists[i]);
       }
     }
-    for (let i = 0; i < forbiddenLists.length; ++i) {
-      if (this.forbiddenEnabled) {
-        listManager.enableUpdate(forbiddenLists[i]);
-      } else {
-        listManager.disableUpdate(forbiddenLists[i]);
-      }
-    }
     for (let i = 0; i < blockedLists.length; ++i) {
       if (this.blockedEnabled) {
         listManager.enableUpdate(blockedLists[i]);
       } else {
         listManager.disableUpdate(blockedLists[i]);
       }
     }
     listManager.maybeToggleUpdateChecking();
@@ -320,17 +306,16 @@ this.SafeBrowsing = {
     const phishURL    = "itisatrap.org/firefox/its-a-trap.html";
     const malwareURL  = "itisatrap.org/firefox/its-an-attack.html";
     const unwantedURL = "itisatrap.org/firefox/unwanted.html";
     const trackerURLs = [
       "trackertest.org/",
       "itisatracker.org/",
     ];
     const whitelistURL  = "itisatrap.org/?resource=itisatracker.org";
-    const forbiddenURL  = "itisatrap.org/firefox/forbidden.html";
     const blockedURL    = "itisatrap.org/firefox/blocked.html";
 
     let update = "n:1000\ni:test-malware-simple\nad:1\n" +
                  "a:1:32:" + malwareURL.length + "\n" +
                  malwareURL + "\n";
     update += "n:1000\ni:test-phish-simple\nad:1\n" +
               "a:1:32:" + phishURL.length + "\n" +
               phishURL  + "\n";
@@ -341,19 +326,16 @@ this.SafeBrowsing = {
               "ad:" + trackerURLs.length + "\n";
     trackerURLs.forEach((trackerURL, i) => {
       update += "a:" + (i + 1) + ":32:" + trackerURL.length + "\n" +
                 trackerURL + "\n";
     });
     update += "n:1000\ni:test-trackwhite-simple\nad:1\n" +
               "a:1:32:" + whitelistURL.length + "\n" +
               whitelistURL;
-    update += "n:1000\ni:test-forbid-simple\nad:1\n" +
-              "a:1:32:" + forbiddenURL.length + "\n" +
-              forbiddenURL;
     update += "n:1000\ni:test-block-simple\nad:1\n" +
               "a:1:32:" + blockedURL.length + "\n" +
               blockedURL;
     log("addMozEntries:", update);
 
     let db = Cc["@mozilla.org/url-classifier/dbservice;1"].
              getService(Ci.nsIUrlClassifierDBService);
 
@@ -367,17 +349,17 @@ this.SafeBrowsing = {
         Services.obs.notifyObservers(db, "mozentries-update-finished", "error");
       },
       updateSuccess:      function() {
         Services.obs.notifyObservers(db, "mozentries-update-finished", "success");
       }
     };
 
     try {
-      let tables = "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-forbid-simple,test-block-simple";
+      let tables = "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple";
       db.beginUpdate(dummyListener, tables, "");
       db.beginStream("", "");
       db.updateStream(update);
       db.finishStream();
       db.finishUpdate();
     } catch(ex) {
       // beginUpdate will throw harmlessly if there's an existing update in progress, ignore failures.
       log("addMozEntries failed!", ex);
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -67,31 +67,27 @@ LazyLogModule gUrlClassifierDbServiceLog
 #define CHECK_PHISHING_DEFAULT  false
 
 #define CHECK_TRACKING_PREF     "privacy.trackingprotection.enabled"
 #define CHECK_TRACKING_DEFAULT  false
 
 #define CHECK_TRACKING_PB_PREF    "privacy.trackingprotection.pbmode.enabled"
 #define CHECK_TRACKING_PB_DEFAULT false
 
-#define CHECK_FORBIDDEN_PREF    "browser.safebrowsing.forbiddenURIs.enabled"
-#define CHECK_FORBIDDEN_DEFAULT false
-
 #define CHECK_BLOCKED_PREF    "browser.safebrowsing.blockedURIs.enabled"
 #define CHECK_BLOCKED_DEFAULT false
 
 #define GETHASH_NOISE_PREF      "urlclassifier.gethashnoise"
 #define GETHASH_NOISE_DEFAULT   4
 
 // Comma-separated lists
 #define MALWARE_TABLE_PREF              "urlclassifier.malwareTable"
 #define PHISH_TABLE_PREF                "urlclassifier.phishTable"
 #define TRACKING_TABLE_PREF             "urlclassifier.trackingTable"
 #define TRACKING_WHITELIST_TABLE_PREF   "urlclassifier.trackingWhitelistTable"
-#define FORBIDDEN_TABLE_PREF            "urlclassifier.forbiddenTable"
 #define BLOCKED_TABLE_PREF              "urlclassifier.blockedTable"
 #define DOWNLOAD_BLOCK_TABLE_PREF       "urlclassifier.downloadBlockTable"
 #define DOWNLOAD_ALLOW_TABLE_PREF       "urlclassifier.downloadAllowTable"
 #define DISALLOW_COMPLETION_TABLE_PREF  "urlclassifier.disallow_completions"
 
 #define CONFIRM_AGE_PREF        "urlclassifier.max-complete-age"
 #define CONFIRM_AGE_DEFAULT_SEC (45 * 60)
 
@@ -194,19 +190,16 @@ TablesToResponse(const nsACString& table
     return NS_ERROR_PHISHING_URI;
   }
   if (FindInReadable(NS_LITERAL_CSTRING("-track-"), tables)) {
     return NS_ERROR_TRACKING_URI;
   }
   if (FindInReadable(NS_LITERAL_CSTRING("-unwanted-"), tables)) {
     return NS_ERROR_UNWANTED_URI;
   }
-  if (FindInReadable(NS_LITERAL_CSTRING("-forbid-"), tables)) {
-    return NS_ERROR_FORBIDDEN_URI;
-  }
   if (FindInReadable(NS_LITERAL_CSTRING("-block-"), tables)) {
     return NS_ERROR_BLOCKED_URI;
   }
   return NS_OK;
 }
 
 static nsCString
 ProcessLookupResults(LookupResultArray* results)
@@ -1039,17 +1032,16 @@ class nsUrlClassifierClassifyCallback fi
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIURLCLASSIFIERCALLBACK
 
   nsUrlClassifierClassifyCallback(nsIURIClassifierCallback *c,
                                   bool checkMalware,
                                   bool checkPhishing,
                                   bool checkTracking,
-                                  bool checkForbidden,
                                   bool checkBlocked)
     : mCallback(c)
     {}
 
 private:
   ~nsUrlClassifierClassifyCallback() {}
 
   nsCOMPtr<nsIURIClassifierCallback> mCallback;
@@ -1100,17 +1092,16 @@ nsUrlClassifierDBService::GetInstance(ns
   return sUrlClassifierDBService;
 }
 
 
 nsUrlClassifierDBService::nsUrlClassifierDBService()
  : mCheckMalware(CHECK_MALWARE_DEFAULT)
  , mCheckPhishing(CHECK_PHISHING_DEFAULT)
  , mCheckTracking(CHECK_TRACKING_DEFAULT)
- , mCheckForbiddenURIs(CHECK_FORBIDDEN_DEFAULT)
  , mCheckBlockedURIs(CHECK_BLOCKED_DEFAULT)
  , mInUpdate(false)
 {
 }
 
 nsUrlClassifierDBService::~nsUrlClassifierDBService()
 {
   sUrlClassifierDBService = nullptr;
@@ -1148,22 +1139,16 @@ nsUrlClassifierDBService::ReadTablesFrom
   }
 
   Preferences::GetCString(TRACKING_WHITELIST_TABLE_PREF, &tables);
   if (!tables.IsEmpty()) {
     allTables.Append(',');
     allTables.Append(tables);
   }
 
-  Preferences::GetCString(FORBIDDEN_TABLE_PREF, &tables);
-  if (!tables.IsEmpty()) {
-    allTables.Append(',');
-    allTables.Append(tables);
-  }
-
   Preferences::GetCString(BLOCKED_TABLE_PREF, &tables);
   if (!tables.IsEmpty()) {
     allTables.Append(',');
     allTables.Append(tables);
   }
 
   Classifier::SplitTables(allTables, mGethashTables);
 
@@ -1189,18 +1174,16 @@ nsUrlClassifierDBService::Init()
   // Retrieve all the preferences.
   mCheckMalware = Preferences::GetBool(CHECK_MALWARE_PREF,
     CHECK_MALWARE_DEFAULT);
   mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
     CHECK_PHISHING_DEFAULT);
   mCheckTracking =
     Preferences::GetBool(CHECK_TRACKING_PREF, CHECK_TRACKING_DEFAULT) ||
     Preferences::GetBool(CHECK_TRACKING_PB_PREF, CHECK_TRACKING_PB_DEFAULT);
-  mCheckForbiddenURIs = Preferences::GetBool(CHECK_FORBIDDEN_PREF,
-    CHECK_FORBIDDEN_DEFAULT);
   mCheckBlockedURIs = Preferences::GetBool(CHECK_BLOCKED_PREF,
     CHECK_BLOCKED_DEFAULT);
   uint32_t gethashNoise = Preferences::GetUint(GETHASH_NOISE_PREF,
     GETHASH_NOISE_DEFAULT);
   gFreshnessGuarantee = Preferences::GetInt(CONFIRM_AGE_PREF,
     CONFIRM_AGE_DEFAULT_SEC);
   ReadTablesFromPrefs();
 
@@ -1255,25 +1238,23 @@ nsUrlClassifierDBService::Init()
   // XXX: Do we *really* need to be able to change all of these at runtime?
   // Note: These observers should only be added when everything else above has
   //       succeeded. Failing to do so can cause long shutdown times in certain
   //       situations. See Bug 1247798 and Bug 1244803.
   Preferences::AddStrongObserver(this, CHECK_MALWARE_PREF);
   Preferences::AddStrongObserver(this, CHECK_PHISHING_PREF);
   Preferences::AddStrongObserver(this, CHECK_TRACKING_PREF);
   Preferences::AddStrongObserver(this, CHECK_TRACKING_PB_PREF);
-  Preferences::AddStrongObserver(this, CHECK_FORBIDDEN_PREF);
   Preferences::AddStrongObserver(this, CHECK_BLOCKED_PREF);
   Preferences::AddStrongObserver(this, GETHASH_NOISE_PREF);
   Preferences::AddStrongObserver(this, CONFIRM_AGE_PREF);
   Preferences::AddStrongObserver(this, PHISH_TABLE_PREF);
   Preferences::AddStrongObserver(this, MALWARE_TABLE_PREF);
   Preferences::AddStrongObserver(this, TRACKING_TABLE_PREF);
   Preferences::AddStrongObserver(this, TRACKING_WHITELIST_TABLE_PREF);
-  Preferences::AddStrongObserver(this, FORBIDDEN_TABLE_PREF);
   Preferences::AddStrongObserver(this, BLOCKED_TABLE_PREF);
   Preferences::AddStrongObserver(this, DOWNLOAD_BLOCK_TABLE_PREF);
   Preferences::AddStrongObserver(this, DOWNLOAD_ALLOW_TABLE_PREF);
   Preferences::AddStrongObserver(this, DISALLOW_COMPLETION_TABLE_PREF);
 
   return NS_OK;
 }
 
@@ -1301,22 +1282,16 @@ nsUrlClassifierDBService::BuildTables(bo
       tables.Append(tracking);
     }
     Preferences::GetCString(TRACKING_WHITELIST_TABLE_PREF, &trackingWhitelist);
     if (!trackingWhitelist.IsEmpty()) {
       tables.Append(',');
       tables.Append(trackingWhitelist);
     }
   }
-  nsAutoCString forbidden;
-  Preferences::GetCString(FORBIDDEN_TABLE_PREF, &forbidden);
-  if (mCheckForbiddenURIs && !forbidden.IsEmpty()) {
-    tables.Append(',');
-    tables.Append(forbidden);
-  }
   nsAutoCString blocked;
   Preferences::GetCString(BLOCKED_TABLE_PREF, &blocked);
   if (mCheckBlockedURIs && !blocked.IsEmpty()) {
     tables.Append(',');
     tables.Append(blocked);
   }
 
   if (StringBeginsWith(tables, NS_LITERAL_CSTRING(","))) {
@@ -1330,25 +1305,24 @@ nsUrlClassifierDBService::Classify(nsIPr
                                    bool aTrackingProtectionEnabled,
                                    nsIURIClassifierCallback* c,
                                    bool* result)
 {
   NS_ENSURE_ARG(aPrincipal);
   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
 
   if (!(mCheckMalware || mCheckPhishing || aTrackingProtectionEnabled ||
-        mCheckForbiddenURIs || mCheckBlockedURIs)) {
+        mCheckBlockedURIs)) {
     *result = false;
     return NS_OK;
   }
 
   RefPtr<nsUrlClassifierClassifyCallback> callback =
     new nsUrlClassifierClassifyCallback(c, mCheckMalware, mCheckPhishing,
-                                        mCheckTracking, mCheckForbiddenURIs,
-                                        mCheckBlockedURIs);
+                                        mCheckTracking, mCheckBlockedURIs);
   if (!callback) return NS_ERROR_OUT_OF_MEMORY;
 
   nsAutoCString tables;
   BuildTables(aTrackingProtectionEnabled, tables);
 
   nsresult rv = LookupURI(aPrincipal, tables, callback, false, result);
   if (rv == NS_ERROR_MALFORMED_URI) {
     *result = false;
@@ -1649,28 +1623,24 @@ nsUrlClassifierDBService::Observe(nsISup
     } else if (NS_LITERAL_STRING(CHECK_PHISHING_PREF).Equals(aData)) {
       mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
         CHECK_PHISHING_DEFAULT);
     } else if (NS_LITERAL_STRING(CHECK_TRACKING_PREF).Equals(aData) ||
                NS_LITERAL_STRING(CHECK_TRACKING_PB_PREF).Equals(aData)) {
       mCheckTracking =
         Preferences::GetBool(CHECK_TRACKING_PREF, CHECK_TRACKING_DEFAULT) ||
         Preferences::GetBool(CHECK_TRACKING_PB_PREF, CHECK_TRACKING_PB_DEFAULT);
-    } else if (NS_LITERAL_STRING(CHECK_FORBIDDEN_PREF).Equals(aData)) {
-      mCheckForbiddenURIs = Preferences::GetBool(CHECK_FORBIDDEN_PREF,
-        CHECK_FORBIDDEN_DEFAULT);
     } else if (NS_LITERAL_STRING(CHECK_BLOCKED_PREF).Equals(aData)) {
       mCheckBlockedURIs = Preferences::GetBool(CHECK_BLOCKED_PREF,
         CHECK_BLOCKED_DEFAULT);
     } else if (
       NS_LITERAL_STRING(PHISH_TABLE_PREF).Equals(aData) ||
       NS_LITERAL_STRING(MALWARE_TABLE_PREF).Equals(aData) ||
       NS_LITERAL_STRING(TRACKING_TABLE_PREF).Equals(aData) ||
       NS_LITERAL_STRING(TRACKING_WHITELIST_TABLE_PREF).Equals(aData) ||
-      NS_LITERAL_STRING(FORBIDDEN_TABLE_PREF).Equals(aData) ||
       NS_LITERAL_STRING(BLOCKED_TABLE_PREF).Equals(aData) ||
       NS_LITERAL_STRING(DOWNLOAD_BLOCK_TABLE_PREF).Equals(aData) ||
       NS_LITERAL_STRING(DOWNLOAD_ALLOW_TABLE_PREF).Equals(aData) ||
       NS_LITERAL_STRING(DISALLOW_COMPLETION_TABLE_PREF).Equals(aData)) {
       // Just read everything again.
       ReadTablesFromPrefs();
     } else if (NS_LITERAL_STRING(CONFIRM_AGE_PREF).Equals(aData)) {
       gFreshnessGuarantee = Preferences::GetInt(CONFIRM_AGE_PREF,
@@ -1698,23 +1668,21 @@ nsUrlClassifierDBService::Shutdown()
   mCompleters.Clear();
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     prefs->RemoveObserver(CHECK_MALWARE_PREF, this);
     prefs->RemoveObserver(CHECK_PHISHING_PREF, this);
     prefs->RemoveObserver(CHECK_TRACKING_PREF, this);
     prefs->RemoveObserver(CHECK_TRACKING_PB_PREF, this);
-    prefs->RemoveObserver(CHECK_FORBIDDEN_PREF, this);
     prefs->RemoveObserver(CHECK_BLOCKED_PREF, this);
     prefs->RemoveObserver(PHISH_TABLE_PREF, this);
     prefs->RemoveObserver(MALWARE_TABLE_PREF, this);
     prefs->RemoveObserver(TRACKING_TABLE_PREF, this);
     prefs->RemoveObserver(TRACKING_WHITELIST_TABLE_PREF, this);
-    prefs->RemoveObserver(FORBIDDEN_TABLE_PREF, this);
     prefs->RemoveObserver(BLOCKED_TABLE_PREF, this);
     prefs->RemoveObserver(DOWNLOAD_BLOCK_TABLE_PREF, this);
     prefs->RemoveObserver(DOWNLOAD_ALLOW_TABLE_PREF, this);
     prefs->RemoveObserver(DISALLOW_COMPLETION_TABLE_PREF, this);
     prefs->RemoveObserver(CONFIRM_AGE_PREF, this);
   }
 
   DebugOnly<nsresult> rv;
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -113,20 +113,16 @@ private:
   // TRUE if the nsURIClassifier implementation should check for phishing
   // uris on document loads.
   bool mCheckPhishing;
 
   // TRUE if the nsURIClassifier implementation should check for tracking
   // uris on document loads.
   bool mCheckTracking;
 
-  // TRUE if the nsURIClassifier implementation should check for forbidden
-  // uris on document loads.
-  bool mCheckForbiddenURIs;
-
   // TRUE if the nsURIClassifier implementation should check for blocked
   // uris on document loads.
   bool mCheckBlockedURIs;
 
   // TRUE if a BeginUpdate() has been called without an accompanying
   // CancelUpdate()/FinishUpdate().  This is used to prevent competing
   // updates, not to determine whether an update is still being
   // processed.
--- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
+++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
@@ -50,39 +50,36 @@ function delFile(name) {
 }
 
 function cleanUp() {
   delFile("urlclassifier3.sqlite");
   delFile("safebrowsing/classifier.hashkey");
   delFile("safebrowsing/test-phish-simple.sbstore");
   delFile("safebrowsing/test-malware-simple.sbstore");
   delFile("safebrowsing/test-unwanted-simple.sbstore");
-  delFile("safebrowsing/test-forbid-simple.sbstore");
   delFile("safebrowsing/test-block-simple.sbstore");
   delFile("safebrowsing/test-track-simple.sbstore");
   delFile("safebrowsing/test-trackwhite-simple.sbstore");
   delFile("safebrowsing/test-phish-simple.cache");
   delFile("safebrowsing/test-malware-simple.cache");
   delFile("safebrowsing/test-unwanted-simple.cache");
-  delFile("safebrowsing/test-forbid-simple.cache");
   delFile("safebrowsing/test-block-simple.cache");
   delFile("safebrowsing/test-track-simple.cache");
   delFile("safebrowsing/test-trackwhite-simple.cache");
   delFile("safebrowsing/test-phish-simple.pset");
   delFile("safebrowsing/test-malware-simple.pset");
   delFile("safebrowsing/test-unwanted-simple.pset");
-  delFile("safebrowsing/test-forbid-simple.pset");
   delFile("safebrowsing/test-block-simple.pset");
   delFile("safebrowsing/test-track-simple.pset");
   delFile("safebrowsing/test-trackwhite-simple.pset");
   delFile("testLarge.pset");
   delFile("testNoDelta.pset");
 }
 
-var allTables = "test-phish-simple,test-malware-simple,test-unwanted-simple,test-forbid-simple,test-track-simple,test-trackwhite-simple,test-block-simple";
+var allTables = "test-phish-simple,test-malware-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple";
 
 var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService);
 var streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
                     .getService(Ci.nsIUrlClassifierStreamUpdater);
 
 
 /*
  * Builds an update from an object that looks like:
@@ -129,20 +126,16 @@ function buildPhishingUpdate(chunks, has
 function buildMalwareUpdate(chunks, hashSize) {
   return buildUpdate({"test-malware-simple" : chunks}, hashSize);
 }
 
 function buildUnwantedUpdate(chunks, hashSize) {
   return buildUpdate({"test-unwanted-simple" : chunks}, hashSize);
 }
 
-function buildForbiddenUpdate(chunks, hashSize) {
-  return buildUpdate({"test-forbid-simple" : chunks}, hashSize);
-}
-
 function buildBlockedUpdate(chunks, hashSize) {
   return buildUpdate({"test-block-simple" : chunks}, hashSize);
 }
 
 function buildBareUpdate(chunks, hashSize) {
   return buildUpdate({"" : chunks}, hashSize);
 }
 
@@ -264,21 +257,16 @@ malwareUrlsExist: function(urls, cb)
   this.checkUrls(urls, 'test-malware-simple', cb);
 },
 
 unwantedUrlsExist: function(urls, cb)
 {
   this.checkUrls(urls, 'test-unwanted-simple', cb);
 },
 
-forbiddenUrlsExist: function(urls, cb)
-{
-  this.checkUrls(urls, 'test-forbid-simple', cb);
-},
-
 blockedUrlsExist: function(urls, cb)
 {
   this.checkUrls(urls, 'test-block-simple', cb);
 },
 
 subsDontExist: function(urls, cb)
 {
   // XXX: there's no interface for checking items in the subs table
--- a/toolkit/components/url-classifier/tests/unit/test_dbservice.js
+++ b/toolkit/components/url-classifier/tests/unit/test_dbservice.js
@@ -49,41 +49,39 @@ var chunk6 = chunk6Urls.join("\n");
 
 var chunk7Urls = [
   "l.com/m",
   "n.com/o",
   ];
 var chunk7 = chunk7Urls.join("\n");
 
 // we are going to add chunks 1, 2, 4, 5, and 6 to phish-simple,
-// chunk 2 to malware-simple, chunk 3 to unwanted-simple,
-// chunk 4 to forbid-simple, and chunk 7 to block-simple.
+// chunk 2 to malware-simple, and chunk 3 to unwanted-simple,
+// and chunk 7 to block-simple.
 // Then we'll remove the urls in chunk3 from phish-simple, then
 // expire chunk 1 and chunks 4-7 from phish-simple.
 var phishExpected = {};
 var phishUnexpected = {};
 var malwareExpected = {};
 var unwantedExpected = {};
-var forbiddenExpected = {};
 var blockedExpected = {};
 for (var i = 0; i < chunk2Urls.length; i++) {
   phishExpected[chunk2Urls[i]] = true;
   malwareExpected[chunk2Urls[i]] = true;
 }
 for (var i = 0; i < chunk3Urls.length; i++) {
   unwantedExpected[chunk3Urls[i]] = true;
   delete phishExpected[chunk3Urls[i]];
   phishUnexpected[chunk3Urls[i]] = true;
 }
 for (var i = 0; i < chunk1Urls.length; i++) {
   // chunk1 urls are expired
   phishUnexpected[chunk1Urls[i]] = true;
 }
 for (var i = 0; i < chunk4Urls.length; i++) {
-  forbiddenExpected[chunk4Urls[i]] = true;
   // chunk4 urls are expired
   phishUnexpected[chunk4Urls[i]] = true;
 }
 for (var i = 0; i < chunk5Urls.length; i++) {
   // chunk5 urls are expired
   phishUnexpected[chunk5Urls[i]] = true;
 }
 for (var i = 0; i < chunk6Urls.length; i++) {
@@ -127,17 +125,17 @@ function checkNoHost()
 function tablesCallbackWithoutSub(tables)
 {
   var parts = tables.split("\n");
   parts.sort();
 
   // there's a leading \n here because splitting left an empty string
   // after the trailing newline, which will sort first
   do_check_eq(parts.join("\n"),
-              "\ntest-block-simple;a:1\ntest-forbid-simple;a:1\ntest-malware-simple;a:1\ntest-phish-simple;a:2\ntest-unwanted-simple;a:1");
+              "\ntest-block-simple;a:1\ntest-malware-simple;a:1\ntest-phish-simple;a:2\ntest-unwanted-simple;a:1");
 
   checkNoHost();
 }
 
 
 function expireSubSuccess(result) {
   dbservice.getTables(tablesCallbackWithoutSub);
 }
@@ -145,17 +143,17 @@ function expireSubSuccess(result) {
 function tablesCallbackWithSub(tables)
 {
   var parts = tables.split("\n");
   parts.sort();
 
   // there's a leading \n here because splitting left an empty string
   // after the trailing newline, which will sort first
   do_check_eq(parts.join("\n"),
-              "\ntest-block-simple;a:1\ntest-forbid-simple;a:1\ntest-malware-simple;a:1\ntest-phish-simple;a:2:s:3\ntest-unwanted-simple;a:1");
+              "\ntest-block-simple;a:1\ntest-malware-simple;a:1\ntest-phish-simple;a:2:s:3\ntest-unwanted-simple;a:1");
 
   // verify that expiring a sub chunk removes its name from the list
   var data =
     "n:1000\n" +
     "i:test-phish-simple\n" +
     "sd:3\n";
 
   doSimpleUpdate(data, expireSubSuccess, testFailure);
@@ -204,26 +202,16 @@ function unwantedExists(result) {
 
   try {
     do_check_true(result.indexOf("test-unwanted-simple") != -1);
   } finally {
     checkDone();
   }
 }
 
-function forbiddenExists(result) {
-  dumpn("forbiddenExists: " + result);
-
-  try {
-    do_check_true(result.indexOf("test-forbid-simple") != -1);
-  } finally {
-    checkDone();
-  }
-}
-
 function blockedExists(result) {
   dumpn("blockedExists: " + result);
 
   try {
     do_check_true(result.indexOf("test-block-simple") != -1);
   } finally {
     checkDone();
   }
@@ -253,22 +241,16 @@ function checkState()
   }
 
   for (var key in unwantedExpected) {
     var principal = secMan.createCodebasePrincipal(iosvc.newURI("http://" + key, null, null), {});
     dbservice.lookup(principal, allTables, unwantedExists, true);
     numExpecting++;
   }
 
-  for (var key in forbiddenExpected) {
-    var principal = secMan.createCodebasePrincipal(iosvc.newURI("http://" + key, null, null), {});
-    dbservice.lookup(principal, allTables, forbiddenExists, true);
-    numExpecting++;
-  }
-
   for (var key in blockedExpected) {
     var principal = secMan.createCodebasePrincipal(iosvc.newURI("http://" + key, null, null), {});
     dbservice.lookup(principal, allTables, blockedExists, true);
     numExpecting++;
   }
 }
 
 function testSubSuccess(result)
@@ -314,19 +296,16 @@ function do_adds() {
     "a:6:32:" + chunk6.length + "\n" +
     chunk6 + "\n" +
     "i:test-malware-simple\n" +
     "a:1:32:" + chunk2.length + "\n" +
     chunk2 + "\n" +
     "i:test-unwanted-simple\n" +
     "a:1:32:" + chunk3.length + "\n" +
     chunk3 + "\n" +
-    "i:test-forbid-simple\n" +
-    "a:1:32:" + chunk4.length + "\n" +
-    chunk4 + "\n" +
     "i:test-block-simple\n" +
     "a:1:32:" + chunk7.length + "\n" +
     chunk7 + "\n";
 
   doSimpleUpdate(data, testAddSuccess, testFailure);
 }
 
 function run_test() {
--- a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js
+++ b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js
@@ -127,17 +127,16 @@ function testErrorUrlForward() {
   doTest([update], assertions, true);
 }
 
 function testMultipleTables() {
   var add1Urls = [ "foo-multiple.com/a", "bar-multiple.com/c" ];
   var add2Urls = [ "foo-multiple.com/b" ];
   var add3Urls = [ "bar-multiple.com/d" ];
   var add4Urls = [ "bar-multiple.com/e" ];
-  var add5Urls = [ "bar-multiple.com/f" ];
   var add6Urls = [ "bar-multiple.com/g" ];
 
   var update = "n:1000\n";
   update += "i:test-phish-simple\n";
 
   var update1 = buildBareUpdate(
     [{ "chunkNum" : 1,
        "urls" : add1Urls }]);
@@ -156,34 +155,27 @@ function testMultipleTables() {
   update += "u:data:," + encodeURIComponent(update3) + "\n";
 
   update += "i:test-unwanted-simple\n";
   var update4 = buildBareUpdate(
     [{ "chunkNum" : 4,
        "urls" : add4Urls }]);
   update += "u:data:," + encodeURIComponent(update4) + "\n";
 
-  update += "i:test-forbid-simple\n";
-  var update5 = buildBareUpdate(
-    [{ "chunkNum" : 5,
-       "urls" : add5Urls }]);
-  update += "u:data:," + encodeURIComponent(update5) + "\n";
-
   update += "i:test-block-simple\n";
   var update6 = buildBareUpdate(
     [{ "chunkNum" : 6,
        "urls" : add6Urls }]);
   update += "u:data:," + encodeURIComponent(update6) + "\n";
 
   var assertions = {
-    "tableData" : "test-block-simple;a:6\ntest-forbid-simple;a:5\ntest-malware-simple;a:3\ntest-phish-simple;a:1-2\ntest-unwanted-simple;a:4",
+    "tableData" : "test-block-simple;a:6\ntest-malware-simple;a:3\ntest-phish-simple;a:1-2\ntest-unwanted-simple;a:4",
     "urlsExist" : add1Urls.concat(add2Urls),
     "malwareUrlsExist" : add3Urls,
     "unwantedUrlsExist" : add4Urls,
-    "forbiddenUrlsExist" : add5Urls,
     "blockedUrlsExist" : add6Urls
   };
 
   doTest([update], assertions, false);
 }
 
 function Observer(callback) {
   this.observe = callback;
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -721,17 +721,16 @@
 #define MODULE NS_ERROR_MODULE_URILOADER
   ERROR(NS_ERROR_WONT_HANDLE_CONTENT,   FAILURE(1)),
   /* The load has been cancelled because it was found on a malware or phishing
    * blacklist. */
   ERROR(NS_ERROR_MALWARE_URI,           FAILURE(30)),
   ERROR(NS_ERROR_PHISHING_URI,          FAILURE(31)),
   ERROR(NS_ERROR_TRACKING_URI,          FAILURE(34)),
   ERROR(NS_ERROR_UNWANTED_URI,          FAILURE(35)),
-  ERROR(NS_ERROR_FORBIDDEN_URI,         FAILURE(36)),
   ERROR(NS_ERROR_BLOCKED_URI,           FAILURE(37)),
   /* Used when "Save Link As..." doesn't see the headers quickly enough to
    * choose a filename.  See nsContextMenu.js. */
   ERROR(NS_ERROR_SAVE_LINK_AS_TIMEOUT,  FAILURE(32)),
   /* Used when the data from a channel has already been parsed and cached so it
    * doesn't need to be reparsed from the original source. */
   ERROR(NS_ERROR_PARSED_DATA_CACHED,    FAILURE(33)),