Merge m-c to b2g-inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 07 Aug 2015 16:17:19 -0400
changeset 288540 34d22581dab4cb0ec9fad15b8a4430b3ec8b4e21
parent 288539 7e53166a1b05136c7c4035925bc051a1a008e7ff (current diff)
parent 288498 461fc0a6a130b5cee4c18481db4aa51b818c65f5 (diff)
child 288541 4f63a1124c9aef0725058259d6b854075739a2bb
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
mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6673,22 +6673,20 @@ 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",
+
+  _isChromeUI: false,
+  _sslStatus: null,
+  _uri: null,
 
   // smart getters
   get _identityPopup () {
     delete this._identityPopup;
     return this._identityPopup = document.getElementById("identity-popup");
   },
   get _identityBox () {
     delete this._identityBox;
@@ -6709,30 +6707,20 @@ var gIdentityHandler = {
     return this._identityPopupContentSupp =
       document.getElementById("identity-popup-content-supplemental");
   },
   get _identityPopupContentVerif () {
     delete this._identityPopupContentVerif;
     return this._identityPopupContentVerif =
       document.getElementById("identity-popup-content-verifier");
   },
-  get _identityPopupSecurityContent () {
-    delete this._identityPopupSecurityContent;
-    return this._identityPopupSecurityContent =
-      document.getElementById("identity-popup-security-content");
-  },
-  get _identityPopupSecurityView () {
-    delete this._identityPopupSecurityView;
-    return this._identityPopupSecurityView =
-      document.getElementById("identity-popup-securityView");
-  },
-  get _identityPopupMainView () {
-    delete this._identityPopupMainView;
-    return this._identityPopupMainView =
-      document.getElementById("identity-popup-mainView");
+  get _identityPopupMixedContentLearnMore () {
+    delete this._identityPopupMixedContentLearnMore;
+    return this._identityPopupMixedContentLearnMore =
+      document.getElementById("identity-popup-mcb-learn-more");
   },
   get _identityIconLabel () {
     delete this._identityIconLabel;
     return this._identityIconLabel = document.getElementById("identity-icon-label");
   },
   get _overrideService () {
     delete this._overrideService;
     return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
@@ -6801,23 +6789,43 @@ var gIdentityHandler = {
     // If an element is focused that's not the anchor, clear the focus.
     // Elements of hidden views have -moz-user-focus:ignore but setting that
     // per CSS selector doesn't blur a focused element in those hidden views.
     if (Services.focus.focusedElement != anchor) {
       Services.focus.clearFocus(window);
     }
   },
 
+  disableMixedContentProtection() {
+    // Use telemetry to measure how often unblocking happens
+    const kMIXED_CONTENT_UNBLOCK_EVENT = 2;
+    let histogram =
+      Services.telemetry.getHistogramById(
+        "MIXED_CONTENT_UNBLOCK_COUNTER");
+    histogram.add(kMIXED_CONTENT_UNBLOCK_EVENT);
+    // Reload the page with the content unblocked
+    BrowserReloadWithFlags(
+      Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT);
+    this._identityPopup.hidePopup();
+  },
+
+  enableMixedContentProtection() {
+    gBrowser.selectedBrowser.messageManager.sendAsyncMessage(
+      "MixedContent:ReenableProtection", {});
+    BrowserReload();
+    this._identityPopup.hidePopup();
+  },
+
   /**
-   * Helper to parse out the important parts of _lastStatus (of the SSL cert in
+   * Helper to parse out the important parts of _sslStatus (of the SSL cert in
    * particular) for use in constructing identity UI strings
   */
   getIdentityData : function() {
     var result = {};
-    var status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
+    var status = this._sslStatus.QueryInterface(Ci.nsISSLStatus);
     var cert = status.serverCert;
 
     // Human readable name of Subject
     result.subjectOrg = cert.organization;
 
     // SubjectName fields, broken up for individual access
     if (cert.subjectName) {
       result.subjectNameFields = {};
@@ -6843,77 +6851,77 @@ var gIdentityHandler = {
    * Determine the identity of the page being displayed by examining its SSL cert
    * (if available) and, if necessary, update the UI to reflect this.  Intended to
    * be called by onSecurityChange
    *
    * @param PRUint32 state
    * @param nsIURI uri The address for which the UI should be updated.
    */
   checkIdentity : function(state, uri) {
-    var currentStatus = gBrowser.securityUI
-                                .QueryInterface(Components.interfaces.nsISSLStatusProvider)
-                                .SSLStatus;
-    this._lastStatus = currentStatus;
-    this._lastUri = uri;
-
     let nsIWebProgressListener = Ci.nsIWebProgressListener;
 
-    // For some URIs like data: we can't get a host and so can't do
-    // anything useful here.
+    // For some URIs like data: we can't get a host. URIs without a host will
+    // usually be treated as a non-secure connection if they're not on the
+    // whitelist below and don't resolve to file:// URIs internally.
     let unknown = false;
     try {
       uri.host;
     } catch (e) { unknown = true; }
 
     // Chrome URIs however get special treatment. Some chrome URIs are
     // whitelisted to provide a positive security signal to the user.
     let whitelist = /^about:(accounts|addons|app-manager|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|sessionrestore|support|welcomeback)/i;
     let isChromeUI = uri.schemeIs("about") && whitelist.test(uri.spec);
+    let mode = this.IDENTITY_MODE_UNKNOWN;
+
     if (isChromeUI) {
-      this.setMode(this.IDENTITY_MODE_CHROMEUI);
+      mode = this.IDENTITY_MODE_CHROMEUI;
     } else if (unknown) {
-      this.setMode(this.IDENTITY_MODE_UNKNOWN);
+      // Use default mode.
     } else if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       if (state & nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT) {
-        this.setMode(this.IDENTITY_MODE_MIXED_ACTIVE_BLOCKED_IDENTIFIED);
+        mode = this.IDENTITY_MODE_MIXED_ACTIVE_BLOCKED_IDENTIFIED;
       } else {
-        this.setMode(this.IDENTITY_MODE_IDENTIFIED);
+        mode = this.IDENTITY_MODE_IDENTIFIED;
       }
     } else if (state & nsIWebProgressListener.STATE_IS_SECURE) {
       if (state & nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT) {
-        this.setMode(this.IDENTITY_MODE_MIXED_ACTIVE_BLOCKED);
+        mode = this.IDENTITY_MODE_MIXED_ACTIVE_BLOCKED;
       } else {
-        this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
+        mode = this.IDENTITY_MODE_DOMAIN_VERIFIED;
       }
     } else if (state & nsIWebProgressListener.STATE_IS_BROKEN) {
       if (state & nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) {
-        this.setMode(this.IDENTITY_MODE_MIXED_ACTIVE_LOADED);
+        mode = this.IDENTITY_MODE_MIXED_ACTIVE_LOADED;
       } else if (state & nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT) {
-        this.setMode(this.IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED);
+        mode = 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);
+        mode = this.IDENTITY_MODE_MIXED_DISPLAY_LOADED;
       } else {
-        this.setMode(this.IDENTITY_MODE_USES_WEAK_CIPHER);
+        mode = this.IDENTITY_MODE_USES_WEAK_CIPHER;
       }
-    } else {
-      // 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);
-      }
-    }
+    }
+
+    // We need those values later when populating the control center.
+    this._uri = uri;
+    this._state = state;
+    this._isChromeUI = isChromeUI;
+    this._sslStatus =
+      gBrowser.securityUI.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+
+    // Update the identity block.
+    if (this._identityBox) {
+      this._identityBox.className = mode;
+      this.refreshIdentityBlock(mode);
+    }
+
+    // NOTE: We do NOT update the identity popup (the control center) when
+    // we receive a new security state. If the user opened the popup and looks
+    // at the provided information we don't want to suddenly change the panel
+    // contents.
 
     // 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 &
         (nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT |
@@ -6956,77 +6964,53 @@ var gIdentityHandler = {
    * Return the eTLD+1 version of the current hostname
    */
   getEffectiveHost : function() {
     if (!this._IDNService)
       this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
                          .getService(Ci.nsIIDNService);
     try {
       let baseDomain =
-        Services.eTLD.getBaseDomainFromHost(this._lastUri.host);
+        Services.eTLD.getBaseDomainFromHost(this._uri.host);
       return this._IDNService.convertToDisplayIDN(baseDomain, {});
     } catch (e) {
       // If something goes wrong (e.g. host is an IP address) just fail back
       // to the full domain.
-      return this._lastUri.host;
-    }
-  },
-
-  /**
-   * Update the UI to reflect the specified mode, which should be one of the
-   * IDENTITY_MODE_* constants.
-   */
-  setMode : function(newMode) {
-    if (!this._identityBox) {
-      // No identity box means the identity box is not visible, in which
-      // case there's nothing to do.
-      return;
-    }
-
-    this._identityPopup.className = newMode;
-    this._identityBox.className = newMode;
-    this.setIdentityMessages(newMode);
-
-    // Update the popup too, if it's open
-    if (this._identityPopup.state == "open") {
-      this.setPopupMessages(newMode);
-      this.updateSitePermissions();
-    }
-
-    this._mode = newMode;
+      return this._uri.host;
+    }
   },
 
   /**
    * Set up the messages for the primary identity UI based on the specified mode,
    * and the details of the SSL cert, where applicable
    *
    * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
    */
-  setIdentityMessages : function(newMode) {
+  refreshIdentityBlock(newMode) {
     let icon_label = "";
     let tooltip = "";
     let icon_country_label = "";
     let icon_labels_dir = "ltr";
 
     switch (newMode) {
     case this.IDENTITY_MODE_DOMAIN_VERIFIED:
     case this.IDENTITY_MODE_MIXED_ACTIVE_BLOCKED: {
       let iData = this.getIdentityData();
 
       // Verifier is either the CA Org, for a normal cert, or a special string
       // for certs that are trusted because of a security exception.
       tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
                                                     [iData.caOrg]);
 
       // This can't throw, because URI's with a host that throw don't end up in this case.
-      let host = this._lastUri.host;
+      let host = this._uri.host;
       let port = 443;
       try {
-        if (this._lastUri.port > 0)
-          port = this._lastUri.port;
+        if (this._uri.port > 0)
+          port = this._uri.port;
       } catch (e) {}
 
       if (this._overrideService.hasMatchingOverride(host, port, iData.cert, {}, {}))
         tooltip = gNavigatorBundle.getString("identity.identified.verified_by_you");
 
       break; }
     case this.IDENTITY_MODE_IDENTIFIED:
     case this.IDENTITY_MODE_MIXED_ACTIVE_BLOCKED_IDENTIFIED: {
@@ -7065,92 +7049,154 @@ var gIdentityHandler = {
     // Hide completely if the organization label is empty
     this._identityIconLabel.parentNode.collapsed = icon_label ? false : true;
   },
 
   /**
    * Set up the title and content messages for the identity message popup,
    * based on the specified mode, and the details of the SSL cert, where
    * applicable
-   *
-   * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
    */
-  setPopupMessages : function(newMode) {
-
-    this._identityPopup.className = newMode;
-    this._identityPopupMainView.className = newMode;
-    this._identityPopupSecurityView.className = newMode;
-    this._identityPopupSecurityContent.className = newMode;
+  refreshIdentityPopup() {
+    // Update the "Learn More" hrefs for Mixed Content Blocking.
+    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
+    let learnMoreHref = `${baseURL}mixed-content`;
+    this._identityPopupMixedContentLearnMore.setAttribute("href", learnMoreHref);
+
+    // Basic connection properties.
+    let isBroken = this._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
+    let isSecure = this._state & Ci.nsIWebProgressListener.STATE_IS_SECURE;
+    let isEV = this._state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
+
+    // Determine connection security information.
+    let connection = "not-secure";
+    if (this._isChromeUI) {
+      connection = "chrome";
+    } else if (this._isURILoadedFromFile(this._uri)) {
+      connection = "file";
+    } else if (isEV) {
+      connection = "secure-ev";
+    } else if (isSecure) {
+      connection = "secure";
+    }
+
+    // Mixed content flags.
+    let isMixedActiveContentLoaded =
+      this._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
+    let isMixedActiveContentBlocked =
+      this._state & Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
+    let isMixedPassiveContentLoaded =
+      this._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
+
+    // Determine the mixed content state.
+    let mixedcontent = [];
+    if (isMixedPassiveContentLoaded) {
+      mixedcontent.push("passive-loaded");
+    }
+    if (isMixedActiveContentLoaded) {
+      mixedcontent.push("active-loaded");
+    } else if (isMixedActiveContentBlocked) {
+      mixedcontent.push("active-blocked");
+    }
+    mixedcontent = mixedcontent.join(" ");
+
+    // We have no specific flags for weak ciphers (yet). If a connection is
+    // broken and we can't detect any mixed active content loaded then it's
+    // a weak cipher.
+    let ciphers = "";
+    if (isBroken && !isMixedActiveContentLoaded) {
+      ciphers = "weak";
+    }
+
+    // Update all elements.
+    let elementIDs = [
+      "identity-popup",
+      "identity-popup-securityView-body",
+    ];
+
+    function updateAttribute(elem, attr, value) {
+      if (value) {
+        elem.setAttribute(attr, value);
+      } else {
+        elem.removeAttribute(attr);
+      }
+    }
+
+    for (let id of elementIDs) {
+      let element = document.getElementById(id);
+      updateAttribute(element, "connection", connection);
+      updateAttribute(element, "ciphers", ciphers);
+      updateAttribute(element, "mixedcontent", mixedcontent);
+    }
 
     // Initialize the optional strings to empty values
     let supplemental = "";
     let verifier = "";
     let host = "";
     let owner = "";
 
     try {
       host = this.getEffectiveHost();
     } catch (e) {
       // Some URIs might have no hosts.
     }
 
+    // Fallback for special protocols.
     if (!host) {
-      // Fallback for special protocols.
-      host = this._lastUri.specIgnoringRef;
-    }
-
-    switch (newMode) {
-    case this.IDENTITY_MODE_DOMAIN_VERIFIED:
-    case this.IDENTITY_MODE_MIXED_ACTIVE_BLOCKED:
+      host = this._uri.specIgnoringRef;
+    }
+
+    // Fill in the CA name if we have a valid TLS certificate.
+    if (isSecure) {
       verifier = this._identityBox.tooltipText;
-      break;
-    case this.IDENTITY_MODE_IDENTIFIED:
-    case this.IDENTITY_MODE_MIXED_ACTIVE_BLOCKED_IDENTIFIED: {
-      // If it's identified, then we can populate the dialog with credentials
+    }
+
+    // Fill in organization information if we have a valid EV certificate.
+    if (isEV) {
       let iData = this.getIdentityData();
       host = owner = iData.subjectOrg;
       verifier = this._identityBox.tooltipText;
 
       // Build an appropriate supplemental block out of whatever location data we have
       if (iData.city)
         supplemental += iData.city + "\n";
       if (iData.state && iData.country)
         supplemental += gNavigatorBundle.getFormattedString("identity.identified.state_and_country",
                                                             [iData.state, iData.country]);
       else if (iData.state) // State only
         supplemental += iData.state;
       else if (iData.country) // Country only
         supplemental += iData.country;
-      break;
-    }
-    case this.IDENTITY_MODE_UNKNOWN:
-      supplemental = gNavigatorBundle.getString("identity.not_secure");
-      break;
-    case this.IDENTITY_MODE_USES_WEAK_CIPHER:
-      supplemental = gNavigatorBundle.getString("identity.uses_weak_cipher");
-      break;
-    case this.IDENTITY_MODE_MIXED_DISPLAY_LOADED:
-    case this.IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED:
-      supplemental = gNavigatorBundle.getString("identity.mixed_display_loaded");
-      break;
-    case this.IDENTITY_MODE_MIXED_ACTIVE_LOADED:
-      supplemental = gNavigatorBundle.getString("identity.mixed_active_loaded2");
-      break;
     }
 
     // Push the appropriate strings out to the UI. Need to use |value| for the
     // host as it's a <label> that will be cropped if too long. Using
     // |textContent| would simply wrap the value.
     this._identityPopupContentHost.setAttribute("value", host);
     this._identityPopupContentOwner.textContent = owner;
     this._identityPopupContentSupp.textContent = supplemental;
     this._identityPopupContentVerif.textContent = verifier;
 
-    // Hide subviews when updating panel information.
-    document.getElementById("identity-popup-multiView").showMainView();
+    // Update per-site permissions section.
+    this.updateSitePermissions();
+  },
+
+  _isURILoadedFromFile(uri) {
+    // 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 chanOptions = {uri, loadUsingSystemPrincipal: true};
+    let resolvedURI = NetUtil.newChannel(chanOptions).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);
+    }
+
+    // Check the URI again after resolving.
+    return resolvedURI.schemeIs("file");
   },
 
   /**
    * Click handler for the identity-box element in primary chrome.
    */
   handleIdentityButtonEvent : function(event) {
     event.stopPropagation();
 
@@ -7165,19 +7211,17 @@ var gIdentityHandler = {
       return;
     }
 
     // Make sure that the display:none style we set in xul is removed now that
     // the popup is actually needed
     this._identityPopup.hidden = false;
 
     // Update the popup strings
-    this.setPopupMessages(this._identityBox.className);
-
-    this.updateSitePermissions();
+    this.refreshIdentityPopup();
 
     // Add the "open" attribute to the identity box for styling
     this._identityBox.setAttribute("open", "true");
 
     // Now open the popup, anchored off the primary chrome element
     this._identityPopup.openPopup(this._identityIcons, "bottomcenter topleft");
   },
 
--- a/browser/base/content/test/general/browser_bug590206.js
+++ b/browser/base/content/test/general/browser_bug590206.js
@@ -1,22 +1,29 @@
 /*
  * Test the identity mode UI for a variety of page types
  */
 
+"use strict";
+
 const DUMMY = "browser/browser/base/content/test/general/dummy_page.html";
 
 function loadNewTab(url) {
   return BrowserTestUtils.openNewForegroundTab(gBrowser, url);
 }
 
 function getIdentityMode() {
   return document.getElementById("identity-box").className;
 }
 
+function getConnectionState() {
+  gIdentityHandler.refreshIdentityPopup();
+  return document.getElementById("identity-popup").getAttribute("connection");
+}
+
 // 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");
@@ -44,23 +51,23 @@ add_task(function* test_blank() {
 
   gBrowser.removeTab(newTab);
 });
 
 add_task(function* test_chrome() {
   let oldTab = gBrowser.selectedTab;
 
   let newTab = yield loadNewTab("chrome://mozapps/content/extensions/extensions.xul");
-  is(getIdentityMode(), "fileURI", "Identity should be file");
+  is(getConnectionState(), "file", "Connection should be file");
 
   gBrowser.selectedTab = oldTab;
   is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
   gBrowser.selectedTab = newTab;
-  is(getIdentityMode(), "fileURI", "Identity should be file");
+  is(getConnectionState(), "file", "Connection should be file");
 
   gBrowser.removeTab(newTab);
 });
 
 add_task(function* test_https() {
   let oldTab = gBrowser.selectedTab;
 
   let newTab = yield loadNewTab("https://example.com/" + DUMMY);
@@ -90,40 +97,40 @@ add_task(function* test_addons() {
   gBrowser.removeTab(newTab);
 });
 
 add_task(function* test_file() {
   let oldTab = gBrowser.selectedTab;
   let fileURI = getTestFilePath("");
 
   let newTab = yield loadNewTab(fileURI);
-  is(getIdentityMode(), "fileURI", "Identity should be file");
+  is(getConnectionState(), "file", "Connection should be file");
 
   gBrowser.selectedTab = oldTab;
   is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
   gBrowser.selectedTab = newTab;
-  is(getIdentityMode(), "fileURI", "Identity should be file");
+  is(getConnectionState(), "file", "Connection should be file");
 
   gBrowser.removeTab(newTab);
 });
 
 add_task(function test_resource_uri() {
   let oldTab = gBrowser.selectedTab;
-  let dataURI = "resource://gre/modules/Services.jsm"
+  let dataURI = "resource://gre/modules/Services.jsm";
 
   let newTab = yield loadNewTab(dataURI);
 
-  is(getIdentityMode(), "fileURI", "Identity should be unknown");
+  is(getConnectionState(), "file", "Connection should be file");
 
   gBrowser.selectedTab = oldTab;
   is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
 
   gBrowser.selectedTab = newTab;
-  is(getIdentityMode(), "fileURI", "Identity should be unknown");
+  is(getConnectionState(), "file", "Connection should be file");
 
   gBrowser.removeTab(newTab);
 });
 
 add_task(function test_data_uri() {
   let oldTab = gBrowser.selectedTab;
   let dataURI = "data:text/html,hi"
 
@@ -133,8 +140,24 @@ add_task(function test_data_uri() {
   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_about_uri() {
+  let oldTab = gBrowser.selectedTab;
+  let aboutURI = "about:robots"
+
+  let newTab = yield loadNewTab(aboutURI);
+  is(getConnectionState(), "file", "Connection should be file");
+
+  gBrowser.selectedTab = oldTab;
+  is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+  gBrowser.selectedTab = newTab;
+  is(getConnectionState(), "file", "Connection should be file");
+
+  gBrowser.removeTab(newTab);
+});
--- a/browser/base/content/test/general/browser_identity_UI.js
+++ b/browser/base/content/test/general/browser_identity_UI.js
@@ -38,17 +38,16 @@ var tests = [
   {
     name: "subdomain with port",
     location: "http://sub1.test1.example.org:8000/",
     effectiveHost: "example.org"
   },
   {
     name: "subdomain HTTPS",
     location: "https://test1.example.com/",
-
     effectiveHost: "example.com",
     isHTTPS: true
   },
   {
     name: "view-source HTTPS",
     location: "view-source:https://example.com/",
     effectiveHost: null,
     isHTTPS: true
@@ -99,17 +98,19 @@ function nextTest() {
     if (!gForward)
       gTestDesc += " (second time)";
     content.location.reload(true);
   }
 }
 
 function checkResult() {
   // Sanity check other values, and the value of gIdentityHandler.getEffectiveHost()
-  is(gIdentityHandler._lastUri.spec, gCurrentTest.location, "location matches for test " + gTestDesc);
+  is(gIdentityHandler._uri.spec, gCurrentTest.location, "location matches for test " + gTestDesc);
   // getEffectiveHost can't be called for all modes
-  if (gCurrentTest.effectiveHost === null)
-    is(gIdentityHandler._mode == gIdentityHandler.IDENTITY_MODE_UNKNOWN || gIdentityHandler._mode == gIdentityHandler.IDENTITY_MODE_CHROMEUI, true, "mode matched");
-  else
+  if (gCurrentTest.effectiveHost === null) {
+    let identityBox = document.getElementById("identity-box");
+    is(identityBox.className == gIdentityHandler.IDENTITY_MODE_UNKNOWN || identityBox.className == gIdentityHandler.IDENTITY_MODE_CHROMEUI, true, "mode matched");
+  } else {
     is(gIdentityHandler.getEffectiveHost(), gCurrentTest.effectiveHost, "effectiveHost matches for test " + gTestDesc);
+  }
 
   executeSoon(nextTest);
 }
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -5,57 +5,69 @@
 <panel id="identity-popup"
        type="arrow"
        hidden="true"
        onpopupshown="gIdentityHandler.onPopupShown(event);"
        onpopuphidden="gIdentityHandler.onPopupHidden(event);"
        orient="vertical">
 
   <broadcasterset>
-    <broadcaster id="identity-popup-content-host" value=""/>
+    <broadcaster id="identity-popup-content-host" class="identity-popup-headline" crop="end"/>
+    <broadcaster id="identity-popup-mcb-learn-more" class="text-link plain" value="&identity.learnMore;"/>
   </broadcasterset>
 
   <panelmultiview id="identity-popup-multiView"
                   mainViewId="identity-popup-mainView">
     <panelview id="identity-popup-mainView" flex="1">
 
       <!-- Security Section -->
-      <hbox class="identity-popup-section">
+      <hbox id="identity-popup-security" class="identity-popup-section">
         <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;"/>
+          <label observes="identity-popup-content-host"/>
+          <description class="identity-popup-connection-not-secure"
+                       value="&identity.connectionNotSecure;"
+                       when-connection="not-secure"/>
+          <description class="identity-popup-connection-secure"
+                       value="&identity.connectionSecure;"
+                       when-connection="secure secure-ev"/>
+          <description value="&identity.connectionInternal;"
+                       when-connection="chrome"/>
+          <description value="&identity.connectionFile;"
+                       when-connection="file"/>
+
+          <vbox id="identity-popup-security-descriptions">
+            <description class="identity-popup-warning-gray"
+                         when-mixedcontent="active-blocked">&identity.activeBlocked;</description>
+            <description class="identity-popup-warning-yellow"
+                         when-mixedcontent="passive-loaded">&identity.passiveLoaded;</description>
+            <description when-mixedcontent="active-loaded">&identity.activeLoaded;</description>
+            <description class="identity-popup-warning-yellow"
+                         when-ciphers="weak">&identity.weakEncryption;</description>
+          </vbox>
         </vbox>
-        <button class="identity-popup-expander"
+        <button id="identity-popup-security-expander"
+                class="identity-popup-expander"
+                when-connection="not-secure secure secure-ev"
                 oncommand="gIdentityHandler.toggleSubView('security', this)"/>
       </hbox>
 
       <!-- Tracking Protection Section -->
-      <hbox id="tracking-protection-container" class="identity-popup-section">
+      <hbox id="tracking-protection-container"
+            class="identity-popup-section"
+            when-connection="not-secure secure secure-ev file">
         <vbox id="tracking-protection-content" flex="1">
-          <description class="identity-popup-text identity-popup-headline"
+          <description class="identity-popup-headline"
                        crop="end"
                        value="&trackingProtection.title;" />
 
           <label id="tracking-blocked"
-                 class="identity-popup-text"
                  crop="end">&trackingProtection.detectedBlocked2;</label>
           <label id="tracking-loaded"
-                 class="identity-popup-text"
                  crop="end">&trackingProtection.detectedNotBlocked2;</label>
           <label id="tracking-not-detected"
-                 class="identity-popup-text"
                  crop="end">&trackingProtection.notDetected2;</label>
 
           <button id="tracking-action-unblock"
                   label="&trackingProtection.unblock.label;"
                   accesskey="&trackingProtection.unblock.accesskey;"
                   oncommand="TrackingProtection.disableForCurrentPage();" />
           <button id="tracking-action-unblock-private"
                   label="&trackingProtection.unblockPrivate.label;"
@@ -66,17 +78,17 @@
                   accesskey="&trackingProtection.block2.accesskey;"
                   oncommand="TrackingProtection.enableForCurrentPage();" />
         </vbox>
       </hbox>
 
       <!-- Permissions Section -->
       <hbox id="identity-popup-permissions" class="identity-popup-section">
         <vbox id="identity-popup-permissions-content" flex="1">
-          <label class="identity-popup-text identity-popup-headline"
+          <label class="identity-popup-headline"
                  value="&identity.permissions;"/>
           <vbox id="identity-popup-permission-list"/>
         </vbox>
       </hbox>
 
       <spacer flex="1"/>
 
       <!-- More Information Button -->
@@ -85,34 +97,68 @@
                 label="&identity.moreInfoLinkText2;"
                 oncommand="gIdentityHandler.handleMoreInfoClick(event);"/>
       </hbox>
     </panelview>
 
     <!-- Security SubView -->
     <panelview id="identity-popup-securityView" flex="1">
       <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;"/>
+        <label observes="identity-popup-content-host"/>
+        <description class="identity-popup-connection-not-secure"
+                     value="&identity.connectionNotSecure;"
+                     when-connection="not-secure"/>
+        <description class="identity-popup-connection-secure"
+                     value="&identity.connectionSecure;"
+                     when-connection="secure secure-ev"/>
       </vbox>
 
-      <description id="identity-popup-content-verifier"
-                   class="identity-popup-text"/>
+      <vbox id="identity-popup-securityView-body">
+        <!-- (EV) Certificate Information -->
+        <description id="identity-popup-content-verified-by"
+                     when-connection="secure-ev">&identity.connectionVerified;</description>
+        <description id="identity-popup-content-owner"
+                     when-connection="secure-ev"
+                     class="header"/>
+        <description id="identity-popup-content-supplemental"
+                     when-connection="secure-ev"/>
+        <description id="identity-popup-content-verifier"
+                     when-connection="secure secure-ev"/>
+
+        <!-- Connection is Not Secure -->
+        <description when-connection="not-secure">&identity.description.insecure;</description>
+
+        <!-- Weak Cipher -->
+        <description when-ciphers="weak">&identity.description.weakCipher;</description>
+        <description class="identity-popup-warning-yellow"
+                     when-ciphers="weak">&identity.description.weakCipher2;</description>
+
+        <!-- Active Mixed Content Blocked -->
+        <description class="identity-popup-warning-gray"
+                     when-mixedcontent="active-blocked">&identity.description.activeBlocked; <label observes="identity-popup-mcb-learn-more"/></description>
 
-      <description id="identity-popup-securityView-connection"
-                   class="identity-popup-text">&identity.connectionVerified;</description>
+        <!-- Passive Mixed Content Loaded -->
+        <description when-mixedcontent="passive-loaded">&identity.description.passiveLoaded;</description>
+        <description class="identity-popup-warning-yellow"
+                     when-mixedcontent="passive-loaded">&identity.description.passiveLoaded2; <label observes="identity-popup-mcb-learn-more"/></description>
+
+        <!-- Passive Mixed Content Loaded, Active Mixed Content Blocked -->
+        <description when-mixedcontent="passive-loaded active-blocked">&identity.description.passiveLoaded;</description>
+        <description when-mixedcontent="passive-loaded active-blocked"
+                     class="identity-popup-warning-yellow">&identity.description.passiveLoaded3; <label observes="identity-popup-mcb-learn-more"/></description>
 
-      <description id="identity-popup-content-owner"
-                   class="identity-popup-text"/>
-      <description id="identity-popup-content-supplemental"
-                   class="identity-popup-text"/>
+        <!-- Active Mixed Content Blocking Disabled -->
+        <description when-mixedcontent="active-loaded">&identity.description.activeLoaded;</description>
+        <description when-mixedcontent="active-loaded">&identity.description.activeLoaded2;</description>
+
+        <!-- Buttons to enable/disable mixed content blocking. -->
+        <button when-mixedcontent="active-blocked"
+                label="&identity.disableMixedContentBlocking.label;"
+                accesskey="&identity.disableMixedContentBlocking.accesskey;"
+                oncommand="gIdentityHandler.disableMixedContentProtection()"/>
+        <button when-mixedcontent="active-loaded"
+                label="&identity.enableMixedContentBlocking.label;"
+                accesskey="&identity.enableMixedContentBlocking.accesskey;"
+                oncommand="gIdentityHandler.enableMixedContentProtection()"/>
+      </vbox>
     </panelview>
   </panelmultiview>
 </panel>
--- a/browser/components/loop/test/shared/frontend_tester.py
+++ b/browser/components/loop/test/shared/frontend_tester.py
@@ -67,17 +67,19 @@ class BaseTestFrontendUnits(MarionetteTe
         # Note: when e10s is enabled by default, this pref can go away. The automatic
         # restart will also go away if this is still the only pref set here.
         self.marionette.enforce_gecko_prefs({
             "browser.tabs.remote.autostart": True
         })
 
         # This extends the timeout for find_element. We need this as the tests
         # take an amount of time to run after loading, which we have to wait for.
-        self.marionette.set_search_timeout(60000)
+        self.marionette.set_search_timeout(120000)
+
+        self.marionette.timeouts("page load", 120000)
 
     # srcdir_path should be the directory relative to this file.
     def set_server_prefix(self, srcdir_path):
         # We may be run from a different path than topsrcdir, e.g. in the case
         # of packaged tests. If so, then we have to work out the right directory
         # for the local server.
 
         # First find the top of the working directory.
--- a/browser/components/loop/test/standalone/test_standalone_all.py
+++ b/browser/components/loop/test/standalone/test_standalone_all.py
@@ -1,16 +1,16 @@
 # need to get this dir in the path so that we make the import work
 import os
 import sys
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'shared'))
 
 from frontend_tester import BaseTestFrontendUnits
 
 
-class TestDesktopUnits(BaseTestFrontendUnits):
+class TestStandaloneUnits(BaseTestFrontendUnits):
 
     def setUp(self):
-        super(TestDesktopUnits, self).setUp()
+        super(TestStandaloneUnits, self).setUp()
         self.set_server_prefix("../standalone/")
 
     def test_units(self):
         self.check_page("index.html")
--- a/browser/components/loop/test/ui-showcase/test_ui-showcase.py
+++ b/browser/components/loop/test/ui-showcase/test_ui-showcase.py
@@ -1,16 +1,16 @@
 # need to get this dir in the path so that we make the import work
 import os
 import sys
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'shared'))
 
 from frontend_tester import BaseTestFrontendUnits
 
 
-class TestDesktopUnits(BaseTestFrontendUnits):
+class TestUiShowcaseUnits(BaseTestFrontendUnits):
 
     def setUp(self):
-        super(TestDesktopUnits, self).setUp()
+        super(TestUiShowcaseUnits, self).setUp()
         self.set_server_prefix("../../ui/")
 
     def test_units(self):
         self.check_page("index.html")
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -288,17 +288,17 @@
                       <button id="rejectReSignIn" label="&signIn.label;"/>
                       <button id="rejectUnlinkFxaAccount" label="&forget.label;"/>
                     </hbox>
                   </vbox>
                 </hbox>
               </deck>
         </groupbox>
         <groupbox id="syncOptions">
-          <caption><label>&signedIn.engines.caption;</label></caption>
+          <caption><label>&signedIn.engines.label;</label></caption>
           <hbox id="fxaSyncEngines">
             <vbox align="start">
               <checkbox label="&engine.tabs.label;"
                         accesskey="&engine.tabs.accesskey;"
                         preference="engine.tabs"/>
               <checkbox label="&engine.bookmarks.label;"
                         accesskey="&engine.bookmarks.accesskey;"
                         preference="engine.bookmarks"/>
@@ -355,17 +355,17 @@
       <image class="androidLogo"/>
       <label class="text-link"
              href="https://www.mozilla.org/firefox/android/">
         &mobilePromo.androidLink;
       </label>
       <label>&mobilePromo.end;</label>
     </hbox>
     <spacer flex="1"/>
-    <vbox id="tosPP-small">
+    <vbox id="tosPP-small" align="start">
       <label id="tosPP-small-ToS" class="text-link">
         &prefs.tosLink.label;
       </label>
       <label id="tosPP-small-PP" class="text-link">
         &fxaPrivacyNotice.link.label;
       </label>
     </vbox>
     <label class="androidAttribution">&androidAttribution;</label>
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -686,16 +686,39 @@ you can use these alternative items. Oth
 <!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.">
 
+<!-- Strings for connection state warnings. -->
+<!ENTITY identity.activeBlocked "&brandShortName; has blocked parts of this page that are not secure.">
+<!ENTITY identity.passiveLoaded "Parts of this page are not secure (such as images).">
+<!ENTITY identity.activeLoaded "You have disabled protection on this page.">
+<!ENTITY identity.weakEncryption "This page uses weak encryption.">
+
+<!-- Strings for connection state warnings in the subview. -->
+<!ENTITY identity.description.insecure "Your connection to this site is not private. Information you submit could be viewed by others (like passwords, messages, credit cards, etc.).">
+<!ENTITY identity.description.weakCipher "Your connection to this website uses weak encryption and is not private.">
+<!ENTITY identity.description.weakCipher2 "Other people can view your information or modify the website's behavior.">
+<!ENTITY identity.description.activeBlocked "&brandShortName; has blocked parts of this page that are not secure.">
+<!ENTITY identity.description.passiveLoaded "Your connection is not private and information you share with the site could be viewed by others.">
+<!ENTITY identity.description.passiveLoaded2 "This website contains content that is not secure (such as images).">
+<!ENTITY identity.description.passiveLoaded3 "Although &brandShortName; has blocked some content, there is still content on the page that is not secure (such as images).">
+<!ENTITY identity.description.activeLoaded "This website contains content that is not secure (such as scripts) and your connection to it is not private.">
+<!ENTITY identity.description.activeLoaded2 "Information you share with this site could be viewed by others (like passwords, messages, credit cards, etc.).">
+
+<!ENTITY identity.enableMixedContentBlocking.label "Enable protection">
+<!ENTITY identity.enableMixedContentBlocking.accesskey "E">
+<!ENTITY identity.disableMixedContentBlocking.label "Disable protection for now">
+<!ENTITY identity.disableMixedContentBlocking.accesskey "D">
+<!ENTITY identity.learnMore "Learn More">
+
 <!ENTITY identity.moreInfoLinkText2 "More Information">
 
 <!ENTITY identity.permissions "Permissions">
 
 <!-- Name for the tabs toolbar as spoken by screen readers.
      The word "toolbar" is appended automatically and should not be contained below! -->
 <!ENTITY tabsToolbar.label "Browser tabs">
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -324,21 +324,16 @@ offlineApps.notNowAccessKey=N
 offlineApps.usage=This website (%S) is now storing more than %SMB of data on your computer for offline use.
 offlineApps.manageUsage=Show settings
 offlineApps.manageUsageAccessKey=S
 
 identity.identified.verifier=Verified by: %S
 identity.identified.verified_by_you=You have added a security exception for this site.
 identity.identified.state_and_country=%S, %S
 
-identity.not_secure=Your connection to this site is not private. Information you submit could be viewable to others (for example passwords, messages, credit cards, etc.).
-identity.uses_weak_cipher=Your connection to this website uses weak encryption and is not private. Other people can view your information or modify the website's behavior.
-identity.mixed_display_loaded=The connection to this website is not fully secure because it contains unencrypted elements (such as images).
-identity.mixed_active_loaded2=This website contains interactive content that isn't encrypted (such as scripts). Other people can view your information or modify the website's behavior.
-
 identity.unknown.tooltip=This website does not supply identity information.
 
 trackingProtection.intro.title=How Tracking Protection works
 # LOCALIZATION NOTE (trackingProtection.intro.description): %S is brandShortName
 trackingProtection.intro.description=When the shield is visible, that means Firefox is actively blocking content that tracks you.
 # LOCALIZATION NOTE (trackingProtection.intro.step1of3): Indicates that the intro panel is step one of three in a tour.
 trackingProtection.intro.step1of3=1 of 3
 trackingProtection.intro.nextButton.label=Next
--- a/browser/locales/en-US/chrome/browser/preferences/sync.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/sync.dtd
@@ -84,15 +84,15 @@ both, to better adapt this sentence to t
 <!ENTITY welcome.useOldSync.label "Using an older version of Sync?">
 
 <!ENTITY signedOut.caption            "Take your Web with you">
 <!ENTITY signedOut.description        "Synchronize your bookmarks, history, tabs, passwords, add-ons, and preferences across all your devices.">
 <!ENTITY signedOut.accountBox.title   "Connect with a &syncBrand.fxAccount.label;">
 <!ENTITY signedOut.accountBox.create  "Create Account">
 <!ENTITY signedOut.accountBox.signin  "Sign In">
 
-<!ENTITY signedIn.engines.caption     "Sync between all devices">
+<!ENTITY signedIn.engines.label       "Sync across all devices">
 
 <!ENTITY mobilePromo.start            "Download Firefox for ">
 <!-- LOCALIZATION NOTE (mobilePromo.androidLink): This is a link title that links to https://www.mozilla.org/firefox/android/ -->
 <!ENTITY mobilePromo.androidLink      "Androidâ„¢">
 <!ENTITY mobilePromo.end              " to sync with your mobile device.">
 <!ENTITY androidAttribution           "Android is a trademark of Google Inc.">
--- a/browser/themes/osx/controlcenter/panel.css
+++ b/browser/themes/osx/controlcenter/panel.css
@@ -16,25 +16,28 @@
 
 .identity-popup-expander:-moz-focusring > .button-box,
 #identity-popup-more-info-button:-moz-focusring > .button-box {
   @hudButtonFocused@
 }
 
 #tracking-action-block,
 #tracking-action-unblock,
-#tracking-action-unblock-private {
+#tracking-action-unblock-private,
+#identity-popup-securityView-body > button {
   @hudButton@
   min-height: 30px;
 }
 
 #tracking-action-block:hover:active,
 #tracking-action-unblock:hover:active,
-#tracking-action-unblock-private:hover:active {
+#tracking-action-unblock-private:hover:active,
+#identity-popup-securityView-body > button:hover:active {
   @hudButtonPressed@
 }
 
 #tracking-action-block:-moz-focusring,
 #tracking-action-unblock:-moz-focusring,
-#tracking-action-unblock-private:-moz-focusring {
+#tracking-action-unblock-private:-moz-focusring,
+#identity-popup-securityView-body > button:-moz-focusring {
   @hudButtonFocused@
 }
 
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -190,18 +190,20 @@ browser.jar:
 * 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/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/controlcenter/warning-gray.svg  (../shared/controlcenter/warning-gray.svg)
+  skin/classic/browser/controlcenter/warning-yellow.svg  (../shared/controlcenter/warning-yellow.svg)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customize-illustration.png  (../shared/customizableui/customize-illustration.png)
   skin/classic/browser/customizableui/customize-illustration@2x.png  (../shared/customizableui/customize-illustration@2x.png)
   skin/classic/browser/customizableui/customize-illustration-rtl.png  (../shared/customizableui/customize-illustration-rtl.png)
   skin/classic/browser/customizableui/customize-illustration-rtl@2x.png  (../shared/customizableui/customize-illustration-rtl@2x.png)
   skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -1,38 +1,44 @@
-/* Show the organization name only for EV certs. */
-#identity-popup-securityView:not(.verifiedIdentity) > #identity-popup-content-owner,
-#identity-popup-securityView:not(.verifiedIdentity) > #identity-popup-securityView-connection,
-/* Show the "Verified by" label only for DV and EV certs. */
-#identity-popup-securityView:not(.verifiedIdentity):not(.verifiedDomain) > #identity-popup-content-verifier,
-/* Show a longer explanation for non-secure sites, mixed content, and weak
-   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 {
+%if 0
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+%endif
+
+/* Hide all conditional elements by default. */
+:-moz-any([when-connection],[when-mixedcontent],[when-ciphers]) {
   display: none;
 }
 
-/* PANEL */
+/* Show the right elements for the right connection states. */
+#identity-popup[connection=not-secure] [when-connection~=not-secure],
+#identity-popup[connection=secure-ev] [when-connection~=secure-ev],
+#identity-popup[connection=secure] [when-connection~=secure],
+#identity-popup[connection=chrome] [when-connection~=chrome],
+#identity-popup[connection=file] [when-connection~=file],
+/* Show weak cipher messages when needed. */
+#identity-popup[ciphers=weak]:not([mixedcontent]) [when-ciphers~=weak],
+/* Show mixed content warnings when needed */
+#identity-popup[mixedcontent~=active-loaded] [when-mixedcontent=active-loaded],
+#identity-popup[mixedcontent~=passive-loaded]:not([mixedcontent~=active-loaded]) [when-mixedcontent=passive-loaded],
+#identity-popup[mixedcontent~=active-blocked]:not([mixedcontent~=passive-loaded]) [when-mixedcontent=active-blocked],
+/* Show the right elements when there is mixed passive content loaded and active blocked. */
+#identity-popup[mixedcontent~=active-blocked][mixedcontent~=passive-loaded] [when-mixedcontent~=active-blocked][when-mixedcontent~=passive-loaded],
+/* Show 'disable MCB' button always when there is mixed active content blocked. */
+#identity-popup-securityView-body[mixedcontent~=active-blocked] > button[when-mixedcontent=active-blocked] {
+  display: inherit;
+}
+
+/* Hide 'not secure' message in subview when weak cipher or mixed content messages are shown. */
+#identity-popup-securityView-body:-moz-any([mixedcontent],[ciphers]) > description[when-connection=not-secure],
+/* Hide 'passive-loaded (only)' message when there is mixed passive content loaded and active blocked. */
+#identity-popup-securityView-body[mixedcontent~=passive-loaded][mixedcontent~=active-blocked] > description[when-mixedcontent=passive-loaded] {
+  display: none;
+}
 
 #identity-popup,
 #identity-popup:not([panelopen]) .panel-viewstack[viewtype="main"]:not([transitioning]) #identity-popup-mainView {
   /* Tiny hack to ensure the panel shrinks back to its original
      size after closing a subview that is bigger than the main view. */
   max-height: 0;
 }
 
@@ -138,97 +144,114 @@
 
 .identity-popup-expander:hover:active {
   background-color: hsla(210,4%,10%,.12);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
 }
 
 /* CONTENT */
 
-.identity-popup-text {
+#identity-popup-security-content > description,
+#identity-popup-security-descriptions > description,
+#identity-popup-securityView-header > description,
+#identity-popup-securityView-body > description,
+#tracking-protection-content > label {
   white-space: pre-wrap;
   font-size: 110%;
   margin: 0;
 }
 
 .identity-popup-headline {
   margin: 2px 0 4px;
   font-size: 150%;
 }
 
-/* SECURITY */
+.identity-popup-warning-gray {
+  -moz-padding-start: 24px;
+  background: url(chrome://browser/skin/controlcenter/warning-gray.svg) no-repeat 0 50%;
+}
 
-#identity-popup-securityView > .identity-popup-text:not(#identity-popup-content-owner) {
-  margin: 2px 0 4px;
+.identity-popup-warning-yellow {
+  -moz-padding-start: 24px;
+  background: url(chrome://browser/skin/controlcenter/warning-yellow.svg) no-repeat 0 50%;
 }
 
+/* SECURITY */
+
 .identity-popup-connection-secure {
   color: #418220;
 }
 
 .identity-popup-connection-not-secure {
   color: #d74345;
 }
 
-#identity-popup-security-content.chromeUI {
-  background-image: url(chrome://branding/content/icon48.png);
-}
-
-/* SECURITY SUBVIEW */
-
 #identity-popup-securityView {
   padding-bottom: 2em;
   overflow: hidden;
 }
 
 #identity-popup-securityView,
 #identity-popup-security-content {
   background-image: url(chrome://browser/skin/controlcenter/conn-not-secure.svg);
 }
 
-#identity-popup-securityView.verifiedDomain,
-#identity-popup-securityView.verifiedIdentity,
-#identity-popup-security-content.verifiedDomain,
-#identity-popup-security-content.verifiedIdentity {
+#identity-popup[connection=chrome] #identity-popup-securityView,
+#identity-popup[connection=chrome] #identity-popup-security-content {
+  background-image: url(chrome://branding/content/icon48.png);
+}
+
+#identity-popup[connection^=secure] #identity-popup-securityView,
+#identity-popup[connection^=secure] #identity-popup-security-content {
   background-image: url(chrome://browser/skin/controlcenter/conn-secure.svg);
 }
 
-#identity-popup-securityView.weakCipher,
-#identity-popup-securityView.mixedDisplayContent,
-#identity-popup-securityView.mixedDisplayContentLoadedActiveBlocked,
-#identity-popup-security-content.weakCipher,
-#identity-popup-security-content.mixedDisplayContent,
-#identity-popup-security-content.mixedDisplayContentLoadedActiveBlocked {
+#identity-popup[ciphers=weak] #identity-popup-securityView,
+#identity-popup[ciphers=weak] #identity-popup-security-content,
+#identity-popup[mixedcontent~=passive-loaded] #identity-popup-securityView,
+#identity-popup[mixedcontent~=passive-loaded] #identity-popup-security-content {
   background-image: url(chrome://browser/skin/controlcenter/conn-degraded.svg);
 }
 
-#identity-popup-securityView.mixedActiveContent,
-#identity-popup-security-content.mixedActiveContent {
+#identity-popup[mixedcontent~=active-loaded] #identity-popup-securityView,
+#identity-popup[mixedcontent~=active-loaded] #identity-popup-security-content {
   background-image: url(chrome://browser/skin/controlcenter/mcb-disabled.svg);
 }
 
+#identity-popup-security-descriptions > description {
+  margin-top: 6px;
+  color: Graytext;
+}
+
 #identity-popup-securityView-header {
   border-bottom: 1px solid var(--panel-separator-color);
   padding-bottom: 1em;
-  margin-bottom: 1em;
 }
 
-#identity-popup-content-owner {
-  font-weight: 700;
+#identity-popup-securityView-body {
+  -moz-padding-end: 1em;
 }
 
