Merge m-c to b2g-inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 29 Jul 2015 13:50:26 -0400
changeset 286942 ee17e2b7ede8b9feaf161280a10a7618e043d4bc
parent 286941 26fbff8ad033dbb6cf8835efac824acc26ca0ec1 (current diff)
parent 286875 62cd40885e9362dc726f681675b64aa9577982aa (diff)
child 286943 d30bd2a5787226a5ce22c201cb5fa6ac13c8c4c2
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound. a=merge CLOSED TREE
browser/devtools/styleinspector/test/browser_ruleview_override.js
security/manager/ssl/tests/mochitest/bugs/mochitest.ini
security/manager/ssl/tests/mochitest/bugs/test_bug480509.html
testing/web-platform/meta/IndexedDB/idbcursor_delete_index5.htm.ini
testing/web-platform/meta/IndexedDB/idbcursor_delete_objectstore5.htm.ini
testing/web-platform/meta/workers/Worker_ErrorEvent_bubbles_cancelable.htm.ini
toolkit/components/url-classifier/PrivateBrowsingTrackingProtectionWhitelist.js
toolkit/components/url-classifier/nsIPrivateBrowsingTrackingProtectionWhitelist.idl
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -14,16 +14,18 @@ namespace mozilla {
 namespace a11y {
 
 bool
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
 {
   if (mShutdown)
     return true;
 
+  CheckDocTree();
+
   if (aData.NewTree().IsEmpty()) {
     NS_ERROR("no children being added");
     return false;
   }
 
   ProxyAccessible* parent = GetAccessible(aData.ID());
 
   // XXX This should really never happen, but sometimes we fail to fire the
@@ -43,16 +45,18 @@ DocAccessibleParent::RecvShowEvent(const
   MOZ_ASSERT(consumed == aData.NewTree().Length());
 #ifdef DEBUG
   for (uint32_t i = 0; i < consumed; i++) {
     uint64_t id = aData.NewTree()[i].ID();
     MOZ_ASSERT(mAccessibles.GetEntry(id));
   }
 #endif
 
+  CheckDocTree();
+
   return true;
 }
 
 uint32_t
 DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
                                 const nsTArray<a11y::AccessibleData>& aNewTree,
                                 uint32_t aIdx, uint32_t aIdxInParent)
 {
@@ -113,16 +117,18 @@ DocAccessibleParent::RecvHideEvent(const
     NS_ERROR("invalid root being removed!");
     return true;
   }
 
   ProxyAccessible* parent = root->Parent();
   parent->RemoveChild(root);
   root->Shutdown();
 
+  CheckDocTree();
+
   return true;
 }
 
 bool
 DocAccessibleParent::RecvEvent(const uint64_t& aID, const uint32_t& aEventType)
 {
   ProxyAccessible* proxy = GetAccessible(aID);
   if (!proxy) {
@@ -185,19 +191,22 @@ bool
 DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
 {
   // One document should never directly be the child of another.
   // We should always have at least an outer doc accessible in between.
   MOZ_ASSERT(aID);
   if (!aID)
     return false;
 
+  CheckDocTree();
+
   auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
   bool result = AddChildDoc(childDoc, aID, false);
   MOZ_ASSERT(result);
+  CheckDocTree();
   return result;
 }
 
 bool
 DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
                                  uint64_t aParentID, bool aCreating)
 {
   // We do not use GetAccessible here because we want to be sure to not get the
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -704,18 +704,22 @@
 ; Modules
 @RESPATH@/modules/*
 
 ; Safe Browsing
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
 @RESPATH@/components/nsUrlClassifierListManager.js
 @RESPATH@/components/nsUrlClassifierLib.js
+@RESPATH@/components/url-classifier.xpt
+
+; Private Browsing
+@RESPATH@/components/privatebrowsing.xpt
+@RESPATH@/components/PrivateBrowsing.manifest
 @RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
-@RESPATH@/components/url-classifier.xpt
 
 ; GNOME hooks
 #ifdef MOZ_ENABLE_GNOME_COMPONENT
 @RESPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@
 #endif
 
 ; ANGLE on Win32
 #ifdef XP_WIN32
--- a/browser/base/content/browser-trackingprotection.js
+++ b/browser/base/content/browser-trackingprotection.js
@@ -121,16 +121,19 @@ let TrackingProtection = {
     } else {
       Services.perms.add(normalizedUrl,
         "trackingprotection", Services.perms.ALLOW_ACTION);
     }
 
     // Telemetry for disable protection.
     this.eventsHistogram.add(1);
 
+    // Hide the control center.
+    document.getElementById("identity-popup").hidePopup();
+
     BrowserReload();
   },
 
   enableForCurrentPage() {
     // Remove the current host from the 'trackingprotection' consumer
     // of the permission manager. This effectively removes this host
     // from the tracking protection allowlist.
     let normalizedUrl = Services.io.newURI(
@@ -141,16 +144,19 @@ let TrackingProtection = {
       PrivateBrowsingUtils.removeFromTrackingAllowlist(normalizedUrl);
     } else {
       Services.perms.remove(normalizedUrl, "trackingprotection");
     }
 
     // Telemetry for enable protection.
     this.eventsHistogram.add(2);
 
+    // Hide the control center.
+    document.getElementById("identity-popup").hidePopup();
+
     BrowserReload();
   },
 
   showIntroPanel: Task.async(function*() {
     let mm = gBrowser.selectedBrowser.messageManager;
     let brandBundle = document.getElementById("bundle_brand");
     let brandShortName = brandBundle.getString("brandShortName");
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1134,17 +1134,19 @@ var gBrowserInit = {
     mm.addMessageListener("PageVisibility:Show", function(message) {
       if (message.target == gBrowser.selectedBrowser) {
         setTimeout(pageShowEventHandlers, 0, message.data.persisted);
       }
     });
 
     gBrowser.addEventListener("AboutTabCrashedLoad", function(event) {
 #ifdef MOZ_CRASHREPORTER
-      TabCrashReporter.onAboutTabCrashedLoad(gBrowser.getBrowserForDocument(event.target));
+      TabCrashReporter.onAboutTabCrashedLoad(gBrowser.getBrowserForDocument(event.target), {
+        crashedTabCount: SessionStore.crashedTabCount,
+      });
 #endif
     }, false, true);
 
     gBrowser.addEventListener("AboutTabCrashedMessage", function(event) {
       let ownerDoc = event.originalTarget;
 
       if (!ownerDoc.documentURI.startsWith("about:tabcrashed")) {
         return;
@@ -1166,21 +1168,17 @@ var gBrowserInit = {
       switch (event.detail.message) {
       case "closeTab":
         gBrowser.removeTab(tab, { animate: true });
         break;
       case "restoreTab":
         SessionStore.reviveCrashedTab(tab);
         break;
       case "restoreAll":
-        for (let browserWin of browserWindows()) {
-          for (let tab of browserWin.gBrowser.tabs) {
-            SessionStore.reviveCrashedTab(tab);
-          }
-        }
+        SessionStore.reviveAllCrashedTabs();
         break;
       }
     }, false, true);
 
     let uriToLoad = this._getUriToLoad();
     if (uriToLoad && uriToLoad != "about:blank") {
       if (uriToLoad instanceof Ci.nsISupportsArray) {
         let count = uriToLoad.Count();
@@ -2406,18 +2404,20 @@ function BrowserViewSource(browser) {
     outerWindowID: browser.outerWindowID,
     URL: browser.currentURI.spec,
   });
 }
 
 // doc - document to use for source, or null for this window's document
 // initialTab - name of the initial tab to display, or null for the first tab
 // imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
-function BrowserPageInfo(doc, initialTab, imageElement) {
-  var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
+// frameOuterWindowID - the id of the frame that the context menu opened in; can be null/omitted
+function BrowserPageInfo(doc, initialTab, imageElement, frameOuterWindowID) {
+  var args = {doc: doc, initialTab: initialTab, imageElement: imageElement,
+              frameOuterWindowID: frameOuterWindowID};
   var windows = Services.wm.getEnumerator("Browser:page-info");
 
   var documentURL = doc ? doc.location : window.gBrowser.selectedBrowser.contentDocumentAsCPOW.location;
 
   // Check for windows matching the url
   while (windows.hasMoreElements()) {
     var currentWindow = windows.getNext();
     if (currentWindow.closed) {
@@ -6628,16 +6628,17 @@ var gIdentityHandler = {
   IDENTITY_MODE_UNKNOWN                                : "unknownIdentity",  // No trusted identity information
   IDENTITY_MODE_USES_WEAK_CIPHER                       : "unknownIdentity weakCipher",  // SSL with RC4 cipher suite or SSL3
   IDENTITY_MODE_MIXED_DISPLAY_LOADED                   : "unknownIdentity mixedContent mixedDisplayContent",  // SSL with unauthenticated display content
   IDENTITY_MODE_MIXED_ACTIVE_LOADED                    : "unknownIdentity mixedContent mixedActiveContent",  // SSL with unauthenticated active (and perhaps also display) content
   IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED    : "unknownIdentity mixedContent mixedDisplayContentLoadedActiveBlocked",  // SSL with unauthenticated display content; unauthenticated active content is blocked.
   IDENTITY_MODE_MIXED_ACTIVE_BLOCKED                   : "verifiedDomain mixedContent mixedActiveBlocked",  // SSL with unauthenticated active content blocked; no unauthenticated display content
   IDENTITY_MODE_MIXED_ACTIVE_BLOCKED_IDENTIFIED        : "verifiedIdentity mixedContent mixedActiveBlocked",  // SSL with unauthenticated active content blocked; no unauthenticated display content
   IDENTITY_MODE_CHROMEUI                               : "chromeUI",         // Part of the product's UI
+  IDENTITY_MODE_FILE_URI                               : "fileURI",  // File path
 
   // Cache the most recent SSLStatus and Location seen in checkIdentity
   _lastStatus : null,
   _lastUri : null,
   _mode : "unknownIdentity",
 
   // smart getters
   get _identityPopup () {
@@ -6843,17 +6844,30 @@ var gIdentityHandler = {
       } else if (state & nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT) {
         this.setMode(this.IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED);
       } else if (state & nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT) {
         this.setMode(this.IDENTITY_MODE_MIXED_DISPLAY_LOADED);
       } else {
         this.setMode(this.IDENTITY_MODE_USES_WEAK_CIPHER);
       }
     } else {
-      this.setMode(this.IDENTITY_MODE_UNKNOWN);
+      // Create a channel for the sole purpose of getting the resolved URI
+      // of the request to determine if it's loaded from the file system.
+      let resolvedURI = NetUtil.newChannel({uri,loadUsingSystemPrincipal:true}).URI;
+      if (resolvedURI.schemeIs("jar")) {
+        // Given a URI "jar:<jar-file-uri>!/<jar-entry>"
+        // create a new URI using <jar-file-uri>!/<jar-entry>
+        resolvedURI = NetUtil.newURI(resolvedURI.path);
+      }
+
+      if (resolvedURI.schemeIs("file")) {
+        this.setMode(this.IDENTITY_MODE_FILE_URI);
+      } else {
+        this.setMode(this.IDENTITY_MODE_UNKNOWN);
+      }
     }
 
     // Show the doorhanger when:
     // - mixed active content is blocked
     // - mixed active content is loaded (detected but not blocked)
     // - tracking content is blocked
     // - tracking content is not blocked
     if (state &
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -34,16 +34,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
   "resource:///modules/PlacesUIUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "PageMenuChild", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuChild();
 });
 
+XPCOMUtils.defineLazyModuleGetter(this, "Feeds", "resource:///modules/Feeds.jsm");
+
 // TabChildGlobal
 var global = this;
 
 // Load the form validation popup handler
 var formSubmitObserver = new FormSubmitObserver(content, this);
 
 addMessageListener("ContextMenu:DoCustomCommand", function(message) {
   PageMenuChild.executeMenu(message.data);
@@ -835,8 +837,401 @@ addMessageListener("ContextMenu:SetAsDes
       Cu.reportError(e);
       disable = true;
     }
   }
 
   if (disable)
     sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result", { disable });
 });
+
+let pageInfoListener = {
+
+  init: function(chromeGlobal) {
+    chromeGlobal.addMessageListener("PageInfo:getData", this, false, true);
+  },
+
+  receiveMessage: function(message) {
+    this.imageViewRows = [];
+    this.frameList = [];
+    this.strings = message.data.strings;
+
+    let frameOuterWindowID = message.data.frameOuterWindowID;
+
+    // If inside frame then get the frame's window and document.
+    if (frameOuterWindowID) {
+      this.window = Services.wm.getOuterWindowWithId(frameOuterWindowID);
+      this.document = this.window.document;
+    }
+    else {
+      this.document = content.document;
+      this.window = content.window;
+    }
+
+    let pageInfoData = {metaViewRows: this.getMetaInfo(), docInfo: this.getDocumentInfo(),
+                        feeds: this.getFeedsInfo(), windowInfo: this.getWindowInfo()};
+    sendAsyncMessage("PageInfo:data", pageInfoData);
+
+    // Separate step so page info dialog isn't blank while waiting for this to finish.
+    this.getMediaInfo();
+
+    // Send the message after all the media elements have been walked through.
+    let pageInfoMediaData = {imageViewRows: this.imageViewRows};
+
+    this.imageViewRows = null;
+    this.frameList = null;
+    this.strings = null;
+    this.window = null;
+    this.document = null;
+
+    sendAsyncMessage("PageInfo:mediaData", pageInfoMediaData);
+  },
+
+  getMetaInfo: function() {
+    let metaViewRows = [];
+
+    // Get the meta tags from the page.
+    let metaNodes = this.document.getElementsByTagName("meta");
+
+    for (let metaNode of metaNodes) {
+      metaViewRows.push([metaNode.name || metaNode.httpEquiv || metaNode.getAttribute("property"),
+                        metaNode.content]);
+    }
+
+    return metaViewRows;
+  },
+
+  getWindowInfo: function() {
+    let windowInfo = {};
+    windowInfo.isTopWindow = this.window == this.window.top;
+
+    let hostName = null;
+    try {
+      hostName = this.window.location.host;
+    }
+    catch (exception) { }
+
+    windowInfo.hostName = hostName;
+    return windowInfo;
+  },
+
+  getDocumentInfo: function() {
+    let docInfo = {};
+    docInfo.title = this.document.title;
+    docInfo.location = this.document.location.toString();
+    docInfo.referrer = this.document.referrer;
+    docInfo.compatMode = this.document.compatMode;
+    docInfo.contentType = this.document.contentType;
+    docInfo.characterSet = this.document.characterSet;
+    docInfo.lastModified = this.document.lastModified;
+
+    let documentURIObject = {};
+    documentURIObject.spec = this.document.documentURIObject.spec;
+    documentURIObject.originCharset = this.document.documentURIObject.originCharset;
+    docInfo.documentURIObject = documentURIObject;
+
+    docInfo.isContentWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(content);
+
+    return docInfo;
+  },
+
+  getFeedsInfo: function() {
+    let feeds = [];
+    // Get the feeds from the page.
+    let linkNodes = this.document.getElementsByTagName("link");
+    let length = linkNodes.length;
+    for (let i = 0; i < length; i++) {
+      let link = linkNodes[i];
+      if (!link.href) {
+        continue;
+      }
+      let rel = link.rel && link.rel.toLowerCase();
+      let rels = {};
+
+      if (rel) {
+        for each (let relVal in rel.split(/\s+/)) {
+          rels[relVal] = true;
+        }
+      }
+
+      if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
+        let type = Feeds.isValidFeed(link, this.document.nodePrincipal, "feed" in rels);
+        if (type) {
+          type = this.strings[type] || this.strings["application/rss+xml"];
+          feeds.push([link.title, type, link.href]);
+        }
+      }
+    }
+    return feeds;
+  },
+
+  // Only called once to get the media tab's media elements from the content page.
+  // The actual work is done with a TreeWalker that calls doGrab() once for
+  // each element node in the document.
+  getMediaInfo: function()
+  {
+    this.goThroughFrames(this.document, this.window);
+    this.processFrames();
+  },
+
+  goThroughFrames: function(aDocument, aWindow)
+  {
+    this.frameList.push(aDocument);
+    if (aWindow && aWindow.frames.length > 0) {
+      let num = aWindow.frames.length;
+      for (let i = 0; i < num; i++) {
+        this.goThroughFrames(aWindow.frames[i].document, aWindow.frames[i]);  // recurse through the frames
+      }
+    }
+  },
+
+  processFrames: function()
+  {
+    if (this.frameList.length) {
+      let doc = this.frameList[0];
+      let iterator = doc.createTreeWalker(doc, content.NodeFilter.SHOW_ELEMENT, elem => this.grabAll(elem));
+      this.frameList.shift();
+      this.doGrab(iterator);
+    }
+  },
+
+  /**
+   * This function's previous purpose in pageInfo.js was to get loop through 500 elements at a time.
+   * The iterator filter will filter for media elements.
+   * #TODO Bug 1175794: refactor pageInfo.js to receive a media element at a time
+   * from messages and continually update UI.
+   */
+  doGrab: function(iterator)
+  {
+    while (true)
+    {
+      if (!iterator.nextNode()) {
+        this.processFrames();
+        return;
+      }
+    }
+  },
+
+  grabAll: function(elem)
+  {
+    // Check for images defined in CSS (e.g. background, borders), any node may have multiple.
+    let computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
+
+    let addImage = (url, type, alt, elem, isBg) => {
+      let element = this.serializeElementInfo(url, type, alt, elem, isBg);
+      this.imageViewRows.push([url, type, alt, element, isBg]);
+    };
+
+    if (computedStyle) {
+      let addImgFunc = (label, val) => {
+        if (val.primitiveType == content.CSSPrimitiveValue.CSS_URI) {
+          addImage(val.getStringValue(), label, this.strings.notSet, elem, true);
+        }
+        else if (val.primitiveType == content.CSSPrimitiveValue.CSS_STRING) {
+          // This is for -moz-image-rect.
+          // TODO: Reimplement once bug 714757 is fixed.
+          let strVal = val.getStringValue();
+          if (strVal.search(/^.*url\(\"?/) > -1) {
+            let url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
+            addImage(url, label, this.strings.notSet, elem, true);
+          }
+        }
+        else if (val.cssValueType == content.CSSValue.CSS_VALUE_LIST) {
+          // Recursively resolve multiple nested CSS value lists.
+          for (let i = 0; i < val.length; i++) {
+            addImgFunc(label, val.item(i));
+          }
+        }
+      };
+
+      addImgFunc(this.strings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
+      addImgFunc(this.strings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
+      addImgFunc(this.strings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
+      addImgFunc(this.strings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
+    }
+
+    // One swi^H^H^Hif-else to rule them all.
+    if (elem instanceof content.HTMLImageElement) {
+      addImage(elem.src, this.strings.mediaImg,
+               (elem.hasAttribute("alt")) ? elem.alt : this.strings.notSet, elem, false);
+    }
+    else if (elem instanceof content.SVGImageElement) {
+      try {
+        // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
+        //       or the URI formed from the baseURI and the URL is not a valid URI.
+        let href = makeURLAbsolute(elem.baseURI, elem.href.baseVal);
+        addImage(href, this.strings.mediaImg, "", elem, false);
+      } catch (e) { }
+    }
+    else if (elem instanceof content.HTMLVideoElement) {
+      addImage(elem.currentSrc, this.strings.mediaVideo, "", elem, false);
+    }
+    else if (elem instanceof content.HTMLAudioElement) {
+      addImage(elem.currentSrc, this.strings.mediaAudio, "", elem, false);
+    }
+    else if (elem instanceof content.HTMLLinkElement) {
+      if (elem.rel && /\bicon\b/i.test(elem.rel)) {
+        addImage(elem.href, this.strings.mediaLink, "", elem, false);
+      }
+    }
+    else if (elem instanceof content.HTMLInputElement || elem instanceof content.HTMLButtonElement) {
+      if (elem.type.toLowerCase() == "image") {
+        addImage(elem.src, this.strings.mediaInput,
+                 (elem.hasAttribute("alt")) ? elem.alt : this.strings.notSet, elem, false);
+      }
+    }
+    else if (elem instanceof content.HTMLObjectElement) {
+      addImage(elem.data, this.strings.mediaObject, this.getValueText(elem), elem, false);
+    }
+    else if (elem instanceof content.HTMLEmbedElement) {
+      addImage(elem.src, this.strings.mediaEmbed, "", elem, false);
+    }
+
+    return content.NodeFilter.FILTER_ACCEPT;
+  },
+
+  /**
+   * Set up a JSON element object with all the instanceOf and other infomation that
+   * makePreview in pageInfo.js uses to figure out how to display the preview.
+   */
+
+  serializeElementInfo: function(url, type, alt, item, isBG)
+  {
+    // Interface for image loading content.
+    const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
+
+    let result = {};
+
+    let imageText;
+    if (!isBG &&
+        !(item instanceof content.SVGImageElement) &&
+        !(this.document instanceof content.ImageDocument)) {
+      imageText = item.title || item.alt;
+
+      if (!imageText && !(item instanceof content.HTMLImageElement)) {
+        imageText = this.getValueText(item);
+      }
+    }
+
+    result.imageText = imageText;
+    result.longDesc = item.longDesc;
+    result.numFrames = 1;
+
+    if (item instanceof content.HTMLObjectElement ||
+      item instanceof content.HTMLEmbedElement ||
+      item instanceof content.HTMLLinkElement) {
+      result.mimeType = item.type;
+    }
+
+    if (!result.mimeType && !isBG && item instanceof nsIImageLoadingContent) {
+      // Interface for image loading content.
+      const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
+      let imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
+      if (imageRequest) {
+        result.mimeType = imageRequest.mimeType;
+        let image = !(imageRequest.imageStatus & imageRequest.STATUS_ERROR) && imageRequest.image;
+        if (image) {
+          result.numFrames = image.numFrames;
+        }
+      }
+    }
+
+    // if we have a data url, get the MIME type from the url
+    if (!result.mimeType && url.startsWith("data:")) {
+      let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
+      if (dataMimeType)
+        result.mimeType = dataMimeType[1].toLowerCase();
+    }
+
+    result.HTMLLinkElement = item instanceof content.HTMLLinkElement;
+    result.HTMLInputElement = item instanceof content.HTMLInputElement;
+    result.HTMLImageElement = item instanceof content.HTMLImageElement;
+    result.HTMLObjectElement = item instanceof content.HTMLObjectElement;
+    result.SVGImageElement = item instanceof content.SVGImageElement;
+    result.HTMLVideoElement = item instanceof content.HTMLVideoElement;
+    result.HTMLAudioElement = item instanceof content.HTMLAudioElement;
+
+    if (!isBG) {
+      result.width = item.width;
+      result.height = item.height;
+    }
+
+    if (item instanceof content.SVGImageElement) {
+      result.SVGImageElementWidth = item.width.baseVal.value;
+      result.SVGImageElementHeight = item.height.baseVal.value;
+    }
+
+    result.baseURI = item.baseURI;
+
+    return result;
+  },
+
+  //******** Other Misc Stuff
+  // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
+  // parse a node to extract the contents of the node
+  getValueText: function(node)
+  {
+
+    let valueText = "";
+
+    // Form input elements don't generally contain information that is useful to our callers, so return nothing.
+    if (node instanceof content.HTMLInputElement ||
+        node instanceof content.HTMLSelectElement ||
+        node instanceof content.HTMLTextAreaElement) {
+      return valueText;
+    }
+
+    // Otherwise recurse for each child.
+    let length = node.childNodes.length;
+
+    for (let i = 0; i < length; i++) {
+      let childNode = node.childNodes[i];
+      let nodeType = childNode.nodeType;
+
+      // Text nodes are where the goods are.
+      if (nodeType == content.Node.TEXT_NODE) {
+        valueText += " " + childNode.nodeValue;
+      }
+      // And elements can have more text inside them.
+      else if (nodeType == content.Node.ELEMENT_NODE) {
+        // Images are special, we want to capture the alt text as if the image weren't there.
+        if (childNode instanceof content.HTMLImageElement) {
+          valueText += " " + this.getAltText(childNode);
+        }
+        else {
+          valueText += " " + this.getValueText(childNode);
+        }
+      }
+    }
+
+    return this.stripWS(valueText);
+  },
+
+  // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
+  // Traverse the tree in search of an img or area element and grab its alt tag.
+  getAltText: function(node)
+  {
+    let altText = "";
+
+    if (node.alt) {
+      return node.alt;
+    }
+    let length = node.childNodes.length;
+    for (let i = 0; i < length; i++) {
+      if ((altText = this.getAltText(node.childNodes[i]) != undefined)) { // stupid js warning...
+        return altText;
+      }
+    }
+    return "";
+  },
+
+  // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
+  // Strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space.
+  stripWS: function(text)
+  {
+    let middleRE = /\s+/g;
+    let endRE = /(^\s+)|(\s+$)/g;
+
+    text = text.replace(middleRE, " ");
+    return text.replace(endRE, "");
+  }
+};
+pageInfoListener.init(this);
\ No newline at end of file
--- a/browser/base/content/contentSearchUI.js
+++ b/browser/base/content/contentSearchUI.js
@@ -91,20 +91,16 @@ ContentSearchUIController.prototype = {
   },
 
   get engines() {
     return this._engines;
   },
 
   set engines(val) {
     this._engines = val;
-    if (!this._table.hidden) {
-      this._setUpOneOffButtons();
-      return;
-    }
     this._pendingOneOffRefresh = true;
   },
 
   // The selectedIndex is the index of the element with the "selected" class in
   // the list obtained by concatenating the suggestion rows, one-off buttons, and
   // search settings button.
   get selectedIndex() {
     let allElts = [...this._suggestionsList.children,
@@ -122,34 +118,64 @@ ContentSearchUIController.prototype = {
   set selectedIndex(idx) {
     // Update the table's rows, and the input when there is a selection.
     this._table.removeAttribute("aria-activedescendant");
     this.input.removeAttribute("aria-activedescendant");
 
     let allElts = [...this._suggestionsList.children,
                    ...this._oneOffButtons,
                    document.getElementById("contentSearchSettingsButton")];
+    // If we are selecting a suggestion and a one-off is selected, don't deselect it.
+    let excludeIndex = idx < this.numSuggestions && this.selectedButtonIndex > -1 ?
+                       this.numSuggestions + this.selectedButtonIndex : -1;
     for (let i = 0; i < allElts.length; ++i) {
       let elt = allElts[i];
       let ariaSelectedElt = i < this.numSuggestions ? elt.firstChild : elt;
       if (i == idx) {
         elt.classList.add("selected");
         ariaSelectedElt.setAttribute("aria-selected", "true");
         this.input.setAttribute("aria-activedescendant", ariaSelectedElt.id);
       }
-      else {
+      else if (i != excludeIndex) {
         elt.classList.remove("selected");
         ariaSelectedElt.setAttribute("aria-selected", "false");
       }
     }
   },
 
+  get selectedButtonIndex() {
+    let elts = [...this._oneOffButtons,
+                document.getElementById("contentSearchSettingsButton")];
+    for (let i = 0; i < elts.length; ++i) {
+      if (elts[i].classList.contains("selected")) {
+        return i;
+      }
+    }
+    return -1;
+  },
+
+  set selectedButtonIndex(idx) {
+    let elts = [...this._oneOffButtons,
+                document.getElementById("contentSearchSettingsButton")];
+    for (let i = 0; i < elts.length; ++i) {
+      let elt = elts[i];
+      if (i == idx) {
+        elt.classList.add("selected");
+        elt.setAttribute("aria-selected", "true");
+      }
+      else {
+        elt.classList.remove("selected");
+        elt.setAttribute("aria-selected", "false");
+      }
+    }
+  },
+
   get selectedEngineName() {
-    let selectedElt = this._table.querySelector(".selected");
-    if (selectedElt && selectedElt.engineName) {
+    let selectedElt = this._oneOffsTable.querySelector(".selected");
+    if (selectedElt) {
       return selectedElt.engineName;
     }
     return this.defaultEngine.name;
   },
 
   get numSuggestions() {
     return this._suggestionsList.children.length;
   },
@@ -189,17 +215,17 @@ ContentSearchUIController.prototype = {
     this._sendMsg("AddFormHistoryEntry", this.input.value);
   },
 
   handleEvent: function (event) {
     this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
   },
 
   _onCommand: function(aEvent) {
-    if (this.selectedIndex == this.numSuggestions + this._oneOffButtons.length) {
+    if (this.selectedButtonIndex == this._oneOffButtons.length) {
       // Settings button was selected.
       this._sendMsg("ManageEngines");
       return;
     }
 
     this.search(aEvent);
 
     if (aEvent) {
@@ -259,29 +285,68 @@ ContentSearchUIController.prototype = {
       this._getSuggestions();
       this.selectAndUpdateInput(-1);
     }
     this._updateSearchWithHeader();
   },
 
   _onKeypress: function (event) {
     let selectedIndexDelta = 0;
+    let selectedSuggestionDelta = 0;
+    let selectedOneOffDelta = 0;
+
     switch (event.keyCode) {
     case event.DOM_VK_UP:
-      if (!this._table.hidden) {
-        selectedIndexDelta = -1;
+      if (this._table.hidden) {
+        return;
       }
+      if (event.getModifierState("Accel")) {
+        if (event.shiftKey) {
+          selectedSuggestionDelta = -1;
+          break;
+        }
+        this._cycleCurrentEngine(true);
+        break;
+      }
+      if (event.altKey) {
+        selectedOneOffDelta = -1;
+        break;
+      }
+      selectedIndexDelta = -1;
       break;
     case event.DOM_VK_DOWN:
       if (this._table.hidden) {
         this._getSuggestions();
+        return;
       }
-      else {
-        selectedIndexDelta = 1;
+      if (event.getModifierState("Accel")) {
+        if (event.shiftKey) {
+          selectedSuggestionDelta = 1;
+          break;
+        }
+        this._cycleCurrentEngine(false);
+        break;
+      }
+      if (event.altKey) {
+        selectedOneOffDelta = 1;
+        break;
       }
+      selectedIndexDelta = 1;
+      break;
+    case event.DOM_VK_TAB:
+      if (this._table.hidden) {
+        return;
+      }
+      // Shift+tab when either the first or no one-off is selected, as well as
+      // tab when the settings button is selected, should change focus as normal.
+      if ((this.selectedButtonIndex <= 0 && event.shiftKey) ||
+          this.selectedButtonIndex == this._oneOffButtons.length && !event.shiftKey) {
+        return;
+      }
+      selectedOneOffDelta = event.shiftKey ? -1 : 1;
       break;
     case event.DOM_VK_RIGHT:
       // Allow normal caret movement until the caret is at the end of the input.
       if (this.input.selectionStart != this.input.selectionEnd ||
           this.input.selectionEnd != this.input.value.length) {
         return;
       }
       if (this.numSuggestions && this.selectedIndex >= 0 &&
@@ -292,47 +357,107 @@ ContentSearchUIController.prototype = {
       } else {
         // If we didn't select anything, make sure to remove the attributes
         // in case they were populated last time.
         this.input.removeAttribute("selection-index");
         this.input.removeAttribute("selection-kind");
       }
       this._stickyInputValue = this.input.value;
       this._hideSuggestions();
-      break;
+      return;
     case event.DOM_VK_RETURN:
       this._onCommand(event);
-      break;
+      return;
     case event.DOM_VK_DELETE:
       if (this.selectedIndex >= 0) {
         this.deleteSuggestionAtIndex(this.selectedIndex);
       }
-      break;
+      return;
     case event.DOM_VK_ESCAPE:
       if (!this._table.hidden) {
         this._hideSuggestions();
       }
+      return;
     default:
       return;
     }
 
+    let currentIndex = this.selectedIndex;
     if (selectedIndexDelta) {
-      // Update the selection.
-      let newSelectedIndex = this.selectedIndex + selectedIndexDelta;
+      let newSelectedIndex = currentIndex + selectedIndexDelta;
       if (newSelectedIndex < -1) {
         newSelectedIndex = this.numSuggestions + this._oneOffButtons.length;
       }
-      else if (this.numSuggestions + this._oneOffButtons.length < newSelectedIndex) {
+      // If are moving up from the first one off, we have to deselect the one off
+      // manually because the selectedIndex setter tries to exclude the selected
+      // one-off (which is desirable for accel+shift+up/down).
+      if (currentIndex == this.numSuggestions && selectedIndexDelta == -1) {
+        this.selectedButtonIndex = -1;
+      }
+      this.selectAndUpdateInput(newSelectedIndex);
+    }
+
+    else if (selectedSuggestionDelta) {
+      let newSelectedIndex;
+      if (currentIndex >= this.numSuggestions || currentIndex == -1) {
+        // No suggestion already selected, select the first/last one appropriately.
+        newSelectedIndex = selectedSuggestionDelta == 1 ?
+                           0 : this.numSuggestions - 1;
+      }
+      else {
+        newSelectedIndex = currentIndex + selectedSuggestionDelta;
+      }
+      if (newSelectedIndex >= this.numSuggestions) {
         newSelectedIndex = -1;
       }
       this.selectAndUpdateInput(newSelectedIndex);
+    }
 
-      // Prevent the input's caret from moving.
-      event.preventDefault();
+    else if (selectedOneOffDelta) {
+      let newSelectedIndex;
+      let currentButton = this.selectedButtonIndex;
+      if (currentButton == -1 || currentButton == this._oneOffButtons.length) {
+        // No one-off already selected, select the first/last one appropriately.
+        newSelectedIndex = selectedOneOffDelta == 1 ?
+                           0 : this._oneOffButtons.length - 1;
+      }
+      else {
+        newSelectedIndex = currentButton + selectedOneOffDelta;
+      }
+      // Allow selection of the settings button via the tab key.
+      if (newSelectedIndex == this._oneOffButtons.length &&
+          event.keyCode != event.DOM_VK_TAB) {
+        newSelectedIndex = -1;
+      }
+      this.selectedButtonIndex = newSelectedIndex;
     }
+
+    // Prevent the input's caret from moving.
+    event.preventDefault();
+  },
+
+  _currentEngineIndex: -1,
+  _cycleCurrentEngine: function (aReverse) {
+    if ((this._currentEngineIndex == this._oneOffButtons.length - 1 && !aReverse) ||
+        (this._currentEngineIndex < 0 && aReverse)) {
+      return;
+    }
+    this._currentEngineIndex += aReverse ? -1 : 1;
+    let engine;
+    if (this._currentEngineIndex == -1) {
+      engine = this._originalDefaultEngine;
+    } else {
+      let button = this._oneOffButtons[this._currentEngineIndex];
+      engine = {
+        name: button.engineName,
+        icon: button.firstChild.getAttribute("src"),
+      };
+    }
+    this._sendMsg("SetCurrentEngine", engine.name);
+    this.defaultEngine = engine;
   },
 
   _onFocus: function () {
     if (this._mousedown) {
       return;
     }
     // When the input box loses focus to something in our table, we refocus it
     // immediately. This causes the focus highlight to flicker, so we set a
@@ -351,26 +476,40 @@ ContentSearchUIController.prototype = {
       setTimeout(() => this.input.focus(), 0);
       return;
     }
     this.input.removeAttribute("keepfocus");
     this._hideSuggestions();
   },
 
   _onMousemove: function (event) {
-    this.selectedIndex = this._indexOfTableItem(event.target);
+    let idx = this._indexOfTableItem(event.target);
+    if (idx >= this.numSuggestions) {
+      this.selectedButtonIndex = idx - this.numSuggestions;
+      return;
+    }
+    this.selectedIndex = idx;
   },
 
   _onMouseup: function (event) {
     if (event.button == 2) {
       return;
     }
     this._onCommand(event);
   },
 
+  _onMouseout: function (event) {
+    // We only deselect one-off buttons and the settings button when they are
+    // moused out.
+    let idx = this._indexOfTableItem(event.originalTarget);
+    if (idx >= this.numSuggestions) {
+      this.selectedButtonIndex = -1;
+    }
+  },
+
   _onClick: function (event) {
     this._onMouseup(event);
   },
 
   _onContentSearchService: function (event) {
     let methodName = "_onMsg" + event.detail.type;
     if (methodName in this) {
       this[methodName](event.detail.data);
@@ -422,16 +561,20 @@ ContentSearchUIController.prototype = {
     if (this._table.hidden) {
       this.selectedIndex = -1;
       if (this._pendingOneOffRefresh) {
         this._setUpOneOffButtons();
         delete this._pendingOneOffRefresh;
       }
       this._table.hidden = false;
       this.input.setAttribute("aria-expanded", "true");
+      this._originalDefaultEngine = {
+        name: this.defaultEngine.name,
+        icon: this.defaultEngine.icon,
+      };
     }
   },
 
   _onMsgState: function (state) {
     this.defaultEngine = {
       name: state.currentEngine.name,
       icon: this._getFaviconURIFromBuffer(state.currentEngine.iconBuffer),
     };
@@ -442,20 +585,16 @@ ContentSearchUIController.prototype = {
     this._onMsgState(state);
   },
 
   _onMsgCurrentEngine: function (engine) {
     this.defaultEngine = {
       name: engine.name,
       icon: this._getFaviconURIFromBuffer(engine.iconBuffer),
     };
-    if (!this._table.hidden) {
-      this._setUpOneOffButtons();
-      return;
-    }
     this._pendingOneOffRefresh = true;
   },
 
   _onMsgStrings: function (strings) {
     this._strings = strings;
     this._updateDefaultEngineHeader();
     this._updateSearchWithHeader();
     document.getElementById("contentSearchSettingsButton").textContent =
@@ -567,16 +706,19 @@ ContentSearchUIController.prototype = {
   _clearSuggestionRows: function() {
     while (this._suggestionsList.firstElementChild) {
       this._suggestionsList.firstElementChild.remove();
     }
   },
 
   _hideSuggestions: function () {
     this.input.setAttribute("aria-expanded", "false");
+    this.selectedIndex = -1;
+    this.selectedButtonIndex = -1;
+    this._currentEngineIndex = -1;
     this._table.hidden = true;
   },
 
   _indexOfTableItem: function (elt) {
     if (elt.classList.contains("contentSearchOneOffItem")) {
       return this.numSuggestions + this._oneOffButtons.indexOf(elt);
     }
     if (elt.classList.contains("contentSearchSettingsButton")) {
@@ -600,21 +742,17 @@ ContentSearchUIController.prototype = {
 
     // When the search input box loses focus, we want to immediately give focus
     // back to it if the blur was because the user clicked somewhere in the table.
     // onBlur uses the _mousedown flag to detect this.
     this._table.addEventListener("mousedown", () => { this._mousedown = true; });
     document.addEventListener("mouseup", () => { delete this._mousedown; });
 
     // Deselect the selected element on mouseout if it wasn't a suggestion.
-    this._table.addEventListener("mouseout", () => {
-      if (this.selectedIndex >= this.numSuggestions) {
-        this.selectAndUpdateInput(-1);
-      }
-    });
+    this._table.addEventListener("mouseout", this);
 
     // If a search is loaded in the same tab, ensure the suggestions dropdown
     // is hidden immediately when the page starts loading and not when it first
     // appears, in order to provide timely feedback to the user.
     window.addEventListener("beforeunload", () => { this._hideSuggestions(); });
 
     let headerRow = document.createElementNS(HTML_NS, "tr");
     let header = document.createElementNS(HTML_NS, "td");
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1046,17 +1046,18 @@ nsContextMenu.prototype = {
     urlSecurityCheck(this.imageDescURL,
                      this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
     openUILink(this.imageDescURL, e, { disallowInheritPrincipal: true,
                                        referrerURI: gContextMenuContentData.documentURIObject });
   },
 
   viewFrameInfo: function() {
-    BrowserPageInfo(this.target.ownerDocument);
+    BrowserPageInfo(this.target.ownerDocument, null, null,
+                    this.frameOuterWindowID);
   },
 
   reloadImage: function() {
     urlSecurityCheck(this.mediaURL,
                      this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
 
     this.browser.messageManager.sendAsyncMessage("ContextMenu:ReloadImage",
--- a/browser/base/content/pageinfo/feeds.js
+++ b/browser/base/content/pageinfo/feeds.js
@@ -1,48 +1,18 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
-XPCOMUtils.defineLazyModuleGetter(this, "Feeds",
-  "resource:///modules/Feeds.jsm");
-
-function initFeedTab()
+function initFeedTab(feeds)
 {
-  const feedTypes = {
-    "application/rss+xml": gBundle.getString("feedRss"),
-    "application/atom+xml": gBundle.getString("feedAtom"),
-    "text/xml": gBundle.getString("feedXML"),
-    "application/xml": gBundle.getString("feedXML"),
-    "application/rdf+xml": gBundle.getString("feedXML")
-  };
-
-  // get the feeds
-  var linkNodes = gDocument.getElementsByTagName("link");
-  var length = linkNodes.length;
-  for (var i = 0; i < length; i++) {
-    var link = linkNodes[i];
-    if (!link.href)
-      continue;
-
-    var rel = link.rel && link.rel.toLowerCase();
-    var rels = {};
-    if (rel) {
-      for each (let relVal in rel.split(/\s+/))
-        rels[relVal] = true;
-    }
-
-    if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
-      var type = Feeds.isValidFeed(link, gDocument.nodePrincipal, "feed" in rels);
-      if (type) {
-        type = feedTypes[type] || feedTypes["application/rss+xml"];
-        addRow(link.title, type, link.href);
-      }
-    }
+  for (let feed of feeds) {
+    let [name, type, url] = feed;
+    addRow(name, type, url);
   }
 
   var feedListbox = document.getElementById("feedListbox");
   document.getElementById("feedTab").hidden = feedListbox.getRowCount() == 0;
 }
 
 function onSubscribeFeed()
 {
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -47,18 +47,29 @@ pageInfoTreeView.prototype = {
   {
     this.data[row][column.index] = value;
   },
 
   addRow: function(row)
   {
     this.rows = this.data.push(row);
     this.rowCountChanged(this.rows - 1, 1);
-    if (this.selection.count == 0 && this.rowCount && !gImageElement)
+    if (this.selection.count == 0 && this.rowCount && !gImageElement) {
       this.selection.select(0);
+    }
+  },
+
+  addRows: function(rows)
+  {
+    this.data = this.data.concat(rows);
+    this.rowCountChanged(this.rows, rows.length);
+    this.rows = this.data.length;
+    if (this.selection.count == 0 && this.rowCount && !gImageElement) {
+      this.selection.select(0);
+    }
   },
 
   rowCountChanged: function(index, count)
   {
     this.tree.rowCountChanged(index, count);
   },
 
   invalidate: function()
@@ -135,18 +146,17 @@ pageInfoTreeView.prototype = {
   cycleCell: function(row, column) { },
   isEditable: function(row, column) { return false; },
   isSelectable: function(row, column) { return false; },
   performAction: function(action) { },
   performActionOnCell: function(action, row, column) { }
 };
 
 // mmm, yummy. global variables.
-var gWindow = null;
-var gDocument = null;
+var gDocInfo = null;
 var gImageElement = null;
 
 // column number to help using the data array
 const COL_IMAGE_ADDRESS = 0;
 const COL_IMAGE_TYPE    = 1;
 const COL_IMAGE_SIZE    = 2;
 const COL_IMAGE_ALT     = 3;
 const COL_IMAGE_COUNT   = 4;
@@ -281,44 +291,32 @@ const XHTMLre = RegExp(XHTMLNSre + "|" +
  * These arrays are used to hold callbacks that Page Info will call at
  * various stages. Use them by simply appending a function to them.
  * For example, add a function to onLoadRegistry by invoking
  *   "onLoadRegistry.push(XXXLoadFunc);"
  * The XXXLoadFunc should be unique to the overlay module, and will be
  * invoked as "XXXLoadFunc();"
  */
 
-// These functions are called to build the data displayed in the Page
-// Info window. The global variables gDocument and gWindow are set.
+// These functions are called to build the data displayed in the Page Info window.
 var onLoadRegistry = [ ];
 
 // These functions are called to remove old data still displayed in
 // the window when the document whose information is displayed
 // changes. For example, at this time, the list of images of the Media
 // tab is cleared.
 var onResetRegistry = [ ];
 
-// These are called once for each subframe of the target document and
-// the target document itself. The frame is passed as an argument.
-var onProcessFrame = [ ];
-
-// These functions are called once for each element (in all subframes, if any)
-// in the target document. The element is passed as an argument.
-var onProcessElement = [ ];
-
 // These functions are called once when all the elements in all of the target
 // document (and all of its subframes, if any) have been processed
 var onFinished = [ ];
 
 // These functions are called once when the Page Info window is closed.
 var onUnloadRegistry = [ ];
 
-// These functions are called once when an image preview is shown.
-var onImagePreviewShown = [ ];
-
 /* Called when PageInfo window is loaded.  Arguments are:
  *  window.arguments[0] - (optional) an object consisting of
  *                         - doc: (optional) document to use for source. if not provided,
  *                                the calling window's document will be used
  *                         - initialTab: (optional) id of the inital tab to display
  */
 function onLoadPageInfo()
 {
@@ -336,48 +334,73 @@ function onLoadPageInfo()
   gStrings.mediaInput = gBundle.getString("mediaInput");
   gStrings.mediaVideo = gBundle.getString("mediaVideo");
   gStrings.mediaAudio = gBundle.getString("mediaAudio");
 
   var args = "arguments" in window &&
              window.arguments.length >= 1 &&
              window.arguments[0];
 
-  if (!args || !args.doc) {
-    gWindow = window.opener.gBrowser.selectedBrowser.contentWindowAsCPOW;
-    gDocument = gWindow.document;
-  }
-
   // init media view
   var imageTree = document.getElementById("imagetree");
   imageTree.view = gImageView;
 
   /* Select the requested tab, if the name is specified */
   loadTab(args);
   Components.classes["@mozilla.org/observer-service;1"]
             .getService(Components.interfaces.nsIObserverService)
             .notifyObservers(window, "page-info-dialog-loaded", null);
 }
 
-function loadPageInfo()
+function loadPageInfo(frameOuterWindowID)
 {
-  var titleFormat = gWindow != gWindow.top ? "pageInfo.frame.title"
-                                           : "pageInfo.page.title";
-  document.title = gBundle.getFormattedString(titleFormat, [gDocument.location]);
+  let mm = window.opener.gBrowser.selectedBrowser.messageManager;
 
-  document.getElementById("main-window").setAttribute("relatedUrl", gDocument.location);
+  gStrings["application/rss+xml"]  = gBundle.getString("feedRss");
+  gStrings["application/atom+xml"] = gBundle.getString("feedAtom");
+  gStrings["text/xml"]             = gBundle.getString("feedXML");
+  gStrings["application/xml"]      = gBundle.getString("feedXML");
+  gStrings["application/rdf+xml"]  = gBundle.getString("feedXML");
+
+  // Look for pageInfoListener in content.js. Sends message to listener with arguments.
+  mm.sendAsyncMessage("PageInfo:getData", {strings: gStrings,
+                      frameOuterWindowID: frameOuterWindowID});
+
+  let pageInfoData = null;
 
-  // do the easy stuff first
-  makeGeneralTab();
+  // Get initial pageInfoData needed to display the general, feeds, permission and security tabs.
+  mm.addMessageListener("PageInfo:data", function onmessage(message) {
+    mm.removeMessageListener("PageInfo:data", onmessage);
+    pageInfoData = message.data;
+    let docInfo = pageInfoData.docInfo;
+    let windowInfo = pageInfoData.windowInfo;
+    let uri = makeURI(docInfo.documentURIObject.spec,
+                      docInfo.documentURIObject.originCharset);
+    gDocInfo = docInfo;
+
+    var titleFormat = windowInfo.isTopWindow ? "pageInfo.frame.title"
+                                             : "pageInfo.page.title";
+    document.title = gBundle.getFormattedString(titleFormat, [docInfo.location]);
 
-  // and then the hard stuff
-  makeTabs(gDocument, gWindow);
+    document.getElementById("main-window").setAttribute("relatedUrl", docInfo.location);
+
+    makeGeneralTab(pageInfoData.metaViewRows, docInfo);
+    initFeedTab(pageInfoData.feeds);
+    onLoadPermission(uri);
+    securityOnLoad(uri, windowInfo);
+  });
 
-  initFeedTab();
-  onLoadPermission();
+  // Get the media elements from content script to setup the media tab.
+  mm.addMessageListener("PageInfo:mediaData", function onmessage(message){
+    mm.removeMessageListener("PageInfo:mediaData", onmessage);
+    makeMediaTab(message.data.imageViewRows);
+
+    // Loop through onFinished and execute the functions on it.
+    onFinished.forEach(function(func) { func(pageInfoData); });
+  });
 
   /* Call registered overlay init functions */
   onLoadRegistry.forEach(function(func) { func(); });
 }
 
 function resetPageInfo(args)
 {
   /* Reset Meta tags part */
@@ -438,25 +461,32 @@ function showTab(id)
 {
   var deck  = document.getElementById("mainDeck");
   var pagel = document.getElementById(id + "Panel");
   deck.selectedPanel = pagel;
 }
 
 function loadTab(args)
 {
-  if (args && args.doc) {
-    gDocument = args.doc;
-    gWindow = gDocument.defaultView;
+  // If the "View Image Info" context menu item was used, the related image
+  // element is provided as an argument. This can't be a background image.
+  let imageElement = args && args.imageElement;
+  if (imageElement) {
+    gImageElement = {currentSrc: imageElement.currentSrc,
+                     width: imageElement.width, height: imageElement.height,
+                     imageText: imageElement.title || imageElement.alt};
+  }
+  else {
+    gImageElement = null;
   }
 
-  gImageElement = args && args.imageElement;
+  let frameOuterWindowID = args && args.frameOuterWindowID;
 
   /* Load the page info */
-  loadPageInfo();
+  loadPageInfo(frameOuterWindowID);
 
   var initialTab = (args && args.initialTab) || "generalTab";
   var radioGroup = document.getElementById("viewGroup");
   initialTab = document.getElementById(initialTab) || document.getElementById("generalTab");
   radioGroup.selectedItem = initialTab;
   radioGroup.selectedItem.doCommand();
   radioGroup.focus();
 }
@@ -486,127 +516,83 @@ function openCacheEntry(key, cb)
     },
     onCacheEntryAvailable: function(entry, isNew, appCache, status) {
       cb(entry);
     }
   };
   diskStorage.asyncOpenURI(Services.io.newURI(key, null, null), "", nsICacheStorage.OPEN_READONLY, checkCacheListener);
 }
 
-function makeGeneralTab()
+function makeGeneralTab(metaViewRows, docInfo)
 {
-  var title = (gDocument.title) ? gBundle.getFormattedString("pageTitle", [gDocument.title]) : gBundle.getString("noPageTitle");
+  var title = (docInfo.title) ? gBundle.getFormattedString("pageTitle", [docInfo.title]) : gBundle.getString("noPageTitle");
   document.getElementById("titletext").value = title;
 
-  var url = gDocument.location.toString();
+  var url = docInfo.location;
   setItemValue("urltext", url);
 
-  var referrer = ("referrer" in gDocument && gDocument.referrer);
+  var referrer = ("referrer" in docInfo && docInfo.referrer);
   setItemValue("refertext", referrer);
 
-  var mode = ("compatMode" in gDocument && gDocument.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
+  var mode = ("compatMode" in docInfo && docInfo.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
   document.getElementById("modetext").value = gBundle.getString(mode);
 
   // find out the mime type
-  var mimeType = gDocument.contentType;
+  var mimeType = docInfo.contentType;
   setItemValue("typetext", mimeType);
 
   // get the document characterset
-  var encoding = gDocument.characterSet;
+  var encoding = docInfo.characterSet;
   document.getElementById("encodingtext").value = encoding;
 
-  // get the meta tags
-  var metaNodes = gDocument.getElementsByTagName("meta");
-  var length = metaNodes.length;
+  let length = metaViewRows.length;
 
   var metaGroup = document.getElementById("metaTags");
   if (!length)
     metaGroup.collapsed = true;
   else {
     var metaTagsCaption = document.getElementById("metaTagsCaption");
     if (length == 1)
       metaTagsCaption.label = gBundle.getString("generalMetaTag");
     else
       metaTagsCaption.label = gBundle.getFormattedString("generalMetaTags", [length]);
     var metaTree = document.getElementById("metatree");
     metaTree.view = gMetaView;
 
-    for (var i = 0; i < length; i++)
-      gMetaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv || metaNodes[i].getAttribute("property"),
-                        metaNodes[i].content]);
+    // Add the metaViewRows onto the general tab's meta info tree.
+    gMetaView.addRows(metaViewRows);
 
     metaGroup.collapsed = false;
   }
 
   // get the date of last modification
-  var modifiedText = formatDate(gDocument.lastModified, gStrings.notSet);
+  var modifiedText = formatDate(docInfo.lastModified, gStrings.notSet);
   document.getElementById("modifiedtext").value = modifiedText;
 
   // get cache info
   var cacheKey = url.replace(/#.*$/, "");
   openCacheEntry(cacheKey, function(cacheEntry) {
     var sizeText;
     if (cacheEntry) {
       var pageSize = cacheEntry.dataSize;
       var kbSize = formatNumber(Math.round(pageSize / 1024 * 100) / 100);
       sizeText = gBundle.getFormattedString("generalSize", [kbSize, formatNumber(pageSize)]);
     }
     setItemValue("sizetext", sizeText);
   });
-
-  securityOnLoad();
-}
-
-//******** Generic Build-a-tab
-// Assumes the views are empty. Only called once to build the tabs, and
-// does so by farming the task off to another thread via setTimeout().
-// The actual work is done with a TreeWalker that calls doGrab() once for
-// each element node in the document.
-
-var gFrameList = [ ];
-
-function makeTabs(aDocument, aWindow)
-{
-  goThroughFrames(aDocument, aWindow);
-  processFrames();
 }
 
-function goThroughFrames(aDocument, aWindow)
-{
-  gFrameList.push(aDocument);
-  if (aWindow && aWindow.frames.length > 0) {
-    var num = aWindow.frames.length;
-    for (var i = 0; i < num; i++)
-      goThroughFrames(aWindow.frames[i].document, aWindow.frames[i]);  // recurse through the frames
-  }
-}
-
-function processFrames()
+function makeMediaTab(imageViewRows)
 {
-  if (gFrameList.length) {
-    var doc = gFrameList[0];
-    onProcessFrame.forEach(function(func) { func(doc); });
-    var iterator = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, grabAll);
-    gFrameList.shift();
-    setTimeout(doGrab, 10, iterator);
-    onFinished.push(selectImage);
+  // Call addImage passing in the image rows to add to the view on the Media Tab.
+  for (let image of imageViewRows) {
+    let [url, type, alt, elem, isBg] = image;
+    addImage(url, type, alt, elem, isBg);
   }
-  else
-    onFinished.forEach(function(func) { func(); });
-}
-
-function doGrab(iterator)
-{
-  for (var i = 0; i < 500; ++i)
-    if (!iterator.nextNode()) {
-      processFrames();
-      return;
-    }
-
-  setTimeout(doGrab, 10, iterator);
+  selectImage();
 }
 
 function addImage(url, type, alt, elem, isBg)
 {
   if (!url)
     return;
 
   if (!gImageHash.hasOwnProperty(url))
@@ -634,88 +620,27 @@ function addImage(url, type, alt, elem, 
       Components.classes["@mozilla.org/observer-service;1"]
                 .getService(Components.interfaces.nsIObserverService)
                 .addObserver(imagePermissionObserver, "perm-changed", false);
     }
   }
   else {
     var i = gImageHash[url][type][alt];
     gImageView.data[i][COL_IMAGE_COUNT]++;
-    if (elem == gImageElement)
+    // The same image can occur several times on the page at different sizes.
+    // If the "View Image Info" context menu item was used, ensure we select
+    // the correct element.
+    if (!gImageView.data[i][COL_IMAGE_BG] &&
+        gImageElement && url == gImageElement.currentSrc &&
+        gImageElement.width == elem.width &&
+        gImageElement.height == elem.height &&
+        gImageElement.imageText == elem.imageText) {
       gImageView.data[i][COL_IMAGE_NODE] = elem;
-  }
-}
-
-function grabAll(elem)
-{
-  // check for images defined in CSS (e.g. background, borders), any node may have multiple
-  var computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
-
-  if (computedStyle) {
-    var addImgFunc = function (label, val) {
-      if (val.primitiveType == CSSPrimitiveValue.CSS_URI) {
-        addImage(val.getStringValue(), label, gStrings.notSet, elem, true);
-      }
-      else if (val.primitiveType == CSSPrimitiveValue.CSS_STRING) {
-        // This is for -moz-image-rect.
-        // TODO: Reimplement once bug 714757 is fixed
-        var strVal = val.getStringValue();
-        if (strVal.search(/^.*url\(\"?/) > -1) {
-          url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
-          addImage(url, label, gStrings.notSet, elem, true);
-        }
-      }
-      else if (val.cssValueType == CSSValue.CSS_VALUE_LIST) {
-        // recursively resolve multiple nested CSS value lists
-        for (var i = 0; i < val.length; i++)
-          addImgFunc(label, val.item(i));
-      }
-    };
-
-    addImgFunc(gStrings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
-    addImgFunc(gStrings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
-    addImgFunc(gStrings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
-    addImgFunc(gStrings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
+    }
   }
-
-  // one swi^H^H^Hif-else to rule them all
-  if (elem instanceof HTMLImageElement)
-    addImage(elem.src, gStrings.mediaImg,
-             (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
-  else if (elem instanceof SVGImageElement) {
-    try {
-      // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
-      //       or the URI formed from the baseURI and the URL is not a valid URI
-      var href = makeURLAbsolute(elem.baseURI, elem.href.baseVal);
-      addImage(href, gStrings.mediaImg, "", elem, false);
-    } catch (e) { }
-  }
-  else if (elem instanceof HTMLVideoElement) {
-    addImage(elem.currentSrc, gStrings.mediaVideo, "", elem, false);
-  }
-  else if (elem instanceof HTMLAudioElement) {
-    addImage(elem.currentSrc, gStrings.mediaAudio, "", elem, false);
-  }
-  else if (elem instanceof HTMLLinkElement) {
-    if (elem.rel && /\bicon\b/i.test(elem.rel))
-      addImage(elem.href, gStrings.mediaLink, "", elem, false);
-  }
-  else if (elem instanceof HTMLInputElement || elem instanceof HTMLButtonElement) {
-    if (elem.type.toLowerCase() == "image")
-      addImage(elem.src, gStrings.mediaInput,
-               (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
-  }
-  else if (elem instanceof HTMLObjectElement)
-    addImage(elem.data, gStrings.mediaObject, getValueText(elem), elem, false);
-  else if (elem instanceof HTMLEmbedElement)
-    addImage(elem.src, gStrings.mediaEmbed, "", elem, false);
-
-  onProcessElement.forEach(function(func) { func(elem); });
-
-  return NodeFilter.FILTER_ACCEPT;
 }
 
 //******** Link Stuff
 function openURL(target)
 {
   var url = target.parentNode.childNodes[2].value;
   window.open(url, "_blank", "chrome");
 }
@@ -809,24 +734,25 @@ function saveMedia()
     if (url) {
       var titleKey = "SaveImageTitle";
 
       if (item instanceof HTMLVideoElement)
         titleKey = "SaveVideoTitle";
       else if (item instanceof HTMLAudioElement)
         titleKey = "SaveAudioTitle";
 
-      saveURL(url, null, titleKey, false, false, makeURI(item.baseURI), gDocument);
+      saveURL(url, null, titleKey, false, false, makeURI(item.baseURI),
+              null, gDocInfo.isContentWindowPrivate);
     }
   } else {
     selectSaveFolder(function(aDirectory) {
       if (aDirectory) {
         var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
           internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
-                       aChosenData, aBaseURI, gDocument);
+                       aChosenData, aBaseURI, null, gDocInfo.isContentWindowPrivate);
         };
 
         for (var i = 0; i < rowArray.length; i++) {
           var v = rowArray[i];
           var dir = aDirectory.clone();
           var item = gImageView.data[v][COL_IMAGE_NODE];
           var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
           var uri = makeURI(uriString);
@@ -888,37 +814,27 @@ function onImageSelect()
     mediaSaveBox.collapsed = true;
     splitter.collapsed     = false;
     previewBox.collapsed   = false;
     tree.flex = 0;
     makePreview(getSelectedRows(tree)[0]);
   }
 }
 
+// Makes the media preview (image, video, etc) for the selected row on the media tab.
 function makePreview(row)
 {
   var imageTree = document.getElementById("imagetree");
   var item = gImageView.data[row][COL_IMAGE_NODE];
   var url = gImageView.data[row][COL_IMAGE_ADDRESS];
   var isBG = gImageView.data[row][COL_IMAGE_BG];
   var isAudio = false;
 
   setItemValue("imageurltext", url);
-
-  var imageText;
-  if (!isBG &&
-      !(item instanceof SVGImageElement) &&
-      !(gDocument instanceof ImageDocument)) {
-    imageText = item.title || item.alt;
-
-    if (!imageText && !(item instanceof HTMLImageElement))
-      imageText = getValueText(item);
-  }
-  setItemValue("imagetext", imageText);
-
+  setItemValue("imagetext", item.imageText);
   setItemValue("imagelongdesctext", item.longDesc);
 
   // get cache info
   var cacheKey = url.replace(/#.*$/, "");
   openCacheEntry(cacheKey, function(cacheEntry) {
     // find out the file size
     var sizeText;
     if (cacheEntry) {
@@ -926,42 +842,18 @@ function makePreview(row)
       var kbSize = Math.round(imageSize / 1024 * 100) / 100;
       sizeText = gBundle.getFormattedString("generalSize",
                                             [formatNumber(kbSize), formatNumber(imageSize)]);
     }
     else
       sizeText = gBundle.getString("mediaUnknownNotCached");
     setItemValue("imagesizetext", sizeText);
 
-    var mimeType;
-    var numFrames = 1;
-    if (item instanceof HTMLObjectElement ||
-        item instanceof HTMLEmbedElement ||
-        item instanceof HTMLLinkElement)
-      mimeType = item.type;
-
-    if (!mimeType && !isBG && item instanceof nsIImageLoadingContent) {
-      var imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
-      if (imageRequest) {
-        mimeType = imageRequest.mimeType;
-        var image = imageRequest.image;
-        if (image)
-          numFrames = image.numFrames;
-      }
-    }
-
-    if (!mimeType)
-      mimeType = getContentTypeFromHeaders(cacheEntry);
-
-    // if we have a data url, get the MIME type from the url
-    if (!mimeType && url.startsWith("data:")) {
-      let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
-      if (dataMimeType)
-        mimeType = dataMimeType[1].toLowerCase();
-    }
+    var mimeType = item.mimeType || this.getContentTypeFromHeaders(cacheEntry);
+    var numFrames = item.numFrames;
 
     var imageType;
     if (mimeType) {
       // We found the type, try to display it nicely
       let imageMimeType = /^image\/(.*)/i.exec(mimeType);
       if (imageMimeType) {
         imageType = imageMimeType[1].toUpperCase();
         if (numFrames > 1)
@@ -986,20 +878,20 @@ function makePreview(row)
 
     var isProtocolAllowed = checkProtocol(gImageView.data[row]);
 
     var newImage = new Image;
     newImage.id = "thepreviewimage";
     var physWidth = 0, physHeight = 0;
     var width = 0, height = 0;
 
-    if ((item instanceof HTMLLinkElement || item instanceof HTMLInputElement ||
-         item instanceof HTMLImageElement ||
-         item instanceof SVGImageElement ||
-         (item instanceof HTMLObjectElement && mimeType && mimeType.startsWith("image/")) || isBG) && isProtocolAllowed) {
+    if ((item.HTMLLinkElement || item.HTMLInputElement ||
+         item.HTMLImageElement || item.SVGImageElement ||
+         (item.HTMLObjectElement && mimeType && mimeType.startsWith("image/")) ||
+         isBG) && isProtocolAllowed) {
       newImage.setAttribute("src", url);
       physWidth = newImage.width || 0;
       physHeight = newImage.height || 0;
 
       // "width" and "height" attributes must be set to newImage,
       // even if there is no "width" or "height attribute in item;
       // otherwise, the preview image cannot be displayed correctly.
       if (!isBG) {
@@ -1008,39 +900,39 @@ function makePreview(row)
       }
       else {
         // the Width and Height of an HTML tag should not be used for its background image
         // (for example, "table" can have "width" or "height" attributes)
         newImage.width = newImage.naturalWidth;
         newImage.height = newImage.naturalHeight;
       }
 
-      if (item instanceof SVGImageElement) {
-        newImage.width = item.width.baseVal.value;
-        newImage.height = item.height.baseVal.value;
+      if (item.SVGImageElement) {
+        newImage.width = item.SVGImageElementWidth;
+        newImage.height = item.SVGImageElementHeight;
       }
 
       width = newImage.width;
       height = newImage.height;
 
       document.getElementById("theimagecontainer").collapsed = false
       document.getElementById("brokenimagecontainer").collapsed = true;
     }
-    else if (item instanceof HTMLVideoElement && isProtocolAllowed) {
+    else if (item.HTMLVideoElement && isProtocolAllowed) {
       newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
       newImage.id = "thepreviewimage";
       newImage.src = url;
       newImage.controls = true;
       width = physWidth = item.videoWidth;
       height = physHeight = item.videoHeight;
 
       document.getElementById("theimagecontainer").collapsed = false;
       document.getElementById("brokenimagecontainer").collapsed = true;
     }
-    else if (item instanceof HTMLAudioElement && isProtocolAllowed) {
+    else if (item.HTMLAudioElement && isProtocolAllowed) {
       newImage = new Audio;
       newImage.id = "thepreviewimage";
       newImage.src = url;
       newImage.controls = true;
       isAudio = true;
 
       document.getElementById("theimagecontainer").collapsed = false;
       document.getElementById("brokenimagecontainer").collapsed = true;
@@ -1068,18 +960,16 @@ function makePreview(row)
       }
     }
     setItemValue("imagedimensiontext", imageSize);
 
     makeBlockImage(url);
 
     imageContainer.removeChild(oldImage);
     imageContainer.appendChild(newImage);
-
-    onImagePreviewShown.forEach(function(func) { func(); });
   });
 }
 
 function makeBlockImage(url)
 {
   var permissionManager = Components.classes[PERMISSION_CONTRACTID]
                                     .getService(nsIPermissionManager);
   var prefs = Components.classes[PREFERENCES_CONTRACTID]
@@ -1125,79 +1015,19 @@ var imagePermissionObserver = {
   }
 }
 
 function getContentTypeFromHeaders(cacheEntryDescriptor)
 {
   if (!cacheEntryDescriptor)
     return null;
 
-  return (/^Content-Type:\s*(.*?)\s*(?:\;|$)/mi
-          .exec(cacheEntryDescriptor.getMetaDataElement("response-head")))[1];
-}
-
-//******** Other Misc Stuff
-// Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
-// parse a node to extract the contents of the node
-function getValueText(node)
-{
-  var valueText = "";
-
-  // form input elements don't generally contain information that is useful to our callers, so return nothing
-  if (node instanceof HTMLInputElement ||
-      node instanceof HTMLSelectElement ||
-      node instanceof HTMLTextAreaElement)
-    return valueText;
-
-  // otherwise recurse for each child
-  var length = node.childNodes.length;
-  for (var i = 0; i < length; i++) {
-    var childNode = node.childNodes[i];
-    var nodeType = childNode.nodeType;
-
-    // text nodes are where the goods are
-    if (nodeType == Node.TEXT_NODE)
-      valueText += " " + childNode.nodeValue;
-    // and elements can have more text inside them
-    else if (nodeType == Node.ELEMENT_NODE) {
-      // images are special, we want to capture the alt text as if the image weren't there
-      if (childNode instanceof HTMLImageElement)
-        valueText += " " + getAltText(childNode);
-      else
-        valueText += " " + getValueText(childNode);
-    }
-  }
-
-  return stripWS(valueText);
-}
-
-// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
-// traverse the tree in search of an img or area element and grab its alt tag
-function getAltText(node)
-{
-  var altText = "";
-
-  if (node.alt)
-    return node.alt;
-  var length = node.childNodes.length;
-  for (var i = 0; i < length; i++)
-    if ((altText = getAltText(node.childNodes[i]) != undefined))  // stupid js warning...
-      return altText;
-  return "";
-}
-
-// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
-// strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space
-function stripWS(text)
-{
-  var middleRE = /\s+/g;
-  var endRE = /(^\s+)|(\s+$)/g;
-
-  text = text.replace(middleRE, " ");
-  return text.replace(endRE, "");
+  let headers = cacheEntryDescriptor.getMetaDataElement("response-head");
+  let type = /^Content-Type:\s*(.*?)\s*(?:\;|$)/mi.exec(headers);
+  return type && type[1];
 }
 
 function setItemValue(id, value)
 {
   var item = document.getElementById(id);
   if (value) {
     item.parentNode.collapsed = false;
     item.value = value;
@@ -1276,18 +1106,23 @@ function doSelectAll()
 
 function selectImage()
 {
   if (!gImageElement)
     return;
 
   var tree = document.getElementById("imagetree");
   for (var i = 0; i < tree.view.rowCount; i++) {
-    if (gImageElement == gImageView.data[i][COL_IMAGE_NODE] &&
-        !gImageView.data[i][COL_IMAGE_BG]) {
+    // If the image row element is the image selected from the "View Image Info" context menu item.
+    let image = gImageView.data[i][COL_IMAGE_NODE];
+    if (!gImageView.data[i][COL_IMAGE_BG] &&
+        gImageElement.currentSrc == gImageView.data[i][COL_IMAGE_ADDRESS] &&
+        gImageElement.width == image.width &&
+        gImageElement.height == image.height &&
+        gImageElement.imageText == image.imageText) {
       tree.view.selection.select(i);
       tree.treeBoxObject.ensureRowIsVisible(i);
       tree.focus();
       return;
     }
   }
 }
 
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -23,19 +23,18 @@ var permissionObserver = {
           initRow(permission.type);
         else if (permission.type.startsWith("plugin"))
           setPluginsRadioState();
       }
     }
   }
 };
 
-function onLoadPermission()
+function onLoadPermission(uri)
 {
-  var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
   var permTab = document.getElementById("permTab");
   if (SitePermissions.isSupportedURI(uri)) {
     gPermURI = uri;
     var hostText = document.getElementById("hostText");
     hostText.value = gPermURI.prePath;
 
     for (var i of gPermissions)
       initRow(i);
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -4,39 +4,40 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
                                   "resource://gre/modules/LoginHelper.jsm");
 
 var security = {
+  init: function(uri, windowInfo) {
+    this.uri = uri;
+    this.windowInfo = windowInfo;
+  },
+
   // Display the server certificate (static)
   viewCert : function () {
     var cert = security._cert;
     viewCertHelper(window, cert);
   },
 
   _getSecurityInfo : function() {
     const nsIX509Cert = Components.interfaces.nsIX509Cert;
     const nsIX509CertDB = Components.interfaces.nsIX509CertDB;
     const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
     const nsISSLStatusProvider = Components.interfaces.nsISSLStatusProvider;
     const nsISSLStatus = Components.interfaces.nsISSLStatus;
 
     // We don't have separate info for a frame, return null until further notice
     // (see bug 138479)
-    if (gWindow != gWindow.top)
+    if (!this.windowInfo.isTopWindow)
       return null;
 
-    var hName = null;
-    try {
-      hName = gWindow.location.host;
-    }
-    catch (exception) { }
+    var hostName = this.windowInfo.hostName;
 
     var ui = security._getSecurityUI();
     if (!ui)
       return null;
 
     var isBroken =
       (ui.state & Components.interfaces.nsIWebProgressListener.STATE_IS_BROKEN);
     var isMixed =
@@ -51,26 +52,25 @@ var security = {
 
     if (!isInsecure && status) {
       status.QueryInterface(nsISSLStatus);
       var cert = status.serverCert;
       var issuerName =
         this.mapIssuerOrganization(cert.issuerOrganization) || cert.issuerName;
 
       var retval = {
-        hostName : hName,
+        hostName : hostName,
         cAName : issuerName,
         encryptionAlgorithm : undefined,
         encryptionStrength : undefined,
         version: undefined,
         isBroken : isBroken,
         isMixed : isMixed,
         isEV : isEV,
-        cert : cert,
-        fullLocation : gWindow.location
+        cert : cert
       };
 
       var version;
       try {
         retval.encryptionAlgorithm = status.cipherName;
         retval.encryptionStrength = status.secretKeyLength;
         version = status.protocolVersion;
       }
@@ -90,26 +90,26 @@ var security = {
         case nsISSLStatus.TLS_VERSION_1_2:
           retval.version = "TLS 1.2"
           break;
       }
 
       return retval;
     } else {
       return {
-        hostName : hName,
+        hostName : hostName,
         cAName : "",
         encryptionAlgorithm : "",
         encryptionStrength : 0,
         version: "",
         isBroken : isBroken,
         isMixed : isMixed,
         isEV : isEV,
-        cert : null,
-        fullLocation : gWindow.location
+        cert : null
+
       };
     }
   },
 
   // Find the secureBrowserUI object (if present)
   _getSecurityUI : function() {
     if (window.opener.gBrowser)
       return window.opener.gBrowser.securityUI;
@@ -135,23 +135,22 @@ var security = {
   {
     var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                        .getService(Components.interfaces.nsIWindowMediator);
     var win = wm.getMostRecentWindow("Browser:Cookies");
     var eTLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"].
                       getService(Components.interfaces.nsIEffectiveTLDService);
 
     var eTLD;
-    var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
     try {
-      eTLD = eTLDService.getBaseDomain(uri);
+      eTLD = eTLDService.getBaseDomain(this.uri);
     }
     catch (e) {
       // getBaseDomain will fail if the host is an IP address or is empty
-      eTLD = uri.asciiHost;
+      eTLD = this.uri.asciiHost;
     }
 
     if (win) {
       win.gCookiesWindow.setFilter(eTLD);
       win.focus();
     }
     else
       window.openDialog("chrome://browser/content/preferences/cookies.xul",
@@ -163,17 +162,19 @@ var security = {
    */
   viewPasswords : function() {
     LoginHelper.openPasswordManager(window, this._getSecurityInfo().hostName);
   },
 
   _cert : null
 };
 
-function securityOnLoad() {
+function securityOnLoad(uri, windowInfo) {
+  security.init(uri, windowInfo);
+
   var info = security._getSecurityInfo();
   if (!info) {
     document.getElementById("securityTab").hidden = true;
     return;
   }
   else {
     document.getElementById("securityTab").hidden = false;
   }
@@ -221,17 +222,16 @@ function securityOnLoad() {
   }
   else
     viewCert.collapsed = true;
 
   /* Set Privacy & History section text */
   var yesStr = pageInfoBundle.getString("yes");
   var noStr = pageInfoBundle.getString("no");
 
-  var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
   setText("security-privacy-cookies-value",
           hostHasCookies(uri) ? yesStr : noStr);
   setText("security-privacy-passwords-value",
           realmHasPasswords(uri) ? yesStr : noStr);
   
   var visitCount = previousVisitCount(info.hostName);
   if(visitCount > 1) {
     setText("security-privacy-history-value",
--- a/browser/base/content/test/general/browser_bug517902.js
+++ b/browser/base/content/test/general/browser_bug517902.js
@@ -9,17 +9,17 @@ function test() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
 
     var doc = gBrowser.contentDocument;
     var testImg = doc.getElementById("test-image");
     var pageInfo = BrowserPageInfo(doc, "mediaTab", testImg);
 
     pageInfo.addEventListener("load", function () {
       pageInfo.removeEventListener("load", arguments.callee, true);
-      pageInfo.onImagePreviewShown.push(function () {
+      pageInfo.onFinished.push(function () {
         executeSoon(function () {
           var pageInfoImg = pageInfo.document.getElementById("thepreviewimage");
 
           is(pageInfoImg.src, testImg.src, "selected image has the correct source");
           is(pageInfoImg.width, testImg.width, "selected image has the correct width");
           is(pageInfoImg.height, testImg.height, "selected image has the correct height");
 
           pageInfo.close();
--- a/browser/base/content/test/general/browser_bug537013.js
+++ b/browser/base/content/test/general/browser_bug537013.js
@@ -10,17 +10,18 @@ let texts = [
   "Klein bottle for sale. Inquire within.",
   "To err is human; to forgive is not company policy."
 ];
 
 let Clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
 let HasFindClipboard = Clipboard.supportsFindClipboard();
 
 function addTabWithText(aText, aCallback) {
-  let newTab = gBrowser.addTab("data:text/html,<h1 id='h1'>" + aText + "</h1>");
+  let newTab = gBrowser.addTab("data:text/html;charset=utf-8,<h1 id='h1'>" +
+                               aText + "</h1>");
   tabs.push(newTab);
   gBrowser.selectedTab = newTab;
 }
 
 function setFindString(aString) {
   gFindBar.open();
   gFindBar._findField.focus();
   gFindBar._findField.select();
@@ -73,16 +74,22 @@ function continueTests1() {
 
   // While we're here, let's test bug 253793
   gBrowser.reload();
   gBrowser.addEventListener("DOMContentLoaded", continueTests2, true);
 }
 
 function continueTests2() {
   gBrowser.removeEventListener("DOMContentLoaded", continueTests2, true);
+  waitForCondition(() => !gFindBar.getElement("highlight").checked,
+                   continueTests3,
+                   "Highlight never reset!");
+}
+
+function continueTests3() {
   ok(!gFindBar.getElement("highlight").checked, "Highlight button reset!");
   gFindBar.close();
   ok(gFindBar.hidden, "First tab doesn't show find bar!");
   gBrowser.selectedTab = tabs[1];
   ok(!gFindBar.hidden, "Second tab shows find bar!");
   // Test for bug 892384
   is(gFindBar._findField.getAttribute("focused"), "true",
      "Open findbar refocused on tab change!");
--- a/browser/base/content/test/general/browser_bug590206.js
+++ b/browser/base/content/test/general/browser_bug590206.js
@@ -1,138 +1,140 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
+/*
+ * Test the identity mode UI for a variety of page types
  */
 
 const DUMMY = "browser/browser/base/content/test/general/dummy_page.html";
 
-function loadNewTab(aURL, aCallback) {
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(aURL);
-
-  gBrowser.selectedBrowser.addEventListener("load", function() {
-    if (gBrowser.selectedBrowser.currentURI.spec != aURL)
-      return;
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-
-    aCallback(gBrowser.selectedTab);
-  }, true);
+function loadNewTab(url) {
+  return BrowserTestUtils.openNewForegroundTab(gBrowser, url);
 }
 
 function getIdentityMode() {
   return document.getElementById("identity-box").className;
 }
 
-var TESTS = [
-function test_webpage() {
+// This test is slow on Linux debug e10s
+requestLongerTimeout(2);
+
+add_task(function* test_webpage() {
+  let oldTab = gBrowser.selectedTab;
+
+  let newTab = yield loadNewTab("http://example.com/" + DUMMY);
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.removeTab(newTab);
+});
+
+add_task(function* test_blank() {
   let oldTab = gBrowser.selectedTab;
 
-  loadNewTab("http://example.com/" + DUMMY, function(aNewTab) {
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  let newTab = yield loadNewTab("about:blank");
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
-
-    gBrowser.removeTab(aNewTab);
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    runNextTest();
-  });
-},
+  gBrowser.removeTab(newTab);
+});
 
-function test_blank() {
+add_task(function* test_chrome() {
   let oldTab = gBrowser.selectedTab;
 
-  loadNewTab("about:blank", function(aNewTab) {
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  let newTab = yield loadNewTab("chrome://mozapps/content/extensions/extensions.xul");
+  is(getIdentityMode(), "fileURI", "Identity should be file");
 
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
-
-    gBrowser.removeTab(aNewTab);
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "fileURI", "Identity should be file");
 
-    runNextTest();
-  });
-},
+  gBrowser.removeTab(newTab);
+});
 
-function test_chrome() {
+add_task(function* test_https() {
   let oldTab = gBrowser.selectedTab;
 
-  // Since users aren't likely to type in full chrome URLs, we won't show
-  // the positive security indicator on it, but we will show it on about:addons.
-  loadNewTab("chrome://mozapps/content/extensions/extensions.xul", function(aNewTab) {
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
-
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  let newTab = yield loadNewTab("https://example.com/" + DUMMY);
+  is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
-
-    gBrowser.removeTab(aNewTab);
-
-    runNextTest();
-  });
-},
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-function test_https() {
-  let oldTab = gBrowser.selectedTab;
-
-  loadNewTab("https://example.com/" + DUMMY, function(aNewTab) {
-    is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
-
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
-
-    gBrowser.removeTab(aNewTab);
+  gBrowser.removeTab(newTab);
+});
 
-    runNextTest();
-  });
-},
-
-function test_addons() {
+add_task(function* test_addons() {
   let oldTab = gBrowser.selectedTab;
 
-  loadNewTab("about:addons", function(aNewTab) {
-    is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+  let newTab = yield loadNewTab("about:addons");
+  is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-    gBrowser.selectedTab = oldTab;
-    is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "chromeUI", "Identity should be chrome");
 
-    gBrowser.selectedTab = aNewTab;
-    is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+  gBrowser.removeTab(newTab);
+});
 
-    gBrowser.removeTab(aNewTab);
+add_task(function* test_file() {
+  let oldTab = gBrowser.selectedTab;
+  let fileURI = getTestFilePath("");
+
+  let newTab = yield loadNewTab(fileURI);
+  is(getIdentityMode(), "fileURI", "Identity should be file");
 
-    runNextTest();
-  });
-}
-];
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-var gTestStart = null;
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "fileURI", "Identity should be file");
+
+  gBrowser.removeTab(newTab);
+});
 
-function runNextTest() {
-  if (gTestStart)
-    info("Test part took " + (Date.now() - gTestStart) + "ms");
+add_task(function test_resource_uri() {
+  let oldTab = gBrowser.selectedTab;
+  let dataURI = "resource://gre/modules/Services.jsm"
+
+  let newTab = yield loadNewTab(dataURI);
 
-  if (TESTS.length == 0) {
-    finish();
-    return;
-  }
+  is(getIdentityMode(), "fileURI", "Identity should be unknown");
+
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "fileURI", "Identity should be unknown");
 
-  info("Running " + TESTS[0].name);
-  gTestStart = Date.now();
-  TESTS.shift()();
-};
+  gBrowser.removeTab(newTab);
+});
+
+add_task(function test_data_uri() {
+  let oldTab = gBrowser.selectedTab;
+  let dataURI = "data:text/html,hi"
 
-function test() {
-  waitForExplicitFinish();
+  let newTab = yield loadNewTab(dataURI);
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
-  runNextTest();
-}
+  gBrowser.selectedTab = newTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.removeTab(newTab);
+});
--- a/browser/base/content/test/general/browser_contentSearchUI.js
+++ b/browser/base/content/test/general/browser_contentSearchUI.js
@@ -97,17 +97,17 @@ add_task(function* rightLeftKeys() {
 
   state = yield msg("key", "VK_DOWN");
   checkState(state, "xfoo", ["xfoo", "xbar"], 0);
 
   // This should make the xfoo suggestion sticky.  To make sure it sticks,
   // trigger suggestions again and cycle through them by pressing Down until
   // nothing is selected again.
   state = yield msg("key", "VK_RIGHT");
-  checkState(state, "xfoo", [], 0);
+  checkState(state, "xfoo", [], -1);
 
   state = yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
   checkState(state, "xfoo", ["xfoofoo", "xfoobar"], -1);
 
   state = yield msg("key", "VK_DOWN");
   checkState(state, "xfoofoo", ["xfoofoo", "xfoobar"], 0);
 
   state = yield msg("key", "VK_DOWN");
@@ -120,30 +120,212 @@ add_task(function* rightLeftKeys() {
   checkState(state, "xfoo", ["xfoofoo", "xfoobar"], 3);
 
   state = yield msg("key", "VK_DOWN");
   checkState(state, "xfoo", ["xfoofoo", "xfoobar"], -1);
 
   yield msg("reset");
 });
 
+add_task(function* tabKey() {
+  yield setUp();
+  yield msg("key", { key: "x", waitForSuggestions: true });
+
+  let state = yield msg("key", "VK_TAB");
+  checkState(state, "x", ["xfoo", "xbar"], 2);
+
+  state = yield msg("key", "VK_TAB");
+  checkState(state, "x", ["xfoo", "xbar"], 3);
+
+  state = yield msg("key", { key: "VK_TAB", modifiers: { shiftKey: true }});
+  checkState(state, "x", ["xfoo", "xbar"], 2);
+
+  state = yield msg("key", { key: "VK_TAB", modifiers: { shiftKey: true }});
+  checkState(state, "x", [], -1);
+
+  yield setUp();
+
+  yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
+
+  for (let i = 0; i < 3; ++i) {
+    state = yield msg("key", "VK_TAB");
+  }
+  checkState(state, "x", [], -1);
+
+  yield setUp();
+
+  yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
+  state = yield msg("key", "VK_DOWN");
+  checkState(state, "xfoo", ["xfoo", "xbar"], 0);
+
+  state = yield msg("key", "VK_TAB");
+  checkState(state, "xfoo", ["xfoo", "xbar"], 0, 0);
+
+  state = yield msg("key", "VK_TAB");
+  checkState(state, "xfoo", ["xfoo", "xbar"], 0, 1);
+
+  state = yield msg("key", "VK_DOWN");
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
+
+  state = yield msg("key", "VK_DOWN");
+  checkState(state, "x", ["xfoo", "xbar"], 2);
+
+  state = yield msg("key", "VK_UP");
+  checkState(state, "xbar", ["xfoo", "xbar"], 1);
+
+  state = yield msg("key", "VK_TAB");
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
+
+  state = yield msg("key", "VK_TAB");
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
+
+  state = yield msg("key", "VK_TAB");
+  checkState(state, "xbar", [], -1);
+
+  yield msg("reset");
+});
+
+add_task(function* cycleSuggestions() {
+  yield setUp();
+  yield msg("key", { key: "x", waitForSuggestions: true });
+
+  let cycle = Task.async(function* (aSelectedButtonIndex) {
+    let modifiers = {
+      shiftKey: true,
+      accelKey: true,
+    };
+  
+    let state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
+    checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex);
+  
+    state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
+    checkState(state, "xbar", ["xfoo", "xbar"], 1, aSelectedButtonIndex);
+  
+    state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
+    checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex);
+  
+    state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
+    checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex);
+  
+    state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
+    checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex);
+  
+    state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
+    checkState(state, "xbar", ["xfoo", "xbar"], 1, aSelectedButtonIndex);
+  
+    state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
+    checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex);
+  
+    state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
+    checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex);
+  });
+  
+  yield cycle();
+
+  // Repeat with a one-off selected.
+  let state = yield msg("key", "VK_TAB");
+  checkState(state, "x", ["xfoo", "xbar"], 2);
+  yield cycle(0);
+
+  // Repeat with the settings button selected.
+  state = yield msg("key", "VK_TAB");
+  checkState(state, "x", ["xfoo", "xbar"], 3);
+  yield cycle(1);
+
+  yield msg("reset");
+});
+
+add_task(function* cycleOneOffs() {
+  yield setUp();
+  yield msg("key", { key: "x", waitForSuggestions: true });
+
+  yield msg("addDuplicateOneOff");
+
+  let state = yield msg("key", "VK_DOWN");
+  state = yield msg("key", "VK_DOWN");
+  checkState(state, "xbar", ["xfoo", "xbar"], 1);
+
+  let modifiers = {
+    altKey: true,
+  };
+
+  state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
+
+  state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
+
+  state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
+  checkState(state, "xbar", ["xfoo", "xbar"], 1);
+
+  state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
+
+  state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
+
+  state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
+  checkState(state, "xbar", ["xfoo", "xbar"], 1);
+
+  // If the settings button is selected, pressing alt+up/down should select the
+  // last/first one-off respectively (and deselect the settings button).
+  yield msg("key", "VK_TAB");
+  yield msg("key", "VK_TAB");
+  state = yield msg("key", "VK_TAB"); // Settings button selected.
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 2);
+
+  state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
+
+  state = yield msg("key", "VK_TAB");
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 2);
+
+  state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
+  checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
+
+  yield msg("removeLastOneOff");
+  yield msg("reset");
+});
+
 add_task(function* mouse() {
   yield setUp();
 
   let state = yield msg("key", { key: "x", waitForSuggestions: true });
   checkState(state, "x", ["xfoo", "xbar"], -1);
 
-  for (let i = 0; i < 4; ++i) {
-    state = yield msg("mousemove", i);
-    checkState(state, "x", ["xfoo", "xbar"], i);
-  }
+  state = yield msg("mousemove", 0);
+  checkState(state, "x", ["xfoo", "xbar"], 0);
+
+  state = yield msg("mousemove", 1);
+  checkState(state, "x", ["xfoo", "xbar"], 1);
+
+  state = yield msg("mousemove", 2);
+  checkState(state, "x", ["xfoo", "xbar"], 1, 0);
+
+  state = yield msg("mousemove", 3);
+  checkState(state, "x", ["xfoo", "xbar"], 1, 1);
 
   state = yield msg("mousemove", -1);
+  checkState(state, "x", ["xfoo", "xbar"], 1);
+
+  yield msg("reset");
+  yield setUp();
+
+  state = yield msg("key", { key: "x", waitForSuggestions: true });
   checkState(state, "x", ["xfoo", "xbar"], -1);
 
+  state = yield msg("mousemove", 0);
+  checkState(state, "x", ["xfoo", "xbar"], 0);
+
+  state = yield msg("mousemove", 2);
+  checkState(state, "x", ["xfoo", "xbar"], 0, 0);
+
+  state = yield msg("mousemove", -1);
+  checkState(state, "x", ["xfoo", "xbar"], 0);
+
   yield msg("reset");
 });
 
 add_task(function* formHistory() {
   yield setUp();
 
   // Type an X and add it to form history.
   let state = yield msg("key", { key: "x", waitForSuggestions: true });
@@ -192,16 +374,43 @@ add_task(function* formHistory() {
 
   // Type an X again.  The form history entry should still be gone.
   state = yield msg("key", { key: "x", waitForSuggestions: true });
   checkState(state, "x", ["xfoo", "xbar"], -1);
 
   yield msg("reset");
 });
 
+add_task(function* cycleEngines() {
+  yield setUp();
+  yield msg("key", "VK_DOWN");
+
+  function promiseEngineChange(newEngineName) {
+    let deferred = Promise.defer();
+    Services.obs.addObserver(function resolver(subj, topic, data) {
+      if (data != "engine-current") {
+        return;
+      }
+      is(subj.name, newEngineName, "Engine cycled correctly");
+      Services.obs.removeObserver(resolver, "browser-search-engine-modified");
+      deferred.resolve();
+    }, "browser-search-engine-modified", false);
+  }
+
+  let p = promiseEngineChange(TEST_ENGINE_PREFIX + " " + TEST_ENGINE_2_BASENAME);
+  yield msg("key", { key: "VK_DOWN", modifiers: { accelKey: true }});
+  yield p;
+
+  p = promiseEngineChange(TEST_ENGINE_PREFIX + " " + TEST_ENGINE_BASENAME);
+  yield msg("key", { key: "VK_UP", modifiers: { accelKey: true }});
+  yield p;
+
+  yield msg("reset");
+});
+
 add_task(function* search() {
   yield setUp();
 
   let modifiers = {};
   ["altKey", "ctrlKey", "metaKey", "shiftKey"].forEach(k => modifiers[k] = true);
 
   // Test typing a query and pressing enter.
   let p = msg("waitForSearch");
@@ -292,29 +501,67 @@ add_task(function* search() {
   eventData.searchString = "x";
   eventData.engineName = TEST_ENGINE_PREFIX + " " + TEST_ENGINE_2_BASENAME;
   delete eventData.selection;
   SimpleTest.isDeeply(eventData, mesg, "Search event data");
 
   yield promiseTab();
   yield setUp();
 
+  // Test selecting a suggestion, then clicking a one-off without deselecting the
+  // suggestion.
+  yield msg("key", { key: "x", waitForSuggestions: true });
+  p = msg("waitForSearch");
+  yield msg("mousemove", 1);
+  yield msg("mousemove", 3);
+  yield msg("click", { eltIdx: 3, modifiers: modifiers });
+  mesg = yield p;
+  eventData.searchString = "xfoo"
+  eventData.selection = {
+    index: 1,
+    kind: "mouse",
+  };
+  SimpleTest.isDeeply(eventData, mesg, "Search event data");
+
+  yield promiseTab();
+  yield setUp();
+
+  // Same as above, but with the keyboard.
+  delete modifiers.button;
+  yield msg("key", { key: "x", waitForSuggestions: true });
+  p = msg("waitForSearch");
+  yield msg("key", "VK_DOWN");
+  yield msg("key", "VK_DOWN");
+  yield msg("key", "VK_TAB");
+  yield msg("key", { key: "VK_RETURN", modifiers: modifiers });
+  mesg = yield p;
+  eventData.selection = {
+    index: 1,
+    kind: "key",
+  };
+  SimpleTest.isDeeply(eventData, mesg, "Search event data");
+
+  yield promiseTab();
+  yield setUp();
+
   // Test searching when using IME composition.
   let state = yield msg("startComposition", { data: "" });
   checkState(state, "", [], -1);
   state = yield msg("changeComposition", { data: "x", waitForSuggestions: true });
   checkState(state, "x", [{ str: "x", type: "formHistory" },
                           { str: "xfoo", type: "formHistory" }, "xbar"], -1);
   yield msg("commitComposition");
   delete modifiers.button;
   p = msg("waitForSearch");
   yield msg("key", { key: "VK_RETURN", modifiers: modifiers });
   mesg = yield p;
+  eventData.searchString = "x"
   eventData.originalEvent = modifiers;
   eventData.engineName = TEST_ENGINE_PREFIX + " " + TEST_ENGINE_BASENAME;
+  delete eventData.selection;
   SimpleTest.isDeeply(eventData, mesg, "Search event data");
 
   yield promiseTab();
   yield setUp();
 
   state = yield msg("startComposition", { data: "" });
   checkState(state, "", [], -1);
   state = yield msg("changeComposition", { data: "x", waitForSuggestions: true });
@@ -423,36 +670,49 @@ function msg(type, data=null) {
     }
     gMsgMan.removeMessageListener(TEST_MSG, onMsg);
     deferred.resolve(msg.data.data);
   });
   return deferred.promise;
 }
 
 function checkState(actualState, expectedInputVal, expectedSuggestions,
-                    expectedSelectedIdx) {
+                    expectedSelectedIdx, expectedSelectedButtonIdx) {
   expectedSuggestions = expectedSuggestions.map(sugg => {
     return typeof(sugg) == "object" ? sugg : {
       str: sugg,
       type: "remote",
     };
   });
 
+  if (expectedSelectedIdx == -1 && expectedSelectedButtonIdx != undefined) {
+    expectedSelectedIdx = expectedSuggestions.length + expectedSelectedButtonIdx;
+  }
+  
   let expectedState = {
     selectedIndex: expectedSelectedIdx,
     numSuggestions: expectedSuggestions.length,
     suggestionAtIndex: expectedSuggestions.map(s => s.str),
     isFormHistorySuggestionAtIndex:
       expectedSuggestions.map(s => s.type == "formHistory"),
 
     tableHidden: expectedSuggestions.length == 0,
 
     inputValue: expectedInputVal,
     ariaExpanded: expectedSuggestions.length == 0 ? "false" : "true",
   };
+  if (expectedSelectedButtonIdx != undefined) {
+    expectedState.selectedButtonIndex = expectedSelectedButtonIdx;
+  }
+  else if (expectedSelectedIdx < expectedSuggestions.length) {
+    expectedState.selectedButtonIndex = -1;
+  }
+  else {
+    expectedState.selectedButtonIndex = expectedSelectedIdx - expectedSuggestions.length;
+  }
 
   SimpleTest.isDeeply(actualState, expectedState, "State");
 }
 
 var gMsgMan;
 
 function promiseTab() {
   let deferred = Promise.defer();
--- a/browser/base/content/test/general/browser_pageInfo.js
+++ b/browser/base/content/test/general/browser_pageInfo.js
@@ -10,17 +10,17 @@ function test() {
     Services.obs.addObserver(observer, "page-info-dialog-loaded", false);
     pageInfo = BrowserPageInfo();
   }, true);
   content.location =
     "https://example.com/browser/browser/base/content/test/general/feed_tab.html";
 
   function observer(win, topic, data) {
     Services.obs.removeObserver(observer, "page-info-dialog-loaded");
-    handlePageInfo();
+    pageInfo.onFinished.push(handlePageInfo);
   }
 
   function handlePageInfo() {
     ok(pageInfo.document.getElementById("feedTab"), "Feed tab");
     let feedListbox = pageInfo.document.getElementById("feedListbox");
     ok(feedListbox, "Feed list");
 
     var feedRowsNum = feedListbox.getRowCount();
--- a/browser/base/content/test/general/browser_trackingUI_5.js
+++ b/browser/base/content/test/general/browser_trackingUI_5.js
@@ -17,16 +17,21 @@ registerCleanupFunction(function() {
 
 function hidden(sel) {
   let win = browser.ownerGlobal;
   let el = win.document.querySelector(sel);
   let display = win.getComputedStyle(el).getPropertyValue("display", null);
   return display === "none";
 }
 
+function identityPopupState() {
+  let win = browser.ownerGlobal;
+  return win.document.getElementById("identity-popup").state;
+}
+
 function clickButton(sel) {
   let win = browser.ownerGlobal;
   let el = win.document.querySelector(sel);
   el.doCommand();
 }
 
 function testTrackingPage(window) {
   info("Tracking content must be blocked");
@@ -80,16 +85,18 @@ add_task(function* testExceptionAddition
   info("Load a test page containing tracking elements");
   yield promiseTabLoadEvent(tab, TRACKING_PAGE);
 
   testTrackingPage(tab.ownerDocument.defaultView);
 
   info("Disable TP for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
+  is(identityPopupState(), "closed", "foobar");
+
   yield tabReloadPromise;
   testTrackingPageUnblocked();
 
   info("Test that the exception is remembered across tabs in the same private window");
   tab = browser.selectedTab = browser.addTab();
 
   info("Load a test page containing tracking elements");
   yield promiseTabLoadEvent(tab, TRACKING_PAGE);
@@ -110,13 +117,15 @@ add_task(function* testExceptionPersiste
   info("Load a test page containing tracking elements");
   yield promiseTabLoadEvent(tab, TRACKING_PAGE);
 
   testTrackingPage(tab.ownerDocument.defaultView);
 
   info("Disable TP for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
+  is(identityPopupState(), "closed", "foobar");
+
   yield tabReloadPromise;
   testTrackingPageUnblocked();
 
   privateWin.close();
 });
--- a/browser/base/content/test/general/contentSearchUI.js
+++ b/browser/base/content/test/general/contentSearchUI.js
@@ -119,21 +119,37 @@ let messageHandlers = {
     ack("click");
   },
 
   addInputValueToFormHistory: function () {
     gController.addInputValueToFormHistory();
     ack("addInputValueToFormHistory");
   },
 
+  addDuplicateOneOff: function () {
+    let btn = gController._oneOffButtons[gController._oneOffButtons.length - 1];
+    let newBtn = btn.cloneNode(true);
+    btn.parentNode.appendChild(newBtn);
+    gController._oneOffButtons.push(newBtn);
+    ack("addDuplicateOneOff");
+  },
+
+  removeLastOneOff: function () {
+    gController._oneOffButtons.pop().remove();
+    ack("removeLastOneOff");
+  },
+
   reset: function () {
-    // Reset both the input and suggestions by select all + delete.
+    // Reset both the input and suggestions by select all + delete. If there was
+    // no text entered, this won't have any effect, so also escape to ensure the
+    // suggestions table is closed.
     gController.input.focus();
     content.synthesizeKey("a", { accelKey: true });
     content.synthesizeKey("VK_DELETE", {});
+    content.synthesizeKey("VK_ESCAPE", {});
     ack("reset");
   },
 };
 
 function ack(aType, aData) {
   sendAsyncMessage(TEST_MSG, { type: aType, data: aData || currentState() });
 }
 
@@ -160,16 +176,17 @@ function waitForContentSearchEvent(messa
     mm.removeMessageListener("ContentSearch", listener);
     cb(aMsg.data.data);
   });
 }
 
 function currentState() {
   let state = {
     selectedIndex: gController.selectedIndex,
+    selectedButtonIndex: gController.selectedButtonIndex,
     numSuggestions: gController._table.hidden ? 0 : gController.numSuggestions,
     suggestionAtIndex: [],
     isFormHistorySuggestionAtIndex: [],
 
     tableHidden: gController._table.hidden,
 
     inputValue: gController.input.value,
     ariaExpanded: gController.input.getAttribute("aria-expanded"),
--- a/browser/base/content/test/plugins/browser_pageInfo_plugins.js
+++ b/browser/base/content/test/plugins/browser_pageInfo_plugins.js
@@ -29,17 +29,17 @@ function doOnOpenPageInfo(continuation) 
   // windows if we don't keep a reference to every window we've opened.
   // So, don't reuse pointers to opened Page Info windows - simply append
   // to this list.
   gPageInfo = BrowserPageInfo(null, "permTab");
 }
 
 function pageInfoObserve(win, topic, data) {
   Services.obs.removeObserver(pageInfoObserve, "page-info-dialog-loaded");
-  executeSoon(gNextTest);
+  gPageInfo.onFinished.push(() => executeSoon(gNextTest));
 }
 
 function finishTest() {
   gPermissionManager.remove(makeURI("http://127.0.0.1:8888/"), gTestPermissionString);
   gPermissionManager.remove(makeURI("http://127.0.0.1:8888/"), gSecondTestPermissionString);
   Services.prefs.clearUserPref("plugins.click_to_play");
   gBrowser.removeCurrentTab();
 
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -22,16 +22,18 @@
         <vbox id="identity-popup-security-content" flex="1">
           <label class="identity-popup-headline" crop="end">
             <observes element="identity-popup-content-host" attribute="value"/>
           </label>
           <label class="identity-popup-connection-secure identity-popup-text"
                  value="&identity.connectionSecure;"/>
           <label class="identity-popup-connection-not-secure identity-popup-text"
                  value="&identity.connectionNotSecure;"/>
+          <label class="identity-popup-connection-file-uri identity-popup-text"
+                 value="&identity.connectionFile;"/>
           <label class="identity-popup-connection-internal identity-popup-text"
                  value="&identity.connectionInternal;"/>
         </vbox>
         <button class="identity-popup-expander"
                 oncommand="gIdentityHandler.toggleSubView('security', this)"/>
       </hbox>
 
       <!-- Tracking Protection Section -->
@@ -90,16 +92,18 @@
       <vbox id="identity-popup-securityView-header">
         <label class="identity-popup-headline" crop="end">
           <observes element="identity-popup-content-host" attribute="value"/>
         </label>
         <label class="identity-popup-connection-secure identity-popup-text"
                value="&identity.connectionSecure;"/>
         <label class="identity-popup-connection-not-secure identity-popup-text"
                value="&identity.connectionNotSecure;"/>
+        <label class="identity-popup-connection-file-uri identity-popup-text"
+               value="&identity.connectionFile;"/>
         <label class="identity-popup-connection-internal identity-popup-text"
                value="&identity.connectionInternal;"/>
       </vbox>
 
       <description id="identity-popup-content-verifier"
                    class="identity-popup-text"/>
 
       <description id="identity-popup-securityView-connection"
--- a/browser/components/customizableui/test/browser_946320_tabs_from_other_computers.js
+++ b/browser/components/customizableui/test/browser_946320_tabs_from_other_computers.js
@@ -116,22 +116,18 @@ function configureFxAccountIdentity() {
   let MockInternal = {
     newAccountState(credentials) {
       isnot(credentials, "not expecting credentials");
       let storageManager = new MockFxaStorageManager();
       // and init storage with our user.
       storageManager.initialize(user);
       return new AccountState(storageManager);
     },
-    getCertificate(data, keyPair, mustBeValidUntil) {
-      this.cert = {
-        validUntil: this.now() + 10000,
-        cert: "certificate",
-      };
-      return Promise.resolve(this.cert.cert);
+    _getAssertion(audience) {
+      return Promise.resolve("assertion");
     },
     getCertificateSigned() {
       return Promise.resolve();
     },
   };
   let mockTSC = { // TokenServerClient
     getTokenFromBrowserIDAssertion: function(uri, assertion, cb) {
       token.uid = "username";
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -177,16 +177,20 @@ this.SessionStore = {
   get promiseInitialized() {
     return SessionStoreInternal.promiseInitialized;
   },
 
   get canRestoreLastSession() {
     return SessionStoreInternal.canRestoreLastSession;
   },
 
+  get crashedTabCount() {
+    return SessionStoreInternal._crashedBrowsersCount;
+  },
+
   set canRestoreLastSession(val) {
     SessionStoreInternal.canRestoreLastSession = val;
   },
 
   init: function ss_init() {
     SessionStoreInternal.init();
   },
 
@@ -297,16 +301,20 @@ this.SessionStore = {
   getCurrentState: function (aUpdateAll) {
     return SessionStoreInternal.getCurrentState(aUpdateAll);
   },
 
   reviveCrashedTab(aTab) {
     return SessionStoreInternal.reviveCrashedTab(aTab);
   },
 
+  reviveAllCrashedTabs() {
+    return SessionStoreInternal.reviveAllCrashedTabs();
+  },
+
   navigateAndRestore(tab, loadArguments, historyIndex) {
     return SessionStoreInternal.navigateAndRestore(tab, loadArguments, historyIndex);
   }
 };
 
 // Freeze the SessionStore object. We don't want anyone to modify it.
 Object.freeze(SessionStore);
 
@@ -326,16 +334,19 @@ let SessionStoreInternal = {
   // For each <browser> element, records the current epoch.
   _browserEpochs: new WeakMap(),
 
   // Any browsers that fires the oop-browser-crashed event gets stored in
   // here - that way we know which browsers to ignore messages from (until
   // they get restored).
   _crashedBrowsers: new WeakSet(),
 
+  // The number of crashed browsers.
+  _crashedBrowsersCount: 0,
+
   // A map (xul:browser -> nsIFrameLoader) that maps a browser to the last
   // associated frameLoader we heard about.
   _lastKnownFrameLoader: new WeakMap(),
 
   // A map (xul:browser -> object) that maps a browser associated with a
   // recently closed tab to all its necessary state information we need to
   // properly handle final update message.
   _closedTabs: new WeakMap(),
@@ -1403,16 +1414,20 @@ let SessionStoreInternal = {
 
     if (browser.frameLoader) {
       this._lastKnownFrameLoader.set(browser.permanentKey, browser.frameLoader);
     }
 
     if (!aNoNotification) {
       this.saveStateDelayed(aWindow);
     }
+
+    if (this._crashedBrowsers.has(browser.permanentKey)) {
+      this._crashedBrowsersCount++;
+    }
   },
 
   /**
    * remove listeners for a tab
    * @param aWindow
    *        Window reference
    * @param aTab
    *        Tab reference
@@ -1432,16 +1447,20 @@ let SessionStoreInternal = {
       this._resetTabRestoringState(aTab);
       if (previousState == TAB_STATE_RESTORING)
         this.restoreNextTab();
     }
 
     if (!aNoNotification) {
       this.saveStateDelayed(aWindow);
     }
+
+    if (this._crashedBrowsers.has(browser.permanentKey)) {
+      this._crashedBrowsersCount--;
+    }
   },
 
   /**
    * When a tab closes, collect its properties
    * @param aWindow
    *        Window reference
    * @param aTab
    *        Tab reference
@@ -1611,16 +1630,17 @@ let SessionStoreInternal = {
    *
    * @param aWindow
    *        The window that the crashed browser belongs to.
    * @param aBrowser
    *        The <xul:browser> that is now in the crashed state.
    */
   onBrowserCrashed: function(aWindow, aBrowser) {
     this._crashedBrowsers.add(aBrowser.permanentKey);
+    this._crashedBrowsersCount++;
     // If we never got around to restoring this tab, clear its state so
     // that we don't try restoring if the user switches to it before
     // reviving the crashed browser. This is throwing away the information
     // that the tab was in a pending state when the browser crashed, which
     // is an explicit choice. For now, when restoring all crashed tabs, based
     // on a user preference we'll either restore all of them at once, or only
     // restore the selected tab and lazily restore the rest. We'll make no
     // efforts at this time to be smart and restore all of the tabs that had
@@ -2166,16 +2186,33 @@ let SessionStoreInternal = {
     // at this point.
     if (browser.isRemoteBrowser) {
       throw new Error("SessionStore.reviveCrashedTab: " +
                       "Somehow a crashed browser is still remote.")
     }
 
     let data = TabState.collect(aTab);
     this.restoreTab(aTab, data);
+
+    this._crashedBrowsersCount--;
+  },
+
+  /**
+   * Revive all crashed tabs and reset the crashed tabs count to 0.
+   */
+  reviveAllCrashedTabs() {
+    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
+    while (windowsEnum.hasMoreElements()) {
+      let window = windowsEnum.getNext();
+      for (let tab of window.gBrowser.tabs) {
+        this.reviveCrashedTab(tab);
+      }
+    }
+
+    this._crashedBrowsersCount = 0;
   },
 
   /**
    * Navigate the given |tab| by first collecting its current state and then
    * either changing only the index of the currently shown shistory entry,
    * or restoring the exact same state again and passing the new URL to load
    * in |loadArguments|. Use this method to seamlessly switch between pages
    * loaded in the parent and pages loaded in the child process.
--- a/browser/components/sessionstore/test/browser_crashedTabs.js
+++ b/browser/components/sessionstore/test/browser_crashedTabs.js
@@ -336,8 +336,54 @@ add_task(function test_close_tab_after_c
   let promise = promiseEvent(gBrowser.tabContainer, "TabClose");
 
   // Click the close tab button
   clickButton(browser, "closeTab");
   yield promise;
 
   is(gBrowser.tabs.length, 1, "Should have closed the tab");
 });
+
+/**
+ * Checks that "restore all" button is only shown if more than one tab
+ * has crashed.
+ */
+add_task(function* test_hide_restore_all_button() {
+  let newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  let browser = newTab.linkedBrowser;
+  ok(browser.isRemoteBrowser, "Should be a remote browser");
+  yield promiseBrowserLoaded(browser);
+
+  browser.loadURI(PAGE_1);
+  yield promiseBrowserLoaded(browser);
+
+  yield TabStateFlusher.flush(browser);
+
+  // Crash the tab
+  yield crashBrowser(browser);
+
+  let doc = browser.contentDocument;
+  let restoreAllButton = doc.getElementById("restoreAll");
+  let restoreOneButton = doc.getElementById("restoreTab");
+
+  is(restoreAllButton.getAttribute("hidden"), "true", "Restore All button should be hidden");
+  ok(restoreOneButton.classList.contains("primary"), "Restore Tab button should have the primary class");
+
+  let newTab2 = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+
+  browser.loadURI(PAGE_2);
+  yield promiseBrowserLoaded(browser);
+
+  // Crash the tab
+  yield crashBrowser(browser);
+
+  doc = browser.contentDocument;
+  restoreAllButton = doc.getElementById("restoreAll");
+  restoreOneButton = doc.getElementById("restoreTab");
+
+  ok(!restoreAllButton.hasAttribute("hidden"), "Restore All button should not be hidden");
+  ok(!(restoreOneButton.classList.contains("primary")), "Restore Tab button should not have the primary class");
+
+  gBrowser.removeTab(newTab);
+  gBrowser.removeTab(newTab2);
+});
--- a/browser/config/mozconfigs/linux64/opt-tsan
+++ b/browser/config/mozconfigs/linux64/opt-tsan
@@ -1,8 +1,11 @@
 ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
 
 . $topsrcdir/build/unix/mozconfig.tsan
 
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=tsan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/tooltool-manifests/linux64/tsan.manifest
+++ b/browser/config/tooltool-manifests/linux64/tsan.manifest
@@ -3,10 +3,17 @@
 "clang_version": "r241773"
 }, 
 {
 "size": 89690541, 
 "digest": "470d258d9785a120fcba65eee90daa632a42affa0f97f57d70fc8285bd76bcc27d4d0d70b6c37577ab271a04c843b6269425391a8d6df1967718dba26dd3a73d", 
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2",
 "unpack": true
+},
+{
+"size": 4431740,
+"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3",
+"algorithm": "sha512",
+"filename": "gtk3.tar.xz",
+"unpack": true
 }
 ]
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -0,0 +1,34 @@
+[
+{
+"clang_version": "r183744"
+}, 
+{
+"size": 70350828, 
+"digest": "6cd04e8ec44c6fef159349c22bd0476891e4a2d46479f9586283eaf3305e42f79c720d40dfec0e78d8899c1651189b12e285de60862ffd0612b0dac7a0c336c6", 
+"algorithm": "sha512", 
+"unpack": true,
+"filename": "clang.tar.bz2"
+},
+{
+"size": 2581027, 
+"digest": "9b59abef2bd4ae3a5b792de96e1336d879c1c5b6b07382797ae1bcc299e74ddf805bc95b7bc813cf3b4586db5eb4d0f41d09b2f85f0629cf27e57a4de851129c", 
+"algorithm": "sha512", 
+"unpack": true,
+"filename": "cctools.tar.gz"
+},
+{
+"size": 35215976, 
+"visibility": "internal", 
+"digest": "8be736545ddab25ebded188458ce974d5c9a7e29f3c50d2ebfbcb878f6aff853dd2ff5a3528bdefc64396a10101a1b50fd2fe52000140df33643cebe1ea759da", 
+"algorithm": "sha512", 
+"unpack": true,
+"filename": "MacOSX10.7.sdk.tar.bz2"
+},
+{
+"size": 167175,
+"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+"algorithm": "sha512",
+"unpack": true,
+"filename": "sccache.tar.bz2"
+}
+]
--- a/browser/devtools/shared/test/browser_outputparser.js
+++ b/browser/devtools/shared/test/browser_outputparser.js
@@ -13,17 +13,17 @@ add_task(function*() {
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
 function* performTest() {
   let [host, , doc] = yield createHost("bottom", "data:text/html," +
     "<h1>browser_outputParser.js</h1><div></div>");
 
-  let parser = new OutputParser();
+  let parser = new OutputParser(doc);
   testParseCssProperty(doc, parser);
   testParseCssVar(doc, parser);
 
   host.destroy();
 }
 
 // Class name used in color swatch.
 let COLOR_TEST_CLASS = "test-class";
--- a/browser/devtools/styleeditor/styleeditor.xul
+++ b/browser/devtools/styleeditor/styleeditor.xul
@@ -129,17 +129,17 @@
       <xul:box class="splitview-side-details devtools-main-content"/>
 
       <div id="splitview-templates" hidden="true">
         <li id="splitview-tpl-summary-stylesheet" tabindex="0">
           <xul:label class="stylesheet-enabled" tabindex="0"
             tooltiptext="&visibilityToggle.tooltip;"
             accesskey="&saveButton.accesskey;"></xul:label>
           <hgroup class="stylesheet-info">
-            <h1><a class="stylesheet-name" tabindex="0"><xul:label crop="start"/></a></h1>
+            <h1><a class="stylesheet-name" tabindex="0"><xul:label crop="center"/></a></h1>
             <div class="stylesheet-more">
               <h3 class="stylesheet-title"></h3>
               <h3 class="stylesheet-linked-file"></h3>
               <h3 class="stylesheet-rule-count"></h3>
               <xul:spacer/>
               <h3><xul:label class="stylesheet-saveButton"
                     tooltiptext="&saveButton.tooltip;"
                     accesskey="&saveButton.accesskey;">&saveButton.label;</xul:label></h3>
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -132,17 +132,17 @@ UpdateProcess.prototype = {
 function CssComputedView(inspector, document, pageStyle) {
   this.inspector = inspector;
   this.styleDocument = document;
   this.styleWindow = this.styleDocument.defaultView;
   this.pageStyle = pageStyle;
 
   this.propertyViews = [];
 
-  this._outputParser = new OutputParser();
+  this._outputParser = new OutputParser(document);
 
   let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
     .getService(Ci.nsIXULChromeRegistry);
   this.getRTLAttr = chromeReg.isLocaleRTL("global") ? "rtl" : "ltr";
 
   // Create bound methods.
   this.focusWindow = this.focusWindow.bind(this);
   this._onKeypress = this._onKeypress.bind(this);
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -329,17 +329,21 @@ ElementStyle.prototype = {
     // Gather all the text properties applied by these rules, ordered
     // from more- to less-specific. Text properties from keyframes rule are
     // excluded from being marked as overridden since a number of criteria such
     // as time, and animation overlay are required to be check in order to
     // determine if the property is overridden.
     let textProps = [];
     for (let rule of this.rules) {
       if (rule.pseudoElement == pseudo && !rule.keyframes) {
-        textProps = textProps.concat(rule.textProps.slice(0).reverse());
+        for (let textProp of rule.textProps.slice(0).reverse()) {
+          if (textProp.enabled) {
+            textProps.push(textProp);
+          }
+        }
       }
     }
 
     // Gather all the computed properties applied by those text
     // properties.
     let computedProps = [];
     for (let textProp of textProps) {
       computedProps = computedProps.concat(textProp.computed);
@@ -1156,17 +1160,17 @@ TextProperty.prototype = {
  */
 function CssRuleView(inspector, document, aStore, aPageStyle) {
   this.inspector = inspector;
   this.styleDocument = document;
   this.styleWindow = this.styleDocument.defaultView;
   this.store = aStore || {};
   this.pageStyle = aPageStyle;
 
-  this._outputParser = new OutputParser();
+  this._outputParser = new OutputParser(document);
 
   this._onKeypress = this._onKeypress.bind(this);
   this._onAddRule = this._onAddRule.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
   this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
@@ -3158,17 +3162,18 @@ TextPropertyEditor.prototype = {
     // Populate the computed styles.
     this._updateComputed();
 
     // Update the rule property highlight.
     this.ruleView._updatePropertyHighlight(this);
   },
 
   _onStartEditing: function() {
-    this._previewValue(this.prop.value);
+    this.element.classList.remove("ruleview-overridden");
+    this.enable.style.visibility = "hidden";
   },
 
   /**
    * Populate the list of computed styles.
    */
   _updateComputed: function() {
     // Clear out existing viewers.
     while (this.computed.hasChildNodes()) {
@@ -3463,19 +3468,16 @@ TextPropertyEditor.prototype = {
    */
   _previewValue: function(aValue) {
     // Since function call is throttled, we need to make sure we are still
     // editing, and any selector modifications have been completed
     if (!this.editing || this.ruleEditor.isEditing) {
       return;
     }
 
-    this.element.classList.remove("ruleview-overridden");
-    this.enable.style.visibility = "hidden";
-
     let val = parseSingleValue(aValue);
     this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
                                               val.priority);
   },
 
   /**
    * Validate this property. Does it make sense for this value to be assigned
    * to this property name? This does not apply the property value
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -107,27 +107,31 @@ skip-if = e10s # Bug 1039528: "inspect e
 [browser_ruleview_filtereditor-commit-on-ENTER.js]
 [browser_ruleview_filtereditor-revert-on-ESC.js]
 skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
 [browser_ruleview_inherit.js]
 [browser_ruleview_keybindings.js]
 [browser_ruleview_keyframes-rule_01.js]
 [browser_ruleview_keyframes-rule_02.js]
 [browser_ruleview_livepreview.js]
+[browser_ruleview_mark_overridden_01.js]
+[browser_ruleview_mark_overridden_02.js]
+[browser_ruleview_mark_overridden_03.js]
+[browser_ruleview_mark_overridden_04.js]
+[browser_ruleview_mark_overridden_05.js]
 [browser_ruleview_mathml-element.js]
 [browser_ruleview_media-queries.js]
 [browser_ruleview_multiple-properties-duplicates.js]
 [browser_ruleview_multiple-properties-priority.js]
 [browser_ruleview_multiple-properties-unfinished_01.js]
 [browser_ruleview_multiple-properties-unfinished_02.js]
 [browser_ruleview_multiple_properties_01.js]
 [browser_ruleview_multiple_properties_02.js]
 [browser_ruleview_original-source-link.js]
 [browser_ruleview_cycle-color.js]
-[browser_ruleview_override.js]
 [browser_ruleview_pseudo-element_01.js]
 [browser_ruleview_pseudo-element_02.js]
 skip-if = e10s # Bug 1090340
 [browser_ruleview_pseudo_lock_options.js]
 [browser_ruleview_refresh-on-attribute-change_01.js]
 [browser_ruleview_refresh-on-attribute-change_02.js]
 [browser_ruleview_refresh-on-style-change.js]
 [browser_ruleview_search-filter-computed-list_01.js]
--- a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.js
@@ -1,16 +1,15 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-// Tests that a disabled property is previewed when the property name or value
-// editor is focused and the property remains disabled when the escaping out of
+// Tests that a disabled property remains disabled when the escaping out of
 // the property editor.
 
 let TEST_URI = [
   "<style type='text/css'>",
   "#testid {",
   "  background-color: blue;",
   "}",
   "</style>",
@@ -34,42 +33,41 @@ function* testDisableProperty(inspector,
 
   let newValue = yield executeInContent("Test:GetRulePropertyValue", {
     styleSheetIndex: 0,
     ruleIndex: 0,
     name: "background-color"
   });
   is(newValue, "", "background-color should have been unset.");
 
-  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+  yield testEditDisableProperty(view, ruleEditor, propEditor,
     propEditor.nameSpan, "VK_ESCAPE");
-  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+  yield testEditDisableProperty(view, ruleEditor, propEditor,
     propEditor.valueSpan, "VK_ESCAPE");
-  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+  yield testEditDisableProperty(view, ruleEditor, propEditor,
     propEditor.valueSpan, "VK_TAB");
-  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+  yield testEditDisableProperty(view, ruleEditor, propEditor,
     propEditor.valueSpan, "VK_RETURN");
 }
 
-function* testPreviewDisableProperty(view, ruleEditor, propEditor,
+function* testEditDisableProperty(view, ruleEditor, propEditor,
     editableField, commitKey) {
   let editor = yield focusEditableField(view, editableField);
-  yield ruleEditor.rule._applyingModifications;
 
   ok(!propEditor.element.classList.contains("ruleview-overridden"),
     "property is not overridden.");
   is(propEditor.enable.style.visibility, "hidden",
     "property enable checkbox is hidden.");
 
   let newValue = yield executeInContent("Test:GetRulePropertyValue", {
     styleSheetIndex: 0,
     ruleIndex: 0,
     name: "background-color"
   });
-  is(newValue, "blue", "background-color should have been previewed.");
+  is(newValue, "", "background-color should remain unset.");
 
   let onBlur = once(editor.input, "blur");
   EventUtils.synthesizeKey(commitKey, {}, view.styleWindow);
   yield onBlur;
   yield ruleEditor.rule._applyingModifications;
 
   ok(!propEditor.prop.enabled, "property is disabled.");
   ok(propEditor.element.classList.contains("ruleview-overridden"),
@@ -79,10 +77,10 @@ function* testPreviewDisableProperty(vie
   ok(!propEditor.enable.getAttribute("checked"),
     "property enable checkbox is not checked.");
 
   newValue = yield executeInContent("Test:GetRulePropertyValue", {
     styleSheetIndex: 0,
     ruleIndex: 0,
     name: "background-color"
   });
-  is(newValue, "", "background-color should have been unset.");
+  is(newValue, "", "background-color should remain unset.");
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_01.js
@@ -0,0 +1,64 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly based on the
+// specificity of the rule
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  ".testclass {",
+  "  background-color: green;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let elementStyle = view._elementStyle;
+
+  let idRule = elementStyle.rules[1];
+  let idProp = idRule.textProps[0];
+  is(idProp.name, "background-color",
+    "First ID property should be background-color");
+  is(idProp.value, "blue", "First ID property value should be blue");
+  ok(!idProp.overridden, "ID prop should not be overridden.");
+  ok(!idProp.editor.element.classList.contains("ruleview-overridden"),
+    "ID property editor should not have ruleview-overridden class");
+
+  let classRule = elementStyle.rules[2];
+  let classProp = classRule.textProps[0];
+  is(classProp.name, "background-color",
+    "First class prop should be background-color");
+  is(classProp.value, "green", "First class property value should be green");
+  ok(classProp.overridden, "Class property should be overridden.");
+  ok(classProp.editor.element.classList.contains("ruleview-overridden"),
+    "Class property editor should have ruleview-overridden class");
+
+  // Override background-color by changing the element style.
+  let elementRule = elementStyle.rules[0];
+  elementRule.createProperty("background-color", "purple", "");
+  yield elementRule._applyingModifications;
+
+  let elementProp = elementRule.textProps[0];
+  ok(!elementProp.overridden,
+    "Element style property should not be overridden");
+  ok(idProp.overridden, "ID property should be overridden");
+  ok(idProp.editor.element.classList.contains("ruleview-overridden"),
+    "ID property editor should have ruleview-overridden class");
+  ok(classProp.overridden, "Class property should be overridden");
+  ok(classProp.editor.element.classList.contains("ruleview-overridden"),
+    "Class property editor should have ruleview-overridden class");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_02.js
@@ -0,0 +1,45 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly for short hand
+// properties and the computed list properties
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  margin-left: 1px;",
+  "}",
+  ".testclass {",
+  "  margin: 2px;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let elementStyle = view._elementStyle;
+
+  let classRule = elementStyle.rules[2];
+  let classProp = classRule.textProps[0];
+  ok(!classProp.overridden,
+    "Class prop shouldn't be overridden, some props are still being used.");
+
+  for (let computed of classProp.computed) {
+    if (computed.name.indexOf("margin-left") == 0) {
+      ok(computed.overridden, "margin-left props should be overridden.");
+    } else {
+      ok(!computed.overridden,
+        "Non-margin-left props should not be overridden.");
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_03.js
@@ -0,0 +1,48 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly based on the
+// priority for the rule
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  ".testclass {",
+  "  background-color: green !important;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let elementStyle = view._elementStyle;
+
+  let idRule = elementStyle.rules[1];
+  let idProp = idRule.textProps[0];
+  ok(idProp.overridden, "Not-important rule should be overridden.");
+
+  let classRule = elementStyle.rules[2];
+  let classProp = classRule.textProps[0];
+  ok(!classProp.overridden, "Important rule should not be overridden.");
+
+  let elementRule = elementStyle.rules[0];
+  let elementProp = elementRule.createProperty("background-color", "purple",
+    "important");
+  yield elementRule._applyingModifications;
+
+  ok(!elementProp.overridden, "New important prop should not be overriden.");
+  ok(idProp.overridden, "ID property should be overridden.");
+  ok(classProp.overridden, "Class property should be overridden.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_04.js
@@ -0,0 +1,42 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly if a property gets
+// disabled
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  ".testclass {",
+  "  background-color: green;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let elementStyle = view._elementStyle;
+
+  let idRule = elementStyle.rules[1];
+  let idProp = idRule.textProps[0];
+
+  idProp.setEnabled(false);
+  yield idRule._applyingModifications;
+
+  let classRule = elementStyle.rules[2];
+  let classProp = classRule.textProps[0];
+  ok(!classProp.overridden,
+    "Class prop should not be overridden after id prop was disabled.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_mark_overridden_05.js
@@ -0,0 +1,36 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view marks overridden rules correctly based on the
+// order of the property
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: green;",
+  "}",
+  "</style>",
+  "<div id='testid' class='testclass'>Styled Node</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testMarkOverridden(inspector, view);
+});
+
+function* testMarkOverridden(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+
+  yield createNewRuleViewProperty(ruleEditor, "background-color: red;");
+
+  let firstProp = ruleEditor.rule.textProps[0];
+  let secondProp = ruleEditor.rule.textProps[1];
+
+  ok(firstProp.overridden, "First property should be overridden.");
+  ok(!secondProp.overridden, "Second property should not be overridden.");
+}
--- a/browser/devtools/styleinspector/test/browser_ruleview_multiple-properties-unfinished_01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_multiple-properties-unfinished_01.js
@@ -31,19 +31,19 @@ function waitRuleViewChanged(view, n) {
       deferred.resolve();
     }
   }
   view.on("ruleview-changed", listener);
   return deferred.promise;
 }
 function* testCreateNewMultiUnfinished(inspector, ruleEditor, view) {
   let onMutation = inspector.once("markupmutation");
-  // There is 6 rule-view updates, one for the rule view creation,
-  // one for each new property and one last for throttle update.
-  let onRuleViewChanged = waitRuleViewChanged(view, 6);
+  // There is 5 rule-view updates, one for the rule view creation,
+  // one for each new property
+  let onRuleViewChanged = waitRuleViewChanged(view, 5);
   yield createNewRuleViewProperty(ruleEditor,
     "color:blue;background : orange   ; text-align:center; border-color: ");
   yield onMutation;
   yield onRuleViewChanged;
 
   is(ruleEditor.rule.textProps.length, 4, "Should have created new text properties.");
   is(ruleEditor.propertyList.children.length, 4, "Should have created property editors.");
 
deleted file mode 100644
--- a/browser/devtools/styleinspector/test/browser_ruleview_override.js
+++ /dev/null
@@ -1,151 +0,0 @@
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// Test the display of overridden declarations in the rule-view
-
-add_task(function*() {
-  yield addTab("data:text/html;charset=utf-8,browser_ruleview_override.js");
-  let {toolbox, inspector, view} = yield openRuleView();
-
-  yield simpleOverride(inspector, view);
-  yield partialOverride(inspector, view);
-  yield importantOverride(inspector, view);
-  yield disableOverride(inspector, view);
-});
-
-function* createTestContent(inspector, style) {
-  let onMutated = inspector.once("markupmutation");
-  let styleNode = addStyle(content.document, style);
-  content.document.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
-  yield onMutated;
-  yield selectNode("#testid", inspector);
-  return styleNode;
-}
-
-function* removeTestContent(inspector, node) {
-  let onMutated = inspector.once("markupmutation");
-  node.remove();
-  yield onMutated;
-}
-
-function* simpleOverride(inspector, view) {
-  let styleNode = yield createTestContent(inspector, '' +
-    '#testid {' +
-    '  background-color: blue;' +
-    '} ' +
-    '.testclass {' +
-    '  background-color: green;' +
-    '}');
-
-  let elementStyle = view._elementStyle;
-
-  let idRule = elementStyle.rules[1];
-  let idProp = idRule.textProps[0];
-  is(idProp.name, "background-color", "First ID prop should be background-color");
-  ok(!idProp.overridden, "ID prop should not be overridden.");
-
-  let classRule = elementStyle.rules[2];
-  let classProp = classRule.textProps[0];
-  is(classProp.name, "background-color", "First class prop should be background-color");
-  ok(classProp.overridden, "Class property should be overridden.");
-
-  // Override background-color by changing the element style.
-  let elementRule = elementStyle.rules[0];
-  elementRule.createProperty("background-color", "purple", "");
-  yield elementRule._applyingModifications;
-
-  let elementProp = elementRule.textProps[0];
-  is(classProp.name, "background-color", "First element prop should now be background-color");
-  ok(!elementProp.overridden, "Element style property should not be overridden");
-  ok(idProp.overridden, "ID property should be overridden");
-  ok(classProp.overridden, "Class property should be overridden");
-
-  yield removeTestContent(inspector, styleNode);
-}
-
-function* partialOverride(inspector, view) {
-  let styleNode = yield createTestContent(inspector, '' +
-    // Margin shorthand property...
-    '.testclass {' +
-    '  margin: 2px;' +
-    '}' +
-    // ... will be partially overridden.
-    '#testid {' +
-    '  margin-left: 1px;' +
-    '}');
-
-  let elementStyle = view._elementStyle;
-
-  let classRule = elementStyle.rules[2];
-  let classProp = classRule.textProps[0];
-  ok(!classProp.overridden,
-    "Class prop shouldn't be overridden, some props are still being used.");
-
-  for (let computed of classProp.computed) {
-    if (computed.name.indexOf("margin-left") == 0) {
-      ok(computed.overridden, "margin-left props should be overridden.");
-    } else {
-      ok(!computed.overridden, "Non-margin-left props should not be overridden.");
-    }
-  }
-
-  yield removeTestContent(inspector, styleNode);
-}
-
-function* importantOverride(inspector, view) {
-  let styleNode = yield createTestContent(inspector, '' +
-    // Margin shorthand property...
-    '.testclass {' +
-    '  background-color: green !important;' +
-    '}' +
-    // ... will be partially overridden.
-    '#testid {' +
-    '  background-color: blue;' +
-    '}');
-
-  let elementStyle = view._elementStyle;
-
-  let idRule = elementStyle.rules[1];
-  let idProp = idRule.textProps[0];
-  ok(idProp.overridden, "Not-important rule should be overridden.");
-
-  let classRule = elementStyle.rules[2];
-  let classProp = classRule.textProps[0];
-  ok(!classProp.overridden, "Important rule should not be overridden.");
-
-  yield removeTestContent(inspector, styleNode);
-
-  let elementRule = elementStyle.rules[0];
-  let elementProp = elementRule.createProperty("background-color", "purple", "important");
-  yield elementRule._applyingModifications;
-
-  ok(classProp.overridden, "New important prop should override class property.");
-  ok(!elementProp.overridden, "New important prop should not be overriden.");
-}
-
-function* disableOverride(inspector, view) {
-  let styleNode = yield createTestContent(inspector, '' +
-    '#testid {' +
-    '  background-color: blue;' +
-    '}' +
-    '.testclass {' +
-    '  background-color: green;' +
-    '}');
-
-  let elementStyle = view._elementStyle;
-
-  let idRule = elementStyle.rules[1];
-  let idProp = idRule.textProps[0];
-
-  idProp.setEnabled(false);
-  yield idRule._applyingModifications;
-
-  let classRule = elementStyle.rules[2];
-  let classProp = classRule.textProps[0];
-  ok(!classProp.overridden, "Class prop should not be overridden after id prop was disabled.");
-
-  yield removeTestContent(inspector, styleNode);
-}
--- a/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js
@@ -305,17 +305,17 @@ function test() {
       name: "background",
       value: "rgb(255, var(--g-value, 0), 192)",
       test: fragment => {
 	is(fragment.textContent, "rgb(255, var(--g-value, 0), 192)");
       }
     }
   ];
 
-  let parser = new OutputParser();
+  let parser = new OutputParser(document);
   for (let i = 0; i < testData.length; i ++) {
     let data = testData[i];
     info("Output-parser test data " + i + ". {" + data.name + " : " + data.value + ";}");
     data.test(parser.parseCssProperty(data.name, data.value, {
       colorClass: COLOR_CLASS,
       urlClass: URL_CLASS,
       bezierClass: CUBIC_BEZIER_CLASS,
       defaultColorType: false
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -409,39 +409,25 @@ function* waitForComputedStyleProperty(s
 }
 
 /**
  * Given an inplace editable element, click to switch it to edit mode, wait for
  * focus
  * @return a promise that resolves to the inplace-editor element when ready
  */
 let focusEditableField = Task.async(function*(ruleView, editable, xOffset=1, yOffset=1, options={}) {
-  // Focusing the name or value input is going to fire a preview and update the rule view
-  let expectRuleViewUpdate =
-    editable.classList.contains("ruleview-propertyname") ||
-    editable.classList.contains("ruleview-propertyvalue");
-  let onRuleViewChanged;
-  if (expectRuleViewUpdate) {
-    onRuleViewChanged = ruleView.once("ruleview-changed");
-  }
-
   let onFocus = once(editable.parentNode, "focus", true);
   info("Clicking on editable field to turn to edit mode");
   EventUtils.synthesizeMouse(editable, xOffset, yOffset, options,
     editable.ownerDocument.defaultView);
-  let event = yield onFocus;
+  yield onFocus;
 
   info("Editable field gained focus, returning the input field now");
   let onEdit = inplaceEditor(editable.ownerDocument.activeElement);
 
-  if (expectRuleViewUpdate) {
-    info("Waiting for rule view update");
-    yield onRuleViewChanged;
-  }
-
   return onEdit;
 });
 
 /**
  * Given a tooltip object instance (see Tooltip.js), checks if it is set to
  * toggle and hover and if so, checks if the given target is a valid hover target.
  * This won't actually show the tooltip (the less we interact with XUL panels
  * during test runs, the better).
@@ -796,21 +782,21 @@ function getRuleViewRuleEditor(view, chi
  * @param {RuleEditor} ruleEditor An instance of RuleEditor that will receive
  * the new property
  * @return a promise that resolves to the newly created editor when ready and
  * focused
  */
 let focusNewRuleViewProperty = Task.async(function*(ruleEditor) {
   info("Clicking on a close ruleEditor brace to start editing a new property");
   ruleEditor.closeBrace.scrollIntoView();
-  let editor = yield focusEditableField(ruleEditor.ruleView, ruleEditor.closeBrace);
+  let editor = yield focusEditableField(ruleEditor.ruleView,
+    ruleEditor.closeBrace);
 
-  is(inplaceEditor(ruleEditor.newPropSpan), editor, "Focused editor is the new property editor.");
-  is(ruleEditor.rule.textProps.length,  0, "Starting with one new text property.");
-  is(ruleEditor.propertyList.children.length, 1, "Starting with two property editors.");
+  is(inplaceEditor(ruleEditor.newPropSpan), editor,
+    "Focused editor is the new property editor.");
 
   return editor;
 });
 
 /**
  * Create a new property name in the rule-view, focusing a new property editor
  * by clicking on the close brace, and then entering the given text.
  * Keep in mind that the rule-view knows how to handle strings with multiple
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_595934_message_categories.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_595934_message_categories.js
@@ -37,17 +37,16 @@ const TESTS = [
       form.submit();
     },
   },
   {
     // #3
     file: "test-bug-595934-workers.html",
     category: "Web Worker",
     matchString: "fooBarWorker",
-    expectError: true,
   },
   {
     // #4
     file: "test-bug-595934-malformedxml.xhtml",
     category: "malformed-xml",
     matchString: "no element found",
   },
   {
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -633,20 +633,24 @@
 @RESPATH@/modules/*
 
 ; Safe Browsing
 #ifdef MOZ_URL_CLASSIFIER
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
 @RESPATH@/components/nsUrlClassifierListManager.js
 @RESPATH@/components/nsUrlClassifierLib.js
-@RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
 @RESPATH@/components/url-classifier.xpt
 #endif
 
+; Private Browsing
+@RESPATH@/components/privatebrowsing.xpt
+@RESPATH@/components/PrivateBrowsing.manifest
+@RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
+
 ; ANGLE GLES-on-D3D rendering library
 #ifdef MOZ_ANGLE_RENDERER
 @BINPATH@/libEGL.dll
 @BINPATH@/libGLESv2.dll
 
 #ifdef MOZ_D3DCOMPILER_VISTA_DLL
 @BINPATH@/@MOZ_D3DCOMPILER_VISTA_DLL@
 #endif
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -675,16 +675,17 @@ you can use these alternative items. Oth
 <!ENTITY spellAddDictionaries.accesskey "A">
 
 <!ENTITY editBookmark.done.label                     "Done">
 <!ENTITY editBookmark.cancel.label                   "Cancel">
 <!ENTITY editBookmark.removeBookmark.accessKey       "R">
 
 <!ENTITY identity.connectionSecure "Secure Connection">
 <!ENTITY identity.connectionNotSecure "Connection is Not Secure">
+<!ENTITY identity.connectionFile "This page is stored on your computer.">
 <!ENTITY identity.connectionVerified "&brandShortName; verified that you are securely connected to this site, run by:">
 <!ENTITY identity.connectionInternal "This is a secure &brandShortName; page.">
 
 <!ENTITY identity.moreInfoLinkText2 "More Information">
 
 <!ENTITY identity.permissions "Permissions">
 
 <!-- Name for the tabs toolbar as spoken by screen readers.
--- a/browser/modules/ContentCrashReporters.jsm
+++ b/browser/modules/ContentCrashReporters.jsm
@@ -83,25 +83,35 @@ this.TabCrashReporter = {
           this.browserMap.delete(browser);
           browser.contentDocument.documentElement.classList.remove("crashDumpAvailable");
           browser.contentDocument.documentElement.classList.add("crashDumpSubmitted");
         }
       }
     }
   },
 
-  onAboutTabCrashedLoad: function (aBrowser) {
+  onAboutTabCrashedLoad: function (aBrowser, aParams) {
+    // If there was only one tab open that crashed, do not show the "restore all tabs" button
+    if (aParams.crashedTabCount == 1) {
+      this.hideRestoreAllButton(aBrowser);
+    }
+
     if (!this.childMap)
       return;
 
     let dumpID = this.childMap.get(this.browserMap.get(aBrowser));
     if (!dumpID)
       return;
 
     aBrowser.contentDocument.documentElement.classList.add("crashDumpAvailable");
+  },
+
+  hideRestoreAllButton: function (aBrowser) {
+    aBrowser.contentDocument.getElementById("restoreAll").setAttribute("hidden", true);
+    aBrowser.contentDocument.getElementById("restoreTab").setAttribute("class", "primary");
   }
 }
 
 this.PluginCrashReporter = {
   /**
    * Makes the PluginCrashReporter ready to hear about and
    * submit crash reports.
    */
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -141,16 +141,17 @@ browser.jar:
   skin/classic/browser/loop/menuPanel.png             (loop/menuPanel.png)
   skin/classic/browser/loop/menuPanel@2x.png          (loop/menuPanel@2x.png)
   skin/classic/browser/loop/toolbar.png               (loop/toolbar.png)
   skin/classic/browser/loop/toolbar@2x.png            (loop/toolbar@2x.png)
   skin/classic/browser/loop/toolbar-inverted.png      (loop/toolbar-inverted.png)
   skin/classic/browser/loop/toolbar-inverted@2x.png   (loop/toolbar-inverted@2x.png)
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/controlcenter/arrow-subview.svg  (../shared/controlcenter/arrow-subview.svg)
+  skin/classic/browser/controlcenter/arrow-subview-back.svg  (../shared/controlcenter/arrow-subview-back.svg)
   skin/classic/browser/controlcenter/conn-not-secure.svg  (../shared/controlcenter/conn-not-secure.svg)
   skin/classic/browser/controlcenter/conn-secure.svg  (../shared/controlcenter/conn-secure.svg)
   skin/classic/browser/controlcenter/conn-degraded.svg  (../shared/controlcenter/conn-degraded.svg)
   skin/classic/browser/controlcenter/mcb-disabled.svg  (../shared/controlcenter/mcb-disabled.svg)
   skin/classic/browser/controlcenter/permissions.svg  (../shared/controlcenter/permissions.svg)
   skin/classic/browser/controlcenter/tracking-protection.svg                 (../shared/controlcenter/tracking-protection.svg)
   skin/classic/browser/controlcenter/tracking-protection-disabled.svg        (../shared/controlcenter/tracking-protection-disabled.svg)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -185,16 +185,17 @@ browser.jar:
   skin/classic/browser/loop/toolbar-inverted.png      (loop/toolbar-inverted.png)
   skin/classic/browser/loop/toolbar-inverted@2x.png   (loop/toolbar-inverted@2x.png)
   skin/classic/browser/yosemite/loop/menuPanel.png          (loop/menuPanel-yosemite.png)
   skin/classic/browser/yosemite/loop/menuPanel@2x.png       (loop/menuPanel-yosemite@2x.png)
   skin/classic/browser/yosemite/loop/toolbar.png            (loop/toolbar-yosemite.png)
   skin/classic/browser/yosemite/loop/toolbar@2x.png         (loop/toolbar-yosemite@2x.png)
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/controlcenter/arrow-subview.svg  (../shared/controlcenter/arrow-subview.svg)
+  skin/classic/browser/controlcenter/arrow-subview-back.svg  (../shared/controlcenter/arrow-subview-back.svg)
   skin/classic/browser/controlcenter/conn-not-secure.svg  (../shared/controlcenter/conn-not-secure.svg)
   skin/classic/browser/controlcenter/conn-secure.svg  (../shared/controlcenter/conn-secure.svg)
   skin/classic/browser/controlcenter/conn-degraded.svg  (../shared/controlcenter/conn-degraded.svg)
   skin/classic/browser/controlcenter/mcb-disabled.svg  (../shared/controlcenter/mcb-disabled.svg)
   skin/classic/browser/controlcenter/permissions.svg  (../shared/controlcenter/permissions.svg)
   skin/classic/browser/controlcenter/tracking-protection.svg                 (../shared/controlcenter/tracking-protection.svg)
   skin/classic/browser/controlcenter/tracking-protection-disabled.svg        (../shared/controlcenter/tracking-protection-disabled.svg)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/controlcenter/arrow-subview-back.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <polygon fill="#fff" points="12,3.5 10.5,2 4.625,8 10.5,14 12,12.5 7.625,8" />
+</svg>
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -7,21 +7,26 @@
    connection security. Show the organization address for EV certs. */
 #identity-popup-securityView:not(.unknownIdentity):not(.verifiedIdentity):not(.mixedContent):not(.weakCipher) > #identity-popup-content-supplemental,
 /* Show the "Connection is secure" labels only for EV and DV certs. */
 #identity-popup-security-content:not(.verifiedIdentity):not(.verifiedDomain) > .identity-popup-connection-secure,
 #identity-popup-securityView:not(.verifiedIdentity):not(.verifiedDomain) > #identity-popup-securityView-header > .identity-popup-connection-secure,
 /* Show the "Connection is not secure" labels only for non-secure sites. */
 #identity-popup-security-content:not(.unknownIdentity) > .identity-popup-connection-not-secure,
 #identity-popup-securityView:not(.unknownIdentity) > #identity-popup-securityView-header > .identity-popup-connection-not-secure,
+/* Show "This page is stored on your computer" only for file URLs. */
+#identity-popup-security-content:not(.fileURI) > .identity-popup-connection-file-uri,
+#identity-popup-securityView:not(.fileURI) > #identity-popup-securityView-header > .identity-popup-connection-file-uri,
 /* Show "This is a secure internal page" only for whitelisted pages. */
 #identity-popup-securityView:not(.chromeUI) > #identity-popup-securityView-header > .identity-popup-connection-internal,
 #identity-popup-security-content:not(.chromeUI) > .identity-popup-connection-internal,
 /* Hide the subsection arrow for whitelisted chromeUI pages. */
 #identity-popup-security-content.chromeUI + .identity-popup-expander,
+/* Hide the subsection arrow for whitelisted file URI pages. */
+#identity-popup-security-content.fileURI + .identity-popup-expander,
 /* Hide the tracking protection section for whitelisted chromeUI pages. */
 #identity-popup-mainView.chromeUI > #tracking-protection-container {
   display: none;
 }
 
 /* PANEL */
 
 #identity-popup,
@@ -106,26 +111,18 @@
 
 .identity-popup-expander:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
 
 .identity-popup-expander[panel-multiview-anchor] {
   transition: background-color 250ms ease-in;
   background-color: Highlight;
-  background-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted.png"),
+  background-image: url("chrome://browser/skin/controlcenter/arrow-subview-back.svg"),
                     linear-gradient(rgba(255,255,255,0.3), transparent);
-  color: HighlightText;
-}
-
-@media (min-resolution: 1.1dppx) {
-  .identity-popup-expander[panel-multiview-anchor] {
-    background-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted@2x.png"),
-                      linear-gradient(rgba(255,255,255,0.3), transparent);
-  }
 }
 
 .identity-popup-expander > .button-box {
   padding: 0;
   -moz-appearance: none;
   border: solid ThreeDShadow;
   border-width: 0 0 0 1px;
 }
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -197,16 +197,17 @@ browser.jar:
         skin/classic/browser/loop/toolbar-lunaSilver.png             (loop/toolbar-lunaSilver.png)
         skin/classic/browser/loop/toolbar-lunaSilver@2x.png          (loop/toolbar-lunaSilver@2x.png)
         skin/classic/browser/loop/toolbar-win8.png                   (loop/toolbar-win8.png)
         skin/classic/browser/loop/toolbar-win8@2x.png                (loop/toolbar-win8@2x.png)
         skin/classic/browser/loop/toolbar-XP.png                     (loop/toolbar-XP.png)
         skin/classic/browser/loop/toolbar-XP@2x.png                  (loop/toolbar-XP@2x.png)
 *       skin/classic/browser/controlcenter/panel.css                 (controlcenter/panel.css)
         skin/classic/browser/controlcenter/arrow-subview.svg  (../shared/controlcenter/arrow-subview.svg)
+        skin/classic/browser/controlcenter/arrow-subview-back.svg  (../shared/controlcenter/arrow-subview-back.svg)
         skin/classic/browser/controlcenter/conn-not-secure.svg  (../shared/controlcenter/conn-not-secure.svg)
         skin/classic/browser/controlcenter/conn-degraded.svg  (../shared/controlcenter/conn-degraded.svg)
         skin/classic/browser/controlcenter/conn-secure.svg  (../shared/controlcenter/conn-secure.svg)
         skin/classic/browser/controlcenter/mcb-disabled.svg  (../shared/controlcenter/mcb-disabled.svg)
         skin/classic/browser/controlcenter/permissions.svg  (../shared/controlcenter/permissions.svg)
         skin/classic/browser/controlcenter/tracking-protection.svg                 (../shared/controlcenter/tracking-protection.svg)
         skin/classic/browser/controlcenter/tracking-protection-disabled.svg        (../shared/controlcenter/tracking-protection-disabled.svg)
         skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -168,134 +168,134 @@ if test -z "$BUILDING_JS" -o -n "$JS_STA
         # we don't need to pass data to and from legacy char* APIs
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_CHARSET_IS_UTF8"
         # make sure to not accidentally pick up system-icu headers
         ICU_CPPFLAGS="$ICU_CPPFLAGS -I$icudir/common -I$icudir/i18n"
 
         ICU_CROSS_BUILD_OPT=""
 
         if test "$CROSS_COMPILE"; then
-    	# Remove _DEPEND_CFLAGS from HOST_FLAGS to avoid configure error
-    	HOST_ICU_CFLAGS="$HOST_CFLAGS"
-    	HOST_ICU_CXXFLAGS="$HOST_CXXFLAGS"
+            # Remove _DEPEND_CFLAGS from HOST_FLAGS to avoid configure error
+            HOST_ICU_CFLAGS="$HOST_CFLAGS"
+            HOST_ICU_CXXFLAGS="$HOST_CXXFLAGS"
 
-    	HOST_ICU_CFLAGS=`echo $HOST_ICU_CFLAGS | sed "s|$_DEPEND_CFLAGS||g"`
-    	HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CFXXLAGS | sed "s|$_DEPEND_CFLAGS||g"`
+            HOST_ICU_CFLAGS=`echo $HOST_ICU_CFLAGS | sed "s|$_DEPEND_CFLAGS||g"`
+            HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CFXXLAGS | sed "s|$_DEPEND_CFLAGS||g"`
 
-    	# ICU requires RTTI
-    	if test "$GNU_CC"; then
-    	    HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CXXFLAGS | sed 's|-fno-rtti|-frtti|g'`
-    	elif test "$_MSC_VER"; then
-    	    HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CXXFLAGS | sed 's|-GR-|-GR|g'`
-    	fi
+            # ICU requires RTTI
+            if test "$GNU_CC"; then
+                HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CXXFLAGS | sed 's|-fno-rtti|-frtti|g'`
+            elif test "$_MSC_VER"; then
+                HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CXXFLAGS | sed 's|-GR-|-GR|g'`
+            fi
 
-    	HOST_ICU_BUILD_OPTS=""
-    	if test -n "$MOZ_DEBUG"; then
-    	    HOST_ICU_BUILD_OPTS="$HOST_ICU_BUILD_OPTS --enable-debug"
-    	fi
+            HOST_ICU_BUILD_OPTS=""
+            if test -n "$MOZ_DEBUG"; then
+                HOST_ICU_BUILD_OPTS="$HOST_ICU_BUILD_OPTS --enable-debug"
+            fi
 
-    	abs_srcdir=`(cd $srcdir; pwd)`
-    	mkdir -p $_objdir/intl/icu/host
-        (export AR="$HOST_AR"
-         export RANLIB="$HOST_RANLIB"
-         export CC="$HOST_CC"
-         export CXX="$HOST_CXX"
-         export CPP="$HOST_CPP"
-         export LD="$HOST_LD"
-         export CFLAGS="$HOST_ICU_CFLAGS $HOST_OPTIMIZE_FLAGS"
-         export CPPFLAGS="$ICU_CPPFLAGS"
-         export CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMIZE_FLAGS"
-         export LDFLAGS="$HOST_LDFLAGS"
-         ac_configure_args="$HOST_ICU_BUILD_OPTS"
-         ac_configure_args="$ac_configure_args --enable-static --disable-shared --enable-extras=no --enable-icuio=no --enable-layout=no --enable-tests=no --enable-samples=no"
-         AC_OUTPUT_SUBDIRS_NOW(intl/icu/source:intl/icu/host)
-        ) || exit 1
-    	# generate config/icucross.mk
-    	$GMAKE -C $_objdir/intl/icu/host/ config/icucross.mk
+            abs_srcdir=`(cd $srcdir; pwd)`
+            mkdir -p $_objdir/intl/icu/host
+            (export AR="$HOST_AR"
+             export RANLIB="$HOST_RANLIB"
+             export CC="$HOST_CC"
+             export CXX="$HOST_CXX"
+             export CPP="$HOST_CPP"
+             export LD="$HOST_LD"
+             export CFLAGS="$HOST_ICU_CFLAGS $HOST_OPTIMIZE_FLAGS"
+             export CPPFLAGS="$ICU_CPPFLAGS"
+             export CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMIZE_FLAGS"
+             export LDFLAGS="$HOST_LDFLAGS"
+             ac_configure_args="$HOST_ICU_BUILD_OPTS"
+             ac_configure_args="$ac_configure_args --enable-static --disable-shared --enable-extras=no --enable-icuio=no --enable-layout=no --enable-tests=no --enable-samples=no"
+             AC_OUTPUT_SUBDIRS_NOW(intl/icu/source:intl/icu/host)
+            ) || exit 1
+            # generate config/icucross.mk
+            $GMAKE -C $_objdir/intl/icu/host/ config/icucross.mk
 
-    	# --with-cross-build requires absolute path
-    	ICU_HOST_PATH=`cd $_objdir/intl/icu/host && pwd`
-    	ICU_CROSS_BUILD_OPT="--with-cross-build=$ICU_HOST_PATH --disable-tools"
-    	ICU_TARGET_OPT="--build=$build --host=$target"
+            # --with-cross-build requires absolute path
+            ICU_HOST_PATH=`cd $_objdir/intl/icu/host && pwd`
+            ICU_CROSS_BUILD_OPT="--with-cross-build=$ICU_HOST_PATH --disable-tools"
+            ICU_TARGET_OPT="--build=$build --host=$target"
         else
-    	# CROSS_COMPILE isn't set build and target are i386 and x86-64.
-    	# So we must set target for --build and --host.
-    	ICU_TARGET_OPT="--build=$target --host=$target"
+            # CROSS_COMPILE isn't set build and target are i386 and x86-64.
+            # So we must set target for --build and --host.
+            ICU_TARGET_OPT="--build=$target --host=$target"
         fi
 
         if test -z "$MOZ_SHARED_ICU"; then
-    	# To reduce library size, use static linking
-    	ICU_LINK_OPTS="--enable-static --disable-shared"
+            # To reduce library size, use static linking
+            ICU_LINK_OPTS="--enable-static --disable-shared"
         else
-    	ICU_LINK_OPTS="--disable-static --enable-shared"
+            ICU_LINK_OPTS="--disable-static --enable-shared"
         fi
         # Force the ICU static libraries to be position independent code
         ICU_CFLAGS="$DSO_PIC_CFLAGS $CFLAGS"
         ICU_CXXFLAGS="$DSO_PIC_CFLAGS $CXXFLAGS"
 
         ICU_BUILD_OPTS=""
         if test -n "$MOZ_DEBUG" -o "MOZ_DEBUG_SYMBOLS"; then
-    	ICU_CFLAGS="$ICU_CFLAGS $MOZ_DEBUG_FLAGS"
-    	ICU_CXXFLAGS="$ICU_CXXFLAGS $MOZ_DEBUG_FLAGS"
-    	if test -n "$CROSS_COMPILE" -a "$OS_TARGET" = "Darwin" \
-    		-a "$HOST_OS_ARCH" != "Darwin"
-    	then
-    	    # Bug 951758: Cross-OSX builds with non-Darwin hosts have issues
-    	    # with -g and friends (like -gdwarf and -gfull) because they try
-    	    # to run dsymutil
-    	    changequote(,)
-    	    ICU_CFLAGS=`echo $ICU_CFLAGS | sed 's|-g[^ \t]*||g'`
-    	    ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-g[^ \t]*||g'`
-    	    changequote([,])
-    	fi
+            ICU_CFLAGS="$ICU_CFLAGS $MOZ_DEBUG_FLAGS"
+            ICU_CXXFLAGS="$ICU_CXXFLAGS $MOZ_DEBUG_FLAGS"
+            if test -n "$CROSS_COMPILE" -a "$OS_TARGET" = "Darwin" \
+                    -a "$HOST_OS_ARCH" != "Darwin"
+            then
+                # Bug 951758: Cross-OSX builds with non-Darwin hosts have issues
+                # with -g and friends (like -gdwarf and -gfull) because they try
+                # to run dsymutil
+                changequote(,)
+                ICU_CFLAGS=`echo $ICU_CFLAGS | sed 's|-g[^ \t]*||g'`
+                ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-g[^ \t]*||g'`
+                changequote([,])
+            fi
 
-    	ICU_LDFLAGS="$MOZ_DEBUG_LDFLAGS"
-    	if test -z "$MOZ_DEBUG"; then
-    	    # To generate debug symbols, it requires MOZ_DEBUG_FLAGS.
-    	    # But, not debug build.
-    	    ICU_CFLAGS="$ICU_CFLAGS -UDEBUG -DNDEBUG"
-    	    ICU_CXXFLAGS="$ICU_CXXFLAGS -UDEBUG -DNDEBUG"
-    	elif test -z "$MOZ_NO_DEBUG_RTL"; then
-    	    ICU_BUILD_OPTS="$ICU_BUILD_OPTS --enable-debug"
-    	fi
+            ICU_LDFLAGS="$MOZ_DEBUG_LDFLAGS"
+            if test -z "$MOZ_DEBUG"; then
+                # To generate debug symbols, it requires MOZ_DEBUG_FLAGS.
+                # But, not debug build.
+                ICU_CFLAGS="$ICU_CFLAGS -UDEBUG -DNDEBUG"
+                ICU_CXXFLAGS="$ICU_CXXFLAGS -UDEBUG -DNDEBUG"
+            elif test -z "$MOZ_NO_DEBUG_RTL"; then
+                ICU_BUILD_OPTS="$ICU_BUILD_OPTS --enable-debug"
+            fi
         fi
         if test -z "$MOZ_OPTIMIZE"; then
-    	ICU_BUILD_OPTS="$ICU_BUILD_OPTS --disable-release"
+            ICU_BUILD_OPTS="$ICU_BUILD_OPTS --disable-release"
         else
-    	ICU_CFLAGS="$ICU_CFLAGS $MOZ_OPTIMIZE_FLAGS"
-    	ICU_CXXFLAGS="$ICU_CXXFLAGS $MOZ_OPTIMIZE_FLAGS"
+            ICU_CFLAGS="$ICU_CFLAGS $MOZ_OPTIMIZE_FLAGS"
+            ICU_CXXFLAGS="$ICU_CXXFLAGS $MOZ_OPTIMIZE_FLAGS"
         fi
 
         if test "$am_cv_langinfo_codeset" = "no"; then
-    	# ex. Android
-    	ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_HAVE_NL_LANGINFO_CODESET=0"
+            # ex. Android
+            ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_HAVE_NL_LANGINFO_CODESET=0"
         fi
 
         # ICU requires RTTI
         if test "$GNU_CC"; then
-    	ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-fno-rtti|-frtti|g'`
+            ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-fno-rtti|-frtti|g'`
         else
-    	if test "$_MSC_VER"; then
-    	    ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-GR-|-GR|g'`
-    	fi
+            if test "$_MSC_VER"; then
+                ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-GR-|-GR|g'`
+            fi
 
-    	# Add RTL flags for MSVCRT.DLL
-    	if test -n "$MOZ_DEBUG" -a -z "$MOZ_NO_DEBUG_RTL"; then
-    	    ICU_CFLAGS="$ICU_CFLAGS -MDd"
-    	    ICU_CXXFLAGS="$ICU_CXXFLAGS -MDd"
-    	else
-    	    ICU_CFLAGS="$ICU_CFLAGS -MD"
-    	    ICU_CXXFLAGS="$ICU_CXXFLAGS -MD"
-    	fi
+            # Add RTL flags for MSVCRT.DLL
+            if test -n "$MOZ_DEBUG" -a -z "$MOZ_NO_DEBUG_RTL"; then
+                ICU_CFLAGS="$ICU_CFLAGS -MDd"
+                ICU_CXXFLAGS="$ICU_CXXFLAGS -MDd"
+            else
+                ICU_CFLAGS="$ICU_CFLAGS -MD"
+                ICU_CXXFLAGS="$ICU_CXXFLAGS -MD"
+            fi
 
-    	# add disable optimize flag for workaround for bug 899948
-    	if test -z "$MOZ_OPTIMIZE"; then
-    	    ICU_CFLAGS="$ICU_CFLAGS -Od"
-    	    ICU_CXXFLAGS="$ICU_CXXFLAGS -Od"
-    	fi
+            # add disable optimize flag for workaround for bug 899948
+            if test -z "$MOZ_OPTIMIZE"; then
+                ICU_CFLAGS="$ICU_CFLAGS -Od"
+                ICU_CXXFLAGS="$ICU_CXXFLAGS -Od"
+            fi
         fi
 
         if test -n "$gonkdir"; then
             ICU_CXXFLAGS="-I$gonkdir/abi/cpp/include $ICU_CXXFLAGS"
         fi
 
         if test -z "$MOZ_SHARED_ICU"; then
           ICU_CXXFLAGS="$ICU_CXXFLAGS -DU_STATIC_IMPLEMENTATION"
new file mode 100644
--- /dev/null
+++ b/build/macosx/cross-mozconfig.common
@@ -0,0 +1,50 @@
+# 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/.
+
+MOZ_AUTOMATION_L10N_CHECK=0
+#TODO: bug 935237 - fix packaging
+MOZ_AUTOMATION_PACKAGE=0
+#TODO: bug 543111 - fix Breakpad
+MOZ_AUTOMATION_BUILD_SYMBOLS=0
+MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
+
+if [ "x$IS_NIGHTLY" = "xyes" ]; then
+  # Some nightlies (eg: Mulet) don't want these set.
+  MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
+  MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
+fi
+. "$topsrcdir/build/mozconfig.common"
+#TODO: bug 543111 - fix Breakpad
+ac_add_options --disable-crashreporter
+
+# ld needs libLTO.so from llvm
+mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/clang/lib"
+
+CROSS_CCTOOLS_PATH=$topsrcdir/cctools
+CROSS_SYSROOT=$topsrcdir/MacOSX10.7.sdk
+CROSS_PRIVATE_FRAMEWORKS=$CROSS_SYSROOT/System/Library/PrivateFrameworks
+FLAGS="-target x86_64-apple-darwin10 -mlinker-version=136 -B $CROSS_CCTOOLS_PATH/bin -isysroot $CROSS_SYSROOT"
+
+export CC="$topsrcdir/clang/bin/clang $FLAGS"
+export CXX="$topsrcdir/clang/bin/clang++ $FLAGS"
+export CPP="$topsrcdir/clang/bin/clang $FLAGS -E"
+export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
+export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip"
+export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin10-
+#TODO: bug 1184202 - would be nice if these could be detected with TOOLCHAIN_PREFIX automatically
+export AR=${TOOLCHAIN_PREFIX}ar
+export RANLIB=${TOOLCHAIN_PREFIX}ranlib
+export STRIP=${TOOLCHAIN_PREFIX}strip
+export OTOOL=${TOOLCHAIN_PREFIX}otool
+
+export HOST_CC=gcc
+export HOST_CXX=g++
+export HOST_LDFLAGS="-g"
+
+ac_add_options --target=x86_64-apple-darwin
+ac_add_options --with-macos-private-frameworks=$CROSS_PRIVATE_FRAMEWORKS
+
+. "$topsrcdir/build/mozconfig.cache"
+
+export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
copy from build/macosx/mozconfig.common
copy to build/macosx/local-mozconfig.common
--- a/build/macosx/mozconfig.common
+++ b/build/macosx/mozconfig.common
@@ -1,37 +1,5 @@
-# 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/.
-
-MOZ_AUTOMATION_L10N_CHECK=0
-
-if [ "x$IS_NIGHTLY" = "xyes" ]; then
-  # Some nightlies (eg: Mulet) don't want these set.
-  MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
-  MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
-  MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
+if test `uname -s` = Linux; then
+  . $topsrcdir/build/macosx/cross-mozconfig.common
+else
+  . $topsrcdir/build/macosx/local-mozconfig.common
 fi
-. "$topsrcdir/build/mozconfig.common"
-
-if [ -d "$topsrcdir/clang" ]; then
-    # mozilla-central based build
-    export CC=$topsrcdir/clang/bin/clang
-    export CXX=$topsrcdir/clang/bin/clang++
-    export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
-elif [ -d "$topsrcdir/../clang" ]; then
-    # comm-central based build
-    export CC=$topsrcdir/../clang/bin/clang
-    export CXX=$topsrcdir/../clang/bin/clang++
-    export LLVMCONFIG=$topsrcdir/../clang/bin/llvm-config
-fi
-
-# If not set use the system default clang
-if [ -z "$CC" ]; then
-    export CC=clang
-fi
-
-# If not set use the system default clang++
-if [ -z "$CXX" ]; then
-    export CXX=clang++
-fi
-
-export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -756,26 +756,16 @@ nsScriptSecurityManager::CheckLoadURIWit
 
     // If the schemes don't match, the policy is specified by the protocol
     // flags on the target URI.  Note that the order of policy checks here is
     // very important!  We start from most restrictive and work our way down.
     // Note that since we're working with the innermost URI, we can just use
     // the methods that work on chains of nested URIs and they will only look
     // at the flags for our one URI.
 
-    // Special case: moz-extension has a whitelist of URIs that are loadable by
-    // anyone.
-    if (targetScheme.EqualsLiteral("moz-extension") && GetAddonPolicyService()) {
-      bool loadable = false;
-      rv = GetAddonPolicyService()->ExtensionURILoadableByAnyone(targetBaseURI, &loadable);
-      if (NS_SUCCEEDED(rv) && loadable) {
-        return NS_OK;
-      }
-    }
-
     // Check for system target URI
     rv = DenyAccessIfURIHasFlags(targetBaseURI,
                                  nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
     if (NS_FAILED(rv)) {
         // Deny access, since the origin principal is not system
         if (reportErrors) {
             ReportError(nullptr, errorTag, sourceURI, aTargetURI);
         }
--- a/caps/tests/mochitest/test_extensionURL.html
+++ b/caps/tests/mochitest/test_extensionURL.html
@@ -84,32 +84,40 @@ https://bugzilla.mozilla.org/show_bug.cg
     aps.setExtensionURIToAddonIdCallback(SpecialPowers.wrapCallback(function (uri) { return 'imaginaryaddon-' + uri.host[0]; }));
 
     function testLoad(url, navigate, shouldThrow) {
       var ifr = document.createElement('iframe');
       var p = new Promise(function(resolve, reject) {
         ifr.onload = function() {
           ok(true, 'Loaded ' + url);
           var prin = SpecialPowers.wrap(ifr.contentWindow).document.nodePrincipal;
-          is(prin.originNoSuffix, url, 'Principal origin is correct: ' + url);
+          function stripTrailingSlash(s) { return s.replace(/\/$/, ''); };
+          is(stripTrailingSlash(prin.URI.spec), url, 'Principal uri is correct: ' + url);
+          function stripPath(s) { return s.replace(/(.*\/\/.+)\/.*/, '$1'); };
+          is(prin.originNoSuffix, stripPath(url), 'Principal origin is correct: ' + prin.originNoSuffix);
           is(prin.originAttributes.addonId, 'imaginaryaddon-' + url[url.indexOf('/') + 2], 'addonId is correct');
-          is(SpecialPowers.wrap(ifr.contentWindow).document.title, 'resource test file',
-             'document looks right');
+          if (/_blank/.test(url)) {
+            is(SpecialPowers.wrap(ifr.contentWindow).document.documentElement.innerHTML,
+               '<head></head><body></body>', 'blank document looks right');
+          } else {
+            is(SpecialPowers.wrap(ifr.contentWindow).document.title, 'resource test file',
+               'document looks right');
+          }
           ifr.remove();
           resolve();
         };
         document.body.appendChild(ifr);
 
         var threw = false;
         try {
           navigate(ifr, url);
         } catch (e) {
           ifr.remove();
           threw = true;
-          ok(/denied|insecure/.test(e), "exceiton correct: " + e);
+          ok(/denied|insecure/.test(e), "exception correct: " + e);
         }
         is(threw, !!shouldThrow, "Correct throwing behavior for: " + url);
         !threw || resolve();
       });
 
       return p;
     }
 
@@ -121,16 +129,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation, /* shouldThrow = */ true))
     .then(setWhitelistCallback.bind(null, /cherise/))
     .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation))
     .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation, /* shouldThrow = */ true))
     .then(setWhitelistCallback.bind(null, /cherise|liebchen/))
     .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation))
     .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithSrc))
     .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithSrc))
+    .then(testLoad.bind(null, 'moz-extension://cherise/_blank.html', navigateWithSrc))
     .then(SimpleTest.finish.bind(SimpleTest),
           function(e) { ok(false, "rejected promise: " + e); SimpleTest.finish() }
     );
   });
 
   </script>
 </head>
 <body>
--- a/configure.in
+++ b/configure.in
@@ -4340,20 +4340,17 @@ cairo-cocoa)
     # Use -Wl as a trick to avoid -framework and framework names from
     # being separated by AC_SUBST_LIST.
     TK_LIBS='-Wl,-framework,CoreLocation -Wl,-framework,QuartzCore -Wl,-framework,Carbon -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,AddressBook -Wl,-framework,OpenGL'
     TK_CFLAGS="-DNO_X11"
     CFLAGS="$CFLAGS $TK_CFLAGS"
     CXXFLAGS="$CXXFLAGS $TK_CFLAGS"
     MOZ_USER_DIR="Mozilla"
     MOZ_FS_LAYOUT=bundle
-    # skip event loop instrumentation on UIKit for now
-    if test "$MOZ_WIDGET_TOOLKIT" == "cocoa"; then
-      MOZ_INSTRUMENT_EVENT_LOOP=1
-    fi
+    MOZ_INSTRUMENT_EVENT_LOOP=1
     ;;
 
 cairo-uikit)
     MOZ_WIDGET_TOOLKIT=uikit
     AC_DEFINE(MOZ_WIDGET_UIKIT)
     LDFLAGS="$LDFLAGS -framework UIKit -lobjc"
     TK_CFLAGS="-DNO_X11"
     TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreGraphics -Wl,-framework,CoreText -Wl,-framework,AVFoundation -Wl,-framework,AudioToolbox -Wl,-framework,CoreMedia -Wl,-framework,CoreVideo -Wl,-framework,OpenGLES -Wl,-framework,QuartzCore'
@@ -9211,32 +9208,16 @@ AC_SUBST(JS_SHARED_LIBRARY)
 MOZ_CREATE_CONFIG_STATUS()
 
 # No need to run subconfigures when building with LIBXUL_SDK_DIR
 if test "$COMPILE_ENVIRONMENT" -a -z "$LIBXUL_SDK_DIR"; then
   MOZ_SUBCONFIGURE_ICU()
   MOZ_SUBCONFIGURE_FFI()
 fi
 
-# Hack around an Apple bug that affects the egrep that comes with OS X 10.7.
-# "env ARCHPREFERENCE=i386,x86_64 arch egrep" first tries to use the 32-bit
-# Intel part of the egrep fat binary, even on 64-bit systems, and falls back on
-# the 64-bit part if it's not a fat binary, as can happen with MacPorts. We
-# (apparently) only need this hack when egrep's "pattern" is particularly long
-# (as in the following code) and the first egrep on our $PATH is Apple's.  See
-# bug 655339.
-case "$host" in
-*-apple-darwin11*)
-    FIXED_EGREP="env ARCHPREFERENCE=i386,x86_64 arch egrep"
-    ;;
-*)
-    FIXED_EGREP="egrep"
-    ;;
-esac
-
 # Run jemalloc configure script
 
 if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -o -n "$MOZ_REPLACE_MALLOC"; then
   ac_configure_args="--build=$build --host=$target --enable-stats --with-jemalloc-prefix=je_ --disable-valgrind"
   # We're using memalign for _aligned_malloc in memory/build/mozmemory_wrap.c
   # on Windows, so just export memalign on all platforms.
   ac_configure_args="$ac_configure_args ac_cv_func_memalign=yes"
   if test -n "$MOZ_REPLACE_MALLOC"; then
--- a/dom/base/MultipartBlobImpl.h
+++ b/dom/base/MultipartBlobImpl.h
@@ -93,17 +93,17 @@ public:
     return mLength;
   }
 
   virtual void GetInternalStream(nsIInputStream** aInputStream,
                                  ErrorResult& aRv) override;
 
   virtual const nsTArray<nsRefPtr<BlobImpl>>* GetSubBlobImpls() const override
   {
-    return &mBlobImpls;
+    return mBlobImpls.Length() ? &mBlobImpls : nullptr;
   }
 
   virtual void GetMozFullPathInternal(nsAString& aFullPath,
                                       ErrorResult& aRv) const override;
 
   virtual nsresult
   SetMutable(bool aMutable) override;
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3470,27 +3470,26 @@ nsPIDOMWindow::SetFrameElementInternal(E
     NS_ERROR("frameElement set on inner window with no outer!");
 
     return;
   }
 
   mOuterWindow->SetFrameElementInternal(aFrameElement);
 }
 
-void
+bool
 nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   mAudioContexts.AppendElement(aAudioContext);
 
+  // Return true if the context should be muted and false if not.
   nsIDocShell* docShell = GetDocShell();
-  if (docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline()) {
-    aAudioContext->Mute();
-  }
+  return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline();
 }
 
 void
 nsPIDOMWindow::RemoveAudioContext(AudioContext* aAudioContext)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   mAudioContexts.RemoveElement(aAudioContext);
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -713,17 +713,17 @@ public:
    */
   virtual void
   FirePopupBlockedEvent(nsIDocument* aDoc,
                         nsIURI* aPopupURI,
                         const nsAString& aPopupWindowName,
                         const nsAString& aPopupWindowFeatures) = 0;
 
   // Inner windows only.
-  void AddAudioContext(mozilla::dom::AudioContext* aAudioContext);
+  bool AddAudioContext(mozilla::dom::AudioContext* aAudioContext);
   void RemoveAudioContext(mozilla::dom::AudioContext* aAudioContext);
   void MuteAudioContexts();
   void UnmuteAudioContexts();
 
   // Given an inner window, return its outer if the inner is the current inner.
   // Otherwise (argument null or not an inner or not current) return null.
   static nsPIDOMWindow* GetOuterFromCurrentInner(nsPIDOMWindow* aInner)
   {
--- a/dom/base/nsPerformance.cpp
+++ b/dom/base/nsPerformance.cpp
@@ -740,23 +740,17 @@ nsPerformance::InsertUserEntry(Performan
 
   if (nsContentUtils::IsUserTimingLoggingEnabled()) {
     nsAutoCString uri;
     nsresult rv = GetOwner()->GetDocumentURI()->GetHost(uri);
     if(NS_FAILED(rv)) {
       // If we have no URI, just put in "none".
       uri.AssignLiteral("none");
     }
-    PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64 "\n",
-            uri.get(),
-            NS_ConvertUTF16toUTF8(aEntry->GetEntryType()).get(),
-            NS_ConvertUTF16toUTF8(aEntry->GetName()).get(),
-            aEntry->StartTime(),
-            aEntry->Duration(),
-            static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC));
+    PerformanceBase::LogEntry(aEntry, uri);
   }
 
   PerformanceBase::InsertUserEntry(aEntry);
 }
 
 DOMHighResTimeStamp
 nsPerformance::DeltaFromNavigationStart(DOMHighResTimeStamp aTime)
 {
@@ -976,16 +970,28 @@ PerformanceBase::Measure(const nsAString
 
 void
 PerformanceBase::ClearMeasures(const Optional<nsAString>& aName)
 {
   ClearUserEntries(aName, NS_LITERAL_STRING("measure"));
 }
 
 void
+PerformanceBase::LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const
+{
+  PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64 "\n",
+          aOwner.BeginReading(),
+          NS_ConvertUTF16toUTF8(aEntry->GetEntryType()).get(),
+          NS_ConvertUTF16toUTF8(aEntry->GetName()).get(),
+          aEntry->StartTime(),
+          aEntry->Duration(),
+          static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC));
+}
+
+void
 PerformanceBase::InsertUserEntry(PerformanceEntry* aEntry)
 {
   mUserEntries.InsertElementSorted(aEntry,
                                    PerformanceEntryComparator());
 }
 
 void
 PerformanceBase::SetResourceTimingBufferSize(uint64_t aMaxSize)
--- a/dom/base/nsPerformance.h
+++ b/dom/base/nsPerformance.h
@@ -345,16 +345,18 @@ protected:
   virtual DOMHighResTimeStamp
   GetPerformanceTimingFromString(const nsAString& aTimingName) = 0;
 
   bool IsResourceEntryLimitReached() const
   {
     return mResourceEntries.Length() >= mResourceTimingBufferSize;
   }
 
+  void LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const;
+
 private:
   nsTArray<nsRefPtr<PerformanceEntry>> mUserEntries;
   nsTArray<nsRefPtr<PerformanceEntry>> mResourceEntries;
 
   uint64_t mResourceTimingBufferSize;
   static const uint64_t kDefaultResourceTimingBufferSize = 150;
 };
 
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -96,18 +96,18 @@ private:
   JSObject* mWrapper;
 };
 
 static void
 DebugWrapperTraceCallback(JS::GCCellPtr aPtr, const char* aName, void* aClosure)
 {
   DebugWrapperTraversalCallback* callback =
     static_cast<DebugWrapperTraversalCallback*>(aClosure);
-  if (aPtr.isObject()) {
-    callback->NoteJSObject(aPtr.toObject());
+  if (aPtr.is<JSObject>()) {
+    callback->NoteJSObject(&aPtr.as<JSObject>());
   }
 }
 
 void
 nsWrapperCache::CheckCCWrapperTraversal(void* aScriptObjectHolder,
                                         nsScriptObjectTracer* aTracer)
 {
   JSObject* wrapper = GetWrapper();
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -317,18 +317,20 @@ private:
     MOZ_ASSERT((aFlagsToUnset & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
     mFlags &= ~aFlagsToUnset;
   }
 
   static void HoldJSObjects(void* aScriptObjectHolder,
                             nsScriptObjectTracer* aTracer);
 
 #ifdef DEBUG
+public:
   void CheckCCWrapperTraversal(void* aScriptObjectHolder,
                                nsScriptObjectTracer* aTracer);
+private:
 #endif // DEBUG
 
   /**
    * If this bit is set then we're preserving the wrapper, which in effect ties
    * the lifetime of the JS object stored in the cache to the lifetime of the
    * native object. We rely on the cycle collector to break the cycle that this
    * causes between the native object and the JS object, so it is important that
    * any native object that supports preserving of its wrapper
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_pluginAudio.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<embed type="application/x-test" width="200" height="200"></embed>
+<script>
+var plugin = document.querySelector("embed");
+onload = function() {
+  plugin.startAudioPlayback();
+};
+function stopAudio() {
+  plugin.stopAudioPlayback();
+}
+function pluginMuted() {
+  return plugin.audioMuted();
+}
+function toggleMuteState(muted) {
+  var Ci = SpecialPowers.Ci;
+  var utils = SpecialPowers.wrap(window).top
+                           .QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils);
+  utils.audioMuted = muted;
+}
+</script>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -235,16 +235,17 @@ support-files =
   wholeTexty-helper.xml
   file_nonascii_blob_url.html
   referrerHelper.js
   test_performance_user_timing.js
   img_referrer_testserver.sjs
   file_audioLoop.html
   file_webaudioLoop.html
   file_webaudioLoop2.html
+  file_pluginAudio.html
   referrer_helper.js
   referrer_testserver.sjs
   script_postmessages_fileList.js
   iframe_postMessages.html
 
 [test_anonymousContent_api.html]
 [test_anonymousContent_append_after_reflow.html]
 [test_anonymousContent_insert.html]
@@ -296,16 +297,18 @@ skip-if = buildapp == 'mulet' || buildap
 [test_messagemanager_targetchain.html]
 [test_named_frames.html]
 [test_navigator_resolve_identity.html]
 [test_navigator_language.html]
 [test_openDialogChromeOnly.html]
 [test_open_null_features.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
 [test_postMessage_solidus.html]
+[test_pluginAudioNotification.html]
+skip-if = (buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android') # Plugins don't work on Android and/or B2G/Mulet
 [test_screen_orientation.html]
 [test_settimeout_extra_arguments.html]
 [test_settimeout_inner.html]
 [test_setting_opener.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_simplecontentpolicy.html]
 skip-if = e10s || buildapp == 'b2g'
 [test_url.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_pluginAudioNotification.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for audio controller in windows</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<pre id="test">
+</pre>
+<iframe></iframe>
+
+<script type="application/javascript">
+
+// Copied from /dom/plugins/test/mochitest/utils.js
+function getTestPlugin(pluginName) {
+  var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
+                                 .getService(SpecialPowers.Ci.nsIPluginHost);
+  var tags = ph.getPluginTags();
+  var name = pluginName || "Test Plug-in";
+  for (var tag of tags) {
+    if (tag.name == name) {
+      return tag;
+    }
+  }
+
+  ok(false, "Could not find plugin tag with plugin name '" + name + "'");
+  return null;
+}
+// Copied from /dom/plugins/test/mochitest/utils.js
+function setTestPluginEnabledState(newEnabledState, pluginName) {
+  var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
+  if (!oldEnabledState) {
+    return;
+  }
+  var plugin = getTestPlugin(pluginName);
+  while (plugin.enabledState != newEnabledState) {
+    // Run a nested event loop to wait for the preference change to
+    // propagate to the child. Yuck!
+    SpecialPowers.Services.tm.currentThread.processNextEvent(true);
+  }
+  SimpleTest.registerCleanupFunction(function() {
+    SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
+  });
+}
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+SimpleTest.waitForExplicitFinish();
+
+var expectedNotification = null;
+var iframe = null;
+
+var observer = {
+  observe: function(subject, topic, data) {
+    is(topic, "media-playback", "media-playback received");
+    is(data, expectedNotification, "This is the right notification");
+    SimpleTest.executeSoon(runTest);
+  }
+};
+
+var observerService = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
+                                   .getService(SpecialPowers.Ci.nsIObserverService);
+
+var tests = [
+  function() {
+    iframe = document.querySelector("iframe");
+    SpecialPowers.pushPrefEnv({"set": [["media.useAudioChannelService", true]]}, runTest);
+  },
+
+  function() {
+    observerService.addObserver(observer, "media-playback", false);
+    ok(true, "Observer set");
+    runTest();
+  },
+
+  function() {
+    expectedNotification = 'active';
+    iframe.src = "file_pluginAudio.html";
+  },
+
+  function() {
+    ok(!iframe.contentWindow.pluginMuted(), "Plugin should not be muted");
+    iframe.contentWindow.toggleMuteState(true);
+    ok(iframe.contentWindow.pluginMuted(), "Plugin should be muted");
+    iframe.contentWindow.toggleMuteState(false);
+    ok(!iframe.contentWindow.pluginMuted(), "Plugin should not be muted");
+    runTest();
+  },
+
+  function() {
+    expectedNotification = 'inactive';
+    iframe.contentWindow.stopAudio();
+  },
+
+  function() {
+    observerService.removeObserver(observer, "media-playback");
+    ok(true, "Observer removed");
+    runTest();
+  }
+];
+
+function runTest() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+onload = runTest;
+
+</script>
+</body>
+</html>
+
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -874,16 +874,66 @@ template <class T>
 struct TypeNeedsOuterization
 {
   // We only need to outerize Window objects, so anything inheriting from
   // nsGlobalWindow (which inherits from EventTarget itself).
   static const bool value =
     IsBaseOf<nsGlobalWindow, T>::value || IsSame<EventTarget, T>::value;
 };
 
+#ifdef DEBUG
+template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct CheckWrapperCacheTracing
+{
+  static inline void Check(T* aObject)
+  {
+  }
+};
+
+template<typename T>
+struct CheckWrapperCacheTracing<T, true>
+{
+  static void Check(T* aObject)
+  {
+    // Rooting analysis thinks QueryInterface may GC, but we're dealing with
+    // a subset of QueryInterface, C++ only types here.
+    JS::AutoSuppressGCAnalysis nogc;
+
+    nsWrapperCache* wrapperCacheFromQI = nullptr;
+    aObject->QueryInterface(NS_GET_IID(nsWrapperCache),
+                            reinterpret_cast<void**>(&wrapperCacheFromQI));
+
+    MOZ_ASSERT(wrapperCacheFromQI,
+               "Missing nsWrapperCache from QueryInterface implementation?");
+
+    if (!wrapperCacheFromQI->GetWrapperPreserveColor()) {
+      // Can't assert that we trace the wrapper, since we don't have any
+      // wrapper to trace.
+      return;
+    }
+
+    nsISupports* ccISupports = nullptr;
+    aObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
+                            reinterpret_cast<void**>(&ccISupports));
+    MOZ_ASSERT(ccISupports,
+               "nsWrapperCache object which isn't cycle collectable?");
+
+    nsXPCOMCycleCollectionParticipant* participant = nullptr;
+    CallQueryInterface(ccISupports, &participant);
+    MOZ_ASSERT(participant, "Can't QI to CycleCollectionParticipant?");
+
+    bool wasPreservingWrapper = wrapperCacheFromQI->PreservingWrapper();
+    wrapperCacheFromQI->SetPreservingWrapper(true);
+    wrapperCacheFromQI->CheckCCWrapperTraversal(ccISupports, participant);
+    wrapperCacheFromQI->SetPreservingWrapper(wasPreservingWrapper);
+  }
+};
+
+#endif
+
 template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
 MOZ_ALWAYS_INLINE bool
 DoGetOrCreateDOMReflector(JSContext* cx, T* value,
                           JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(value);
   JSObject* obj = value->GetWrapperPreserveColor();
   // We can get rid of this when we remove support for hasXPConnectImpls.
@@ -898,16 +948,22 @@ DoGetOrCreateDOMReflector(JSContext* cx,
 
     obj = value->WrapObject(cx, nullptr);
     if (!obj) {
       // At this point, obj is null, so just return false.
       // Callers seem to be testing JS_IsExceptionPending(cx) to
       // figure out whether WrapObject() threw.
       return false;
     }
+
+#ifdef DEBUG
+    if (IsBaseOf<nsWrapperCache, T>::value) {
+      CheckWrapperCacheTracing<T>::Check(value);
+    }
+#endif
   }
 
 #ifdef DEBUG
   const DOMJSClass* clasp = GetDOMClass(obj);
   // clasp can be null if the cache contained a non-DOM object.
   if (clasp) {
     // Some sanity asserts about our object.  Specifically:
     // 1)  If our class claims we're nsISupports, we better be nsISupports
--- a/dom/cache/PrincipalVerifier.cpp
+++ b/dom/cache/PrincipalVerifier.cpp
@@ -126,29 +126,36 @@ PrincipalVerifier::VerifyOnMainThread()
   // We disallow null principal and unknown app IDs on the client side, but
   // double-check here.
   if (NS_WARN_IF(principal->GetIsNullPrincipal() ||
                  principal->GetUnknownAppId())) {
     DispatchToInitiatingThread(NS_ERROR_FAILURE);
     return;
   }
 
+  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
+  if (NS_WARN_IF(!ssm)) {
+    DispatchToInitiatingThread(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
+    return;
+  }
+
+  // Verify if a child process uses system principal, which is not allowed
+  // to prevent system principal is spoofed.
+  if (NS_WARN_IF(actor && ssm->IsSystemPrincipal(principal))) {
+    DispatchToInitiatingThread(NS_ERROR_FAILURE);
+    return;
+  }
+
   // Verify that a child process claims to own the app for this principal
   if (NS_WARN_IF(actor && !AssertAppPrincipal(actor, principal))) {
     DispatchToInitiatingThread(NS_ERROR_FAILURE);
     return;
   }
   actor = nullptr;
 
-  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
-  if (NS_WARN_IF(!ssm)) {
-    DispatchToInitiatingThread(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
-    return;
-  }
-
 #ifdef DEBUG
   // Sanity check principal origin by using it to construct a URI and security
   // checking it.  Don't do this for the system principal, though, as its origin
   // is a synthetic [System Principal] string.
   if (!ssm->IsSystemPrincipal(principal)) {
     nsAutoCString origin;
     rv = principal->GetOriginNoSuffix(origin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1820,16 +1820,22 @@ WebGLContext::DidRefresh()
 }
 
 bool
 WebGLContext::TexImageFromVideoElement(const TexImageTarget texImageTarget,
                                        GLint level, GLenum internalFormat,
                                        GLenum format, GLenum type,
                                        mozilla::dom::Element& elt)
 {
+    if (type == LOCAL_GL_HALF_FLOAT_OES &&
+        !gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float))
+    {
+        type = LOCAL_GL_HALF_FLOAT;
+    }
+
     if (!ValidateTexImageFormatAndType(format, type,
                                        WebGLTexImageFunc::TexImage,
                                        WebGLTexDimensions::Tex2D))
     {
         return false;
     }
 
     HTMLVideoElement* video = HTMLVideoElement::FromContentOrNull(&elt);
--- a/dom/html/test/test_fullscreen-api-race.html
+++ b/dom/html/test/test_fullscreen-api-race.html
@@ -107,23 +107,36 @@ function next() {
     SimpleTest.finish();
     return;
   }
   var win = test.openWinFunc();
   new Promise(resolve => {
     SimpleTest.waitForFocus(resolve, win, true);
   }).then(() => {
     return new Promise(resolve => {
-      function listener() {
+      var retried = false;
+      function listener(evt) {
+        if (!retried && evt.type == "mozfullscreenerror") {
+          todo(false, "Failed to enter fullscreen, but try again");
+          retried = true;
+          SimpleTest.waitForFocus(() => {
+            win.document.documentElement.mozRequestFullScreen();
+          }, win, true);
+          return;
+        }
         win.removeEventListener("mozfullscreenchange", listener);
+        win.removeEventListener("mozfullscreenerror", listener);
+        is(evt.type, "mozfullscreenchange", "Should get fullscreenchange");
         ok(win.document.mozFullScreen, "Should have entered fullscreen");
         ok(win.fullScreen, "The window should be in fullscreen");
         test.actionFunc(win).then(resolve);
       }
+      info("About to enter fullscreen");
       win.addEventListener("mozfullscreenchange", listener);
+      win.addEventListener("mozfullscreenerror", listener);
       win.document.documentElement.mozRequestFullScreen();
     });
   }).then(() => {
     ok(win.closed, "The window should have been closed");
     SimpleTest.waitForFocus(next);
   });
 }
 
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -677,17 +677,18 @@ IDBCursor::Delete(JSContext* aCx, ErrorR
   if (!mTransaction->IsWriteAllowed()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
     return nullptr;
   }
 
   if (IsSourceDeleted() ||
       !mHaveValue ||
       mType == Type_ObjectStoreKey ||
-      mType == Type_IndexKey) {
+      mType == Type_IndexKey ||
+      mContinueCalled) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return nullptr;
   }
 
   MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index);
   MOZ_ASSERT(!mKey.IsUnset());
 
   IDBObjectStore* objectStore;
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -1070,16 +1070,18 @@ void
 BlobDataFromBlobImpl(BlobImpl* aBlobImpl, BlobData& aBlobData)
 {
   MOZ_ASSERT(gProcessType != GeckoProcessType_Default);
   MOZ_ASSERT(aBlobImpl);
 
   const nsTArray<nsRefPtr<BlobImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls();
 
   if (subBlobs) {
+    MOZ_ASSERT(subBlobs->Length());
+
     aBlobData = nsTArray<BlobData>();
 
     nsTArray<BlobData>& subBlobDatas = aBlobData.get_ArrayOfBlobData();
     subBlobDatas.SetLength(subBlobs->Length());
 
     for (uint32_t count = subBlobs->Length(), index = 0;
          index < count;
          index++) {
@@ -1093,18 +1095,16 @@ BlobDataFromBlobImpl(BlobImpl* aBlobImpl
   if (remoteBlob) {
     BlobChild* actor = remoteBlob->GetBlobChild();
     MOZ_ASSERT(actor);
 
     aBlobData = actor->ParentID();
     return;
   }
 
-  MOZ_ASSERT(aBlobImpl->IsMemoryFile());
-
   ErrorResult rv;
   nsCOMPtr<nsIInputStream> inputStream;
   aBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
   MOZ_ALWAYS_TRUE(!rv.Failed());
 
   DebugOnly<bool> isNonBlocking;
   MOZ_ASSERT(NS_SUCCEEDED(inputStream->IsNonBlocking(&isNonBlocking)));
   MOZ_ASSERT(isNonBlocking);
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -9,22 +9,24 @@
 #include "mozilla/Attributes.h"
 #include "gmp-entrypoints.h"
 #include "prlink.h"
 #include "prenv.h"
 #include "nsAutoPtr.h"
 
 #include <string>
 
-#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+#ifdef XP_WIN
+#include "windows.h"
+#ifdef MOZ_SANDBOX
 #include "mozilla/Scoped.h"
-#include "windows.h"
 #include <intrin.h>
 #include <assert.h>
 #endif
+#endif
 
 #if defined(HASH_NODE_ID_WITH_DEVICE_ID)
 // In order to provide EME plugins with a "device binding" capability,
 // in the parent we generate and store some random bytes as salt for every
 // (origin, urlBarOrigin) pair that uses EME. We store these bytes so
 // that every time we revisit the same origin we get the same salt.
 // We send this salt to the child on startup. The child collects some
 // device specific data and munges that with the salt to create the
@@ -191,37 +193,39 @@ GMPLoaderImpl::Load(const char* aUTF8Lib
       }
     }
   } else
 #endif
   {
     nodeId = std::string(aOriginSalt, aOriginSalt + aOriginSaltLen);
   }
 
-#if defined(XP_WIN) && defined(MOZ_SANDBOX)
-  // If the GMP DLL is a side-by-side assembly with static imports then the DLL
-  // loader will attempt to create an activation context which will fail because
-  // of the sandbox. If we create an activation context before we start the
-  // sandbox then this one will get picked up by the DLL loader.
+#ifdef XP_WIN
   int pathLen = MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, nullptr, 0);
   if (pathLen == 0) {
     return false;
   }
 
   nsAutoArrayPtr<wchar_t> widePath(new wchar_t[pathLen]);
   if (MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, widePath, pathLen) == 0) {
     return false;
   }
 
+#ifdef MOZ_SANDBOX
+  // If the GMP DLL is a side-by-side assembly with static imports then the DLL
+  // loader will attempt to create an activation context which will fail because
+  // of the sandbox. If we create an activation context before we start the
+  // sandbox then this one will get picked up by the DLL loader.
   ACTCTX actCtx = { sizeof(actCtx) };
   actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
   actCtx.lpSource = widePath;
   actCtx.lpResourceName = ISOLATIONAWARE_MANIFEST_RESOURCE_ID;
   ScopedActCtxHandle actCtxHandle(CreateActCtx(&actCtx));
 #endif
+#endif
 
   // Start the sandbox now that we've generated the device bound node id.
   // This must happen after the node id is bound to the device id, as
   // generating the device id requires privileges.
   if (mSandboxStarter && !mSandboxStarter->Start(aUTF8LibPath)) {
     return false;
   }
 
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -256,65 +256,96 @@ private:
 #ifdef MOZ_FMP4
 class MP4ContainerParser : public ContainerParser {
 public:
   explicit MP4ContainerParser(const nsACString& aType)
     : ContainerParser(aType)
     , mMonitor("MP4ContainerParser Index Monitor")
   {}
 
-  bool HasAtom(const mp4_demuxer::AtomType& aAtom, const MediaByteBuffer* aData) {
-    mp4_demuxer::ByteReader reader(aData);
-
-    while (reader.Remaining() >= 8) {
-      uint64_t size = reader.ReadU32();
-      const uint8_t* typec = reader.Peek(4);
-      uint32_t type = reader.ReadU32();
-      MSE_DEBUGV(MP4ContainerParser ,"Checking atom:'%c%c%c%c'",
-                typec[0], typec[1], typec[2], typec[3]);
-      if (mp4_demuxer::AtomType(type) == aAtom) {
-        reader.DiscardRemaining();
-        return true;
-      }
-      if (size == 1) {
-        // 64 bits size.
-        if (!reader.CanReadType<uint64_t>()) {
-          break;
-        }
-        size = reader.ReadU64();
-      } else if (size == 0) {
-        // Atom extends to the end of the buffer, it can't have what we're
-        // looking for.
-        break;
-      }
-      if (reader.Remaining() < size - 8) {
-        // Incomplete atom.
-        break;
-      }
-      reader.Read(size - 8);
-    }
-    reader.DiscardRemaining();
-    return false;
-  }
-
   bool IsInitSegmentPresent(MediaByteBuffer* aData) override
   {
     ContainerParser::IsInitSegmentPresent(aData);
     // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
     // file is the 'ftyp' atom followed by a file type. We just check for a
     // vaguely valid 'ftyp' atom.
-    return HasAtom(mp4_demuxer::AtomType("ftyp"), aData);
+    AtomParser parser(mType, aData);
+    return parser.StartWithInitSegment();
   }
 
   bool IsMediaSegmentPresent(MediaByteBuffer* aData) override
   {
-    ContainerParser::IsMediaSegmentPresent(aData);
-    return HasAtom(mp4_demuxer::AtomType("moof"), aData);
+    AtomParser parser(mType, aData);
+    return parser.StartWithMediaSegment();
   }
 
+private:
+  class AtomParser {
+  public:
+    AtomParser(const nsACString& aType, const MediaByteBuffer* aData)
+    {
+      const nsCString mType(aType); // for logging macro.
+      mp4_demuxer::ByteReader reader(aData);
+      mp4_demuxer::AtomType initAtom("ftyp");
+      mp4_demuxer::AtomType mediaAtom("moof");
+
+      while (reader.Remaining() >= 8) {
+        uint64_t size = reader.ReadU32();
+        const uint8_t* typec = reader.Peek(4);
+        uint32_t type = reader.ReadU32();
+        MSE_DEBUGV(AtomParser ,"Checking atom:'%c%c%c%c'",
+                   typec[0], typec[1], typec[2], typec[3]);
+        if (mInitOffset.isNothing() &&
+            mp4_demuxer::AtomType(type) == initAtom) {
+          mInitOffset = Some(reader.Offset());
+        }
+        if (mMediaOffset.isNothing() &&
+            mp4_demuxer::AtomType(type) == mediaAtom) {
+          mMediaOffset = Some(reader.Offset());
+        }
+        if (mInitOffset.isSome() && mMediaOffset.isSome()) {
+          // We have everything we need.
+          break;
+        }
+        if (size == 1) {
+          // 64 bits size.
+          if (!reader.CanReadType<uint64_t>()) {
+            break;
+          }
+          size = reader.ReadU64();
+        } else if (size == 0) {
+          // Atom extends to the end of the buffer, it can't have what we're
+          // looking for.
+          break;
+        }
+        if (reader.Remaining() < size - 8) {
+          // Incomplete atom.
+          break;
+        }
+        reader.Read(size - 8);
+      }
+      reader.DiscardRemaining();
+    }
+
+    bool StartWithInitSegment()
+    {
+      return mInitOffset.isSome() &&
+        (mMediaOffset.isNothing() || mInitOffset.ref() < mMediaOffset.ref());
+    }
+    bool StartWithMediaSegment()
+    {
+      return mMediaOffset.isSome() &&
+        (mInitOffset.isNothing() || mMediaOffset.ref() < mInitOffset.ref());
+    }
+  private:
+    Maybe<size_t> mInitOffset;
+    Maybe<size_t> mMediaOffset;
+  };
+
+public:
   bool ParseStartAndEndTimestamps(MediaByteBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd) override
   {
     MonitorAutoLock mon(mMonitor); // We're not actually racing against anything,
                                    // but mParser requires us to hold a monitor.
     bool initSegment = IsInitSegmentPresent(aData);
     if (initSegment) {
       mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/mp4"));
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -776,19 +776,18 @@ TrackBuffersManager::CreateDemuxerforMIM
   NS_WARNING("Not supported (yet)");
   return;
 }
 
 void
 TrackBuffersManager::InitializationSegmentReceived()
 {
   MOZ_ASSERT(mParser->HasCompleteInitData());
-  mInitData = mParser->InitData();
   mCurrentInputBuffer = new SourceBufferResource(mType);
-  mCurrentInputBuffer->AppendData(mInitData);
+  mCurrentInputBuffer->AppendData(mParser->InitData());
   uint32_t length =
     mParser->InitSegmentRange().mEnd - (mProcessedInput - mInputBuffer->Length());
   if (mInputBuffer->Length() == length) {
     mInputBuffer = nullptr;
   } else {
     mInputBuffer->RemoveElementsAt(0, length);
   }
   CreateDemuxerforMIMEType();
@@ -873,17 +872,17 @@ TrackBuffersManager::OnDemuxerInitDone(n
     mVideoTracks.mNeedRandomAccessPoint = true;
     mAudioTracks.mNeedRandomAccessPoint = true;
 
     mVideoTracks.mLongestFrameDuration = mVideoTracks.mLastFrameDuration;
     mAudioTracks.mLongestFrameDuration = mAudioTracks.mLastFrameDuration;
   }
 
   // 4. Let active track flag equal false.
-  mActiveTrack = false;
+  bool activeTrack = false;
 
   // Increase our stream id.
   uint32_t streamID = sStreamSourceID++;
 
   // 5. If the first initialization segment received flag is false, then run the following steps:
   if (!mFirstInitializationSegmentReceived) {
     mAudioTracks.mNumTracks = numAudios;
     // TODO:
@@ -906,17 +905,17 @@ TrackBuffersManager::OnDemuxerInitDone(n
       //   2. Let new audio track be a new AudioTrack object.
       //   3. Generate a unique ID and assign it to the id property on new audio track.
       //   4. Assign audio language to the language property on new audio track.
       //   5. Assign audio label to the label property on new audio track.
       //   6. Assign current audio kind to the kind property on new audio track.
       //   7. If audioTracks.length equals 0, then run the following steps:
       //     1. Set the enabled property on new audio track to true.
       //     2. Set active track flag to true.
-      mActiveTrack = true;
+      activeTrack = true;
       //   8. Add new audio track to the audioTracks attribute on this SourceBuffer object.
       //   9. Queue a task to fire a trusted event named addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object referenced by the audioTracks attribute on this SourceBuffer object.
       //   10. Add new audio track to the audioTracks attribute on the HTMLMediaElement.
       //   11. Queue a task to fire a trusted event named addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object referenced by the audioTracks attribute on the HTMLMediaElement.
       mAudioTracks.mBuffers.AppendElement(TrackBuffer());
       // 10. Add the track description for this track to the track buffer.
       mAudioTracks.mInfo = new SharedTrackInfo(info.mAudio, streamID);
       mAudioTracks.mLastInfo = mAudioTracks.mInfo;
@@ -938,29 +937,32 @@ TrackBuffersManager::OnDemuxerInitDone(n
       //   2. Let new video track be a new VideoTrack object.
       //   3. Generate a unique ID and assign it to the id property on new video track.
       //   4. Assign video language to the language property on new video track.
       //   5. Assign video label to the label property on new video track.
       //   6. Assign current video kind to the kind property on new video track.
       //   7. If videoTracks.length equals 0, then run the following steps:
       //     1. Set the selected property on new video track to true.
       //     2. Set active track flag to true.
-      mActiveTrack = true;
+      activeTrack = true;
       //   8. Add new video track to the videoTracks attribute on this SourceBuffer object.
       //   9. Queue a task to fire a trusted event named addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object referenced by the videoTracks attribute on this SourceBuffer object.
       //   10. Add new video track to the videoTracks attribute on the HTMLMediaElement.
       //   11. Queue a task to fire a trusted event named addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object referenced by the videoTracks attribute on the HTMLMediaElement.
       mVideoTracks.mBuffers.AppendElement(TrackBuffer());
       // 10. Add the track description for this track to the track buffer.
       mVideoTracks.mInfo = new SharedTrackInfo(info.mVideo, streamID);
       mVideoTracks.mLastInfo = mVideoTracks.mInfo;
     }
     // 4. For each text track in the initialization segment, run following steps:
     // 5. If active track flag equals true, then run the following steps:
     // This is handled by SourceBuffer once the promise is resolved.
+    if (activeTrack) {
+      mActiveTrack = true;
+    }
 
     // 6. Set first initialization segment received flag to true.
     mFirstInitializationSegmentReceived = true;
   } else {
     mAudioTracks.mLastInfo = new SharedTrackInfo(info.mAudio, streamID);
     mVideoTracks.mLastInfo = new SharedTrackInfo(info.mVideo, streamID);
   }
 
@@ -980,16 +982,19 @@ TrackBuffersManager::OnDemuxerInitDone(n
     mEncrypted = true;
   }
 
   {
     MonitorAutoLock mon(mMonitor);
     mInfo = info;
   }
 
+  // We now have a valid init data ; we can store it for later use.
+  mInitData = mParser->InitData();
+
   // 3. Remove the initialization segment bytes from the beginning of the input buffer.
   // This step has already been done in InitializationSegmentReceived when we
   // transferred the content into mCurrentInputBuffer.
   mCurrentInputBuffer->EvictAll();
   RecreateParser();
 
   // 4. Set append state to WAITING_FOR_SEGMENT.
   SetAppendState(AppendState::WAITING_FOR_SEGMENT);
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -100,8 +100,10 @@ skip-if = ((os == "win" && os_version ==
 [test_TruncatedDuration.html]
 skip-if = true # bug 1182946
 [test_TruncatedDuration_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_WaitingOnMissingData.html]
 skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized.
 [test_WaitingOnMissingData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
+[test_WaitingToEndedTransition_mp4.html]
+skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/test/test_WaitingToEndedTransition_mp4.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252">
+  <title>MSE: |waiting| event when source data is missing</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="mediasource.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test"><script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+runWithMSE(function(ms, el) {
+  el.controls = true;
+  once(ms, 'sourceopen').then(function() {
+    ok(true, "Receive a sourceopen event");
+    var audiosb = ms.addSourceBuffer("audio/mp4");
+    var videosb = ms.addSourceBuffer("video/mp4");
+    fetchAndLoad(audiosb, 'bipbop/bipbop_audio', ['init'], '.mp4')
+    .then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', ['init'], '.mp4'))
+    .then(fetchAndLoad.bind(null, audiosb, 'bipbop/bipbop_audio', range(1, 5), '.m4s'))
+    .then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', range(1, 6), '.m4s'))
+    .then(function() {
+      // HTMLMediaElement fires 'waiting' if somebody invokes |play()| before the MDSM
+      // has notified it of available data. Make sure that we get 'playing' before
+      // we starting waiting for 'waiting'.
+      info("Invoking play()");
+      var p = once(el, 'playing');
+      el.play();
+      return p;
+    }).then(function() {
+      ok(true, "Video playing. It should play for a bit, then fire 'waiting'");
+      var p = once(el, 'waiting');
+      el.play();
+      return p;
+    }).then(function() {
+      // currentTime is based on the current video frame, so if the audio ends just before
+      // the next video frame, currentTime can be up to 1 frame's worth earlier than
+      // min(audioEnd, videoEnd).
+      isfuzzy(el.currentTime, Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 1/60,
+              1/30, "Got a waiting event at " + el.currentTime);
+    }).then(function() {
+      var p = once(el, 'ended');
+      ms.endOfStream();
+      return p;
+    }).then(function() {
+      is(el.duration, 4.005, "Video has correct duration: " + el.duration);
+      is(el.currentTime, el.duration, "Video has correct currentTime.");
+      SimpleTest.finish();
+    });
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/995289.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+var r0=new AudioContext();
+var r5=r0.createOscillator();
+var r6=r0.createPeriodicWave(new Float32Array(1),new Float32Array(1));
+r5.frequency.value = 4294967295;
+r5.start(0);
+r5.setPeriodicWave(r6);
+</script>
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -62,16 +62,17 @@ load 925619-2.html
 load 926619.html
 load 933151.html
 load 933156.html
 load 944851.html
 load 952756.html
 load 966636.html
 load 986901.html
 load 990794.html
+load 995289.html
 load 1012609.html
 load 1015662.html
 load 1020205.html
 load 1041466.html
 load 1045650.html
 skip-if(Android||B2G) test-pref(media.navigator.permission.disabled,true) load 1028458.html # bug 1048863
 load analyser-channels-1.html
 load buffer-source-ended-1.html
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -96,22 +96,27 @@ AudioContext::AudioContext(nsPIDOMWindow
   , mAudioContextState(AudioContextState::Suspended)
   , mNumberOfChannels(aNumberOfChannels)
   , mNodeCount(0)
   , mIsOffline(aIsOffline)
   , mIsStarted(!aIsOffline)
   , mIsShutDown(false)
   , mCloseCalled(false)
 {
-  aWindow->AddAudioContext(this);
+  bool mute = aWindow->AddAudioContext(this);
 
   // Note: AudioDestinationNode needs an AudioContext that must already be
   // bound to the window.
   mDestination = new AudioDestinationNode(this, aIsOffline, aChannel,
                                           aNumberOfChannels, aLength, aSampleRate);
+
+  // The context can't be muted until it has a destination.
+  if (mute) {
+    Mute();
+  }
 }
 
 void
 AudioContext::Init()
 {
   // We skip calling SetIsOnlyNodeForContext and the creation of the
   // audioChannelAgent during mDestination's constructor, because we can only
   // call them after mDestination has been set up.
--- a/dom/media/webaudio/WaveShaperNode.cpp
+++ b/dom/media/webaudio/WaveShaperNode.cpp
@@ -319,20 +319,24 @@ WaveShaperNode::SetCurve(const Nullable<
     floats.ComputeLengthAndData();
 
     uint32_t argLength = floats.Length();
     if (argLength < 2) {
       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
       return;
     }
 
-    mCurve = floats.Obj();
+    if (!curve.SetLength(argLength, fallible)) {
+      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
 
-    curve.SetLength(argLength);
     PodCopy(curve.Elements(), floats.Data(), floats.Length());
+
+    mCurve = floats.Obj();
   } else {
     mCurve = nullptr;
   }
 
   AudioNodeStream* ns = mStream;
   MOZ_ASSERT(ns, "Why don't we have a stream here?");
   ns->SetRawArrayData(curve);
 }
--- a/dom/media/webaudio/blink/PeriodicWave.cpp
+++ b/dom/media/webaudio/blink/PeriodicWave.cpp
@@ -142,17 +142,17 @@ void PeriodicWave::waveDataForFundamenta
     // So the lower table data will have a larger range index.
     unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
     unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
 
     lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
     higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
 
     // Ranges from 0 -> 1 to interpolate between lower -> higher.
-    tableInterpolationFactor = pitchRange - rangeIndex1;
+    tableInterpolationFactor = rangeIndex2 - pitchRange;
 }
 
 unsigned PeriodicWave::maxNumberOfPartials() const
 {
     return m_periodicWaveSize / 2;
 }
 
 unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -154,16 +154,17 @@ skip-if = (toolkit == 'gonk' && !debug) 
 [test_pannerNodeAbove.html]
 [test_pannerNodeChannelCount.html]
 [test_pannerNodeHRTFSymmetry.html]
 [test_pannerNodeTail.html]
 [test_pannerNode_maxDistance.html]
 [test_stereoPannerNode.html]
 [test_stereoPannerNodePassThrough.html]
 [test_periodicWave.html]
+[test_periodicWaveBandLimiting.html]
 [test_scriptProcessorNode.html]
 [test_scriptProcessorNodeChannelCount.html]
 [test_scriptProcessorNodePassThrough.html]
 [test_scriptProcessorNode_playbackTime1.html]
 [test_scriptProcessorNodeZeroInputOutput.html]
 [test_scriptProcessorNodeNotConnected.html]
 [test_singleSourceDest.html]
 [test_stereoPanningWithGain.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_periodicWaveBandLimiting.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<title>Test effect of band limiting on PeriodicWave signals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const sampleRate = 48000;
+const bufferSize = 12800;
+const epsilon = 0.01;
+
+// "All implementations must support arrays up to at least 8192", but the
+// linear interpolation of the current implementation distorts the higher
+// frequency components too much to pass this test.
+const frequencyIndexMax = 100;
+
+// A set of oscillators are created near the Nyquist frequency.
+// These are factors giving each oscillator frequency relative to the Nyquist.
+// The first is an octave below Nyquist and the last is just above.
+const OCTAVE_BELOW = 0;
+const HALF_BELOW = 1;
+const NEAR_BELOW = 2;
+const ABOVE = 3;
+const oscillatorFactors = [0.5, Math.sqrt(0.5), 0.99, 1.01];
+const oscillatorCount = oscillatorFactors.length;
+
+// Return magnitude relative to unit sine wave
+function magnitude(array) {
+  var mag = 0
+  for (var i = 0; i < array.length; ++i) {
+    sample = array[i];
+    mag += sample * sample;
+  }
+  return Math.sqrt(2 * mag / array.length);
+}
+
+function test_frequency_index(frequencyIndex) {
+
+  var context =
+    new OfflineAudioContext(oscillatorCount, bufferSize, sampleRate);
+
+  var merger = context.createChannelMerger(oscillatorCount);
+  merger.connect(context.destination);
+
+  var real = new Float32Array(frequencyIndex + 1);
+  real[frequencyIndex] = 1;
+  var image = new Float32Array(real.length);
+  var wave = context.createPeriodicWave(real, image);
+
+  for (var i = 0; i < oscillatorCount; ++i) {
+    var oscillator = context.createOscillator();
+    oscillator.frequency.value =
+      oscillatorFactors[i] * sampleRate / (2 * frequencyIndex);
+    oscillator.connect(merger, 0, i);
+    oscillator.setPeriodicWave(wave);
+    oscillator.start(0);
+  }
+
+  return context.startRendering().
+    then((buffer) => {
+      assert_equals(buffer.numberOfChannels, oscillatorCount);
+      var magnitudes = [];
+      for (var i = 0; i < oscillatorCount; ++i) {
+        magnitudes[i] = magnitude(buffer.getChannelData(i));
+      }
+      // Unaffected by band-limiting one octave below Nyquist.
+      assert_approx_equals(magnitudes[OCTAVE_BELOW], 1, epsilon,
+                           "magnitude with frequency octave below Nyquist");
+      // Still at least half the amplitude at half octave below Nyquist.
+      assert_greater_than(magnitudes[HALF_BELOW], 0.5 * (1 - epsilon),
+                          "magnitude with frequency half octave below Nyquist");
+      // Approaching zero or zero near Nyquist.
+      assert_less_than(magnitudes[NEAR_BELOW], 0.1,
+                       "magnitude with frequency near Nyquist");
+      assert_equals(magnitudes[ABOVE], 0,
+                   "magnitude with frequency above Nyquist");
+    });
+}
+
+// The 5/4 ratio with rounding up provides sampling across a range of
+// octaves and offsets within octaves.
+for (var frequencyIndex = 1;
+     frequencyIndex < frequencyIndexMax;
+     frequencyIndex = Math.floor((5 * frequencyIndex + 3) / 4)) {
+  promise_test(() => test_frequency_index(frequencyIndex),
+               "Frequency " + frequencyIndex);
+}
+</script>
--- a/dom/media/webspeech/synth/windows/SapiService.cpp
+++ b/dom/media/webspeech/synth/windows/SapiService.cpp
@@ -127,16 +127,18 @@ SapiCallback::OnSpeechEvent(const SPEVEN
     mTask->DispatchBoundary(NS_LITERAL_STRING("word"),
                             GetTickCount() - mStartingTime, mCurrentIndex);
     break;
   case SPEI_SENTENCE_BOUNDARY:
     mCurrentIndex = static_cast<ULONG>(speechEvent.lParam) - mTextOffset;
     mTask->DispatchBoundary(NS_LITERAL_STRING("sentence"),
                             GetTickCount() - mStartingTime, mCurrentIndex);
     break;
+  default:
+    break;
   }
 }
 
 // static
 void __stdcall
 SapiService::SpeechEventCallback(WPARAM aWParam, LPARAM aLParam)
 {
   nsRefPtr<SapiService> service = (SapiService*) aWParam;
@@ -175,17 +177,17 @@ SapiService::~SapiService()
 
 bool
 SapiService::Init()
 {
   MOZ_ASSERT(!mInitialized);
 
   if (Preferences::GetBool("media.webspeech.synth.test")) {
     // When enabled, we shouldn't add OS backend (Bug 1160844)
-    return nullptr;
+    return false;
   }
 
   if (FAILED(CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL, IID_ISpVoice,
                               getter_AddRefs(mSapiClient)))) {
     return false;
   }
 
   // Set interest for all the events we are interested in
--- a/dom/plugins/base/npapi.h
+++ b/dom/plugins/base/npapi.h
@@ -406,17 +406,19 @@ typedef enum {
 #endif
 #if defined(XP_MACOSX)
 #ifndef NP_NO_CARBON
   , NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */
 #endif
   , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */
   , NPNVsupportsUpdatedCocoaTextInputBool = 3002 /* TRUE if the browser supports the updated
                                                     Cocoa text input specification. */
+#endif
   , NPNVmuteAudioBool = 4000 /* Request that the browser wants to mute or unmute the plugin */
+#if defined(XP_MACOSX)
   , NPNVsupportsCompositingCoreAnimationPluginsBool = 74656 /* TRUE if the browser supports
                                                                CA model compositing */
 #endif
 } NPNVariable;
 
 typedef enum {
   NPNURLVCookie = 501,
   NPNURLVProxy
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -101,16 +101,18 @@ using mozilla::plugins::PluginModuleCont
 #include <android/log.h>
 #include "android_npapi.h"
 #include "ANPBase.h"
 #include "AndroidBridge.h"
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #endif
 
+#include "nsIAudioChannelAgent.h"
+
 using namespace mozilla;
 using namespace mozilla::plugins::parent;
 
 // We should make this const...
 static NPNetscapeFuncs sBrowserFuncs = {
   sizeof(sBrowserFuncs),
   (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR,
   _geturl,
@@ -2397,16 +2399,56 @@ NPError
       return NPERR_NO_ERROR;
     }
 
     case NPPVpluginUsesDOMForCursorBool: {
       bool useDOMForCursor = (result != nullptr);
       return inst->SetUsesDOMForCursor(useDOMForCursor);
     }
 
+    case NPPVpluginIsPlayingAudio: {
+      bool isMuted = !result;
+
+      nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*) npp->ndata;
+      MOZ_ASSERT(inst);
+
+      if (isMuted && !inst->HasAudioChannelAgent()) {
+        return NPERR_NO_ERROR;
+      }
+
+      nsCOMPtr<nsIAudioChannelAgent> agent;
+      nsresult rv = inst->GetOrCreateAudioChannelAgent(getter_AddRefs(agent));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NPERR_NO_ERROR;
+      }
+
+      MOZ_ASSERT(agent);
+
+      if (isMuted) {
+        rv = agent->NotifyStoppedPlaying();
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NPERR_NO_ERROR;
+        }
+      } else {
+        float volume = 0.0;
+        bool muted = true;
+        rv = agent->NotifyStartedPlaying(&volume, &muted);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NPERR_NO_ERROR;
+        }
+
+        rv = inst->WindowVolumeChanged(volume, muted);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NPERR_NO_ERROR;
+        }
+      }
+
+      return NPERR_NO_ERROR;
+    }
+
 #ifndef MOZ_WIDGET_ANDROID
     // On android, their 'drawing model' uses the same constant!
     case NPPVpluginDrawingModel: {
       if (inst) {
         inst->SetDrawingModel((NPDrawingModel)NS_PTR_TO_INT32(result));
         return NPERR_NO_ERROR;
       }
       else {
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -35,16 +35,17 @@
 #include "nsSize.h"
 #include "nsNetCID.h"
 #include "nsIContent.h"
 #include "nsVersionComparator.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "nsILoadContext.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
+#include "AudioChannelService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "ANPBase.h"
 #include <android/log.h>
 #include "android_npapi.h"
@@ -164,17 +165,17 @@ static std::map<NPP, nsNPAPIPluginInstan
 #endif
 
 using namespace mozilla;
 using namespace mozilla::plugins::parent;
 using namespace mozilla::layers;
 
 static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID);
 
-NS_IMPL_ISUPPORTS0(nsNPAPIPluginInstance)
+NS_IMPL_ISUPPORTS(nsNPAPIPluginInstance, nsIAudioChannelAgentCallback)
 
 nsNPAPIPluginInstance::nsNPAPIPluginInstance()
   : mDrawingModel(kDefaultDrawingModel)
 #ifdef MOZ_WIDGET_ANDROID
   , mANPDrawingModel(0)
   , mFullScreenOrientation(dom::eScreenOrientation_LandscapePrimary)
   , mWakeLocked(false)
   , mFullScreen(false)
@@ -248,16 +249,17 @@ nsNPAPIPluginInstance::~nsNPAPIPluginIns
 
 uint32_t nsNPAPIPluginInstance::gInUnsafePluginCalls = 0;
 
 void
 nsNPAPIPluginInstance::Destroy()
 {
   Stop();
   mPlugin = nullptr;
+  mAudioChannelAgent = nullptr;
 
 #if MOZ_WIDGET_ANDROID
   if (mContentSurface)
     mContentSurface->SetFrameAvailableCallback(nullptr);
 
   mContentTexture = nullptr;
   mContentSurface = nullptr;
 
@@ -1783,8 +1785,76 @@ nsNPAPIPluginInstance::GetRunID(uint32_t
 
   PluginLibrary* library = mPlugin->GetLibrary();
   if (!library) {
     return NS_ERROR_FAILURE;
   }
 
   return library->GetRunID(aRunID);
 }
+
+nsresult
+nsNPAPIPluginInstance::GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent)
+{
+  if (!mAudioChannelAgent) {
+    nsresult rv;
+    mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
+    if (NS_WARN_IF(!mAudioChannelAgent)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsPIDOMWindow> window = GetDOMWindow();
+    if (NS_WARN_IF(!window)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    rv = mAudioChannelAgent->Init(window->GetCurrentInnerWindow(),
+                                 (int32_t)AudioChannelService::GetDefaultAudioChannel(),
+                                 this);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  nsCOMPtr<nsIAudioChannelAgent> agent = mAudioChannelAgent;
+  agent.forget(aAgent);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowVolumeChanged(float aVolume, bool aMuted)
+{
+  // We just support mute/unmute
+  nsresult rv = SetMuted(aMuted);
+  NS_WARN_IF(NS_FAILED(rv));
+  return rv;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowAudioCaptureChanged()
+{
+  return NS_OK;
+}
+
+nsresult
+nsNPAPIPluginInstance::SetMuted(bool aIsMuted)
+{
+  if (RUNNING != mRunning)
+    return NS_OK;
+
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance informing plugin of mute state change this=%p\n",this));
+
+  if (!mPlugin || !mPlugin->GetLibrary())
+    return NS_ERROR_FAILURE;
+
+  NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+  if (!pluginFunctions->setvalue)
+    return NS_ERROR_FAILURE;
+
+  PluginDestructionGuard guard(this);
+
+  NPError error;
+  NPBool value = static_cast<NPBool>(aIsMuted);
+  NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVmuteAudioBool, &value), this,
+                          NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+  return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
+}
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -12,16 +12,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsITimer.h"
 #include "nsIPluginInstanceOwner.h"
 #include "nsIURI.h"
 #include "nsIChannel.h"
 #include "nsHashKeys.h"
 #include <prinrval.h>
 #include "js/TypeDecls.h"
+#include "nsIAudioChannelAgent.h"
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsAutoPtr.h"
 #include "nsIRunnable.h"
 #include "GLContextTypes.h"
 #include "AndroidSurfaceTexture.h"
 #include "AndroidBridge.h"
 #include <map>
 class PluginEventRunnable;
@@ -69,23 +70,24 @@ public:
   NPP npp;
   uint32_t id;
   nsCOMPtr<nsITimer> timer;
   void (*callback)(NPP npp, uint32_t timerID);
   bool inCallback;
   bool needUnschedule;
 };
 
-class nsNPAPIPluginInstance : public nsISupports
+class nsNPAPIPluginInstance final : public nsIAudioChannelAgentCallback
 {
 private:
   typedef mozilla::PluginLibrary PluginLibrary;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
 
   nsresult Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const nsACString& aMIMEType);
   nsresult Start();
   nsresult Stop();
   nsresult SetWindow(NPWindow* window);
   nsresult NewStreamFromPlugin(const char* type, const char* target, nsIOutputStream* *result);
   nsresult Print(NPPrint* platformPrint);
   nsresult HandleEvent(void* event, int16_t* result,
@@ -112,16 +114,25 @@ public:
   nsresult GetPluginAPIVersion(uint16_t* version);
   nsresult InvalidateRect(NPRect *invalidRect);
   nsresult InvalidateRegion(NPRegion invalidRegion);
   nsresult GetMIMEType(const char* *result);
   nsresult GetJSContext(JSContext* *outContext);
   nsPluginInstanceOwner* GetOwner();
   void SetOwner(nsPluginInstanceOwner *aOwner);
 
+  bool HasAudioChannelAgent() const
+  {
+    return !!mAudioChannelAgent;
+  }
+
+  nsresult GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent);
+
+  nsresult SetMuted(bool aIsMuted);
+
   nsNPAPIPlugin* GetPlugin();
 
   nsresult GetNPP(NPP * aNPP);
 
   NPError SetWindowless(bool aWindowless);
 
   NPError SetTransparent(bool aTransparent);
 
@@ -399,16 +410,18 @@ private:
 
   static uint32_t gInUnsafePluginCalls;
 
   // The arrays can only be released when the plugin instance is destroyed,
   // because the plugin, in in-process mode, might keep a reference to them.
   uint32_t mCachedParamLength;
   char **mCachedParamNames;
   char **mCachedParamValues;
+
+  nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
 };
 
 // On Android, we need to guard against plugin code leaking entries in the local
 // JNI ref table. See https://bugzilla.mozilla.org/show_bug.cgi?id=780831#c21
 #ifdef MOZ_WIDGET_ANDROID
   #define MAIN_THREAD_JNI_REF_GUARD mozilla::AutoLocalJNIFrame jniFrame
 #else
   #define MAIN_THREAD_JNI_REF_GUARD
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -82,16 +82,18 @@ child:
 
   intr NPP_GetValue_NPPVpluginScriptableNPObject()
     returns (nullable PPluginScriptableObject value, NPError result);
 
   intr NPP_SetValue_NPNVprivateModeBool(bool value) returns (NPError result);
   intr NPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId()
     returns (nsCString plug_id, NPError result);
 
+  intr NPP_SetValue_NPNVmuteAudioBool(bool muted) returns (NPError result);
+
   intr NPP_HandleEvent(NPRemoteEvent event)
     returns (int16_t handled);
   // special cases where we need to a shared memory buffer
   intr NPP_HandleEvent_Shmem(NPRemoteEvent event, Shmem buffer)
     returns (int16_t handled, Shmem rtnbuffer);
   // special cases where we need an iosurface
   intr NPP_HandleEvent_IOSurface(NPRemoteEvent event, uint32_t surfaceid)
     returns (int16_t handled);
@@ -145,16 +147,18 @@ parent:
   intr NPN_SetValue_NPPVpluginTransparent(bool transparent)
     returns (NPError result);
   intr NPN_SetValue_NPPVpluginUsesDOMForCursor(bool useDOMForCursor)
     returns (NPError result);
   intr NPN_SetValue_NPPVpluginDrawingModel(int drawingModel)
     returns (NPError result);
   intr NPN_SetValue_NPPVpluginEventModel(int eventModel)
     returns (NPError result);
+  intr NPN_SetValue_NPPVpluginIsPlayingAudio(bool isAudioPlaying)
+    returns (NPError result);
 
   intr NPN_GetURL(nsCString url, nsCString target)
     returns (NPError result);
   intr NPN_PostURL(nsCString url, nsCString target, nsCString buffer, bool file)
     returns (NPError result);
 
   /**
    * Covers both NPN_GetURLNotify and NPN_PostURLNotify.
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -621,16 +621,24 @@ PluginInstanceChild::NPN_SetValue(NPPVar
 
         PLUGIN_LOG_DEBUG(("  Plugin requested event model id # %i\n",
             eventModel));
 
         return rv;
     }
 #endif
 
+    case NPPVpluginIsPlayingAudio: {
+        NPError rv = NPERR_GENERIC_ERROR;
+        if (!CallNPN_SetValue_NPPVpluginIsPlayingAudio((NPBool)(intptr_t)aValue, &rv)) {
+            return NPERR_GENERIC_ERROR;
+        }
+        return rv;
+    }
+
     default:
         MOZ_LOG(GetPluginLog(), LogLevel::Warning,
                ("In PluginInstanceChild::NPN_SetValue: Unhandled NPPVariable %i (%s)",
                 (int) aVar, NPPVariableToString(aVar)));
         return NPERR_GENERIC_ERROR;
     }
 }
 
@@ -761,16 +769,30 @@ PluginInstanceChild::AnswerNPP_SetValue_
     }
 
     NPBool v = value;
     *result = mPluginIface->setvalue(GetNPP(), NPNVprivateModeBool, &v);
     return true;
 }
 
 bool
+PluginInstanceChild::AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value,
+                                                          NPError* result)
+{
+    if (!mPluginIface->setvalue) {
+        *result = NPERR_GENERIC_ERROR;
+        return true;
+    }
+
+    NPBool v = value;
+    *result = mPluginIface->setvalue(GetNPP(), NPNVmuteAudioBool, &v);
+    return true;
+}
+
+bool
 PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
                                            int16_t* handled)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
 
 #if defined(MOZ_X11) && defined(DEBUG)
     if (GraphicsExpose == event.event.type)
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -75,16 +75,18 @@ protected:
     virtual bool
     AnswerNPP_GetValue_NPPVpluginScriptableNPObject(PPluginScriptableObjectChild** value,
                                                     NPError* result) override;
     virtual bool
     AnswerNPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId(nsCString* aPlugId,
                                                            NPError* aResult) override;
     virtual bool
     AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value, NPError* result) override;
+    virtual bool
+    AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value, NPError* result) override;
 
     virtual bool
     AnswerNPP_HandleEvent(const NPRemoteEvent& event, int16_t* handled) override;
     virtual bool
     AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
                                 Shmem&& mem,
                                 int16_t* handled,
                                 Shmem* rtnmem) override;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -436,16 +436,25 @@ PluginInstanceParent::AnswerNPN_SetValue
     return true;
 #else
     *result = NPERR_GENERIC_ERROR;
     return true;
 #endif
 }
 
 bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(
+    const bool& isAudioPlaying, NPError* result)
+{
+    *result = mNPNIface->setvalue(mNPP, NPPVpluginIsPlayingAudio,
+                                  (void*)(intptr_t)isAudioPlaying);
+    return true;
+}
+
+bool
 PluginInstanceParent::AnswerNPN_GetURL(const nsCString& url,
                                        const nsCString& target,
                                        NPError* result)
 {
     *result = mNPNIface->geturl(mNPP,
                                 NullableStringGet(url),
                                 NullableStringGet(target));
     return true;
@@ -1137,25 +1146,32 @@ PluginInstanceParent::NPP_GetValue(NPPVa
                 (int) aVariable, NPPVariableToString(aVariable)));
         return NPERR_GENERIC_ERROR;
     }
 }
 
 NPError
 PluginInstanceParent::NPP_SetValue(NPNVariable variable, void* value)
 {
+    NPError result;
     switch (variable) {
     case NPNVprivateModeBool:
-        NPError result;
         if (!CallNPP_SetValue_NPNVprivateModeBool(*static_cast<NPBool*>(value),
                                                   &result))
             return NPERR_GENERIC_ERROR;
 
         return result;
 
+    case NPNVmuteAudioBool:
+        if (!CallNPP_SetValue_NPNVmuteAudioBool(*static_cast<NPBool*>(value),
+                                                &result))
+            return NPERR_GENERIC_ERROR;
+
+        return result;
+
     default:
         NS_ERROR("Unhandled NPNVariable in NPP_SetValue");
         MOZ_LOG(GetPluginLog(), LogLevel::Warning,
                ("In PluginInstanceParent::NPP_SetValue: Unhandled NPNVariable %i (%s)",
                 (int) variable, NPNVariableToString(variable)));
         return NPERR_GENERIC_ERROR;
     }
 }
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -123,16 +123,19 @@ public:
     AnswerNPN_SetValue_NPPVpluginUsesDOMForCursor(const bool& useDOMForCursor,
                                                   NPError* result) override;
     virtual bool
     AnswerNPN_SetValue_NPPVpluginDrawingModel(const int& drawingModel,
                                               NPError* result) override;
     virtual bool
     AnswerNPN_SetValue_NPPVpluginEventModel(const int& eventModel,
                                              NPError* result) override;
+    virtual bool
+    AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(const bool& isAudioPlaying,
+                                                NPError* result) override;
 
     virtual bool
     AnswerNPN_GetURL(const nsCString& url, const nsCString& target,
                      NPError *result) override;
 
     virtual bool
     AnswerNPN_PostURL(const nsCString& url, const nsCString& target,
                       const nsCString& buffer, const bool& file,
--- a/dom/plugins/test/testplugin/README
+++ b/dom/plugins/test/testplugin/README
@@ -414,8 +414,19 @@ x86-only on some OSes:
   as evil plugins or extensions might do.
 
 == HiDPI Mode ==
 
 * queryContentsScaleFactor()
 Returns the contents scale factor. On platforms without support for this query
 always returns 1.0 (a double value). Likewise on hardware without HiDPI mode
 support.
+
+== Plugin audio channel support ==
+
+* startAudioPlayback()
+Simulates the plugin starting to play back audio.
+
+* stopAudioPlayback()
+Simulates the plugin stopping to play back audio.
+
+* audioMuted()
+Returns the last value set by NPP_SetValue(NPNVmuteAudioBool).
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -163,16 +163,19 @@ static bool getWindowPosition(NPObject* 
 static bool constructObject(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool setSitesWithData(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getLastKeyText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getNPNVdocumentOrigin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool queryContentsScaleFactor(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool echoString(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool startAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool stopAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool getAudioMuted(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 
 static const NPUTF8* sPluginMethodIdentifierNames[] = {
   "npnEvaluateTest",
   "npnInvokeTest",
   "npnInvokeDefaultTest",
   "setUndefinedValueTest",
   "identifierToStringTest",
   "timerTest",
@@ -229,16 +232,19 @@ static const NPUTF8* sPluginMethodIdenti
   "constructObject",
   "setSitesWithData",
   "setSitesWithDataCapabilities",
   "getLastKeyText",
   "getNPNVdocumentOrigin",
   "getMouseUpEventCount",
   "queryContentsScaleFactor",
   "echoString",
+  "startAudioPlayback",
+  "stopAudioPlayback",
+  "audioMuted",
 };
 static NPIdentifier sPluginMethodIdentifiers[MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames)];
 static const ScriptableFunction sPluginMethodFunctions[] = {
   npnEvaluateTest,
   npnInvokeTest,
   npnInvokeDefaultTest,
   setUndefinedValueTest,
   identifierToStringTest,
@@ -296,16 +302,19 @@ static const ScriptableFunction sPluginM
   constructObject,
   setSitesWithData,
   setSitesWithDataCapabilities,
   getLastKeyText,
   getNPNVdocumentOrigin,
   getMouseUpEventCount,
   queryContentsScaleFactor,
   echoString,
+  startAudioPlayback,
+  stopAudioPlayback,
+  getAudioMuted,
 };
 
 static_assert(MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
               MOZ_ARRAY_LENGTH(sPluginMethodFunctions),
               "Arrays should have the same size");
 
 static const NPUTF8* sPluginPropertyIdentifierNames[] = {
   "propertyAndMethod"
@@ -779,16 +788,18 @@ NPP_New(NPMIMEType pluginType, NPP insta
   instanceData->throwOnNextInvoke = false;
   instanceData->runScriptOnPaint = false;
   instanceData->dontTouchElement = false;
   instanceData->testrange = nullptr;
   instanceData->hasWidget = false;
   instanceData->npnNewStream = false;
   instanceData->invalidateDuringPaint = false;
   instanceData->slowPaint = false;
+  instanceData->playingAudio = false;
+  instanceData->audioMuted = false;
   instanceData->writeCount = 0;
   instanceData->writeReadyCount = 0;
   memset(&instanceData->window, 0, sizeof(instanceData->window));
   instanceData->crashOnDestroy = false;
   instanceData->cleanupWidget = true; // only used by nptest_gtk
   instanceData->topLevelWindowActivationState = ACTIVATION_STATE_UNKNOWN;
   instanceData->topLevelWindowActivationEventCount = 0;
   instanceData->focusState = ACTIVATION_STATE_UNKNOWN;
@@ -1435,16 +1446,21 @@ NPP_GetValue(NPP instance, NPPVariable v
 NPError
 NPP_SetValue(NPP instance, NPNVariable variable, void* value)
 {
   if (variable == NPNVprivateModeBool) {
     InstanceData* instanceData = (InstanceData*)(instance->pdata);
     instanceData->lastReportedPrivateModeState = bool(*static_cast<NPBool*>(value));
     return NPERR_NO_ERROR;
   }
+  if (variable == NPNVmuteAudioBool) {
+    InstanceData* instanceData = (InstanceData*)(instance->pdata);
+    instanceData->audioMuted = bool(*static_cast<NPBool*>(value));
+    return NPERR_NO_ERROR;
+  }
   return NPERR_GENERIC_ERROR;
 }
 
 void
 NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status, void* notifyData)
 {
   if (notifyData) {
     URLNotifyData* nd = static_cast<URLNotifyData*>(notifyData);
@@ -3702,8 +3718,50 @@ bool echoString(NPObject* npobj, const N
   }
 
   std::copy(arg.UTF8Characters, arg.UTF8Characters + arg.UTF8Length, buffer);
   STRINGN_TO_NPVARIANT(buffer, arg.UTF8Length, *result);
 
   return true;
 }
 
+static bool
+toggleAudioPlayback(NPObject* npobj, uint32_t argCount, bool playingAudio, NPVariant* result)
+{
+  if (argCount != 0) {
+    return false;
+  }
+
+  NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+  InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+  id->playingAudio = playingAudio;
+
+  NPN_SetValue(npp, NPPVpluginIsPlayingAudio, (void*)playingAudio);
+
+  VOID_TO_NPVARIANT(*result);
+  return true;
+}
+
+static bool
+startAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  return toggleAudioPlayback(npobj, argCount, true, result);
+}
+
+static bool
+stopAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  return toggleAudioPlayback(npobj, argCount, false, result);
+}
+
+static bool
+getAudioMuted(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  if (argCount != 0) {
+    return false;
+  }
+
+  NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+  InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+  BOOLEAN_TO_NPVARIANT(id->audioMuted, *result);
+  return true;
+}
+
--- a/dom/plugins/test/testplugin/nptest.h
+++ b/dom/plugins/test/testplugin/nptest.h
@@ -101,16 +101,18 @@ typedef struct InstanceData {
   bool throwOnNextInvoke;
   bool runScriptOnPaint;
   bool dontTouchElement;
   uint32_t timerID[2];
   bool timerTestResult;
   bool asyncCallbackResult;
   bool invalidateDuringPaint;
   bool slowPaint;
+  bool playingAudio;
+  bool audioMuted;
   int32_t winX;
   int32_t winY;
   int32_t lastMouseX;
   int32_t lastMouseY;
   int32_t widthAtLastPaint;
   int32_t paintCount;
   int32_t writeCount;
   int32_t writeReadyCount;
--- a/dom/push/PushServiceChildPreload.jsm
+++ b/dom/push/PushServiceChildPreload.jsm
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+this.EXPORTED_SYMBOLS = [];
+
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "swm",
                                    "@mozilla.org/serviceworkers/manager;1",
--- a/dom/webidl/IDBCursor.webidl
+++ b/dom/webidl/IDBCursor.webidl
@@ -34,12 +34,13 @@ interface IDBCursor {
 
     [Throws]
     void       continue (optional any key);
 
     [Throws]
     IDBRequest delete ();
 };
 
+[Exposed=(Window,Worker)]
 interface IDBCursorWithValue : IDBCursor {
     [Throws]
     readonly    attribute any value;
 };
--- a/dom/workers/Performance.cpp
+++ b/dom/workers/Performance.cpp
@@ -59,16 +59,26 @@ Performance::GetPerformanceTimingFromStr
   if (aProperty.EqualsLiteral("navigationStart")) {
     return mWorkerPrivate->NowBaseTimeHighRes();
   }
 
   MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync");
   return 0;
 }
 
+void
+Performance::InsertUserEntry(PerformanceEntry* aEntry)
+{
+  if (mWorkerPrivate->PerformanceLoggingEnabled()) {
+    PerformanceBase::LogEntry(aEntry,
+                              NS_ConvertUTF16toUTF8(mWorkerPrivate->ScriptURL()));
+  }
+  PerformanceBase::InsertUserEntry(aEntry);
+}
+
 DOMHighResTimeStamp
 Performance::DeltaFromNavigationStart(DOMHighResTimeStamp aTime)
 {
   if (aTime == 0) {
     return 0;
   }
 
   return aTime - mWorkerPrivate->NowBaseTimeHighRes();
--- a/dom/workers/Performance.h
+++ b/dom/workers/Performance.h
@@ -21,16 +21,18 @@ class WorkerPrivate;
 class Performance final : public PerformanceBase
 {
 public:
   explicit Performance(WorkerPrivate* aWorkerPrivate);
 
 private:
   ~Performance();
 
+  void InsertUserEntry(PerformanceEntry* aEntry) override;
+
   WorkerPrivate* mWorkerPrivate;
 
 public:
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL (public APIs)
   DOMHighResTimeStamp Now() const override;
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -153,16 +153,17 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 
 
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
 #define DUMP_CONTROLLED_BY_PREF 1
 #define PREF_DOM_WINDOW_DUMP_ENABLED "browser.dom.window.dump.enabled"
 #endif
 
 #define PREF_DOM_CACHES_ENABLED        "dom.caches.enabled"
 #define PREF_DOM_CACHES_TESTING_ENABLED "dom.caches.testing.enabled"
+#define PREF_WORKERS_PERFORMANCE_LOGGING_ENABLED "dom.performance.enable_user_timing_logging"
 #define PREF_DOM_WORKERNOTIFICATION_ENABLED  "dom.webnotifications.enabled"
 #define PREF_WORKERS_LATEST_JS_VERSION "dom.workers.latestJSVersion"
 #define PREF_INTL_ACCEPT_LANGUAGES     "intl.accept_languages"
 #define PREF_SERVICEWORKERS_ENABLED    "dom.serviceWorkers.enabled"
 #define PREF_SERVICEWORKERS_TESTING_ENABLED "dom.serviceWorkers.testing.enabled"
 #define PREF_INTERCEPTION_ENABLED      "dom.serviceWorkers.interception.enabled"
 #define PREF_INTERCEPTION_OPAQUE_ENABLED "dom.serviceWorkers.interception.opaque.enabled"
 
@@ -1937,16 +1938,20 @@ RuntimeService::Init()
                                   PREF_INTERCEPTION_OPAQUE_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_OPAQUE_ENABLED))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
                                   PREF_DOM_CACHES_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_CACHES_TESTING))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
+                                  PREF_WORKERS_PERFORMANCE_LOGGING_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_PERFORMANCE_LOGGING_ENABLED))) ||
+      NS_FAILED(Preferences::RegisterCallbackAndCall(
+                                  WorkerPrefChanged,
                                   PREF_SERVICEWORKERS_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS_TESTING))) ||
       NS_FAILED(Preferences::RegisterCallback(LoadRuntimeOptions,
                                               PREF_JS_OPTIONS_PREFIX,
                                               nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                                    LoadRuntimeOptions,
                                                    PREF_WORKERS_OPTIONS_PREFIX,
@@ -2144,16 +2149,20 @@ RuntimeService::Cleanup()
                                   PREF_SERVICEWORKERS_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS_TESTING))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_DOM_CACHES_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_CACHES_TESTING))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
+                                  PREF_WORKERS_PERFORMANCE_LOGGING_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_PERFORMANCE_LOGGING_ENABLED))) ||
+        NS_FAILED(Preferences::UnregisterCallback(
+                                  WorkerPrefChanged,
                                   PREF_INTERCEPTION_OPAQUE_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_OPAQUE_ENABLED))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_INTERCEPTION_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_ENABLED))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
@@ -2703,16 +2712,17 @@ RuntimeService::WorkerPrefChanged(const 
 
   const WorkerPreference key =
     static_cast<WorkerPreference>(reinterpret_cast<uintptr_t>(aClosure));
 
   switch (key) {
     case WORKERPREF_DOM_CACHES:
     case WORKERPREF_DOM_CACHES_TESTING:
     case WORKERPREF_DOM_WORKERNOTIFICATION:
+    case WORKERPREF_PERFORMANCE_LOGGING_ENABLED:
 #ifdef DUMP_CONTROLLED_BY_PREF
     case WORKERPREF_DUMP:
 #endif
     case WORKERPREF_INTERCEPTION_ENABLED:
     case WORKERPREF_INTERCEPTION_OPAQUE_ENABLED:
     case WORKERPREF_SERVICEWORKERS:
     case WORKERPREF_SERVICEWORKERS_TESTING:
       sDefaultPreferences[key] = Preferences::GetBool(aPrefName, false);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1560,17 +1560,17 @@ public:
     // they show up in the error console.
     if (!JSREPORT_IS_WARNING(aFlags)) {
       // First fire an ErrorEvent at the worker.
       RootedDictionary<ErrorEventInit> init(aCx);
       init.mMessage = aMessage;
       init.mFilename = aFilename;
       init.mLineno = aLineNumber;
       init.mCancelable = true;
-      init.mBubbles = true;
+      init.mBubbles = false;
 
       if (aTarget) {
         nsRefPtr<ErrorEvent> event =
           ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
         event->SetTrusted(true);
 
         nsEventStatus status = nsEventStatus_eIgnore;
         aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1317,16 +1317,23 @@ public:
   bool
   DOMCachesTestingEnabled() const
   {
     AssertIsOnWorkerThread();
     return mPreferences[WORKERPREF_DOM_CACHES_TESTING];
   }
 
   bool
+  PerformanceLoggingEnabled() const
+  {
+    AssertIsOnWorkerThread();
+    return mPreferences[WORKERPREF_PERFORMANCE_LOGGING_ENABLED];
+  }
+
+  bool
   OnLine() const
   {
     AssertIsOnWorkerThread();
     return mOnLine;
   }
 
   void
   StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult);
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -200,16 +200,17 @@ enum WorkerPreference
   WORKERPREF_DUMP = 0, // browser.dom.window.dump.enabled
   WORKERPREF_DOM_CACHES, // dom.caches.enabled
   WORKERPREF_SERVICEWORKERS, // dom.serviceWorkers.enabled
   WORKERPREF_INTERCEPTION_ENABLED, // dom.serviceWorkers.interception.enabled
   WORKERPREF_DOM_WORKERNOTIFICATION, // dom.webnotifications.workers.enabled
   WORKERPREF_DOM_CACHES_TESTING, // dom.caches.testing.enabled
   WORKERPREF_SERVICEWORKERS_TESTING, // dom.serviceWorkers.testing.enabled
   WORKERPREF_INTERCEPTION_OPAQUE_ENABLED, // dom.serviceWorkers.interception.opaque.enabled
+  WORKERPREF_PERFORMANCE_LOGGING_ENABLED, // dom.performance.enable_user_timing_logging
   WORKERPREF_COUNT
 };
 
 // Implemented in WorkerPrivate.cpp
 
 struct WorkerLoadInfo
 {
   // All of these should be released in WorkerPrivateParent::ForgetMainThreadObjects.
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/1158031.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+
+function boom()
+{
+    var w = new Worker("data:text/javascript;charset=UTF-8,");
+    w.postMessage(new Blob([], {}));
+}
+
+</script>
+<body onload="boom();"></body>
--- a/dom/workers/test/crashtests.list
+++ b/dom/workers/test/crashtests.list
@@ -1,1 +1,2 @@
 load 943516.html
+load 1158031.html
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -127,16 +127,18 @@ var interfaceNamesInGlobalScope =
     "FileReaderSync",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FormData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Headers",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBCursor",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursorWithValue",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBDatabase",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBFactory",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBIndex",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBKeyRange",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -119,16 +119,18 @@ var interfaceNamesInGlobalScope =
     "FileReaderSync",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FormData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Headers",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBCursor",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursorWithValue",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBDatabase",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBFactory",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBIndex",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBKeyRange",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/xbl/nsXBLDocumentInfo.cpp
+++ b/dom/xbl/nsXBLDocumentInfo.cpp
@@ -72,17 +72,17 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsX
       iter.UserData()->Trace(aCallbacks, aClosure);
     }
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 static void
 UnmarkXBLJSObject(JS::GCCellPtr aPtr, const char* aName, void* aClosure)
 {
-  JS::ExposeObjectToActiveJS(aPtr.toObject());
+  JS::ExposeObjectToActiveJS(&aPtr.as<JSObject>());
 }
 
 static PLDHashOperator
 UnmarkProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
 {
   aProto->Trace(TraceCallbackFunc(UnmarkXBLJSObject), nullptr);
   return PL_DHASH_NEXT;
 }
--- a/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp
+++ b/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp
@@ -102,14 +102,14 @@ nsresult EvaluateAdminConfigScript(const
     }
 
     AutoSafeJSContext cx;
     JSAutoCompartment ac(cx, autoconfigSb);
 
     nsAutoCString script(js_buffer, length);
     JS::RootedValue v(cx);
     rv = xpc->EvalInSandboxObject(NS_ConvertUTF8toUTF16(script), filename, cx,
-                                  autoconfigSb, &v);
+                                  autoconfigSb, JSVERSION_LATEST, &v);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -520,33 +520,16 @@ GfxPatternToCairoPattern(const Pattern& 
 
       MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
       GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
       cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
 
       matrix = &pattern.mMatrix;
 
       const std::vector<GradientStop>& stops = cairoStops->GetStops();
-      if (stops.size() >= 2 && stops.front().offset == stops.back().offset) {
-        // Certain Cairo backends that use pixman to implement gradients can have jagged
-        // edges occur with hard stops. Such hard stops are used for implementing certain
-        // types of CSS borders. Work around this by turning these hard-stops into half-pixel
-        // gradients to anti-alias them. See bug 1033375
-        Matrix patternToDevice = aTransform * pattern.mMatrix;
-        Float gradLength = (patternToDevice * pattern.mEnd - patternToDevice * pattern.mBegin).Length();
-        if (gradLength > 0) {
-          Float aaOffset = 0.25 / gradLength;
-          CairoPatternAddGradientStop(pat, stops.front(), -aaOffset);
-          for (size_t i = 1; i < stops.size()-1; ++i) {
-            CairoPatternAddGradientStop(pat, stops[i]);
-          }
-          CairoPatternAddGradientStop(pat, stops.back(), aaOffset);
-          break;
-        }
-      }
       for (size_t i = 0; i < stops.size(); ++i) {
         CairoPatternAddGradientStop(pat, stops[i]);
       }
 
       break;
     }
     case PatternType::RADIAL_GRADIENT:
     {
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -616,17 +616,19 @@ Factory::CreateDualDrawTargetForD3D10Tex
 }
 
 void
 Factory::SetDirect3D10Device(ID3D10Device1 *aDevice)
 {
   // do not throw on failure; return error codes and disconnect the device
   // On Windows 8 error codes are the default, but on Windows 7 the
   // default is to throw (or perhaps only with some drivers?)
-  aDevice->SetExceptionMode(0);
+  if (aDevice) {
+    aDevice->SetExceptionMode(0);
+  }
   mD3D10Device = aDevice;
 }
 
 ID3D10Device1*
 Factory::GetDirect3D10Device()
 {
 #ifdef DEBUG
   if (mD3D10Device) {
--- a/gfx/cairo/cairo/src/cairo-rectangle.c
+++ b/gfx/cairo/cairo/src/cairo-rectangle.c
@@ -208,18 +208,17 @@ cairo_bool_t
 	    t1 = B1x - P1x;
 	    t2 = B2x - P1x;
 	} else {
 	    t1 = P1x - B2x;
 	    t2 = P1x - B1x;
 	    xlen = - xlen;
 	}
 
-	if ((t1 < 0 || t1 > xlen) &&
-	    (t2 < 0 || t2 > xlen))
+        if (t1 > xlen || t2 < 0)
 	    return FALSE;
     } else {
 	/* Fully vertical line -- check that X is in bounds */
 	if (P1x < B1x || P1x > B2x)
 	    return FALSE;
     }
 
     if (ylen) {
@@ -227,18 +226,17 @@ cairo_bool_t
 	    t3 = B1y - P1y;
 	    t4 = B2y - P1y;
 	} else {
 	    t3 = P1y - B2y;
 	    t4 = P1y - B1y;
 	    ylen = - ylen;
 	}
 
-	if ((t3 < 0 || t3 > ylen) &&
-	    (t4 < 0 || t4 > ylen))
+        if (t3 > ylen || t4 < 0)
 	    return FALSE;
     } else {
 	/* Fully horizontal line -- check Y */
 	if (P1y < B1y || P1y > B2y)
 	    return FALSE;
     }
 
     /* If we had a horizontal or vertical line, then it's already been checked */
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -337,21 +337,21 @@ TextureClient::CreateForDrawing(ISurface
 
 #if defined(MOZ_WIDGET_GONK) || defined(XP_WIN)
   int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
 #endif
 
 #ifdef XP_WIN
   LayersBackend parentBackend = aAllocator->GetCompositorBackendType();
   if (parentBackend == LayersBackend::LAYERS_D3D11 &&
-      (aMoz2DBackend == gfx::BackendType::DIRECT2D ||
-        aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) &&
-      gfxWindowsPlatform::GetPlatform()->GetD3D10Device() &&
+      ((aMoz2DBackend == gfx::BackendType::DIRECT2D && Factory::GetDirect3D10Device()) ||
+       (aMoz2DBackend == gfx::BackendType::DIRECT2D1_1 && Factory::GetDirect3D11Device())) &&
       aSize.width <= maxTextureSize &&
-      aSize.height <= maxTextureSize) {
+      aSize.height <= maxTextureSize)
+  {
     texture = new TextureClientD3D11(aAllocator, aFormat, aTextureFlags);
   }
   if (parentBackend == LayersBackend::LAYERS_D3D9 &&
       aMoz2DBackend == gfx::BackendType::CAIRO &&
       aAllocator->IsSameProcess() &&
       aSize.width <= maxTextureSize &&
       aSize.height <= maxTextureSize &&
       NS_IsMainThread()) {
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -494,22 +494,16 @@ gfxASurface::GetSubpixelAntialiasingEnab
       return false;
 #ifdef MOZ_TREE_CAIRO
     return cairo_surface_get_subpixel_antialiasing(mSurface) == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED;
 #else
     return true;
 #endif
 }
 
-gfxMemoryLocation
-gfxASurface::GetMemoryLocation() const
-{
-    return gfxMemoryLocation::IN_PROCESS_HEAP;
-}
-
 int32_t
 gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
 {
     switch (format) {
         case gfxImageFormat::ARGB32:
         case gfxImageFormat::RGB24:
             return 4;
         case gfxImageFormat::RGB16_565:
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -154,22 +154,16 @@ public:
     // is capable of measuring its own size accurately.  If not, the caller
     // must fall back to a computed size.  (Note that gfxASurface can actually
     // measure itself, but we must |return false| here because it serves as the
     // (conservative) default for all the sub-classes.  Therefore, this
     // function should only be called on a |gfxASurface*| that actually points
     // to a sub-class of gfxASurface.)
     virtual bool SizeOfIsMeasured() const { return false; }
 
-    /**
-     * Where does this surface's memory live?  By default, we say it's in this
-     * process's heap.
-     */
-    virtual gfxMemoryLocation GetMemoryLocation() const;
-
     static int32_t BytePerPixelFromFormat(gfxImageFormat format);
 
     virtual const mozilla::gfx::IntSize GetSize() const;
 
     virtual mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
 
     void SetOpaqueRect(const gfxRect& aRect);
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2232,17 +2232,17 @@ gfxPlatform::ShouldUseLayersAcceleration
       InSafeMode() ||
       (acceleratedEnv && *acceleratedEnv == '0'))
   {
     return false;
   }
   if (gfxPrefs::LayersAccelerationForceEnabled()) {
     return true;
   }
-  if (gfxPlatform::GetPlatform()->AccelerateLayersByDefault()) {
+  if (AccelerateLayersByDefault()) {
     return true;
   }
   if (acceleratedEnv && *acceleratedEnv != '0') {
     return true;
   }
   return false;
 }
 
--- a/gfx/thebes/gfxTypes.h
+++ b/gfx/thebes/gfxTypes.h
@@ -87,20 +87,9 @@ enum class gfxSurfaceType {
 
 enum class gfxContentType {
   COLOR       = 0x1000,
   ALPHA       = 0x2000,
   COLOR_ALPHA = 0x3000,
   SENTINEL    = 0xffff
 };
 
-/**
-  * The memory used by a gfxASurface (as reported by KnownMemoryUsed()) can
-  * either live in this process's heap, in this process but outside the
-  * heap, or in another process altogether.
-  */
-enum class gfxMemoryLocation {
-  IN_PROCESS_HEAP,
-  IN_PROCESS_NONHEAP,
-  OUT_OF_PROCESS
-};
-
 #endif /* GFX_TYPES_H */
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -368,39 +368,45 @@ public:
                               gfxWindowsPlatform::sD3D9SharedTextureUsed,
                               "Memory used for D3D9 shared textures");
   }
 };
 
 NS_IMPL_ISUPPORTS(D3D9SharedTextureReporter, nsIMemoryReporter)
 
 gfxWindowsPlatform::gfxWindowsPlatform()
-  : mD3D11DeviceInitialized(false)
+  : mRenderMode(RENDER_GDI)
   , mIsWARP(false)
   , mHasDeviceReset(false)
   , mDoesD3D11TextureSharingWork(false)
+  , mAcceleration(FeatureStatus::Unused)
   , mD3D11Status(FeatureStatus::Unused)
   , mD2DStatus(FeatureStatus::Unused)
+  , mD2D1Status(FeatureStatus::Unused)
 {
     mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
     mUseClearTypeAlways = UNINITIALIZED_VALUE;
 
-    mUsingGDIFonts = false;
-
     /* 
      * Initialize COM 
      */ 
     CoInitialize(nullptr); 
 
     RegisterStrongMemoryReporter(new GfxD2DVramReporter());
 
-    if (gfxPrefs::Direct2DUse1_1()) {
-      InitD3D11Devices();
+    // Set up the D3D11 feature levels we can ask for.
+    if (IsWin8OrLater()) {
+      mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
     }
+    mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
+    mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
+    mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
+    mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
 
+    InitializeDevices();
     UpdateRenderMode();
 
     RegisterStrongMemoryReporter(new GPUAdapterReporter());
     RegisterStrongMemoryReporter(new D3D11TextureReporter());
     RegisterStrongMemoryReporter(new D3D9TextureReporter());
     RegisterStrongMemoryReporter(new D3D9SurfaceImageReporter());
     RegisterStrongMemoryReporter(new D3D9SharedTextureReporter());
 }
@@ -433,171 +439,116 @@ bool
 gfxWindowsPlatform::CanUseHardwareVideoDecoding()
 {
     if (!gfxPrefs::LayersPreferD3D9() && !mDoesD3D11TextureSharingWork) {
         return false;
     }
     return !IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
 }
 
-FeatureStatus
-gfxWindowsPlatform::InitD2DSupport()
-{
-#ifdef CAIRO_HAS_D2D_SURFACE
-  bool d2dBlocked = false;
-  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
-  if (gfxInfo) {
-    int32_t status;
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
-      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-        d2dBlocked = true;
-      }
-    }
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
-      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-        d2dBlocked = true;
-      }
-    }
-  }
-
-  // If D2D is blocked or D3D9 is prefered, and D2D is not force-enabled, then
-  // we don't attempt to use D2D.
-  if (!gfxPrefs::Direct2DForceEnabled()) {
-    if (d2dBlocked) {
-      return FeatureStatus::Blacklisted;
-    }
-    if (gfxPrefs::LayersPreferD3D9()) {
-      return FeatureStatus::Disabled;
-    }
-  }
-
-  // Do not ever try to use D2D if it's explicitly disabled or if we're not
-  // using DWrite fonts.
-  if (gfxPrefs::Direct2DDisabled() || mUsingGDIFonts) {
-    return FeatureStatus::Disabled;
-  }
-
-  if (!IsVistaOrLater() || !GetD3D11Device()) {
-    return FeatureStatus::Unavailable;
-  }
-  if (!mDoesD3D11TextureSharingWork) {
-    return FeatureStatus::Failed;
-  }
-  if (InSafeMode()) {
-    return FeatureStatus::Blocked;
-  }
-
-  VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled());
-  if (!mD3D10Device || !GetD3D11Device()) {
-    return FeatureStatus::Failed;
-  }
-
-  mRenderMode = RENDER_DIRECT2D;
-  mUseDirectWrite = true;
-  return FeatureStatus::Available;
-#else
-  return FeatureStatus::Unavailable;
-#endif
-}
-
-void
+bool
 gfxWindowsPlatform::InitDWriteSupport()
 {
-#ifdef CAIRO_HAS_DWRITE_FONT
-  // Enable when it's preffed on -and- we're using Vista or higher. Or when
-  // we're going to use D2D.
-  if (mDWriteFactory || (!mUseDirectWrite || !IsVistaOrLater())) {
-    return;
-  }
+  MOZ_ASSERT(!mDWriteFactory && IsVistaOrLater());
 
   mozilla::ScopedGfxFeatureReporter reporter("DWrite");
   decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*)
       GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory");
-
   if (!createDWriteFactory) {
-    return;
+    return false;
   }
 
   // I need a direct pointer to be able to cast to IUnknown**, I also need to
   // remember to release this because the nsRefPtr will AddRef it.
-  IDWriteFactory *factory;
+  RefPtr<IDWriteFactory> factory;
   HRESULT hr = createDWriteFactory(
       DWRITE_FACTORY_TYPE_SHARED,
       __uuidof(IDWriteFactory),
-      reinterpret_cast<IUnknown**>(&factory));
+      (IUnknown **)((IDWriteFactory **)byRef(factory)));
+  if (FAILED(hr) || !factory) {
+    return false;
+  }
+
+  mDWriteFactory = factory;
 
-  if (SUCCEEDED(hr) && factory) {
-    mDWriteFactory = factory;
-    factory->Release();
-    hr = mDWriteFactory->CreateTextAnalyzer(getter_AddRefs(mDWriteAnalyzer));
+  SetupClearTypeParams();
+  reporter.SetSuccessful();
+  return true;
+}
+
+bool
+gfxWindowsPlatform::HandleDeviceReset()
+{
+  DeviceResetReason resetReason = DeviceResetReason::OK;
+  if (!DidRenderingDeviceReset(&resetReason)) {
+    return false;
   }
 
-  SetupClearTypeParams();
+  Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
+
+  // Remove devices and adapters.
+  mD3D10Device = nullptr;
+  mD3D11Device = nullptr;
+  mD3D11ContentDevice = nullptr;
+  mD3D11ImageBridgeDevice = nullptr;
+  mAdapter = nullptr;
+  Factory::SetDirect3D11Device(nullptr);
+  Factory::SetDirect3D10Device(nullptr);
+
+  // Reset local state. Note: we leave feature status variables as-is. They
+  // will be recomputed by InitializeDevices().
+  mIsWARP = false;
+  mHasDeviceReset = false;
+  mDoesD3D11TextureSharingWork = false;
+  mDeviceResetReason = DeviceResetReason::OK;
+
+  imgLoader::Singleton()->ClearCache(true);
+  imgLoader::Singleton()->ClearCache(false);
+  gfxAlphaBoxBlur::ShutdownBlurCache();
 
-  if (hr == S_OK) {
-    reporter.SetSuccessful();
+  InitializeDevices();
+  return true;
+}
+
+void
+gfxWindowsPlatform::UpdateBackendPrefs()
+{
+  uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
+  uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
+  BackendType defaultBackend = BackendType::CAIRO;
+  if (GetD2DStatus() == FeatureStatus::Available) {
+    mRenderMode = RENDER_DIRECT2D;
+    canvasMask |= BackendTypeBit(BackendType::DIRECT2D);
+    contentMask |= BackendTypeBit(BackendType::DIRECT2D);
+    if (GetD2D1Status() == FeatureStatus::Available) {
+      contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
+      canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
+      defaultBackend = BackendType::DIRECT2D1_1;
+    } else {
+      defaultBackend = BackendType::DIRECT2D;
+    }
+  } else {
+    mRenderMode = RENDER_GDI;
+    canvasMask |= BackendTypeBit(BackendType::SKIA);
   }
-#endif
+  contentMask |= BackendTypeBit(BackendType::SKIA);
+  InitBackendPrefs(canvasMask, defaultBackend, contentMask, defaultBackend);
 }
 
 void
 gfxWindowsPlatform::UpdateRenderMode()
 {
-/* Pick the default render mode for
- * desktop.
- */
-    bool didReset = false;
-    DeviceResetReason resetReason = DeviceResetReason::OK;
-    if (DidRenderingDeviceReset(&resetReason)) {
-      Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
-      mD3D11DeviceInitialized = false;
-      mD3D11Device = nullptr;
-      mD3D11ContentDevice = nullptr;
-      mAdapter = nullptr;
-      mDeviceResetReason = DeviceResetReason::OK;
-      mHasDeviceReset = false;
+  bool didReset = HandleDeviceReset();
 
-      imgLoader::Singleton()->ClearCache(true);
-      imgLoader::Singleton()->ClearCache(false);
-      gfxAlphaBoxBlur::ShutdownBlurCache();
-      Factory::SetDirect3D11Device(nullptr);
-
-      didReset = true;
-    }
-
-    mRenderMode = RENDER_GDI;
-    mUseDirectWrite = gfxPrefs::DirectWriteFontRenderingEnabled();
+  UpdateBackendPrefs();
 
-    mD2DStatus = InitD2DSupport();
-    InitDWriteSupport();
-
-    uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
-    uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
-    BackendType defaultBackend = BackendType::CAIRO;
-    if (mRenderMode == RENDER_DIRECT2D) {
-      canvasMask |= BackendTypeBit(BackendType::DIRECT2D);
-      contentMask |= BackendTypeBit(BackendType::DIRECT2D);
-      if (gfxPrefs::Direct2DUse1_1() && Factory::SupportsD2D1() &&
-          GetD3D11ContentDevice()) {
-        contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
-        canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
-        defaultBackend = BackendType::DIRECT2D1_1;
-      } else {
-        defaultBackend = BackendType::DIRECT2D;
-      }
-    } else {
-      canvasMask |= BackendTypeBit(BackendType::SKIA);
-    }
-    contentMask |= BackendTypeBit(BackendType::SKIA);
-    InitBackendPrefs(canvasMask, defaultBackend,
-                     contentMask, defaultBackend);
-
-    if (didReset) {
-      mScreenReferenceDrawTarget = CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
-    }
+  if (didReset) {
+    mScreenReferenceDrawTarget =
+      CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
+  }
 }
 
 #ifdef CAIRO_HAS_D2D_SURFACE
 HRESULT
 gfxWindowsPlatform::CreateDevice(nsRefPtr<IDXGIAdapter1> &adapter1,
                                  int featureLevelIndex)
 {
   nsModuleHandle d3d10module(LoadLibrarySystem32(L"d3d10_1.dll"));
@@ -639,21 +590,16 @@ gfxWindowsPlatform::CreateDevice(nsRefPt
   return device ? S_OK : hr;
 }
 #endif
 
 void
 gfxWindowsPlatform::VerifyD2DDevice(bool aAttemptForce)
 {
 #ifdef CAIRO_HAS_D2D_SURFACE
-    DriverInitCrashDetection detectCrashes;
-    if (detectCrashes.DisableAcceleration()) {
-      return;
-    }
-
     if (mD3D10Device) {
         if (SUCCEEDED(mD3D10Device->GetDeviceRemovedReason())) {
             return;
         }
         mD3D10Device = nullptr;
 
         // Surface cache needs to be invalidated since it may contain vector
         // images rendered with our old, broken D2D device.
@@ -711,44 +657,58 @@ gfxWindowsPlatform::VerifyD2DDevice(bool
       reporter1_1.SetSuccessful();
     }
 #endif
 }
 
 gfxPlatformFontList*
 gfxWindowsPlatform::CreatePlatformFontList()
 {
-    mUsingGDIFonts = false;
     gfxPlatformFontList *pfl;
 #ifdef CAIRO_HAS_DWRITE_FONT
     // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd
     // crashers so blacklist them altogether
     if (IsNotWin7PreRTM() && GetDWriteFactory()) {
         pfl = new gfxDWriteFontList();
         if (NS_SUCCEEDED(pfl->InitFontList())) {
             return pfl;
         }
         // DWrite font initialization failed! Don't know why this would happen,
         // but apparently it can - see bug 594865.
         // So we're going to fall back to GDI fonts & rendering.
         gfxPlatformFontList::Shutdown();
-        SetRenderMode(RENDER_GDI);
+        DisableD2D();
     }
 #endif
     pfl = new gfxGDIFontList();
-    mUsingGDIFonts = true;
 
     if (NS_SUCCEEDED(pfl->InitFontList())) {
         return pfl;
     }
 
     gfxPlatformFontList::Shutdown();
     return nullptr;
 }
 
+// This function will permanently disable D2D for the session. It's intended to
+// be used when, after initially chosing to use Direct2D, we encounter a
+// scenario we can't support.
+//
+// This is called during gfxPlatform::Init() so at this point there should be no
+// DrawTargetD2D/1 instances.
+void
+gfxWindowsPlatform::DisableD2D()
+{
+  mD2DStatus = FeatureStatus::Failed;
+  mD2D1Status = FeatureStatus::Failed;
+  Factory::SetDirect3D11Device(nullptr);
+  Factory::SetDirect3D10Device(nullptr);
+  UpdateBackendPrefs();
+}
+
 already_AddRefed<gfxASurface>
 gfxWindowsPlatform::CreateOffscreenSurface(const IntSize& aSize,
                                            gfxImageFormat aFormat)
 {
     nsRefPtr<gfxASurface> surf = nullptr;
 
 #ifdef CAIRO_HAS_WIN32_SURFACE
     if (mRenderMode == RENDER_GDI || mRenderMode == RENDER_DIRECT2D)
@@ -1551,46 +1511,28 @@ gfxWindowsPlatform::GetD3D9DeviceManager
   }
 
   return mDeviceManager;
 }
 
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11Device()
 {
-  if (mD3D11DeviceInitialized) {
-    return mD3D11Device;
-  }
-
-  InitD3D11Devices();
-
   return mD3D11Device;
 }
 
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11ContentDevice()
 {
-  if (mD3D11DeviceInitialized) {
-    return mD3D11ContentDevice;
-  }
-
-  InitD3D11Devices();
-
   return mD3D11ContentDevice;
 }
 
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11ImageBridgeDevice()
 {
-  if (mD3D11DeviceInitialized) {
-    return mD3D11ImageBridgeDevice;
-  }
-
-  InitD3D11Devices();
-
   return mD3D11ImageBridgeDevice;
 }
 
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11DeviceForCurrentThread()
 {
   if (NS_IsMainThread()) {
     return GetD3D11ContentDevice();
@@ -1913,65 +1855,76 @@ bool DoesD3D11TextureSharingWork(ID3D11D
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
 }
 
 bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device)
 {
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE);
 }
 
+static inline bool
+CanUseWARP()
+{
+  if (gfxPrefs::LayersD3D11ForceWARP()) {
+    return true;
+  }
+
+  // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703.
+  if (!IsWin8OrLater() ||
+      gfxPrefs::LayersD3D11DisableWARP() ||
+      GetModuleHandleA("nvdxgiwrap.dll"))
+  {
+    return false;
+  }
+  return true;
+}
+
 auto
 gfxWindowsPlatform::CheckD3D11Support() -> D3D11Status
 {
   if (gfxPrefs::LayersD3D11ForceWARP()) {
-    return D3D11Status::ForceWARP;
+    return D3D11Status::OnlyWARP;
   }
-
   if (nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo()) {
     int32_t status;
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
       if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-        // See if we can use WARP instead.
-        //
-        // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703 for more.
-        if (gfxPrefs::LayersD3D11DisableWARP() || GetModuleHandleA("nvdxgiwrap.dll")) {
+        if (!CanUseWARP()) {
           return D3D11Status::Blocked;
         }
-        return D3D11Status::TryWARP;
+        return D3D11Status::OnlyWARP;
       }
     }
   }
-
-  // Either nsIGfxInfo was bugged or we're not blacklisted.
   if (!GetDXGIAdapter()) {
-    return D3D11Status::TryWARP;
+    return D3D11Status::OnlyWARP;
   }
   return D3D11Status::Ok;
 }
 
 // We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
 // since it doesn't include d3d11.h, so we use a static here. It should only
 // be used within InitD3D11Devices.
 decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
 
 bool
-gfxWindowsPlatform::AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+gfxWindowsPlatform::AttemptD3D11DeviceCreation()
 {
   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
   MOZ_ASSERT(adapter);
 
   HRESULT hr = E_INVALIDARG;
   MOZ_SEH_TRY {
     hr =
       sD3D11CreateDeviceFn(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
                            // Use
                            // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
                            // to prevent bug 1092260. IE 11 also uses this flag.
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
-                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     gfxCriticalError() << "Crash during D3D11 device creation";
     return false;
   }
 
   if (FAILED(hr) || !DoesD3D11DeviceWork(mD3D11Device)) {
     gfxCriticalError() << "D3D11 device creation failed" << hexa(hr);
@@ -1981,195 +1934,296 @@ gfxWindowsPlatform::AttemptD3D11DeviceCr
     return false;
   }
 
   CheckIfRenderTargetViewNeedsRecreating(mD3D11Device);
 
   // Only test this when not using WARP since it can fail and cause
   // GetDeviceRemovedReason to return weird values.
   mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
+  mD3D11Device->SetExceptionMode(0);
   mIsWARP = false;
   return true;
 }
 
 bool
-gfxWindowsPlatform::AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+gfxWindowsPlatform::AttemptWARPDeviceCreation()
 {
-  MOZ_ASSERT(!mD3D11Device);
-
   ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
 
   MOZ_SEH_TRY {
     HRESULT hr =
       sD3D11CreateDeviceFn(nullptr, D3D_DRIVER_TYPE_WARP, nullptr,
                            // Use
                            // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
                            // to prevent bug 1092260. IE 11 also uses this flag.
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
-                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
 
     if (FAILED(hr)) {
       // This should always succeed... in theory.
       gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
       return false;
     }
 
     reporterWARP.SetSuccessful();
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
     return false;
+
   }
 
+  // Only test for texture sharing on Windows 8 since it puts the device into
+  // an unusable state if used on Windows 7
+  if (IsWin8OrLater()) {
+    mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
+  }
+  mD3D11Device->SetExceptionMode(0);
   mIsWARP = true;
   return true;
 }
 
 bool
-gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation()
 {
   HRESULT hr = E_INVALIDARG;
   MOZ_SEH_TRY {
     hr =
       sD3D11CreateDeviceFn(mIsWARP ? nullptr : GetDXGIAdapter(),
                            mIsWARP ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_UNKNOWN,
                            nullptr,
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
-                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11ContentDevice), nullptr, nullptr);
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     return false;
   }
 
-  return SUCCEEDED(hr);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  mD3D11ContentDevice->SetExceptionMode(0);
+
+  nsRefPtr<ID3D10Multithread> multi;
+  mD3D11ContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
+  multi->SetMultithreadProtected(TRUE);
+
+  Factory::SetDirect3D11Device(mD3D11ContentDevice);
+  return true;
 }
 
 bool
-gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation()
 {
   HRESULT hr = E_INVALIDARG;
   MOZ_SEH_TRY{
     hr =
       sD3D11CreateDeviceFn(GetDXGIAdapter(), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
-                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11ImageBridgeDevice), nullptr, nullptr);
   } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
     return false;
   }
 
   if (FAILED(hr)) {
     return false;
   }
 
   mD3D11ImageBridgeDevice->SetExceptionMode(0);
-
-  return DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice);
+  if (!DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice)) {
+    mD3D11ImageBridgeDevice = nullptr;
+    return false;
+  }
+  return true;
 }
 
 void
-gfxWindowsPlatform::InitD3D11Devices()
+gfxWindowsPlatform::InitializeDevices()
 {
-  // This function attempts to initialize our D3D11 devices. If the hardware
-  // is not blacklisted for D3D11 layers. This will first attempt to create a
-  // hardware accelerated device. If this creation fails or the hardware is
+  // If we previously crashed initializing devices, or if we're in safe mode,
+  // bail out now.
+  DriverInitCrashDetection detectCrashes;
+  if (detectCrashes.DisableAcceleration() || InSafeMode()) {
+    mAcceleration = FeatureStatus::Blocked;
+    return;
+  }
+
+  // If acceleration is disabled, we refuse to initialize anything.
+  if (!ShouldUseLayersAcceleration()) {
+    mAcceleration = FeatureStatus::Disabled;
+    return;
+  }
+
+  // At this point, as far as we know, we can probably accelerate.
+  mAcceleration = FeatureStatus::Available;
+
+  // If we're going to prefer D3D9, stop here. The rest of this function
+  // attempts to use D3D11 features.
+  if (gfxPrefs::LayersPreferD3D9()) {
+    mD3D11Status = FeatureStatus::Disabled;
+    return;
+  }
+
+  // First, initialize D3D11. If this succeeds we attempt to use Direct2D.
+  InitializeD3D11();
+  if (mD3D11Status == FeatureStatus::Available) {
+    InitializeD2D();
+  }
+}
+
+void
+gfxWindowsPlatform::InitializeD3D11()
+{
+  // This function attempts to initialize our D3D11 devices, if the hardware
+  // is not blacklisted for D3D11 layers. This first attempt will try to create
+  // a hardware accelerated device. If this creation fails or the hardware is
   // blacklisted, then this function will abort if WARP is disabled, causing us
   // to fallback to D3D9 or Basic layers. If WARP is not disabled it will use
   // a WARP device which should always be available on Windows 7 and higher.
-  mD3D11DeviceInitialized = true;
-  mDoesD3D11TextureSharingWork = false;
 
-  MOZ_ASSERT(!mD3D11Device);
-
-  DriverInitCrashDetection detectCrashes;
-  if (InSafeMode() || detectCrashes.DisableAcceleration()) {
-    mD3D11Status = FeatureStatus::Blocked;
-    return;
-  }
-
-  D3D11Status status = CheckD3D11Support();
-  if (status == D3D11Status::Blocked) {
+  // Check if D3D11 is supported on this hardware.
+  D3D11Status support = CheckD3D11Support();
+  if (support == D3D11Status::Blocked) {
     mD3D11Status = FeatureStatus::Blacklisted;
     return;
   }
 
+  // Check if D3D11 is available on this system.
   nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
   sD3D11CreateDeviceFn =
     (decltype(D3D11CreateDevice)*)GetProcAddress(d3d11Module, "D3D11CreateDevice");
-
   if (!sD3D11CreateDeviceFn) {
     // We should just be on Windows Vista or XP in this case.
     mD3D11Status = FeatureStatus::Unavailable;
     return;
   }
 
-  nsTArray<D3D_FEATURE_LEVEL> featureLevels;
-  if (IsWin8OrLater()) {
-    featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
-  }
-  featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
-  featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
-  featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
-  featureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
-
-  if (status == D3D11Status::Ok) {
-    if (!AttemptD3D11DeviceCreation(featureLevels)) {
-      status = D3D11Status::TryWARP;
-    }
+  // If hardware acceleration is allowed, attempt to create a device. If this
+  // fails, we fall back to WARP.
+  if (support == D3D11Status::Ok && !AttemptD3D11DeviceCreation()) {
+    support = D3D11Status::OnlyWARP;
   }
-
-  if (IsWin8OrLater() &&
-      !gfxPrefs::LayersD3D11DisableWARP() &&
-      (status == D3D11Status::TryWARP || status == D3D11Status::ForceWARP))
-  {
-    AttemptWARPDeviceCreation(featureLevels);
-    mD3D11Status = FeatureStatus::Failed;
-  }
-
-  // Only test for texture sharing on Windows 8 since it puts the device into
-  // an unusable state if used on Windows 7
-  if (mD3D11Device && IsWin8OrLater()) {
-    mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
+  if (support == D3D11Status::OnlyWARP && CanUseWARP()) {
+    AttemptWARPDeviceCreation();
   }
 
   if (!mD3D11Device) {
-    // We could not get a D3D11 compositor, and there's nothing more we can try.
+    // Nothing more we can do.
+    mD3D11Status = FeatureStatus::Failed;
+    return;
+  }
+
+  // If we got here, we successfully got a D3D11 device.
+  mD3D11Status = FeatureStatus::Available;
+  MOZ_ASSERT(mD3D11Device);
+
+  if (!mIsWARP) {
+    AttemptD3D11ImageBridgeDeviceCreation();
+  }
+
+  // We leak these everywhere and we need them our entire runtime anyway, let's
+  // leak it here as well. We keep the pointer to sD3D11CreateDeviceFn around
+  // as well for D2D1 and device resets.
+  d3d11Module.disown();
+}
+
+static bool
+IsD2DBlacklisted()
+{
+  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+  if (gfxInfo) {
+    int32_t status;
+    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
+      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+void
+gfxWindowsPlatform::InitializeD2D()
+{
+  if (!gfxPrefs::Direct2DForceEnabled()) {
+    if (IsD2DBlacklisted()) {
+      mD2DStatus = FeatureStatus::Blacklisted;
+      return;
+    }
+  }
+
+  // Do not ever try to use D2D if it's explicitly disabled.
+  if (gfxPrefs::Direct2DDisabled()) {
+    mD2DStatus = FeatureStatus::Disabled;
     return;
   }
 
-  mD3D11Device->SetExceptionMode(0);
-  mD3D11Status = FeatureStatus::Available;
+  // Direct2D is only Vista or higher, but we require a D3D11 compositor to
+  // use it. (This check may be implied by the fact that we do not get here
+  // without a D3D11 compositor device.)
+  if (!IsVistaOrLater()) {
+    mD2DStatus = FeatureStatus::Unavailable;
+    return;
+  }
+  if (!mDoesD3D11TextureSharingWork) {
+    mD2DStatus = FeatureStatus::Failed;
+    return;
+  }
 
-  // We create our device for D2D content drawing here. Normally we don't use
-  // D2D content drawing when using WARP. However when WARP is forced by
-  // default we will let Direct2D use WARP as well.
-  if (Factory::SupportsD2D1() && (!mIsWARP || (status == D3D11Status::ForceWARP))) {
-    if (!AttemptD3D11ContentDeviceCreation(featureLevels)) {
-      mD3D11ContentDevice = nullptr;
-      d3d11Module.disown();
-      return;
-    }
+  // Using Direct2D depends on DWrite support.
+  if (!mDWriteFactory && !InitDWriteSupport()) {
+    mD2DStatus = FeatureStatus::Failed;
+    return;
+  }
 
-    mD3D11ContentDevice->SetExceptionMode(0);
-    nsRefPtr<ID3D10Multithread> multi;
-    mD3D11ContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
-    multi->SetMultithreadProtected(TRUE);
+  // Initialize D2D 1.1.
+  InitializeD2D1();
 
-    Factory::SetDirect3D11Device(mD3D11ContentDevice);
+  // Initialize D2D 1.0.
+  VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled());
+  if (!mD3D10Device) {
+    mD2DStatus = FeatureStatus::Failed;
+    return;
   }
 
-  if (!mIsWARP) {
-    if (!AttemptD3D11ImageBridgeDeviceCreation(featureLevels)) {
-      mD3D11ImageBridgeDevice = nullptr;
-    }
+  mD2DStatus = FeatureStatus::Available;
+}
+
+bool
+gfxWindowsPlatform::InitializeD2D1()
+{
+  ScopedGfxFeatureReporter d2d1_1("D2D1.1");
+
+  if (!Factory::SupportsD2D1()) {
+    mD2D1Status = FeatureStatus::Unavailable;
+    return false;
+  }
+  if (!gfxPrefs::Direct2DUse1_1()) {
+    mD2D1Status = FeatureStatus::Disabled;
+    return false;
   }
 
-  // We leak these everywhere and we need them our entire runtime anyway, let's
-  // leak it here as well.
-  d3d11Module.disown();
+  // Normally we don't use D2D content drawing when using WARP. However if
+  // WARP is force-enabled, we wlil let Direct2D use WARP as well.
+  if (mIsWARP && !gfxPrefs::LayersD3D11ForceWARP()) {
+    mD2D1Status = FeatureStatus::Blocked;
+    return false;
+  }
+
+  if (!AttemptD3D11ContentDeviceCreation()) {
+    mD2D1Status = FeatureStatus::Failed;
+    return false;
+  }
+
+  mD2D1Status = FeatureStatus::Available;
+  d2d1_1.SetSuccessful();
+  return true;
 }
 
 already_AddRefed<ID3D11Device>
 gfxWindowsPlatform::CreateD3D11DecoderDevice()
 {
   nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
   decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*)
     GetProcAddress(d3d11Module, "D3D11CreateDevice");
@@ -2437,33 +2491,43 @@ gfxWindowsPlatform::GetAcceleratedCompos
     if (gfxPlatform::CanUseDirect3D9()) {
       aBackends.AppendElement(LayersBackend::LAYERS_D3D9);
     } else {
       NS_WARNING("Direct3D 9-accelerated layers are not supported on this system.");
     }
   }
 }
 
+// Some features are dependent on other features. If this is the case, we
+// try to propagate the status of the parent feature if it wasn't available.
 FeatureStatus
-gfxWindowsPlatform::GetD2D1Status()
+gfxWindowsPlatform::GetD3D11Status() const
 {
-  if (GetD2DStatus() != FeatureStatus::Available ||
-      !Factory::SupportsD2D1())
-  {
+  if (mAcceleration != FeatureStatus::Available) {
+    return mAcceleration;
+  }
+  return mD3D11Status;
+}
+
+FeatureStatus
+gfxWindowsPlatform::GetD2DStatus() const
+{
+  if (GetD3D11Status() != FeatureStatus::Available) {
     return FeatureStatus::Unavailable;
   }
-
-  if (!GetD3D11ContentDevice()) {
-    return FeatureStatus::Failed;
-  }
+  return mD2DStatus;
+}
 
-  if (!gfxPrefs::Direct2DUse1_1()) {
-    return FeatureStatus::Disabled;
+FeatureStatus
+gfxWindowsPlatform::GetD2D1Status() const
+{
+  if (GetD3D11Status() != FeatureStatus::Available) {
+    return FeatureStatus::Unavailable;
   }
-  return FeatureStatus::Available;
+  return mD2D1Status;
 }
 
 unsigned
 gfxWindowsPlatform::GetD3D11Version()
 {
   ID3D11Device* device = GetD3D11Device();
   if (!device) {
     return 0;
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -224,19 +224,18 @@ public:
     static void GetCleartypeParams(nsTArray<ClearTypeParameterInfo>& aParams);
 
     virtual void FontsPrefsChanged(const char *aPref);
 
     void SetupClearTypeParams();
 
 #ifdef CAIRO_HAS_DWRITE_FONT
     IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; }
-    inline bool DWriteEnabled() { return mUseDirectWrite; }
+    inline bool DWriteEnabled() { return !!mDWriteFactory; }
     inline DWRITE_MEASURING_MODE DWriteMeasuringMode() { return mMeasuringMode; }
-    IDWriteTextAnalyzer *GetDWriteAnalyzer() { return mDWriteAnalyzer; }
 
     IDWriteRenderingParams *GetRenderingParams(TextRenderingMode aRenderMode)
     { return mRenderingParams[aRenderMode]; }
 #else
     inline bool DWriteEnabled() { return false; }
 #endif
     void OnDeviceManagerDestroy(mozilla::layers::DeviceManagerD3D9* aDeviceManager);
     mozilla::layers::DeviceManagerD3D9* GetD3D9DeviceManager();
@@ -258,92 +257,94 @@ public:
     bool IsWARP() { return mIsWARP; }
     bool DoesD3D11TextureSharingWork() { return mDoesD3D11TextureSharingWork; }
 
     bool SupportsApzWheelInput() const override {
       return true;
     }
     bool SupportsApzTouchInput() const override;
 
+    // Recreate devices as needed for a device reset. Returns true if a device
+    // reset occurred.
+    bool HandleDeviceReset();
+    void UpdateBackendPrefs();
+
     // Return the diagnostic status of DirectX initialization. If
     // initialization has not been attempted, this returns
     // FeatureStatus::Unused.
-    mozilla::gfx::FeatureStatus GetD3D11Status() const {
-      return mD3D11Status;
-    }
-    mozilla::gfx::FeatureStatus GetD2DStatus() const {
-      return mD2DStatus;
-    }
+    mozilla::gfx::FeatureStatus GetD3D11Status() const;
+    mozilla::gfx::FeatureStatus GetD2DStatus() const;
+    mozilla::gfx::FeatureStatus GetD2D1Status() const;
     unsigned GetD3D11Version();
-    mozilla::gfx::FeatureStatus GetD2D1Status();
 
     virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
     static mozilla::Atomic<size_t> sD3D11MemoryUsed;
     static mozilla::Atomic<size_t> sD3D9MemoryUsed;
     static mozilla::Atomic<size_t> sD3D9SurfaceImageUsed;
     static mozilla::Atomic<size_t> sD3D9SharedTextureUsed;
 
 protected:
     bool AccelerateLayersByDefault() override {
       return true;
     }
     void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends);
+    virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
 
 protected:
     RenderMode mRenderMode;
 
     int8_t mUseClearTypeForDownloadableFonts;
     int8_t mUseClearTypeAlways;
 
 private:
     void Init();
 
-    void InitD3D11Devices();
+    void InitializeDevices();
+    void InitializeD3D11();
+    void InitializeD2D();
+    bool InitializeD2D1();
+    bool InitDWriteSupport();
 
-    // Used by InitD3D11Devices().
+    void DisableD2D();
+
+    // Used by InitializeD3D11().
     enum class D3D11Status {
       Ok,
-      TryWARP,
-      ForceWARP,
+      OnlyWARP,
       Blocked
     };
     D3D11Status CheckD3D11Support();
-    bool AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
-    bool AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
-    bool AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
-    bool AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
-
-    // Used by UpdateRenderMode().
-    mozilla::gfx::FeatureStatus InitD2DSupport();
-    void InitDWriteSupport();
+    bool AttemptD3D11DeviceCreation();
+    bool AttemptWARPDeviceCreation();
+    bool AttemptD3D11ImageBridgeDeviceCreation();
+    bool AttemptD3D11ContentDeviceCreation();
 
     IDXGIAdapter1 *GetDXGIAdapter();
     bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason);
 
-    bool mUseDirectWrite;
-    bool mUsingGDIFonts;
-
 #ifdef CAIRO_HAS_DWRITE_FONT
     nsRefPtr<IDWriteFactory> mDWriteFactory;
-    nsRefPtr<IDWriteTextAnalyzer> mDWriteAnalyzer;
     nsRefPtr<IDWriteRenderingParams> mRenderingParams[TEXT_RENDERING_COUNT];
     DWRITE_MEASURING_MODE mMeasuringMode;
 #endif
     mozilla::RefPtr<IDXGIAdapter1> mAdapter;
     nsRefPtr<mozilla::layers::DeviceManagerD3D9> mDeviceManager;
     mozilla::RefPtr<ID3D10Device1> mD3D10Device;
     mozilla::RefPtr<ID3D11Device> mD3D11Device;
     mozilla::RefPtr<ID3D11Device> mD3D11ContentDevice;
     mozilla::RefPtr<ID3D11Device> mD3D11ImageBridgeDevice;
-    bool mD3D11DeviceInitialized;
     mozilla::RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
     bool mIsWARP;
     bool mHasDeviceReset;
     bool mDoesD3D11TextureSharingWork;
     DeviceResetReason mDeviceResetReason;
 
+    // These should not be accessed directly. Use the Get[Feature]Status
+    // accessors instead.
+    mozilla::gfx::FeatureStatus mAcceleration;
     mozilla::gfx::FeatureStatus mD3D11Status;
     mozilla::gfx::FeatureStatus mD2DStatus;
+    mozilla::gfx::FeatureStatus mD2D1Status;
 
-    virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
+    nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
 };
 
 #endif /* GFX_WINDOWS_PLATFORM_H */
--- a/gfx/thebes/gfxWindowsSurface.cpp
+++ b/gfx/thebes/gfxWindowsSurface.cpp
@@ -295,14 +295,8 @@ gfxWindowsSurface::GetSize() const
         return mozilla::gfx::IntSize(-1, -1);
     }
 
     NS_ASSERTION(mSurface != nullptr, "CairoSurface() shouldn't be nullptr when mSurfaceValid is TRUE!");
 
     return mozilla::gfx::IntSize(cairo_win32_surface_get_width(mSurface),
                       cairo_win32_surface_get_height(mSurface));
 }
-
-gfxMemoryLocation
-gfxWindowsSurface::GetMemoryLocation() const
-{
-    return gfxMemoryLocation::IN_PROCESS_NONHEAP;
-}
--- a/gfx/thebes/gfxWindowsSurface.h
+++ b/gfx/thebes/gfxWindowsSurface.h
@@ -60,20 +60,16 @@ public:
     nsresult BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName);
     nsresult EndPrinting();
     nsresult AbortPrinting();
     nsresult BeginPage();
     nsresult EndPage();
 
     const mozilla::gfx::IntSize GetSize() const;
 
-    // The memory used by this surface lives in this process's address space,
-    // but not in the heap.
-    virtual gfxMemoryLocation GetMemoryLocation() const;
-
 private:
     void MakeInvalid(mozilla::gfx::IntSize& size);
 
     bool mOwnsDC;
     bool mForPrinting;
 
     HDC mDC;
     HWND mWnd;
--- a/gfx/thebes/gfxXlibSurface.cpp
+++ b/gfx/thebes/gfxXlibSurface.cpp
@@ -601,14 +601,8 @@ gfxXlibSurface::GetGLXPixmap()
         NS_ASSERTION(CairoStatus() != CAIRO_STATUS_SURFACE_FINISHED,
             "GetGLXPixmap called after surface finished");
 #endif
         mGLXPixmap = gl::sGLXLibrary.CreatePixmap(this);
     }
     return mGLXPixmap;
 }
 #endif
-
-gfxMemoryLocation
-gfxXlibSurface::GetMemoryLocation() const
-{
-    return gfxMemoryLocation::OUT_OF_PROCESS;
-}
--- a/gfx/thebes/gfxXlibSurface.h
+++ b/gfx/thebes/gfxXlibSurface.h
@@ -80,20 +80,16 @@ public:
     // Release ownership of this surface's Pixmap.  This is only valid
     // on gfxXlibSurfaces for which the user called TakePixmap(), or
     // on those created by a Create() factory method.
     Drawable ReleasePixmap();
 
     // Find a visual and colormap pair suitable for rendering to this surface.
     bool GetColormapAndVisual(Colormap* colormap, Visual **visual);
 
-    // This surface is a wrapper around X pixmaps, which are stored in the X
-    // server, not the main application.
-    virtual gfxMemoryLocation GetMemoryLocation() const override;
-
 #if defined(GL_PROVIDER_GLX)
     GLXPixmap GetGLXPixmap();
 #endif
 
     // Return true if cairo will take its slow path when this surface is used
     // in a pattern with EXTEND_PAD.  As a workaround for XRender's RepeatPad
     // not being implemented correctly on old X servers, cairo avoids XRender
     // and instead reads back to perform EXTEND_PAD with pixman.  Cairo does
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -329,22 +329,19 @@ DoCollectSizeOfCompositingSurfaces(const
   SurfaceKey key = RasterSurfaceKey(aSurface->GetImageSize(),
                                     imgIContainer::DECODE_FLAGS_DEFAULT,
                                     /* aFrameNum = */ 0);
 
   // Create a counter for this surface.
   SurfaceMemoryCounter counter(key, /* aIsLocked = */ true, aType);
 
   // Extract the surface's memory usage information.
-  size_t heap = aSurface
-    ->SizeOfExcludingThis(gfxMemoryLocation::IN_PROCESS_HEAP, aMallocSizeOf);
+  size_t heap = 0, nonHeap = 0;
+  aSurface->AddSizeOfExcludingThis(aMallocSizeOf, heap, nonHeap);
   counter.Values().SetDecodedHeap(heap);
-
-  size_t nonHeap = aSurface
-    ->SizeOfExcludingThis(gfxMemoryLocation::IN_PROCESS_NONHEAP, nullptr);
   counter.Values().SetDecodedNonHeap(nonHeap);
 
   // Record it.
   aCounters.AppendElement(counter);
 }
 
 void
 FrameAnimator::CollectSizeOfCompositingSurfaces(
--- a/image/Image.h
+++ b/image/Image.h
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_image_Image_h
 #define mozilla_image_Image_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
-#include "gfx2DGlue.h"                // for gfxMemoryLocation
+#include "gfx2DGlue.h"
 #include "imgIContainer.h"
 #include "ImageURL.h"
 #include "nsStringFwd.h"
 #include "ProgressTracker.h"
 #include "SurfaceCache.h"
 
 class nsIRequest;
 class nsIInputStream;
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -311,20 +311,16 @@ private:
   DrawableFrameRef LookupFrame(uint32_t aFrameNum,
                                const nsIntSize& aSize,
                                uint32_t aFlags);
   uint32_t GetCurrentFrameIndex() const;
   uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
 
   nsIntRect GetFirstFrameRect();
 
-  size_t
-    SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
-                                            MallocSizeOf aMallocSizeOf) const;
-
   Pair<DrawResult, nsRefPtr<layers::Image>>
     GetCurrentImage(layers::ImageContainer* aContainer, uint32_t aFlags);
 
   void UpdateImageContainer();
 
   // We would like to just check if we have a zero lock count, but we can't do
   // that for animated images because in EnsureAnimExists we lock the image and
   // never unlock so that animated images always have their lock count >= 1. In
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -202,23 +202,20 @@ public:
       MOZ_ASSERT(aCachedSurface, "Should have a CachedSurface");
 
       SurfaceMemoryCounter counter(aCachedSurface->GetSurfaceKey(),
                                    aCachedSurface->IsLocked());
 
       if (aCachedSurface->mSurface) {
         counter.SubframeSize() = Some(aCachedSurface->mSurface->GetSize());
 
-        size_t heap = aCachedSurface->mSurface
-          ->SizeOfExcludingThis(gfxMemoryLocation::IN_PROCESS_HEAP,
-                                mMallocSizeOf);
+        size_t heap = 0, nonHeap = 0;
+        aCachedSurface->mSurface->AddSizeOfExcludingThis(mMallocSizeOf,
+                                                         heap, nonHeap);
         counter.Values().SetDecodedHeap(heap);
-
-        size_t nonHeap = aCachedSurface->mSurface
-          ->SizeOfExcludingThis(gfxMemoryLocation::IN_PROCESS_NONHEAP, nullptr);
         counter.Values().SetDecodedNonHeap(nonHeap);
       }
 
       mCounters.AppendElement(counter);
     }
 
   private:
     nsTArray<SurfaceMemoryCounter>& mCounters;
--- a/image/SurfaceCache.h
+++ b/image/SurfaceCache.h
@@ -9,17 +9,17 @@
  */
 
 #ifndef mozilla_image_SurfaceCache_h
 #define mozilla_image_SurfaceCache_h
 
 #include "mozilla/Maybe.h"           // for Maybe
 #include "mozilla/MemoryReporting.h" // for MallocSizeOf
 #include "mozilla/HashFunctions.h"   // for HashGeneric and AddToHash
-#include "gfx2DGlue.h"               // for gfxMemoryLocation
+#include "gfx2DGlue.h"
 #include "gfxPoint.h"                // for gfxSize
 #include "nsCOMPtr.h"                // for already_AddRefed
 #include "mozilla/gfx/Point.h"       // for mozilla::gfx::IntSize
 #include "mozilla/gfx/2D.h"          // for SourceSurface
 #include "SVGImageContext.h"         // for SVGImageContext
 
 namespace mozilla {
 namespace image {
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -1110,48 +1110,34 @@ bool imgFrame::GetCompositingFailed() co
 
 void
 imgFrame::SetCompositingFailed(bool val)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mCompositingFailed = val;
 }
 
-size_t
-imgFrame::SizeOfExcludingThis(gfxMemoryLocation aLocation,
-                              MallocSizeOf aMallocSizeOf) const
+void
+imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+                                 size_t& aHeapSizeOut,
+                                 size_t& aNonHeapSizeOut) const
 {
   MonitorAutoLock lock(mMonitor);
 
-  // aMallocSizeOf is only used if aLocation is
-  // gfxMemoryLocation::IN_PROCESS_HEAP.  It
-  // should be nullptr otherwise.
-  MOZ_ASSERT(
-    (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&  aMallocSizeOf) ||
-    (aLocation != gfxMemoryLocation::IN_PROCESS_HEAP && !aMallocSizeOf),
-    "mismatch between aLocation and aMallocSizeOf");
-
-  size_t n = 0;
-
-  if (mPalettedImageData && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mPalettedImageData);
+  if (mPalettedImageData) {
+    aHeapSizeOut += aMallocSizeOf(mPalettedImageData);
   }
-  if (mImageSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mImageSurface);
+  if (mImageSurface) {
+    aHeapSizeOut += aMallocSizeOf(mImageSurface);
   }
-  if (mOptSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mOptSurface);
+  if (mOptSurface) {
+    aHeapSizeOut += aMallocSizeOf(mOptSurface);
   }
 
-  if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mVBuf);
-    n += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
+  if (mVBuf) {
+    aHeapSizeOut += aMallocSizeOf(mVBuf);
+    aHeapSizeOut += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
+    aNonHeapSizeOut += mVBuf->NonHeapSizeOfExcludingThis();
   }
-
-  if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_NONHEAP) {
-    n += mVBuf->NonHeapSizeOfExcludingThis();
-  }
-
-  return n;
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -259,18 +259,18 @@ public:
   void SetOptimizable();
 
   Color SinglePixelColor() const;
   bool IsSinglePixel() const;
 
   already_AddRefed<SourceSurface> GetSurface();
   already_AddRefed<DrawTarget> GetDrawTarget();
 
-  size_t SizeOfExcludingThis(gfxMemoryLocation aLocation,
-                             MallocSizeOf aMallocSizeOf) const;
+  void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut,
+                              size_t& aNonHeapSizeOut) const;
 
 private: // methods
 
   ~imgFrame();
 
   nsresult LockImageData();
   nsresult UnlockImageData();
   nsresult Optimize();
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -848,16 +848,17 @@ MessageChannel::Send(Message* aMsg, Mess
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
     if (mCurrentTransaction == 0)
         mListener->OnBeginSyncTransaction();
 
 #ifdef OS_WIN
     SyncStackFrame frame(this, false);
+    NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
 
     CxxStackFrame f(*this, OUT_MESSAGE, msg);
 
     MonitorAutoLock lock(*mMonitor);
 
     if (mTimedOutMessageSeqno) {
         // Don't bother sending another sync message if a previous one timed out
@@ -989,16 +990,17 @@ MessageChannel::Send(Message* aMsg, Mess
 bool
 MessageChannel::Call(Message* aMsg, Message* aReply)
 {
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
 #ifdef OS_WIN
     SyncStackFrame frame(this, true);
+    NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
 
     // This must come before MonitorAutoLock, as its destructor acquires the
     // monitor lock.
     CxxStackFrame cxxframe(*this, OUT_MESSAGE, aMsg);
 
     MonitorAutoLock lock(*mMonitor);
     if (!Connected()) {
@@ -1027,16 +1029,22 @@ MessageChannel::Call(Message* aMsg, Mess
         // might have already processed the OnError event. if so,
         // trying another loop iteration will be futile because
         // channel state will have been cleared
         if (!Connected()) {
             ReportConnectionError("MessageChannel::Call");
             return false;
         }
 
+#ifdef OS_WIN
+        /* We should pump messages at this point to ensure that the IPC peer
+           does not become deadlocked on a pending inter-thread SendMessage() */
+        neuteredRgn.PumpOnce();
+#endif
+
         // Now might be the time to process a message deferred because of race
         // resolution.
         MaybeUndeferIncall();
 
         // Wait for an event to occur.
         while (!InterruptEventOccurred()) {
             bool maybeTimedOut = !WaitForInterruptNotify();
 
@@ -1143,16 +1151,17 @@ MessageChannel::Call(Message* aMsg, Mess
     return true;
 }
 
 bool
 MessageChannel::WaitForIncomingMessage()
 {
 #ifdef OS_WIN
     SyncStackFrame frame(this, true);
+    NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
 
     { // Scope for lock
         MonitorAutoLock lock(*mMonitor);
         AutoEnterWaitForIncoming waitingForIncoming(*this);
         if (mChannelState != ChannelConnected) {
             return false;
         }
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -10,16 +10,19 @@
 
 #include "base/basictypes.h"
 #include "base/message_loop.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Vector.h"
 #include "mozilla/WeakPtr.h"
+#if defined(OS_WIN)
+#include "mozilla/ipc/Neutering.h"
+#endif // defined(OS_WIN)
 #include "mozilla/ipc/Transport.h"
 #include "MessageLink.h"
 #include "nsAutoPtr.h"
 
 #include <deque>
 #include <stack>
 #include <math.h>
 
new file mode 100644
--- /dev/null
+++ b/ipc/glue/Neutering.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_ipc_Neutering_h
+#define mozilla_ipc_Neutering_h
+
+#include "mozilla/GuardObjects.h"
+
+/**
+ * This header declares RAII wrappers for Window neutering. See
+ * WindowsMessageLoop.cpp for more details.
+ */
+
+namespace mozilla {
+namespace ipc {
+
+/**
+ * This class is a RAII wrapper around Window neutering. As long as a
+ * NeuteredWindowRegion object is instantiated, Win32 windows belonging to the
+ * current thread will be neutered. It is safe to nest multiple instances of
+ * this class.
+ */
+class MOZ_STACK_CLASS NeuteredWindowRegion
+{
+public:
+  explicit NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+  ~NeuteredWindowRegion();
+
+  /**
+   * This function clears any backlog of nonqueued messages that are pending for
+   * the current thread.
+   */
+  void PumpOnce();
+
+private:
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  bool mNeuteredByThis;
+};
+
+/**
+ * This class is analagous to MutexAutoUnlock for Mutex; it is an RAII class
+ * that is to be instantiated within a NeuteredWindowRegion, thus temporarily
+ * disabling neutering for the remainder of its enclosing block.
+ * @see NeuteredWindowRegion
+ */
+class MOZ_STACK_CLASS DeneuteredWindowRegion
+{
+public:
+  DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
+  ~DeneuteredWindowRegion();
+
+private:
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  bool mReneuter;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_Neutering_h
+
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -3,16 +3,17 @@
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 
 #include "WindowsMessageLoop.h"
+#include "Neutering.h"
 #include "MessageChannel.h"
 
 #include "nsAutoPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsIXULAppInfo.h"
 #include "WinUtils.h"
 
@@ -857,16 +858,91 @@ MessageChannel::SpinInternalEventLoop()
 
 static inline bool
 IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
 {
   return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
     (aTimeout <= (PR_IntervalNow() - aStart));
 }
 
+static HHOOK gWindowHook;
+
+static inline void
+StartNeutering()
+{
+  MOZ_ASSERT(gUIThreadId);
+  MOZ_ASSERT(!gWindowHook);
+  NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
+               "Shouldn't be pumping already!");
+  MessageChannel::SetIsPumpingMessages(true);
+  gWindowHook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
+                                   nullptr, gUIThreadId);
+  NS_ASSERTION(gWindowHook, "Failed to set hook!");
+}
+
+static void
+StopNeutering()
+{
+  MOZ_ASSERT(MessageChannel::IsPumpingMessages());
+  ::UnhookWindowsHookEx(gWindowHook);
+  gWindowHook = NULL;
+  ::UnhookNeuteredWindows();
+  // Before returning we need to set a hook to run any deferred messages that
+  // we received during the IPC call. The hook will unset itself as soon as
+  // someone else calls GetMessage, PeekMessage, or runs code that generates
+  // a "nonqueued" message.
+  ::ScheduleDeferredMessageRun();
+  MessageChannel::SetIsPumpingMessages(false);
+}
+
+NeuteredWindowRegion::NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+  : mNeuteredByThis(!gWindowHook)
+{
+  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  if (aDoNeuter && mNeuteredByThis) {
+    StartNeutering();
+  }
+}
+
+NeuteredWindowRegion::~NeuteredWindowRegion()
+{
+  if (gWindowHook && mNeuteredByThis) {
+    StopNeutering();
+  }
+}
+
+void
+NeuteredWindowRegion::PumpOnce()
+{
+  MSG msg = {0};
+  // Pump any COM messages so that we don't hang due to STA marshaling.
+  if (gCOMWindow && ::PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
+      ::TranslateMessage(&msg);
+      ::DispatchMessageW(&msg);
+  }
+  // Expunge any nonqueued messages on the current thread.
+  ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
+}
+
+DeneuteredWindowRegion::DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
+  : mReneuter(gWindowHook != NULL)
+{
+  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  if (mReneuter) {
+    StopNeutering();
+  }
+}
+
+DeneuteredWindowRegion::~DeneuteredWindowRegion()
+{
+  if (mReneuter) {
+    StartNeutering();
+  }
+}
+
 bool
 MessageChannel::WaitForSyncNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
   // Use a blocking wait if this channel does not require
@@ -911,25 +987,16 @@ MessageChannel::WaitForSyncNotify()
     InitTimeoutData(&timeoutData, mTimeoutMs);
 
     // We only do this to ensure that we won't get stuck in
     // MsgWaitForMultipleObjects below.
     timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
     NS_ASSERTION(timerId, "SetTimer failed!");
   }
 
-  // Setup deferred processing of native events while we wait for a response.
-  NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
-               "Shouldn't be pumping already!");
-
-  MessageChannel::SetIsPumpingMessages(true);
-  HHOOK windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
-                                      nullptr, gUIThreadId);
-  NS_ASSERTION(windowHook, "Failed to set hook!");
-
   {
     while (1) {
       MSG msg = { 0 };
       // Don't get wrapped up in here if the child connection dies.
       {
         MonitorAutoLock lock(*mMonitor);
         if (!Connected()) {
           break;
@@ -993,35 +1060,21 @@ MessageChannel::WaitForSyncNotify()
       if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
           !haveSentMessagesPending) {
         // Message was for child, we should wait a bit.
         SwitchToThread();
       }
     }
   }
 
-  // Unhook the neutered window procedure hook.
-  UnhookWindowsHookEx(windowHook);
-
-  // Unhook any neutered windows procedures so messages can be delivered
-  // normally.
-  UnhookNeuteredWindows();
-
-  // Before returning we need to set a hook to run any deferred messages that
-  // we received during the IPC call. The hook will unset itself as soon as
-  // someone else calls GetMessage, PeekMessage, or runs code that generates
-  // a "nonqueued" message.
-  ScheduleDeferredMessageRun();
-
   if (timerId) {
     KillTimer(nullptr, timerId);
+    timerId = 0;
   }
 
-  MessageChannel::SetIsPumpingMessages(false);
-
   return WaitResponse(timedout);
 }
 
 bool
 MessageChannel::WaitForInterruptNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
@@ -1045,66 +1098,38 @@ MessageChannel::WaitForInterruptNotify()
 
   MonitorAutoUnlock unlock(*mMonitor);
 
   bool timedout = false;
 
   UINT_PTR timerId = 0;
   TimeoutData timeoutData = { 0 };
 
-  // windowHook is used as a flag variable for the loop below: if it is set
+  // gWindowHook is used as a flag variable for the loop below: if it is set
   // and we start to spin a nested event loop, we need to clear the hook and
   // process deferred/pending messages.
-  // If windowHook is nullptr, MessageChannel::IsPumpingMessages should be false.
-  HHOOK windowHook = nullptr;
-
   while (1) {
-    NS_ASSERTION((!!windowHook) == MessageChannel::IsPumpingMessages(),
-                 "windowHook out of sync with reality");
+    NS_ASSERTION((!!gWindowHook) == MessageChannel::IsPumpingMessages(),
+                 "gWindowHook out of sync with reality");
 
     if (mTopFrame->mSpinNestedEvents) {
-      if (windowHook) {
-        UnhookWindowsHookEx(windowHook);
-        windowHook = nullptr;
-
-        if (timerId) {
-          KillTimer(nullptr, timerId);
-          timerId = 0;
-        }
-
-        // Used by widget to assert on incoming native events
-        MessageChannel::SetIsPumpingMessages(false);
-
-        // Unhook any neutered windows procedures so messages can be delievered
-        // normally.
-        UnhookNeuteredWindows();
-
-        // Send all deferred "nonqueued" message to the intended receiver.
-        // We're dropping into SpinInternalEventLoop so we should be fairly
-        // certain these will get delivered soohn.
-        ScheduleDeferredMessageRun();
+      if (gWindowHook && timerId) {
+        KillTimer(nullptr, timerId);
+        timerId = 0;
       }
+      DeneuteredWindowRegion deneuteredRgn;
       SpinInternalEventLoop();
       ResetEvent(mEvent);
       return true;
     }
 
-    if (!windowHook) {
-      MessageChannel::SetIsPumpingMessages(true);
-      windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
-                                    nullptr, gUIThreadId);
-      NS_ASSERTION(windowHook, "Failed to set hook!");
-
-      NS_ASSERTION(!timerId, "Timer already initialized?");
-
-      if (mTimeoutMs != kNoTimeout) {
-        InitTimeoutData(&timeoutData, mTimeoutMs);
-        timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
-        NS_ASSERTION(timerId, "SetTimer failed!");
-      }
+    if (mTimeoutMs != kNoTimeout && !timerId) {
+      InitTimeoutData(&timeoutData, mTimeoutMs);
+      timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
+      NS_ASSERTION(timerId, "SetTimer failed!");
     }
 
     MSG msg = { 0 };
 
     // Don't get wrapped up in here if the child connection dies.
     {
       MonitorAutoLock lock(*mMonitor);
       if (!Connected()) {
@@ -1146,37 +1171,21 @@ MessageChannel::WaitForInterruptNotify()
     // MsgWaitForMultipleObjects every time.
     if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
         !haveSentMessagesPending) {
       // Message was for child, we should wait a bit.
       SwitchToThread();
     }
   }
 
-  if (windowHook) {
-    // Unhook the neutered window procedure hook.
-    UnhookWindowsHookEx(windowHook);
-
-    // Unhook any neutered windows procedures so messages can be delivered
-    // normally.
-    UnhookNeuteredWindows();
-
-    // Before returning we need to set a hook to run any deferred messages that
-    // we received during the IPC call. The hook will unset itself as soon as
-    // someone else calls GetMessage, PeekMessage, or runs code that generates
-    // a "nonqueued" message.
-    ScheduleDeferredMessageRun();
-
-    if (timerId) {
-      KillTimer(nullptr, timerId);
-    }
+  if (timerId) {
+    KillTimer(nullptr, timerId);
+    timerId = 0;
   }
 
-  MessageChannel::SetIsPumpingMessages(false);
-
   return WaitResponse(timedout);
 }
 
 void
 MessageChannel::NotifyWorkerThread()
 {
   mMonitor->AssertCurrentThreadOwns();
 
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -20,16 +20,17 @@ EXPORTS.mozilla.ipc += [
     'FileDescriptorSetChild.h',
     'FileDescriptorSetParent.h',
     'FileDescriptorUtils.h',
     'GeckoChildProcessHost.h',
     'InputStreamUtils.h',
     'IOThreadChild.h',
     'MessageChannel.h',
     'MessageLink.h',
+    'Neutering.h',
     'ProcessChild.h',
     'ProtocolUtils.h',
     'ScopedXREEmbed.h',
     'SharedMemory.h',
     'SharedMemoryBasic.h',
     'SharedMemorySysV.h',
     'Shmem.h',
     'Transport.h',
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -158,63 +158,52 @@ class JS_FRIEND_API(GCCellPtr)
   public:
     // Construction from a void* and trace kind.
     GCCellPtr(void* gcthing, JS::TraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {}
 
     // Automatically construct a null GCCellPtr from nullptr.
     MOZ_IMPLICIT GCCellPtr(decltype(nullptr)) : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {}
 
     // Construction from an explicit type.
-    explicit GCCellPtr(JSObject* obj) : ptr(checkedCast(obj, JS::TraceKind::Object)) { }
-    explicit GCCellPtr(JSFunction* fun) : ptr(checkedCast(fun, JS::TraceKind::Object)) { }
-    explicit GCCellPtr(JSString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
+    template <typename T>
+    explicit GCCellPtr(T* p) : ptr(checkedCast(p, JS::MapTypeToTraceKind<T>::kind)) { }
+    explicit GCCellPtr(JSFunction* p) : ptr(checkedCast(p, JS::TraceKind::Object)) { }
     explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
-    explicit GCCellPtr(JS::Symbol* sym) : ptr(checkedCast(sym, JS::TraceKind::Symbol)) { }
-    explicit GCCellPtr(JSScript* script) : ptr(checkedCast(script, JS::TraceKind::Script)) { }
     explicit GCCellPtr(const Value& v);
 
     JS::TraceKind kind() const {
         JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask);
         if (uintptr_t(traceKind) != OutOfLineTraceKindMask)
             return traceKind;
         return outOfLineKind();
     }
 
     // Allow GCCellPtr to be used in a boolean context.
     explicit operator bool() const {
         MOZ_ASSERT(bool(asCell()) == (kind() != JS::TraceKind::Null));
         return asCell();
     }
 
     // Simplify checks to the kind.
-    bool isObject() const { return kind() == JS::TraceKind::Object; }
-    bool isScript() const { return kind() == JS::TraceKind::Script; }
-    bool isString() const { return kind() == JS::TraceKind::String; }
-    bool isSymbol() const { return kind() == JS::TraceKind::Symbol; }
-    bool isShape() const { return kind() == JS::TraceKind::Shape; }
-    bool isObjectGroup() const { return kind() == JS::TraceKind::ObjectGroup; }
+    template <typename T>
+    bool is() const { return kind() == JS::MapTypeToTraceKind<T>::kind; }
 
     // Conversions to more specific types must match the kind. Access to
     // further refined types is not allowed directly from a GCCellPtr.
-    JSObject* toObject() const {
-        MOZ_ASSERT(kind() == JS::TraceKind::Object);
-        return reinterpret_cast<JSObject*>(asCell());
-    }
-    JSString* toString() const {
-        MOZ_ASSERT(kind() == JS::TraceKind::String);
-        return reinterpret_cast<JSString*>(asCell());
+    template <typename T>
+    T& as() const {
+        MOZ_ASSERT(kind() == JS::MapTypeToTraceKind<T>::kind);
+        // We can't use static_cast here, because the fact that JSObject
+        // inherits from js::gc::Cell is not part of the public API.
+        return *reinterpret_cast<T*>(asCell());
     }
-    JSScript* toScript() const {
-        MOZ_ASSERT(kind() == JS::TraceKind::Script);
-        return reinterpret_cast<JSScript*>(asCell());
-    }
-    Symbol* toSymbol() const {
-        MOZ_ASSERT(kind() == JS::TraceKind::Symbol);
-        return reinterpret_cast<Symbol*>(asCell());
-    }
+
+    // Return a pointer to the cell this |GCCellPtr| refers to, or |nullptr|.
+    // (It would be more symmetrical with |to| for this to return a |Cell&|, but
+    // the result can be |nullptr|, and null references are undefined behavior.)
     js::gc::Cell* asCell() const {
         return reinterpret_cast<js::gc::Cell*>(ptr & ~OutOfLineTraceKindMask);
     }
 
     // The CC's trace logger needs an identity that is XPIDL serializable.
     uint64_t unsafeAsInteger() const {
         return static_cast<uint64_t>(unsafeAsUIntPtr());
     }
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -604,16 +604,20 @@ class DispatchWrapper
 #if JS_BITS_PER_WORD == 32
     uint32_t padding; // Ensure the storage fields have CellSize alignment.
 #endif
     T storage;
 
   public:
     // Mimic a pointer type, so that we can drop into Rooted.
     MOZ_IMPLICIT DispatchWrapper(const T& initial) : tracer(&T::trace), storage(initial) {}
+    MOZ_IMPLICIT DispatchWrapper(T&& initial)
+      : tracer(&T::trace),
+        storage(mozilla::Forward<T>(initial))
+    { }
     T* operator &() { return &storage; }
     const T* operator &() const { return &storage; }
     operator T&() { return storage; }
     operator const T&() const { return storage; }
 
     // Trace the contained storage (of unknown type) using the trace function
     // we set aside when we did know the type.
     static void TraceWrapped(JSTracer* trc, JS::StaticTraceable* thingp, const char* name) {
--- a/js/public/TraceKind.h
+++ b/js/public/TraceKind.h
@@ -2,16 +2,28 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 js_TraceKind_h
 #define js_TraceKind_h
 
+#include "js/TypeDecls.h"
+
+// Forward declarations of all the types a TraceKind can denote.
+namespace js {
+class BaseShape;
+class LazyScript;
+class ObjectGroup;
+namespace jit {
+class JitCode;
+} // namespace jit
+} // namespace js
+
 namespace JS {
 
 // When tracing a thing, the GC needs to know about the layout of the object it
 // is looking at. There are a fixed number of different layouts that the GC
 // knows about. The "trace kind" is a static map which tells which layout a GC
 // thing has.
 //
 // Although this map is public, the details are completely hidden. Not all of
@@ -42,11 +54,87 @@ enum class TraceKind
     JitCode = 0x1F,
     LazyScript = 0x2F
 };
 const static uintptr_t OutOfLineTraceKindMask = 0x07;
 static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set");
 static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set");
 static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set");
 
+#define JS_FOR_EACH_TRACEKIND(D) \
+ /* PrettyName       TypeName           AddToCCKind */ \
+    D(BaseShape,     js::BaseShape,     true) \
+    D(JitCode,       js::jit::JitCode,  true) \
+    D(LazyScript,    js::LazyScript,    true) \
+    D(Object,        JSObject,          true) \
+    D(ObjectGroup,   js::ObjectGroup,   true) \
+    D(Script,        JSScript,          true) \
+    D(Shape,         js::Shape,         true) \
+    D(String,        JSString,          false) \
+    D(Symbol,        JS::Symbol,        false)
+
+// Map from base trace type to the trace kind.
+template <typename T> struct MapTypeToTraceKind {};
+#define JS_EXPAND_DEF(name, type, _) \
+    template <> struct MapTypeToTraceKind<type> { \
+        static const JS::TraceKind kind = JS::TraceKind::name; \
+    };
+JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+
+// Fortunately, few places in the system need to deal with fully abstract
+// cells. In those places that do, we generally want to move to a layout
+// templated function as soon as possible. This template wraps the upcast
+// for that dispatch.
+//
+// Given a call:
+//
+//    DispatchTraceKindTyped(f, thing, traceKind, ... args)
+//
+// Downcast the |void *thing| to the specific type designated by |traceKind|,
+// and pass it to the functor |f| along with |... args|, forwarded. Pass the
+// type designated by |traceKind| as the functor's template argument. The
+// |thing| parameter is optional; without it, we simply pass through |... args|.
+
+// GCC and Clang require an explicit template declaration in front of the
+// specialization of operator() because it is a dependent template. MSVC, on
+// the other hand, gets very confused if we have a |template| token there.
+#ifdef _MSC_VER
+# define JS_DEPENDENT_TEMPLATE_HINT
+#else
+# define JS_DEPENDENT_TEMPLATE_HINT template
+#endif
+template <typename F, typename... Args>
+auto
+DispatchTraceKindTyped(F f, JS::TraceKind traceKind, Args&&... args)
+  -> decltype(f. JS_DEPENDENT_TEMPLATE_HINT operator()<JSObject>(mozilla::Forward<Args>(args)...))
+{
+    switch (traceKind) {
+#define JS_EXPAND_DEF(name, type, _) \
+      case JS::TraceKind::name: \
+        return f. JS_DEPENDENT_TEMPLATE_HINT operator()<type>(mozilla::Forward<Args>(args)...);
+      JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+      default:
+          MOZ_CRASH("Invalid trace kind in DispatchTraceKindTyped.");
+    }
+}
+#undef JS_DEPENDENT_TEMPLATE_HINT
+
+template <typename F, typename... Args>
+auto
+DispatchTraceKindTyped(F f, void* thing, JS::TraceKind traceKind, Args&&... args)
+  -> decltype(f(reinterpret_cast<JSObject*>(0), mozilla::Forward<Args>(args)...))
+{
+    switch (traceKind) {
+#define JS_EXPAND_DEF(name, type, _) \
+      case JS::TraceKind::name: \
+          return f(static_cast<type*>(thing), mozilla::Forward<Args>(args)...);
+      JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+      default:
+          MOZ_CRASH("Invalid trace kind in DispatchTraceKindTyped.");
+    }
+}
+
 } // namespace JS
 
 #endif // js_TraceKind_h
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -21,25 +21,16 @@ template <typename T> class Heap;
 template <typename T> class TenuredHeap;
 
 // Returns a static string equivalent of |kind|.
 JS_FRIEND_API(const char*)
 GCTraceKindToAscii(JS::TraceKind kind);
 
 } // namespace JS
 
-namespace js {
-class BaseShape;
-class LazyScript;
-class ObjectGroup;
-namespace jit {
-class JitCode;
-} // namespace jit
-} // namespace js
-
 enum WeakMapTraceKind {
     DoNotTraceWeakMaps = 0,
     TraceWeakMapValues = 1,
     TraceWeakMapKeysValues = 2
 };
 
 class JS_PUBLIC_API(JSTracer)
 {
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -30,16 +30,18 @@
 
 #include "gc/Nursery-inl.h"
 #include "vm/String-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
+using JS::MapTypeToTraceKind;
+
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::IsBaseOf;
 using mozilla::IsSame;
 using mozilla::MakeRange;
 using mozilla::PodCopy;
 
 // Tracing Overview
@@ -250,17 +252,17 @@ template <>
 void
 CheckTracedThing<jsid>(JSTracer* trc, jsid id)
 {
     DispatchIdTyped(CheckTracedFunctor<jsid>(), id, trc);
 }
 
 #define IMPL_CHECK_TRACED_THING(_, type, __) \
     template void CheckTracedThing<type*>(JSTracer*, type*);
-FOR_EACH_GC_LAYOUT(IMPL_CHECK_TRACED_THING);
+JS_FOR_EACH_TRACEKIND(IMPL_CHECK_TRACED_THING);
 #undef IMPL_CHECK_TRACED_THING
 } // namespace js
 
 static bool
 ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, Cell* cell)
 {
     if (!trc->isMarkingTracer())
         return true;
@@ -399,17 +401,17 @@ template <typename T,
                         : IsBaseOf<Shape, T>::value        ? JS::TraceKind::Shape
                         : IsBaseOf<BaseShape, T>::value    ? JS::TraceKind::BaseShape
                         : IsBaseOf<jit::JitCode, T>::value ? JS::TraceKind::JitCode
                         : IsBaseOf<LazyScript, T>::value   ? JS::TraceKind::LazyScript
                         :                                    JS::TraceKind::ObjectGroup>
 struct BaseGCType;
 #define IMPL_BASE_GC_TYPE(name, type_, _) \
     template <typename T> struct BaseGCType<T, JS::TraceKind:: name> { typedef type_ type; };
-FOR_EACH_GC_LAYOUT(IMPL_BASE_GC_TYPE);
+JS_FOR_EACH_TRACEKIND(IMPL_BASE_GC_TYPE);
 #undef IMPL_BASE_GC_TYPE
 
 // Our barrier templates are parameterized on the pointer types so that we can
 // share the definitions with Value and jsid. Thus, we need to strip the
 // pointer before sending the type to BaseGCType and re-add it on the other
 // side. As such:
 template <typename T> struct PtrBaseGCType {};
 template <> struct PtrBaseGCType<Value> { typedef Value type; };
@@ -549,17 +551,17 @@ struct TraceRootFunctor {
 
 void
 js::TraceGenericPointerRoot(JSTracer* trc, Cell** thingp, const char* name)
 {
     MOZ_ASSERT(thingp);
     if (!*thingp)
         return;
     TraceRootFunctor f;
-    CallTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
+    DispatchTraceKindTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
 }
 
 // A typed functor adaptor for TraceManuallyBarrieredEdge.
 struct TraceManuallyBarrieredEdgeFunctor {
     template <typename T>
     void operator()(JSTracer* trc, Cell** thingp, const char* name) {
         TraceManuallyBarrieredEdge(trc, reinterpret_cast<T**>(thingp), name);
     }
@@ -567,29 +569,29 @@ struct TraceManuallyBarrieredEdgeFunctor
 
 void
 js::TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, Cell** thingp, const char* name)
 {
     MOZ_ASSERT(thingp);
     if (!*thingp)
         return;
     TraceManuallyBarrieredEdgeFunctor f;
-    CallTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
+    DispatchTraceKindTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
 }
 
 // This method is responsible for dynamic dispatch to the real tracer
 // implementation. Consider replacing this choke point with virtual dispatch:
 // a sufficiently smart C++ compiler may be able to devirtualize some paths.
 template <typename T>
 void
 DispatchToTracer(JSTracer* trc, T* thingp, const char* name)
 {
 #define IS_SAME_TYPE_OR(name, type, _) mozilla::IsSame<type*, T>::value ||
     static_assert(
-            FOR_EACH_GC_LAYOUT(IS_SAME_TYPE_OR)
+            JS_FOR_EACH_TRACEKIND(IS_SAME_TYPE_OR)
             mozilla::IsSame<T, JS::Value>::value ||
             mozilla::IsSame<T, jsid>::value,
             "Only the base cell layout types are allowed into marking/tracing internals");
 #undef IS_SAME_TYPE_OR
     if (trc->isMarkingTracer())
         return DoMarking(static_cast<GCMarker*>(trc), *thingp);
     if (trc->isTenuringTracer())
         return static_cast<TenuringTracer*>(trc)->traverse(thingp);
@@ -1726,17 +1728,17 @@ struct PushArenaFunctor {
     template <typename T> void operator()(GCMarker* gcmarker, ArenaHeader* aheader) {
         PushArenaTyped<T>(gcmarker, aheader);
     }
 };
 
 void
 gc::PushArena(GCMarker* gcmarker, ArenaHeader* aheader)
 {
-    CallTyped(PushArenaFunctor(), MapAllocToTraceKind(aheader->getAllocKind()), gcmarker, aheader);
+    DispatchTraceKindTyped(PushArenaFunctor(), MapAllocToTraceKind(aheader->getAllocKind()), gcmarker, aheader);
 }
 
 #ifdef DEBUG
 void
 GCMarker::checkZone(void* p)
 {
     MOZ_ASSERT(started);
     DebugOnly<Cell*> cell = static_cast<Cell*>(p);
@@ -2111,17 +2113,17 @@ js::TenuringTracer::moveElementsToTenure
 /*** IsMarked / IsAboutToBeFinalized **************************************************************/
 
 template <typename T>
 static inline void
 CheckIsMarkedThing(T* thingp)
 {
 #define IS_SAME_TYPE_OR(name, type, _) mozilla::IsSame<type*, T>::value ||
     static_assert(
-            FOR_EACH_GC_LAYOUT(IS_SAME_TYPE_OR)
+            JS_FOR_EACH_TRACEKIND(IS_SAME_TYPE_OR)
             false, "Only the base cell layout types are allowed into marking/tracing internals");
 #undef IS_SAME_TYPE_OR
 
 #ifdef DEBUG
     MOZ_ASSERT(thingp);
     MOZ_ASSERT(*thingp);
     JSRuntime* rt = (*thingp)->runtimeFromAnyThread();
     MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(*thingp),
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -521,17 +521,17 @@ BufferGrayRootsTracer::onChild(const JS:
     gc::TenuredCell* tenured = gc::TenuredCell::fromPointer(thing.asCell());
 
     Zone* zone = tenured->zone();
     if (zone->isCollecting()) {
         // See the comment on SetMaybeAliveFlag to see why we only do this for
         // objects and scripts. We rely on gray root buffering for this to work,
         // but we only need to worry about uncollected dead compartments during
         // incremental GCs (when we do gray root buffering).
-        CallTyped(SetMaybeAliveFunctor(), tenured, thing.kind());
+        DispatchTraceKindTyped(SetMaybeAliveFunctor(), tenured, thing.kind());
 
         if (!zone->gcGrayRoots.append(tenured))
             bufferingGrayRootsFailed = true;
     }
 }
 
 void
 GCRuntime::markBufferedGrayRoots(JS::Zone* zone)
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -15,42 +15,46 @@ class JSLinearString;
 namespace js {
 
 class PropertyName;
 class NativeObject;
 class ArrayObject;
 class GlobalObject;
 class PlainObject;
 class ScriptSourceObject;
+class SavedFrame;
 class Shape;
 class ObjectGroup;
 
 // These are internal counterparts to the public types such as HandleObject.
 
 typedef JS::Handle<NativeObject*>      HandleNativeObject;
 typedef JS::Handle<Shape*>             HandleShape;
 typedef JS::Handle<ObjectGroup*>       HandleObjectGroup;
 typedef JS::Handle<JSAtom*>            HandleAtom;
 typedef JS::Handle<JSLinearString*>    HandleLinearString;
 typedef JS::Handle<PropertyName*>      HandlePropertyName;
 typedef JS::Handle<ArrayObject*>       HandleArrayObject;
 typedef JS::Handle<PlainObject*>       HandlePlainObject;
+typedef JS::Handle<SavedFrame*>        HandleSavedFrame;
 typedef JS::Handle<ScriptSourceObject*> HandleScriptSource;
 
 typedef JS::MutableHandle<Shape*>      MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*>     MutableHandleAtom;
 typedef JS::MutableHandle<NativeObject*> MutableHandleNativeObject;
 typedef JS::MutableHandle<PlainObject*> MutableHandlePlainObject;
+typedef JS::MutableHandle<SavedFrame*> MutableHandleSavedFrame;
 
 typedef JS::Rooted<NativeObject*>      RootedNativeObject;
 typedef JS::Rooted<Shape*>             RootedShape;
 typedef JS::Rooted<ObjectGroup*>       RootedObjectGroup;
 typedef JS::Rooted<JSAtom*>            RootedAtom;
 typedef JS::Rooted<JSLinearString*>    RootedLinearString;
 typedef JS::Rooted<PropertyName*>      RootedPropertyName;
 typedef JS::Rooted<ArrayObject*>       RootedArrayObject;
 typedef JS::Rooted<GlobalObject*>      RootedGlobalObject;
 typedef JS::Rooted<PlainObject*>       RootedPlainObject;
+typedef JS::Rooted<SavedFrame*>        RootedSavedFrame;
 typedef JS::Rooted<ScriptSourceObject*> RootedScriptSource;
 
 } /* namespace js */
 
 #endif /* gc_Rooting_h */
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -48,17 +48,17 @@ DoCallback(JS::CallbackTracer* trc, T* t
 {
     CheckTracedThing(trc, *thingp);
     JS::AutoTracingName ctx(trc, name);
     trc->dispatchToOnEdge(thingp);
     return *thingp;
 }
 #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(name, type, _) \
     template type* DoCallback<type*>(JS::CallbackTracer*, type**, const char*);
-FOR_EACH_GC_LAYOUT(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS);
+JS_FOR_EACH_TRACEKIND(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS);
 #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
 
 template <typename S>
 struct DoCallbackFunctor : public IdentityDefaultAdaptor<S> {
     template <typename T> S operator()(T* t, JS::CallbackTracer* trc, const char* name) {
         return js::gc::RewrapValueOrId<S, T*>::wrap(DoCallback(trc, &t, name));
     }
 };
@@ -188,17 +188,17 @@ struct TraceChildrenFunctor {
     }
 };
 
 void
 js::TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind)
 {
     MOZ_ASSERT(thing);
     TraceChildrenFunctor f;
-    CallTyped(f, kind, trc, thing);
+    DispatchTraceKindTyped(f, kind, trc, thing);
 }
 
 JS_PUBLIC_API(void)
 JS_TraceRuntime(JSTracer* trc)
 {
     AssertHeapIsIdle(trc->runtime());
     TraceRuntime(trc);
 }
@@ -326,33 +326,33 @@ struct ObjectGroupCycleCollectorTracer :
 
     JS::CallbackTracer* innerTracer;
     Vector<ObjectGroup*, 4, SystemAllocPolicy> seen, worklist;
 };
 
 void
 ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing)
 {
-    if (thing.isObject() || thing.isScript()) {
+    if (thing.is<JSObject>() || thing.is<JSScript>()) {
         // Invoke the inner cycle collector callback on this child. It will not
         // recurse back into TraceChildren.
         innerTracer->onChild(thing);
         return;
     }
 
-    if (thing.isObjectGroup()) {
+    if (thing.is<ObjectGroup>()) {
         // If this group is required to be in an ObjectGroup chain, trace it
         // via the provided worklist rather than continuing to recurse.
-        ObjectGroup* group = static_cast<ObjectGroup*>(thing.asCell());
-        if (group->maybeUnboxedLayout()) {
+        ObjectGroup& group = thing.as<ObjectGroup>();
+        if (group.maybeUnboxedLayout()) {
             for (size_t i = 0; i < seen.length(); i++) {
-                if (seen[i] == group)
+                if (seen[i] == &group)
                     return;
             }
-            if (seen.append(group) && worklist.append(group)) {
+            if (seen.append(&group) && worklist.append(&group)) {
                 return;
             } else {
                 // If append fails, keep tracing normally. The worst that will
                 // happen is we end up overrecursing.
             }
         }
     }
 
--- a/js/src/gdb/lib-for-tests/catcher.py
+++ b/js/src/gdb/lib-for-tests/catcher.py
@@ -10,13 +10,13 @@
 # simple as possible!
 
 import os
 import sys
 import traceback
 try:
     # testlibdir is set on the GDB command line, via:
     # --eval-command python testlibdir=...
-    execfile(os.path.join(testlibdir, 'prologue.py'))
+    exec(open(os.path.join(testlibdir, 'prologue.py')).read())
 except Exception as err:
     sys.stderr.write('Error running GDB prologue:\n')
     traceback.print_exc()
     sys.exit(1)
--- a/js/src/gdb/lib-for-tests/prologue.py
+++ b/js/src/gdb/lib-for-tests/prologue.py
@@ -18,19 +18,19 @@ def run_fragment(fragment, function='bre
         assert bp.hit_count == 1
     finally:
         bp.delete()
     gdb.execute('frame 1')
 
 # Assert that |actual| is equal to |expected|; if not, complain in a helpful way.
 def assert_eq(actual, expected):
     if actual != expected:
-        raise AssertionError, """Unexpected result:
+        raise AssertionError("""Unexpected result:
 expected: %r
-actual:   %r""" % (expected, actual)
+actual:   %r""" % (expected, actual))
 
 # Assert that |value|'s pretty-printed form is |form|. If |value| is a
 # string, then evaluate it with gdb.parse_and_eval to produce a value.
 def assert_pretty(value, form):
     if isinstance(value, str):
         value = gdb.parse_and_eval(value)
     assert_eq(str(value), form)
 
@@ -39,37 +39,37 @@ def assert_pretty(value, form):
 def assert_subprinter_registered(printer, subprinter):
     # Match a line containing |printer| followed by a colon, and then a
     # series of more-indented lines containing |subprinter|.
 
     names = { 'printer': re.escape(printer), 'subprinter': re.escape(subprinter) }
     pat = r'^( +)%(printer)s *\n(\1 +.*\n)*\1 +%(subprinter)s *\n' % names
     output = gdb.execute('info pretty-printer', to_string=True)
     if not re.search(pat, output, re.MULTILINE):
-        raise AssertionError, ("assert_subprinter_registered failed to find pretty-printer:\n"
-                               "  %s:%s\n"
-                               "'info pretty-printer' says:\n"
-                               "%s" % (printer, subprinter, output))
+        raise AssertionError("assert_subprinter_registered failed to find pretty-printer:\n"
+                             "  %s:%s\n"
+                             "'info pretty-printer' says:\n"
+                             "%s" % (printer, subprinter, output))
 
 # Request full stack traces for Python errors.
 gdb.execute('set python print-stack full')
 
 # Tell GDB not to ask the user about the things we tell it to do.
 gdb.execute('set confirm off', False)
 
 # Some print settings that make testing easier.
 gdb.execute('set print static-members off')
 gdb.execute('set print address off')
 gdb.execute('set print pretty off')
 gdb.execute('set width 0')
 
 try:
     # testscript is set on the GDB command line, via:
     # --eval-command python testscript=...
-    execfile(testscript)
+    exec(open(testscript).read())
 except AssertionError as err:
     sys.stderr.write('\nAssertion traceback:\n')
     (t, v, tb) = sys.exc_info()
     traceback.print_tb(tb)
     sys.stderr.write('\nTest assertion failed:\n')
     sys.stderr.write(str(err))
     sys.exit(1)
 
--- a/js/src/gdb/moz.build
+++ b/js/src/gdb/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 GeckoProgram('gdb-tests', linkage=None)
 
 UNIFIED_SOURCES += [
     'gdb-tests.cpp',
     'tests/test-asmjs.cpp',
+    'tests/test-GCCellPtr.cpp',
     'tests/test-Interpreter.cpp',
     'tests/test-jsid.cpp',
     'tests/test-JSObject.cpp',
     'tests/test-JSString.cpp',
     'tests/test-JSSymbol.cpp',
     'tests/test-jsval.cpp',
     'tests/test-prettyprinters.cpp',
     'tests/test-Root.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/gdb/mozilla/GCCellPtr.py
@@ -0,0 +1,49 @@
+# Pretty-printers for GCCellPtr values.
+
+import gdb
+import mozilla.prettyprinters
+
+from mozilla.prettyprinters import pretty_printer
+
+# Forget any printers from previous loads of this module.
+mozilla.prettyprinters.clear_module_printers(__name__)
+
+# Cache information about the JS::TraceKind type for this objfile.
+class GCCellPtrTypeCache(object):
+    def __init__(self, cache):
+        self.TraceKind_t = gdb.lookup_type('JS::TraceKind')
+
+        # Build a mapping from TraceKind enum values to the types they denote.
+        e = gdb.types.make_enum_dict(self.TraceKind_t)
+        kind_to_type = {}
+        def kind(k, t):
+            kind_to_type[e['JS::TraceKind::' + k]] = gdb.lookup_type(t)
+        kind('Object',      'JSObject')
+        kind('String',      'JSString')
+        kind('Symbol',      'JS::Symbol')
+        kind('Script',      'JSScript')
+        kind('Shape',       'js::Shape')
+        kind('ObjectGroup', 'js::ObjectGroup')
+        kind('BaseShape',   'js::BaseShape')
+        kind('JitCode',     'js::jit::JitCode')
+        kind('LazyScript',  'js::LazyScript')
+        self.kind_to_type = kind_to_type
+
+        self.Null = e['JS::TraceKind::Null']
+        self.mask = gdb.parse_and_eval('JS::OutOfLineTraceKindMask')
+
+@pretty_printer('JS::GCCellPtr')
+class GCCellPtr(object):
+    def __init__(self, value, cache):
+        self.value = value
+        if not cache.mod_GCCellPtr:
+            cache.mod_GCCellPtr = GCCellPtrTypeCache(cache)
+        self.cache = cache
+
+    def to_string(self):
+        ptr = self.value['ptr']
+        kind = ptr & self.cache.mod_GCCellPtr.mask
+        if kind == self.cache.mod_GCCellPtr.Null:
+            return "JS::GCCellPtr(nullptr)"
+        tipe = self.cache.mod_GCCellPtr.kind_to_type[int(kind)]
+        return "JS::GCCellPtr(({}*) {})".format(tipe, ptr.cast(self.cache.void_ptr_t))
--- a/js/src/gdb/mozilla/JSSymbol.py
+++ b/js/src/gdb/mozilla/JSSymbol.py
@@ -13,17 +13,17 @@ UniqueSymbol = 0xffffffff
 
 @ptr_pretty_printer("JS::Symbol")
 class JSSymbolPtr(mozilla.prettyprinters.Pointer):
     def __init__(self, value, cache):
         super(JSSymbolPtr, self).__init__(value, cache)
         self.value = value
 
     def to_string(self):
-        code = int(self.value['code_'])
+        code = int(self.value['code_']) & 0xffffffff
         desc = str(self.value['description_'])
         if code == InSymbolRegistry:
             return "Symbol.for({})".format(desc)
         elif code == UniqueSymbol:
             return "Symbol({})".format(desc)
         else:
             # Well-known symbol. Strip off the quotes added by the JSString *
             # pretty-printer.
--- a/js/src/gdb/mozilla/Root.py
+++ b/js/src/gdb/mozilla/Root.py
@@ -13,16 +13,20 @@ mozilla.prettyprinters.clear_module_prin
 class Common(object):
     # The name of the template member holding the referent.
     member = 'ptr'
 
     # If True, this is a handle type, and should be dereferenced. If False,
     # the template member holds the referent directly.
     handle = False
 
+    # If True, we should strip typedefs from our referent type. (Rooted<T>
+    # uses template magic that gives the referent a noisy type.)
+    strip_typedefs = False
+
     # Initialize a pretty-printer for |value|, using |cache|.
     #
     # If given, |content_printer| is a pretty-printer constructor to use for
     # this handle/root/etc.'s referent. Usually, we can just omit this argument
     # and let GDB choose a pretty-printer for the referent given its type, but
     # when the referent is a typedef of an integral type (say, |jsid| in a
     # non-|DEBUG| build), the GNU toolchain (at least) loses the typedef name,
     # and all we know about the referent is its fundamental integer type ---
@@ -37,28 +41,30 @@ class Common(object):
     def __init__(self, value, cache, content_printer=None):
         self.value = value
         self.cache = cache
         self.content_printer = content_printer
     def to_string(self):
         ptr = self.value[self.member]
         if self.handle:
             ptr = ptr.dereference()
+        if self.strip_typedefs:
+            ptr = ptr.cast(ptr.type.strip_typedefs())
         if self.content_printer:
             return self.content_printer(ptr, self.cache).to_string()
         else:
             # As of 2012-11, GDB suppresses printing pointers in replacement
             # values; see http://sourceware.org/ml/gdb/2012-11/msg00055.html
             # That means that simply returning the 'ptr' member won't work.
             # Instead, just invoke GDB's formatter ourselves.
             return str(ptr)
 
 @template_pretty_printer("JS::Rooted")
 class Rooted(Common):
-    pass
+    strip_typedefs = True
 
 @template_pretty_printer("JS::Handle")
 class Handle(Common):
     handle = True
 
 @template_pretty_printer("JS::MutableHandle")
 class MutableHandle(Common):
     handle = True
--- a/js/src/gdb/mozilla/asmjs.py
+++ b/js/src/gdb/mozilla/asmjs.py
@@ -20,17 +20,17 @@ def on_stop(event):
             sigaction_buffers[process] = buf
 
         # See if AsmJSFaultHandler is installed as the SIGSEGV signal action.
         sigaction_fn = gdb.parse_and_eval('__sigaction')
         sigaction_fn(SIGSEGV, 0, buf)
         AsmJSFaultHandler = gdb.parse_and_eval("AsmJSFaultHandler")
         if buf['__sigaction_handler']['sa_handler'] == AsmJSFaultHandler:
             # Advise the user that magic is happening.
-            print "js/src/gdb/mozilla/asmjs.py: Allowing AsmJSFaultHandler to run."
+            print("js/src/gdb/mozilla/asmjs.py: Allowing AsmJSFaultHandler to run.")
 
             # If AsmJSFaultHandler doesn't handle this segfault, it will unhook
             # itself and re-raise.
             gdb.execute("continue")
 
 def on_exited(event):
     if event.inferior in sigaction_buffers:
         del sigaction_buffers[event.inferior]
--- a/js/src/gdb/mozilla/autoload.py
+++ b/js/src/gdb/mozilla/autoload.py
@@ -3,23 +3,24 @@
 print("Loading JavaScript value pretty-printers; see js/src/gdb/README.")
 print("If they cause trouble, type: disable pretty-printer .* SpiderMonkey")
 
 import gdb.printing
 import mozilla.prettyprinters
 
 # Import the pretty-printer modules. As a side effect, loading these
 # modules registers their printers with mozilla.prettyprinters.
+import mozilla.GCCellPtr
 import mozilla.Interpreter
-import mozilla.jsid
 import mozilla.JSObject
 import mozilla.JSString
 import mozilla.JSSymbol
+import mozilla.Root
+import mozilla.jsid
 import mozilla.jsval
-import mozilla.Root
 
 # The user may have personal pretty-printers. Get those, too, if they exist.
 try:
     import my_mozilla_printers
 except ImportError:
     pass
 
 # Register our pretty-printers with |objfile|.
--- a/js/src/gdb/mozilla/prettyprinters.py
+++ b/js/src/gdb/mozilla/prettyprinters.py
@@ -174,20 +174,21 @@ class TypeCache(object):
         self.void_ptr_t = self.void_t.pointer()
         try:
             self.JSString_ptr_t = gdb.lookup_type('JSString').pointer()
             self.JSSymbol_ptr_t = gdb.lookup_type('JS::Symbol').pointer()
             self.JSObject_ptr_t = gdb.lookup_type('JSObject').pointer()
         except gdb.error:
             raise NotSpiderMonkeyObjfileError
 
-        self.mod_JSString = None
+        self.mod_GCCellPtr = None
+        self.mod_Interpreter = None
         self.mod_JSObject = None
+        self.mod_JSString = None
         self.mod_jsval = None
-        self.mod_Interpreter = None
 
 # Yield a series of all the types that |t| implements, by following typedefs
 # and iterating over base classes. Specifically:
 # - |t| itself is the first value yielded.
 # - If we yield a typedef, we later yield its definition.
 # - If we yield a type with base classes, we later yield those base classes.
 # - If we yield a type with some base classes that are typedefs,
 #   we yield all the type's base classes before following the typedefs.
--- a/js/src/gdb/run-tests.py
+++ b/js/src/gdb/run-tests.py
@@ -156,17 +156,17 @@ class Test(TaskPool.Task):
         return [OPTIONS.gdb_executable,
                 '-nw',          # Don't create a window (unnecessary?)
                 '-nx',          # Don't read .gdbinit.
                 '--ex', 'add-auto-load-safe-path %s' % (OPTIONS.builddir,),
                 '--ex', 'set env LD_LIBRARY_PATH %s' % os.path.join(OPTIONS.objdir, 'js', 'src'),
                 '--ex', 'file %s' % (os.path.join(OPTIONS.builddir, 'gdb-tests'),),
                 '--eval-command', 'python testlibdir=%r' % (testlibdir,),
                 '--eval-command', 'python testscript=%r' % (self.test_path,),
-                '--eval-command', 'python execfile(%r)' % os.path.join(testlibdir, 'catcher.py')]
+                '--eval-command', 'python exec(open(%r).read())' % os.path.join(testlibdir, 'catcher.py')]
 
     def start(self, pipe, deadline):
         super(Test, self).start(pipe, deadline)
         if OPTIONS.show_cmd:
             self.summary.interleave_output(lambda: self.show_cmd(sys.stdout))
 
     def onStdout(self, text):
         self.stdout += text
new file mode 100644
--- /dev/null
+++ b/js/src/gdb/tests/test-GCCellPtr.cpp
@@ -0,0 +1,23 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+#include "js/HeapAPI.h"
+
+FRAGMENT(GCCellPtr, simple) {
+  JS::GCCellPtr nulll(nullptr);
+
+  JS::Rooted<JSObject*> glob(cx, JS::CurrentGlobalOrNull(cx));
+  JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0));
+  JS::Rooted<Symbol*> unique(cx, NewSymbol(cx, nullptr));
+
+  JS::GCCellPtr object(glob.get());
+  JS::GCCellPtr string(empty.get());
+  JS::GCCellPtr symbol(unique.get());
+
+  breakpoint();
+
+  (void) nulll;
+  (void) object;
+  (void) string;
+  (void) symbol;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/gdb/tests/test-GCCellPtr.py
@@ -0,0 +1,10 @@
+# Tests for GCCellPtr pretty-printing
+
+assert_subprinter_registered('SpiderMonkey', 'JS::GCCellPtr')
+
+run_fragment('GCCellPtr.simple')
+
+assert_pretty('nulll', 'JS::GCCellPtr(nullptr)')
+assert_pretty('object', 'JS::GCCellPtr((JSObject*) )')
+assert_pretty('string', 'JS::GCCellPtr((JSString*) )')
+assert_pretty('symbol', 'JS::GCCellPtr((JS::Symbol*) )')
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug-1136806.js
@@ -0,0 +1,7 @@
+// |jit-test| allow-unhandlable-oom; allow-oom
+
+if (typeof oomAfterAllocations == "function") {
+  Debugger()
+  oomAfterAllocations(6)
+  Debugger()
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1186271.js
@@ -0,0 +1,18 @@
+function f(x) {
+    return Math.imul(1, x >>> 0) / 9 | 0;
+}
+function g(x) {
+    return 1 * (x >>> 0) / 9 | 0;
+}
+function h(x) {
+    return (x >>> 0) / 9 | 0;
+}
+
+assertEq(0, f(4294967296));
+assertEq(-238609294, f(2147483648));
+
+assertEq(0, g(4294967296));
+assertEq(238609294, g(2147483648));
+
+assertEq(0, h(4294967296));
+assertEq(238609294, h(2147483648));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/caching-and-ccws.js
@@ -0,0 +1,35 @@
+// Test that the SavedFrame caching doesn't get messed up in the presence of
+// cross-compartment calls.
+
+const funcSource = "function call(f) { return f(); }";
+
+const g1 = newGlobal();
+const g2 = newGlobal();
+
+g1.eval(funcSource);
+g2.eval(funcSource);
+eval(funcSource);
+
+function doSaveStack() {
+  return saveStack();
+}
+
+const captureStacksAcrossCompartmens = () =>
+  [this, g1, g2].map(g => g.call(doSaveStack));
+
+(function f0() {
+  const stacks = [];
+
+  for (var i = 0; i < 2; i++)
+    stacks.push(...captureStacksAcrossCompartmens());
+
+  const [s1, s2, s3, s4, s5, s6] = stacks;
+
+  assertEq(s1 != s2, true);
+  assertEq(s2 != s3, true);
+  assertEq(s3 != s1, true);
+
+  assertEq(s1, s4);
+  assertEq(s2, s5);
+  assertEq(s3, s6);
+}());
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/caching-and-frame-count.js
@@ -0,0 +1,38 @@
+// Test that the SavedFrame caching doesn't mess up counts. Specifically, that
+// if we capture only the first n frames of a stack, we don't cache that stack
+// and return it for when someone else captures another stack and asks for more
+// frames than that last time.
+
+function stackLength(stack) {
+  return stack === null
+    ? 0
+    : 1 + stackLength(stack.parent);
+}
+
+(function f0() {
+  (function f1() {
+    (function f2() {
+      (function f3() {
+        (function f4() {
+          (function f5() {
+            (function f6() {
+              (function f7() {
+                (function f8() {
+                  (function f9() {
+                    const s1 = saveStack(3);
+                    const s2 = saveStack(5);
+                    const s3 = saveStack(0 /* no limit */);
+
+                    assertEq(stackLength(s1), 3);
+                    assertEq(stackLength(s2), 5);
+                    assertEq(stackLength(s3), 11);
+                  }());
+                }());
+              }());
+            }());
+          }());
+        }());
+      }());
+    }());
+  }());
+}());
--- a/js/src/jit-test/tests/saved-stacks/gc-frame-cache.js
+++ b/js/src/jit-test/tests/saved-stacks/gc-frame-cache.js
@@ -1,76 +1,87 @@
 // Test that SavedFrame instances get removed from the SavedStacks frames cache
 // after a GC.
 
 const FUZZ_FACTOR = 3;
 
-function assertAboutEq(actual, expected) {
-  if (Math.abs(actual - expected) > FUZZ_FACTOR)
-    throw new Error("Assertion failed: expected about " + expected + ", got " + actual +
-                    ". FUZZ_FACTOR = " + FUZZ_FACTOR);
+function isAboutEq(actual, expected) {
+  return Math.abs(actual - expected) <= FUZZ_FACTOR;
 }
 
 var stacks = [];
 
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
+(function () {
+  // Use an IIFE here so that we don't keep these saved stacks alive in the
+  // frame cache when we test that they all go away at the end of the test.
+
+  var startCount = getSavedFrameCount();
+  print("startCount = " + startCount);
 
-assertAboutEq(getSavedFrameCount(), 50);
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());