-#identity-popup-content-verifier {
+#identity-popup-content-verifier ~ description {
+  margin-top: 1em;
   color: Graytext;
 }
 
-#identity-popup-content-owner,
-#identity-popup-securityView > #identity-popup-securityView-connection.identity-popup-text {
+description#identity-popup-content-verified-by,
+description#identity-popup-content-owner,
+description#identity-popup-content-verifier,
+#identity-popup-securityView-body > button {
   margin-top: 1em;
 }
 
+#identity-popup-securityView-body > button {
+  margin-inline-start: 0;
+  margin-inline-end: 0;
+}
+
 /* TRACKING PROTECTION */
 
 #tracking-protection-content {
   background-image: url("chrome://browser/skin/controlcenter/tracking-protection.svg");
 }
 
 #tracking-protection-content[state="loaded-tracking-content"]  {
   background-image: url("chrome://browser/skin/controlcenter/tracking-protection-disabled.svg");
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/controlcenter/warning-gray.svg
@@ -0,0 +1,9 @@
+<?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" xmlns:xlink="http://www.w3.org/1999/xlink"
+     width="16" height="16" viewBox="0 0 16 16">
+  <path fill="#808080" d="M14.8,12.5L9.3,1.9C9,1.3,8.5,1,8,1C7.5,1,7,1.3,6.7,1.9L1.2,12.5c-0.3,0.6-0.3,1.2,0,1.7C1.5,14.7,2,15,2.6,15h10.8 c0.6,0,1.1-0.3,1.4-0.8C15.1,13.7,15.1,13.1,14.8,12.5z"/>
+  <path fill="#fff" d="M8,11c-0.8,0-1.5,0.7-1.5,1.5C6.5,13.3,7.2,14,8,14 c0.8,0,1.5-0.7,1.5-1.5C9.5,11.7,8.8,11,8,11z M8,10L8,10C8.6,10,9,9.6,9,9l0.2-4.2c0-0.7-0.5-1.2-1.2-1.2S6.8,4.1,6.8,4.8L7,9 C7,9.6,7.4,10,8,10z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/controlcenter/warning-yellow.svg
@@ -0,0 +1,9 @@
+<?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" xmlns:xlink="http://www.w3.org/1999/xlink"
+     width="16" height="16" viewBox="0 0 16 16">
+  <path fill="#ffbf00" d="M14.8,12.5L9.3,1.9C9,1.3,8.5,1,8,1C7.5,1,7,1.3,6.7,1.9L1.2,12.5c-0.3,0.6-0.3,1.2,0,1.7C1.5,14.7,2,15,2.6,15h10.8 c0.6,0,1.1-0.3,1.4-0.8C15.1,13.7,15.1,13.1,14.8,12.5z"/>
+  <path fill="#fff" d="M8,11c-0.8,0-1.5,0.7-1.5,1.5C6.5,13.3,7.2,14,8,14 c0.8,0,1.5-0.7,1.5-1.5C9.5,11.7,8.8,11,8,11z M8,10L8,10C8.6,10,9,9.6,9,9l0.2-4.2c0-0.7-0.5-1.2-1.2-1.2S6.8,4.1,6.8,4.8L7,9 C7,9.6,7.4,10,8,10z"/>
+</svg>
--- a/build/annotationProcessors/utils/GeneratableElementIterator.java
+++ b/build/annotationProcessors/utils/GeneratableElementIterator.java
@@ -45,17 +45,17 @@ public class GeneratableElementIterator 
 
         // Sort the elements to ensure determinism.
         Arrays.sort(objs, new AlphabeticAnnotatableEntityComparator<Member>());
         mObjects = objs;
 
         // Check for "Wrap ALL the things" flag.
         for (Annotation annotation : aClass.getDeclaredAnnotations()) {
             final String annotationTypeName = annotation.annotationType().getName();
-            if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI")) {
+            if (annotationTypeName.equals("org.mozilla.gecko.annotation.WrapForJNI")) {
                 mIterateEveryEntry = true;
                 break;
             }
         }
 
         findNextValue();
     }
 
@@ -66,17 +66,17 @@ public class GeneratableElementIterator 
     private void findNextValue() {
         while (mElementIndex < mObjects.length) {
             Member candidateElement = mObjects[mElementIndex];
             mElementIndex++;
             for (Annotation annotation : ((AnnotatedElement) candidateElement).getDeclaredAnnotations()) {
                 // WrappedJNIMethod has parameters. Use Reflection to obtain them.
                 Class<? extends Annotation> annotationType = annotation.annotationType();
                 final String annotationTypeName = annotationType.getName();
-                if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI")) {
+                if (annotationTypeName.equals("org.mozilla.gecko.annotation.WrapForJNI")) {
                     String stubName = null;
                     boolean isMultithreadedStub = false;
                     boolean noThrow = false;
                     boolean narrowChars = false;
                     boolean catchException = false;
                     try {
                         // Determine the explicitly-given name of the stub to generate, if any.
                         final Method stubNameMethod = annotationType.getDeclaredMethod("stubName");
@@ -99,25 +99,25 @@ public class GeneratableElementIterator 
                         narrowChars = (Boolean) narrowCharsMethod.invoke(annotation);
 
                         // Determine if we should catch exceptions
                         final Method catchExceptionMethod = annotationType.getDeclaredMethod("catchException");
                         catchExceptionMethod.setAccessible(true);
                         catchException = (Boolean) catchExceptionMethod.invoke(annotation);
 
                     } catch (NoSuchMethodException e) {
-                        System.err.println("Unable to find expected field on WrapElementForJNI annotation. Did the signature change?");
+                        System.err.println("Unable to find expected field on WrapForJNI annotation. Did the signature change?");
                         e.printStackTrace(System.err);
                         System.exit(3);
                     } catch (IllegalAccessException e) {
-                        System.err.println("IllegalAccessException reading fields on WrapElementForJNI annotation. Seems the semantics of Reflection have changed...");
+                        System.err.println("IllegalAccessException reading fields on WrapForJNI annotation. Seems the semantics of Reflection have changed...");
                         e.printStackTrace(System.err);
                         System.exit(4);
                     } catch (InvocationTargetException e) {
-                        System.err.println("InvocationTargetException reading fields on WrapElementForJNI annotation. This really shouldn't happen.");
+                        System.err.println("InvocationTargetException reading fields on WrapForJNI annotation. This really shouldn't happen.");
                         e.printStackTrace(System.err);
                         System.exit(5);
                     }
 
                     // If the method name was not explicitly given in the annotation generate one...
                     if (stubName.isEmpty()) {
                         stubName = Utils.getNativeName(candidateElement);
                     }
--- a/dom/alarm/test/mochitest.ini
+++ b/dom/alarm/test/mochitest.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 support-files =
   file_empty.html
+  system_message_chrome_script.js
 
 [test_alarm_add_data.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_alarm_add_date.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_alarm_add_respectTimezone.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_alarm_non_permitted_app.html]
new file mode 100644
--- /dev/null
+++ b/dom/alarm/test/system_message_chrome_script.js
@@ -0,0 +1,18 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+'use strict';
+
+const { classes: Cc, interfaces: Ci } = Components;
+
+const systemMessenger = Cc["@mozilla.org/system-message-internal;1"]
+                          .getService(Ci.nsISystemMessagesInternal);
+const ioService = Cc["@mozilla.org/network/io-service;1"]
+                    .getService(Ci.nsIIOService);
+
+addMessageListener("trigger-register-page", function(aData) {
+  systemMessenger.registerPage(aData.type,
+                               ioService.newURI(aData.pageURL, null, null),
+                               ioService.newURI(aData.manifestURL, null, null));
+  sendAsyncMessage("page-registered");
+});
--- a/dom/alarm/test/test_bug1037079.html
+++ b/dom/alarm/test/test_bug1037079.html
@@ -9,30 +9,44 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test">
   <script type="application/javascript">
 
   "use strict";
 
+  function registerPage() {
+    var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('system_message_chrome_script.js'));
+    gScript.addMessageListener("page-registered", function pageRegisteredHandler() {
+      gScript.removeMessageListener("page-registered", pageRegisteredHandler);
+      gScript.destroy();
+      testFireTimeAlert();
+    });
+
+    gScript.sendAsyncMessage("trigger-register-page",
+                             { type: "alarm",
+                               manifestURL: window.location.origin + "/manifest.webapp",
+                               pageURL: window.location.href });
+  }
+
   function testFireTimeAlert() {
     var secondsLater = new Date();
     secondsLater.setSeconds(secondsLater.getSeconds() + 10);
 
     var domRequest;
     try {
       // Set system message handler.
       navigator.mozSetMessageHandler('alarm', function(message){
         ok(true, "Time alert has been fired.");
         SimpleTest.finish();
       });
 
       domRequest = navigator.mozAlarms.add(secondsLater, "honorTimezone",
-    		                               {type: "timer"});
+                                           {type: "timer"});
     } catch (e) {
       ok(false, "Unexpected exception trying to set time alert.");
 
       return SimpleTest.finish();
     }
     domRequest.onsuccess = function(e) {
       ok(true, "Set time alert. Waiting to be fired.");
     };
@@ -57,17 +71,17 @@
                  SpecialPowers.Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED) {
         ok(true, "mozAlarms is not allowed for non-installed apps. " +
                  "TODO Bug 876981.");
         isAllowedToTest = false;
       }
 
       if (isAllowedToTest) {
         ok(true, "Start to test...");
-        testFireTimeAlert();
+        registerPage();
       } else {
         // A sanity check to make sure we must run tests on Firefox OS (B2G).
         if (navigator.userAgent.indexOf("Mobile") != -1 &&
             navigator.appVersion.indexOf("Android") == -1) {
           ok(false, "Should run the test on Firefox OS (B2G)!");
         }
 
         SimpleTest.finish();
--- a/dom/alarm/test/test_bug1090896.html
+++ b/dom/alarm/test/test_bug1090896.html
@@ -9,16 +9,30 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test">
   <script type="application/javascript">
 
   "use strict";
 
+  function registerPage() {
+    var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('system_message_chrome_script.js'));
+    gScript.addMessageListener("page-registered", function pageRegisteredHandler() {
+      gScript.removeMessageListener("page-registered", pageRegisteredHandler);
+      gScript.destroy();
+      testFireTimeAlertWithNoData();
+    });
+
+    gScript.sendAsyncMessage("trigger-register-page",
+                             { type: "alarm",
+                               manifestURL: window.location.origin + "/manifest.webapp",
+                               pageURL: window.location.href });
+  }
+
   function testFireTimeAlertWithNoData() {
     var secondsLater = new Date();
     secondsLater.setSeconds(secondsLater.getSeconds() + 1);
 
     var domRequest;
     try {
       // Set system message handler.
       navigator.mozSetMessageHandler('alarm', function(message){
@@ -56,17 +70,17 @@
                  SpecialPowers.Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED) {
         ok(true, "mozAlarms is not allowed for non-installed apps. " +
                  "TODO Bug 876981.");
         isAllowedToTest = false;
       }
 
       if (isAllowedToTest) {
         ok(true, "Start to test...");
-        testFireTimeAlertWithNoData();
+        registerPage();
       } else {
         // A sanity check to make sure we must run tests on Firefox OS (B2G).
         if (navigator.userAgent.indexOf("Mobile") != -1 &&
             navigator.appVersion.indexOf("Android") == -1) {
           ok(false, "Should run the test on Firefox OS (B2G)!");
         }
 
         SimpleTest.finish();
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -229,16 +229,17 @@ OutputStreamData::Init(DecodedStream* aD
   mListener = new OutputStreamListener(aDecodedStream, aStream);
   aStream->AddListener(mListener);
 }
 
 DecodedStream::DecodedStream(MediaQueue<MediaData>& aAudioQueue,
                              MediaQueue<MediaData>& aVideoQueue)
   : mMonitor("DecodedStream::mMonitor")
   , mPlaying(false)
+  , mVolume(1.0)
   , mAudioQueue(aAudioQueue)
   , mVideoQueue(aVideoQueue)
 {
 }
 
 DecodedStream::~DecodedStream()
 {
 }
@@ -414,16 +415,23 @@ DecodedStream::SetPlaying(bool aPlaying)
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mPlaying = aPlaying;
   if (mData) {
     mData->SetPlaying(aPlaying);
   }
 }
 
 void
+DecodedStream::SetVolume(double aVolume)
+{
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  mVolume = aVolume;
+}
+
+void
 DecodedStream::InitTracks()
 {
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   if (mData->mStreamInitialized) {
     return;
   }
 
@@ -661,23 +669,23 @@ DecodedStream::AdvanceTracks()
   }
 
   if (!mData->mHaveSentFinish) {
     mData->mStream->AdvanceKnownTracksTime(endPosition);
   }
 }
 
 bool
-DecodedStream::SendData(double aVolume, bool aIsSameOrigin)
+DecodedStream::SendData(bool aIsSameOrigin)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()");
 
   InitTracks();
-  SendAudio(aVolume, aIsSameOrigin);
+  SendAudio(mVolume, aIsSameOrigin);
   SendVideo(aIsSameOrigin);
   AdvanceTracks();
 
   bool finished = (!mInfo.HasAudio() || mAudioQueue.IsFinished()) &&
                   (!mInfo.HasVideo() || mVideoQueue.IsFinished());
 
   if (finished && !mData->mHaveSentFinish) {
     mData->mHaveSentFinish = true;
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -56,24 +56,27 @@ public:
   void StartPlayback(int64_t aStartTime, const MediaInfo& aInfo);
   // Mimic MDSM::StopAudioThread.
   void StopPlayback();
 
   void DestroyData();
   void RecreateData();
   void Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
   void Remove(MediaStream* aStream);
+
   void SetPlaying(bool aPlaying);
+  void SetVolume(double aVolume);
+
   int64_t AudioEndTime() const;
   int64_t GetPosition() const;
   bool IsFinished() const;
   bool HasConsumers() const;
 
   // Return true if stream is finished.
-  bool SendData(double aVolume, bool aIsSameOrigin);
+  bool SendData(bool aIsSameOrigin);
 
 protected:
   virtual ~DecodedStream();
 
 private:
   ReentrantMonitor& GetReentrantMonitor() const;
   void RecreateData(MediaStreamGraph* aGraph);
   void Connect(OutputStreamData* aStream);
@@ -92,16 +95,18 @@ private:
   // required by bug 1146482. DecodedStream needs to release monitor before
   // calling back into MDSM functions in order to prevent deadlocks.
   //
   // Please move all capture-stream related code from MDSM into DecodedStream
   // and apply "dispatch + mirroring" to get rid of this monitor in the future.
   mutable ReentrantMonitor mMonitor;
 
   bool mPlaying;
+  double mVolume;
+
   Maybe<int64_t> mStartTime;
   MediaInfo mInfo;
 
   MediaQueue<MediaData>& mAudioQueue;
   MediaQueue<MediaData>& mVideoQueue;
 };
 
 } // namespace mozilla
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -371,17 +371,17 @@ int64_t MediaDecoderStateMachine::GetDec
 }
 
 void MediaDecoderStateMachine::SendStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
 
-  bool finished = mDecodedStream->SendData(mVolume, mSameOriginMedia);
+  bool finished = mDecodedStream->SendData(mSameOriginMedia);
 
   const auto clockTime = GetClock();
   while (true) {
     const MediaData* a = AudioQueue().PeekFront();
 
     // If we discard audio samples fed to the stream immediately, we will
     // keep decoding audio samples till the end and consume a lot of memory.
     // Therefore we only discard those behind the stream clock to throttle
@@ -1164,16 +1164,17 @@ void MediaDecoderStateMachine::SetState(
 
 void MediaDecoderStateMachine::VolumeChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mAudioSink) {
     mAudioSink->SetVolume(mVolume);
   }
+  mDecodedStream->SetVolume(mVolume);
 }
 
 void MediaDecoderStateMachine::RecomputeDuration()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   TimeUnit duration;
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -169,17 +169,17 @@ public:
   {
   }
 
   VideoInfo(int32_t aWidth, int32_t aHeight)
     : TrackInfo(kVideoTrack, NS_LITERAL_STRING("2"), NS_LITERAL_STRING("main"),
                 EmptyString(), EmptyString(), true, 2)
     , mDisplay(nsIntSize(aWidth, aHeight))
     , mStereoMode(StereoMode::MONO)
-    , mImage(nsIntSize(aWidth, aHeight))
+    , mImage(nsIntRect(0, 0, aWidth, aHeight))
     , mCodecSpecificConfig(new MediaByteBuffer)
     , mExtraData(new MediaByteBuffer)
   {
   }
 
   VideoInfo(const VideoInfo& aOther)
     : TrackInfo(aOther)
     , mDisplay(aOther.mDisplay)
@@ -212,18 +212,18 @@ public:
 
   // Size in pixels at which the video is rendered. This is after it has
   // been scaled by its aspect ratio.
   nsIntSize mDisplay;
 
   // Indicates the frame layout for single track stereo videos.
   StereoMode mStereoMode;
 
-  // Size in pixels of decoded video's image.
-  nsIntSize mImage;
+  // Visible area of the decoded video's image.
+  nsIntRect mImage;
   nsRefPtr<MediaByteBuffer> mCodecSpecificConfig;
   nsRefPtr<MediaByteBuffer> mExtraData;
 };
 
 class AudioInfo : public TrackInfo {
 public:
   AudioInfo()
     : TrackInfo(kAudioTrack, NS_LITERAL_STRING("1"), NS_LITERAL_STRING("main"),
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -318,16 +318,22 @@ RTCPeerConnection.prototype = {
   contractID: PC_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
                                          Ci.nsIDOMGlobalPropertyInitializer]),
   init: function(win) { this._win = win; },
 
   __init: function(rtcConfig) {
     this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+    // TODO: Update this code once we support pc.setConfiguration, to track
+    // setting from content independently from pref (Bug 1181768).
+    if (rtcConfig.iceTransportPolicy == "all" &&
+        Services.prefs.getBoolPref("media.peerconnection.ice.relay_only")) {
+      rtcConfig.iceTransportPolicy = "relay";
+    }
     if (!rtcConfig.iceServers ||
         !Services.prefs.getBoolPref("media.peerconnection.use_document_iceservers")) {
       try {
          rtcConfig.iceServers =
            JSON.parse(Services.prefs.getCharPref("media.peerconnection.default_iceservers") || "[]");
       } catch (e) {
         this.logWarning(
             "Ignoring invalid media.peerconnection.default_iceservers in about:config",
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -255,17 +255,18 @@ static const uint8_t sTestH264ExtraData[
 static already_AddRefed<MediaDataDecoder>
 CreateTestH264Decoder(layers::LayersBackend aBackend,
                       VideoInfo& aConfig)
 {
   aConfig.mMimeType = "video/avc";
   aConfig.mId = 1;
   aConfig.mDuration = 40000;
   aConfig.mMediaTime = 0;
-  aConfig.mDisplay = aConfig.mImage = nsIntSize(64, 64);
+  aConfig.mDisplay = nsIntSize(64, 64);
+  aConfig.mImage = nsIntRect(0, 0, 64, 64);
   aConfig.mExtraData = new MediaByteBuffer();
   aConfig.mExtraData->AppendElements(sTestH264ExtraData,
                                      MOZ_ARRAY_LENGTH(sTestH264ExtraData));
 
   PlatformDecoderModule::Init();
 
   nsRefPtr<PlatformDecoderModule> platform = PlatformDecoderModule::Create();
   if (!platform) {
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -310,22 +310,27 @@ TrackBuffersManager::Ended()
 }
 
 void
 TrackBuffersManager::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("");
 
+  // Abort pending operations if any.
+  AbortAppendData();
+
   nsRefPtr<TrackBuffersManager> self = this;
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableFunction([self] () {
       // Clear our sourcebuffer
       self->CodedFrameRemoval(TimeInterval(TimeUnit::FromSeconds(0),
                                            TimeUnit::FromInfinity()));
+      self->mProcessingPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
+      self->mAppendPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
       self->mMediaSourceDuration.DisconnectIfConnected();
     });
   GetTaskQueue()->Dispatch(task.forget());
 }
 
 #if defined(DEBUG)
 void
 TrackBuffersManager::Dump(const char* aPath)
@@ -656,20 +661,19 @@ TrackBuffersManager::SegmentParserLoop()
           RecreateParser(false);
         }
         continue;
       }
       if (mParser->IsMediaSegmentPresent(mInputBuffer)) {
         SetAppendState(AppendState::PARSING_MEDIA_SEGMENT);
         continue;
       }
-      // We have neither an init segment nor a media segment, this is invalid
-      // data. We can ignore it.
-      MSE_DEBUG("Found invalid data, ignoring.");
-      mInputBuffer = nullptr;
+      // We have neither an init segment nor a media segment, this is either
+      // invalid data or not enough data to detect a segment type.
+      MSE_DEBUG("Found invalid or incomplete data.");
       NeedMoreData();
       return;
     }
 
     int64_t start, end;
     mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
     mProcessedInput += mInputBuffer->Length();
 
@@ -1029,35 +1033,42 @@ TrackBuffersManager::OnDemuxerInitFailed
   RejectAppend(NS_ERROR_FAILURE, __func__);
 }
 
 nsRefPtr<TrackBuffersManager::CodedFrameProcessingPromise>
 TrackBuffersManager::CodedFrameProcessing()
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mProcessingPromise.IsEmpty());
-  nsRefPtr<CodedFrameProcessingPromise> p = mProcessingPromise.Ensure(__func__);
 
   MediaByteRange mediaRange = mParser->MediaSegmentRange();
   if (mediaRange.IsNull()) {
     AppendDataToCurrentInputBuffer(mInputBuffer);
     mInputBuffer = nullptr;
   } else {
+    MOZ_ASSERT(mProcessedInput >= mInputBuffer->Length());
+    if (int64_t(mProcessedInput - mInputBuffer->Length()) > mediaRange.mEnd) {
+      // Something is not quite right with the data appended. Refuse it.
+      // This would typically happen if the previous media segment was partial
+      // yet a new complete media segment was added.
+      return CodedFrameProcessingPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+    }
     // The mediaRange is offset by the init segment position previously added.
     uint32_t length =
       mediaRange.mEnd - (mProcessedInput - mInputBuffer->Length());
     nsRefPtr<MediaByteBuffer> segment = new MediaByteBuffer;
-    MOZ_ASSERT(mInputBuffer->Length() >= length);
     if (!segment->AppendElements(mInputBuffer->Elements(), length, fallible)) {
       return CodedFrameProcessingPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
     }
     AppendDataToCurrentInputBuffer(segment);
     mInputBuffer->RemoveElementsAt(0, length);
   }
 
+  nsRefPtr<CodedFrameProcessingPromise> p = mProcessingPromise.Ensure(__func__);
+
   DoDemuxVideo();
 
   return p;
 }
 
 void
 TrackBuffersManager::OnDemuxFailed(TrackType aTrack,
                                    DemuxerFailureReason aFailure)
@@ -1627,23 +1638,25 @@ TrackBuffersManager::RemoveFrames(const 
         lastRemovedIndex - firstRemovedIndex.ref() + 1;
     }
   }
 
   if (aTrackData.mNextInsertionIndex.isSome()) {
     if (aTrackData.mNextInsertionIndex.ref() > firstRemovedIndex.ref() &&
         aTrackData.mNextInsertionIndex.ref() <= lastRemovedIndex + 1) {
       aTrackData.ResetAppendState();
+      MSE_DEBUG("NextInsertionIndex got reset.");
     } else if (aTrackData.mNextInsertionIndex.ref() > lastRemovedIndex + 1) {
       aTrackData.mNextInsertionIndex.ref() -=
         lastRemovedIndex - firstRemovedIndex.ref() + 1;
     }
   }
 
   // Update our buffered range to exclude the range just removed.
+  removedIntervals.SetFuzz(TimeUnit::FromMicroseconds(maxSampleDuration/2));
   aTrackData.mBufferedRanges -= removedIntervals;
 
   data.RemoveElementsAt(firstRemovedIndex.ref(),
                         lastRemovedIndex - firstRemovedIndex.ref() + 1);
 }
 
 void
 TrackBuffersManager::RecreateParser(bool aReuseInitData)
--- a/dom/media/platforms/agnostic/VPXDecoder.cpp
+++ b/dom/media/platforms/agnostic/VPXDecoder.cpp
@@ -25,18 +25,17 @@ extern PRLogModuleInfo* gMediaDecoderLog
 VPXDecoder::VPXDecoder(const VideoInfo& aConfig,
                        ImageContainer* aImageContainer,
                        FlushableTaskQueue* aTaskQueue,
                        MediaDataDecoderCallback* aCallback)
   : mImageContainer(aImageContainer)
   , mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
   , mIter(nullptr)
-  , mDisplayWidth(aConfig.mDisplay.width)
-  , mDisplayHeight(aConfig.mDisplay.height)
+  , mInfo(aConfig)
 {
   MOZ_COUNT_CTOR(VPXDecoder);
   if (aConfig.mMimeType.EqualsLiteral("video/webm; codecs=vp8")) {
     mCodec = Codec::VP8;
   } else if (aConfig.mMimeType.EqualsLiteral("video/webm; codecs=vp9")) {
     mCodec = Codec::VP9;
   } else {
     mCodec = -1;
@@ -120,33 +119,32 @@ VPXDecoder::DoDecodeFrame(MediaRawData* 
     b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
 
     b.mPlanes[2].mData = img->planes[2];
     b.mPlanes[2].mStride = img->stride[2];
     b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
     b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
     b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
 
-    IntRect picture = IntRect(0, 0, img->d_w, img->d_h);
     VideoInfo info;
-    info.mDisplay = nsIntSize(mDisplayWidth, mDisplayHeight);
+    info.mDisplay = mInfo.mDisplay;
     nsRefPtr<VideoData> v = VideoData::Create(info,
                                               mImageContainer,
                                               aSample->mOffset,
                                               aSample->mTime,
                                               aSample->mDuration,
                                               b,
                                               aSample->mKeyframe,
                                               aSample->mTimecode,
-                                              picture);
+                                              mInfo.mImage);
 
     if (!v) {
       LOG("Image allocation error source %ldx%ld display %ldx%ld picture %ldx%ld",
-          img->d_w, img->d_h, mDisplayWidth, mDisplayHeight,
-          picture.width, picture.height);
+          img->d_w, img->d_h, mInfo.mDisplay.width, mInfo.mDisplay.height,
+          mInfo.mImage.width, mInfo.mImage.height);
       return -1;
     }
     mCallback->Output(v);
     return 1;
   }
   mIter = nullptr;
   return 0;
 }
--- a/dom/media/platforms/agnostic/VPXDecoder.h
+++ b/dom/media/platforms/agnostic/VPXDecoder.h
@@ -51,17 +51,16 @@ private:
   nsRefPtr<ImageContainer> mImageContainer;
   RefPtr<FlushableTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
 
   // VPx decoder state
   vpx_codec_ctx_t mVPX;
   vpx_codec_iter_t mIter;
 
-  uint32_t mDisplayWidth;
-  uint32_t mDisplayHeight;
+  const VideoInfo& mInfo;
 
   int mCodec;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -250,16 +250,19 @@ public:
     ENVOKE_CALLBACK(Output, data);
     return NS_OK;
   }
 };
 
 
 bool AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType)
 {
+  if (!AndroidBridge::Bridge() || (AndroidBridge::Bridge()->GetAPIVersion() < 16)) {
+    return false;
+  }
   if (aMimeType.EqualsLiteral("video/mp4") ||
       aMimeType.EqualsLiteral("video/avc")) {
     return true;
   }
   return static_cast<bool>(mozilla::CreateDecoder(aMimeType));
 }
 
 already_AddRefed<MediaDataDecoder>
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -202,16 +202,22 @@ WMFVideoMFTManager::InitInternal(bool aF
   NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   RefPtr<IMFAttributes> attr(decoder->GetAttributes());
   UINT32 aware = 0;
   if (attr) {
       attr->GetUINT32(MF_SA_D3D_AWARE, &aware);
       attr->SetUINT32(CODECAPI_AVDecNumWorkerThreads,
                       WMFDecoderModule::GetNumDecoderThreads());
+      hr = attr->SetUINT32(CODECAPI_AVLowLatencyMode, TRUE);
+      if (SUCCEEDED(hr)) {
+        LOG("Enabling Low Latency Mode");
+      } else {
+        LOG("Couldn't enable Low Latency Mode");
+      }
   }
 
   if (useDxva) {
     if (aware) {
       // TODO: Test if I need this anywhere... Maybe on Vista?
       //hr = attr->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE);
       //NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
       MOZ_ASSERT(mDXVA2Manager);
--- a/dom/media/test/test_mediarecorder_record_timeslice.html
+++ b/dom/media/test/test_mediarecorder_record_timeslice.html
@@ -20,16 +20,17 @@ function startTest(test, token) {
   var expectedMimeType = test.type.substring(0, test.type.indexOf(';'));
 
   element.token = token;
   manager.started(token);
 
   element.src = test.name;
   element.test = test;
   element.stream = element.mozCaptureStream();
+  element.loop = true;
 
   var mediaRecorder = new MediaRecorder(element.stream);
 
   mediaRecorder.onerror = function () {
     ok(false, 'Unexpected onerror callback fired');
   };
 
   mediaRecorder.onwarning = function () {
@@ -63,35 +64,35 @@ function startTest(test, token) {
     // We'll stop recording upon the 1st blob being received
     if (dataAvailableCount === 1) {
       mediaRecorder.onstop = function (evt) {
         info('onstop fired');
 
         if (!onDataAvailableFirst) {
           ok(false, 'onstop unexpectedly fired before ondataavailable');
         }
-
+        element.pause();
         manager.finished(token);
       };
 
       mediaRecorder.stop();
       is(mediaRecorder.state, 'inactive',
          'Media recorder is inactive after being stopped');
       is(mediaRecorder.stream, element.stream,
          'Media recorder stream = element stream post recording');
 
     } else if (dataAvailableCount === 2) {
       // Ensure we've received at least two ondataavailable events before onstop
       onDataAvailableFirst = true;
     }
   };
 
-  // Start recording once canplaythrough fires
-  element.oncanplaythrough = function() {
-    element.oncanplaythrough = null;
+  // Start recording once metadataloaded fires
+  element.onloadedmetadata = function() {
+    element.onloadedmetadata = null;
     mediaRecorder.start(250);
     is(mediaRecorder.state, 'recording', 'Media recorder should be recording');
     is(mediaRecorder.stream, element.stream,
        'Media recorder stream = element stream at the start of recording');
   };
 
   element.play();
 }
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -113,16 +113,18 @@ skip-if = toolkit == 'gonk' # B2G emulat
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably
 [test_peerConnection_offerRequiresReceiveAudio.html]
 [test_peerConnection_offerRequiresReceiveVideo.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideoAudio.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_promiseSendOnly.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+[test_peerConnection_relayOnly.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_callbacks.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_replaceTrack.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_syncSetDescription.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_setLocalAnswerInHaveLocalOffer.html]
 [test_peerConnection_setLocalAnswerInStable.html]
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -1184,28 +1184,25 @@ PeerConnectionWrapper.prototype = {
    * Registers a callback for the ICE connection state change and
    * reports success (=connected) or failure via the callbacks.
    * States "new" and "checking" are ignored.
    *
    * @returns {Promise}
    *          resolves when connected, rejects on failure
    */
   waitForIceConnected : function() {
-    return new Promise((resolve, reject) => {
-      var iceConnectedChanged = () => {
-        if (this.isIceConnected()) {
-          delete this.ice_connection_callbacks.waitForIceConnected;
-          resolve();
-        } else if (! this.isIceConnectionPending()) {
-          delete this.ice_connection_callbacks.waitForIceConnected;
-          resolve();
-        }
+    return new Promise((resolve, reject) =>
+        this.ice_connection_callbacks.waitForIceConnected = () => {
+      if (this.isIceConnected()) {
+        delete this.ice_connection_callbacks.waitForIceConnected;
+        resolve();
+      } else if (!this.isIceConnectionPending()) {
+        delete this.ice_connection_callbacks.waitForIceConnected;
+        reject(new Error('ICE failed'));
       }
-
-      this.ice_connection_callbacks.waitForIceConnected = iceConnectedChanged;
     });
   },
 
   /**
    * Setup a onicecandidate handler
    *
    * @param {object} test
    *        A PeerConnectionTest object to which the ice candidates gets
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_relayOnly.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+createHTML({
+  bug: "1187775",
+  title: "peer connection ICE fails on relay-only without TURN"
+});
+
+function PC_LOCAL_NO_CANDIDATES(test) {
+  var isnt = can => is(can, null, "No candidates: " + JSON.stringify(can));
+  test.pcLocal._pc.addEventListener("icecandidate", e => isnt(e.candidate));
+}
+
+function PC_BOTH_WAIT_FOR_ICE_FAILED(test) {
+  var isFail = (f, reason, msg) =>
+    f().then(() => { throw new Error(msg + " must fail"); },
+             e => is(e.message, reason, msg + " must fail with: " + e.message));
+
+  return Promise.all([
+    isFail(() => waitForIceConnected(test, test.pcLocal), "ICE failed", "Local ICE"),
+    isFail(() => waitForIceConnected(test, test.pcRemote), "ICE failed", "Remote ICE")
+  ])
+  .then(() => ok(true, "ICE on both sides must fail."));
+}
+
+var pushPrefs = (...p) => new Promise(r => SpecialPowers.pushPrefEnv({set: p}, r));
+var test;
+
+runNetworkTest(options =>
+    pushPrefs(['media.peerconnection.ice.stun_client_maximum_transmits', 3],
+              ['media.peerconnection.ice.trickle_grace_period', 5000]).then(() => {
+  options = options || {};
+  options.config_local = options.config_local || {};
+  var servers = options.config_local.iceServers || [];
+  // remove any turn servers
+  options.config_local.iceServers = servers.filter(server =>
+      server.urls.every(u => !u.toLowerCase().startsWith('turn')));
+
+  // Here's the setting we're testing. Comment out and this test should fail:
+  options.config_local.iceTransportPolicy = "relay";
+
+  test = new PeerConnectionTest(options);
+  test.setMediaConstraints([{audio: true}, {video: true}],
+                           [{audio: true}, {video: true}]);
+  test.chain.remove("PC_LOCAL_SETUP_ICE_LOGGER");  // Needed to suppress failing
+  test.chain.remove("PC_REMOTE_SETUP_ICE_LOGGER"); // on ICE-failure.
+  test.chain.insertAfter("PC_LOCAL_SETUP_ICE_HANDLER", PC_LOCAL_NO_CANDIDATES);
+  test.chain.replace("PC_LOCAL_WAIT_FOR_ICE_CONNECTED", PC_BOTH_WAIT_FOR_ICE_FAILED);
+  test.chain.removeAfter("PC_BOTH_WAIT_FOR_ICE_FAILED");
+  test.run();
+}));
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -311,18 +311,17 @@ WebMDemuxer::ReadMetadata()
         // Video track's frame sizes will overflow. Ignore the video track.
         continue;
       }
 
       mVideoTrack = track;
       mHasVideo = true;
 
       mInfo.mVideo.mDisplay = displaySize;
-
-      mPicture = pictureRect;
+      mInfo.mVideo.mImage = pictureRect;
 
       switch (params.stereo_mode) {
         case NESTEGG_VIDEO_MONO:
           mInfo.mVideo.mStereoMode = StereoMode::MONO;
           break;
         case NESTEGG_VIDEO_STEREO_LEFT_RIGHT:
           mInfo.mVideo.mStereoMode = StereoMode::LEFT_RIGHT;
           break;
--- a/dom/media/webm/WebMDemuxer.h
+++ b/dom/media/webm/WebMDemuxer.h
@@ -143,19 +143,16 @@ private:
   uint64_t mSeekPreroll;
 
   int64_t mLastAudioFrameTime;
 
   // Calculate the frame duration from the last decodeable frame using the
   // previous frame's timestamp.  In NS.
   int64_t mLastVideoFrameTime;
 
-  // Picture region, as relative to the initial frame size.
-  nsIntRect mPicture;
-
   // Codec ID of audio track
   int mAudioCodec;
   // Codec ID of video track
   int mVideoCodec;
 
   // Booleans to indicate if we have audio and/or video data
   bool mHasVideo;
   bool mHasAudio;
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -737,21 +737,19 @@ SystemMessageInternal.prototype = {
       return MSG_SENT_FAILURE_PERM_DENIED;
     }
 
     // Queue this message in the corresponding pages.
     let page = this._findPage(aType, aPageURL, aManifestURL);
     if (!page) {
       debug("Message " + aType + " is not registered for " +
             aPageURL + " @ " + aManifestURL);
-      // FIXME bug 1140275 should only send message to page registered in manifest
-      // return MSG_SENT_FAILURE_PERM_DENIED;
+      return MSG_SENT_FAILURE_PERM_DENIED;
     }
-    if (page)
-      this._queueMessage(page, aMessage, aMessageID);
+    this._queueMessage(page, aMessage, aMessageID);
 
     let appPageIsRunning = false;
     let pageKey = this._createKeyForPage({ type: aType,
                                            manifestURL: aManifestURL,
                                            pageURL: aPageURL });
 
     let cache = this._findCacheForApp(aManifestURL);
     let targets = this._listeners[aManifestURL];
@@ -796,18 +794,17 @@ SystemMessageInternal.prototype = {
       // wake it up. We still need to acquire a CPU wake lock for that page and
       // expect that we will receive a "SystemMessageManager:HandleMessagesDone"
       // or a "SystemMessageManager:HandleMessageDone" message when the page
       // finishes handling the system message with other pending messages. At
       // that point, we'll release the lock we acquired.
       result = MSG_SENT_FAILURE_APP_NOT_RUNNING;
       this._acquireCpuWakeLock(pageKey);
     }
-    if (page)
-      this._openAppPage(page, aMessage, aExtra, result);
+    this._openAppPage(page, aMessage, aExtra, result);
     return result;
   },
 
   _resolvePendingPromises: function(aMessageID) {
     if (!this._pendingPromises.has(aMessageID)) {
       debug("Unknown pendingPromise messageID. This seems a bug!!");
       return;
     }
--- a/dom/messages/test/mochitest.ini
+++ b/dom/messages/test/mochitest.ini
@@ -1,2 +1,6 @@
+[DEFAULT]
+support-files =
+  system_message_chrome_script.js
+
 [test_bug_993732.html]
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
new file mode 100644
--- /dev/null
+++ b/dom/messages/test/system_message_chrome_script.js
@@ -0,0 +1,18 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+'use strict';
+
+const { classes: Cc, interfaces: Ci } = Components;
+
+const systemMessenger = Cc["@mozilla.org/system-message-internal;1"]
+                          .getService(Ci.nsISystemMessagesInternal);
+const ioService = Cc["@mozilla.org/network/io-service;1"]
+                    .getService(Ci.nsIIOService);
+
+addMessageListener("trigger-register-page", function(aData) {
+  systemMessenger.registerPage(aData.type,
+                               ioService.newURI(aData.pageURL, null, null),
+                               ioService.newURI(aData.manifestURL, null, null));
+  sendAsyncMessage("page-registered");
+});
--- a/dom/messages/test/test_bug_993732.html
+++ b/dom/messages/test/test_bug_993732.html
@@ -14,16 +14,30 @@
 
   "use strict";
 
   // The syndrome of Bug 993732 is that the running app (either foreground or background)
   // is not able to receive system messages. Even worse, the app will be killed when the
   // listening system message is broadcast. So this test case uses the alarm message
   // to test if a running app can receive the system message.
 
+  function registerPage() {
+    var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('system_message_chrome_script.js'));
+    gScript.addMessageListener("page-registered", function pageRegisteredHandler() {
+      gScript.removeMessageListener("page-registered", pageRegisteredHandler);
+      gScript.destroy();
+      testAlarm(10000);
+    });
+
+    gScript.sendAsyncMessage("trigger-register-page",
+                             { type: "alarm",
+                               manifestURL: window.location.origin + "/manifest.webapp",
+                               pageURL: window.location.href });
+  }
+
   function testAlarm(aMillisecondsFromNow) {
     var at = new Date();
     at.setTime(at.getTime() + aMillisecondsFromNow);
 
     navigator.mozSetMessageHandler('alarm', function(message) {
       ok(true, "We got alarm message!");
       SimpleTest.finish();
     });
@@ -35,29 +49,29 @@
       ok(false,
          "Unexpected exception while adding alarm " + aMillisecondsFromNow + " ms from now.");
       SimpleTest.finish();
     }
     domRequest.onsuccess = function(e) {
       // Waiting for alarm message.
     };
     domRequest.onerror = function(e) {
-      ok(false, "Unable to add alarm for tomorrow`.");
+      ok(false, "Unable to add alarm.");
       SimpleTest.finish();
     };
   }
 
   function startTests() {
 
     SpecialPowers.pushPrefEnv({"set": [["dom.mozAlarms.enabled", true]]}, function() {
       // Currently applicable only on FxOS
       if (navigator.userAgent.indexOf("Mobile") != -1 &&
           navigator.appVersion.indexOf("Android") == -1)
       {
-        testAlarm(10000);
+        registerPage();
       } else {
         ok(true, "mozAlarms on Firefox OS only.");
         SimpleTest.finish();
       }
     });
   }
 
   SimpleTest.expectAssertions(0, 9);
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -63,20 +63,22 @@ intr protocol PPluginInstance
   manages PBrowserStream;
   manages PPluginStream;
   manages PStreamNotify;
   manages PPluginSurface;
 
 child:
   intr __delete__();
 
-  // Return value is only used on Windows and only when the window needs its
-  // parent set to the chrome widget native window.
-  intr NPP_SetWindow(NPRemoteWindow window)
-    returns (NPRemoteWindow childWindowToBeAdopted);
+  // This is only used on Windows and, for windowed plugins, must be called
+  // before the first call to NPP_SetWindow.
+  intr CreateChildPluginWindow(NPRemoteWindow window)
+    returns (NPRemoteWindow createdChild);
+
+  intr NPP_SetWindow(NPRemoteWindow window);
 
   intr NPP_GetValue_NPPVpluginWantsAllNetworkStreams()
     returns (bool value, NPError result);
 
   // this message is not used on non-X platforms
   intr NPP_GetValue_NPPVpluginNeedsXEmbed()
     returns (bool value, NPError result);
 
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -1141,18 +1141,44 @@ void PluginInstanceChild::DeleteWindow()
 #endif
 
   // We don't have to keep the plug-in window ID any longer.
   mWindow.window = nullptr;
 }
 #endif
 
 bool
-PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow,
-                                         NPRemoteWindow* aChildWindowToBeAdopted)
+PluginInstanceChild::AnswerCreateChildPluginWindow(const NPRemoteWindow& aWindow,
+                                                   NPRemoteWindow* aCreatedChild)
+{
+#if defined(XP_WIN)
+    MOZ_ASSERT(aWindow.type == NPWindowTypeWindow);
+    MOZ_ASSERT(!mPluginWindowHWND);
+
+    if ((GetQuirks() & PluginModuleChild::QUIRK_QUICKTIME_AVOID_SETWINDOW) &&
+        aWindow.width == 0 && aWindow.height == 0) {
+
+        // Skip CreateChildPluginWindow call for hidden QuickTime plugins.
+        return true;
+    }
+
+    if (!CreatePluginWindow()) {
+        return false;
+    }
+
+    aCreatedChild->window = reinterpret_cast<uint64_t>(mPluginWindowHWND);
+    return true;
+#else
+    NS_NOTREACHED("PluginInstanceChild::CreateChildPluginWindow not implemented!");
+    return false;
+#endif
+}
+
+bool
+PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
 {
     PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
                       FULLFUNCTION,
                       aWindow.window,
                       aWindow.x, aWindow.y,
                       aWindow.width, aWindow.height));
     NS_ASSERTION(!mLayersRendering && !mPendingPluginCall,
                  "Shouldn't be receiving NPP_SetWindow with layer rendering");
@@ -1230,45 +1256,27 @@ PluginInstanceChild::AnswerNPP_SetWindow
       {
           if ((GetQuirks() & PluginModuleChild::QUIRK_QUICKTIME_AVOID_SETWINDOW) &&
               aWindow.width == 0 &&
               aWindow.height == 0) {
             // Skip SetWindow call for hidden QuickTime plugins
             return true;
           }
 
-          if (!CreatePluginWindow())
-              return false;
+          MOZ_ASSERT(mPluginWindowHWND,
+                     "Child plugin window must exist before call to SetWindow");
+
+          HWND parentHWND =  reinterpret_cast<HWND>(aWindow.window);
+          if (mPluginWindowHWND != parentHWND) {
+              mPluginParentHWND = parentHWND;
+              ShowWindow(mPluginWindowHWND, SW_SHOWNA);
+          }
 
           SizePluginWindow(aWindow.width, aWindow.height);
 
-          // If the window is not our parent set the return child window so that
-          // it can be re-parented in the chrome process. Re-parenting now
-          // happens there as we might not have sufficient permission.
-          // Also, this needs to be after SizePluginWindow because SetWindowPos
-          // relies on things that it sets.
-          HWND parentWindow = reinterpret_cast<HWND>(aWindow.window);
-          if (mPluginParentHWND != parentWindow  && IsWindow(parentWindow)) {
-              mPluginParentHWND = parentWindow;
-              aChildWindowToBeAdopted->window =
-                  reinterpret_cast<uint64_t>(mPluginWindowHWND);
-          } else {
-              // Now we know that the window has the correct parent we can show
-              // it. The actual visibility is controlled by its parent.
-              // First time round, these calls are made by our caller after the
-              // parent is set.
-              ShowWindow(mPluginWindowHWND, SW_SHOWNA);
-
-              // This used to be called in SizePluginWindow, but we need to make
-              // sure that mPluginWindowHWND has had it's parent set correctly,
-              // otherwise it can cause a focus issue.
-              SetWindowPos(mPluginWindowHWND, nullptr, 0, 0, aWindow.width,
-                           aWindow.height, SWP_NOZORDER | SWP_NOREPOSITION);
-          }
-
           mWindow.window = (void*)mPluginWindowHWND;
           mWindow.x = aWindow.x;
           mWindow.y = aWindow.y;
           mWindow.width = aWindow.width;
           mWindow.height = aWindow.height;
           mWindow.type = aWindow.type;
 
           if (mPluginIface->setwindow) {
@@ -1453,16 +1461,18 @@ PluginInstanceChild::DestroyPluginWindow
 
 void
 PluginInstanceChild::SizePluginWindow(int width,
                                       int height)
 {
     if (mPluginWindowHWND) {
         mPluginSize.x = width;
         mPluginSize.y = height;
+        SetWindowPos(mPluginWindowHWND, nullptr, 0, 0, width, height,
+                     SWP_NOZORDER | SWP_NOREPOSITION);
     }
 }
 
 // See chromium's webplugin_delegate_impl.cc for explanation of this function.
 // static
 LRESULT CALLBACK
 PluginInstanceChild::DummyWindowProc(HWND hWnd,
                                      UINT message,
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -60,18 +60,20 @@ class PluginInstanceChild : public PPlug
                                              LPARAM lParam);
     static LRESULT CALLBACK PluginWindowProcInternal(HWND hWnd,
                                                      UINT message,
                                                      WPARAM wParam,
                                                      LPARAM lParam);
 #endif
 
 protected:
-    bool AnswerNPP_SetWindow(const NPRemoteWindow& window,
-                             NPRemoteWindow* aChildWindowToBeAdopted) override;
+    bool AnswerCreateChildPluginWindow(const NPRemoteWindow& window,
+                                       NPRemoteWindow* aCreatedChild) override;
+
+    bool AnswerNPP_SetWindow(const NPRemoteWindow& window) override;
 
     virtual bool
     AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(bool* wantsAllStreams, NPError* rv) override;
     virtual bool
     AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(bool* needs, NPError* rv) override;
     virtual bool
     AnswerNPP_GetValue_NPPVpluginScriptableNPObject(PPluginScriptableObjectChild** value,
                                                     NPError* result) override;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -112,16 +112,18 @@ PluginInstanceParent::PluginInstancePare
     , mUseSurrogate(true)
     , mNPP(npp)
     , mNPNIface(npniface)
     , mIsWhitelistedForShumway(false)
     , mWindowType(NPWindowTypeWindow)
     , mDrawingModel(kDefaultDrawingModel)
 #if defined(OS_WIN)
     , mPluginHWND(nullptr)
+    , mChildPluginHWND(nullptr)
+    , mChildPluginsParentHWND(nullptr)
     , mPluginWndProc(nullptr)
     , mNestedEventState(false)
 #endif // defined(XP_WIN)
 #if defined(XP_MACOSX)
     , mShWidth(0)
     , mShHeight(0)
     , mShColorSpace(nullptr)
 #endif
@@ -1020,41 +1022,47 @@ PluginInstanceParent::NPP_SetWindow(cons
 
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     const NPSetWindowCallbackStruct* ws_info =
       static_cast<NPSetWindowCallbackStruct*>(aWindow->ws_info);
     window.visualID = ws_info->visual ? ws_info->visual->visualid : None;
     window.colormap = ws_info->colormap;
 #endif
 
-    NPRemoteWindow childWindow;
-    if (!CallNPP_SetWindow(window, &childWindow)) {
-        return NPERR_GENERIC_ERROR;
+#if defined(XP_WIN)
+    // On Windows we need to create and set the parent before we set the window
+    // on the plugin, or certain things like keyboard interaction will not work.
+    if (!mChildPluginHWND && mWindowType == NPWindowTypeWindow) {
+        NPRemoteWindow childWindow;
+        if (!CallCreateChildPluginWindow(window, &childWindow)) {
+            return NPERR_GENERIC_ERROR;
+        }
+
+        mChildPluginHWND = reinterpret_cast<HWND>(childWindow.window);
     }
 
-#if defined(XP_WIN)
-    // If a child window is returned it means that we need to re-parent it.
-    if (childWindow.window) {
+    // It's not clear if the parent window would ever change, but when this was
+    // done in the NPAPI child it used to allow for this.
+    if (mChildPluginHWND && mPluginHWND != mChildPluginsParentHWND) {
         nsCOMPtr<nsIWidget> widget;
         static_cast<const nsPluginNativeWindow*>(aWindow)->
             GetPluginWidget(getter_AddRefs(widget));
         if (widget) {
             widget->SetNativeData(NS_NATIVE_CHILD_WINDOW,
-                                  static_cast<uintptr_t>(childWindow.window));
+                                  reinterpret_cast<uintptr_t>(mChildPluginHWND));
         }
 
-        // Now it has got the correct parent, make sure it is visible.
-        // In subsequent calls to SetWindow these calls happen in the Child.
-        HWND childHWND = reinterpret_cast<HWND>(childWindow.window);
-        ShowWindow(childHWND, SW_SHOWNA);
-        SetWindowPos(childHWND, nullptr, 0, 0, window.width, window.height,
-                     SWP_NOZORDER | SWP_NOREPOSITION);
+        mChildPluginsParentHWND = mPluginHWND;
     }
 #endif
 
+    if (!CallNPP_SetWindow(window)) {
+        return NPERR_GENERIC_ERROR;
+    }
+
     return NPERR_NO_ERROR;
 }
 
 NPError
 PluginInstanceParent::NPP_GetValue(NPPVariable aVariable,
                                    void* _retval)
 {
     switch (aVariable) {
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -361,16 +361,18 @@ private:
     void SubclassPluginWindow(HWND aWnd);
     void UnsubclassPluginWindow();
 
 private:
     gfx::SharedDIBWin  mSharedSurfaceDib;
     nsIntRect          mPluginPort;
     nsIntRect          mSharedSize;
     HWND               mPluginHWND;
+    HWND               mChildPluginHWND;
+    HWND               mChildPluginsParentHWND;
     WNDPROC            mPluginWndProc;
     bool               mNestedEventState;
 
     // This will automatically release the textures when this object goes away.
     nsRefPtrHashtable<nsPtrHashKey<void>, ID3D10Texture2D> mTextureMap;
 #endif // defined(XP_WIN)
 #if defined(MOZ_WIDGET_COCOA)
 private:
--- a/dom/presentation/provider/PresentationDeviceProviderModule.cpp
+++ b/dom/presentation/provider/PresentationDeviceProviderModule.cpp
@@ -23,17 +23,18 @@ static const mozilla::Module::CIDEntry k
 };
 
 static const mozilla::Module::ContractIDEntry kPresentationDeviceProviderContracts[] = {
   { MULTICAST_DNS_PROVIDER_CONTRACT_ID, &kMULTICAST_DNS_PROVIDER_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kPresentationDeviceProviderCategories[] = {
-#if defined(MOZ_WIDGET_ANDROID) || (defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 16)
+#if (defined(MOZ_WIDGET_ANDROID) && ANDROID_VERSION >= 21) || \
+    (defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 16)
   { PRESENTATION_DEVICE_PROVIDER_CATEGORY, "MulticastDNSDeviceProvider", MULTICAST_DNS_PROVIDER_CONTRACT_ID },
 #endif
   { nullptr }
 };
 
 static const mozilla::Module kPresentationDeviceProviderModule = {
   mozilla::Module::kVersion,
   kPresentationDeviceProviderCIDs,
--- a/dom/requestsync/tests/mochitest.ini
+++ b/dom/requestsync/tests/mochitest.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 skip-if = e10s
 support-files =
   file_app.template.webapp
   file_app.sjs
   file_basic_app.html
   common_app.js
   common_basic.js
+  system_message_chrome_script.js
 
 [test_webidl.html]
 [test_minInterval.html]
 [test_basic.html]
 [test_basic_app.html]
 skip-if = buildapp == 'b2g'
 [test_wakeUp.html]
 skip-if = !(buildapp == 'b2g' && toolkit == 'gonk')
new file mode 100644
--- /dev/null
+++ b/dom/requestsync/tests/system_message_chrome_script.js
@@ -0,0 +1,18 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+'use strict';
+
+const { classes: Cc, interfaces: Ci } = Components;
+
+const systemMessenger = Cc["@mozilla.org/system-message-internal;1"]
+                          .getService(Ci.nsISystemMessagesInternal);
+const ioService = Cc["@mozilla.org/network/io-service;1"]
+                    .getService(Ci.nsIIOService);
+
+addMessageListener("trigger-register-page", function(aData) {
+  systemMessenger.registerPage(aData.type,
+                               ioService.newURI(aData.pageURL, null, null),
+                               ioService.newURI(aData.manifestURL, null, null));
+  sendAsyncMessage("page-registered");
+});
--- a/dom/requestsync/tests/test_runNow.html
+++ b/dom/requestsync/tests/test_runNow.html
@@ -8,16 +8,30 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <div id="container"></div>
   <script type="application/javascript;version=1.7">
 
   var taskExecuted = false;
 
+  function registerPage() {
+    var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('system_message_chrome_script.js'));
+    gScript.addMessageListener("page-registered", function pageRegisteredHandler() {
+      gScript.removeMessageListener("page-registered", pageRegisteredHandler);
+      gScript.destroy();
+      runTests();
+    });
+
+    gScript.sendAsyncMessage("trigger-register-page",
+                             { type: "request-sync",
+                               manifestURL: window.location.origin + "/manifest.webapp",
+                               pageURL: window.location.href });
+  }
+
   function setMessageHandler() {
     navigator.mozSetMessageHandler('request-sync', function(e) {
       ok(true, "One event has been received!");
 
       if (e.task == "oneShot") {
         is(e.data, 42, "e.data is correct");
         is(e.lastSync, 0, "e.lastSync is correct");
         is(e.oneShot, true, "e.oneShot is correct");
@@ -80,16 +94,18 @@
 
     function() {
       if (SpecialPowers.isMainProcess()) {
         SpecialPowers.Cu.import("resource://gre/modules/RequestSyncService.jsm");
       }
       runTests();
     },
 
+    registerPage,
+
     setMessageHandler,
 
     test_register_oneShot,
 
     test_runNow,
 
     test_unregister_oneShot,
   ];
--- a/dom/requestsync/tests/test_wakeUp.html
+++ b/dom/requestsync/tests/test_wakeUp.html
@@ -15,16 +15,30 @@
   var multiShotCounter = 0;
 
   function maybeDone() {
     if (oneShotCounter == 1 && multiShotCounter == 5) {
       runTests();
     }
   }
 
+  function registerPage() {
+    var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('system_message_chrome_script.js'));
+    gScript.addMessageListener("page-registered", function pageRegisteredHandler() {
+      gScript.removeMessageListener("page-registered", pageRegisteredHandler);
+      gScript.destroy();
+      runTests();
+    });
+
+    gScript.sendAsyncMessage("trigger-register-page",
+                             { type: "request-sync",
+                               manifestURL: window.location.origin + "/manifest.webapp",
+                               pageURL: window.location.href });
+  }
+
   function setMessageHandler() {
     navigator.mozSetMessageHandler('request-sync', function(e) {
       ok(true, "One event has been received!");
 
       if (e.task == "oneShot") {
         is(e.data, 42, "e.data is correct");
         is(e.lastSync, 0, "e.lastSync is correct");
         is(e.oneShot, true, "e.oneShot is correct");
@@ -141,16 +155,17 @@
 
     function() {
       if (SpecialPowers.isMainProcess()) {
         SpecialPowers.Cu.import("resource://gre/modules/RequestSyncService.jsm");
       }
       runTests();
     },
 
+    registerPage,
     setMessageHandler,
 
     test_register_oneShot,
     test_register_multiShots,
 
     test_wait,
 
     test_unregister_oneShot,
--- a/dom/storage/DOMStorageDBThread.cpp
+++ b/dom/storage/DOMStorageDBThread.cpp
@@ -182,28 +182,28 @@ DOMStorageDBThread::GetScopesHavingData(
 nsresult
 DOMStorageDBThread::InsertDBOp(DOMStorageDBThread::DBOperation* aOperation)
 {
   MonitorAutoLock monitor(mThreadObserver->GetMonitor());
 
   // Sentinel to don't forget to delete the operation when we exit early.
   nsAutoPtr<DOMStorageDBThread::DBOperation> opScope(aOperation);
 
+  if (NS_FAILED(mStatus)) {
+    MonitorAutoUnlock unlock(mThreadObserver->GetMonitor());
+    aOperation->Finalize(mStatus);
+    return mStatus;
+  }
+
   if (mStopIOThread) {
     // Thread use after shutdown demanded.
     MOZ_ASSERT(false);
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  if (NS_FAILED(mStatus)) {
-    MonitorAutoUnlock unlock(mThreadObserver->GetMonitor());
-    aOperation->Finalize(mStatus);
-    return mStatus;
-  }
-
   switch (aOperation->Type()) {
   case DBOperation::opPreload:
   case DBOperation::opPreloadUrgent:
     if (mPendingTasks.IsScopeUpdatePending(aOperation->Scope())) {
       // If there is a pending update operation for the scope first do the flush
       // before we preload the cache.  This may happen in an extremely rare case
       // when a child process throws away its cache before flush on the parent
       // has finished.  If we would preloaded the cache as a priority operation 
--- a/dom/webidl/RTCConfiguration.webidl
+++ b/dom/webidl/RTCConfiguration.webidl
@@ -9,20 +9,27 @@
 
 dictionary RTCIceServer {
     (DOMString or sequence<DOMString>) urls;
     DOMString  url; //deprecated
     DOMString? credential = null;
     DOMString? username = null;
 };
 
+enum RTCIceTransportPolicy {
+    "none",
+    "relay",
+    "all"
+};
+
 enum RTCBundlePolicy {
     "balanced",
     "max-compat",
     "max-bundle"
 };
 
 dictionary RTCConfiguration {
     sequence<RTCIceServer> iceServers;
+    RTCIceTransportPolicy  iceTransportPolicy = "all";
     RTCBundlePolicy bundlePolicy = "balanced";
     DOMString? peerIdentity = null;
     sequence<RTCCertificate> certificates;
 };
--- a/extensions/cookie/test/unit/xpcshell.ini
+++ b/extensions/cookie/test/unit/xpcshell.ini
@@ -6,16 +6,17 @@ support-files =
   cookieprompt.js
   cookieprompt.manifest
 
 [test_bug526789.js]
 [test_bug650522.js]
 [test_bug667087.js]
 [test_cookies_async_failure.js]
 [test_cookies_persistence.js]
+skip-if = true # Bug 863738
 [test_cookies_privatebrowsing.js]
 [test_cookies_profile_close.js]
 [test_cookies_read.js]
 [test_cookies_sync_failure.js]
 [test_cookies_thirdparty.js]
 [test_cookies_thirdparty_session.js]
 [test_domain_eviction.js]
 [test_eviction.js]
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -1055,51 +1055,52 @@ gfxPlatformFontList::SizeOfFontFamilyTab
         // We don't count the size of the family here, because this is an
         // *extra* reference to a family that will have already been counted in
         // the main list.
         n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
     }
     return n;
 }
 
+/*static*/ size_t
+gfxPlatformFontList::SizeOfFontEntryTableExcludingThis(
+    const FontEntryTable& aTable,
+    MallocSizeOf aMallocSizeOf)
+{
+    size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+    for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
+        // The font itself is counted by its owning family; here we only care
+        // about the names stored in the hashtable keys.
+        n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+    }
+    return n;
+}
+
 void
 gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                             FontListSizes* aSizes) const
 {
     aSizes->mFontListSize +=
         mFontFamilies.ShallowSizeOfExcludingThis(aMallocSizeOf);
     for (auto iter = mFontFamilies.ConstIter(); !iter.Done(); iter.Next()) {
         aSizes->mFontListSize +=
             iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
         iter.Data()->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
     }
 
     aSizes->mFontListSize +=
         SizeOfFontFamilyTableExcludingThis(mOtherFamilyNames, aMallocSizeOf);
 
     if (mExtraNames) {
-        // For these two tables, the font itself is counted by its owning
-        // family; here we only care about the names stored in the hashtable
-        // keys.
         aSizes->mFontListSize +=
-            mExtraNames->mFullnames.ShallowSizeOfExcludingThis(aMallocSizeOf);
-        for (auto iter = mExtraNames->mFullnames.ConstIter();
-             !iter.Done();
-             iter.Next()) {
-            aSizes->mFontListSize +=
-                iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
-        }
+            SizeOfFontEntryTableExcludingThis(mExtraNames->mFullnames,
+                                              aMallocSizeOf);
         aSizes->mFontListSize +=
-            mExtraNames->mPostscriptNames.ShallowSizeOfExcludingThis(aMallocSizeOf);
-        for (auto iter = mExtraNames->mPostscriptNames.ConstIter();
-             !iter.Done();
-             iter.Next()) {
-            aSizes->mFontListSize +=
-                iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
-        }
+            SizeOfFontEntryTableExcludingThis(mExtraNames->mPostscriptNames,
+                                              aMallocSizeOf);
     }
 
     aSizes->mFontListSize +=
         mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf);
     aSizes->mFontListSize +=
         mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
 
     aSizes->mFontListSize +=
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -297,21 +297,25 @@ protected:
     void GetPrefsAndStartLoader();
 
     // for font list changes that affect all documents
     void ForceGlobalReflow();
 
     void RebuildLocalFonts();
 
     typedef nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> FontFamilyTable;
+    typedef nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> FontEntryTable;
 
     // used by memory reporter to accumulate sizes of family names in the table
     static size_t
     SizeOfFontFamilyTableExcludingThis(const FontFamilyTable& aTable,
                                        mozilla::MallocSizeOf aMallocSizeOf);
+    static size_t
+    SizeOfFontEntryTableExcludingThis(const FontEntryTable& aTable,
+                                      mozilla::MallocSizeOf aMallocSizeOf);
 
     // canonical family name ==> family entry (unique, one name per family entry)
     FontFamilyTable mFontFamilies;
 
 #if defined(XP_MACOSX)
     // hidden system fonts used within UI elements
     FontFamilyTable mSystemFontFamilies;
 #endif
@@ -323,20 +327,21 @@ protected:
     // flag set after InitOtherFamilyNames is called upon first name lookup miss
     bool mOtherFamilyNamesInitialized;
 
     // flag set after fullname and Postcript name lists are populated
     bool mFaceNameListsInitialized;
 
     struct ExtraNames {
       ExtraNames() : mFullnames(64), mPostscriptNames(64) {}
+
       // fullname ==> font entry (unique, one name per font entry)
-      nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> mFullnames;
+      FontEntryTable mFullnames;
       // Postscript name ==> font entry (unique, one name per font entry)
-      nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> mPostscriptNames;
+      FontEntryTable mPostscriptNames;
     };
     nsAutoPtr<ExtraNames> mExtraNames;
 
     // face names missed when face name loading takes a long time
     nsAutoPtr<nsTHashtable<nsStringHashKey> > mFaceNamesMissed;
 
     // localized family names missed when face name loading takes a long time
     nsAutoPtr<nsTHashtable<nsStringHashKey> > mOtherNamesMissed;
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -1413,16 +1413,17 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
         if (skipSides.Bottom()) {
           nscoord ymost = fragmentClip.YMost();
           nscoord overflow = ymost - aFrameArea.YMost();
           if (overflow > 0) {
             fragmentClip.height -= overflow;
           }
         }
       }
+      fragmentClip = fragmentClip.Intersect(aDirtyRect);
       renderContext->
         Clip(NSRectToSnappedRect(fragmentClip,
                                  aForFrame->PresContext()->AppUnitsPerDevPixel(),
                                  aDrawTarget));
 
       RectCornerRadii clipRectRadii;
       if (hasBorderRadius) {
         Float spreadDistance = shadowItem->mSpread / twipsPerPixel;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/1178575-2-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html><head>
+    <meta charset="utf-8">
+    <title>Testcase #2 for bug 1178575</title>
+    <style type="text/css">
+
+        html,body {
+            color:black; background-color:white; font-size:16px; padding:0; margin:20px;
+        }
+
+        div {
+            position: absolute;
+            left: 0;
+            right: 0;
+            box-shadow: 0 0 0 10px rgba(0, 255, 0, 0.5);
+            margin-top: 10px;
+            height: 40px;
+        }
+
+        span {
+          position: relative;
+          display: block;
+          overflow: hidden;
+          height: 100px;
+        }
+
+    </style>
+</head>
+<body><span><div></div><span></body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/1178575-2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+    <meta charset="utf-8">
+    <title>Testcase #2 for bug 1178575</title>
+    <style type="text/css">
+
+        html,body {
+            color:black; background-color:white; font-size:16px; padding:0; margin:20px;
+        }
+
+        div {
+            position: absolute;
+            left: 0;
+            right: 0;
+            box-shadow: 0 0 0 10px rgba(0, 255, 0, 0.5);
+            margin-top: 10px;
+        }
+
+        span {
+          position: relative;
+          display: block;
+          overflow: hidden;
+          height: 200px;
+        }
+
+    </style>
+<script>
+function tweak() {
+  document.querySelector('div').style.height = "40px";
+  document.documentElement.removeAttribute("class");
+}
+window.addEventListener("MozReftestInvalidate", tweak, false);
+</script>
+</head>
+<body><span><div></div><span></body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/1178575-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html><head>
+    <meta charset="utf-8">
+    <title>Testcase for bug 1178575</title>
+    <style type="text/css">
+
+        html,body {
+            color:black; background-color:white; font-size:16px; padding:0; margin:0;
+        }
+
+        div {
+            position: absolute;
+            left: 0;
+            right: 0;
+            box-shadow: 0 0 0 10px rgba(0, 255, 0, 0.5);
+            margin-top: 10px;
+            height: 3em;
+        }
+
+    </style>
+</head>
+<body><div></div></body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/1178575.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+    <meta charset="utf-8">
+    <title>Testcase for bug 1178575</title>
+    <style type="text/css">
+
+        html,body {
+            color:black; background-color:white; font-size:16px; padding:0; margin:0;
+        }
+
+        div {
+            position: absolute;
+            left: 0;
+            right: 0;
+            box-shadow: 0 0 0 10px rgba(0, 255, 0, 0.5);
+            margin-top: 10px;
+        }
+
+    </style>
+<script>
+function tweak() {
+  document.querySelector('div').style.height = "3em";
+  document.documentElement.removeAttribute("class");
+}
+window.addEventListener("MozReftestInvalidate", tweak, false);
+</script>
+</head>
+<body><div></div></body></html>
--- a/layout/reftests/box-shadow/reftest.list
+++ b/layout/reftests/box-shadow/reftest.list
@@ -24,8 +24,10 @@ random-if(d2d) == boxshadow-threecorners
 
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref.html
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref2.html
 == overflow-not-scrollable-2.html overflow-not-scrollable-2-ref.html
 fails-if(B2G||Mulet) == 611574-1.html 611574-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fails-if(B2G||Mulet) == 611574-2.html 611574-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fuzzy-if(winWidget,5,30) == fieldset.html fieldset-ref.html # minor anti-aliasing problem on Windows
 fuzzy-if(winWidget,5,30) == fieldset-inset.html fieldset-inset-ref.html # minor anti-aliasing problem on Windows
+== 1178575.html 1178575-ref.html
+== 1178575-2.html 1178575-2-ref.html
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -376,19 +376,20 @@ void NrIceCtx::trickle_cb(void *arg, nr_
   s->SignalCandidate(s, candidate_str);
 }
 
 RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& name,
                                   bool offerer,
                                   bool set_interface_priorities,
                                   bool allow_loopback,
                                   bool tcp_enabled,
-                                  bool allow_link_local) {
+                                  bool allow_link_local,
+                                  Policy policy) {
 
-  RefPtr<NrIceCtx> ctx = new NrIceCtx(name, offerer);
+  RefPtr<NrIceCtx> ctx = new NrIceCtx(name, offerer, policy);
 
   // Initialize the crypto callbacks and logging stuff
   if (!initialized) {
     NR_reg_init(NR_REG_MODE_LOCAL);
     RLogRingBuffer::CreateInstance();
     nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl;
     initialized = true;
 
@@ -480,16 +481,19 @@ RefPtr<NrIceCtx> NrIceCtx::Create(const 
   }
 
   // Create the ICE context
   int r;
 
   UINT4 flags = offerer ? NR_ICE_CTX_FLAGS_OFFERER:
       NR_ICE_CTX_FLAGS_ANSWERER;
   flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION;
+  if (policy == ICE_POLICY_RELAY) {
+    flags |= NR_ICE_CTX_FLAGS_RELAY_ONLY;
+  }
 
   r = nr_ice_ctx_create(const_cast<char *>(name.c_str()), flags,
                         &ctx->ctx_);
   if (r) {
     MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name << "'");
     return nullptr;
   }
 
@@ -604,16 +608,21 @@ nsresult NrIceCtx::SetControlling(Contro
             controlling);
   return NS_OK;
 }
 
 NrIceCtx::Controlling NrIceCtx::GetControlling() {
   return (peer_->controlling) ? ICE_CONTROLLING : ICE_CONTROLLED;
 }
 
+nsresult NrIceCtx::SetPolicy(Policy policy) {
+  policy_ = policy;
+  return NS_OK;
+}
+
 nsresult NrIceCtx::SetStunServers(const std::vector<NrIceStunServer>&
                                   stun_servers) {
   if (stun_servers.empty())
     return NS_OK;
 
   auto servers = MakeUnique<nr_ice_stun_server[]>(stun_servers.size());
 
   for (size_t i=0; i < stun_servers.size(); ++i) {
@@ -709,16 +718,19 @@ abort:
     nr_socket_wrapper_factory_destroy(&wrapper);
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult NrIceCtx::StartGathering() {
   ASSERT_ON_THREAD(sts_target_);
+  if (policy_ == ICE_POLICY_NONE) {
+    return NS_OK;
+  }
   SetGatheringState(ICE_CTX_GATHER_STARTED);
   // This might start gathering for the first time, or again after
   // renegotiation, or might do nothing at all if gathering has already
   // finished.
   int r = nr_ice_gather(ctx_, &NrIceCtx::gather_cb, this);
 
   if (!r) {
     SetGatheringState(ICE_CTX_GATHER_COMPLETE);
@@ -783,16 +795,21 @@ nsresult NrIceCtx::ParseGlobalAttributes
   }
 
   return NS_OK;
 }
 
 nsresult NrIceCtx::StartChecks() {
   int r;
 
+  if (policy_ == ICE_POLICY_NONE) {
+    MOZ_MTLOG(ML_ERROR, "Couldn't start peer checks because policy == none");
+    SetConnectionState(ICE_CTX_FAILED);
+    return NS_ERROR_FAILURE;
+  }
   r=nr_ice_peer_ctx_pair_candidates(peer_);
   if (r) {
     MOZ_MTLOG(ML_ERROR, "Couldn't pair candidates on "
               << name_ << "'");
     SetConnectionState(ICE_CTX_FAILED);
     return NS_ERROR_FAILURE;
   }
 
--- a/media/mtransport/nricectx.h
+++ b/media/mtransport/nricectx.h
@@ -203,22 +203,28 @@ class NrIceCtx {
                         ICE_CTX_GATHER_STARTED,
                         ICE_CTX_GATHER_COMPLETE
   };
 
   enum Controlling { ICE_CONTROLLING,
                      ICE_CONTROLLED
   };
 
+  enum Policy { ICE_POLICY_NONE,
+                ICE_POLICY_RELAY,
+                ICE_POLICY_ALL
+  };
+
   static RefPtr<NrIceCtx> Create(const std::string& name,
                                  bool offerer,
                                  bool set_interface_priorities = true,
                                  bool allow_loopback = false,
                                  bool tcp_enabled = true,
-                                 bool allow_link_local = false);
+                                 bool allow_link_local = false,
+                                 Policy policy = ICE_POLICY_ALL);
 
   // Deinitialize all ICE global state. Used only for testing.
   static void internal_DeinitializeGlobal();
 
 
   nr_ice_ctx *ctx() { return ctx_; }
   nr_ice_peer_ctx *peer() { return peer_; }
 
@@ -267,16 +273,24 @@ class NrIceCtx {
   // Set the other side's global attributes
   nsresult ParseGlobalAttributes(std::vector<std::string> attrs);
 
   // Set whether we are controlling or not.
   nsresult SetControlling(Controlling controlling);
 
   Controlling GetControlling();
 
+  // Set whether we're allowed to produce none, relay or all candidates.
+  // TODO(jib@mozilla.com): Work out what change means mid-connection (1181768)
+  nsresult SetPolicy(Policy policy);
+
+  Policy policy() const {
+    return policy_;
+  }
+
   // Set the STUN servers. Must be called before StartGathering
   // (if at all).
   nsresult SetStunServers(const std::vector<NrIceStunServer>& stun_servers);
 
   // Set the TURN servers. Must be called before StartGathering
   // (if at all).
   nsresult SetTurnServers(const std::vector<NrIceTurnServer>& turn_servers);
 
@@ -310,27 +324,29 @@ class NrIceCtx {
 
   // The thread to direct method calls to
   nsCOMPtr<nsIEventTarget> thread() { return sts_target_; }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceCtx)
 
  private:
   NrIceCtx(const std::string& name,
-           bool offerer)
+           bool offerer,
+           Policy policy)
   : connection_state_(ICE_CTX_INIT),
     gathering_state_(ICE_CTX_GATHER_INIT),
     name_(name),
     offerer_(offerer),
     streams_(),
     ctx_(nullptr),
     peer_(nullptr),
     ice_handler_vtbl_(nullptr),
     ice_handler_(nullptr),
-    trickle_(true) {
+    trickle_(true),
+    policy_(policy) {
     // XXX: offerer_ will be used eventually;  placate clang in the meantime.
     (void)offerer_;
   }
 
   virtual ~NrIceCtx();
 
   DISALLOW_COPY_ASSIGN(NrIceCtx);
 
@@ -366,13 +382,14 @@ class NrIceCtx {
   bool offerer_;
   std::vector<RefPtr<NrIceMediaStream> > streams_;
   nr_ice_ctx *ctx_;
   nr_ice_peer_ctx *peer_;
   nr_ice_handler_vtbl* ice_handler_vtbl_;  // Must be pointer
   nr_ice_handler* ice_handler_;  // Must be pointer
   bool trickle_;
   nsCOMPtr<nsIEventTarget> sts_target_; // The thread to run on
+  Policy policy_;
 };
 
 
 }  // close namespace
 #endif
--- a/media/mtransport/test/turn_unittest.cpp
+++ b/media/mtransport/test/turn_unittest.cpp
@@ -499,17 +499,18 @@ int main(int argc, char **argv)
   NSS_NoDB_Init(nullptr);
   NSS_SetDomesticPolicy();
 
   // Set up the ICE registry, etc.
   // TODO(ekr@rtfm.com): Clean up
   std::string dummy("dummy");
   RUN_ON_THREAD(test_utils->sts_target(),
                 WrapRunnableNM(&NrIceCtx::Create,
-                               dummy, false, false, false, false, false),
+                               dummy, false, false, false, false, false,
+                               NrIceCtx::ICE_POLICY_ALL),
                 NS_DISPATCH_SYNC);
 
   // Start the tests
   ::testing::InitGoogleTest(&argc, argv);
 
   int rv = RUN_ALL_TESTS();
   delete test_utils;
   return rv;
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
@@ -129,16 +129,17 @@ static int nr_ice_candidate_format_stun_
 
     _status=0;
    abort:
     return(_status);
   }
 
 int nr_ice_candidate_create(nr_ice_ctx *ctx,nr_ice_component *comp,nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_socket_tcp_type tcp_type, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp)
   {
+    assert(!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) || ctype == RELAYED);
     nr_ice_candidate *cand=0;
     nr_ice_candidate *tmp=0;
     int r,_status;
     char label[512];
 
     if(!(cand=RCALLOC(sizeof(nr_ice_candidate))))
       ABORT(R_NO_MEMORY);
     cand->state=NR_ICE_CAND_STATE_CREATED;
@@ -916,16 +917,17 @@ static void nr_ice_turn_allocated_cb(NR_
 
 /* Format the candidate attribute as per ICE S 15.1 */
 int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen)
   {
     int r,_status;
     char addr[64];
     int port;
     int len;
+    nr_transport_addr *raddr;
 
     assert(!strcmp(nr_ice_candidate_type_names[HOST], "host"));
     assert(!strcmp(nr_ice_candidate_type_names[RELAYED], "relay"));
 
     if(r=nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr)))
       ABORT(r);
     if(r=nr_transport_addr_get_port(&cand->addr,&port))
       ABORT(r);
@@ -934,33 +936,36 @@ int nr_ice_format_candidate_attribute(nr
       port=9;
     snprintf(attr,maxlen,"candidate:%s %d %s %u %s %d typ %s",
       cand->foundation, cand->component_id, cand->addr.protocol==IPPROTO_UDP?"UDP":"TCP",cand->priority, addr, port,
       nr_ctype_name(cand->type));
 
     len=strlen(attr); attr+=len; maxlen-=len;
 
     /* raddr, rport */
+    raddr = (cand->stream->ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) ?
+      &cand->addr : &cand->base;
+
     switch(cand->type){
       case HOST:
         break;
       case SERVER_REFLEXIVE:
       case PEER_REFLEXIVE:
-        if(r=nr_transport_addr_get_addrstring(&cand->base,addr,sizeof(addr)))
+        if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr)))
           ABORT(r);
-        if(r=nr_transport_addr_get_port(&cand->base,&port))
+        if(r=nr_transport_addr_get_port(raddr,&port))
           ABORT(r);
 
         snprintf(attr,maxlen," raddr %s rport %d",addr,port);
         break;
       case RELAYED:
         // comes from XorMappedAddress via AllocateResponse
-        if(r=nr_transport_addr_get_addrstring(&cand->base,addr,sizeof(addr)))
+        if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr)))
           ABORT(r);
-        if(r=nr_transport_addr_get_port(&cand->base,&port))
+        if(r=nr_transport_addr_get_port(raddr,&port))
           ABORT(r);
 
         snprintf(attr,maxlen," raddr %s rport %d",addr,port);
         break;
       default:
         assert(0);
         ABORT(R_INTERNAL);
         break;
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
@@ -255,17 +255,22 @@ static void nr_ice_candidate_pair_stun_c
           while(cand){
             if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
               break;
 
             cand=TAILQ_NEXT(cand,entry_comp);
           }
 
           /* OK, nothing found, must be peer reflexive */
-          if(!cand){
+          if(!cand) {
+            if (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) {
+              /* Any STUN response with a reflexive address in it is unwanted
+                 when we'll send on relay only. Bail since cand is used below. */
+              goto done;
+            }
             if(r=nr_ice_candidate_create(pair->pctx->ctx,
               pair->local->component,pair->local->isock,pair->local->osock,
               PEER_REFLEXIVE,pair->local->tcp_type,0,pair->local->component->component_id,&cand))
               ABORT(r);
             if(r=nr_transport_addr_copy(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr))
               ABORT(r);
             cand->state=NR_ICE_CAND_STATE_INITIALIZED;
             TAILQ_INSERT_TAIL(&pair->local->component->candidates,cand,entry_comp);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -221,63 +221,69 @@ static int nr_ice_component_initialize_u
       r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): host address %s",ctx->label,addrs[i].addr.as_string);
       if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addrs[i].addr,&sock))){
         r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create socket for address %s",ctx->label,addrs[i].addr.as_string);
         continue;
       }
 
       if(r=nr_ice_socket_create(ctx,component,sock,NR_ICE_SOCKET_TYPE_DGRAM,&isock))
         ABORT(r);
-      /* Create one host candidate */
-      if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0,0,
-        component->component_id,&cand))
-        ABORT(r);
-
-      TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
-      component->candidate_ct++;
-      cand=0;
 
-      /* And a srvrflx candidate for each STUN server */
-      for(j=0;j<ctx->stun_server_ct;j++){
-        /* Skip non-UDP */
-        if(ctx->stun_servers[j].transport!=IPPROTO_UDP)
-          continue;
+      if (!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) {
+        /* Create one host candidate */
+        if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0,0,
+          component->component_id,&cand))
+          ABORT(r);
 
-        if(r=nr_ice_candidate_create(ctx,component,
-          isock,sock,SERVER_REFLEXIVE,0,
-          &ctx->stun_servers[j],component->component_id,&cand))
-          ABORT(r);
         TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
         component->candidate_ct++;
         cand=0;
+
+        /* And a srvrflx candidate for each STUN server */
+        for(j=0;j<ctx->stun_server_ct;j++){
+          /* Skip non-UDP */
+          if(ctx->stun_servers[j].transport!=IPPROTO_UDP)
+            continue;
+
+          if(r=nr_ice_candidate_create(ctx,component,
+            isock,sock,SERVER_REFLEXIVE,0,
+            &ctx->stun_servers[j],component->component_id,&cand))
+            ABORT(r);
+          TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+          component->candidate_ct++;
+          cand=0;
+        }
       }
 
 #ifdef USE_TURN
-      /* And both a srvrflx and relayed candidate for each TURN server */
+      /* And both a srvrflx and relayed candidate for each TURN server (unless
+         we're in relay-only mode, in which case just the relayed one) */
       for(j=0;j<ctx->turn_server_ct;j++){
         nr_socket *turn_sock;
-        nr_ice_candidate *srvflx_cand;
+        nr_ice_candidate *srvflx_cand=0;
 
         /* Skip non-UDP */
         if (ctx->turn_servers[j].turn_server.transport != IPPROTO_UDP)
           continue;
 
-        /* srvrflx */
-        if(r=nr_ice_candidate_create(ctx,component,
-          isock,sock,SERVER_REFLEXIVE,0,
-          &ctx->turn_servers[j].turn_server,component->component_id,&cand))
-          ABORT(r);
-        cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */
-        cand->done_cb=nr_ice_gather_finished_cb;
-        cand->cb_arg=cand;
+        if (!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) {
+          /* srvrflx */
+          if(r=nr_ice_candidate_create(ctx,component,
+            isock,sock,SERVER_REFLEXIVE,0,
+            &ctx->turn_servers[j].turn_server,component->component_id,&cand))
+            ABORT(r);
+          cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */
+          cand->done_cb=nr_ice_gather_finished_cb;
+          cand->cb_arg=cand;
 
-        TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
-        component->candidate_ct++;
-        srvflx_cand=cand;
-
+          TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+          component->candidate_ct++;
+          srvflx_cand=cand;
+          cand=0;
+        }
         /* relayed*/
         if(r=nr_socket_turn_create(sock, &turn_sock))
           ABORT(r);
         if(r=nr_ice_candidate_create(ctx,component,
           isock,turn_sock,RELAYED,0,
           &ctx->turn_servers[j].turn_server,component->component_id,&cand))
            ABORT(r);
         cand->u.relayed.srvflx_candidate=srvflx_cand;
@@ -403,16 +409,19 @@ static int nr_ice_component_initialize_t
       if(r!=R_NOT_FOUND)
         ABORT(r);
     }
 
     if ((r=NR_reg_get_char(NR_ICE_REG_ICE_TCP_DISABLE, &ice_tcp_disabled))) {
       if (r != R_NOT_FOUND)
         ABORT(r);
     }
+    if (ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) {
+      ice_tcp_disabled = 1;
+    }
 
     for(i=0;i<addr_ct;i++){
       char suppress;
       nr_ice_socket *isock_psv=0;
       nr_ice_socket *isock_so=0;
 
       if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
         if(r!=R_NOT_FOUND)
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
@@ -152,16 +152,17 @@ struct nr_ice_ctx_ {
   void *trickle_cb_arg;
 };
 
 int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp);
 #define NR_ICE_CTX_FLAGS_OFFERER                           1
 #define NR_ICE_CTX_FLAGS_ANSWERER                          (1<<1)
 #define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION             (1<<2)
 #define NR_ICE_CTX_FLAGS_LITE                              (1<<3)
+#define NR_ICE_CTX_FLAGS_RELAY_ONLY                        (1<<4)
 
 int nr_ice_ctx_destroy(nr_ice_ctx **ctxp);
 int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg);
 int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
 void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg);
 int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp);
 int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp);
 int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -517,16 +517,30 @@ PeerConnectionConfiguration::Init(const 
       setBundlePolicy(kBundleMaxCompat);
       break;
     case dom::RTCBundlePolicy::Max_bundle:
       setBundlePolicy(kBundleMaxBundle);
       break;
     default:
       MOZ_CRASH();
   }
+
+  switch (aSrc.mIceTransportPolicy) {
+    case dom::RTCIceTransportPolicy::None:
+      setIceTransportPolicy(NrIceCtx::ICE_POLICY_NONE);
+      break;
+    case dom::RTCIceTransportPolicy::Relay:
+      setIceTransportPolicy(NrIceCtx::ICE_POLICY_RELAY);
+      break;
+    case dom::RTCIceTransportPolicy::All:
+      setIceTransportPolicy(NrIceCtx::ICE_POLICY_ALL);
+      break;
+    default:
+      MOZ_CRASH();
+  }
   return NS_OK;
 }
 
 nsresult
 PeerConnectionConfiguration::AddIceServer(const RTCIceServer &aServer)
 {
   NS_ENSURE_STATE(aServer.mUrls.WasPassed());
   NS_ENSURE_STATE(aServer.mUrls.Value().IsStringSequence());
@@ -733,17 +747,18 @@ PeerConnectionImpl::Initialize(PeerConne
   mMedia->SignalIceConnectionStateChange.connect(
       this,
       &PeerConnectionImpl::IceConnectionStateChange);
 
   mMedia->SignalCandidate.connect(this, &PeerConnectionImpl::CandidateReady);
 
   // Initialize the media object.
   res = mMedia->Init(aConfiguration.getStunServers(),
-                     aConfiguration.getTurnServers());
+                     aConfiguration.getTurnServers(),
+                     aConfiguration.getIceTransportPolicy());
   if (NS_FAILED(res)) {
     CSFLogError(logTag, "%s: Couldn't initialize media object", __FUNCTION__);
     return res;
   }
 
   PeerConnectionCtx::GetInstance()->mPeerConnections[mHandle] = this;
 
   mJsepSession = MakeUnique<JsepSessionImpl>(mName,
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -144,17 +144,19 @@ class PCUuidGenerator : public mozilla::
 
  private:
   nsCOMPtr<nsIUUIDGenerator> mGenerator;
 };
 
 class PeerConnectionConfiguration
 {
 public:
-  PeerConnectionConfiguration() : mBundlePolicy(kBundleBalanced) {}
+  PeerConnectionConfiguration()
+  : mBundlePolicy(kBundleBalanced),
+    mIceTransportPolicy(NrIceCtx::ICE_POLICY_ALL) {}
 
   bool addStunServer(const std::string& addr, uint16_t port,
                      const char* transport)
   {
     NrIceStunServer* server(NrIceStunServer::Create(addr, port, transport));
     if (!server) {
       return false;
     }
@@ -181,26 +183,29 @@ public:
     return true;
   }
   void addStunServer(const NrIceStunServer& server) { mStunServers.push_back (server); }
   void addTurnServer(const NrIceTurnServer& server) { mTurnServers.push_back (server); }
   const std::vector<NrIceStunServer>& getStunServers() const { return mStunServers; }
   const std::vector<NrIceTurnServer>& getTurnServers() const { return mTurnServers; }
   void setBundlePolicy(JsepBundlePolicy policy) { mBundlePolicy = policy;}
   JsepBundlePolicy getBundlePolicy() const { return mBundlePolicy; }
+  void setIceTransportPolicy(NrIceCtx::Policy policy) { mIceTransportPolicy = policy;}
+  NrIceCtx::Policy getIceTransportPolicy() const { return mIceTransportPolicy; }
 
 #ifndef MOZILLA_EXTERNAL_LINKAGE
   nsresult Init(const RTCConfiguration& aSrc);
   nsresult AddIceServer(const RTCIceServer& aServer);
 #endif
 
 private:
   std::vector<NrIceStunServer> mStunServers;
   std::vector<NrIceTurnServer> mTurnServers;
   JsepBundlePolicy mBundlePolicy;
+  NrIceCtx::Policy mIceTransportPolicy;
 };
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
 // Not an inner class so we can forward declare.
 class RTCStatsQuery {
   public:
     explicit RTCStatsQuery(bool internalStats);
     ~RTCStatsQuery();
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -225,17 +225,18 @@ PeerConnectionMedia::PeerConnectionMedia
       mDNSResolver(new NrIceResolver()),
       mUuidGen(MakeUnique<PCUuidGenerator>()),
       mMainThread(mParent->GetMainThread()),
       mSTSThread(mParent->GetSTSThread()),
       mProxyResolveCompleted(false) {
 }
 
 nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers,
-                                   const std::vector<NrIceTurnServer>& turn_servers)
+                                   const std::vector<NrIceTurnServer>& turn_servers,
+                                   NrIceCtx::Policy policy)
 {
   nsresult rv;
 #if defined(MOZILLA_XPCOMRT_API)
   // TODO(Bug 1126039) Standalone XPCOMRT does not currently support nsIProtocolProxyService or nsIIOService
   mProxyResolveCompleted = true;
 #else
 
   nsCOMPtr<nsIProtocolProxyService> pps =
@@ -309,17 +310,18 @@ nsresult PeerConnectionMedia::Init(const
 
   // TODO(ekr@rtfm.com): need some way to set not offerer later
   // Looks like a bug in the NrIceCtx API.
   mIceCtx = NrIceCtx::Create("PC:" + mParentName,
                              true, // Offerer
                              true, // Explicitly set priorities
                              mParent->GetAllowIceLoopback(),
                              ice_tcp,
-                             mParent->GetAllowIceLinkLocal());
+                             mParent->GetAllowIceLinkLocal(),
+                             policy);
   if(!mIceCtx) {
     CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
   if (NS_FAILED(rv = mIceCtx->SetStunServers(stun_servers))) {
     CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__);
     return rv;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -223,17 +223,18 @@ class RemoteSourceStreamInfo : public So
 class PeerConnectionMedia : public sigslot::has_slots<> {
   ~PeerConnectionMedia() {}
 
  public:
   explicit PeerConnectionMedia(PeerConnectionImpl *parent);
 
   PeerConnectionImpl* GetPC() { return mParent; }
   nsresult Init(const std::vector<NrIceStunServer>& stun_servers,
-                const std::vector<NrIceTurnServer>& turn_servers);
+                const std::vector<NrIceTurnServer>& turn_servers,
+                NrIceCtx::Policy policy);
   // WARNING: This destroys the object!
   void SelfDestruct();
 
   RefPtr<NrIceCtx> ice_ctx() const { return mIceCtx; }
 
   RefPtr<NrIceMediaStream> ice_media_stream(size_t i) const {
     return mIceCtx->GetStream(i);
   }
--- a/memory/mozjemalloc/jemalloc.c
+++ b/memory/mozjemalloc/jemalloc.c
@@ -6777,17 +6777,17 @@ void*
 	/*
 	 * In order for all trailing bytes to be zeroed, the caller needs to
 	 * use calloc(), followed by recalloc().  However, the current calloc()
 	 * implementation only zeros the bytes requested, so if recalloc() is
 	 * to work 100% correctly, calloc() will need to change to zero
 	 * trailing bytes.
 	 */
 
-	ptr = realloc(ptr, newsize);
+	ptr = realloc_impl(ptr, newsize);
 	if (ptr != NULL && oldsize < newsize) {
 		memset((void *)((uintptr_t)ptr + oldsize), 0, newsize -
 		    oldsize);
 	}
 
 	return ptr;
 }
 
--- a/mobile/android/base/ANRReporter.java
+++ b/mobile/android/base/ANRReporter.java
@@ -15,18 +15,18 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.Reader;
 import java.util.Locale;
 import java.util.UUID;
 import java.util.regex.Pattern;
 
 import org.json.JSONObject;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
 import android.os.Looper;
@@ -49,21 +49,21 @@ public final class ANRReporter extends B
     private static final String TRACES_CHARSET = "utf-8";
     private static final String PING_CHARSET = "utf-8";
 
     private static final ANRReporter sInstance = new ANRReporter();
     private static int sRegisteredCount;
     private Handler mHandler;
     private volatile boolean mPendingANR;
 
-    @WrapElementForJNI
+    @WrapForJNI
     private static native boolean requestNativeStack(boolean unwind);
-    @WrapElementForJNI
+    @WrapForJNI
     private static native String getNativeStack();
-    @WrapElementForJNI
+    @WrapForJNI
     private static native void releaseNativeStack();
 
     public static void register(Context context) {
         if (sRegisteredCount++ != 0) {
             // Already registered
             return;
         }
         sInstance.start(context);
--- a/mobile/android/base/DownloadsIntegration.java
+++ b/mobile/android/base/DownloadsIntegration.java
@@ -1,20 +1,20 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.util.NativeEventListener;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 
 import java.io.File;
 import java.lang.IllegalArgumentException;
 import java.util.ArrayList;
 import java.util.List;
 
 import android.app.DownloadManager;
 import android.content.Context;
@@ -103,17 +103,17 @@ public class DownloadsIntegration implem
             // Download Manager package does not exist
             return false;
         }
 
         return (PackageManager.COMPONENT_ENABLED_STATE_ENABLED == state ||
                 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT == state);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void scanMedia(final String aFile, String aMimeType) {
         String mimeType = aMimeType;
         if (UNKNOWN_MIME_TYPES.contains(mimeType)) {
             // If this is a generic undefined mimetype, erase it so that we can try to determine
             // one from the file extension below.
             mimeType = "";
         }
 
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -25,28 +25,28 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PanZoomController;
 import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.mozglue.JNITarget;
 import org.mozilla.gecko.mozglue.RobocopTarget;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.prompts.PromptService;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoRequest;
 import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.NativeEventListener;
 import org.mozilla.gecko.util.NativeJSContainer;
@@ -456,38 +456,38 @@ public class GeckoAppShell
 
     // Synchronously notify a Gecko observer; must be called from Gecko thread.
     public static native void notifyGeckoObservers(String subject, String data);
 
     /*
      *  The Gecko-side API: API methods that Gecko calls
      */
 
-    @WrapElementForJNI(allowMultithread = true, noThrow = true)
+    @WrapForJNI(allowMultithread = true, noThrow = true)
     public static void handleUncaughtException(Thread thread, Throwable e) {
         CRASH_HANDLER.uncaughtException(thread, e);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void notifyIME(int type) {
         if (editableListener != null) {
             editableListener.notifyIME(type);
         }
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void notifyIMEContext(int state, String typeHint,
                                         String modeHint, String actionHint) {
         if (editableListener != null) {
             editableListener.notifyIMEContext(state, typeHint,
                                                modeHint, actionHint);
         }
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void notifyIMEChange(String text, int start, int end, int newEnd) {
         if (newEnd < 0) { // Selection change
             editableListener.onSelectionChange(start, end);
         } else { // Text change
             editableListener.onTextChange(text, start, end, newEnd);
         }
     }
 
@@ -529,17 +529,17 @@ public class GeckoAppShell
                 }
                 long waited = SystemClock.uptimeMillis() - time;
                 Log.d(LOGTAG, "Gecko event sync taking too long: " + waited + "ms");
             }
         }
     }
 
     // Signal the Java thread that it's time to wake up
-    @WrapElementForJNI
+    @WrapForJNI
     public static void acknowledgeEvent() {
         synchronized (sEventAckLock) {
             sWaitingForEventAck = false;
             sEventAckLock.notifyAll();
         }
     }
 
     private static final Runnable sCallbackRunnable = new Runnable() {
@@ -550,17 +550,17 @@ public class GeckoAppShell
             if (nextDelay >= 0) {
                 ThreadUtils.getUiHandler().postDelayed(this, nextDelay);
             }
         }
     };
 
     private static native long runUiThreadCallback();
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     private static void requestUiThreadCallback(long delay) {
         ThreadUtils.getUiHandler().postDelayed(sCallbackRunnable, delay);
     }
 
     private static float getLocationAccuracy(Location location) {
         float radius = location.getAccuracy();
         return (location.hasAccuracy() && radius > 0) ? radius : 1001;
     }
@@ -586,17 +586,17 @@ public class GeckoAppShell
                  getLocationAccuracy(location) < getLocationAccuracy(lastKnownLocation))) {
                 lastKnownLocation = location;
             }
         }
 
         return lastKnownLocation;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void enableLocation(final boolean enable) {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     LocationManager lm = getLocationManager(getContext());
                     if (lm == null) {
                         return;
                     }
@@ -642,22 +642,22 @@ public class GeckoAppShell
             // which allows enabling/disabling location update notifications from the cell radio.
             // CONTROL_LOCATION_UPDATES is not for use by normal applications, but we might be
             // hitting this problem if the Tegras are confused about missing cell radios.
             Log.e(LOGTAG, "LOCATION_SERVICE not found?!", e);
             return null;
         }
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void enableLocationHighAccuracy(final boolean enable) {
         locationHighAccuracyEnabled = enable;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void enableSensor(int aSensortype) {
         GeckoInterface gi = getGeckoInterface();
         if (gi == null)
             return;
         SensorManager sm = (SensorManager)
             getContext().getSystemService(Context.SENSOR_SERVICE);
 
         switch(aSensortype) {
@@ -717,17 +717,17 @@ public class GeckoAppShell
                 sm.registerListener(gi.getSensorEventListener(), gGameRotationVectorSensor, SensorManager.SENSOR_DELAY_GAME);
             break;
 
         default:
             Log.w(LOGTAG, "Error! Can't enable unknown SENSOR type " + aSensortype);
         }
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void disableSensor(int aSensortype) {
         GeckoInterface gi = getGeckoInterface();
         if (gi == null)
             return;
 
         SensorManager sm = (SensorManager)
             getContext().getSystemService(Context.SENSOR_SERVICE);
 
@@ -771,60 +771,60 @@ public class GeckoAppShell
             if (gGyroscopeSensor != null)
                 sm.unregisterListener(gi.getSensorEventListener(), gGyroscopeSensor);
             break;
         default:
             Log.w(LOGTAG, "Error! Can't disable unknown SENSOR type " + aSensortype);
         }
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void startMonitoringGamepad() {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     AndroidGamepadManager.startup();
                 }
             });
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void stopMonitoringGamepad() {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     AndroidGamepadManager.shutdown();
                 }
             });
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void gamepadAdded(final int device_id, final int service_id) {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     AndroidGamepadManager.gamepadAdded(device_id, service_id);
                 }
             });
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void moveTaskToBack() {
         if (getGeckoInterface() != null)
             getGeckoInterface().getActivity().moveTaskToBack(true);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     static void scheduleRestart() {
         getGeckoInterface().doRestart();
     }
 
     // Creates a homescreen shortcut for a web page.
     // This is the entry point from nsIShellService.
-    @WrapElementForJNI
+    @WrapForJNI
     static void createShortcut(final String aTitle, final String aURI, final String aIconData) {
         // We have the favicon data (base64) decoded on the background thread, callback here, then
         // call the other createShortcut method with the decoded favicon.
         // This is slightly contrived, but makes the images available to the favicon cache.
         Favicons.getSizedFavicon(getContext(), aURI, aIconData, Integer.MAX_VALUE, 0,
             new OnFaviconLoadedListener() {
                 @Override
                 public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
@@ -938,41 +938,41 @@ public class GeckoAppShell
                                    halfSize - sHeight,
                                    halfSize + sWidth,
                                    halfSize + sHeight),
                           null);
 
         return bitmap;
     }
 
-    @WrapElementForJNI(stubName = "GetHandlersForMimeTypeWrapper")
+    @WrapForJNI(stubName = "GetHandlersForMimeTypeWrapper")
     static String[] getHandlersForMimeType(String aMimeType, String aAction) {
         Intent intent = getIntentForActionString(aAction);
         if (aMimeType != null && aMimeType.length() > 0)
             intent.setType(aMimeType);
         return getHandlersForIntent(intent);
     }
 
-    @WrapElementForJNI(stubName = "GetHandlersForURLWrapper")
+    @WrapForJNI(stubName = "GetHandlersForURLWrapper")
     static String[] getHandlersForURL(String aURL, String aAction) {
         // aURL may contain the whole URL or just the protocol
         Uri uri = aURL.indexOf(':') >= 0 ? Uri.parse(aURL) : new Uri.Builder().scheme(aURL).build();
 
         Intent intent = getOpenURIIntent(getContext(), uri.toString(), "",
             TextUtils.isEmpty(aAction) ? Intent.ACTION_VIEW : aAction, "");
 
         return getHandlersForIntent(intent);
     }
 
-    @WrapElementForJNI(stubName = "GetHWEncoderCapability")
+    @WrapForJNI(stubName = "GetHWEncoderCapability")
     static boolean getHWEncoderCapability() {
       return HardwareCodecCapabilityUtils.getHWEncoderCapability();
     }
 
-    @WrapElementForJNI(stubName = "GetHWDecoderCapability")
+    @WrapForJNI(stubName = "GetHWDecoderCapability")
     static boolean getHWDecoderCapability() {
       return HardwareCodecCapabilityUtils.getHWDecoderCapability();
     }
 
     static List<ResolveInfo> queryIntentActivities(Intent intent) {
         final PackageManager pm = getContext().getPackageManager();
 
         // Exclude any non-exported activities: we can't open them even if we want to!
@@ -1023,22 +1023,22 @@ public class GeckoAppShell
     static Intent getIntentForActionString(String aAction) {
         // Default to the view action if no other action as been specified.
         if (TextUtils.isEmpty(aAction)) {
             return new Intent(Intent.ACTION_VIEW);
         }
         return new Intent(aAction);
     }
 
-    @WrapElementForJNI(stubName = "GetExtensionFromMimeTypeWrapper")
+    @WrapForJNI(stubName = "GetExtensionFromMimeTypeWrapper")
     static String getExtensionFromMimeType(String aMimeType) {
         return MimeTypeMap.getSingleton().getExtensionFromMimeType(aMimeType);
     }
 
-    @WrapElementForJNI(stubName = "GetMimeTypeFromExtensionsWrapper")
+    @WrapForJNI(stubName = "GetMimeTypeFromExtensionsWrapper")
     static String getMimeTypeFromExtensions(String aFileExt) {
         StringTokenizer st = new StringTokenizer(aFileExt, ".,; ");
         String type = null;
         String subType = null;
         while (st.hasMoreElements()) {
             String ext = st.nextToken();
             String mt = getMimeTypeFromExtension(ext);
             if (mt == null)
@@ -1089,17 +1089,17 @@ public class GeckoAppShell
      * @param mimeType an optional MIME type string.
      * @param packageName an optional app package name.
      * @param className an optional intent class name.
      * @param action an Android action specifier, such as
      *               <code>Intent.ACTION_SEND</code>.
      * @param title the title to use in <code>ACTION_SEND</code> intents.
      * @return true if the activity started successfully; false otherwise.
      */
-    @WrapElementForJNI
+    @WrapForJNI
     public static boolean openUriExternal(String targetURI,
                                           String mimeType,
                                           String packageName,
                                           String className,
                                           String action,
                                           String title) {
         final Context context = getContext();
         final Intent intent = getOpenURIIntent(context, targetURI,
@@ -1377,17 +1377,17 @@ public class GeckoAppShell
     public static void setNotificationClient(NotificationClient client) {
         if (notificationClient == null) {
             notificationClient = client;
         } else {
             Log.d(LOGTAG, "Notification client already set");
         }
     }
 
-    @WrapElementForJNI(stubName = "ShowAlertNotificationWrapper")
+    @WrapForJNI(stubName = "ShowAlertNotificationWrapper")
     public static void showAlertNotification(String aImageUrl, String aAlertTitle, String aAlertText,
                                              String aAlertCookie, String aAlertName) {
         // The intent to launch when the user clicks the expanded notification
         String app = getContext().getClass().getName();
         Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CALLBACK);
         notificationIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, app);
         notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
@@ -1404,23 +1404,23 @@ public class GeckoAppShell
                 getContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
         ALERT_COOKIES.put(aAlertName, aAlertCookie);
         callObserver(aAlertName, "alertshow", aAlertCookie);
 
         notificationClient.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void alertsProgressListener_OnProgress(String aAlertName, long aProgress, long aProgressMax, String aAlertText) {
         int notificationID = aAlertName.hashCode();
         notificationClient.update(notificationID, aProgress, aProgressMax, aAlertText);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void closeNotification(String aAlertName) {
         String alertCookie = ALERT_COOKIES.get(aAlertName);
         if (alertCookie != null) {
             callObserver(aAlertName, "alertfinished", alertCookie);
             ALERT_COOKIES.remove(aAlertName);
         }
 
         removeObserver(aAlertName);
@@ -1438,39 +1438,39 @@ public class GeckoAppShell
             if (notificationClient.isOngoing(notificationID)) {
                 // When clicked, keep the notification if it displays progress
                 return;
             }
         }
         closeNotification(aAlertName);
     }
 
-    @WrapElementForJNI(stubName = "GetDpiWrapper")
+    @WrapForJNI(stubName = "GetDpiWrapper")
     public static int getDpi() {
         if (sDensityDpi == 0) {
             sDensityDpi = getContext().getResources().getDisplayMetrics().densityDpi;
         }
 
         return sDensityDpi;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static float getDensity() {
         return getContext().getResources().getDisplayMetrics().density;
     }
 
     private static boolean isHighMemoryDevice() {
         return HardwareUtils.getMemSize() > HIGH_MEMORY_DEVICE_THRESHOLD_MB;
     }
 
     /**
      * Returns the colour depth of the default screen. This will either be
      * 24 or 16.
      */
-    @WrapElementForJNI(stubName = "GetScreenDepthWrapper")
+    @WrapForJNI(stubName = "GetScreenDepthWrapper")
     public static synchronized int getScreenDepth() {
         if (sScreenDepth == 0) {
             sScreenDepth = 16;
             PixelFormat info = new PixelFormat();
             PixelFormat.getPixelFormatInfo(getGeckoInterface().getActivity().getWindowManager().getDefaultDisplay().getPixelFormat(), info);
             if (info.bitsPerPixel >= 24 && isHighMemoryDevice()) {
                 sScreenDepth = 24;
             }
@@ -1483,23 +1483,23 @@ public class GeckoAppShell
         if (sScreenDepth != 0) {
             Log.e(LOGTAG, "Tried to override screen depth after it's already been set");
             return;
         }
 
         sScreenDepth = aScreenDepth;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void setFullScreen(boolean fullscreen) {
         if (getGeckoInterface() != null)
             getGeckoInterface().setFullScreen(fullscreen);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void performHapticFeedback(boolean aIsLongPress) {
         // Don't perform haptic feedback if a vibration is currently playing,
         // because the haptic feedback will nuke the vibration.
         if (!sVibrationMaybePlaying || System.nanoTime() >= sVibrationEndTime) {
             LayerView layerView = getLayerView();
             layerView.performHapticFeedback(aIsLongPress ?
                                             HapticFeedbackConstants.LONG_PRESS :
                                             HapticFeedbackConstants.VIRTUAL_KEY);
@@ -1522,104 +1522,104 @@ public class GeckoAppShell
 
     // Vibrate only if haptic feedback is enabled.
     public static void vibrateOnHapticFeedbackEnabled(int[] milliseconds) {
         if (Settings.System.getInt(getContext().getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) > 0) {
             vibrate(convertIntToLongArray(milliseconds), -1);
         }
     }
 
-    @WrapElementForJNI(stubName = "Vibrate1")
+    @WrapForJNI(stubName = "Vibrate1")
     public static void vibrate(long milliseconds) {
         sVibrationEndTime = System.nanoTime() + milliseconds * 1000000;
         sVibrationMaybePlaying = true;
         vibrator().vibrate(milliseconds);
     }
 
-    @WrapElementForJNI(stubName = "VibrateA")
+    @WrapForJNI(stubName = "VibrateA")
     public static void vibrate(long[] pattern, int repeat) {
         // If pattern.length is even, the last element in the pattern is a
         // meaningless delay, so don't include it in vibrationDuration.
         long vibrationDuration = 0;
         int iterLen = pattern.length - (pattern.length % 2 == 0 ? 1 : 0);
         for (int i = 0; i < iterLen; i++) {
           vibrationDuration += pattern[i];
         }
 
         sVibrationEndTime = System.nanoTime() + vibrationDuration * 1000000;
         sVibrationMaybePlaying = true;
         vibrator().vibrate(pattern, repeat);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void cancelVibrate() {
         sVibrationMaybePlaying = false;
         sVibrationEndTime = 0;
         vibrator().cancel();
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void showInputMethodPicker() {
         InputMethodManager imm = (InputMethodManager)
             getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
         imm.showInputMethodPicker();
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void setKeepScreenOn(final boolean on) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 // TODO
             }
         });
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void notifyDefaultPrevented(final boolean defaultPrevented) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 LayerView view = getLayerView();
                 PanZoomController controller = (view == null ? null : view.getPanZoomController());
                 if (controller != null) {
                     controller.notifyDefaultActionPrevented(defaultPrevented);
                 }
             }
         });
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static boolean isNetworkLinkUp() {
         ConnectivityManager cm = (ConnectivityManager)
            getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
         try {
             NetworkInfo info = cm.getActiveNetworkInfo();
             if (info == null || !info.isConnected())
                 return false;
         } catch (SecurityException se) {
             return false;
         }
         return true;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static boolean isNetworkLinkKnown() {
         ConnectivityManager cm = (ConnectivityManager)
             getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
         try {
             if (cm.getActiveNetworkInfo() == null)
                 return false;
         } catch (SecurityException se) {
             return false;
         }
         return true;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static int networkLinkType() {
         ConnectivityManager cm = (ConnectivityManager)
             getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
         NetworkInfo info = cm.getActiveNetworkInfo();
         if (info == null) {
             return LINK_TYPE_UNKNOWN;
         }
 
@@ -1668,17 +1668,17 @@ public class GeckoAppShell
                 return LINK_TYPE_4G; // 3.9G
             case TelephonyManager.NETWORK_TYPE_UNKNOWN:
             default:
                 Log.w(LOGTAG, "Connected to an unknown mobile network!");
                 return LINK_TYPE_UNKNOWN;
         }
     }
 
-    @WrapElementForJNI(stubName = "GetSystemColoursWrapper")
+    @WrapForJNI(stubName = "GetSystemColoursWrapper")
     public static int[] getSystemColors() {
         // attrsAppearance[] must correspond to AndroidSystemColors structure in android/AndroidBridge.h
         final int[] attrsAppearance = {
             android.R.attr.textColor,
             android.R.attr.textColorPrimary,
             android.R.attr.textColorPrimaryInverse,
             android.R.attr.textColorSecondary,
             android.R.attr.textColorSecondaryInverse,
@@ -1705,17 +1705,17 @@ public class GeckoAppShell
                 result[idx] = color;
             }
             appearance.recycle();
         }
 
         return result;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void killAnyZombies() {
         GeckoProcessesVisitor visitor = new GeckoProcessesVisitor() {
             @Override
             public boolean callback(int pid) {
                 if (pid != android.os.Process.myPid())
                     android.os.Process.killProcess(pid);
                 return true;
             }
@@ -1835,17 +1835,17 @@ public class GeckoAppShell
                 String file = split[nameColumn];
                 if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(file) && file.startsWith(filter))
                     Log.d(LOGTAG, "[OPENFILE] " + name + "(" + split[pidColumn] + ") : " + file);
             }
             in.close();
         } catch (Exception e) { }
     }
 
-    @WrapElementForJNI(stubName = "GetIconForExtensionWrapper")
+    @WrapForJNI(stubName = "GetIconForExtensionWrapper")
     public static byte[] getIconForExtension(String aExt, int iconSize) {
         try {
             if (iconSize <= 0)
                 iconSize = 16;
 
             if (aExt != null && aExt.length() > 1 && aExt.charAt(0) == '.')
                 aExt = aExt.substring(1);
 
@@ -1893,39 +1893,39 @@ public class GeckoAppShell
         if (resolveInfo == null)
             return null;
 
         ActivityInfo activityInfo = resolveInfo.activityInfo;
 
         return activityInfo.loadIcon(pm);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static boolean getShowPasswordSetting() {
         try {
             int showPassword =
                 Settings.System.getInt(getContext().getContentResolver(),
                                        Settings.System.TEXT_SHOW_PASSWORD, 1);
             return (showPassword > 0);
         }
         catch (Exception e) {
             return true;
         }
     }
 
-    @WrapElementForJNI(stubName = "AddPluginViewWrapper")
+    @WrapForJNI(stubName = "AddPluginViewWrapper")
     public static void addPluginView(View view,
                                      float x, float y,
                                      float w, float h,
                                      boolean isFullScreen) {
         if (getGeckoInterface() != null)
              getGeckoInterface().addPluginView(view, new RectF(x, y, x + w, y + h), isFullScreen);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void removePluginView(View view, boolean isFullScreen) {
         if (getGeckoInterface() != null)
             getGeckoInterface().removePluginView(view, isFullScreen);
     }
 
     /**
      * A plugin that wish to be loaded in the WebView must provide this permission
      * in their AndroidManifest.xml.
@@ -2121,17 +2121,17 @@ public class GeckoAppShell
             throws NameNotFoundException, ClassNotFoundException {
         Context pluginContext = getContext().createPackageContext(packageName,
                 Context.CONTEXT_INCLUDE_CODE |
                 Context.CONTEXT_IGNORE_SECURITY);
         ClassLoader pluginCL = pluginContext.getClassLoader();
         return pluginCL.loadClass(className);
     }
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public static Class<?> loadPluginClass(String className, String libName) {
         if (getGeckoInterface() == null)
             return null;
         try {
             final String packageName = getPluginPackage(libName);
             final int contextFlags = Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY;
             final Context pluginContext = getContext().createPackageContext(packageName, contextFlags);
             return pluginContext.getClassLoader().loadClass(className);
@@ -2141,17 +2141,17 @@ public class GeckoAppShell
         } catch (android.content.pm.PackageManager.NameNotFoundException nnfe) {
             Log.w(LOGTAG, "Couldn't find package.", nnfe);
             return null;
         }
     }
 
     private static ContextGetter sContextGetter;
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public static Context getContext() {
         return sContextGetter.getContext();
     }
 
     public static void setContextGetter(ContextGetter cg) {
         sContextGetter = cg;
     }
 
@@ -2205,17 +2205,17 @@ public class GeckoAppShell
     public static android.hardware.Camera sCamera;
 
     static native void cameraCallbackBridge(byte[] data);
 
     static final int kPreferredFPS = 25;
     static byte[] sCameraBuffer;
 
 
-    @WrapElementForJNI(stubName = "InitCameraWrapper")
+    @WrapForJNI(stubName = "InitCameraWrapper")
     static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     try {
                         if (getGeckoInterface() != null)
                             getGeckoInterface().enableCameraView();
                     } catch (Exception e) {}
@@ -2299,17 +2299,17 @@ public class GeckoAppShell
             result[3] = params.getPreviewFrameRate();
         } catch(RuntimeException e) {
             Log.w(LOGTAG, "initCamera RuntimeException.", e);
             result[0] = result[1] = result[2] = result[3] = 0;
         }
         return result;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     static synchronized void closeCamera() {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     try {
                         if (getGeckoInterface() != null)
                             getGeckoInterface().disableCameraView();
                     } catch (Exception e) {}
@@ -2321,194 +2321,194 @@ public class GeckoAppShell
             sCamera = null;
             sCameraBuffer = null;
         }
     }
 
     /*
      * Battery API related methods.
      */
-    @WrapElementForJNI
+    @WrapForJNI
     public static void enableBatteryNotifications() {
         GeckoBatteryManager.enableNotifications();
     }
 
-    @WrapElementForJNI(stubName = "HandleGeckoMessageWrapper")
+    @WrapForJNI(stubName = "HandleGeckoMessageWrapper")
     public static void handleGeckoMessage(final NativeJSContainer message) {
         EventDispatcher.getInstance().dispatchEvent(message);
         message.disposeNative();
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void disableBatteryNotifications() {
         GeckoBatteryManager.disableNotifications();
     }
 
-    @WrapElementForJNI(stubName = "GetCurrentBatteryInformationWrapper")
+    @WrapForJNI(stubName = "GetCurrentBatteryInformationWrapper")
     public static double[] getCurrentBatteryInformation() {
         return GeckoBatteryManager.getCurrentInformation();
     }
 
-    @WrapElementForJNI(stubName = "CheckURIVisited")
+    @WrapForJNI(stubName = "CheckURIVisited")
     static void checkUriVisited(String uri) {
         GlobalHistory.getInstance().checkUriVisited(uri);
     }
 
-    @WrapElementForJNI(stubName = "MarkURIVisited")
+    @WrapForJNI(stubName = "MarkURIVisited")
     static void markUriVisited(final String uri) {
         final Context context = getContext();
         final BrowserDB db = GeckoProfile.get(context).getDB();
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 GlobalHistory.getInstance().add(context, db, uri);
             }
         });
     }
 
-    @WrapElementForJNI(stubName = "SetURITitle")
+    @WrapForJNI(stubName = "SetURITitle")
     static void setUriTitle(final String uri, final String title) {
         final Context context = getContext();
         final BrowserDB db = GeckoProfile.get(context).getDB();
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 GlobalHistory.getInstance().update(context.getContentResolver(), db, uri, title);
             }
         });
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     static void hideProgressDialog() {
         // unused stub
     }
 
     /*
      * WebSMS related methods.
      */
-    @WrapElementForJNI(stubName = "SendMessageWrapper")
+    @WrapForJNI(stubName = "SendMessageWrapper")
     public static void sendMessage(String aNumber, String aMessage, int aRequestId) {
         if (!SmsManager.isEnabled()) {
             return;
         }
 
         SmsManager.getInstance().send(aNumber, aMessage, aRequestId);
     }
 
-    @WrapElementForJNI(stubName = "GetMessageWrapper")
+    @WrapForJNI(stubName = "GetMessageWrapper")
     public static void getMessage(int aMessageId, int aRequestId) {
         if (!SmsManager.isEnabled()) {
             return;
         }
 
         SmsManager.getInstance().getMessage(aMessageId, aRequestId);
     }
 
-    @WrapElementForJNI(stubName = "DeleteMessageWrapper")
+    @WrapForJNI(stubName = "DeleteMessageWrapper")
     public static void deleteMessage(int aMessageId, int aRequestId) {
         if (!SmsManager.isEnabled()) {
             return;
         }
 
         SmsManager.getInstance().deleteMessage(aMessageId, aRequestId);
     }
 
-    @WrapElementForJNI(stubName = "CreateMessageListWrapper")
+    @WrapForJNI(stubName = "CreateMessageListWrapper")
     public static void createMessageList(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, String aDelivery, boolean aHasRead, boolean aRead, long aThreadId, boolean aReverse, int aRequestId) {
         if (!SmsManager.isEnabled()) {
             return;
         }
 
         SmsManager.getInstance().createMessageList(aStartDate, aEndDate, aNumbers, aNumbersCount, aDelivery, aHasRead, aRead, aThreadId, aReverse, aRequestId);
     }
 
-    @WrapElementForJNI(stubName = "GetNextMessageInListWrapper")
+    @WrapForJNI(stubName = "GetNextMessageInListWrapper")
     public static void getNextMessageInList(int aListId, int aRequestId) {
         if (!SmsManager.isEnabled()) {
             return;
         }
 
         SmsManager.getInstance().getNextMessageInList(aListId, aRequestId);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void clearMessageList(int aListId) {
         if (!SmsManager.isEnabled()) {
             return;
         }
 
         SmsManager.getInstance().clearMessageList(aListId);
     }
 
     /* Called by JNI from AndroidBridge, and by reflection from tests/BaseTest.java.in */
-    @WrapElementForJNI
+    @WrapForJNI
     @RobocopTarget
     public static boolean isTablet() {
         return HardwareUtils.isTablet();
     }
 
     public static void viewSizeChanged() {
         LayerView v = getLayerView();
         if (v != null && v.isIMEEnabled()) {
             sendEventToGecko(GeckoEvent.createBroadcastEvent(
                     "ScrollTo:FocusedInput", ""));
         }
     }
 
-    @WrapElementForJNI(stubName = "GetCurrentNetworkInformationWrapper")
+    @WrapForJNI(stubName = "GetCurrentNetworkInformationWrapper")
     public static double[] getCurrentNetworkInformation() {
         return GeckoNetworkManager.getInstance().getCurrentInformation();
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void enableNetworkNotifications() {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 GeckoNetworkManager.getInstance().enableNotifications();
             }
         });
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void disableNetworkNotifications() {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 GeckoNetworkManager.getInstance().disableNotifications();
             }
         });
     }
 
-    @WrapElementForJNI(stubName = "GetScreenOrientationWrapper")
+    @WrapForJNI(stubName = "GetScreenOrientationWrapper")
     public static short getScreenOrientation() {
         return GeckoScreenOrientation.getInstance().getScreenOrientation().value;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void enableScreenOrientationNotifications() {
         GeckoScreenOrientation.getInstance().enableNotifications();
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void disableScreenOrientationNotifications() {
         GeckoScreenOrientation.getInstance().disableNotifications();
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void lockScreenOrientation(int aOrientation) {
         GeckoScreenOrientation.getInstance().lock(aOrientation);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void unlockScreenOrientation() {
         GeckoScreenOrientation.getInstance().unlock();
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static boolean pumpMessageLoop(final Message msg) {
         final Handler geckoHandler = ThreadUtils.sGeckoHandler;
 
         if (msg.obj == geckoHandler && msg.getTarget() == geckoHandler) {
             // Our "queue is empty" message; see runGecko()
             return false;
         }
 
@@ -2516,52 +2516,52 @@ public class GeckoAppShell
             Looper.myLooper().quit();
         } else {
             msg.getTarget().dispatchMessage(msg);
         }
 
         return true;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static void notifyWakeLockChanged(String topic, String state) {
         if (getGeckoInterface() != null)
             getGeckoInterface().notifyWakeLockChanged(topic, state);
     }
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public static void registerSurfaceTextureFrameListener(Object surfaceTexture, final int id) {
         ((SurfaceTexture)surfaceTexture).setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
             @Override
             public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                 GeckoAppShell.onSurfaceTextureFrameAvailable(surfaceTexture, id);
             }
         });
     }
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public static void unregisterSurfaceTextureFrameListener(Object surfaceTexture) {
         ((SurfaceTexture)surfaceTexture).setOnFrameAvailableListener(null);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static boolean unlockProfile() {
         // Try to kill any zombie Fennec's that might be running
         GeckoAppShell.killAnyZombies();
 
         // Then force unlock this profile
         if (getGeckoInterface() != null) {
             GeckoProfile profile = getGeckoInterface().getProfile();
             File lock = profile.getFile(".parentlock");
             return lock.exists() && lock.delete();
         }
         return false;
     }
 
-    @WrapElementForJNI(stubName = "GetProxyForURIWrapper")
+    @WrapForJNI(stubName = "GetProxyForURIWrapper")
     public static String getProxyForURI(String spec, String scheme, String host, int port) {
         final ProxySelector ps = new ProxySelector();
 
         Proxy proxy = ps.select(scheme, host);
         if (Proxy.NO_PROXY.equals(proxy)) {
             return "DIRECT";
         }
         
@@ -2648,22 +2648,22 @@ public class GeckoAppShell
     // Don't fail silently, tell the user that we weren't able to share the image
     private static final void showImageShareFailureToast() {
         Toast toast = Toast.makeText(getContext(),
                                      getContext().getResources().getString(R.string.share_image_failed),
                                      Toast.LENGTH_SHORT);
         toast.show();
     }
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     static InputStream createInputStream(URLConnection connection) throws IOException {
         return connection.getInputStream();
     }
 
-    @WrapElementForJNI(allowMultithread = true, narrowChars = true)
+    @WrapForJNI(allowMultithread = true, narrowChars = true)
     static URLConnection getConnection(String url) {
         try {
             String spec;
             if (url.startsWith("android://")) {
                 spec = url.substring(10);
             } else {
                 spec = url.substring(8);
             }
@@ -2674,28 +2674,28 @@ public class GeckoAppShell
                 spec = spec.replaceFirst("/", ":/");
             }
         } catch(Exception ex) {
             return null;
         }
         return null;
     }
 
-    @WrapElementForJNI(allowMultithread = true, narrowChars = true)
+    @WrapForJNI(allowMultithread = true, narrowChars = true)
     static String connectionGetMimeType(URLConnection connection) {
         return connection.getContentType();
     }
 
     /**
      * Retrieve the absolute path of an external storage directory.
      *
      * @param type The type of directory to return
      * @return Absolute path of the specified directory or null on failure
      */
-    @WrapElementForJNI
+    @WrapForJNI
     static String getExternalPublicDirectory(final String type) {
         final String state = Environment.getExternalStorageState();
         if (!Environment.MEDIA_MOUNTED.equals(state) &&
             !Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
             // External storage is not available.
             return null;
         }
 
@@ -2714,17 +2714,17 @@ public class GeckoAppShell
         } else if ("music".equals(type)) {
             systemType = Environment.DIRECTORY_MUSIC;
         } else {
             return null;
         }
         return Environment.getExternalStoragePublicDirectory(systemType).getAbsolutePath();
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     static int getMaxTouchPoints() {
         PackageManager pm = getContext().getPackageManager();
         if (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND)) {
             // at least, 5+ fingers.
             return 5;
         } else if (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)) {
             // at least, 2+ fingers.
             return 2;
--- a/mobile/android/base/GeckoJavaSampler.java
+++ b/mobile/android/base/GeckoJavaSampler.java
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.SparseArray;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.annotation.WrapForJNI;
 
 import java.lang.Thread;
 import java.util.Set;
 
 public class GeckoJavaSampler {
     private static final String LOGTAG = "JavaSampler";
     private static Thread sSamplingThread;
     private static SamplingThread sSamplingRunnable;
@@ -121,82 +121,82 @@ public class GeckoJavaSampler {
                 int readPos = (startPos + aSampleId) % mSamples.get(aThreadId).length;
                 return mSamples.get(aThreadId)[readPos];
             }
             return null;
         }
     }
 
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "GetThreadNameJavaProfilingWrapper")
+    @WrapForJNI(allowMultithread = true, stubName = "GetThreadNameJavaProfilingWrapper")
     public synchronized static String getThreadName(int aThreadId) {
         if (aThreadId == 0 && sMainThread != null) {
             return sMainThread.getName();
         }
         return null;
     }
 
     private synchronized static Sample getSample(int aThreadId, int aSampleId) {
         return sSamplingRunnable.getSample(aThreadId, aSampleId);
     }
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "GetSampleTimeJavaProfiling")
+    @WrapForJNI(allowMultithread = true, stubName = "GetSampleTimeJavaProfiling")
     public synchronized static double getSampleTime(int aThreadId, int aSampleId) {
         Sample sample = getSample(aThreadId, aSampleId);
         if (sample != null) {
             if (sample.mJavaTime != 0) {
                 return (sample.mJavaTime -
                     SystemClock.elapsedRealtime()) + getProfilerTime();
             }
             System.out.println("Sample: " + sample.mTime);
             return sample.mTime;
         }
         return 0;
     }
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "GetFrameNameJavaProfilingWrapper")
+    @WrapForJNI(allowMultithread = true, stubName = "GetFrameNameJavaProfilingWrapper")
     public synchronized static String getFrameName(int aThreadId, int aSampleId, int aFrameId) {
         Sample sample = getSample(aThreadId, aSampleId);
         if (sample != null && aFrameId < sample.mFrames.length) {
             Frame frame = sample.mFrames[aFrameId];
             if (frame == null) {
                 return null;
             }
             return frame.className + "." + frame.methodName + "()";
         }
         return null;
     }
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "StartJavaProfiling")
+    @WrapForJNI(allowMultithread = true, stubName = "StartJavaProfiling")
     public static void start(int aInterval, int aSamples) {
         synchronized (GeckoJavaSampler.class) {
             if (sSamplingRunnable != null) {
                 return;
             }
             sSamplingRunnable = new SamplingThread(aInterval, aSamples);
             sSamplingThread = new Thread(sSamplingRunnable, "Java Sampler");
             sSamplingThread.start();
         }
     }
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "PauseJavaProfiling")
+    @WrapForJNI(allowMultithread = true, stubName = "PauseJavaProfiling")
     public static void pause() {
         synchronized (GeckoJavaSampler.class) {
             sSamplingRunnable.mPauseSampler = true;
         }
     }
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "UnpauseJavaProfiling")
+    @WrapForJNI(allowMultithread = true, stubName = "UnpauseJavaProfiling")
     public static void unpause() {
         synchronized (GeckoJavaSampler.class) {
             sSamplingRunnable.mPauseSampler = false;
         }
     }
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "StopJavaProfiling")
+    @WrapForJNI(allowMultithread = true, stubName = "StopJavaProfiling")
     public static void stop() {
         synchronized (GeckoJavaSampler.class) {
             if (sSamplingThread == null) {
                 return;
             }
 
             sSamplingRunnable.mStopSampler = true;
             try {
--- a/mobile/android/base/RestrictedProfiles.java
+++ b/mobile/android/base/RestrictedProfiles.java
@@ -1,18 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.mozglue.RobocopTarget;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 import org.mozilla.gecko.restrictions.DefaultConfiguration;
 import org.mozilla.gecko.restrictions.GuestProfileConfiguration;
 import org.mozilla.gecko.restrictions.RestrictedProfileConfiguration;
 import org.mozilla.gecko.restrictions.Restriction;
 import org.mozilla.gecko.restrictions.RestrictionConfiguration;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -85,30 +85,30 @@ public class RestrictedProfiles {
 
         throw new IllegalArgumentException("Unknown action " + action);
     }
 
     private static boolean canLoadUrl(final Context context, final String url) {
         return getConfiguration(context).canLoadUrl(url);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static boolean isUserRestricted() {
         return isUserRestricted(GeckoAppShell.getContext());
     }
 
     public static boolean isUserRestricted(final Context context) {
         return getConfiguration(context).isRestricted();
     }
 
     public static boolean isAllowed(final Context context, final Restriction restriction) {
         return getConfiguration(context).isAllowed(restriction);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public static boolean isAllowed(int action, String url) {
         final Restriction restriction;
         try {
             restriction = geckoActionToRestriction(action);
         } catch (IllegalArgumentException ex) {
             // Unknown actions represent a coding error, so we
             // refuse the action and log.
             Log.e(LOGTAG, "Unknown action " + action + "; check calling code.");
--- a/mobile/android/base/SurfaceBits.java
+++ b/mobile/android/base/SurfaceBits.java
@@ -1,17 +1,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/. */
 
 package org.mozilla.gecko;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.annotation.WrapForJNI;
 
 import java.nio.ByteBuffer;
 
-@WrapElementForJNI
+@WrapForJNI
 public class SurfaceBits {
     public int width;
     public int height;
     public int format;
     public ByteBuffer buffer;
 }
--- a/mobile/android/base/ThumbnailHelper.java
+++ b/mobile/android/base/ThumbnailHelper.java
@@ -1,18 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.mozglue.DirectBufferAllocator;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.util.Log;
 import android.util.TypedValue;
 
 import java.nio.ByteBuffer;
 import java.util.LinkedList;
@@ -148,17 +148,17 @@ public final class ThumbnailHelper {
         }
 
         Log.d(LOGTAG, "Sending thumbnail event: " + mWidth + ", " + mHeight);
         GeckoEvent e = GeckoEvent.createThumbnailEvent(tab.getId(), mWidth, mHeight, mBuffer);
         GeckoAppShell.sendEventToGecko(e);
     }
 
     /* This method is invoked by JNI once the thumbnail data is ready. */
-    @WrapElementForJNI(stubName = "SendThumbnail")
+    @WrapForJNI(stubName = "SendThumbnail")
     public static void notifyThumbnail(ByteBuffer data, int tabId, boolean success, boolean shouldStore) {
         Tab tab = Tabs.getInstance().getTab(tabId);
         ThumbnailHelper helper = ThumbnailHelper.getInstance();
         if (success && tab != null) {
             helper.handleThumbnailData(tab, data, shouldStore ? CachePolicy.STORE : CachePolicy.NO_STORE);
         }
         helper.processNextThumbnail(tab);
     }
rename from mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java
rename to mobile/android/base/annotation/WrapForJNI.java
--- a/mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java
+++ b/mobile/android/base/annotation/WrapForJNI.java
@@ -1,13 +1,13 @@
 /* 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/. */
 
-package org.mozilla.gecko.mozglue.generatorannotations;
+package org.mozilla.gecko.annotation;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 /**
  * This annotation is used to tag methods that are to have wrapper methods generated in the android
@@ -19,17 +19,17 @@ import java.lang.annotation.Target;
  * generated method stub.
  *
  * allowMultithreaded should be used as sparingly as possible - the resulting code will allow the
  * Java method to be invoked from the C side from multiple threads. Often, this isn't what is wanted
  * and may lead to subtle bugs.
  */
 @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
 @Retention(RetentionPolicy.RUNTIME)
-public @interface WrapElementForJNI {
+public @interface WrapForJNI {
     // Optional parameter specifying the name of the generated method stub. If omitted, the name
     // of the Java method will be used.
     String stubName() default "";
 
     /**
      * If set, the generated method stub will support being called from any thread via the use of
      * GetJNIForThread. This is rarely useful, at time of writing, as well as possibly risky.
      * See information in AndroidBridge.cpp regarding GetJNIForThread.
--- a/mobile/android/base/gfx/DisplayPortMetrics.java
+++ b/mobile/android/base/gfx/DisplayPortMetrics.java
@@ -1,39 +1,39 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.util.FloatUtils;
 
 import android.graphics.RectF;
 
 /*
  * This class keeps track of the area we request Gecko to paint, as well
  * as the resolution of the paint. The area may be different from the visible
  * area of the page, and the resolution may be different from the resolution
  * used in the compositor to render the page. This is so that we can ask Gecko
  * to paint a much larger area without using extra memory, and then render some
  * subsection of that with compositor scaling.
  */
 public final class DisplayPortMetrics {
-    @WrapElementForJNI
+    @WrapForJNI
     public final float resolution;
-    @WrapElementForJNI
+    @WrapForJNI
     private final RectF mPosition;
 
     public DisplayPortMetrics() {
         this(0, 0, 0, 0, 1);
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     public DisplayPortMetrics(float left, float top, float right, float bottom, float resolution) {
         this.resolution = resolution;
         mPosition = new RectF(left, top, right, bottom);
     }
 
     public float getLeft() {
         return mPosition.left;
     }
--- a/mobile/android/base/gfx/GLController.java
+++ b/mobile/android/base/gfx/GLController.java
@@ -1,20 +1,20 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoThread;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.util.Log;
 
 import javax.microedition.khronos.egl.EGL10;
 import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.egl.EGLContext;
 import javax.microedition.khronos.egl.EGLDisplay;
@@ -245,17 +245,17 @@ public class GLController {
             }
         }
         if (mEGLSurfaceForCompositor == null) {
             Log.w(LOGTAG, "eglCreateWindowSurface returned no surface!");
         }
         return mEGLSurfaceForCompositor != null;
     }
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "CreateEGLSurfaceForCompositorWrapper")
+    @WrapForJNI(allowMultithread = true, stubName = "CreateEGLSurfaceForCompositorWrapper")
     private synchronized EGLSurface createEGLSurfaceForCompositor() {
         AttemptPreallocateEGLSurfaceForCompositor();
         EGLSurface result = mEGLSurfaceForCompositor;
         mEGLSurfaceForCompositor = null;
         return result;
     }
 
     private String getEGLError() {
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -1,23 +1,23 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.gfx.LayerView.DrawListener;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.ZoomConstraints;
 import org.mozilla.gecko.mozglue.RobocopTarget;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.util.FloatUtils;
 import org.mozilla.gecko.AppConstants;
 
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.os.SystemClock;
@@ -431,49 +431,49 @@ class GeckoLayerClient implements LayerV
             });
 
             setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE);
             mDisplayPort = DisplayPortCalculator.calculate(getViewportMetrics(), null);
         }
         return mDisplayPort;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     DisplayPortMetrics getDisplayPort(boolean pageSizeUpdate, boolean isBrowserContentDisplayed, int tabId, ImmutableViewportMetrics metrics) {
         Tabs tabs = Tabs.getInstance();
         if (isBrowserContentDisplayed && tabs.isSelectedTabId(tabId)) {
             // for foreground tabs, send the viewport update unless the document
             // displayed is different from the content document. In that case, just
             // calculate the display port.
             return handleViewportMessage(metrics, pageSizeUpdate ? ViewportMessageType.PAGE_SIZE : ViewportMessageType.UPDATE);
         } else {
             // for background tabs, request a new display port calculation, so that
             // when we do switch to that tab, we have the correct display port and
             // don't need to draw twice (once to allow the first-paint viewport to
             // get to java, and again once java figures out the display port).
             return DisplayPortCalculator.calculate(metrics, null);
         }
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     void contentDocumentChanged() {
         mContentDocumentIsDisplayed = false;
     }
 
-    @WrapElementForJNI
+    @WrapForJNI
     boolean isContentDocumentDisplayed() {
         return mContentDocumentIsDisplayed;
     }
 
     // This is called on the Gecko thread to determine if we're still interested
     // in the update of this display-port to continue. We can return true here
     // to abort the current update and continue with any subsequent ones. This
     // is useful for slow-to-render pages when the display-port starts lagging
     // behind enough that continuing to draw it is wasted effort.
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public ProgressiveUpdateData progressiveUpdateCallback(boolean aHasPendingNewThebesContent,
                                                            float x, float y, float width, float height,
                                                            float resolution, boolean lowPrecision) {
         // Reset the checkerboard risk flag when switching to low precision
         // rendering.
         if (lowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
             // Skip low precision rendering until we're at risk of checkerboarding.
             if (!mProgressiveUpdateWasInDanger) {
@@ -577,17 +577,17 @@ class GeckoLayerClient implements LayerV
     }
 
     /** The compositor invokes this function just before compositing a frame where the document
       * is different from the document composited on the last frame. In these cases, the viewport
       * information we have in Java is no longer valid and needs to be replaced with the new
       * viewport information provided. setPageRect will never be invoked on the same frame that
       * this function is invoked on; and this function will always be called prior to syncViewportInfo.
       */
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public void setFirstPaintViewport(float offsetX, float offsetY, float zoom,
             float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
         synchronized (getLock()) {
             ImmutableViewportMetrics currentMetrics = getViewportMetrics();
 
             Tab tab = Tabs.getInstance().getSelectedTab();
 
             RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
@@ -637,17 +637,17 @@ class GeckoLayerClient implements LayerV
         mContentDocumentIsDisplayed = true;
     }
 
     /** The compositor invokes this function whenever it determines that the page rect
       * has changed (based on the information it gets from layout). If setFirstPaintViewport
       * is invoked on a frame, then this function will not be. For any given frame, this
       * function will be invoked before syncViewportInfo.
       */
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public void setPageRect(float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
         synchronized (getLock()) {
             RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
             float ourZoom = getViewportMetrics().zoomFactor;
             setPageRect(RectUtils.scale(cssPageRect, ourZoom), cssPageRect);
             // Here the page size of the document has changed, but the document being displayed
             // is still the same. Therefore, we don't need to send anything to browser.js; any
             // changes we need to make to the display port will get sent the next time we call
@@ -658,17 +658,17 @@ class GeckoLayerClient implements LayerV
     /** The compositor invokes this function on every frame to figure out what part of the
       * page to display, and to inform Java of the current display port. Since it is called
       * on every frame, it needs to be ultra-fast.
       * It avoids taking any locks or allocating any objects. We keep around a
       * mCurrentViewTransform so we don't need to allocate a new ViewTransform
       * every time we're called. NOTE: we might be able to return a ImmutableViewportMetrics
       * which would avoid the copy into mCurrentViewTransform.
       */
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public ViewTransform syncViewportInfo(int x, int y, int width, int height, float resolution, boolean layersUpdated) {
         // getViewportMetrics is thread safe so we don't need to synchronize.
         // We save the viewport metrics here, so we later use it later in
         // createFrame (which will be called by nsWindow::DrawWindowUnderlay on
         // the native side, by the compositor). The viewport
         // metrics can change between here and there, as it's accessed outside
         // of the compositor thread.
         mFrameMetrics = getViewportMetrics();
@@ -715,31 +715,31 @@ class GeckoLayerClient implements LayerV
             for (DrawListener listener : mDrawListeners) {
                 listener.drawFinished();
             }
         }
 
         return mCurrentViewTransform;
     }
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public ViewTransform syncFrameMetrics(float offsetX, float offsetY, float zoom,
                 float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom,
                 boolean layersUpdated, int x, int y, int width, int height, float resolution,
                 boolean isFirstPaint)
     {
         if (isFirstPaint) {
             setFirstPaintViewport(offsetX, offsetY, zoom,
                                   cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
         }
 
         return syncViewportInfo(x, y, width, height, resolution, layersUpdated);
     }
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public LayerRenderer.Frame createFrame() {
         // Create the shaders and textures if necessary.
         if (!mLayerRendererInitialized) {
             if (mLayerRenderer == null) {
                 return null;
             }
             mLayerRenderer.checkMonitoringEnabled();
             mLayerRenderer.createDefaultProgram();
@@ -749,22 +749,22 @@ class GeckoLayerClient implements LayerV
         try {
             return mLayerRenderer.createFrame(mFrameMetrics);
         } catch (Exception e) {
             Log.w(LOGTAG, e);
             return null;
         }
     }
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public void activateProgram() {
         mLayerRenderer.activateDefaultProgram();
     }
 
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public void deactivateProgramAndRestoreState(boolean enableScissor,
             int scissorX, int scissorY, int scissorW, int scissorH)
     {
         mLayerRenderer.deactivateDefaultProgram();
         mLayerRenderer.restoreState(enableScissor, scissorX, scissorY, scissorW, scissorH);
     }
 
     private void geometryChanged(DisplayPortMetrics displayPort) {
--- a/mobile/android/base/gfx/ImmutableViewportMetrics.java
+++ b/mobile/android/base/gfx/ImmutableViewportMetrics.java
@@ -1,16 +1,16 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.util.FloatUtils;
 
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.DisplayMetrics;
 
 /**
  * ImmutableViewportMetrics are used to store the viewport metrics
@@ -48,17 +48,17 @@ public class ImmutableViewportMetrics {
         marginLeft = marginTop = marginRight = marginBottom = 0;
         zoomFactor = 1.0f;
         isRTL = false;
     }
 
     /** This constructor is used by native code in AndroidJavaWrappers.cpp, be
      * careful when modifying the signature.
      */
-    @WrapElementForJNI(allowMultithread = true)
+    @WrapForJNI(allowMultithread = true)
     public ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop,
         float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft,
         float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom,
         float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight,
         float aViewportRectBottom, float aZoomFactor)
     {
         this(aPageRectLeft, aPageRectTop,
              aPageRectRight, aPageRectBottom, aCssPageRectLeft,
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -5,27 +5,27 @@
 
 package org.mozilla.gecko.gfx;
 
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 import java.util.ArrayList;
 
 import org.mozilla.gecko.AndroidGamepadManager;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAccessibility;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.ZoomConstraints;
 import org.mozilla.gecko.mozglue.RobocopTarget;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -510,31 +510,31 @@ public class LayerView extends FrameLayo
 
     public Object getNativeWindow() {
         if (mSurfaceView != null)
             return mSurfaceView.getHolder();
 
         return mTextureView.getSurfaceTexture();
     }
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "RegisterCompositorWrapper")
+    @WrapForJNI(allowMultithread = true, stubName = "RegisterCompositorWrapper")
     public static GLController registerCxxCompositor() {
         try {
             LayerView layerView = GeckoAppShell.getLayerView();
             GLController controller = layerView.getGLController();
             controller.compositorCreated();
             return controller;
         } catch (Exception e) {
             Log.e(LOGTAG, "Error registering compositor!", e);
             return null;
         }
     }
 
     //This method is called on the Gecko main thread.
-    @WrapElementForJNI(allowMultithread = true, stubName = "updateZoomedView")
+    @WrapForJNI(allowMultithread = true, stubName = "updateZoomedView")
     public static void updateZoomedView(ByteBuffer data) {
         LayerView layerView = GeckoAppShell.getLayerView();
         if (layerView != null) {
             LayerRenderer layerRenderer = layerView.getRenderer();
             if (layerRenderer != null) {
                 layerRenderer.updateZoomedView(data);
             }
         }
--- a/mobile/android/base/gfx/NativePanZoomController.java
+++ b/mobile/android/base/gfx/NativePanZoomController.java
@@ -1,18 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoThread;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.util.GeckoEventListener;
 
 import org.json.JSONObject;
 
 import android.graphics.PointF;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -92,17 +92,17 @@ class NativePanZoomController implements
     public native void destroy();
     @Override
     public native boolean getRedrawHint();
     @Override
     public native void setOverScrollMode(int overscrollMode);
     @Override
     public native int getOverScrollMode();
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "RequestContentRepaintWrapper")
+    @WrapForJNI(allowMultithread = true, stubName = "RequestContentRepaintWrapper")
     private void requestContentRepaint(float x, float y, float width, float height, float resolution) {
         mTarget.forceRedraw(new DisplayPortMetrics(x, y, x + width, y + height, resolution));
     }
 
     @Override
     public void setOverscrollHandler(final Overscroll listener) {
     }
 }
--- a/mobile/android/base/gfx/ProgressiveUpdateData.java
+++ b/mobile/android/base/gfx/ProgressiveUpdateData.java
@@ -1,24 +1,24 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.annotation.WrapForJNI;
 
 /**
  * This is the data structure that's returned by the progressive tile update
  * callback function. It encompasses the current viewport and a boolean value
  * representing whether the front-end is interested in the current progressive
  * update continuing.
  */
-@WrapElementForJNI
+@WrapForJNI
 public class ProgressiveUpdateData {
     public float x;
     public float y;
     public float scale;
     public boolean abort;
 
     public void setViewport(ImmutableViewportMetrics viewport) {
         this.x = viewport.viewportRectLeft;
--- a/mobile/android/base/gfx/ViewTransform.java
+++ b/mobile/android/base/gfx/ViewTransform.java
@@ -1,18 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.gfx;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.annotation.WrapForJNI;
 
-@WrapElementForJNI
+@WrapForJNI
 public class ViewTransform {
     public float x;
     public float y;
     public float scale;
     public float fixedLayerMarginLeft;
     public float fixedLayerMarginTop;
     public float fixedLayerMarginRight;
     public float fixedLayerMarginBottom;
--- a/mobile/android/base/mdns/MulticastDNSManager.java
+++ b/mobile/android/base/mdns/MulticastDNSManager.java
@@ -32,17 +32,18 @@ import java.util.Map;
  * @See nsIDNSServiceDiscovery.idl
  */
 public abstract class MulticastDNSManager {
     protected static final String LOGTAG = "GeckoMDNSManager";
     private static MulticastDNSManager instance = null;
 
     public static MulticastDNSManager getInstance(final Context context) {
         if (instance == null) {
-            if (Versions.feature16Plus) {
+            // Bug 1188935: There's a bug on Android 4.4 and before.
+            if (Versions.feature21Plus) {
                 instance = new NsdMulticastDNSManager(context);
             } else {
                 instance = new DummyMulticastDNSManager();
             }
         }
         return instance;
     }
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -9,16 +9,17 @@ SPHINX_TREES['fennec'] = 'docs'
 
 include('android-services.mozbuild')
 
 thirdparty_source_dir = TOPSRCDIR + '/mobile/android/thirdparty/'
 
 constants_jar = add_java_jar('constants')
 constants_jar.sources = [
     'adjust/AdjustHelperInterface.java',
+    'annotation/WrapForJNI.java',
     'SysInfo.java',
 ]
 constants_jar.generated_sources = [
     'preprocessed/org/mozilla/gecko/AdjustConstants.java',
     'preprocessed/org/mozilla/gecko/AppConstants.java',
 ]
 
 if CONFIG['MOZ_INSTALL_TRACKING']:
@@ -49,17 +50,16 @@ resjar.generated_sources += ['android/su
 resjar.javac_flags += ['-Xlint:all']
 
 mgjar = add_java_jar('gecko-mozglue')
 mgjar.sources += [
     'mozglue/ByteBufferInputStream.java',
     'mozglue/ContextUtils.java',
     'mozglue/DirectBufferAllocator.java',
     'mozglue/GeckoLoader.java',
-    'mozglue/generatorannotations/WrapElementForJNI.java',
     'mozglue/JNIObject.java',
     'mozglue/JNITarget.java',
     'mozglue/NativeReference.java',
     'mozglue/NativeZip.java',
     'mozglue/RobocopTarget.java',
     'mozglue/WebRTCJNITarget.java',
 ]
 mgjar.generated_sources = [] # Keep it this way.
@@ -128,16 +128,17 @@ if CONFIG['MOZ_WEBRTC']:
         video_render_root + 'ViERenderer.java',
     ]
     wrjar.sources += [
         audio_root + 'AudioManagerAndroid.java',
         audio_root + 'WebRtcAudioRecord.java',
         audio_root + 'WebRtcAudioTrack.java',
     ]
     wrjar.extra_jars = [
+        'constants.jar',
         'gecko-R.jar',
         'gecko-browser.jar',
         'gecko-util.jar',
         'gecko-mozglue.jar',
     ]
     wrjar.javac_flags += ['-Xlint:all,-deprecation,-cast']
 
 gbjar = add_java_jar('gecko-browser')
--- a/mobile/android/base/mozglue/NativeZip.java
+++ b/mobile/android/base/mozglue/NativeZip.java
@@ -1,16 +1,16 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.mozglue;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.mozglue.JNITarget;
 
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.util.zip.Inflater;
 import java.util.zip.InflaterInputStream;
 
 public class NativeZip implements NativeReference {
     private static final int DEFLATE = 8;
@@ -64,17 +64,17 @@ public class NativeZip implements Native
         return _getInputStream(mObj, path);
     }
 
     private static native long getZip(String path);
     private static native long getZipFromByteBuffer(ByteBuffer buffer);
     private static native void _release(long obj);
     private native InputStream _getInputStream(long obj, String path);
 
-    @WrapElementForJNI
+    @JNITarget
     private InputStream createInputStream(ByteBuffer buffer, int compression) {
         if (compression != STORE && compression != DEFLATE) {
             throw new IllegalArgumentException("Unexpected compression: " + compression);
         }
 
         InputStream input = new ByteBufferInputStream(buffer, this);
         if (compression == DEFLATE) {
             Inflater inflater = new Inflater(true);
--- a/mobile/android/base/sqlite/MatrixBlobCursor.java
+++ b/mobile/android/base/sqlite/MatrixBlobCursor.java
@@ -15,18 +15,18 @@
  * limitations under the License.
  */
 
 package org.mozilla.gecko.sqlite;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 
 import android.database.AbstractCursor;
 import android.database.CursorIndexOutOfBoundsException;
 import android.util.Log;
 
 /**
  * A mutable cursor implementation backed by an array of {@code Object}s. Use
  * {@link #newRow()} to add rows. Automatically expands internal capacity
@@ -51,17 +51,17 @@ public class MatrixBlobCursor extends Ab
 
     /**
      * Constructs a new cursor with the given initial capacity.
      *
      * @param columnNames names of the columns, the ordering of which
      *  determines column ordering elsewhere in this cursor
      * @param initialCapacity in rows
      */
-    @WrapElementForJNI
+    @WrapForJNI
     public MatrixBlobCursor(String[] columnNames, int initialCapacity) {
         this.columnNames = columnNames;
         this.columnCount = columnNames.length;
 
         if (initialCapacity < 1) {
             initialCapacity = 1;
         }
 
@@ -72,17 +72,17 @@ public class MatrixBlobCursor extends Ab
     }
 
     /**
      * Constructs a new cursor.
      *
      * @param columnNames names of the columns, the ordering of which
      *  determines column ordering elsewhere in this cursor
      */
-    @WrapElementForJNI
+    @WrapForJNI
     public MatrixBlobCursor(String[] columnNames) {
         this(columnNames, 16);
     }
 
     /**
      * Closes the Cursor, releasing all of its resources.
      */
     public void close() {
@@ -127,17 +127,17 @@ public class MatrixBlobCursor extends Ab
      * Adds a new row to the end with the given column values. Not safe
      * for concurrent use.
      *
      * @throws IllegalArgumentException if {@code columnValues.length !=
      *  columnNames.length}
      * @param columnValues in the same order as the the column names specified
      *  at cursor construction time
      */
-    @WrapElementForJNI
+    @WrapForJNI
     public void addRow(Object[] columnValues) {
         if (columnValues.length != columnCount) {
             throw new IllegalArgumentException("columnNames.length = "
                     + columnCount + ", columnValues.length = "
                     + columnValues.length);
         }
 
         int start = rowCount++ * columnCount;
@@ -149,17 +149,17 @@ public class MatrixBlobCursor extends Ab
      * Adds a new row to the end with the given column values. Not safe
      * for concurrent use.
      *
      * @throws IllegalArgumentException if {@code columnValues.size() !=
      *  columnNames.length}
      * @param columnValues in the same order as the the column names specified
      *  at cursor construction time
      */
-    @WrapElementForJNI
+    @WrapForJNI
     public void addRow(Iterable<?> columnValues) {
         final int start = rowCount * columnCount;
 
         if (columnValues instanceof ArrayList<?>) {
             addRow((ArrayList<?>) columnValues, start);
             return;
         }
 
@@ -183,17 +183,17 @@ public class MatrixBlobCursor extends Ab
                     "columnValues.size() < columnNames.length");
         }
 
         // Increase row count here in case we encounter an exception.
         rowCount++;
     }
 
     /** Optimization for {@link ArrayList}. */
-    @WrapElementForJNI
+    @WrapForJNI
     private void addRow(ArrayList<?> columnValues, int start) {
         final int size = columnValues.size();
         if (size != columnCount) {
             throw new IllegalArgumentException("columnNames.length = "
                     + columnCount + ", columnValues.size() = " + size);
         }
 
         final int end = start + columnCount;
--- a/mobile/android/base/sqlite/SQLiteBridgeException.java
+++ b/mobile/android/base/sqlite/SQLiteBridgeException.java
@@ -1,18 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.sqlite;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.annotation.WrapForJNI;
 
-@WrapElementForJNI
+@WrapForJNI
 public class SQLiteBridgeException extends RuntimeException {
     static final long serialVersionUID = 1L;
 
     public SQLiteBridgeException() {}
     public SQLiteBridgeException(String msg) {
         super(msg);
     }
 }
--- a/mobile/android/base/util/Clipboard.java
+++ b/mobile/android/base/util/Clipboard.java
@@ -1,18 +1,18 @@
 /* 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/. */
 
 package org.mozilla.gecko.util;
 
 import java.util.concurrent.SynchronousQueue;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 
 import android.content.ClipData;
 import android.content.Context;
 import android.util.Log;
 
 public final class Clipboard {
     // Volatile but not synchronized: we don't care about the race condition in
     // init, because both app contexts will be the same, but we do care about a
@@ -27,17 +27,17 @@ public final class Clipboard {
     public static void init(final Context c) {
         if (mContext != null) {
             Log.w(LOGTAG, "Clipboard.init() called twice!");
             return;
         }
         mContext = c.getApplicationContext();
     }
 
-    @WrapElementForJNI(stubName = "GetClipboardTextWrapper")
+    @WrapForJNI(stubName = "GetClipboardTextWrapper")
     public static String getText() {
         // If we're on the UI thread or the background thread, we have a looper on the thread
         // and can just call this directly. For any other threads, post the call to the
         // background thread.
 
         if (ThreadUtils.isOnUiThread() || ThreadUtils.isOnBackgroundThread()) {
             return getClipboardTextImpl();
         }
@@ -54,17 +54,17 @@ public final class Clipboard {
 
         try {
             return sClipboardQueue.take();
         } catch (InterruptedException ie) {
             return "";
         }
     }
 
-    @WrapElementForJNI(stubName = "SetClipboardText")
+    @WrapForJNI(stubName = "SetClipboardText")
     public static void setText(final CharSequence text) {
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             @SuppressWarnings("deprecation")
             public void run() {
                 // In API Level 11 and above, CLIPBOARD_SERVICE returns android.content.ClipboardManager,
                 // which is a subclass of android.text.ClipboardManager.
                 if (Versions.feature11Plus) {
@@ -85,32 +85,32 @@ public final class Clipboard {
                 cm.setText(text);
             }
         });
     }
 
     /**
      * @return true if the clipboard is nonempty, false otherwise.
      */
-    @WrapElementForJNI
+    @WrapForJNI
     public static boolean hasText() {
         if (Versions.feature11Plus) {
             android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
             return cm.hasPrimaryClip();
         }
 
         // Deprecated.
         android.text.ClipboardManager cm = (android.text.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
         return cm.hasText();
     }
 
     /**
      * Deletes all text from the clipboard.
      */
-    @WrapElementForJNI
+    @WrapForJNI
     public static void clearText() {
         setText(null);
     }
 
     /**
      * On some devices, access to the clipboard service needs to happen
      * on a thread with a looper, so this function requires a looper is
      * present on the thread.
--- a/mobile/android/base/util/NativeJSContainer.java
+++ b/mobile/android/base/util/NativeJSContainer.java
@@ -1,28 +1,27 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.util;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.JNITarget;
 
 /**
  * NativeJSContainer is a wrapper around the SpiderMonkey JSAPI to make it possible to
  * access Javascript objects in Java.
  *
  * A container must only be used on the thread it is attached to. To use it on another
  * thread, call {@link #clone()} to make a copy, and use the copy on the other thread.
  * When a copy is first used, it becomes attached to the thread using it.
  */
-@JNITarget
-@WrapElementForJNI
+@WrapForJNI
 public final class NativeJSContainer extends NativeJSObject
 {
     private NativeJSContainer() {
     }
 
     /**
      * Make a copy of this container for use by another thread. When the copy is first used,
      * it becomes attached to the thread using it.
--- a/mobile/android/base/util/NativeJSObject.java
+++ b/mobile/android/base/util/NativeJSObject.java
@@ -1,27 +1,26 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.util;
 
-import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.JNIObject;
 import org.mozilla.gecko.mozglue.JNITarget;
 
 import android.os.Bundle;
 
 /**
  * NativeJSObject is a wrapper around the SpiderMonkey JSAPI to make it possible to
  * access Javascript objects in Java.
  */
-@JNITarget
-@WrapElementForJNI
+@WrapForJNI
 public class NativeJSObject extends JNIObject
 {
     @SuppressWarnings("serial")
     @JNITarget
     public static final class InvalidPropertyException extends RuntimeException {
         public InvalidPropertyException(final String msg) {
             super(msg);
         }
--- a/mobile/android/config/proguard/proguard.cfg
+++ b/mobile/android/config/proguard/proguard.cfg
@@ -174,30 +174,30 @@
 -keepclasseswithmembers class * {
     @org.mozilla.gecko.mozglue.WebRTCJNITarget <methods>;
 }
 -keepclasseswithmembers class * {
     @org.mozilla.gecko.mozglue.WebRTCJNITarget <fields>;
 }
 
 # Keep generator-targeted entry points.
--keep @interface org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI
--keep @org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI class *
+-keep @interface org.mozilla.gecko.annotation.WrapForJNI
+-keep @org.mozilla.gecko.annotation.WrapForJNI class *
 -keepclassmembers class * {
-    @org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI *;
+    @org.mozilla.gecko.annotation.WrapForJNI *;
 }
 -keepclasseswithmembers class * {
-    @org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI <methods>;
+    @org.mozilla.gecko.annotation.WrapForJNI <methods>;
 }
 -keepclasseswithmembers class * {
-    @org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI <fields>;
+    @org.mozilla.gecko.annotation.WrapForJNI <fields>;
 }
 
 # Keep all members of an annotated class.
--keepclassmembers @org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI class * {
+-keepclassmembers @org.mozilla.gecko.annotation.WrapForJNI class * {
     *;
 }
 
 -keep class **.R$*
 
 # Keep classes, and all their contents, compiled before mozglue.RobocopTarget.
 -keep class org.mozilla.gecko.AppConstants {
     *;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -393,16 +393,17 @@ pref("media.peerconnection.video.min_bit
 pref("media.peerconnection.video.start_bitrate", 300);
 pref("media.peerconnection.video.max_bitrate", 2000);
 #endif
 pref("media.navigator.permission.disabled", false);
 pref("media.peerconnection.default_iceservers", "[]");
 pref("media.peerconnection.ice.loopback", false); // Set only for testing in offline environments.
 pref("media.peerconnection.ice.tcp", false);
 pref("media.peerconnection.ice.link_local", false); // Set only for testing IPV6 in networks that don't assign IPV6 addresses
+pref("media.peerconnection.ice.relay_only", false); // Limit candidates to TURN
 pref("media.peerconnection.use_document_iceservers", true);
 pref("media.peerconnection.identity.enabled", true);
 pref("media.peerconnection.identity.timeout", 10000);
 pref("media.peerconnection.ice.stun_client_maximum_transmits", 7);
 pref("media.peerconnection.ice.trickle_grace_period", 5000);
 // These values (aec, agc, and noice) are from media/webrtc/trunk/webrtc/common_types.h
 // kXxxUnchanged = 0, kXxxDefault = 1, and higher values are specific to each
 // setting (for Xxx = Ec, Agc, or Ns).  Defaults are all set to kXxxDefault here.
@@ -471,27 +472,23 @@ pref("media.track.enabled", false);
 pref("media.mediasource.enabled", true);
 #else
 pref("media.mediasource.enabled", false);
 #endif
 
 pref("media.mediasource.mp4.enabled", true);
 pref("media.mediasource.webm.enabled", false);
 
-#if defined(MOZ_WIDGET_GONK)
-pref("media.mediasource.format-reader", false);
-#else
 // Enable new MediaSource architecture.
 pref("media.mediasource.format-reader", true);
-#endif
 
 // Enable new MediaFormatReader architecture for webm in MSE
 pref("media.mediasource.format-reader.webm", false);
 // Enable new MediaFormatReader architecture for plain webm.
-pref("media.format-reader.webm", false);
+pref("media.format-reader.webm", true);
 
 #ifdef MOZ_WEBSPEECH
 pref("media.webspeech.recognition.enable", false);
 pref("media.webspeech.synth.enabled", false);
 #endif
 #ifdef MOZ_WEBM_ENCODER
 pref("media.encoder.webm.enabled", true);
 #endif
--- a/netwerk/dns/mdns/libmdns/moz.build
+++ b/netwerk/dns/mdns/libmdns/moz.build
@@ -1,15 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android' and CONFIG['ANDROID_VERSION'] >= '21':
     EXTRA_COMPONENTS += [
         'nsDNSServiceDiscovery.js',
         'nsDNSServiceDiscovery.manifest',
     ]
 
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['ANDROID_VERSION'] >= '16':
     UNIFIED_SOURCES += [
         'MDNSResponderOperator.cpp',
--- a/security/certverifier/ExtendedValidation.cpp
+++ b/security/certverifier/ExtendedValidation.cpp
@@ -536,29 +536,16 @@ static struct nsMyTrustedEVInfo myTruste
       0x86, 0x5B, 0xDF, 0x1C, 0xD4, 0x10, 0x2E, 0x7D, 0x07, 0x59, 0xAF,
       0x63, 0x5A, 0x7C, 0xF4, 0x72, 0x0D, 0xC9, 0x63, 0xC5, 0x3B },
     "MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpH"
     "bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu",
     "BAAAAAABIVhTCKI=",
     nullptr
   },
   {
-    // CN=Buypass Class 3 CA 1,O=Buypass AS-983163327,C=NO
-    "2.16.578.1.26.1.3.3",
-    "Buypass EV OID",
-    SEC_OID_UNKNOWN,
-    { 0xB7, 0xB1, 0x2B, 0x17, 0x1F, 0x82, 0x1D, 0xAA, 0x99, 0x0C, 0xD0,
-      0xFE, 0x50, 0x87, 0xB1, 0x28, 0x44, 0x8B, 0xA8, 0xE5, 0x18, 0x4F,
-      0x84, 0xC5, 0x1E, 0x02, 0xB5, 0xC8, 0xFB, 0x96, 0x2B, 0x24 },
-    "MEsxCzAJBgNVBAYTAk5PMR0wGwYDVQQKDBRCdXlwYXNzIEFTLTk4MzE2MzMyNzEd"
-    "MBsGA1UEAwwUQnV5cGFzcyBDbGFzcyAzIENBIDE=",
-    "Ag==",
-    nullptr
-  },
-  {
     // CN=Buypass Class 3 Root CA,O=Buypass AS-983163327,C=NO
     "2.16.578.1.26.1.3.3",
     "Buypass EV OID",
     SEC_OID_UNKNOWN,
     { 0xED, 0xF7, 0xEB, 0xBC, 0xA2, 0x7A, 0x2A, 0x38, 0x4D, 0x38, 0x7B,
       0x7D, 0x40, 0x10, 0xC6, 0x66, 0xE2, 0xED, 0xB4, 0x84, 0x3E, 0x4C,
       0x29, 0xB4, 0xAE, 0x1D, 0x5B, 0x93, 0x32, 0xE6, 0xB2, 0x4D },
     "ME4xCzAJBgNVBAYTAk5PMR0wGwYDVQQKDBRCdXlwYXNzIEFTLTk4MzE2MzMyNzEg"
@@ -1246,27 +1233,28 @@ IdentityInfoInit()
 
     entry.cert = CERT_FindCertByIssuerAndSN(nullptr, &ias);
 
     SECITEM_FreeItem(&ias.derIssuer, false);
     SECITEM_FreeItem(&ias.serialNumber, false);
 
     // If an entry is missing in the NSS root database, it may be because the
     // root database is out of sync with what we expect (e.g. a different
-    // version of system NSS is installed). We will just silently avoid
-    // treating that root cert as EV.
+    // version of system NSS is installed). We assert on debug builds, but
+    // silently continue on release builds. In both cases, the root cert does
+    // not get EV treatment.
     if (!entry.cert) {
 #ifdef DEBUG
       // The debug CA structs are at positions 0 to NUM_TEST_EV_ROOTS - 1, and
       // are NOT in the NSS root DB.
       if (iEV < NUM_TEST_EV_ROOTS) {
         continue;
       }
 #endif
-      //PR_NOT_REACHED("Could not find EV root in NSS storage");
+      PR_NOT_REACHED("Could not find EV root in NSS storage");
       continue;
     }
 
     unsigned char certFingerprint[SHA256_LENGTH];
     rv = PK11_HashBuf(SEC_OID_SHA256, certFingerprint,
                       entry.cert->derCert.data,
                       static_cast<int32_t>(entry.cert->derCert.len));
     PR_ASSERT(rv == SECSuccess);
--- a/testing/mozharness/mozharness/mozilla/buildbot.py
+++ b/testing/mozharness/mozharness/mozilla/buildbot.py
@@ -176,8 +176,42 @@ class BuildbotMixin(object):
             ]
 
         for d in downloadables:
             sendchange += [d]
 
         retcode = self.run_command(buildbot + sendchange)
         if retcode != 0:
             self.info("The sendchange failed but we don't want to turn the build orange: %s" % retcode)
+
+    def query_build_name(self):
+        build_name = self.config.get('platform')
+        if not build_name:
+            self.fatal('Must specify "platform" in the mozharness config for indexing')
+
+        return build_name
+
+    def query_build_type(self):
+        if self.config.get('build_type'):
+            build_type = self.config['build_type']
+        elif self.config.get('pgo_build'):
+            build_type = 'pgo'
+        elif self.config.get('debug_build', False):
+            build_type = 'debug'
+        else:
+            build_type = 'opt'
+        return build_type
+
+    def buildid_to_dict(self, buildid):
+        """Returns an dict with the year, month, day, hour, minute, and second
+           as keys, as parsed from the buildid"""
+        buildidDict = {}
+        try:
+            # strptime is no good here because it strips leading zeros
+            buildidDict['year'] = buildid[0:4]
+            buildidDict['month'] = buildid[4:6]
+            buildidDict['day'] = buildid[6:8]
+            buildidDict['hour'] = buildid[8:10]
+            buildidDict['minute'] = buildid[10:12]
+            buildidDict['second'] = buildid[12:14]
+        except:
+            self.fatal('Could not parse buildid into YYYYMMDDHHMMSS: %s' % buildid)
+        return buildidDict
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -1365,16 +1365,17 @@ or run without that action (ie: --no-{ac
                 self.return_code = self.worst_level(
                     EXIT_STATUS_DICT[TBPL_WARNING], self.return_code,
                     AUTOMATION_EXIT_CODES[::-1]
                 )
 
         self.generated_build_props = True
 
     def upload_files(self):
+        dirs = self.query_abs_dirs()
         auth = os.path.join(os.getcwd(), self.config['taskcluster_credentials_file'])
         credentials = {}
         execfile(auth, credentials)
         client_id = credentials.get('taskcluster_clientId')
         access_token = credentials.get('taskcluster_accessToken')
         if not client_id or not access_token:
             self.warning('Skipping S3 file upload: No taskcluster credentials.')
             return
@@ -1386,29 +1387,57 @@ or run without that action (ie: --no-{ac
         # which is necessary before the virtualenv can be created.
         self.create_virtualenv()
         self.activate_virtualenv()
 
         # Enable Taskcluster debug logging, so at least we get some debug
         # messages while we are testing uploads.
         logging.getLogger('taskcluster').setLevel(logging.DEBUG)
 
+        routes_json = os.path.join(dirs['abs_src_dir'],
+                                   'testing/taskcluster/routes.json')
+        with open(routes_json) as f:
+            contents = json.load(f)
+            if self.query_is_nightly():
+                templates = contents['nightly']
+
+                # Nightly builds with l10n counterparts also publish to the
+                # 'en-US' locale.
+                if self.config.get('publish_nightly_en_US_routes'):
+                    templates.extend(contents['l10n'])
+            else:
+                templates = contents['routes']
+        routes = []
+        for template in templates:
+            fmt = {
+                'index': 'index.garbage.staging.mshal-testing', # TODO
+                'project': self.buildbot_config['properties']['branch'],
+                'head_rev': self.query_revision(),
+                'build_product': self.config['stage_product'],
+                'build_name': self.query_build_name(),
+                'build_type': self.query_build_type(),
+                'locale': 'en-US',
+            }
+            fmt.update(self.buildid_to_dict(self.query_buildid()))
+            routes.append(template.format(**fmt))
+        self.info("Using routes: %s" % routes)
+
         tc = Taskcluster(self.branch,
                          self.query_pushdate(), # Use pushdate as the rank
                          client_id,
                          access_token,
                          self.log_obj,
                          )
 
         index = self.config.get('taskcluster_index', 'index.garbage.staging')
         # TODO: Bug 1165980 - these should be in tree
-        routes = [
+        routes.extend([
             "%s.buildbot.branches.%s.%s" % (index, self.branch, self.stage_platform),
             "%s.buildbot.revisions.%s.%s.%s" % (index, self.query_revision(), self.branch, self.stage_platform),
-        ]
+        ])
         task = tc.create_task(routes)
         tc.claim_task(task)
 
         # Some trees may not be setting uploadFiles, so default to []. Normally
         # we'd only expect to get here if the build completes successfully,
         # which means we should have uploadFiles.
         files = self.query_buildbot_property('uploadFiles') or []
         if not files:
@@ -1468,17 +1497,16 @@ or run without that action (ie: --no-{ac
             '.zip',
             '.json',
         )
 
         # Also upload our mozharness log files
         files.extend([os.path.join(self.log_obj.abs_log_dir, x) for x in self.log_obj.log_files.values()])
 
         # Also upload our buildprops.json file.
-        dirs = self.query_abs_dirs()
         files.extend([os.path.join(dirs['base_work_dir'], 'buildprops.json')])
 
         for upload_file in files:
             # Create an S3 artifact for each file that gets uploaded. We also
             # check the uploaded file against the property conditions so that we
             # can set the buildbot config with the correct URLs for package
             # locations.
             tc.create_artifact(task, upload_file)
--- a/testing/mozharness/scripts/desktop_l10n.py
+++ b/testing/mozharness/scripts/desktop_l10n.py
@@ -201,20 +201,18 @@ class DesktopSingleLocale(LocalesMixin, 
         self.upload_env = None
         self.revision = None
         self.version = None
         self.upload_urls = {}
         self.locales_property = {}
         self.l10n_dir = None
         self.package_urls = {}
         self.pushdate = None
-        # Each locale adds its list of files to upload_files - some will be
-        # duplicates (like the mar binaries), so we use a set to prune those
-        # when uploading to taskcluster.
-        self.upload_files = set()
+        # upload_files is a dictionary of files to upload, keyed by locale.
+        self.upload_files = {}
 
         if 'mock_target' in self.config:
             self.enable_mock()
 
     def _pre_config_lock(self, rw_config):
         """replaces 'configuration_tokens' with their values, before the
            configuration gets locked. If some of the configuration_tokens
            are not present, stops the execution of the script"""
@@ -744,19 +742,19 @@ class DesktopSingleLocale(LocalesMixin, 
         cwd = dirs['abs_locales_dir']
         output = self._get_output_from_make(target=target, cwd=cwd, env=env)
         self.info('UPLOAD_FILES is "%s"' % (output))
         files = shlex.split(output)
         if not files:
             self.error('failed to get upload file list for locale %s' % (locale))
             return FAILURE
 
-        for f in files:
-            abs_file = os.path.abspath(os.path.join(cwd, f))
-            self.upload_files.update([abs_file])
+        self.upload_files[locale] = [
+            os.path.abspath(os.path.join(cwd, f)) for f in files
+        ]
         return SUCCESS
 
     def make_installers(self, locale):
         """wrapper for make installers-(locale)"""
         env = self.query_l10n_env()
         self._copy_mozconfig()
         env['L10NBASEDIR'] = self.l10n_dir
         dirs = self.query_abs_dirs()
@@ -1013,41 +1011,58 @@ class DesktopSingleLocale(LocalesMixin, 
 
         # Enable Taskcluster debug logging, so at least we get some debug
         # messages while we are testing uploads.
         logging.getLogger('taskcluster').setLevel(logging.DEBUG)
 
         branch = self.config['branch']
         platform = self.config['platform']
         revision = self._query_revision()
-        tc = Taskcluster(self.config['branch'],
-                         self.query_pushdate(),
-                         client_id,
-                         access_token,
-                         self.log_obj,
-                         )
+
+        routes_json = os.path.join(self.query_abs_dirs()['abs_mozilla_dir'],
+                                   'testing/taskcluster/routes.json')
+        with open(routes_json) as f:
+            contents = json.load(f)
+            templates = contents['l10n']
 
-        index = self.config.get('taskcluster_index', 'index.garbage.staging')
-        # TODO: Bug 1165980 - these should be in tree. Note the '.l10n' suffix.
-        routes = [
-            "%s.buildbot.branches.%s.%s.l10n" % (index, branch, platform),
-            "%s.buildbot.revisions.%s.%s.%s.l10n" % (index, revision, branch, platform),
-        ]
+        for locale, files in self.upload_files.iteritems():
+            self.info("Uploading files to S3 for locale '%s': %s" % (locale, files))
+            routes = []
+            for template in templates:
+                fmt = {
+                    # TODO: Bug 1133074
+                    #index = self.config.get('taskcluster_index', 'index.garbage.staging')
+                    'index': 'index.garbage.staging.mshal-testing',
+                    'project': branch,
+                    'head_rev': revision,
+                    'build_product': self.config['stage_product'],
+                    'build_name': self.query_build_name(),
+                    'build_type': self.query_build_type(),
+                    'locale': locale,
+                }
+                fmt.update(self.buildid_to_dict(self._query_buildid()))
+                routes.append(template.format(**fmt))
+                self.info('Using routes: %s' % routes)
 
-        task = tc.create_task(routes)
-        tc.claim_task(task)
+            tc = Taskcluster(branch,
+                             self.query_pushdate(),
+                             client_id,
+                             access_token,
+                             self.log_obj,
+                             )
+            task = tc.create_task(routes)
+            tc.claim_task(task)
 
-        self.info("Uploading files to S3: %s" % self.upload_files)
-        for upload_file in self.upload_files:
-            # Create an S3 artifact for each file that gets uploaded. We also
-            # check the uploaded file against the property conditions so that we
-            # can set the buildbot config with the correct URLs for package
-            # locations.
-            tc.create_artifact(task, upload_file)
-        tc.report_completed(task)
+            for upload_file in files:
+                # Create an S3 artifact for each file that gets uploaded. We also
+                # check the uploaded file against the property conditions so that we
+                # can set the buildbot config with the correct URLs for package
+                # locations.
+                tc.create_artifact(task, upload_file)
+            tc.report_completed(task)
 
     def query_pushdate(self):
         if self.pushdate:
             return self.pushdate
 
         mozilla_dir = self.config['mozilla_dir']
         repo = None
         for repository in self.config['repos']:
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -80,18 +80,16 @@ const TELEMETRY_INTERVAL = 60000;
 // Delay before intializing telemetry (ms)
 const TELEMETRY_DELAY = 60000;
 // Delay before initializing telemetry if we're testing (ms)
 const TELEMETRY_TEST_DELAY = 100;
 // Execute a scheduler tick every 5 minutes.
 const SCHEDULER_TICK_INTERVAL_MS = 5 * 60 * 1000;
 // When user is idle, execute a scheduler tick every 60 minutes.
 const SCHEDULER_TICK_IDLE_INTERVAL_MS = 60 * 60 * 1000;
-// The maximum number of times a scheduled operation can fail.
-const SCHEDULER_RETRY_ATTEMPTS = 3;
 
 // The tolerance we have when checking if it's midnight (15 minutes).
 const SCHEDULER_MIDNIGHT_TOLERANCE_MS = 15 * 60 * 1000;
 
 // Coalesce the daily and aborted-session pings if they are both due within
 // two minutes from each other.
 const SCHEDULER_COALESCE_THRESHOLD_MS = 2 * 60 * 1000;
 
@@ -376,19 +374,16 @@ let TelemetryScheduler = {
   _lastSessionCheckpointTime: 0,
 
   // For sanity checking.
   _lastAdhocPingTime: 0,
   _lastTickTime: 0,
 
   _log: null,
 
-  // The number of times a daily ping fails.
-  _dailyPingRetryAttempts: 0,
-
   // The timer which drives the scheduler.
   _schedulerTimer: null,
   // The interval used by the scheduler timer.
   _schedulerInterval: 0,
   _shuttingDown: true,
   _isUserIdle: false,
 
   /**
@@ -541,35 +536,31 @@ let TelemetryScheduler = {
     }
     this._lastTickTime = now;
 
     // Check if the daily ping is due.
     const shouldSendDaily = this._isDailyPingDue(nowDate);
 
     if (shouldSendDaily) {
       this._log.trace("_schedulerTickLogic - Daily ping due.");
-      return Impl._sendDailyPing().then(() => this._dailyPingSucceeded(now),
-                                        () => this._dailyPingFailed(now));
+      this._lastDailyPingTime = now;
+      return Impl._sendDailyPing();
     }
 
     // Check if the aborted-session ping is due. If a daily ping was saved above, it was
     // already duplicated as an aborted-session ping.
     const isAbortedPingDue =
       (now - this._lastSessionCheckpointTime) >= ABORTED_SESSION_UPDATE_INTERVAL_MS;
     if (isAbortedPingDue) {
       this._log.trace("_schedulerTickLogic - Aborted session ping due.");
       return this._saveAbortedPing(now);
     }
 
     // No ping is due.
     this._log.trace("_schedulerTickLogic - No ping due.");
-    // It's possible, because of sleeps, that we're no longer within midnight tolerance for
-    // daily pings. Because of that, daily retry attempts would not be 0 on the next midnight.
-    // Reset that count on do-nothing ticks.
-    this._dailyPingRetryAttempts = 0;
     return Promise.resolve();
   },
 
   /**
    * Update the scheduled pings if some other ping was sent.
    * @param {String} reason The reason of the ping that was sent.
    * @param {Object} [competingPayload=null] The payload of the ping that was sent. The
    *                 reason of this payload will be changed.
@@ -593,43 +584,16 @@ let TelemetryScheduler = {
         this._lastDailyPingTime = now.getTime();
       }
     }
 
     this._rescheduleTimeout();
   },
 
   /**
-   * Called when a scheduled operation successfully completes (ping sent or saved).
-   * @param {Number} now The current time, in milliseconds.
-   */
-  _dailyPingSucceeded: function(now) {
-    this._log.trace("_dailyPingSucceeded");
-    this._lastDailyPingTime = now;
-    this._dailyPingRetryAttempts = 0;
-  },
-
-  /**
-   * Called when a scheduled operation fails (ping sent or saved).
-   * @param {Number} now The current time, in milliseconds.
-   */
-  _dailyPingFailed: function(now) {
-    this._log.error("_dailyPingFailed");
-    this._dailyPingRetryAttempts++;
-
-    // If we reach the maximum number of retry attempts for a daily ping, log the error
-    // and skip this daily ping.
-    if (this._dailyPingRetryAttempts >= SCHEDULER_RETRY_ATTEMPTS) {
-      this._log.error("_pingFailed - The daily ping failed too many times. Skipping it.");
-      this._dailyPingRetryAttempts = 0;
-      this._lastDailyPingTime = now;
-    }
-  },
-
-  /**
    * Stops the scheduler.
    */
   shutdown: function() {
     if (this._shuttingDown) {
       if (this._log) {
         this._log.error("shutdown - Already shut down");
       } else {
         Cu.reportError("TelemetryScheduler.shutdown - Already shut down");
@@ -1889,18 +1853,18 @@ let Impl = {
       addEnvironment: true,
     };
 
     let promise = TelemetryController.submitExternalPing(getPingType(payload), payload, options);
 
     // Also save the payload as an aborted session. If we delay this, aborted-session can
     // lag behind for the profileSubsessionCounter and other state, complicating analysis.
     if (IS_UNIFIED_TELEMETRY) {
-      let abortedPromise = this._saveAbortedSessionPing(payload);
-      promise = promise.then(() => abortedPromise);
+      this._saveAbortedSessionPing(payload)
+          .catch(e => this._log.error("_sendDailyPing - Failed to save the aborted session ping", e));
     }
 
     return promise;
   },
 
   /**
    * Loads session data from the session data file.
    * @return {Promise<boolean>} A promise which is resolved with a true argument when
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -1677,12 +1677,112 @@ add_task(function* test_schedulerUserIdl
   now.setMinutes(50);
   fakeNow(now);
   fakeIdleNotification("idle");
   Assert.equal(schedulerTimeout, 10 * 60 * 1000);
 
   yield TelemetrySession.shutdown();
 });
 
+add_task(function* test_DailyDueAndIdle() {
+  if (gIsAndroid || gIsGonk) {
+    // We don't have the aborted session or the daily ping here.
+    return;
+  }
+
+  yield TelemetrySession.reset();
+  clearPendingPings();
+  PingServer.clearRequests();
+
+  let receivedPingRequest = null;
+  // Register a ping handler that will assert when receiving multiple daily pings.
+  PingServer.registerPingHandler(req => {
+    Assert.ok(!receivedPingRequest, "Telemetry must only send one daily ping.");
+    receivedPingRequest = req;
+  });
+
+  let schedulerTickCallback = null;
+  let now = new Date(2030, 1, 1, 0, 0, 0);
+  fakeNow(now);
+  // Fake scheduler functions to control daily collection flow in tests.
+  fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {});
+  yield TelemetrySession.setup();
+
+  // Trigger the daily ping.
+  let firstDailyDue = new Date(2030, 1, 2, 0, 0, 0);
+  fakeNow(firstDailyDue);
+
+  // Run a scheduler tick: it should trigger the daily ping.
+  Assert.ok(!!schedulerTickCallback);
+  let tickPromise = schedulerTickCallback();
+
+  // Send an idle and then an active user notification.
+  fakeIdleNotification("idle");
+  fakeIdleNotification("active");
+
+  // Wait on the tick promise.
+  yield tickPromise;
+
+  yield TelemetrySend.testWaitOnOutgoingPings();
+
+  // Decode the ping contained in the request and check that's a daily ping.
+  Assert.ok(receivedPingRequest, "Telemetry must send one daily ping.");
+  const receivedPing = decodeRequestPayload(receivedPingRequest);
+  checkPingFormat(receivedPing, PING_TYPE_MAIN, true, true);
+  Assert.equal(receivedPing.payload.info.reason, REASON_DAILY);
+
+  yield TelemetrySession.shutdown();
+});
+
+add_task(function* test_userIdleAndSchedlerTick() {
+  if (gIsAndroid || gIsGonk) {
+    // We don't have the aborted session or the daily ping here.
+    return;
+  }
+
+  yield TelemetrySession.reset();
+  clearPendingPings();
+  PingServer.clearRequests();
+
+  let receivedPingRequest = null;
+  // Register a ping handler that will assert when receiving multiple daily pings.
+  PingServer.registerPingHandler(req => {
+    Assert.ok(!receivedPingRequest, "Telemetry must only send one daily ping.");
+    receivedPingRequest = req;
+  });
+
+  let schedulerTickCallback = null;
+  let now = new Date(2030, 1, 1, 0, 0, 0);
+  fakeNow(now);
+  // Fake scheduler functions to control daily collection flow in tests.
+  fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {});
+  yield TelemetrySession.setup();
+
+  // Move the current date/time to midnight.
+  let firstDailyDue = new Date(2030, 1, 2, 0, 0, 0);
+  fakeNow(firstDailyDue);
+
+  // The active notification should trigger a scheduler tick. The latter will send the
+  // due daily ping.
+  fakeIdleNotification("active");
+
+  // Immediately running another tick should not send a daily ping again.
+  Assert.ok(!!schedulerTickCallback);
+  yield schedulerTickCallback();
+
+  // A new "idle" notification should not send a new daily ping.
+  fakeIdleNotification("idle");
+
+  yield TelemetrySend.testWaitOnOutgoingPings();
+
+  // Decode the ping contained in the request and check that's a daily ping.
+  Assert.ok(receivedPingRequest, "Telemetry must send one daily ping.");
+  const receivedPing = decodeRequestPayload(receivedPingRequest);
+  checkPingFormat(receivedPing, PING_TYPE_MAIN, true, true);
+  Assert.equal(receivedPing.payload.info.reason, REASON_DAILY);
+
+  yield TelemetrySession.shutdown();
+});
+
 add_task(function* stopServer(){
   yield PingServer.stop();
   do_test_finished();
 });
--- a/toolkit/content/tests/chrome/test_mousecapture.xul
+++ b/toolkit/content/tests/chrome/test_mousecapture.xul
@@ -6,17 +6,17 @@
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
 <script>
 <![CDATA[
 
-SimpleTest.expectAssertions(12);
+SimpleTest.expectAssertions(6, 12);
 
 SimpleTest.waitForExplicitFinish();
 
 var captureRetargetMode = false;
 var cachedMouseDown = null;
 var previousWidth = 0, originalWidth = 0;
 var loadInWindow = false;
 
--- a/toolkit/devtools/gcli/commands/rulers.js
+++ b/toolkit/devtools/gcli/commands/rulers.js
@@ -54,42 +54,42 @@ exports.items = [
   // The server rulers command is hidden by default, it's just used by the
   // client command.
   {
     name: "rulers_server",
     runAt: "server",
     hidden: true,
     exec: function(args, context) {
       let env = context.environment;
+      let { document } = env;
 
       // Calling the command again after the rulers have been shown once hides
       // them.
-      if (highlighters.has(env.document)) {
-        let { highlighter, environment } = highlighters.get(env.document);
+      if (highlighters.has(document)) {
+        let { highlighter } = highlighters.get(document);
         highlighter.destroy();
-        environment.destroy();
         return false;
       }
 
       // Otherwise, display the rulers.
       let environment = new HighlighterEnvironment();
       environment.initFromWindow(env.window);
       let highlighter = new RulersHighlighter(environment);
 
       // Store the instance of the rulers highlighter for this document so we
       // can hide it later.
-      highlighters.set(env.document, { highlighter, environment });
+      highlighters.set(document, { highlighter, environment });
 
       // Listen to the highlighter's destroy event which may happen if the
       // window is refreshed or closed with the rulers shown.
       events.once(highlighter, "destroy", () => {
-        if (highlighters.has(env.document)) {
-          let { environment } = highlighters.get(env.document);
+        if (highlighters.has(document)) {
+          let { environment } = highlighters.get(document);
           environment.destroy();
-          highlighters.delete(env.document);
+          highlighters.delete(document);
         }
       });
 
       highlighter.show();
       return true;
     }
   }
 ];
--- a/toolkit/devtools/server/actors/gcli.js
+++ b/toolkit/devtools/server/actors/gcli.js
@@ -260,17 +260,17 @@ const GcliActor = ActorClass({
           throw new Error("environment.chromeDocument is not available in runAt:server commands");
         },
 
         get window() {
           return tabActor.window;
         },
 
         get document() {
-          return tabActor.window.document;
+          return tabActor.window && tabActor.window.document;
         }
       };
 
       return new Requisition(this._system, { environment: environment });
     });
 
     return this._requisitionPromise;
   },
--- a/toolkit/devtools/server/actors/highlighter.css
+++ b/toolkit/devtools/server/actors/highlighter.css
@@ -243,25 +243,16 @@
 :-moz-native-anonymous .rulers-highlighter-elements > g {
   opacity: 0.8;
 }
 
 :-moz-native-anonymous .rulers-highlighter-elements > g > rect {
   fill: #fff;
 }
 
-/*
-  Setting `stroke-width` with a low floating point number, ensure that `path`
-  draws always 1px despites the zoom level, with `crispEdges` on the ancestor.
-*/
-:-moz-native-anonymous .rulers-highlighter-ruler-graduations,
-:-moz-native-anonymous .rulers-highlighter-ruler-markers {
-  stroke-width: 0.0001;
-}
-
 :-moz-native-anonymous .rulers-highlighter-ruler-graduations {
   stroke: #bebebe;
 }
 
 :-moz-native-anonymous .rulers-highlighter-ruler-markers {
   stroke: #202020;
 }
 
@@ -277,9 +268,8 @@
 :-moz-native-anonymous .rulers-highlighter-horizontal-labels > text {
   text-anchor: left;
 }
 
 :-moz-native-anonymous .rulers-highlighter-vertical-labels > text {
   transform: rotate(-90deg);
   text-anchor: end;
 }
-
--- a/toolkit/devtools/server/actors/highlighter.js
+++ b/toolkit/devtools/server/actors/highlighter.js
@@ -2928,36 +2928,80 @@ RulersHighlighter.prototype = {
     this.markup.getElement(`${prefix}x-axis-text`)
                         .setAttribute("transform", `translate(${-scrollX})`);
     this.markup.getElement(`${prefix}y-axis-ruler`)
                         .setAttribute("transform", `translate(0, ${-scrollY})`);
     this.markup.getElement(`${prefix}y-axis-text`)
                         .setAttribute("transform", `translate(0, ${-scrollY})`);
   },
 
+  _update: function() {
+    setIgnoreLayoutChanges(true);
+
+    let zoom = LayoutHelpers.getCurrentZoom(this.win);
+    let isZoomChanged = zoom !== this._zoom;
+
+    if (isZoomChanged) {
+      this._zoom = zoom;
+      this.updateViewport();
+    }
+
+    setIgnoreLayoutChanges(false, this.win.document.documentElement);
+
+    this._rafID = this.win.requestAnimationFrame(() => this._update());
+  },
+
+  _cancelUpdate: function() {
+    if (this._rafID) {
+      this.win.cancelAnimationFrame(this._rafID);
+      this._rafID = 0;
+    }
+  },
+  updateViewport: function() {
+    let { devicePixelRatio } = this.win;
+
+    // Because `devicePixelRatio` is affected by zoom (see bug 809788),
+    // in order to get the "real" device pixel ratio, we need divide by `zoom`
+    let pixelRatio = devicePixelRatio / this._zoom;
+
+    // The "real" device pixel ratio is used to calculate the max stroke
+    // width we can actually assign: on retina, for instance, it would be 0.5,
+    // where on non high dpi monitor would be 1.
+    let minWidth = 1 / pixelRatio;
+    let strokeWidth = Math.min(minWidth, minWidth / this._zoom);
+
+    this.markup.getElement(this.ID_CLASS_PREFIX + "root").setAttribute("style",
+      `stroke-width:${strokeWidth};`);
+  },
+
   destroy: function() {
     this.hide();
 
     this.win.removeEventListener("scroll", this, true);
     this.win.removeEventListener("pagehide", this, true);
 
     this.markup.destroy();
 
     events.emit(this, "destroy");
   },
 
   show: function() {
     this.markup.removeAttributeForElement(this.ID_CLASS_PREFIX + "elements",
       "hidden");
+
+    this._update();
+
     return true;
   },
 
   hide: function() {
     this.markup.setAttributeForElement(this.ID_CLASS_PREFIX + "elements",
       "hidden", "true");
+
+    this._cancelUpdate();
   }
 };
 
 register(RulersHighlighter);
 exports.RulersHighlighter = RulersHighlighter;
 
 /**
  * The SimpleOutlineHighlighter is a class that has the same API than the
--- a/toolkit/webapps/tests/chrome.ini
+++ b/toolkit/webapps/tests/chrome.ini
@@ -26,14 +26,14 @@ skip-if = (os == "win" && os_version == 
 [test_packaged_uninstall.xul]
 skip-if = (os == "win" && os_version == "5.1") || (os == 'mac' && os_version == '10.10') # WinXP: bug 981251; OS X 10.10: bug 1123085
 [test_hosted_update_from_webapp_runtime.xul]
 skip-if = asan
 [test_packaged_update_from_webapp_runtime.xul]
 skip-if = asan
 [test_hosted_icons.xul]
 [test_hosted_checkforupdates_from_webapp_runtime.xul]
-skip-if = asan || ((os == "win" && os_version == "6.2") && debug) # Win8: bug 1162987
+skip-if = asan || (os == "win" && os_version == "6.2") # Win8: bug 1162987
 [test_packaged_icons.xul]
 [test_packaged_checkforupdates_from_webapp_runtime.xul]
-skip-if = asan || ((os == "win" && os_version == "6.2") && debug) # Win8: bug 1154545
+skip-if = asan || (os == "win" && os_version == "6.2") # Win8: bug 1154545
 [test_webapp_runtime_executable_update.xul]
 [test_non_ascii_app_name.xul]
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -1209,26 +1209,16 @@ float ViewTransform::Y() const
     return mozilla::jni::Field<Y_t>::Get(this, nullptr);
 }
 
 void ViewTransform::Y(float a0) const
 {
     return mozilla::jni::Field<Y_t>::Set(this, nullptr, a0);
 }
 
-constexpr char NativeZip::name[];
-
-constexpr char NativeZip::CreateInputStream_t::name[];
-constexpr char NativeZip::CreateInputStream_t::signature[];
-
-mozilla::jni::Object::LocalRef NativeZip::CreateInputStream(mozilla::jni::Object::Param a0, int32_t a1) const
-{
-    return mozilla::jni::Method<CreateInputStream_t>::Call(this, nullptr, a0, a1);
-}
-
 constexpr char MatrixBlobCursor::name[];
 
 constexpr char MatrixBlobCursor::New_t::name[];
 constexpr char MatrixBlobCursor::New_t::signature[];
 
 MatrixBlobCursor::LocalRef MatrixBlobCursor::New(mozilla::jni::ObjectArray::Param a0)
 {
     return mozilla::jni::Constructor<New_t>::Call(nullptr, nullptr, a0);
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2821,51 +2821,16 @@ public:
     };
 
     float Y() const;
 
     void Y(float) const;
 
 };
 
-class NativeZip : public mozilla::jni::Class<NativeZip>
-{
-public:
-    typedef mozilla::jni::Ref<NativeZip> Ref;
-    typedef mozilla::jni::LocalRef<NativeZip> LocalRef;
-    typedef mozilla::jni::GlobalRef<NativeZip> GlobalRef;
-    typedef const mozilla::jni::Param<NativeZip>& Param;
-
-    static constexpr char name[] =
-            "org/mozilla/gecko/mozglue/NativeZip";
-
-protected:
-    NativeZip(jobject instance) : Class(instance) {}
-
-public:
-    struct CreateInputStream_t {
-        typedef NativeZip Owner;
-        typedef mozilla::jni::Object::LocalRef ReturnType;
-        typedef mozilla::jni::Object::Param SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::Object::Param,
-                int32_t> Args;
-        static constexpr char name[] = "createInputStream";
-        static constexpr char signature[] =
-                "(Ljava/nio/ByteBuffer;I)Ljava/io/InputStream;";
-        static const bool isStatic = false;
-        static const bool isMultithreaded = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-    };
-
-    mozilla::jni::Object::LocalRef CreateInputStream(mozilla::jni::Object::Param, int32_t) const;
-
-};
-
 class MatrixBlobCursor : public mozilla::jni::Class<MatrixBlobCursor>
 {
 public:
     typedef mozilla::jni::Ref<MatrixBlobCursor> Ref;
     typedef mozilla::jni::LocalRef<MatrixBlobCursor> LocalRef;
     typedef mozilla::jni::GlobalRef<MatrixBlobCursor> GlobalRef;
     typedef const mozilla::jni::Param<MatrixBlobCursor>& Param;
 
--- a/widget/gtk/gtk3drawing.c
+++ b/widget/gtk/gtk3drawing.c
@@ -2633,26 +2633,35 @@ moz_gtk_get_widget_border(GtkThemeWidget
                           gboolean inhtml)
 {
     GtkWidget* w;
     GtkStyleContext* style;
     *left = *top = *right = *bottom = 0;
 
     switch (widget) {
     case MOZ_GTK_BUTTON:
+    case MOZ_GTK_TOOLBAR_BUTTON:
         {
             ensure_button_widget();
             style = gtk_widget_get_style_context(gButtonWidget);
 
             *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gButtonWidget));
 
             /* Don't add this padding in HTML, otherwise the buttons will
                become too big and stuff the layout. */
             if (!inhtml) {
+                if (widget == MOZ_GTK_TOOLBAR_BUTTON) {
+                    gtk_style_context_save(style);
+                    gtk_style_context_add_class(style, "image-button");
+                }
+              
                 moz_gtk_add_style_padding(style, left, top, right, bottom);
+                
+                if (widget == MOZ_GTK_TOOLBAR_BUTTON)
+                    gtk_style_context_restore(style);
             }
 
             moz_gtk_add_style_border(style, left, top, right, bottom);
             return MOZ_GTK_SUCCESS;
         }
     case MOZ_GTK_ENTRY:
         {
             ensure_entry_widget();
@@ -3104,16 +3113,17 @@ moz_gtk_widget_paint(GtkThemeWidgetType 
                      GtkTextDirection direction)
 {
     /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086
      */
     cairo_new_path(cr);
 
     switch (widget) {
     case MOZ_GTK_BUTTON:
+    case MOZ_GTK_TOOLBAR_BUTTON:
         if (state->depressed) {
             ensure_toggle_button_widget();
             return moz_gtk_button_paint(cr, rect, state,
                                         (GtkReliefStyle) flags,
                                         gToggleButtonWidget, direction);
         }
         ensure_button_widget();
         return moz_gtk_button_paint(cr, rect, state,
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -84,16 +84,18 @@ typedef gint (*style_prop_t)(GtkStyle*, 
 /*** checkbox/radio flags ***/
 #define MOZ_GTK_WIDGET_CHECKED 1
 #define MOZ_GTK_WIDGET_INCONSISTENT (1 << 1)
 
 /*** widget type constants ***/
 typedef enum {
   /* Paints a GtkButton. flags is a GtkReliefStyle. */
   MOZ_GTK_BUTTON,
+  /* Paints a button with image and no text */
+  MOZ_GTK_TOOLBAR_BUTTON,
   /* Paints a GtkCheckButton. flags is a boolean, 1=checked, 0=not checked. */
   MOZ_GTK_CHECKBUTTON,
   /* Paints a GtkRadioButton. flags is a boolean, 1=checked, 0=not checked. */
   MOZ_GTK_RADIOBUTTON,
   /**
    * Paints the button of a GtkScrollbar. flags is a GtkArrowType giving
    * the arrow direction.
    */
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -386,21 +386,25 @@ nsNativeThemeGTK::GetGtkWidgetAndState(u
           *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused);
         }
       }
     }
   }
 
   switch (aWidgetType) {
   case NS_THEME_BUTTON:
+    if (aWidgetFlags)
+      *aWidgetFlags = GTK_RELIEF_NORMAL;
+    aGtkWidgetType = MOZ_GTK_BUTTON;
+    break;
   case NS_THEME_TOOLBAR_BUTTON:
   case NS_THEME_TOOLBAR_DUAL_BUTTON:
     if (aWidgetFlags)
-      *aWidgetFlags = (aWidgetType == NS_THEME_BUTTON) ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE;
-    aGtkWidgetType = MOZ_GTK_BUTTON;
+      *aWidgetFlags = GTK_RELIEF_NONE;
+    aGtkWidgetType = MOZ_GTK_TOOLBAR_BUTTON;
     break;
   case NS_THEME_FOCUS_OUTLINE:
     aGtkWidgetType = MOZ_GTK_ENTRY;
     break;
   case NS_THEME_CHECKBOX:
   case NS_THEME_RADIO:
     aGtkWidgetType = (aWidgetType == NS_THEME_RADIO) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON;
     break;