Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 26 Oct 2012 21:22:40 -0400
changeset 111594 f9acc2e4d4e352e380a36ab0fc91f27b6d8fff0a
parent 111593 64c0911707c7b77983209130e41c0dbc5ddc9c27 (current diff)
parent 111555 82ce45ddd3ead1f3b1bda5d61f8169f0d79cdbce (diff)
child 111595 f5aea4c70febd1475a9187f549c26844f867c3a2
child 111617 9219a719028dd4578e18b042e7c0e560cf575a4c
child 112021 aab5d51780473287dbb04b17335321f910640cb5
push id17145
push userryanvm@gmail.com
push dateSat, 27 Oct 2012 02:11:51 +0000
treeherdermozilla-inbound@466970462253 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone19.0a1
first release with
nightly linux32
f9acc2e4d4e3 / 19.0a1 / 20121027030611 / files
nightly linux64
f9acc2e4d4e3 / 19.0a1 / 20121027030611 / files
nightly mac
f9acc2e4d4e3 / 19.0a1 / 20121027030611 / files
nightly win32
f9acc2e4d4e3 / 19.0a1 / 20121027030611 / files
nightly win64
f9acc2e4d4e3 / 19.0a1 / 20121027030611 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge the last PGO-green inbound changeset to m-c.
browser/app/profile/firefox.js
dom/identity/tests/head_identity.js
dom/identity/tests/test_identity_idp_auth_basics.html
dom/identity/tests/test_identity_idp_prov_basics.html
dom/identity/tests/test_identity_rp_basics.html
layout/style/test/test_rem_unit.html
toolkit/components/passwordmgr/test/authenticate.sjs
toolkit/identity/tests/mochitest/head_identity.js
toolkit/identity/tests/mochitest/test_authentication.html
toolkit/identity/tests/mochitest/test_provisioning.html
toolkit/identity/tests/mochitest/test_relying_party.html
--- a/accessible/src/base/nsTextEquivUtils.cpp
+++ b/accessible/src/base/nsTextEquivUtils.cpp
@@ -27,17 +27,17 @@ nsTextEquivUtils::GetNameFromSubtree(Acc
                                      nsAString& aName)
 {
   aName.Truncate();
 
   if (gInitiatorAcc)
     return NS_OK;
 
   gInitiatorAcc = aAccessible;
-  if (IsNameFromSubtreeAllowed(aAccessible)) {
+  if (GetRoleRule(aAccessible->Role()) == eFromSubtree) {
     //XXX: is it necessary to care the accessible is not a document?
     if (aAccessible->IsContent()) {
       nsAutoString name;
       AppendFromAccessibleChildren(aAccessible, &name);
       name.CompressWhitespace();
       if (!IsWhitespaceString(name))
         aName = name;
     }
--- a/accessible/src/base/nsTextEquivUtils.h
+++ b/accessible/src/base/nsTextEquivUtils.h
@@ -84,24 +84,16 @@ public:
    * node or html:br) and appends it to the given string.
    *
    * @param aContent       [in] the text content
    * @param aString        [in, out] the string
    */
   static nsresult AppendTextEquivFromTextContent(nsIContent *aContent,
                                                  nsAString *aString);
 
-  /**
-   * Return true if the given accessible allows name from subtree.
-   */
-  static bool IsNameFromSubtreeAllowed(Accessible* aAccessible)
-  {
-    return GetRoleRule(aAccessible->Role()) == eFromSubtree;
-  }
-
 private:
   /**
    * Iterates accessible children and calculates text equivalent from each
    * child.
    */
   static nsresult AppendFromAccessibleChildren(Accessible* aAccessible,
                                                nsAString *aString);
   
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -1249,18 +1249,18 @@ Accessible::NativeAttributes()
 
   // Expose checkable object attribute if the accessible has checkable state
   if (State() & states::CHECKABLE) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::checkable,
                            NS_LITERAL_STRING("true"));
   }
 
   // Expose 'explicit-name' attribute.
-  if (!nsTextEquivUtils::IsNameFromSubtreeAllowed(this) ||
-      Name(unused) != eNameFromSubtree) {
+  nsAutoString name;
+  if (Name(name) != eNameFromSubtree && !name.IsVoid()) {
     attributes->SetStringProperty(NS_LITERAL_CSTRING("explicit-name"),
                                   NS_LITERAL_STRING("true"), unused);
   }
 
   // Group attributes (level/setsize/posinset)
   GroupPos groupPos = GroupPosition();
   nsAccUtils::SetAccGroupAttrs(attributes, groupPos.level,
                                groupPos.setSize, groupPos.posInSet);
--- a/accessible/tests/mochitest/name/markup.js
+++ b/accessible/tests/mochitest/name/markup.js
@@ -53,16 +53,20 @@ var gTestIterator =
     if (this.markupIdx == -1) {
       this.markupIdx++;
       testNamesForMarkup(this.markupElms[this.markupIdx]);
       return;
     }
 
     this.ruleIdx++;
     if (this.ruleIdx == this.ruleElms.length) {
+      // When test is finished then name is empty and no explict-name.
+      testName(this.elm, null, "No name test. ");
+      testAbsentAttrs(this.elm, {"explicit-name" : "true"});
+
       this.markupIdx++;
       if (this.markupIdx == this.markupElms.length) {
         SimpleTest.finish();
         return;
       }
 
       this.ruleIdx = -1;
 
--- a/accessible/tests/mochitest/name/markuprules.xml
+++ b/accessible/tests/mochitest/name/markuprules.xml
@@ -197,17 +197,16 @@
                      aria-labelledby="l1 l2"
                      label="test4"
                      title="test5"
                      a11yname="option1">option1</html:option>
         <html:option>option2</html:option>
       </html:select>
     </markup>
 
-<!-- Temporarily disabled for causing bug 733848
     <markup ref="html:img" ruleset="htmlimage">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:img id="img"
                 aria-label="Logo of Mozilla"
                 aria-labelledby="l1 l2"
                 alt="Mozilla logo"
                 title="This is a logo"
@@ -219,17 +218,16 @@
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:img id="img"
                  aria-label="Logo of Mozilla"
                  aria-labelledby="l1 l2"
                  title="This is a logo"
                  alt=""
                  src="../moz.png"/>
     </markup>
--->
 
     <markup ref="html:table/html:tr/html:td" ruleset="htmlelm"
             id="markup4test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="tc" a11yname="test4">test4</html:label>
       <html:table>
         <html:tr>
@@ -285,11 +283,11 @@
                   summary="summary_tst6"
                   title="title_tst6">
         <html:caption a11yname="caption_tst6">caption_tst6</html:caption><html:tr>
           <html:td>cell1</html:td>
           <html:td>cell2</html:td>
         </html:tr>
       </html:table>
     </markup>
+
   </rulesample>
-
 </rules>
new file mode 100644
--- /dev/null
+++ b/b2g/chrome/content/identity.js
@@ -0,0 +1,186 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+/* 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/. */
+
+// This JS shim contains the callbacks to fire DOMRequest events for
+// navigator.pay API within the payment processor's scope.
+
+"use strict";
+
+let { classes: Cc, interfaces: Ci, utils: Cu }  = Components;
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsIMessageSender");
+
+XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
+                                   "@mozilla.org/uuid-generator;1",
+                                   "nsIUUIDGenerator");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Logger",
+                                  "resource://gre/modules/identity/LogUtils.jsm");
+
+function log(...aMessageArgs) {
+  Logger.log.apply(Logger, ["injected identity.js"].concat(aMessageArgs));
+}
+
+log("\n\n======================= identity.js =======================\n\n");
+
+// This script may be injected more than once into an iframe.
+// Ensure we don't redefine contstants
+if (typeof kIdentityJSLoaded === 'undefined') {
+  const kReceivedIdentityAssertion = "received-id-assertion";
+  const kIdentityDelegateWatch = "identity-delegate-watch";
+  const kIdentityDelegateRequest = "identity-delegate-request";
+  const kIdentityDelegateLogout = "identity-delegate-logout";
+  const kIdentityDelegateReady = "identity-delegate-ready";
+  const kIdentityDelegateFinished = "identity-delegate-finished";
+  const kIdentityControllerDoMethod = "identity-controller-doMethod";
+  const kIdentktyJSLoaded = true;
+}
+
+var showUI = false;
+var options = null;
+var isLoaded = false;
+var func = null;
+
+/*
+ * Message back to the SignInToWebsite pipe.  Message should be an
+ * object with the following keys:
+ *
+ *   method:             one of 'login', 'logout', 'ready'
+ *   assertion:          optional assertion
+ */
+function identityCall(message) {
+  sendAsyncMessage(kIdentityControllerDoMethod, message);
+}
+
+function identityFinished() {
+  log("identity finished.  closing dialog");
+  closeIdentityDialog(function notifySuccess() {
+    // get ready for next call with a reinit
+    func = null; options = null;
+
+    sendAsyncMessage(kIdentityDelegateFinished);
+  });
+}
+
+/*
+ * Notify the UI to close the dialog and return to the caller application
+ */
+function closeIdentityDialog(aCallback) {
+  let randomId = uuidgen.generateUUID().toString();
+  let id = kReceivedIdentityAssertion + "-" + randomId;
+  let browser = Services.wm.getMostRecentWindow("navigator:browser");
+
+  let detail = {
+    type: kReceivedIdentityAssertion,
+    id: id,
+    showUI: showUI
+  };
+
+  // In order to avoid race conditions, we wait for the UI to notify that
+  // it has successfully closed the identity flow and has recovered the
+  // caller app, before notifying the parent process.
+  content.addEventListener("mozContentEvent", function closeIdentityDialogFinished(evt) {
+    content.removeEventListener("mozContentEvent", closeIdentityDialogFinished);
+
+    if (evt.detail.id == id && aCallback) {
+      aCallback();
+    }
+  });
+
+  browser.shell.sendChromeEvent(detail);
+}
+
+/*
+ * doInternalWatch - call the internal.watch api and relay the results
+ * up to the controller.
+ */
+function doInternalWatch() {
+  log("doInternalWatch:", options, isLoaded);
+  if (options && isLoaded) {
+    log("internal watch options:", options);
+    let BrowserID = content.wrappedJSObject.BrowserID;
+    BrowserID.internal.watch(function(aParams) {
+        log("sending watch method message:", aParams.method);
+        identityCall(aParams);
+        if (aParams.method === "ready") {
+          log("watch finished.");
+          identityFinished();
+        }
+      },
+      JSON.stringify({loggedInUser: options.loggedInUser, origin: options.origin}),
+      function(...things) {
+        log("internal: ", things);
+      }
+    );
+  }
+}
+
+function doInternalRequest() {
+  log("doInternalRequest:", options && isLoaded);
+  if (options && isLoaded) {
+    content.wrappedJSObject.BrowserID.internal.get(
+      options.origin,
+      function(assertion) {
+        if (assertion) {
+          log("request -> assertion, so do login");
+          identityCall({method:'login',assertion:assertion});
+        }
+        identityFinished();
+      },
+      options);
+  }
+}
+
+function doInternalLogout(aOptions) {
+  log("doInternalLogout:", (options && isLoaded));
+  if (options && isLoaded) {
+    let BrowserID = content.wrappedJSObject.BrowserID;
+    log("logging you out of ", options.origin);
+    BrowserID.internal.logout(options.origin, function() {
+      identityCall({method:'logout'});
+      identityFinished();
+    });
+  }
+}
+
+addEventListener("DOMContentLoaded", function(e) {
+  content.addEventListener("load", function(e) {
+    isLoaded = true;
+     // bring da func
+     if (func) func();
+  });
+});
+
+// listen for request
+addMessageListener(kIdentityDelegateRequest, function(aMessage) {
+    log("\n\n* * * * injected identity.js received", kIdentityDelegateRequest, "\n\n\n");
+  options = aMessage.json;
+  showUI = true;
+  func = doInternalRequest;
+  func();
+});
+
+// listen for watch
+addMessageListener(kIdentityDelegateWatch, function(aMessage) {
+    log("\n\n* * * * injected identity.js received", kIdentityDelegateWatch, "\n\n\n");
+  options = aMessage.json;
+  showUI = false;
+  func = doInternalWatch;
+  func();
+});
+
+// listen for logout
+addMessageListener(kIdentityDelegateLogout, function(aMessage) {
+    log("\n\n* * * * injected identity.js received", kIdentityDelegateLogout, "\n\n\n");
+  options = aMessage.json;
+  showUI = false;
+  func = doInternalLogout;
+  func();
+});
new file mode 100644
--- /dev/null
+++ b/b2g/components/SignInToWebsite.jsm
@@ -0,0 +1,352 @@
+/* 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/. */
+
+/*
+ * SignInToWebsite.jsm - UX Controller and means for accessing identity
+ * cookies on behalf of relying parties.
+ *
+ * Currently, the b2g security architecture isolates web applications
+ * so that each window has access only to a local cookie jar:
+ *
+ *     To prevent Web apps from interfering with one another, each one is
+ *     hosted on a separate domain, and therefore may only access the
+ *     resources associated with its domain. These resources include
+ *     things such as IndexedDB databases, cookies, offline storage,
+ *     and so forth.
+ *
+ *     -- https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/Security/Security_model
+ *
+ * As a result, an authentication system like Persona cannot share its
+ * cookie jar with multiple relying parties, and so would require a
+ * fresh login request in every window.  This would not be a good
+ * experience.
+ *
+ *
+ * In order for navigator.id.request() to maintain state in a single
+ * cookie jar, we cause all Persona interactions to take place in a
+ * gaia context that is launched by the system application, with the
+ * result that Persona has a single cookie jar that all Relying
+ * Parties can use.  Since of course those Relying Parties cannot
+ * reach into the system cookie jar, the Controller in this module
+ * provides a way to get messages and data to and fro between the
+ * Relying Party in its window context, and the Persona internal api
+ * in its context.
+ *
+ * On the Relying Party's side, say a web page invokes
+ * navigator.id.watch(), to register callbacks, and then
+ * navigator.id.request() to request an assertion.  The navigator.id
+ * calls are provided by nsDOMIdentity.  nsDOMIdentity messages down
+ * to the privileged DOMIdentity code (using cpmm and ppmm message
+ * managers).  DOMIdentity stores the state of Relying Party flows
+ * using an Identity service (MinimalIdentity.jsm), and emits messages
+ * requesting Persona functions (doWatch, doReady, doLogout).
+ *
+ * The Identity service sends these observer messages to the
+ * Controller in this module, which in turn triggers gaia to open a
+ * window to host the Persona js.  If user interaction is required,
+ * gaia will open the trusty UI.  If user interaction is not required,
+ * and we only need to get to Persona functions, gaia will open a
+ * hidden iframe.  In either case, a window is opened into which the
+ * controller causes the script identity.js to be injected.  This
+ * script provides the glue between the in-page javascript and the
+ * pipe back down to the Controller, translating navigator.internal
+ * function callbacks into messages sent back to the Controller.
+ *
+ * As a result, a navigator.internal function in the hosted popup or
+ * iframe can call back to the injected identity.js (doReady, doLogin,
+ * or doLogout).  identity.js callbacks send messages back through the
+ * pipe to the Controller.  The controller invokes the corresponding
+ * function on the Identity Service (doReady, doLogin, or doLogout).
+ * The IdentityService calls the corresponding callback for the
+ * correct Relying Party, which causes DOMIdentity to send a message
+ * up to the Relying Party through nsDOMIdentity
+ * (Identity:RP:Watch:OnLogin etc.), and finally, nsDOMIdentity
+ * receives these messages and calls the original callback that the
+ * Relying Party registered (navigator.id.watch(),
+ * navigator.id.request(), or navigator.id.logout()).
+ */
+
+"use strict";
+
+const EXPORTED_SYMBOLS = ["SignInToWebsiteController"];
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
+                                  "resource://gre/modules/identity/MinimalIdentity.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Logger",
+                                  "resource://gre/modules/identity/LogUtils.jsm");
+
+// JS shim that contains the callback functions that
+// live within the identity UI provisioning frame.
+const kIdentityShimFile = "chrome://browser/content/identity.js";
+
+// Type of MozChromeEvents to handle id dialogs.
+const kOpenIdentityDialog = "open-id-dialog";
+const kCloseIdentityDialog = "close-id-dialog";
+
+// Observer messages to communicate to shim
+const kIdentityDelegateWatch = "identity-delegate-watch";
+const kIdentityDelegateRequest = "identity-delegate-request";
+const kIdentityDelegateLogout = "identity-delegate-logout";
+const kIdentityDelegateFinished = "identity-delegate-finished";
+const kIdentityDelegateReady = "identity-delegate-ready";
+
+const kIdentityControllerDoMethod = "identity-controller-doMethod";
+
+XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
+                                   "@mozilla.org/uuid-generator;1",
+                                   "nsIUUIDGenerator");
+
+function log(...aMessageArgs) {
+  Logger.log.apply(Logger, ["SignInToWebsiteController"].concat(aMessageArgs));
+}
+
+function getRandomId() {
+  return uuidgen.generateUUID().toString();
+}
+
+/*
+ * GaiaInterface encapsulates the our gaia functions.  There are only two:
+ *
+ * getContent       - return the current content window
+ * sendChromeEvent  - send a chromeEvent from the browser shell
+ */
+let GaiaInterface = {
+  _getBrowser: function SignInToWebsiteController__getBrowser() {
+    return Services.wm.getMostRecentWindow("navigator:browser");
+  },
+
+  getContent: function SignInToWebsiteController_getContent() {
+    return this._getBrowser().getContentWindow();
+  },
+
+  sendChromeEvent: function SignInToWebsiteController_sendChromeEvent(detail) {
+    this._getBrowser().shell.sendChromeEvent(detail);
+  }
+};
+
+/*
+ * The Pipe abstracts the communcation channel between the Controller
+ * and the identity.js code running in the browser window.
+ */
+let Pipe = {
+
+  /*
+   * communicate - launch a gaia window with certain options and
+   * provide a callback for handling messages.
+   *
+   * @param aRpOptions        options describing the Relying Party's
+   *        (dicitonary)      call, such as origin and loggedInEmail.
+   *
+   * @param aGaiaOptions      showUI:   boolean
+   *        (dictionary)      message:  name of the message to emit
+   *                                    (request, logout, watch)
+   *
+   * @param aMessageCallback  function to call on receipt of a
+   *        (function)        do-method message.  These messages name
+   *                          a method ('login', 'logout', etc.) and
+   *                          carry optional parameters.  The Pipe does
+   *                          not know what the messages mean; it is
+   *                          up to the caller to interpret them and
+   *                          act on them.
+   */
+  communicate: function(aRpOptions, aGaiaOptions, aMessageCallback) {
+    log("open gaia dialog with options:", aGaiaOptions);
+
+    // This content variable is injected into the scope of
+    // kIdentityShimFile, where it is used to access the BrowserID object
+    // and its internal API.
+    let content = GaiaInterface.getContent();
+
+    if (!content) {
+      log("ERROR: what the what? no content window?");
+      // aErrorCb.onresult("NO_CONTENT_WINDOW");
+      return;
+    }
+
+    // Prepare a message for gaia.  The parameter showUI signals
+    // whether user interaction is needed.  If it is, gaia will open a
+    // dialog; if not, a hidden iframe.  In each case, BrowserID is
+    // available in the context.
+    let id = kOpenIdentityDialog + "-" + getRandomId();
+    let detail = {
+      type: kOpenIdentityDialog,
+      showUI: aGaiaOptions.showUI || false,
+      id: id
+    };
+
+    // When gaia signals back with a mozContentEvent containing the
+    // unique id we created, we know the window is ready.  We then inject
+    // the magic javascript (kIdentityShimFile) that will give the content
+    // the superpowers it needs to communicate back with this code.
+    content.addEventListener("mozContentEvent", function getAssertion(evt) {
+
+      // Make sure the message is really for us
+      let msg = evt.detail;
+      if (msg.id != id) {
+        return;
+      }
+
+      // We only need to catch the first mozContentEvent from the
+      // iframe or popup, so we remove the listener right away.
+      content.removeEventListener("mozContentEvent", getAssertion);
+
+      // Try to load the identity shim file containing the callbacks
+      // in the content script.  This could be either the visible
+      // popup that the user interacts with, or it could be an invisible
+      // frame.
+      let frame = evt.detail.frame;
+      let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
+      let mm = frameLoader.messageManager;
+      try {
+        log("about to load frame script");
+        mm.loadFrameScript(kIdentityShimFile, true);
+      } catch (e) {
+        log("Error loading ", kIdentityShimFile, " as a frame script: ", e);
+      }
+
+      // There are two messages that the delegate can send back: a "do
+      // method" event, and a "finished" event.  We pass the do-method
+      // events straight to the caller for interpretation and handling.
+      // If we receive a "finished" event, then the delegate is done, so
+      // we shut down the pipe and clean up.
+      mm.addMessageListener(kIdentityControllerDoMethod, aMessageCallback);
+      mm.addMessageListener(kIdentityDelegateFinished, function identityDelegateFinished(message) {
+        mm.removeMessageListener(kIdentityDelegateFinished, identityDelegateFinished);
+        mm.removeMessageListener(kIdentityControllerDoMethod, aMessageCallback);
+      });
+
+      mm.sendAsyncMessage(aGaiaOptions.message, aRpOptions);
+    });
+
+    // Tell gaia to open the identity iframe or trusty popup
+    GaiaInterface.sendChromeEvent(detail);
+  }
+
+};
+
+/*
+ * The controller sits between the IdentityService used by DOMIdentity
+ * and a gaia process launches an (invisible) iframe or (visible)
+ * trusty UI.  Using an injected js script (identity.js), the
+ * controller enables the gaia window to access the persona identity
+ * storage in the system cookie jar and send events back via the
+ * controller into IdentityService and DOM, and ultimately up to the
+ * Relying Party, which is open in a different window context.
+ */
+let SignInToWebsiteController = {
+
+  /*
+   * Initialize the controller.  To use a different gaia communication pipe,
+   * such as when mocking it in tests, pass aOptions.pipe.
+   */
+  init: function SignInToWebsiteController_init(aOptions) {
+    aOptions = aOptions || {};
+    this.pipe = aOptions.pipe || Pipe;
+    Services.obs.addObserver(this, "identity-controller-watch", false);
+    Services.obs.addObserver(this, "identity-controller-request", false);
+    Services.obs.addObserver(this, "identity-controller-logout", false);
+  },
+
+  uninit: function SignInToWebsiteController_uninit() {
+    Services.obs.removeObserver(this, "identity-controller-watch");
+    Services.obs.removeObserver(this, "identity-controller-request");
+    Services.obs.removeObserver(this, "identity-controller-logout");
+  },
+
+  observe: function SignInToWebsiteController_observe(aSubject, aTopic, aData) {
+    log("observe: received", aTopic, "with", aData, "for", aSubject);
+    let options = null;
+    if (aSubject) {
+      options = aSubject.wrappedJSObject;
+    }
+    switch(aTopic) {
+      case "identity-controller-watch":
+        this.doWatch(options);
+        break;
+      case "identity-controller-request":
+        this.doRequest(options);
+        break;
+      case "identity-controller-logout":
+        this.doLogout(options);
+        break;
+      default:
+        Logger.reportError("SignInToWebsiteController", "Unknown observer notification:", aTopic);
+        break;
+    }
+  },
+
+  /*
+   * options:    method          required - name of method to invoke
+   *             assertion       optional
+   */
+  _makeDoMethodCallback: function SignInToWebsiteController__makeDoMethodCallback(aRpId) {
+    return function SignInToWebsiteController_methodCallback(aOptions) {
+      log("doMethod:", aOptions);
+      let message = aOptions.json;
+      if (typeof message === 'string') {
+        message = JSON.parse(message);
+      }
+      switch(message.method) {
+        case "ready":
+          IdentityService.doReady(aRpId);
+          break;
+
+        case "login":
+           IdentityService.doLogin(aRpId, message.assertion);
+          break;
+
+        case "logout":
+          IdentityService.doLogout(aRpId);
+          break;
+
+        default:
+          log("WARNING: wonky method call:", message.method);
+          break;
+      }
+    };
+  },
+
+  doWatch: function SignInToWebsiteController_doWatch(aOptions) {
+    // dom prevents watch from  being called twice
+    log("doWatch:", aOptions);
+    var gaiaOptions = {
+      message: kIdentityDelegateWatch,
+      showUI: false
+    };
+    this.pipe.communicate(aOptions, gaiaOptions, this._makeDoMethodCallback(aOptions.rpId));
+  },
+
+  /**
+   * The website is requesting login so the user must choose an identity to use.
+   */
+  doRequest: function SignInToWebsiteController_doRequest(aOptions) {
+    log("doRequest", aOptions);
+    // tell gaia to open the identity popup
+    var gaiaOptions = {
+      message: kIdentityDelegateRequest,
+      showUI: true
+    };
+    this.pipe.communicate(aOptions, gaiaOptions, this._makeDoMethodCallback(aOptions.rpId));
+  },
+
+  /*
+   *
+   */
+  doLogout: function SignInToWebsiteController_doLogout(aOptions) {
+    log("doLogout", aOptions);
+    var gaiaOptions = {
+      message: kIdentityDelegateLogout,
+      showUI: false
+    };
+    this.pipe.communicate(aOptions, gaiaOptions, this._makeDoMethodCallback(aOptions.rpId));
+  }
+
+};
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/unit/head_identity.js
@@ -0,0 +1,134 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+// The following boilerplate makes sure that XPCom calls
+// that use the profile directory work.
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
+                                  "resource://gre/modules/identity/MinimalIdentity.jsm",
+                                  "IdentityService");
+
+XPCOMUtils.defineLazyModuleGetter(this,
+                                  "Logger",
+                                  "resource://gre/modules/identity/LogUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this,
+                                   "uuidGenerator",
+                                   "@mozilla.org/uuid-generator;1",
+                                   "nsIUUIDGenerator");
+
+const TEST_URL = "https://myfavoriteflan.com";
+const TEST_USER = "uumellmahaye1969@hotmail.com";
+const TEST_PRIVKEY = "i-am-a-secret";
+const TEST_CERT = "i~like~pie";
+
+// The following are utility functions for Identity testing
+
+function log(...aMessageArgs) {
+  Logger.log.apply(Logger, ["test"].concat(aMessageArgs));
+}
+
+function partial(fn) {
+  let args = Array.prototype.slice.call(arguments, 1);
+  return function() {
+    return fn.apply(this, args.concat(Array.prototype.slice.call(arguments)));
+  };
+}
+
+function uuid() {
+  return uuidGenerator.generateUUID().toString();
+}
+
+// create a mock "doc" object, which the Identity Service
+// uses as a pointer back into the doc object
+function mockDoc(aIdentity, aOrigin, aDoFunc) {
+  let mockedDoc = {};
+  mockedDoc.id = uuid();
+  mockedDoc.loggedInEmail = aIdentity;
+  mockedDoc.origin = aOrigin;
+  mockedDoc['do'] = aDoFunc;
+  mockedDoc.doReady = partial(aDoFunc, 'ready');
+  mockedDoc.doLogin = partial(aDoFunc, 'login');
+  mockedDoc.doLogout = partial(aDoFunc, 'logout');
+  mockedDoc.doError = partial(aDoFunc, 'error');
+  mockedDoc.doCancel = partial(aDoFunc, 'cancel');
+  mockedDoc.doCoffee = partial(aDoFunc, 'coffee');
+
+  return mockedDoc;
+}
+
+// create a mock "pipe" object that would normally communicate
+// messages up to gaia (either the trusty ui or the hidden iframe),
+// and convey messages back down from gaia to the controller through
+// the message callback.
+function mockPipe() {
+  let MockedPipe = {
+    communicate: function(aRpOptions, aGaiaOptions, aMessageCallback) {
+      switch (aGaiaOptions.message) {
+        case "identity-delegate-watch":
+          aMessageCallback({json: {method: "ready"}});
+          break;
+        case "identity-delegate-request":
+          aMessageCallback({json: {method: "login", assertion: TEST_CERT}});
+          break;
+        case "identity-delegate-logout":
+          aMessageCallback({json: {method: "logout"}});
+          break;
+        default:
+          throw("what the what?? " + aGaiaOptions.message);
+          break;
+      }
+    }
+  };
+  return MockedPipe;
+}
+
+// mimicking callback funtionality for ease of testing
+// this observer auto-removes itself after the observe function
+// is called, so this is meant to observe only ONE event.
+function makeObserver(aObserveTopic, aObserveFunc) {
+  let observer = {
+    // nsISupports provides type management in C++
+    // nsIObserver is to be an observer
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
+
+    observe: function (aSubject, aTopic, aData) {
+      if (aTopic == aObserveTopic) {
+        Services.obs.removeObserver(observer, aObserveTopic);
+        aObserveFunc(aSubject, aTopic, aData);
+      }
+    }
+  };
+
+  Services.obs.addObserver(observer, aObserveTopic, false);
+}
+
+// a hook to set up the ID service with an identity with keypair and all
+// when ready, invoke callback with the identity.  It's there if we need it.
+function setup_test_identity(identity, cert, cb) {
+  cb();
+}
+
+// takes a list of functions and returns a function that
+// when called the first time, calls the first func,
+// then the next time the second, etc.
+function call_sequentially() {
+  let numCalls = 0;
+  let funcs = arguments;
+
+  return function() {
+    if (!funcs[numCalls]) {
+      let argString = Array.prototype.slice.call(arguments).join(",");
+      do_throw("Too many calls: " + argString);
+      return;
+    }
+    funcs[numCalls].apply(funcs[numCalls],arguments);
+    numCalls += 1;
+  };
+}
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/unit/test_signintowebsite.js
@@ -0,0 +1,162 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests for b2g/components/SignInToWebsite.jsm
+
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
+                                  "resource://gre/modules/identity/MinimalIdentity.jsm",
+                                  "IdentityService");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteController",
+                                  "resource://gre/modules/SignInToWebsite.jsm",
+                                  "SignInToWebsiteController");
+
+Cu.import("resource://gre/modules/identity/LogUtils.jsm");
+
+function log(...aMessageArgs) {
+  Logger.log.apply(Logger, ["test_signintowebsite"].concat(aMessageArgs));
+}
+
+function test_overall() {
+  do_check_neq(MinimalIDService, null);
+  run_next_test();
+}
+
+function test_mock_doc() {
+  do_test_pending();
+  let mockedDoc = mockDoc(null, TEST_URL, function(action, params) {
+    do_check_eq(action, 'coffee');
+    do_test_finished();
+    run_next_test();
+  });
+
+  // A smoke test to ensure that mockedDoc is functioning correctly.
+  // There is presently no doCoffee method in Persona.
+  mockedDoc.doCoffee();
+}
+
+function test_watch() {
+  do_test_pending();
+
+  setup_test_identity("pie@food.gov", TEST_CERT, function() {
+    let controller = SignInToWebsiteController;
+
+    let mockedDoc = mockDoc(null, TEST_URL, function(action, params) {
+      do_check_eq(action, 'ready');
+      controller.uninit();
+      do_test_finished();
+      run_next_test();
+    });
+
+    controller.init({pipe: mockPipe()});
+
+    MinimalIDService.RP.watch(mockedDoc, {});
+  });
+}
+
+function test_request_login() {
+  do_test_pending();
+
+  setup_test_identity("flan@food.gov", TEST_CERT, function() {
+    let controller = SignInToWebsiteController;
+
+    let mockedDoc = mockDoc(null, TEST_URL, call_sequentially(
+      function(action, params) {
+        do_check_eq(action, 'ready');
+        do_check_eq(params, undefined);
+      },
+      function(action, params) {
+        do_check_eq(action, 'login');
+        do_check_eq(params, TEST_CERT);
+        controller.uninit();
+        do_test_finished();
+        run_next_test();
+      }
+    ));
+
+    controller.init({pipe: mockPipe()});
+    MinimalIDService.RP.watch(mockedDoc, {});
+    MinimalIDService.RP.request(mockedDoc.id, {});
+  });
+}
+
+function test_request_logout() {
+  do_test_pending();
+
+  setup_test_identity("flan@food.gov", TEST_CERT, function() {
+    let controller = SignInToWebsiteController;
+
+    let mockedDoc = mockDoc(null, TEST_URL, call_sequentially(
+      function(action, params) {
+        do_check_eq(action, 'ready');
+        do_check_eq(params, undefined);
+      },
+      function(action, params) {
+        do_check_eq(action, 'logout');
+        do_check_eq(params, undefined);
+        controller.uninit();
+        do_test_finished();
+        run_next_test();
+      }
+    ));
+
+    controller.init({pipe: mockPipe()});
+    MinimalIDService.RP.watch(mockedDoc, {});
+    MinimalIDService.RP.logout(mockedDoc.id, {});
+  });
+}
+
+function test_request_login_logout() {
+  do_test_pending();
+
+  setup_test_identity("unagi@food.gov", TEST_CERT, function() {
+    let controller = SignInToWebsiteController;
+
+    let mockedDoc = mockDoc(null, TEST_URL, call_sequentially(
+      function(action, params) {
+        do_check_eq(action, 'ready');
+        do_check_eq(params, undefined);
+      },
+      function(action, params) {
+        do_check_eq(action, 'login');
+        do_check_eq(params, TEST_CERT);
+      },
+      function(action, params) {
+        do_check_eq(action, 'logout');
+        do_check_eq(params, undefined);
+        controller.uninit();
+        do_test_finished();
+        run_next_test();
+      }
+      /*
+      ,function(action, params) {
+        do_check_eq(action, 'ready');
+        do_test_finished();
+        run_next_test();
+      }
+       */
+    ));
+
+    controller.init({pipe: mockPipe()});
+    MinimalIDService.RP.watch(mockedDoc, {});
+    MinimalIDService.RP.request(mockedDoc.id, {});
+    MinimalIDService.RP.logout(mockedDoc.id, {});
+  });
+}
+
+let TESTS = [
+  test_overall,
+  test_mock_doc,
+  test_watch,
+  test_request_login,
+  test_request_logout,
+  test_request_login_logout
+];
+
+TESTS.forEach(add_test);
+
+function run_test() {
+  run_next_test();
+}
--- a/b2g/components/test/unit/xpcshell.ini
+++ b/b2g/components/test/unit/xpcshell.ini
@@ -1,5 +1,11 @@
 [DEFAULT]
 head =
 tail =
 
 [test_bug793310.js]
+
+[test_signintowebsite.js]
+head = head_identity.js
+tail =
+
+
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -220,16 +220,17 @@
 @BINPATH@/components/filepicker.xpt
 #endif
 @BINPATH@/components/find.xpt
 @BINPATH@/components/fuel.xpt
 @BINPATH@/components/gfx.xpt
 @BINPATH@/components/hal.xpt
 @BINPATH@/components/html5.xpt
 @BINPATH@/components/htmlparser.xpt
+@BINPATH@/components/identity.xpt
 @BINPATH@/components/imglib2.xpt
 @BINPATH@/components/imgicon.xpt
 @BINPATH@/components/inspector.xpt
 @BINPATH@/components/intl.xpt
 @BINPATH@/components/jar.xpt
 @BINPATH@/components/jetpack.xpt
 @BINPATH@/components/jsdebugger.xpt
 @BINPATH@/components/jsdservice.xpt
@@ -487,16 +488,20 @@
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
 @BINPATH@/components/Webapps.js
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
 
+@BINPATH@/components/nsDOMIdentity.js
+@BINPATH@/components/nsIDService.js
+@BINPATH@/components/Identity.manifest
+
 @BINPATH@/components/SystemMessageInternal.js
 @BINPATH@/components/SystemMessageManager.js
 @BINPATH@/components/SystemMessageManager.manifest
 
 @BINPATH@/components/Activities.manifest
 @BINPATH@/components/ActivityOptions.js
 @BINPATH@/components/ActivityProxy.js
 @BINPATH@/components/ActivityRequestHandler.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -324,16 +324,19 @@ pref("browser.download.manager.scanWhenD
 pref("browser.download.manager.resumeOnWakeDelay", 10000);
 
 // This allows disabling the Downloads Panel in favor of the old interface.
 pref("browser.download.useToolkitUI", false);
 
 // This controls retention behavior in the Downloads Panel only.
 pref("browser.download.panel.removeFinishedDownloads", false);
 
+// This records whether or not the panel has been shown at least once.
+pref("browser.download.panel.shown", false);
+
 // This records whether or not at least one session with the Downloads Panel
 // enabled has been completed already.
 pref("browser.download.panel.firstSessionCompleted", false);
 
 // search engines URL
 pref("browser.search.searchEnginesURL",      "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");
 
 // pointer to the default engine name
--- a/browser/components/downloads/src/DownloadsCommon.jsm
+++ b/browser/components/downloads/src/DownloadsCommon.jsm
@@ -646,20 +646,30 @@ const DownloadsData = {
   onStateChange: function () { },
 
   onSecurityChange: function () { },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Notifications sent to the most recent browser window only
 
   /**
-   * Set to true after the first download in the session caused the downloads
-   * panel to be displayed.
+   * Set to true after the first download causes the downloads panel to be
+   * displayed.
    */
-  firstDownloadShown: false,
+  get panelHasShownBefore() {
+    try {
+      return Services.prefs.getBoolPref("browser.download.panel.shown");
+    } catch (ex) { }
+    return false;
+  },
+
+  set panelHasShownBefore(aValue) {
+    Services.prefs.setBoolPref("browser.download.panel.shown", aValue);
+    return aValue;
+  },
 
   /**
    * Displays a new download notification in the most recent browser window, if
    * one is currently available.
    */
   _notifyNewDownload: function DD_notifyNewDownload()
   {
     if (DownloadsCommon.useToolkitUI) {
@@ -668,24 +678,24 @@ const DownloadsData = {
 
     // Show the panel in the most recent browser window, if present.
     let browserWin = gBrowserGlue.getMostRecentBrowserWindow();
     if (!browserWin) {
       return;
     }
 
     browserWin.focus();
-    if (this.firstDownloadShown) {
-      // For new downloads after the first one in the session, don't show the
-      // panel automatically, but provide a visible notification in the topmost
+    if (this.panelHasShownBefore) {
+      // For new downloads after the first one, don't show the panel
+      // automatically, but provide a visible notification in the topmost
       // browser window, if the status indicator is already visible.
       browserWin.DownloadsIndicatorView.showEventNotification();
       return;
     }
-    this.firstDownloadShown = true;
+    this.panelHasShownBefore = true;
     browserWin.DownloadsPanel.showPanel();
   }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsDataItem
 
 /**
--- a/browser/components/downloads/test/browser/Makefile.in
+++ b/browser/components/downloads/test/browser/Makefile.in
@@ -7,12 +7,13 @@ srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 relativesrcdir = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_BROWSER_FILES = \
   browser_basic_functionality.js \
+  browser_first_download_panel.js \
   head.js \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure the downloads panel only opens automatically on the first
+ * download it notices. All subsequent downloads, even across sessions, should
+ * not open the panel automatically.
+ */
+function gen_test()
+{
+  try {
+    // Ensure that state is reset in case previous tests didn't finish.
+    for (let yy in gen_resetState()) yield;
+
+    // With this set to false, we should automatically open the panel
+    // the first time a download is started.
+    DownloadsCommon.data.panelHasShownBefore = false;
+
+    prepareForPanelOpen();
+    DownloadsCommon.data._notifyNewDownload();
+    yield;
+
+    // If we got here, that means the panel opened.
+    DownloadsPanel.hidePanel();
+
+    ok(DownloadsCommon.data.panelHasShownBefore,
+       "Should have recorded that the panel was opened on a download.")
+
+    // Next, make sure that if we start another download, we don't open
+    // the panel automatically.
+    panelShouldNotOpen();
+    DownloadsCommon.data._notifyNewDownload();
+    yield waitFor(2);
+  } catch(e) {
+    ok(false, e);
+  } finally {
+    // Clean up when the test finishes.
+    for (let yy in gen_resetState()) yield;
+  }
+}
+
+/**
+ * Call this to record a test failure for the next time the downloads panel
+ * opens.
+ */
+function panelShouldNotOpen()
+{
+  // Hook to wait until the test data has been loaded.
+  let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
+  DownloadsPanel.onViewLoadCompleted = function () {
+    DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
+    ok(false, "Should not have opened the downloads panel.");
+  };
+}
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -7,17 +7,18 @@
  * Provides infrastructure for automated download components tests.
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
-
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
+                                  "resource:///modules/DownloadsCommon.jsm");
 const nsIDM = Ci.nsIDownloadManager;
 
 let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
 gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
 registerCleanupFunction(function () {
   gTestTargetFile.remove(false);
 });
 
@@ -145,19 +146,23 @@ function gen_resetState()
         testRunner.continueTest();
       }
     });
     yield;
   } finally {
     statement.finalize();
   }
 
+  // Reset any prefs that might have been changed.
+  Services.prefs.clearUserPref("browser.download.panel.shown");
+
   // Ensure that the panel is closed and data is unloaded.
   DownloadsCommon.data.clear();
   DownloadsCommon.data._loadState = DownloadsCommon.data.kLoadNone;
+  DownloadsPanel.hidePanel();
 
   // Wait for focus on the main window.
   waitForFocus(testRunner.continueTest);
   yield;
 }
 
 function gen_addDownloadRows(aDataRows)
 {
@@ -222,8 +227,46 @@ function gen_openPanel(aData)
   // Wait for focus on the main window.
   waitForFocus(testRunner.continueTest);
   yield;
 
   // Open the downloads panel, waiting until loading is completed.
   DownloadsPanel.showPanel();
   yield;
 }
+
+/**
+ * Spin the event loop for aSeconds seconds, and then signal the test to
+ * continue.
+ *
+ * @param aSeconds the number of seconds to wait.
+ * @note This helper should _only_ be used when there's no valid event to
+ *       listen to and one can't be made.
+ */
+function waitFor(aSeconds)
+{
+  setTimeout(function() {
+    testRunner.continueTest();
+  }, aSeconds * 1000);
+}
+
+/**
+ * Make it so that the next time the downloads panel opens, we signal to
+ * continue the test. This function needs to be called each time you want
+ * to wait for the panel to open.
+ *
+ * Example usage:
+ *
+ * prepareForPanelOpen();
+ * // Do something to open the panel
+ * yield;
+ * // We can assume the panel is open now.
+ */
+function prepareForPanelOpen()
+{
+  // Hook to wait until the test data has been loaded.
+  let originalOnPopupShown = DownloadsPanel.onPopupShown;
+  DownloadsPanel.onPopupShown = function (aEvent) {
+    DownloadsPanel.onPopupShown = originalOnPopupShown;
+    DownloadsPanel.onPopupShown.apply(this, [aEvent]);
+    testRunner.continueTest();
+  };
+}
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -476,21 +476,29 @@ nsFrameLoader::ReallyStartLoadingInterna
   loadInfo->SetOwner(mOwnerContent->NodePrincipal());
 
   nsCOMPtr<nsIURI> referrer;
   rv = mOwnerContent->NodePrincipal()->GetURI(getter_AddRefs(referrer));
   NS_ENSURE_SUCCESS(rv, rv);
 
   loadInfo->SetReferrer(referrer);
 
+  // Default flags:
+  int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
+
+  // Flags for browser frame:
+  if (OwnerIsBrowserFrame()) {
+    flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
+            nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
+  }
+
   // Kick off the load...
   bool tmpState = mNeedsAsyncDestroy;
   mNeedsAsyncDestroy = true;
-  rv = mDocShell->LoadURI(mURIToLoad, loadInfo,
-                          nsIWebNavigation::LOAD_FLAGS_NONE, false);
+  rv = mDocShell->LoadURI(mURIToLoad, loadInfo, flags, false);
   mNeedsAsyncDestroy = tmpState;
   mURIToLoad = nullptr;
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -23,17 +23,16 @@
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif
 #include "nsBindingManager.h"
 #include "nsGenericHTMLElement.h"
 #ifdef MOZ_MEDIA
 #include "nsHTMLMediaElement.h"
 #endif // MOZ_MEDIA
-#include "jsgc.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsObjectLoadingContent.h"
 #include "nsDOMMutationObserver.h"
 
 using namespace mozilla::dom;
 
 // This macro expects the ownerDocument of content_ to be in scope as
 // |nsIDocument* doc|
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -580,17 +580,17 @@ MediaStreamGraphImpl::UpdateConsumptionS
 void
 MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
                                           GraphTime aDesiredUpToTime,
                                           bool* aEnsureNextIteration)
 {
   bool finished;
   {
     MutexAutoLock lock(aStream->mMutex);
-    if (aStream->mPullEnabled) {
+    if (aStream->mPullEnabled && !aStream->mFinished) {
       for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
         MediaStreamListener* l = aStream->mListeners[j];
         {
           // Compute how much stream time we'll need assuming we don't block
           // the stream at all between mBlockingDecisionsMadeUntilTime and
           // aDesiredUpToTime.
           StreamTime t =
             GraphTimeToStreamTime(aStream, mStateComputedTime) +
--- a/dom/apps/src/PermissionsInstaller.jsm
+++ b/dom/apps/src/PermissionsInstaller.jsm
@@ -77,37 +77,41 @@ const PermissionsTable = { "resource-loc
                            "network-events": {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            contacts: {
                              app: DENY_ACTION,
                              privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
                            },
                            "device-storage:apps": {
                              app: DENY_ACTION,
                              privileged: ALLOW_ACTION,
                              certified: ALLOW_ACTION
                            },
                            "device-storage:pictures": {
                              app: DENY_ACTION,
                              privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
                            },
                            "device-storage:videos": {
                              app: DENY_ACTION,
                              privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
                            },
                            "device-storage:music": {
                              app: DENY_ACTION,
                              privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
                            },
                            sms: {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            telephony: {
                              app: DENY_ACTION,
@@ -147,17 +151,18 @@ const PermissionsTable = { "resource-loc
                            push: {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            settings: {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write"]
                            },
                            permissions: {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            fmradio: {
                              app: ALLOW_ACTION,     // Matrix indicates '?'
@@ -262,23 +267,28 @@ for (let permName in PermissionsTable) {
  * @param string aAccess
  * @returns Array
  **/
 function expandPermissions(aPermName, aAccess) {
   if (!PermissionsTable[aPermName]) {
     Cu.reportError("PermissionsTable.jsm: expandPermissions: Unknown Permission: " + aPermName);
     throw new Error("PermissionsTable.jsm: expandPermissions: Unknown Permission: " + aPermName);
   }
+
+/* 
+Temporarily disabled in order to add access fields to gaia: See Bug 805646
   if (!aAccess && PermissionsTable[aPermName].access ||
       aAccess && !PermissionsTable[aPermName].access) {
     Cu.reportError("PermissionsTable.jsm: expandPermissions: Invalid Manifest : " +
                    aPermName + " " + aAccess + "\n");
     throw new Error("PermissionsTable.jsm: expandPermissions: Invalid Manifest");
   }
-  if (!PermissionsTable[aPermName].access) {
+*/
+
+  if (!PermissionsTable[aPermName].access || !aAccess) {
     return [aPermName];
   }
 
   let requestedSuffixes = [];
   switch(aAccess) {
   case READONLY:
     requestedSuffixes.push("read");
     break;
@@ -292,17 +302,17 @@ function expandPermissions(aPermName, aA
     requestedSuffixes.push("read", "create", "write");
     break;
   default:
     return [];
   }
 
   let permArr = mapSuffixes(aPermName, requestedSuffixes);
 
-  let expandedPerms = [];
+  let expandedPerms = [aPermName];
   for (let idx in permArr) {
     if (PermissionsTable[aPermName].access.indexOf(requestedSuffixes[idx]) != -1) {
       expandedPerms.push(permArr[idx]);
     }
   }
   return expandedPerms;
 }
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -410,16 +410,33 @@ nsDOMWindowUtils::SetResolution(float aX
   }
 
   nsIPresShell* presShell = GetPresShell();
   return presShell ? presShell->SetResolution(aXResolution, aYResolution)
                    : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetResolution(float* aXResolution, float* aYResolution)
+{
+  if (!nsContentUtils::IsCallerChrome()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsIPresShell* presShell = GetPresShell();
+
+  if (presShell) {
+    *aXResolution = presShell->GetXResolution();
+    *aYResolution = presShell->GetYResolution();
+    return NS_OK;
+  }
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::SetIsFirstPaint(bool aIsFirstPaint)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsIPresShell* presShell = GetPresShell();
   if (presShell) {
@@ -489,38 +506,39 @@ NS_IMETHODIMP
 nsDOMWindowUtils::SendMouseEvent(const nsAString& aType,
                                  float aX,
                                  float aY,
                                  int32_t aButton,
                                  int32_t aClickCount,
                                  int32_t aModifiers,
                                  bool aIgnoreRootScrollFrame,
                                  float aPressure,
-                                 unsigned short aInputSourceArg)
+                                 unsigned short aInputSourceArg,
+                                 bool *aPreventDefault)
 {
   return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
                               aIgnoreRootScrollFrame, aPressure,
-                              aInputSourceArg, false);
+                              aInputSourceArg, false, aPreventDefault);
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType,
                                          float aX,
                                          float aY,
                                          int32_t aButton,
                                          int32_t aClickCount,
                                          int32_t aModifiers,
                                          bool aIgnoreRootScrollFrame,
                                          float aPressure,
                                          unsigned short aInputSourceArg)
 {
   SAMPLE_LABEL("nsDOMWindowUtils", "SendMouseEventToWindow");
   return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
                               aIgnoreRootScrollFrame, aPressure,
-                              aInputSourceArg, true);
+                              aInputSourceArg, true, nullptr);
 }
 
 static nsIntPoint
 ToWidgetPoint(float aX, float aY, const nsPoint& aOffset,
               nsPresContext* aPresContext)
 {
   double appPerDev = aPresContext->AppUnitsPerDevPixel();
   nscoord appPerCSS = nsPresContext::AppUnitsPerCSSPixel();
@@ -533,17 +551,18 @@ nsDOMWindowUtils::SendMouseEventCommon(c
                                        float aX,
                                        float aY,
                                        int32_t aButton,
                                        int32_t aClickCount,
                                        int32_t aModifiers,
                                        bool aIgnoreRootScrollFrame,
                                        float aPressure,
                                        unsigned short aInputSourceArg,
-                                       bool aToWindow)
+                                       bool aToWindow,
+                                       bool *aPreventDefault)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   // get the widget to send the event to
   nsPoint offset;
   nsCOMPtr<nsIWidget> widget = GetWidget(&offset);
@@ -560,17 +579,19 @@ nsDOMWindowUtils::SendMouseEventCommon(c
     msg = NS_MOUSE_MOVE;
   else if (aType.EqualsLiteral("mouseover"))
     msg = NS_MOUSE_ENTER;
   else if (aType.EqualsLiteral("mouseout"))
     msg = NS_MOUSE_EXIT;
   else if (aType.EqualsLiteral("contextmenu")) {
     msg = NS_CONTEXTMENU;
     contextMenuKey = (aButton == 0);
-  } else
+  } else if (aType.EqualsLiteral("MozMouseHittest"))
+    msg = NS_MOUSE_MOZHITTEST;
+  else
     return NS_ERROR_FAILURE;
 
   if (aInputSourceArg == nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN) {
     aInputSourceArg = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
   }
 
   nsMouseEvent event(true, msg, widget, nsMouseEvent::eReal,
                      contextMenuKey ?
@@ -601,17 +622,20 @@ nsDOMWindowUtils::SendMouseEventCommon(c
       return NS_ERROR_FAILURE;
     nsIView* view = viewManager->GetRootView();
     if (!view)
       return NS_ERROR_FAILURE;
 
     status = nsEventStatus_eIgnore;
     return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
   }
-  return widget->DispatchEvent(&event, status);
+  nsresult rv = widget->DispatchEvent(&event, status);
+  *aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
+
+  return rv;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendWheelEvent(float aX,
                                  float aY,
                                  double aDeltaX,
                                  double aDeltaY,
                                  double aDeltaZ,
--- a/dom/base/nsDOMWindowUtils.h
+++ b/dom/base/nsDOMWindowUtils.h
@@ -39,12 +39,13 @@ protected:
                                   float aX,
                                   float aY,
                                   int32_t aButton,
                                   int32_t aClickCount,
                                   int32_t aModifiers,
                                   bool aIgnoreRootScrollFrame,
                                   float aPressure,
                                   unsigned short aInputSourceArg,
-                                  bool aToWindow);
+                                  bool aToWindow,
+                                  bool *aPreventDefault);
 
   static mozilla::widget::Modifiers GetWidgetModifiers(int32_t aModifiers);
 };
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -235,16 +235,18 @@ BluetoothHfpManager::BluetoothHfpManager
   sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
   sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
   sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD;
 }
 
 bool
 BluetoothHfpManager::Init()
 {
+  mSocketStatus = GetConnectionStatus();
+
   sHfpObserver = new BluetoothHfpManagerObserver();
   if (!sHfpObserver->Init()) {
     NS_WARNING("Cannot set up Hfp Observers!");
   }
 
   mListener = new BluetoothRilListener();
   if (!mListener->StartListening()) {
     NS_WARNING("Failed to start listening RIL");
@@ -316,24 +318,26 @@ void
 BluetoothHfpManager::NotifySettings()
 {
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
   type.AssignLiteral("bluetooth-hfp-status-changed");
 
   name.AssignLiteral("connected");
-  uint32_t status = GetConnectionStatus();
-  v = status;
+  if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED) {
+    v = true;
+  } else {
+    v = false;
+  }
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   name.AssignLiteral("address");
-  nsString address;
-  GetSocketAddr(address);
-  v = address;
+  GetSocketAddr(mDevicePath);
+  v = mDevicePath;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   if (!BroadcastSystemMessage(type, parameters)) {
     NS_WARNING("Failed to broadcast system message to dialer");
     return;
   }
 }
 
@@ -610,29 +614,31 @@ BluetoothHfpManager::Listen()
     return false;
   }
 
   nsresult rv = bs->ListenSocketViaService(BluetoothReservedChannels::HANDSFREE_AG,
                                            BluetoothSocketType::RFCOMM,
                                            true,
                                            false,
                                            this);
+
+  mSocketStatus = GetConnectionStatus();
+
   return NS_FAILED(rv) ? false : true;
 }
 
 void
 BluetoothHfpManager::Disconnect()
 {
   if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_DISCONNECTED) {
     NS_WARNING("BluetoothHfpManager has disconnected!");
     return;
   }
 
   CloseSocket();
-  Listen();
 }
 
 bool
 BluetoothHfpManager::SendLine(const char* aMessage)
 {
   const char* kHfpCrlf = "\xd\xa";
   nsAutoCString msg;
 
@@ -830,35 +836,44 @@ BluetoothHfpManager::CallStateChanged(in
   }
 
   SetupCIND(aCallIndex, aCallState, false);
 }
 
 void
 BluetoothHfpManager::OnConnectSuccess()
 {
+  // Cache device path for NotifySettings() since we can't get socket address
+  // when a headset disconnect with us
+  GetSocketAddr(mDevicePath);
+
   if (mCurrentCallState == nsIRadioInterfaceLayer::CALL_STATE_CONNECTED ||
       mCurrentCallState == nsIRadioInterfaceLayer::CALL_STATE_DIALING ||
       mCurrentCallState == nsIRadioInterfaceLayer::CALL_STATE_ALERTING) {
-    nsString address;
-    GetSocketAddr(address);
-    OpenScoSocket(address);
+    OpenScoSocket(mDevicePath);
   }
 
+  mSocketStatus = GetConnectionStatus();
+
   NotifySettings();
 }
 
 void
 BluetoothHfpManager::OnConnectError()
 {
   CloseSocket();
+  mSocketStatus = GetConnectionStatus();
   // If connecting for some reason didn't work, restart listening
   Listen();
 }
 
 void
 BluetoothHfpManager::OnDisconnect()
 {
-  NotifySettings();
+  if (mSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
+    Listen();
+    NotifySettings();
+  }
+
   sCINDItems[CINDType::CALL].value = CallState::NO_CALL;
   sCINDItems[CINDType::CALLSETUP].value = CallSetupState::NO_CALLSETUP;
   sCINDItems[CINDType::CALLHELD].value = CallHeldState::NO_CALLHELD;
 }
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -49,14 +49,16 @@ private:
   virtual void OnConnectSuccess() MOZ_OVERRIDE;
   virtual void OnConnectError() MOZ_OVERRIDE;
   virtual void OnDisconnect() MOZ_OVERRIDE;
 
   int mCurrentVgs;
   int mCurrentCallIndex;
   int mCurrentCallState;
   bool mReceiveVgsFlag;
+  nsString mDevicePath;
+  enum mozilla::ipc::SocketConnectionStatus mSocketStatus;
   nsAutoPtr<BluetoothRilListener> mListener;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/BluetoothScoManager.cpp
+++ b/dom/bluetooth/BluetoothScoManager.cpp
@@ -119,16 +119,18 @@ BluetoothScoManagerObserver::Observe(nsI
 
 BluetoothScoManager::BluetoothScoManager()
 {
 }
 
 bool
 BluetoothScoManager::Init()
 {
+  mSocketStatus = GetConnectionStatus();
+
   sScoObserver = new BluetoothScoManagerObserver();
   if (sScoObserver->Init()) {
     NS_WARNING("Cannot set up SCO observers!");
   }
   return true;
 }
 
 BluetoothScoManager::~BluetoothScoManager()
@@ -200,52 +202,91 @@ BluetoothScoManager::Connect(const nsASt
     return false;
   }
 
   if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_CONNECTED) {
     NS_WARNING("Sco socket has been connected");
     return false;
   }
 
+  CloseSocket();
+
   BluetoothService* bs = BluetoothService::Get();
   if (!bs) {
     NS_WARNING("BluetoothService not available!");
     return false;
   }
 
   nsresult rv = bs->GetScoSocket(aDeviceAddress,
                                  true,
                                  false,
                                  this);
 
   return NS_FAILED(rv) ? false : true;
 }
 
+bool
+BluetoothScoManager::Listen()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (gInShutdown) {
+    MOZ_ASSERT(false, "Listen called while in shutdown!");
+    return false;
+  }
+
+  CloseSocket();
+
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return false;
+  }
+
+  nsresult rv = bs->ListenSocketViaService(-1,
+                                           BluetoothSocketType::SCO,
+                                           true,
+                                           false,
+                                           this);
+
+  mSocketStatus = GetConnectionStatus();
+
+  return NS_FAILED(rv) ? false : true;
+}
+
 void
 BluetoothScoManager::Disconnect()
 {
   if (GetConnectionStatus() == SocketConnectionStatus::SOCKET_DISCONNECTED) {
     return;
   }
 
   CloseSocket();
 }
 
 void
 BluetoothScoManager::OnConnectSuccess()
 {
   nsString address;
   GetSocketAddr(address);
   NotifyAudioManager(address);
+
+  mSocketStatus = GetConnectionStatus();
 }
 
 void
 BluetoothScoManager::OnConnectError()
 {
   CloseSocket();
+  mSocketStatus = GetConnectionStatus();
+  Listen();
 }
 
 void
 BluetoothScoManager::OnDisconnect()
 {
-  nsString address = NS_LITERAL_STRING("");
-  NotifyAudioManager(address);
+  if (mSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
+    Listen();
+
+    nsString address = NS_LITERAL_STRING("");
+    NotifyAudioManager(address);
+  }
 }
--- a/dom/bluetooth/BluetoothScoManager.h
+++ b/dom/bluetooth/BluetoothScoManager.h
@@ -22,24 +22,27 @@ public:
   ~BluetoothScoManager();
 
   static BluetoothScoManager* Get();
   void ReceiveSocketData(mozilla::ipc::UnixSocketRawData* aMessage)
     MOZ_OVERRIDE;
 
   bool Connect(const nsAString& aDeviceObjectPath);
   void Disconnect();
+  bool Listen();
 
 private:
   friend class BluetoothScoManagerObserver;
   BluetoothScoManager();
   bool Init();
   void Cleanup();
   nsresult HandleShutdown();
   void NotifyAudioManager(const nsAString& aAddress);
   virtual void OnConnectSuccess() MOZ_OVERRIDE;
   virtual void OnConnectError() MOZ_OVERRIDE;
   virtual void OnDisconnect() MOZ_OVERRIDE;
+
+  int mSocketStatus;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -716,24 +716,32 @@ BluetoothDBusService::AddReservedService
 }
 
 class PrepareProfileManagersRunnable : public nsRunnable
 {
 public:
   NS_IMETHOD
   Run()
   {
-    BluetoothHfpManager* h = BluetoothHfpManager::Get();
-    if (h) {
-      h->Listen();
+    BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
+    if (!hfp || !hfp->Listen()) {
+      NS_WARNING("Failed to start listening for BluetoothHfpManager!");
+      return NS_ERROR_FAILURE;
+    }
+
+    BluetoothScoManager* sco = BluetoothScoManager::Get();
+    if (!sco || !sco->Listen()) {
+      NS_WARNING("Failed to start listening for BluetoothScoManager!");
+      return NS_ERROR_FAILURE;
     }
 
     BluetoothOppManager* opp = BluetoothOppManager::Get();
-    if (opp) {
-      opp->Listen();
+    if (!opp || !opp->Listen()) {
+      NS_WARNING("Failed to start listening for BluetoothOppManager!");
+      return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 };
 
 class ShutdownProfileManagersRunnable : public nsRunnable
 {
--- a/dom/browser-element/mochitest/Makefile.in
+++ b/dom/browser-element/mochitest/Makefile.in
@@ -139,16 +139,19 @@ MOCHITEST_FILES = \
 		browserElement_DOMRequestError.js \
 		test_browserElement_inproc_DOMRequestError.html \
 		file_browserElement_AppFramePermission.html \
 		browserElement_AppFramePermission.js \
 		test_browserElement_inproc_AppFramePermission.html \
 		file_wyciwyg.html \
 		browserElement_ExposableURI.js \
 		test_browserElement_inproc_ExposableURI.html \
+		browserElement_FrameWrongURI.js \
+		test_browserElement_inproc_FrameWrongURI.html \
+		file_browserElement_FrameWrongURI.html \
 		$(NULL)
 
 # Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=774100
 #		test_browserElement_inproc_Reload.html \
 
 # OOP tests don't work on Windows (bug 763081) or native-fennec (bug
 # 774939).
 #
@@ -203,14 +206,15 @@ MOCHITEST_FILES += \
 		test_browserElement_oop_ContextmenuEvents.html \
 		test_browserElement_oop_SendEvent.html \
 		test_browserElement_oop_ScrollEvent.html \
 		test_browserElement_oop_Auth.html \
 		test_browserElement_oop_RemoveBrowserElement.html \
 		test_browserElement_oop_DOMRequestError.html \
 		test_browserElement_oop_AppFramePermission.html \
 		test_browserElement_oop_ExposableURI.html \
+		test_browserElement_oop_FrameWrongURI.html \
 		$(NULL)
 endif #}
 endif #}
 
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_FrameWrongURI.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 804446 - Test that window.open(javascript:..) works with <iframe mozbrowser>.
+
+"use strict";
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+  browserElementTestHelpers.setEnabledPref(true);
+  browserElementTestHelpers.addPermission();
+
+  var iframeJS = document.createElement('iframe');
+  iframeJS.mozbrowser = true;
+
+  iframeJS.addEventListener('mozbrowserloadstart', function(e) {
+    ok(false, "This should not happen!");
+  });
+
+  iframeJS.addEventListener('mozbrowserloadend', function(e) {
+    ok(false, "This should not happen!");
+  });
+
+  iframeJS.src = 'javascript:alert("Foo");';
+  document.body.appendChild(iframeJS);
+
+  var iframe = document.createElement('iframe');
+  iframe.mozbrowser = true;
+
+  var gotPopup = false;
+  iframe.addEventListener('mozbrowseropenwindow', function(e) {
+    is(gotPopup, false, 'Should get just one popup.');
+    gotPopup = true;
+
+    document.body.appendChild(e.detail.frameElement);
+  });
+
+  iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
+    ok(gotPopup, 'Got mozbrowseropenwindow event before showmodalprompt event.');
+    if (e.detail.message.indexOf("success") == 0) {
+      ok(true, e.detail.message);
+      SimpleTest.finish();
+    }
+    else {
+      ok(false, "Got invalid message: " + e.detail.message);
+    }
+  });
+
+  iframe.src = 'file_browserElement_FrameWrongURI.html';
+  document.body.appendChild(iframe);
+}
+
+runTest();
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_FrameWrongURI.html
@@ -0,0 +1,5 @@
+<script>
+  function testSucceeded() { alert("success"); }
+  function callback() { return "<script>opener.testSucceeded()</" + "script>"; }
+  var w = window.open("javascript:opener.callback();");
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_FrameWrongURI.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test of browser element.</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_FrameWrongURI.js">
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_FrameWrongURI.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test of browser element.</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_FrameWrongURI.js">
+</script>
+</body>
+</html>
--- a/dom/camera/GonkRecorderProfiles.cpp
+++ b/dom/camera/GonkRecorderProfiles.cpp
@@ -180,17 +180,17 @@ GonkRecorderVideoProfile::~GonkRecorderV
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 }
 
 GonkRecorderProfileManager::GonkRecorderProfileManager(uint32_t aCameraId)
   : RecorderProfileManager(aCameraId)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-  mMaxQualityIndex = CAMCORDER_QUALITY_LIST_END - CAMCORDER_QUALITY_LIST_START;
+  mMaxQualityIndex = sizeof(ProfileList) / sizeof(ProfileList[0]) - 1;
 }
 
 GonkRecorderProfileManager::~GonkRecorderProfileManager()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 }
 
 bool
--- a/dom/identity/DOMIdentity.jsm
+++ b/dom/identity/DOMIdentity.jsm
@@ -8,22 +8,30 @@ const {classes: Cc, interfaces: Ci, util
 
 // This is the parent process corresponding to nsDOMIdentity.
 let EXPORTED_SYMBOLS = ["DOMIdentity"];
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
+#ifdef MOZ_B2G_VERSION
+                                  "resource://gre/modules/identity/MinimalIdentity.jsm");
+#else
                                   "resource://gre/modules/identity/Identity.jsm");
+#endif
 
 XPCOMUtils.defineLazyModuleGetter(this,
                                   "Logger",
                                   "resource://gre/modules/identity/LogUtils.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
+                                   "@mozilla.org/parentprocessmessagemanager;1",
+                                   "nsIMessageListenerManager");
+
 function log(...aMessageArgs) {
   Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
 }
 
 function IDDOMMessage(aID) {
   this.id = aID;
 }
 
@@ -117,19 +125,17 @@ RPWatchContext.prototype = {
 
 let DOMIdentity = {
   // nsIMessageListener
   receiveMessage: function DOMIdentity_receiveMessage(aMessage) {
     let msg = aMessage.json;
 
     // Target is the frame message manager that called us and is
     // used to send replies back to the proper window.
-    let targetMM = aMessage.target
-                           .QueryInterface(Ci.nsIFrameLoaderOwner)
-                           .frameLoader.messageManager;
+    let targetMM = aMessage.target;
 
     switch (aMessage.name) {
       // RP
       case "Identity:RP:Watch":
         this._watch(msg, targetMM);
         break;
       case "Identity:RP:Request":
         this._request(msg);
@@ -160,53 +166,50 @@ let DOMIdentity = {
         this._authenticationFailure(msg);
         break;
     }
   },
 
   // nsIObserver
   observe: function DOMIdentity_observe(aSubject, aTopic, aData) {
     switch (aTopic) {
-      case "domwindowopened":
-      case "domwindowclosed":
-        let win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindow);
-        this._configureMessages(win, aTopic == "domwindowopened");
-        break;
-
       case "xpcom-shutdown":
+        this._unsubscribeListeners();
+        Services.obs.removeObserver(this, "xpcom-shutdown");
         Services.ww.unregisterNotification(this);
-        Services.obs.removeObserver(this, "xpcom-shutdown");
         break;
     }
   },
 
   messages: ["Identity:RP:Watch", "Identity:RP:Request", "Identity:RP:Logout",
              "Identity:IDP:BeginProvisioning", "Identity:IDP:ProvisioningFailure",
              "Identity:IDP:RegisterCertificate", "Identity:IDP:GenKeyPair",
              "Identity:IDP:BeginAuthentication",
              "Identity:IDP:CompleteAuthentication",
              "Identity:IDP:AuthenticationFailure"],
 
   // Private.
   _init: function DOMIdentity__init() {
     Services.ww.registerNotification(this);
     Services.obs.addObserver(this, "xpcom-shutdown", false);
+    this._subscribeListeners();
   },
 
-  _configureMessages: function DOMIdentity__configureMessages(aWindow, aRegister) {
-    if (!aWindow.messageManager)
-      return;
+  _subscribeListeners: function DOMIdentity__subscribeListeners() {
+    if (!ppmm) return;
+    for (let message of this.messages) {
+      ppmm.addMessageListener(message, this);
+    }
+  },
 
-    let func = aWindow.messageManager[aRegister ? "addMessageListener"
-                                                : "removeMessageListener"];
-
+  _unsubscribeListeners: function DOMIdentity__unsubscribeListeners() {
     for (let message of this.messages) {
-      func.call(aWindow.messageManager, message, this);
+      ppmm.removeMessageListener(message, this);
     }
+    ppmm = null;
   },
 
   _resetFrameState: function(aContext) {
     log("_resetFrameState: ", aContext.id);
     if (!aContext._mm) {
       throw new Error("ERROR: Trying to reset an invalid context");
     }
     let message = new IDDOMMessage(aContext.id);
--- a/dom/identity/Makefile.in
+++ b/dom/identity/Makefile.in
@@ -12,17 +12,20 @@ relativesrcdir   = @relativesrcdir@
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_COMPONENTS = \
     nsDOMIdentity.js \
     nsIDService.js \
     Identity.manifest \
     $(NULL)
 
+EXTRA_PP_JS_MODULES = \
+    DOMIdentity.jsm \
+    $(NULL)
+
 EXTRA_JS_MODULES = \
-    DOMIdentity.jsm \
     $(NULL)
 
 ifdef ENABLE_TESTS
 DIRS += tests
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/identity/nsDOMIdentity.js
+++ b/dom/identity/nsDOMIdentity.js
@@ -12,16 +12,20 @@ const PREF_ENABLED = "dom.identity.enabl
 // Maximum length of a string that will go through IPC
 const MAX_STRING_LENGTH = 2048;
 // Maximum number of times navigator.id.request can be called for a document
 const MAX_RP_CALLS = 100;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsIMessageSender");
+
 // This is the child process corresponding to nsIDOMIdentity.
 
 
 function nsDOMIdentity(aIdentityInternal) {
   this._identityInternal = aIdentityInternal;
 }
 nsDOMIdentity.prototype = {
   __exposedProps__: {
@@ -478,20 +482,17 @@ nsDOMIdentityInternal.prototype = {
 
     let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils);
     this._id = util.outerWindowID;
     this._innerWindowID = util.currentInnerWindowID;
 
     this._log("init was called from " + aWindow.document.location);
 
-    this._mm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIWebNavigation)
-                      .QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIContentFrameMessageManager);
+    this._mm = cpmm;
 
     // Setup listeners for messages from parent process.
     this._messages = [
       "Identity:ResetState",
       "Identity:RP:Watch:OnLogin",
       "Identity:RP:Watch:OnLogout",
       "Identity:RP:Watch:OnReady",
       "Identity:RP:Request:OnCancel",
--- a/dom/identity/tests/Makefile.in
+++ b/dom/identity/tests/Makefile.in
@@ -6,16 +6,11 @@ DEPTH            = @DEPTH@
 topsrcdir        = @top_srcdir@
 srcdir           = @srcdir@
 VPATH            = @srcdir@
 
 relativesrcdir   = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MOCHITEST_FILES = \
-  head_identity.js \
-  test_identity_idp_auth_basics.html \
-  test_identity_idp_prov_basics.html \
-  test_identity_rp_basics.html \
-  $(NULL)
+# XXX bug 805602 update and restore mochitests
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/dom/identity/tests/head_identity.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-const Ci = SpecialPowers.Ci;
-const Cu = SpecialPowers.Cu;
-
-SpecialPowers.setBoolPref("toolkit.identity.debug", true);
-SpecialPowers.setBoolPref("dom.identity.enabled", true);
-
-const Services = Cu.import("resource://gre/modules/Services.jsm").Services;
-const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm")
-                      .DOMIdentity;
-
-let util = SpecialPowers.getDOMWindowUtils(window);
-let outerWinId = util.outerWindowID;
-
-const identity = navigator.id || navigator.mozId;
-
-let index = 0;
-
-// mimicking callback funtionality for ease of testing
-// this observer auto-removes itself after the observe function
-// is called, so this is meant to observe only ONE event.
-function makeObserver(aObserveTopic, aObserveFunc) {
-  function observe(aSubject, aTopic, aData) {
-    if (aTopic == aObserveTopic) {
-      aObserveFunc(aSubject, aTopic, aData);
-      Services.obs.removeObserver(this, aObserveTopic);
-    }
-  }
-
-  Services.obs.addObserver(observe, aObserveTopic, false);
-}
-
-function expectException(aFunc, msg, aErrorType="Error") {
-  info("Expecting an exception: " + msg);
-  msg = msg || "";
-  let caughtEx = null;
-  try {
-    aFunc();
-  } catch (ex) {
-    let exProto = Object.getPrototypeOf(ex);
-    // Don't count NS_* exceptions since they shouldn't be exposed to content
-    if (exProto.toString() == aErrorType
-        && ex.toString().indexOf("NS_ERROR_FAILURE") == -1) {
-      caughtEx = ex;
-    } else {
-      ok(false, ex);
-      return;
-    }
-  }
-  isnot(caughtEx, null, "Check for thrown exception.");
-}
-
-function next() {
-  if (!identity) {
-    todo(false, "DOM API is not available. Skipping tests.");
-    finish_tests();
-    return;
-  }
-  if (index >= steps.length) {
-    ok(false, "Shouldn't get here!");
-    return;
-  }
-  try {
-    let fn = steps[index];
-    info("Begin test " + index + " '" + steps[index].name + "'!");
-    fn();
-  } catch(ex) {
-    ok(false, "Caught exception", ex);
-  }
-  index += 1;
-}
-
-function finish_tests() {
-  info("all done");
-  SpecialPowers.clearUserPref("toolkit.identity.debug");
-  SpecialPowers.clearUserPref("dom.identity.enabled");
-  SimpleTest.finish();
-}
deleted file mode 100644
--- a/dom/identity/tests/test_identity_idp_auth_basics.html
+++ /dev/null
@@ -1,87 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Test for navigator.id identity provider (IDP) authentication basics</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript;version=1.8" src="head_identity.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<a target="_blank">navigator.id identity provider (IDP) authentication basics</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script type="application/javascript;version=1.8">
-
-"use strict"
-
-let steps = [
-  // completeAuthentication tests
-  function completeAuthenticationExists() {
-    is(typeof(identity.completeAuthentication), "function",
-       "Check completeAuthentication is a function");
-    SimpleTest.executeSoon(next);
-  },
-  function completeAuthenticationOutsideFlow() {
-    expectException(function() {
-      identity.completeAuthentication();
-    }, "Check completeAuthentication outside of an auth. flow");
-    SimpleTest.executeSoon(next);
-  },
-
-  // raiseAuthenticationFailure tests
-  function raiseAuthenticationFailureExists() {
-    is(typeof(identity.raiseAuthenticationFailure), "function",
-       "Check raiseAuthenticationFailure is a function");
-    SimpleTest.executeSoon(next);
-  },
-  function raiseAuthenticationFailureNoArgs() {
-    expectException(function() {
-      identity.raiseAuthenticationFailure();
-    }, "raiseAuthenticationFailure with no arguments");
-    SimpleTest.executeSoon(next);
-  },
-
-  // beginAuthentication tests
-  function beginAuthenticationExists() {
-    is(typeof(identity.beginAuthentication), "function",
-       "Check beginAuthentication is a function");
-    SimpleTest.executeSoon(next);
-  },
-  function beginAuthenticationNoArgs() {
-    expectException(function() {
-      identity.beginAuthentication();
-    }, "beginAuthentication with no arguments");
-    SimpleTest.executeSoon(next);
-  },
-  function beginAuthenticationInvalidArg() {
-    expectException(function() {
-      identity.beginAuthentication(999);
-    }, "beginAuthentication with a non-function argument");
-    SimpleTest.executeSoon(next);
-  },
-  function beginAuthenticationArgs() {
-    function beginAuthenticationCb() {
-      throw "beginAuthentication callback shouldn't have been called outside of an "
-              + "auth flow";
-    }
-    is(identity.beginAuthentication(beginAuthenticationCb), undefined,
-       "Check minimum beginAuthentication arguments");
-    SimpleTest.executeSoon(next);
-  },
-
-  finish_tests,
-];
-
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(next);
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/identity/tests/test_identity_idp_prov_basics.html
+++ /dev/null
@@ -1,160 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Test for navigator.id identity provider (IDP) provisioning basics</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript;version=1.8" src="head_identity.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<a target="_blank">navigator.id identity provider (IDP) provisioning basics</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script type="application/javascript;version=1.8">
-
-"use strict"
-
-let IDP = Cu.import("resource://gre/modules/identity/IdentityProvider.jsm").IdentityProvider;
-
-function setupProv() {
-  info("setupProv");
-  // Add a provisioning flow so the DOM calls succeed
-  IDP._provisionFlows[outerWinId] = {
-    sandbox: {},
-    callback: function doCallback(aErr) {
-      info("provisioning callback: " + aErr);
-    },
-  }
-}
-
-function resetAndNext() {
-  info("resetAndNext");
-  // reset DOM state for the next test
-  // Give the flow some time to cross the IPC boundary
-  setTimeout(function() {
-    let provContext = IDP._provisionFlows[outerWinId];
-    if (!provContext) {
-      SimpleTest.executeSoon(next);
-      return;
-    }
-    makeObserver("identity-DOM-state-reset", function() {
-      info("reset done");
-      SimpleTest.executeSoon(next);
-    });
-    DOMIdentity._resetFrameState(provContext.caller);
-  }, 700);
-}
-
-let steps = [
-  // genKeyPair tests
-  function genKeyPairExists() {
-    is(typeof(identity.genKeyPair), "function",
-       "Check genKeyPair is a function");
-    SimpleTest.executeSoon(next);
-  },
-  function genKeyPairOutsideProv() {
-    expectException(function(){
-      identity.genKeyPair(function(){});
-    }, "Check genKeyPair outside of a prov. flow");
-    SimpleTest.executeSoon(next);
-  },
-  function genKeyPairNoArgs() {
-    setupProv();
-    identity.beginProvisioning(function() {
-      expectException(function() {
-        identity.genKeyPair();
-      }, "genKeyPair with no arguments");
-      SimpleTest.executeSoon(resetAndNext);
-    });
-  },
-  function genKeyPairInvalidArg() {
-    setupProv();
-    identity.beginProvisioning(function() {
-      expectException(function() {
-        identity.genKeyPair(999);
-      }, "Check genKeyPair with non-function object argument");
-      SimpleTest.executeSoon(resetAndNext);
-    });
-  },
-
-  // registerCertificate tests
-  function registerCertificateExists() {
-    is(typeof(identity.registerCertificate), "function",
-       "Check registerCertificate is a function");
-    SimpleTest.executeSoon(next);
-  },
-  function registerCertificateNoArgs() {
-    setupProv();
-    identity.beginProvisioning(function() {
-      expectException(function() {
-        identity.registerCertificate();
-      }, "Check registerCertificate with no arguments");
-    });
-    SimpleTest.executeSoon(resetAndNext);
-  },
-  function registerCertificateOutsideProv() {
-    expectException(function(){
-      identity.registerCertificate("foo");
-    }, "Check registerCertificate outside of a prov. flow");
-    SimpleTest.executeSoon(next);
-  },
-
-  // raiseProvisioningFailure tests
-  function raiseProvisioningFailureExists() {
-    is(typeof(identity.raiseProvisioningFailure), "function",
-       "Check raiseProvisioningFailure is a function");
-    SimpleTest.executeSoon(next);
-  },
-  function raiseProvisioningFailureNoArgs() {
-    expectException(function() {
-      identity.raiseProvisioningFailure();
-    }, "raiseProvisioningFailure with no arguments");
-    SimpleTest.executeSoon(next);
-  },
-  function raiseProvisioningFailureWithReason() {
-    identity.raiseProvisioningFailure("my test reason");
-    SimpleTest.executeSoon(next);
-  },
-
-  // beginProvisioning tests
-  function beginProvisioningExists() {
-    is(typeof(identity.beginProvisioning), "function",
-       "Check beginProvisioning is a function");
-    SimpleTest.executeSoon(next);
-  },
-  function beginProvisioningNoArgs() {
-    expectException(function() {
-      identity.beginProvisioning();
-    }, "beginProvisioning with no arguments");
-    SimpleTest.executeSoon(next);
-  },
-  function beginProvisioningInvalidArg() {
-    expectException(function() {
-      identity.beginProvisioning(999);
-    }, "beginProvisioning with a non-function argument");
-    SimpleTest.executeSoon(next);
-  },
-  function beginProvisioningArgs() {
-    function beginProvisioningCb() {
-      SimpleTest.executeSoon(resetAndNext);
-    }
-    is(identity.beginProvisioning(beginProvisioningCb), undefined,
-       "Check minimum beginProvisioning arguments");
-  },
-
-  finish_tests,
-];
-
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(next);
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/identity/tests/test_identity_rp_basics.html
+++ /dev/null
@@ -1,177 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Test for navigator.id relying party (RP) basics</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript;version=1.8" src="head_identity.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<a target="_blank">navigator.id RP basics</a>
-<p id="display"></p>
-<div id="content">
-  <button id='request'>request</button>
-</div>
-<pre id="test">
-<script type="application/javascript;version=1.8">
-
-"use strict";
-
-const RP = Cu.import("resource://gre/modules/identity/RelyingParty.jsm").RelyingParty;
-
-function resetAndNext() {
-  // reset DOM state for the next test
-  makeObserver("identity-DOM-state-reset", function() {
-    SimpleTest.executeSoon(next);
-  });
-  // Give the flow some time to cross the IPC boundary
-  setTimeout(function() {
-    let rpContext = RP._rpFlows[outerWinId];
-    if (!rpContext) {
-      SimpleTest.executeSoon(next);
-      return;
-    }
-    DOMIdentity._resetFrameState(rpContext);
-  }, 700);
-}
-
-let steps = [
-  function nonExistentProp() {
-    is(identity.foobarbaz, undefined, "Check that foobarbaz does not exist");
-    expectException(function() {
-      identity.foobarbaz()
-    }, "Check for exception calling non-existent method", "TypeError");
-    SimpleTest.executeSoon(next);
-  },
-
-  // test request before watch throws an exception
-  function requestBeforeWatch() {
-    var button = document.getElementById('request');
-    button.addEventListener('click', function requestHandler() {
-      button.removeEventListener('click', requestHandler);
-
-      expectException(function() {
-        identity.request();
-      });
-    });
-    SimpleTest.executeSoon(next);
-  },
-
-  // watch tests
-  function watchExists() {
-    is(typeof(identity.watch), "function", "Check watch is a function");
-    SimpleTest.executeSoon(next);
-  },
-  function watchNoArgs() {
-    expectException(function() {
-      identity.watch();
-    }, "watch with no arguments");
-    SimpleTest.executeSoon(next);
-  },
-  function watchEmptyObj() {
-    expectException(function() {
-      identity.watch({});
-    }, "watch with empty object argument");
-    SimpleTest.executeSoon(next);
-  },
-  function watchOnLoginBool() {
-    expectException(function() {
-      identity.watch({onlogin: true});
-    }, "watch with invalid onlogin member");
-    SimpleTest.executeSoon(next);
-  },
-  function watchOnLoginLogoutBool() {
-    expectException(function() {
-      identity.watch({onlogin: true, onlogout: false});
-    }, "watch with invalid onlogin and onlogout members");
-    SimpleTest.executeSoon(next);
-  },
-  function watchMinimumArgs() {
-    function onLoginLogoutCb() {
-      throw "onlogin/onlogout callback shouldn't have been called";
-    }
-    is(identity.watch({onlogin: onLoginLogoutCb, onlogout: onLoginLogoutCb}),
-       undefined, "Check minimum watch argument members");
-    resetAndNext();
-  },
-  function watchOnReadyType() {
-    function onLoginLogoutCb() {
-      throw "onlogin/onlogout callback shouldn't have been called";
-    }
-    let options = {
-      onlogin: onLoginLogoutCb,
-      onlogout: onLoginLogoutCb,
-      onready: 999,
-    }
-    expectException(function() {
-      identity.watch(options)
-    }, "Check onready type");
-    resetAndNext();
-  },
-  function watchLoggedInEmailType() {
-    function onLoginLogoutCb() {
-      throw "onlogin/onlogout callback shouldn't have been called";
-    }
-    let options = {
-      onlogin: onLoginLogoutCb,
-      onlogout: onLoginLogoutCb,
-      loggedInEmail: {},
-    }
-    expectException(function() {
-      identity.watch(options)
-    }, "Check loggedInEmail type");
-    resetAndNext();
-  },
-  function watchOnReadyCalled() {
-    let onLogoutCalled = false;
-    let options = {
-      loggedInEmail: "loggedOut@user.com",
-      onlogin: function onLoginCb(assertion) {
-        throw "onlogin/onlogout callback shouldn't have been called";
-      },
-      onlogout: function onLogoutCb() {
-        is(arguments.length, 0, "Check onlogout argument length");
-        onLogoutCalled = true;
-      },
-      onready: function onReady() {
-        is(arguments.length, 0, "Check onready argument length");
-        ok(onLogoutCalled, "onlogout callback should be called before onready");
-        SimpleTest.executeSoon(next);
-      },
-    }
-    is(identity.watch(options), undefined, "Check onready is called");
-  },
-
-  // request tests
-  function requestExists() {
-    is(typeof(identity.request), "function", "Check request is a function");
-    SimpleTest.executeSoon(next);
-  },
-  function requestNoArgs() {
-    is(identity.request(), undefined, "Check request with no arguments");
-    SimpleTest.executeSoon(next);
-  },
-  function requestEmptyObj() {
-    is(identity.request({}), undefined, "Check request with empty object argument");
-    SimpleTest.executeSoon(next);
-  },
-
-  // logout tests
-  function logoutExists() {
-    is(typeof(identity.logout), "function", "Check logout is a function");
-    SimpleTest.executeSoon(next);
-  },
-
-  finish_tests,
-];
-
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(next);
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -35,17 +35,17 @@ interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 
-[scriptable, uuid(d0b58b1b-58fa-49a0-8e4d-a3693bc65ebd)]
+[scriptable, uuid(b3a3589d-cc9d-4123-9b21-51c66e88b436)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -169,16 +169,18 @@ interface nsIDOMWindowUtils : nsISupport
    *   // elsewhere
    *   browser.setViewportScale(2.0, 2.0);
    *
    * The caller of this method must have UniversalXPConnect
    * privileges.
    */
   void setResolution(in float aXResolution, in float aYResolution);
 
+  void getResolution(out float aXResolution, out float aYResolution);
+
   /**
    * Whether the next paint should be flagged as the first paint for a document.
    * This gives a way to track the next paint that occurs after the flag is
    * set. The flag gets cleared after the next paint.
    *
    * Can only be accessed with UniversalXPConnect privileges.
    */
   attribute boolean isFirstPaint;
@@ -197,17 +199,18 @@ interface nsIDOMWindowUtils : nsISupport
   const long MODIFIER_CAPSLOCK   = 0x0020;
   const long MODIFIER_FN         = 0x0040;
   const long MODIFIER_NUMLOCK    = 0x0080;
   const long MODIFIER_SCROLLLOCK = 0x0100;
   const long MODIFIER_SYMBOLLOCK = 0x0200;
   const long MODIFIER_OS         = 0x0400;
 
   /** Synthesize a mouse event. The event types supported are:
-   *    mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu
+   *    mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu,
+   *    MozMouseHitTest
    *
    * Events are sent in coordinates offset by aX and aY from the window.
    *
    * Note that additional events may be fired as a result of this call. For
    * instance, typically a click event will be fired as a result of a
    * mousedown and mouseup in sequence.
    *
    * Normally at this level of events, the mouseover and mouseout events are
@@ -229,26 +232,28 @@ interface nsIDOMWindowUtils : nsISupport
    * @param aButton button to synthesize
    * @param aClickCount number of clicks that have been performed
    * @param aModifiers modifiers pressed, using constants defined as MODIFIER_*
    * @param aIgnoreRootScrollFrame whether the event should ignore viewport bounds
    *                           during dispatch
    * @param aPressure touch input pressure: 0.0 -> 1.0
    * @param aInputSourceArg input source, see nsIDOMMouseEvent for values,
    *        defaults to mouse input.
+   *
+   * returns true if the page called prevent default on this event
    */
-  void sendMouseEvent(in AString aType,
-                      in float aX,
-                      in float aY,
-                      in long aButton,
-                      in long aClickCount,
-                      in long aModifiers,
-                      [optional] in boolean aIgnoreRootScrollFrame,
-                      [optional] in float aPressure,
-                      [optional] in unsigned short aInputSourceArg);
+  boolean sendMouseEvent(in AString aType,
+                         in float aX,
+                         in float aY,
+                         in long aButton,
+                         in long aClickCount,
+                         in long aModifiers,
+                         [optional] in boolean aIgnoreRootScrollFrame,
+                         [optional] in float aPressure,
+                         [optional] in unsigned short aInputSourceArg);
 
   /** Synthesize a touch event. The event types supported are:
    *    touchstart, touchend, touchmove, and touchcancel
    *
    * Events are sent in coordinates offset by aX and aY from the window.
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without UniversalXPConnect
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1049,17 +1049,18 @@ TabChild::IsRootContentDocument()
 
 bool
 TabChild::RecvLoadURL(const nsCString& uri)
 {
     printf("loading %s, %d\n", uri.get(), NS_IsMainThread());
     SetProcessNameToAppName();
 
     nsresult rv = mWebNav->LoadURI(NS_ConvertUTF8toUTF16(uri).get(),
-                                   nsIWebNavigation::LOAD_FLAGS_NONE,
+                                   nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
+                                   nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER,
                                    NULL, NULL, NULL);
     if (NS_FAILED(rv)) {
         NS_WARNING("mWebNav->LoadURI failed. Eating exception, what else can I do?");
     }
 
     return true;
 }
 
@@ -1270,18 +1271,19 @@ TabChild::RecvMouseEvent(const nsString&
                          const float&    aY,
                          const int32_t&  aButton,
                          const int32_t&  aClickCount,
                          const int32_t&  aModifiers,
                          const bool&     aIgnoreRootScrollFrame)
 {
   nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
   NS_ENSURE_TRUE(utils, true);
+  bool ignored = false;
   utils->SendMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers,
-                        aIgnoreRootScrollFrame, 0, 0);
+                        aIgnoreRootScrollFrame, 0, 0, &ignored);
   return true;
 }
 
 bool
 TabChild::RecvRealMouseEvent(const nsMouseEvent& event)
 {
   nsMouseEvent localEvent(event);
   DispatchWidgetEvent(localEvent);
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -163,23 +163,28 @@ static void OnDestroyImage(void* aPlugin
   nsPluginInstanceOwner* owner = static_cast<nsPluginInstanceOwner*>(aPluginInstanceOwner);
   NS_IF_RELEASE(owner);
 }
 #endif // XP_MACOSX
 
 already_AddRefed<ImageContainer>
 nsPluginInstanceOwner::GetImageContainer()
 {
+  if (!mInstance)
+    return nullptr;
+
+  nsRefPtr<ImageContainer> container;
+
 #if MOZ_WIDGET_ANDROID
   // Right now we only draw with Gecko layers on Honeycomb and higher. See Paint()
   // for what we do on other versions.
   if (AndroidBridge::Bridge()->GetAPIVersion() < 11)
     return NULL;
   
-  nsRefPtr<ImageContainer> container = LayerManager::CreateImageContainer();
+  container = LayerManager::CreateImageContainer();
 
   ImageFormat format = ImageFormat::SHARED_TEXTURE;
   nsRefPtr<Image> img = container->CreateImage(&format, 1);
 
   SharedTextureImage::Data data;
   data.mHandle = mInstance->CreateSharedHandle();
   data.mShareType = mozilla::gl::TextureImage::ThreadShared;
   data.mInverted = mInstance->Inverted();
@@ -195,34 +200,31 @@ nsPluginInstanceOwner::GetImageContainer
   float xResolution = mObjectFrame->PresContext()->GetRootPresContext()->PresShell()->GetXResolution();
   float yResolution = mObjectFrame->PresContext()->GetRootPresContext()->PresShell()->GetYResolution();
   r.Scale(xResolution, yResolution);
   mInstance->NotifySize(nsIntSize(r.width, r.height));
 
   return container.forget();
 #endif
 
-  if (mInstance) {
-    nsRefPtr<ImageContainer> container;
-    // Every call to nsIPluginInstance::GetImage() creates
-    // a new image.  See nsIPluginInstance.idl.
-    mInstance->GetImageContainer(getter_AddRefs(container));
-    if (container) {
+  // Every call to nsIPluginInstance::GetImage() creates
+  // a new image.  See nsIPluginInstance.idl.
+  mInstance->GetImageContainer(getter_AddRefs(container));
+  if (container) {
 #ifdef XP_MACOSX
-      AutoLockImage autoLock(container);
-      Image* image = autoLock.GetImage();
-      if (image && image->GetFormat() == MAC_IO_SURFACE && mObjectFrame) {
-        MacIOSurfaceImage *oglImage = static_cast<MacIOSurfaceImage*>(image);
-        NS_ADDREF_THIS();
-        oglImage->SetUpdateCallback(&DrawPlugin, this);
-        oglImage->SetDestroyCallback(&OnDestroyImage);
-      }
+    AutoLockImage autoLock(container);
+    Image* image = autoLock.GetImage();
+    if (image && image->GetFormat() == MAC_IO_SURFACE && mObjectFrame) {
+      MacIOSurfaceImage *oglImage = static_cast<MacIOSurfaceImage*>(image);
+      NS_ADDREF_THIS();
+      oglImage->SetUpdateCallback(&DrawPlugin, this);
+      oglImage->SetDestroyCallback(&OnDestroyImage);
+    }
 #endif
-      return container.forget();
-    }
+    return container.forget();
   }
   return nullptr;
 }
 
 void
 nsPluginInstanceOwner::SetBackgroundUnknown()
 {
   if (mInstance) {
--- a/dom/tests/mochitest/ajax/offline/Makefile.in
+++ b/dom/tests/mochitest/ajax/offline/Makefile.in
@@ -42,16 +42,17 @@ MOCHITEST_FILES	= \
 	test_foreign.html \
 	test_fallback.html \
 	test_overlap.html \
 	test_redirectManifest.html \
 	test_redirectUpdateItem.html \
 	overlap.cacheManifest \
 	overlap.cacheManifest^headers^ \
 	test_updatingManifest.html \
+	test_updateCheck.html \
 	445544_part1.html \
 	445544_part2.html \
 	445544.cacheManifest \
 	445544.cacheManifest^headers^ \
 	460353_iframe_nomanifest.html \
 	460353_iframe_ownmanifest.html \
 	460353_iframe_samemanifest.html \
 	test_obsolete.html \
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/ajax/offline/test_updateCheck.html
@@ -0,0 +1,86 @@
+<html xmlns="http://www.w3.org/1999/xhtml" manifest="http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingManifest.sjs">
+<head>
+<title>Cache update test</title>
+
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * The test is checking nsIOfflineCacheUpdateService.checkForUpdate API:
+ * - cache a manifest
+ * - check for an update of it, expected is "no update avail"
+ * - modify the manifest on the server
+ * - check for an update again, expected is "update avail"
+ * - check for an update ones again, expected is "update avail" (secondary check to probe
+ *   we didn't screw state of the manifest in the current cache with the first check)
+ * - cache the modified manifest, new version is now in the cache
+ * - last check for an update, expected is "no update avail" again
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var manifest = "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingManifest.sjs";
+var manifestURI = Cc["@mozilla.org/network/io-service;1"]
+                  .getService(Ci.nsIIOService)
+                  .newURI(manifest, null, null);
+var updateService = Cc['@mozilla.org/offlinecacheupdate-service;1']
+                    .getService(Ci.nsIOfflineCacheUpdateService);
+
+function manifestCached()
+{
+  // Run first check for an update
+  updateService.checkForUpdate(manifestURI, 0, false, {
+    observe: function(subject, topic, data) {
+      OfflineTest.is(topic, "offline-cache-update-unavailable", "No update avail");
+
+      // Change the manifest content
+      OfflineTest.setSJSState(manifest, "second");
+
+      // Check we now get notification on update ready
+      updateService.checkForUpdate(manifestURI, 0, false, {
+        observe: function(subject, topic, data) {
+          OfflineTest.is(topic, "offline-cache-update-available", "Update avail (1)");
+
+          // Do the check again.  We must get the same result.  Double check is here
+          // to make sure we don't overwrite any data in the cache by the check it self.
+          updateService.checkForUpdate(manifestURI, 0, false, {
+            observe: function(subject, topic, data) {
+              OfflineTest.is(topic, "offline-cache-update-available", "Update avail (2)");
+
+              // Update the manifest, invokes manifestUpdated()
+              applicationCache.onupdateready = OfflineTest.priv(manifestUpdated);
+              applicationCache.update();
+            }
+          });
+        }
+      });
+    }
+  });
+}
+
+function manifestUpdated()
+{
+  // Check for an update after manifest has been updated
+  updateService.checkForUpdate(manifestURI, 0, false, {
+    observe: function(subject, topic, data) {
+      OfflineTest.is(topic, "offline-cache-update-unavailable", "No update avail (2)");
+
+      OfflineTest.teardown();
+      OfflineTest.finish();
+    }
+  });
+}
+
+if (OfflineTest.setup()) {
+  applicationCache.onerror = OfflineTest.failEvent;
+  applicationCache.oncached = OfflineTest.priv(manifestCached);
+}
+
+</script>
+</head>
+<body>
+</body>
+</html>
--- a/gfx/graphite2/src/gr_logging.cpp
+++ b/gfx/graphite2/src/gr_logging.cpp
@@ -28,17 +28,17 @@ of the License or (at your option) any l
 
 #include "graphite2/Log.h"
 #include "inc/debug.h"
 #include "inc/CharInfo.h"
 #include "inc/Slot.h"
 #include "inc/Segment.h"
 
 #if defined _WIN32
-#include "Windows.h"
+#include "windows.h"
 #endif
 
 using namespace graphite2;
 
 
 extern "C" {
 
 
--- a/gfx/layers/opengl/TiledThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/TiledThebesLayerOGL.cpp
@@ -192,22 +192,22 @@ TiledThebesLayerOGL::ProcessUploadQueue(
   // FIXME: This wont be needed when we do progressive upload and lock
   // tile by tile.
   mMainMemoryTiledBuffer = BasicTiledLayerBuffer();
   mRegionToUpload = nsIntRegion();
 
 }
 
 void
-TiledThebesLayerOGL::RenderTile(TiledTexture aTile,
+TiledThebesLayerOGL::RenderTile(const TiledTexture& aTile,
                                 const gfx3DMatrix& aTransform,
                                 const nsIntPoint& aOffset,
-                                nsIntRegion aScreenRegion,
-                                nsIntPoint aTextureOffset,
-                                nsIntSize aTextureBounds,
+                                const nsIntRegion& aScreenRegion,
+                                const nsIntPoint& aTextureOffset,
+                                const nsIntSize& aTextureBounds,
                                 Layer* aMaskLayer)
 {
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, aTile.mTextureHandle);
     ShaderProgramOGL *program;
     if (aTile.mFormat == LOCAL_GL_RGB) {
       program = mOGLManager->GetProgram(gl::RGBXLayerProgramType, aMaskLayer);
     } else {
       program = mOGLManager->GetProgram(gl::BGRALayerProgramType, aMaskLayer);
--- a/gfx/layers/opengl/TiledThebesLayerOGL.h
+++ b/gfx/layers/opengl/TiledThebesLayerOGL.h
@@ -126,22 +126,22 @@ public:
        OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion)
   {
     NS_ABORT_IF_FALSE(false, "Not supported");
   }
   void PaintedTiledLayerBuffer(const BasicTiledLayerBuffer* mTiledBuffer);
   void ProcessUploadQueue();
 
   // Renders a single given tile.
-  void RenderTile(TiledTexture aTile,
+  void RenderTile(const TiledTexture& aTile,
                   const gfx3DMatrix& aTransform,
                   const nsIntPoint& aOffset,
-                  nsIntRegion aScreenRegion,
-                  nsIntPoint aTextureOffset,
-                  nsIntSize aTextureBounds,
+                  const nsIntRegion& aScreenRegion,
+                  const nsIntPoint& aTextureOffset,
+                  const nsIntSize& aTextureBounds,
                   Layer* aMaskLayer);
 
 private:
   nsIntRegion                  mRegionToUpload;
   BasicTiledLayerBuffer        mMainMemoryTiledBuffer;
   TiledLayerBufferOGL          mVideoMemoryTiledBuffer;
   ReusableTileStoreOGL*        mReusableTileStore;
 };
--- a/ipc/unixsocket/UnixSocket.cpp
+++ b/ipc/unixsocket/UnixSocket.cpp
@@ -773,17 +773,17 @@ UnixSocketConsumer::ListenSocket(UnixSoc
   if (mImpl) {
     NS_WARNING("Socket already connecting/connected!");
     return false;
   }
   nsCString addr;
   mImpl = new UnixSocketImpl(this, aConnector, addr);
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
                                    new SocketAcceptTask(mImpl));
-  mConnectionStatus = SOCKET_CONNECTING;
+  mConnectionStatus = SOCKET_LISTENING;
   return true;
 }
 
 void
 UnixSocketConsumer::CancelSocketTask()
 {
   mConnectionStatus = SOCKET_DISCONNECTED;
   if(!mImpl) {
--- a/ipc/unixsocket/UnixSocket.h
+++ b/ipc/unixsocket/UnixSocket.h
@@ -111,18 +111,19 @@ public:
    */
   virtual void GetSocketAddr(const sockaddr& aAddr,
                              nsAString& aAddrStr) = 0;
 
 };
 
 enum SocketConnectionStatus {
   SOCKET_DISCONNECTED = 0,
-  SOCKET_CONNECTING = 1,
-  SOCKET_CONNECTED = 2
+  SOCKET_LISTENING = 1,
+  SOCKET_CONNECTING = 2,
+  SOCKET_CONNECTED = 3
 };
 
 class UnixSocketConsumer : public RefCounted<UnixSocketConsumer>
 {
 public:
   UnixSocketConsumer();
 
   virtual ~UnixSocketConsumer();
--- a/js/ductwork/debugger/JSDebugger.cpp
+++ b/js/ductwork/debugger/JSDebugger.cpp
@@ -2,17 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "JSDebugger.h"
 #include "nsIXPConnect.h"
 #include "nsThreadUtils.h"
 #include "jsapi.h"
-#include "jsgc.h"
 #include "jsfriendapi.h"
 #include "jsdbgapi.h"
 #include "mozilla/ModuleUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsMemory.h"
 
 #define JSDEBUGGER_CONTRACTID \
   "@mozilla.org/jsdebugger;1"
new file mode 100644
--- /dev/null
+++ b/js/public/HeapAPI.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_heap_api_h___
+#define js_heap_api_h___
+
+/* These values are private to the JS engine. */
+namespace js {
+namespace gc {
+
+/*
+ * Page size must be static to support our arena pointer optimizations, so we
+ * are forced to support each platform with non-4096 pages as a special case.
+ * Note: The freelist supports a maximum arena shift of 15.
+ * Note: Do not use JS_CPU_SPARC here, this header is used outside JS.
+ */
+#if (defined(SOLARIS) || defined(__FreeBSD__)) && \
+    (defined(__sparc) || defined(__sparcv9) || defined(__ia64))
+const size_t PageShift = 13;
+const size_t ArenaShift = PageShift;
+#elif defined(__powerpc__)
+const size_t PageShift = 16;
+const size_t ArenaShift = 12;
+#else
+const size_t PageShift = 12;
+const size_t ArenaShift = PageShift;
+#endif
+const size_t PageSize = size_t(1) << PageShift;
+const size_t ArenaSize = size_t(1) << ArenaShift;
+const size_t ArenaMask = ArenaSize - 1;
+
+const size_t ChunkShift = 20;
+const size_t ChunkSize = size_t(1) << ChunkShift;
+const size_t ChunkMask = ChunkSize - 1;
+
+} /* namespace gc */
+} /* namespace js */
+
+namespace JS {
+
+namespace shadow {
+
+struct ArenaHeader
+{
+    JSCompartment *compartment;
+};
+
+} /* namespace shadow */
+
+static inline shadow::ArenaHeader *
+GetGCThingArena(void *thing)
+{
+    uintptr_t addr = uintptr_t(thing);
+    addr &= ~js::gc::ArenaMask;
+    return reinterpret_cast<shadow::ArenaHeader *>(addr);
+}
+
+static inline JSCompartment *
+GetGCThingCompartment(void *thing)
+{
+    JS_ASSERT(thing);
+    return GetGCThingArena(thing)->compartment;
+}
+
+static inline JSCompartment *
+GetObjectCompartment(JSObject *obj)
+{
+    return GetGCThingCompartment(obj);
+}
+
+} /* namespace JS */
+
+#endif /* js_heap_api_h___ */
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -156,17 +156,16 @@ INSTALLED_HEADERS = \
 		js.msg \
 		jsalloc.h \
 		jsapi.h \
 		jsclass.h \
 		jsclist.h \
 		jsdbgapi.h \
 		jsdhash.h \
 		jsfriendapi.h \
-		jsgc.h \
 		jslock.h \
 		json.h \
 		jsproxy.h \
 		jsprf.h \
 		jsprototypes.h \
 		jsprvtd.h \
 		jspubtd.h \
 		jstypes.h \
@@ -182,21 +181,17 @@ INSTALLED_HEADERS = \
 #
 EXPORTS_NAMESPACES += ds gc
 
 EXPORTS_ds = \
 		BitArray.h \
 		$(NULL)
 
 EXPORTS_gc = \
-		Barrier.h \
-		Heap.h \
 		Root.h \
-		Statistics.h \
-		StoreBuffer.h \
 		$(NULL)
 
 ######################################################
 # BEGIN include exported headers from the JS engine
 #
 #       Ultimately, after cleansing INSTALLED_HEADERS,
 #       these will be the ONLY headers exported by
 #       the js engine
@@ -207,16 +202,17 @@ VPATH		+= \
 
 EXPORTS_NAMESPACES += js
 
 # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so
 # that we ensure we don't over-expose our internal integer typedefs.  Note that
 # LegacyIntTypes.h below is deliberately exempted from this requirement.
 EXPORTS_js = \
 		HashTable.h \
+		HeapAPI.h \
 		LegacyIntTypes.h \
 		MemoryMetrics.h \
 		TemplateLib.h \
 		Utility.h \
 		Vector.h \
 		$(NULL)
 
 ###############################################
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -11,16 +11,17 @@
 #include "mozilla/StandardInteger.h"
 
 #include <stddef.h>
 
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/BitArray.h"
+#include "js/HeapAPI.h"
 
 struct JSCompartment;
 
 extern "C" {
 struct JSRuntime;
 }
 
 namespace js {
@@ -99,43 +100,16 @@ struct Cell
     inline JSCompartment *compartment() const;
 
 #ifdef DEBUG
     inline bool isAligned() const;
 #endif
 };
 
 /*
- * Page size must be static to support our arena pointer optimizations, so we
- * are forced to support each platform with non-4096 pages as a special case.
- * Note: The freelist supports a maximum arena shift of 15.
- * Note: Do not use JS_CPU_SPARC here, this header is used outside JS.
- * Bug 692267: Move page size definition to gc/Memory.h and include it
- *             directly once jsgc.h is no longer an installed header.
- */
-#if (defined(SOLARIS) || defined(__FreeBSD__)) && \
-    (defined(__sparc) || defined(__sparcv9) || defined(__ia64))
-const size_t PageShift = 13;
-const size_t ArenaShift = PageShift;
-#elif defined(__powerpc__)
-const size_t PageShift = 16;
-const size_t ArenaShift = 12;
-#else
-const size_t PageShift = 12;
-const size_t ArenaShift = PageShift;
-#endif
-const size_t PageSize = size_t(1) << PageShift;
-const size_t ArenaSize = size_t(1) << ArenaShift;
-const size_t ArenaMask = ArenaSize - 1;
-
-const size_t ChunkShift = 20;
-const size_t ChunkSize = size_t(1) << ChunkShift;
-const size_t ChunkMask = ChunkSize - 1;
-
-/*
  * This is the maximum number of arenas we allow in the FreeCommitted state
  * before we trigger a GC_SHRINK to release free arenas to the OS.
  */
 const static uint32_t FreeCommittedArenasThreshold = (32 << 20) / ArenaSize;
 
 /*
  * The mark bitmap has one bit per each GC cell. For multi-cell GC things this
  * wastes space but allows to avoid expensive devisions by thing's size when
@@ -398,22 +372,20 @@ struct FreeSpan
             JS_ASSERT(arenaAddr + ArenaSize == next->first);
         }
 #endif
     }
 
 };
 
 /* Every arena has a header. */
-struct ArenaHeader
+struct ArenaHeader : public JS::shadow::ArenaHeader
 {
     friend struct FreeLists;
 
-    JSCompartment   *compartment;
-
     /*
      * ArenaHeader::next has two purposes: when unallocated, it points to the
      * next available Arena's header. When allocated, it points to the next
      * arena of the same size class and compartment.
      */
     ArenaHeader     *next;
 
   private:
@@ -1021,12 +993,11 @@ Cell::compartment() const
 bool
 Cell::isAligned() const
 {
     return Arena::isAligned(address(), arenaHeader()->getThingSize());
 }
 #endif
 
 } /* namespace gc */
-
 } /* namespace js */
 
 #endif /* gc_heap_h___ */
--- a/js/src/jsapi-tests/testIntTypesABI.cpp
+++ b/js/src/jsapi-tests/testIntTypesABI.cpp
@@ -14,16 +14,17 @@
 #include "jsapi.h"
 #include "jsclass.h"
 #include "jscpucfg.h"
 #include "jspubtd.h"
 #include "jstypes.h"
 #include "jsval.h"
 
 #include "js/HashTable.h"
+#include "js/HeapAPI.h"
 #include "js/MemoryMetrics.h"
 #include "js/TemplateLib.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
 
 /*
  * Verify that our public (and intended to be public, versus being that way
  * because we haven't made them private yet) headers don't define
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2447,34 +2447,16 @@ JS_RemoveScriptRootRT(JSRuntime *rt, JSS
     js_RemoveRoot(rt, (void *)rp);
 }
 
 JS_NEVER_INLINE JS_PUBLIC_API(void)
 JS_AnchorPtr(void *p)
 {
 }
 
-#ifdef DEBUG
-
-JS_PUBLIC_API(void)
-JS_DumpNamedRoots(JSRuntime *rt,
-                  void (*dump)(const char *name, void *rp, JSGCRootType type, void *data),
-                  void *data)
-{
-    js_DumpNamedRoots(rt, dump, data);
-}
-
-#endif /* DEBUG */
-
-JS_PUBLIC_API(uint32_t)
-JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
-{
-    return js_MapGCRoots(rt, map, data);
-}
-
 JS_PUBLIC_API(JSBool)
 JS_LockGCThing(JSContext *cx, void *thing)
 {
     JSBool ok;
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     ok = js_LockGCThingRT(cx->runtime, thing);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3627,66 +3627,21 @@ js_RemoveRoot(JSRuntime *rt, void *rp);
 
 /*
  * C-compatible version of the Anchor class. It should be called after the last
  * use of the variable it protects.
  */
 extern JS_NEVER_INLINE JS_PUBLIC_API(void)
 JS_AnchorPtr(void *p);
 
-/*
- * This symbol may be used by embedders to detect the change from the old
- * JS_AddRoot(JSContext *, void *) APIs to the new ones above.
- */
-#define JS_TYPED_ROOTING_API
-
-/* Obsolete rooting APIs. */
-#define JS_EnterLocalRootScope(cx) (JS_TRUE)
-#define JS_LeaveLocalRootScope(cx) ((void) 0)
-#define JS_LeaveLocalRootScopeWithResult(cx, rval) ((void) 0)
-#define JS_ForgetLocalRoot(cx, thing) ((void) 0)
-
 typedef enum JSGCRootType {
     JS_GC_ROOT_VALUE_PTR,
     JS_GC_ROOT_GCTHING_PTR
 } JSGCRootType;
 
-#ifdef DEBUG
-extern JS_PUBLIC_API(void)
-JS_DumpNamedRoots(JSRuntime *rt,
-                  void (*dump)(const char *name, void *rp, JSGCRootType type, void *data),
-                  void *data);
-#endif
-
-/*
- * Call JS_MapGCRoots to map the GC's roots table using map(rp, name, data).
- * The root is pointed at by rp; if the root is unnamed, name is null; data is
- * supplied from the third parameter to JS_MapGCRoots.
- *
- * The map function should return JS_MAP_GCROOT_REMOVE to cause the currently
- * enumerated root to be removed.  To stop enumeration, set JS_MAP_GCROOT_STOP
- * in the return value.  To keep on mapping, return JS_MAP_GCROOT_NEXT.  These
- * constants are flags; you can OR them together.
- *
- * The JSGCRootType parameter indicates whether rp is a pointer to a Value
- * (which is obtained by '(Value *)rp') or a pointer to a GC-thing pointer
- * (which is obtained by '(void **)rp').
- *
- * JS_MapGCRoots returns the count of roots that were successfully mapped.
- */
-#define JS_MAP_GCROOT_NEXT      0       /* continue mapping entries */
-#define JS_MAP_GCROOT_STOP      1       /* stop mapping entries */
-#define JS_MAP_GCROOT_REMOVE    2       /* remove and free the current entry */
-
-typedef int
-(* JSGCRootMapFun)(void *rp, JSGCRootType type, const char *name, void *data);
-
-extern JS_PUBLIC_API(uint32_t)
-JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data);
-
 extern JS_PUBLIC_API(JSBool)
 JS_LockGCThing(JSContext *cx, void *thing);
 
 extern JS_PUBLIC_API(JSBool)
 JS_LockGCThingRT(JSRuntime *rt, void *thing);
 
 extern JS_PUBLIC_API(JSBool)
 JS_UnlockGCThing(JSContext *cx, void *thing);
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -517,16 +517,29 @@ js::TraceWeakMaps(WeakMapTracer *trc)
 
 JS_FRIEND_API(bool)
 js::GCThingIsMarkedGray(void *thing)
 {
     JS_ASSERT(thing);
     return reinterpret_cast<gc::Cell *>(thing)->isMarked(gc::GRAY);
 }
 
+JS_FRIEND_API(JSGCTraceKind)
+js::GCThingTraceKind(void *thing)
+{
+    JS_ASSERT(thing);
+    return gc::GetGCThingTraceKind(thing);
+}
+
+JS_FRIEND_API(void)
+js::UnmarkGrayGCThing(void *thing)
+{
+    static_cast<js::gc::Cell *>(thing)->unmark(js::gc::GRAY);
+}
+
 JS_FRIEND_API(void)
 js::VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback *callback, void *closure)
 {
     for (WrapperMap::Enum e(comp->crossCompartmentWrappers); !e.empty(); e.popFront()) {
         gc::Cell *thing = e.front().key.wrapped;
         if (thing->isMarked(gc::GRAY))
             callback(closure, thing);
     }
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -3,23 +3,32 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsfriendapi_h___
 #define jsfriendapi_h___
 
 #include "jsclass.h"
+#include "jscpucfg.h"
 #include "jspubtd.h"
 #include "jsprvtd.h"
 
+#include "js/HeapAPI.h"
+
 #include "mozilla/GuardObjects.h"
 
 JS_BEGIN_EXTERN_C
 
+#if JS_STACK_GROWTH_DIRECTION > 0
+# define JS_CHECK_STACK_SIZE(limit, lval)  ((uintptr_t)(lval) < limit)
+#else
+# define JS_CHECK_STACK_SIZE(limit, lval)  ((uintptr_t)(lval) > limit)
+#endif
+
 extern JS_FRIEND_API(void)
 JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
 
 extern JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt);
 
 extern JS_FRIEND_API(JSObject *)
 JS_FindCompilationScope(JSContext *cx, JSRawObject obj);
@@ -254,25 +263,37 @@ struct WeakMapTracer {
 };
 
 extern JS_FRIEND_API(void)
 TraceWeakMaps(WeakMapTracer *trc);
 
 extern JS_FRIEND_API(bool)
 GCThingIsMarkedGray(void *thing);
 
+JS_FRIEND_API(void)
+UnmarkGrayGCThing(void *thing);
+
 typedef void
 (GCThingCallback)(void *closure, void *gcthing);
 
 extern JS_FRIEND_API(void)
 VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback *callback, void *closure);
 
 extern JS_FRIEND_API(JSObject *)
 GetWeakmapKeyDelegate(JSObject *key);
 
+JS_FRIEND_API(JSGCTraceKind)
+GCThingTraceKind(void *thing);
+
+/*
+ * Invoke cellCallback on every gray JS_OBJECT in the given compartment.
+ */
+extern JS_FRIEND_API(void)
+IterateGrayObjects(JSCompartment *compartment, GCThingCallback *cellCallback, void *data);
+
 /*
  * Shadow declarations of JS internal structures, for access by inline access
  * functions below. Do not use these structures in any other way. When adding
  * new fields for access by inline methods, make sure to add static asserts to
  * the original header file to ensure that offsets are consistent.
  */
 namespace shadow {
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1279,28 +1279,16 @@ RecordNativeStackTopForGC(JSRuntime *rt)
     if (!rt->requestDepth)
         return;
 #endif
     cgcd->recordStackTop();
 }
 
 } /* namespace js */
 
-bool
-js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, gc::AllocKind *thingKind, void **thing)
-{
-    rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
-    return js::IsAddressableGCThing(rt, w, false, thingKind, NULL, thing) == CGCT_VALID;
-}
-
-#ifdef DEBUG
-static void
-CheckLeakedRoots(JSRuntime *rt);
-#endif
-
 void
 js_FinishGC(JSRuntime *rt)
 {
     /*
      * Wait until the background finalization stops and the helper thread
      * shuts down before we forcefully release any remaining GC memory.
      */
     rt->gcHelperThread.finish();
@@ -1319,20 +1307,16 @@ js_FinishGC(JSRuntime *rt)
     rt->gcSystemAvailableChunkListHead = NULL;
     rt->gcUserAvailableChunkListHead = NULL;
     for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
         Chunk::release(rt, r.front());
     rt->gcChunkSet.clear();
 
     rt->gcChunkPool.expireAndFree(rt, true);
 
-#ifdef DEBUG
-    if (!rt->gcRootsHash.empty())
-        CheckLeakedRoots(rt);
-#endif
     rt->gcRootsHash.clear();
     rt->gcLocksHash.clear();
 }
 
 JSBool
 js_AddRoot(JSContext *cx, Value *vp, const char *name)
 {
     JSBool ok = js_AddRootRT(cx->runtime, vp, name);
@@ -1388,82 +1372,16 @@ js_RemoveRoot(JSRuntime *rt, void *rp)
     rt->gcRootsHash.remove(rp);
     rt->gcPoke = true;
 }
 
 typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
 
-#ifdef DEBUG
-
-static void
-CheckLeakedRoots(JSRuntime *rt)
-{
-    uint32_t leakedroots = 0;
-
-    /* Warn (but don't assert) debug builds of any remaining roots. */
-    for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) {
-        RootEntry &entry = r.front();
-        leakedroots++;
-        fprintf(stderr,
-                "JS engine warning: leaking GC root \'%s\' at %p\n",
-                entry.value.name ? entry.value.name : "", entry.key);
-    }
-
-    if (leakedroots > 0) {
-        if (leakedroots == 1) {
-            fprintf(stderr,
-"JS engine warning: 1 GC root remains after destroying the JSRuntime at %p.\n"
-"                   This root may point to freed memory. Objects reachable\n"
-"                   through it have not been finalized.\n",
-                    (void *) rt);
-        } else {
-            fprintf(stderr,
-"JS engine warning: %lu GC roots remain after destroying the JSRuntime at %p.\n"
-"                   These roots may point to freed memory. Objects reachable\n"
-"                   through them have not been finalized.\n",
-                    (unsigned long) leakedroots, (void *) rt);
-        }
-    }
-}
-
-void
-js_DumpNamedRoots(JSRuntime *rt,
-                  void (*dump)(const char *name, void *rp, JSGCRootType type, void *data),
-                  void *data)
-{
-    for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) {
-        RootEntry &entry = r.front();
-        if (const char *name = entry.value.name)
-            dump(name, entry.key, entry.value.type, data);
-    }
-}
-
-#endif /* DEBUG */
-
-uint32_t
-js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
-{
-    int ct = 0;
-    for (RootEnum e(rt->gcRootsHash); !e.empty(); e.popFront()) {
-        RootEntry &entry = e.front();
-
-        ct++;
-        int mapflags = map(entry.key, entry.value.type, entry.value.name, data);
-
-        if (mapflags & JS_MAP_GCROOT_REMOVE)
-            e.removeFront();
-        if (mapflags & JS_MAP_GCROOT_STOP)
-            break;
-    }
-
-    return ct;
-}
-
 static size_t
 ComputeTriggerBytes(JSCompartment *comp, size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind)
 {
     size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD);
     float trigger = float(base) * comp->gcHeapGrowthFactor;
     return size_t(Min(float(maxBytes), trigger));
 }
 
@@ -2239,36 +2157,16 @@ void
 SetMarkStackLimit(JSRuntime *rt, size_t limit)
 {
     JS_ASSERT(!rt->isHeapBusy());
     rt->gcMarker.setSizeLimit(limit);
 }
 
 } /* namespace js */
 
-static void
-gc_root_traversal(JSTracer *trc, const RootEntry &entry)
-{
-    const char *name = entry.value.name ? entry.value.name : "root";
-    if (entry.value.type == JS_GC_ROOT_GCTHING_PTR)
-        MarkGCThingRoot(trc, reinterpret_cast<void **>(entry.key), name);
-    else
-        MarkValueRoot(trc, reinterpret_cast<Value *>(entry.key), name);
-}
-
-static void
-gc_lock_traversal(const GCLocks::Entry &entry, JSTracer *trc)
-{
-    JS_ASSERT(entry.value >= 1);
-    JS_SET_TRACING_LOCATION(trc, (void *)&entry.key);
-    void *tmp = entry.key;
-    MarkGCThingRoot(trc, &tmp, "locked object");
-    JS_ASSERT(tmp == entry.key);
-}
-
 namespace js {
 
 void
 MarkCompartmentActive(StackFrame *fp)
 {
     fp->script()->compartment()->active = true;
 }
 
@@ -2545,21 +2443,33 @@ MarkRuntime(JSTracer *trc, bool useSaved
 #ifdef JSGC_USE_EXACT_ROOTING
         MarkExactStackRoots(trc);
 #else
         MarkConservativeStackRoots(trc, useSavedRoots);
 #endif
         rt->markSelfHostedGlobal(trc);
     }
 
-    for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
-        gc_root_traversal(trc, r.front());
-
-    for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront())
-        gc_lock_traversal(r.front(), trc);
+    for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) {
+        const RootEntry &entry = r.front();
+        const char *name = entry.value.name ? entry.value.name : "root";
+        if (entry.value.type == JS_GC_ROOT_GCTHING_PTR)
+            MarkGCThingRoot(trc, reinterpret_cast<void **>(entry.key), name);
+        else
+            MarkValueRoot(trc, reinterpret_cast<Value *>(entry.key), name);
+    }
+
+    for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront()) {
+        const GCLocks::Entry &entry = r.front();
+        JS_ASSERT(entry.value >= 1);
+        JS_SET_TRACING_LOCATION(trc, (void *)&entry.key);
+        void *tmp = entry.key;
+        MarkGCThingRoot(trc, &tmp, "locked object");
+        JS_ASSERT(tmp == entry.key);
+    }
 
     if (rt->scriptAndCountsVector) {
         ScriptAndCountsVector &vec = *rt->scriptAndCountsVector;
         for (size_t i = 0; i < vec.length(); i++)
             MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
     }
 
     if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment->isCollecting())
@@ -3128,17 +3038,17 @@ GCHelperThread::startBackgroundAllocatio
 #ifdef JS_THREADSAFE
     if (state == IDLE) {
         state = ALLOCATING;
         PR_NotifyCondVar(wakeup);
     }
 #endif /* JS_THREADSAFE */
 }
 
-JS_FRIEND_API(void)
+void
 GCHelperThread::replenishAndFreeLater(void *ptr)
 {
     JS_ASSERT(freeCursor == freeCursorEnd);
     do {
         if (freeCursor && !freeVector.append(freeCursorEnd - FREE_ARRAY_LENGTH))
             break;
         freeCursor = (void **) js_malloc(FREE_ARRAY_SIZE);
         if (!freeCursor) {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -26,22 +26,16 @@
 #include "gc/Heap.h"
 #include "gc/Statistics.h"
 #include "js/HashTable.h"
 #include "js/Vector.h"
 #include "js/TemplateLib.h"
 
 struct JSCompartment;
 
-#if JS_STACK_GROWTH_DIRECTION > 0
-# define JS_CHECK_STACK_SIZE(limit, lval)  ((uintptr_t)(lval) < limit)
-#else
-# define JS_CHECK_STACK_SIZE(limit, lval)  ((uintptr_t)(lval) > limit)
-#endif
-
 namespace js {
 
 class GCHelperThread;
 struct Shape;
 struct SliceBudget;
 
 namespace ion {
     class IonCode;
@@ -442,22 +436,16 @@ struct ArenaLists {
  * chunks with total capacity of 16MB to avoid buffer resizes during browser
  * startup.
  */
 const size_t INITIAL_CHUNK_CAPACITY = 16 * 1024 * 1024 / ChunkSize;
 
 /* The number of GC cycles an empty chunk can survive before been released. */
 const size_t MAX_EMPTY_CHUNK_AGE = 4;
 
-inline Cell *
-AsCell(JSObject *obj)
-{
-    return reinterpret_cast<Cell *>(obj);
-}
-
 } /* namespace gc */
 
 struct GCPtrHasher
 {
     typedef void *Lookup;
 
     static HashNumber hash(void *key) {
         return HashNumber(uintptr_t(key) >> JS_GCTHING_ZEROBITS);
@@ -477,67 +465,48 @@ struct RootInfo {
 
 typedef js::HashMap<void *,
                     RootInfo,
                     js::DefaultHasher<void *>,
                     js::SystemAllocPolicy> RootedValueMap;
 
 } /* namespace js */
 
-extern JS_FRIEND_API(JSGCTraceKind)
-js_GetGCThingTraceKind(void *thing);
-
 extern JSBool
 js_InitGC(JSRuntime *rt, uint32_t maxbytes);
 
 extern void
 js_FinishGC(JSRuntime *rt);
 
 extern JSBool
 js_AddRoot(JSContext *cx, js::Value *vp, const char *name);
 
 extern JSBool
 js_AddGCThingRoot(JSContext *cx, void **rp, const char *name);
 
-#ifdef DEBUG
-extern void
-js_DumpNamedRoots(JSRuntime *rt,
-                  void (*dump)(const char *name, void *rp, JSGCRootType type, void *data),
-                  void *data);
-#endif
-
-extern uint32_t
-js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data);
-
 /* Table of pointers with count valid members. */
 typedef struct JSPtrTable {
     size_t      count;
     void        **array;
 } JSPtrTable;
 
 extern JSBool
 js_LockGCThingRT(JSRuntime *rt, void *thing);
 
 extern void
 js_UnlockGCThingRT(JSRuntime *rt, void *thing);
 
-extern bool
-js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, js::gc::AllocKind *thingKind, void **thing);
-
 namespace js {
 
 extern void
 MarkCompartmentActive(js::StackFrame *fp);
 
 extern void
 TraceRuntime(JSTracer *trc);
 
-extern JS_FRIEND_API(void)
-MarkContext(JSTracer *trc, JSContext *acx);
-
 /* Must be called with GC lock taken. */
 extern void
 TriggerGC(JSRuntime *rt, js::gcreason::Reason reason);
 
 /* Must be called with GC lock taken. */
 extern void
 TriggerCompartmentGC(JSCompartment *comp, js::gcreason::Reason reason);
 
@@ -545,19 +514,16 @@ extern void
 MaybeGC(JSContext *cx);
 
 extern void
 ShrinkGCBuffers(JSRuntime *rt);
 
 extern void
 ReleaseAllJITCode(FreeOp *op);
 
-extern JS_FRIEND_API(void)
-PrepareForFullGC(JSRuntime *rt);
-
 /*
  * Kinds of js_GC invocation.
  */
 typedef enum JSGCInvocationKind {
     /* Normal invocation. */
     GC_NORMAL           = 0,
 
     /* Minimize GC triggers and release empty GC chunks right away. */
@@ -624,21 +590,21 @@ class GCHelperThread {
 
     bool              sweepFlag;
     bool              shrinkFlag;
 
     Vector<void **, 16, js::SystemAllocPolicy> freeVector;
     void            **freeCursor;
     void            **freeCursorEnd;
 
-    bool    backgroundAllocation;
+    bool              backgroundAllocation;
 
     friend struct js::gc::ArenaLists;
 
-    JS_FRIEND_API(void)
+    void
     replenishAndFreeLater(void *ptr);
 
     static void freeElementsAndArray(void **array, void **end) {
         JS_ASSERT(array <= end);
         for (void **p = array; p != end; ++p)
             js_free(*p);
         js_free(array);
     }
@@ -1107,42 +1073,36 @@ typedef void (*IterateArenaCallback)(JSR
 typedef void (*IterateCellCallback)(JSRuntime *rt, void *data, void *thing,
                                     JSGCTraceKind traceKind, size_t thingSize);
 
 /*
  * This function calls |compartmentCallback| on every compartment,
  * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use
  * cell in the GC heap.
  */
-extern JS_FRIEND_API(void)
+extern void
 IterateCompartmentsArenasCells(JSRuntime *rt, void *data,
                                JSIterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
                                IterateCellCallback cellCallback);
 
 /*
  * Invoke chunkCallback on every in-use chunk.
  */
-extern JS_FRIEND_API(void)
+extern void
 IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback);
 
 /*
  * Invoke cellCallback on every in-use object of the specified thing kind for
  * the given compartment or for all compartments if it is null.
  */
-extern JS_FRIEND_API(void)
+extern void
 IterateCells(JSRuntime *rt, JSCompartment *compartment, gc::AllocKind thingKind,
              void *data, IterateCellCallback cellCallback);
 
-/*
- * Invoke cellCallback on every gray JS_OBJECT in the given compartment.
- */
-extern JS_FRIEND_API(void)
-IterateGrayObjects(JSCompartment *compartment, GCThingCallback *cellCallback, void *data);
-
 } /* namespace js */
 
 extern void
 js_FinalizeStringRT(JSRuntime *rt, JSString *str);
 
 /*
  * Macro to test if a traversal is the marking phase of the GC.
  */
@@ -1204,27 +1164,14 @@ static inline void
 MaybeVerifyBarriers(JSContext *cx, bool always = false)
 {
 }
 
 #endif
 
 } /* namespace gc */
 
-static inline JSCompartment *
-GetGCThingCompartment(void *thing)
-{
-    JS_ASSERT(thing);
-    return reinterpret_cast<gc::Cell *>(thing)->compartment();
-}
-
-static inline JSCompartment *
-GetObjectCompartment(JSObject *obj)
-{
-    return GetGCThingCompartment(obj);
-}
-
 void
 PurgeJITCaches(JSCompartment *c);
 
 } /* namespace js */
 
 #endif /* jsgc_h___ */
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -19,17 +19,16 @@
 #include "XPCWrapper.h"
 #include "jsproxy.h"
 #include "WrapperFactory.h"
 #include "XrayWrapper.h"
 #include "nsNullPrincipal.h"
 #include "nsJSUtils.h"
 #include "mozJSComponentLoader.h"
 #include "nsContentUtils.h"
-#include "jsgc.h"
 #include "jsfriendapi.h"
 #include "AccessCheck.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsPrincipal.h"
 #include "mozilla/Attributes.h"
 #include "nsIScriptContext.h"
 #include "nsJSEnvironment.h"
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -307,17 +307,17 @@ void XPCJSRuntime::TraceGrayJS(JSTracer*
     // Mark these roots as gray so the CC can walk them later.
     self->TraceXPConnectRoots(trc);
 }
 
 static void
 TraceJSObject(void *aScriptThing, const char *name, void *aClosure)
 {
     JS_CALL_TRACER(static_cast<JSTracer*>(aClosure), aScriptThing,
-                   js_GetGCThingTraceKind(aScriptThing), name);
+                   js::GCThingTraceKind(aScriptThing), name);
 }
 
 static PLDHashOperator
 TraceJSHolder(void *holder, nsScriptObjectTracer *&tracer, void *arg)
 {
     tracer->Trace(holder, TraceJSObject, arg);
 
     return PL_DHASH_NEXT;
@@ -354,17 +354,17 @@ struct Closure
 static void
 CheckParticipatesInCycleCollection(void *aThing, const char *name, void *aClosure)
 {
     Closure *closure = static_cast<Closure*>(aClosure);
 
     if (closure->cycleCollectionEnabled)
         return;
 
-    if (AddToCCKind(js_GetGCThingTraceKind(aThing)) &&
+    if (AddToCCKind(js::GCThingTraceKind(aThing)) &&
         xpc_IsGrayGCThing(aThing))
     {
         closure->cycleCollectionEnabled = true;
     }
 }
 
 static PLDHashOperator
 NoteJSHolder(void *holder, nsScriptObjectTracer *&tracer, void *arg)
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -10,17 +10,16 @@
 #include "mozilla/Base64.h"
 #include "mozilla/Util.h"
 
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "jsfriendapi.h"
-#include "jsgc.h"
 #include "dom_quickstubs.h"
 #include "nsNullPrincipal.h"
 #include "nsIURI.h"
 #include "nsJSEnvironment.h"
 #include "nsThreadUtils.h"
 
 #include "XrayWrapper.h"
 #include "WrapperFactory.h"
@@ -569,17 +568,17 @@ nsCycleCollectionParticipant *
 nsXPConnect::GetParticipant()
 {
     return XPConnect_cycleCollectorGlobal.GetParticipant();
 }
 
 JSBool
 xpc_GCThingIsGrayCCThing(void *thing)
 {
-    return AddToCCKind(js_GetGCThingTraceKind(thing)) &&
+    return AddToCCKind(js::GCThingTraceKind(thing)) &&
            xpc_IsGrayGCThing(thing);
 }
 
 struct UnmarkGrayTracer : public JSTracer
 {
     UnmarkGrayTracer() : mTracingShape(false), mPreviousShape(nullptr) {}
     UnmarkGrayTracer(JSTracer *trc, bool aTracingShape)
         : mTracingShape(aTracingShape), mPreviousShape(nullptr)
@@ -618,17 +617,17 @@ UnmarkGrayChildren(JSTracer *trc, void *
         nsXPConnect* xpc = nsXPConnect::GetXPConnect();
         xpc->EnsureGCBeforeCC();
         return;
     }
 
     if (!xpc_IsGrayGCThing(thing))
         return;
 
-    static_cast<js::gc::Cell *>(thing)->unmark(js::gc::GRAY);
+    js::UnmarkGrayGCThing(thing);
 
     /*
      * Trace children of |thing|. If |thing| and its parent are both shapes, |thing| will
      * get saved to mPreviousShape without being traced. The parent will later
      * trace |thing|. This is done to avoid increasing the stack depth during shape
      * tracing. It is safe to do because a shape can only have one child that is a shape.
      */
     UnmarkGrayTracer *tracer = static_cast<UnmarkGrayTracer*>(trc);
@@ -656,17 +655,17 @@ UnmarkGrayChildren(JSTracer *trc, void *
 
 void
 xpc_UnmarkGrayGCThingRecursive(void *thing, JSGCTraceKind kind)
 {
     MOZ_ASSERT(thing, "Don't pass me null!");
     MOZ_ASSERT(kind != JSTRACE_SHAPE, "UnmarkGrayGCThingRecursive not intended for Shapes");
 
     // Unmark.
-    static_cast<js::gc::Cell *>(thing)->unmark(js::gc::GRAY);
+    js::UnmarkGrayGCThing(thing);
 
     // Trace children.
     UnmarkGrayTracer trc;
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
     JS_TracerInit(&trc, rt, UnmarkGrayChildren);
     JS_TraceChildren(&trc, thing, kind);
 }
 
@@ -858,17 +857,17 @@ enum TraverseSelect {
     TRAVERSE_CPP,
     TRAVERSE_FULL
 };
 
 static void
 TraverseGCThing(TraverseSelect ts, void *p, JSGCTraceKind traceKind,
                 nsCycleCollectionTraversalCallback &cb)
 {
-    MOZ_ASSERT(traceKind == js_GetGCThingTraceKind(p));
+    MOZ_ASSERT(traceKind == js::GCThingTraceKind(p));
     bool isMarkedGray = xpc_IsGrayGCThing(p);
 
     if (ts == TRAVERSE_FULL)
         DescribeGCThing(!isMarkedGray, p, traceKind, cb);
 
     // If this object is alive, then all of its children are alive. For JS objects,
     // the black-gray invariant ensures the children are also marked black. For C++
     // objects, the ref count from this object will keep them alive. Thus we don't
@@ -885,17 +884,17 @@ TraverseGCThing(TraverseSelect ts, void 
         NoteGCThingXPCOMChildren(js::GetObjectClass(obj), obj, cb);
     }
 }
 
 NS_METHOD
 nsXPConnectParticipant::TraverseImpl(nsXPConnectParticipant *that, void *p,
                                      nsCycleCollectionTraversalCallback &cb)
 {
-    TraverseGCThing(TRAVERSE_FULL, p, js_GetGCThingTraceKind(p), cb);
+    TraverseGCThing(TRAVERSE_FULL, p, js::GCThingTraceKind(p), cb);
     return NS_OK;
 }
 
 class JSContextParticipant : public nsCycleCollectionParticipant
 {
 public:
     static NS_METHOD RootImpl(void *n)
     {
@@ -2404,26 +2403,26 @@ SetLocationForGlobal(JSObject *global, n
 }
 
 } // namespace xpc
 
 static void
 NoteJSChildGrayWrapperShim(void *data, void *thing)
 {
     TraversalTracer *trc = static_cast<TraversalTracer*>(data);
-    NoteJSChild(trc, thing, js_GetGCThingTraceKind(thing));
+    NoteJSChild(trc, thing, js::GCThingTraceKind(thing));
 }
 
 static void
 TraverseObjectShim(void *data, void *thing)
 {
     nsCycleCollectionTraversalCallback *cb =
         static_cast<nsCycleCollectionTraversalCallback*>(data);
 
-    MOZ_ASSERT(js_GetGCThingTraceKind(thing) == JSTRACE_OBJECT);
+    MOZ_ASSERT(js::GCThingTraceKind(thing) == JSTRACE_OBJECT);
     TraverseGCThing(TRAVERSE_CPP, thing, JSTRACE_OBJECT, *cb);
 }
 
 /*
  * The cycle collection participant for a JSCompartment is intended to produce the same
  * results as if all of the gray GCthings in a compartment were merged into a single node,
  * except for self-edges. This avoids the overhead of representing all of the GCthings in
  * the compartment in the cycle collector graph, which should be much faster if many of
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -97,17 +97,17 @@
 #include <math.h>
 #include "xpcpublic.h"
 #include "jsapi.h"
 #include "jsdhash.h"
 #include "jsprf.h"
 #include "prprf.h"
 #include "jsdbgapi.h"
 #include "jsfriendapi.h"
-#include "jsgc.h"
+#include "js/HeapAPI.h"
 #include "jswrapper.h"
 #include "nscore.h"
 #include "nsXPCOM.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCycleCollector.h"
 #include "nsDebug.h"
 #include "nsISupports.h"
@@ -130,16 +130,17 @@
 #include "XPCForwards.h"
 #include "XPCLog.h"
 #include "xpccomponents.h"
 #include "xpcexception.h"
 #include "xpcjsid.h"
 #include "prlong.h"
 #include "prenv.h"
 #include "prclist.h"
+#include "prcvar.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsXPIDLString.h"
 #include "nsAutoJSValHolder.h"
 
 #include "js/HashTable.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/ReentrantMonitor.h"
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -7,19 +7,19 @@
 
 #ifndef xpcpublic_h
 #define xpcpublic_h
 
 #include "jsapi.h"
 #include "js/MemoryMetrics.h"
 #include "jsclass.h"
 #include "jsfriendapi.h"
-#include "jsgc.h"
 #include "jspubtd.h"
 #include "jsproxy.h"
+#include "js/HeapAPI.h"
 
 #include "nsISupports.h"
 #include "nsIPrincipal.h"
 #include "nsWrapperCache.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "nsMathUtils.h"
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -8144,34 +8144,57 @@ nsCSSFrameConstructor::ProcessRestyledFr
         continue;
     }
 
     if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
         !(hint & nsChangeHint_ReconstructFrame)) {
       if (NeedToReframeForAddingOrRemovingTransform(frame)) {
         NS_UpdateHint(hint, nsChangeHint_ReconstructFrame);
       } else {
-        // We can just add this state bit unconditionally, since it's
-        // conservative. Normally frame construction would set this if needed,
-        // but we're not going to reconstruct the frame so we need to set it.
-        // It's because we need to set this bit on each affected frame
+        // Normally frame construction would set state bits as needed,
+        // but we're not going to reconstruct the frame so we need to set them.
+        // It's because we need to set this state on each affected frame
         // that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up
         // to ancestors (i.e. it can't be an inherited change hint).
-        frame->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
+        if (frame->IsPositioned()) {
+          // If a transform has been added, we'll be taking this path,
+          // but we may be taking this path even if a transform has been
+          // removed. It's OK to add the bit even if it's not needed.
+          frame->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
+          if (!frame->IsAbsoluteContainer()) {
+            frame->MarkAsAbsoluteContainingBlock();
+          }
+        } else {
+          // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still by
+          // transformed by other means. It's OK to have the bit even if it's
+          // not needed.
+          if (frame->IsAbsoluteContainer()) {
+            frame->MarkAsNotAbsoluteContainingBlock();
+          }
+        }
       }
     }
     if (hint & nsChangeHint_ReconstructFrame) {
       // If we ever start passing true here, be careful of restyles
       // that involve a reframe and animations.  In particular, if the
       // restyle we're processing here is an animation restyle, but
       // the style resolution we will do for the frame construction
       // happens async when we're not in an animation restyle already,
       // problems could arise.
       RecreateFramesForContent(content, false);
     } else {
+      if ((frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
+          (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
+        // frame does not maintain overflow rects, so avoid calling
+        // FinishAndStoreOverflow on it:
+        hint = NS_SubtractHint(hint,
+                 NS_CombineHint(nsChangeHint_UpdateOverflow,
+                                nsChangeHint_ChildrenOnlyTransform));
+      }
+
       NS_ASSERTION(frame, "This shouldn't happen");
       if (hint & nsChangeHint_UpdateEffects) {
         nsSVGEffects::UpdateEffects(frame);
       }
       if (hint & nsChangeHint_NeedReflow) {
         StyleChangeReflow(frame, hint);
         didReflowThisFrame = true;
       }
@@ -8279,16 +8302,35 @@ nsCSSFrameConstructor::RestyleElement(El
   if (aPrimaryFrame && aPrimaryFrame->GetContent() != aElement) {
     // XXXbz this is due to image maps messing with the primary frame pointer
     // of <area>s.  See bug 135040.  We can remove this block once that's fixed.
     aPrimaryFrame = nullptr;
   }
   NS_ASSERTION(!aPrimaryFrame || aPrimaryFrame->GetContent() == aElement,
                "frame/content mismatch");
 
+  // If we're restyling the root element and there are 'rem' units in
+  // use, handle dynamic changes to the definition of a 'rem' here.
+  if (GetPresContext()->UsesRootEMUnits() && aPrimaryFrame) {
+    nsStyleContext *oldContext = aPrimaryFrame->GetStyleContext();
+    if (!oldContext->GetParent()) { // check that we're the root element
+      nsRefPtr<nsStyleContext> newContext = mPresShell->StyleSet()->
+        ResolveStyleFor(aElement, nullptr /* == oldContext->GetParent() */);
+      if (oldContext->GetStyleFont()->mFont.size !=
+          newContext->GetStyleFont()->mFont.size) {
+        // The basis for 'rem' units has changed.
+        DoRebuildAllStyleData(aRestyleTracker, nsChangeHint(0));
+        if (aMinHint == 0) {
+          return;
+        }
+        aPrimaryFrame = aElement->GetPrimaryFrame();
+      }
+    }
+  }
+
   if (aMinHint & nsChangeHint_ReconstructFrame) {
     RecreateFramesForContent(aElement, false);
   } else if (aPrimaryFrame) {
     nsStyleChangeList changeList;
     ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint,
                           aRestyleTracker, aRestyleDescendants);
     ProcessRestyledFrames(changeList);
   } else {
@@ -12073,47 +12115,56 @@ nsCSSFrameConstructor::RebuildAllStyleDa
   nsCOMPtr<nsIPresShell> kungFuDeathGrip(mPresShell);
 
   // We may reconstruct frames below and hence process anything that is in the
   // tree. We don't want to get notified to process those items again after.
   mPresShell->GetDocument()->FlushPendingNotifications(Flush_ContentAndNotify);
 
   nsAutoScriptBlocker scriptBlocker;
 
+  nsPresContext *presContext = mPresShell->GetPresContext();
+  presContext->SetProcessingRestyles(true);
+
+  DoRebuildAllStyleData(mPendingRestyles, aExtraHint);
+
+  presContext->SetProcessingRestyles(false);
+
+  // Make sure that we process any pending animation restyles from the
+  // above style change.  Note that we can *almost* implement the above
+  // by just posting a style change -- except we really need to restyle
+  // the root frame rather than the root element's primary frame.
+  ProcessPendingRestyles();
+}
+
+void
+nsCSSFrameConstructor::DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
+                                             nsChangeHint aExtraHint)
+{
   // Tell the style set to get the old rule tree out of the way
   // so we can recalculate while maintaining rule tree immutability
   nsresult rv = mPresShell->StyleSet()->BeginReconstruct();
   if (NS_FAILED(rv)) {
     return;
   }
 
-  nsPresContext *presContext = mPresShell->GetPresContext();
-  presContext->SetProcessingRestyles(true);
   // Recalculate all of the style contexts for the document
   // Note that we can ignore the return value of ComputeStyleChangeFor
   // because we never need to reframe the root frame
   // XXX This could be made faster by not rerunning rule matching
   // (but note that nsPresShell::SetPreferenceStyleRules currently depends
   // on us re-running rule matching here
   nsStyleChangeList changeList;
   // XXX Does it matter that we're passing aExtraHint to the real root
   // frame and not the root node's primary frame?
   // Note: The restyle tracker we pass in here doesn't matter.
   ComputeStyleChangeFor(mPresShell->GetRootFrame(),
                         &changeList, aExtraHint,
-                        mPendingRestyles, true);
+                        aRestyleTracker, true);
   // Process the required changes
   ProcessRestyledFrames(changeList);
-  presContext->SetProcessingRestyles(false);
-
-  // Make sure that we process any pending animation restyles from the
-  // above style change.  Note that we can *almost* implement the above
-  // by just posting a style change -- except we really need to restyle
-  // the root frame rather than the root element's primary frame.
-  ProcessPendingRestyles();
 
   // Tell the style set it's safe to destroy the old rule tree.  We
   // must do this after the ProcessRestyledFrames call in case the
   // change list has frame reconstructs in it (since frames to be
   // reconstructed will still have their old style context pointers
   // until they are destroyed).
   mPresShell->StyleSet()->EndReconstruct();
 }
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -271,16 +271,21 @@ public:
   // itself.
   void ProcessPendingRestyles();
   
   // Rebuilds all style data by throwing out the old rule tree and
   // building a new one, and additionally applying aExtraHint (which
   // must not contain nsChangeHint_ReconstructFrame) to the root frame.
   void RebuildAllStyleData(nsChangeHint aExtraHint);
 
+  // Helper that does part of the work of RebuildAllStyleData, shared by
+  // RestyleElement for 'rem' handling.
+  void DoRebuildAllStyleData(RestyleTracker& aRestyleTracker,
+                             nsChangeHint aExtraHint);
+
   // See PostRestyleEventCommon below.
   void PostRestyleEvent(Element* aElement,
                         nsRestyleHint aRestyleHint,
                         nsChangeHint aMinChangeHint)
   {
     nsPresContext *presContext = mPresShell->GetPresContext();
     if (presContext) {
       PostRestyleEventCommon(aElement, aRestyleHint, aMinChangeHint,
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -44,16 +44,17 @@
 #include "nsSVGIntegrationUtils.h"
 #include "gfxDrawable.h"
 #include "sampler.h"
 #include "nsCSSRenderingBorders.h"
 #include "mozilla/css/ImageLoader.h"
 #include "ImageContainer.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/Types.h"
 #include <ctime>
 
 using namespace mozilla;
 using namespace mozilla::css;
 
 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
 // recalculating this for each frame in a continuation (perf), hold
 // a cache of various coordinate information that we need in order
@@ -277,42 +278,46 @@ protected:
 struct GradientCacheKey : public PLDHashEntryHdr {
   typedef const GradientCacheKey& KeyType;
   typedef const GradientCacheKey* KeyTypePointer;
   enum { ALLOW_MEMMOVE = true };
   const nsRefPtr<nsStyleGradient> mGradient;
   const gfxSize mGradientSize;
   enum { SINGLE_CELL = 0x01 };
   const uint32_t mFlags;
+  const gfx::BackendType mBackendType;
 
   GradientCacheKey(nsStyleGradient* aGradient, const gfxSize& aGradientSize,
-                   uint32_t aFlags)
-    : mGradient(aGradient), mGradientSize(aGradientSize), mFlags(aFlags)
+                   uint32_t aFlags, gfx::BackendType aBackendType)
+    : mGradient(aGradient), mGradientSize(aGradientSize), mFlags(aFlags),
+      mBackendType(aBackendType)
   { }
 
   GradientCacheKey(const GradientCacheKey* aOther)
     : mGradient(aOther->mGradient), mGradientSize(aOther->mGradientSize),
-      mFlags(aOther->mFlags)
+      mFlags(aOther->mFlags), mBackendType(aOther->mBackendType)
   { }
 
   static PLDHashNumber
   HashKey(const KeyTypePointer aKey)
   {
     PLDHashNumber hash = 0;
     hash = AddToHash(hash, aKey->mGradientSize.width);
     hash = AddToHash(hash, aKey->mGradientSize.height);
     hash = AddToHash(hash, aKey->mFlags);
+    hash = AddToHash(hash, aKey->mBackendType);
     hash = aKey->mGradient->Hash(hash);
     return hash;
   }
 
   bool KeyEquals(KeyTypePointer aKey) const
   {
     return (*aKey->mGradient == *mGradient) &&
            (aKey->mGradientSize == mGradientSize) &&
+           (aKey->mBackendType == mBackendType) &&
            (aKey->mFlags == mFlags);
   }
   static KeyTypePointer KeyToPointer(KeyType aKey)
   {
     return &aKey;
   }
 };
 
@@ -371,28 +376,28 @@ class GradientCache MOZ_FINAL : public n
     virtual void NotifyExpired(GradientCacheData* aObject)
     {
       // This will free the gfxPattern.
       RemoveObject(aObject);
       mHashEntries.Remove(aObject->mKey);
     }
 
     GradientCacheData* Lookup(nsStyleGradient* aKey, const gfxSize& aGradientSize,
-                              uint32_t aFlags)
+                              uint32_t aFlags, gfx::BackendType aBackendType)
     {
       // We don't cache gradient that have Calc value, because the Calc object
       // can be deallocated by the time we want to compute the hash, and thus we
       // would have a dangling pointer in some nsStyleCoord in the
       // nsStyleGradient that are in the hash table.
       if (aKey->HasCalc()) {
         return nullptr;
       }
 
       GradientCacheData* gradient =
-        mHashEntries.Get(GradientCacheKey(aKey, aGradientSize, aFlags));
+        mHashEntries.Get(GradientCacheKey(aKey, aGradientSize, aFlags, aBackendType));
 
       if (gradient) {
         MarkUsed(gradient);
       }
 
       return gradient;
     }
 
@@ -2027,18 +2032,28 @@ nsCSSRendering::PaintGradient(nsPresCont
   nscoord appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
   gfxRect oneCellArea =
     nsLayoutUtils::RectToGfxRect(aOneCellArea, appUnitsPerPixel);
   bool gradientRegistered = true;
   uint32_t flags = 0;
   if (aOneCellArea.Contains(aFillArea)) {
     flags |= GradientCacheKey::SINGLE_CELL;
   }
+
+  gfx::BackendType backendType = gfx::BACKEND_NONE;
+  if (ctx->IsCairo()) {
+    backendType = gfx::BACKEND_CAIRO;
+  } else {
+    gfx::DrawTarget* dt = ctx->GetDrawTarget();
+    NS_ASSERTION(dt, "If we are not using Cairo, we should have a draw target.");
+    backendType = dt->GetType();
+  }
+
   GradientCacheData* pattern =
-    gGradientCache->Lookup(aGradient, oneCellArea.Size(), flags);
+    gGradientCache->Lookup(aGradient, oneCellArea.Size(), flags, backendType);
 
   if (pattern == nullptr) {
     // Compute "gradient line" start and end relative to oneCellArea
     gfxPoint lineStart, lineEnd;
     double radiusX = 0, radiusY = 0; // for radial gradients only
     if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
       ComputeLinearGradientLine(aPresContext, aGradient, oneCellArea.Size(),
                                 &lineStart, &lineEnd);
@@ -2277,17 +2292,17 @@ nsCSSRendering::PaintGradient(nsPresCont
     }
 
     // Set repeat mode. Default cairo extend mode is PAD.
     if (aGradient->mRepeating || forceRepeatToCoverTiles) {
       gradientPattern->SetExtend(gfxPattern::EXTEND_REPEAT);
     }
     // Register the gradient newly computed in the cache.
     pattern = new GradientCacheData(gradientPattern, forceRepeatToCoverTiles,
-      GradientCacheKey(aGradient, oneCellArea.Size(), flags));
+      GradientCacheKey(aGradient, oneCellArea.Size(), flags, backendType));
     gradientRegistered = gGradientCache->RegisterEntry(pattern);
   }
 
   // Paint gradient tiles. This isn't terribly efficient, but doing it this
   // way is simple and sure to get pixel-snapping right. We could speed things
   // up by drawing tiles into temporary surfaces and copying those to the
   // destination, but after pixel-snapping tiles may not all be the same size.
   nsRect dirty;
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1656,16 +1656,17 @@ nsPresContext::UIResolutionChangedIntern
 void
 nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint)
 {
   if (!mShell) {
     // We must have been torn down. Nothing to do here.
     return;
   }
 
+  mUsesRootEMUnits = false;
   mUsesViewportUnits = false;
   RebuildUserFontSet();
   AnimationManager()->KeyframesListIsDirty();
 
   mShell->FrameConstructor()->RebuildAllStyleData(aExtraHint);
 }
 
 void
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -948,16 +948,24 @@ public:
   bool IsGlyph() const {
     return mIsGlyph;
   }
 
   void SetIsGlyph(bool aValue) {
     mIsGlyph = aValue;
   }
 
+  bool UsesRootEMUnits() const {
+    return mUsesRootEMUnits;
+  }
+
+  void SetUsesRootEMUnits(bool aValue) {
+    mUsesRootEMUnits = aValue;
+  }
+
   bool UsesViewportUnits() const {
     return mUsesViewportUnits;
   }
 
   void SetUsesViewportUnits(bool aValue) {
     mUsesViewportUnits = aValue;
   }
 
@@ -1211,17 +1219,19 @@ protected:
   unsigned              mMayHaveFixedBackgroundFrames : 1;
   // True if the requests in mInvalidateRequestsSinceLastPaint cover the
   // entire viewport
   unsigned              mAllInvalidated : 1;
 
   // Are we currently drawing an SVG glyph?
   unsigned              mIsGlyph : 1;
 
-  // Does the associated document use viewport units?
+  // Does the associated document use root-em (rem) units?
+  unsigned              mUsesRootEMUnits : 1;
+  // Does the associated document use viewport units (vw/vh/vmin/vmax)?
   unsigned              mUsesViewportUnits : 1;
 
   // Has there been a change to the viewport's dimensions?
   unsigned              mPendingViewportChange : 1;
 
   // Is the current mUserFontSet valid?
   unsigned              mUserFontSetDirty : 1;
   // Has GetUserFontSet() been called?
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -420,17 +420,17 @@ nsComboboxControlFrame::ShowList(bool aS
 
   nsIFrame* listFrame = do_QueryFrame(mListControlFrame);
   if (listFrame) {
     nsIView* view = listFrame->GetView();
     NS_ASSERTION(view, "nsComboboxControlFrame view is null");
     if (view) {
       nsIWidget* widget = view->GetWidget();
       if (widget) {
-        widget->CaptureRollupEvents(this, mDroppedDown, mDroppedDown);
+        widget->CaptureRollupEvents(this, mDroppedDown);
 
         if (!aShowList) {
           nsCOMPtr<nsIRunnable> widgetDestroyer =
             new DestroyWidgetRunnable(GetContent());
           NS_DispatchToMainThread(widgetDestroyer);
         }
       }
     }
@@ -1451,17 +1451,17 @@ nsComboboxControlFrame::DestroyFrom(nsIF
     // Get parent view
     nsIFrame * listFrame = do_QueryFrame(mListControlFrame);
     if (listFrame) {
       nsIView* view = listFrame->GetView();
       NS_ASSERTION(view, "nsComboboxControlFrame view is null");
       if (view) {
         nsIWidget* widget = view->GetWidget();
         if (widget)
-          widget->CaptureRollupEvents(this, false, true);
+          widget->CaptureRollupEvents(this, false);
       }
     }
   }
 
   // Cleanup frames in popup child list
   mPopupFrames.DestroyFramesFrom(aDestructRoot);
   nsContentUtils::DestroyAnonymousContent(&mDisplayContent);
   nsContentUtils::DestroyAnonymousContent(&mButtonContent);
@@ -1504,31 +1504,44 @@ nsComboboxControlFrame::SetInitialChildL
     rv = nsBlockFrame::SetInitialChildList(aListID, aChildList);
   }
   return rv;
 }
 
 //----------------------------------------------------------------------
   //nsIRollupListener
 //----------------------------------------------------------------------
-nsIContent*
-nsComboboxControlFrame::Rollup(uint32_t aCount, bool aGetLastRolledUp)
+bool
+nsComboboxControlFrame::Rollup(uint32_t aCount, nsIContent** aLastRolledUp)
 {
-  if (mDroppedDown) {
-    nsWeakFrame weakFrame(this);
-    mListControlFrame->AboutToRollup(); // might destroy us
-    if (!weakFrame.IsAlive())
-      return nullptr;
-    ShowDropDown(false); // might destroy us
-    if (!weakFrame.IsAlive())
-      return nullptr;
+  if (!mDroppedDown)
+    return false;
+
+  nsWeakFrame weakFrame(this);
+  mListControlFrame->AboutToRollup(); // might destroy us
+  if (!weakFrame.IsAlive())
+    return true;
+  ShowDropDown(false); // might destroy us
+  if (weakFrame.IsAlive()) {
     mListControlFrame->CaptureMouseEvents(false);
   }
 
-  return nullptr;
+  return true;
+}
+
+nsIWidget*
+nsComboboxControlFrame::GetRollupWidget()
+{
+  nsIFrame* listFrame = do_QueryFrame(mListControlFrame);
+  if (!listFrame)
+    return nullptr;
+
+  nsIView* view = listFrame->GetView();
+  MOZ_ASSERT(view);
+  return view->GetWidget();
 }
 
 void
 nsComboboxControlFrame::RollupFromList()
 {
   if (ShowList(false))
     mListControlFrame->CaptureMouseEvents(false);
 }
--- a/layout/forms/nsComboboxControlFrame.h
+++ b/layout/forms/nsComboboxControlFrame.h
@@ -163,17 +163,17 @@ public:
   NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) MOZ_OVERRIDE;
   NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) MOZ_OVERRIDE;
 
   //nsIRollupListener
   /**
    * Hide the dropdown menu and stop capturing mouse events.
    * @note This method might destroy |this|.
    */
-  virtual nsIContent* Rollup(uint32_t aCount, bool aGetLastRolledUp = false);
+  virtual bool Rollup(uint32_t aCount, nsIContent** aLastRolledUp);
   virtual void NotifyGeometryChange();
 
   /**
    * A combobox should roll up if a mousewheel event happens outside of
    * the popup area.
    */
   virtual bool ShouldRollupOnMouseWheelEvent()
     { return true; }
@@ -183,16 +183,18 @@ public:
    * (eg. X-mouse).
    */
   virtual bool ShouldRollupOnMouseActivate()
     { return false; }
 
   virtual uint32_t GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain)
     { return 0; }
 
+  virtual nsIWidget* GetRollupWidget();
+
   //nsIStatefulFrame
   NS_IMETHOD SaveState(SpecialStateID aStateID, nsPresState** aState) MOZ_OVERRIDE;
   NS_IMETHOD RestoreState(nsPresState* aState) MOZ_OVERRIDE;
 
   static bool ToolkitHasNativePopup();
 
 protected:
   friend class RedisplayTextEvent;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -244,22 +244,39 @@ nsIFrame::GetAbsoluteContainingBlock() c
   NS_ASSERTION(IsAbsoluteContainer(), "The frame is not marked as an abspos container correctly");
   nsAbsoluteContainingBlock* absCB = static_cast<nsAbsoluteContainingBlock*>
     (Properties().Get(AbsoluteContainingBlockProperty()));
   NS_ASSERTION(absCB, "The frame is marked as an abspos container but doesn't have the property");
   return absCB;
 }
 
 void
-nsIFrame::MarkAsAbsoluteContainingBlock() {
+nsIFrame::MarkAsAbsoluteContainingBlock()
+{
+  NS_ASSERTION(!Properties().Get(AbsoluteContainingBlockProperty()),
+               "Already has an abs-pos containing block property?");
+  NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
+               "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
   AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
   Properties().Set(AbsoluteContainingBlockProperty(), new nsAbsoluteContainingBlock(GetAbsoluteListID()));
 }
 
 void
+nsIFrame::MarkAsNotAbsoluteContainingBlock()
+{
+  NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
+  NS_ASSERTION(Properties().Get(AbsoluteContainingBlockProperty()),
+               "Should have an abs-pos containing block property");
+  NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
+               "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
+  RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
+  Properties().Delete(AbsoluteContainingBlockProperty());
+}
+
+void
 nsIFrame::ClearDisplayItemCache()
 {
   if (GetStateBits() & NS_FRAME_HAS_CACHED_BACKGROUND) {
     Properties().Delete(CachedBackgroundImage());
     RemoveStateBits(NS_FRAME_HAS_CACHED_BACKGROUND);
   }
 }
 
@@ -6857,16 +6874,20 @@ IsInlineFrame(nsIFrame *aFrame)
   nsIAtom *type = aFrame->GetType();
   return type == nsGkAtoms::inlineFrame;
 }
 
 bool
 nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
                                  nsSize aNewSize)
 {
+  NS_ASSERTION(!((GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
+                 (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)),
+               "Don't call - overflow rects not maintained on these SVG frames");
+
   nsRect bounds(nsPoint(0, 0), aNewSize);
   // Store the passed in overflow area if we are a preserve-3d frame,
   // and it's not just the frame bounds.
   if ((Preserves3D() || HasPerspective()) && (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
                         !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) {
     nsOverflowAreas* initial =
       static_cast<nsOverflowAreas*>(Properties().Get(nsIFrame::InitialOverflowProperty()));
     if (!initial) {
@@ -7033,31 +7054,38 @@ nsIFrame::RecomputePerspectiveChildrenOv
   if (aBounds) {
     SetSize(aBounds->Size());
   }
   nsIFrame::ChildListIterator lists(this);
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame* child = childFrames.get();
+      if ((child->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
+          (child->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
+        continue; // frame does not maintain overflow rects
+      }
       if (child->HasPerspective()) {
         nsOverflowAreas* overflow = 
           static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty()));
         nsRect bounds(nsPoint(0, 0), child->GetSize());
         if (overflow) {
           child->FinishAndStoreOverflow(*overflow, bounds.Size());
         } else {
           nsOverflowAreas boundsOverflow;
           boundsOverflow.SetAllTo(bounds);
           child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
         }
       } else if (child->GetStyleContext()->GetParent() == aStartStyle ||
                  child->GetStyleContext() == aStartStyle) {
-        // Recurse into frames with the same style context, or a direct
-        // child style context.
+        // If a frame is using perspective, then the size used to compute
+        // perspective-origin is the size of the frame belonging to its parent
+        // style context. We must find any descendant frames using our size
+        // (by recurse into frames with the same style context, or a direct
+        // child style context) to update their overflow rects too.
         child->RecomputePerspectiveChildrenOverflow(aStartStyle, nullptr);
       }
     }
   }
   // Restore our old size just in case something depends on this elesewhere.
   SetSize(oldSize);
 }
 
@@ -7075,16 +7103,20 @@ RecomputePreserve3DChildrenOverflow(nsIF
   if (aBounds) {
     aFrame->SetSize(aBounds->Size());
   }
   nsIFrame::ChildListIterator lists(aFrame);
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame* child = childFrames.get();
+      if ((child->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
+          (child->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
+        continue; // frame does not maintain overflow rects
+      }
       if (child->Preserves3DChildren()) {
         RecomputePreserve3DChildrenOverflow(child, NULL);
       } else if (child->Preserves3D()) {
         nsOverflowAreas* overflow = 
           static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty()));
         nsRect bounds(nsPoint(0, 0), child->GetSize());
         if (overflow) {
           child->FinishAndStoreOverflow(*overflow, bounds.Size());
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -2865,17 +2865,18 @@ NS_PTR_TO_INT32(frame->Properties().Get(
   }  
 
   /**
    * Accessors for the absolute containing block.
    */
   bool IsAbsoluteContainer() const { return !!(mState & NS_FRAME_HAS_ABSPOS_CHILDREN); }
   bool HasAbsolutelyPositionedChildren() const;
   nsAbsoluteContainingBlock* GetAbsoluteContainingBlock() const;
-  virtual void MarkAsAbsoluteContainingBlock();
+  void MarkAsAbsoluteContainingBlock();
+  void MarkAsNotAbsoluteContainingBlock();
   // Child frame types override this function to select their own child list name
   virtual mozilla::layout::FrameChildListID GetAbsoluteListID() const { return kAbsoluteList; }
 
   // Checks if we (or any of our descendents) have NS_FRAME_PAINTED_THEBES set, and
   // clears this bit if so.
   bool CheckAndClearPaintedState();
 
   // CSS visibility just doesn't cut it because it doesn't inherit through
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/804323-1-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+body { margin:0; padding:0; overflow:hidden; }
+#new {
+  position: absolute;
+  left: 200px;
+  top: 100px;
+  width: 100px;
+  height: 100px;
+  background: yellow;
+}
+</style>
+</head>
+<body>
+<div id="new"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/804323-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+body { margin:0; padding:0; overflow:hidden; }
+#new {
+  position: absolute;
+  left: 100px;
+  top: 100px;
+  width: 100px;
+  height: 100px;
+  background: yellow;
+}
+</style>
+</head>
+<body>
+<div id="new" style="display:none"></div>
+<script>
+document.body.getBoundingClientRect().height;
+document.body.style.transform = "translateX(100px)";
+document.body.getBoundingClientRect().width;
+document.getElementById("new").style.display = "";
+</script>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1721,8 +1721,9 @@ fuzzy-if(true,17,5859) == 759036-2.html 
 == 776265-2a.html 776265-2-ref.html
 == 776265-2b.html 776265-2-ref.html
 == 776265-2c.html 776265-2-ref.html
 == 776265-2d.html 776265-2-ref.html
 == 787947-1.html 787947-1-ref.html
 fuzzy(40,800) == 797797-1.html 797797-1-ref.html # 'opacity:N' and rgba(,,,N) text don't match precisely
 fuzzy(40,800) == 797797-2.html 797797-2-ref.html # 'opacity:N' and rgba(,,,N) text don't match precisely
 == 801994-1.html 801994-1-ref.html
+== 804323-1.html 804323-1-ref.html
--- a/layout/style/nsCSSPropAliasList.h
+++ b/layout/style/nsCSSPropAliasList.h
@@ -30,28 +30,28 @@
   than needing the CSS_PROP_DOMPROP_PREFIXED(prop) macro).
 
   -. 'pref' is the name of a pref that controls whether the property
   is enabled.  The property is enabled if 'pref' is an empty string,
   or if the boolean property whose name is 'pref' is set to true.
 
  ******/
 
-CSS_PROP_ALIAS(-moz-transform-origin, transform_origin, MozTransformOrigin, "")
-CSS_PROP_ALIAS(-moz-perspective-origin, perspective_origin, MozPerspectiveOrigin, "")
-CSS_PROP_ALIAS(-moz-perspective, perspective, MozPerspective, "")
-CSS_PROP_ALIAS(-moz-transform-style, transform_style, MozTransformStyle, "")
-CSS_PROP_ALIAS(-moz-backface-visibility, backface_visibility, MozBackfaceVisibility, "")
-CSS_PROP_ALIAS(-moz-border-image, border_image, MozBorderImage, "")
-CSS_PROP_ALIAS(-moz-transition, transition, MozTransition, "")
-CSS_PROP_ALIAS(-moz-transition-delay, transition_delay, MozTransitionDelay, "")
-CSS_PROP_ALIAS(-moz-transition-duration, transition_duration, MozTransitionDuration, "")
-CSS_PROP_ALIAS(-moz-transition-property, transition_property, MozTransitionProperty, "")
-CSS_PROP_ALIAS(-moz-transition-timing-function, transition_timing_function, MozTransitionTimingFunction, "")
-CSS_PROP_ALIAS(-moz-animation, animation, MozAnimation, "")
-CSS_PROP_ALIAS(-moz-animation-delay, animation_delay, MozAnimationDelay, "")
-CSS_PROP_ALIAS(-moz-animation-direction, animation_direction, MozAnimationDirection, "")
-CSS_PROP_ALIAS(-moz-animation-duration, animation_duration, MozAnimationDuration, "")
-CSS_PROP_ALIAS(-moz-animation-fill-mode, animation_fill_mode, MozAnimationFillMode, "")
-CSS_PROP_ALIAS(-moz-animation-iteration-count, animation_iteration_count, MozAnimationIterationCount, "")
-CSS_PROP_ALIAS(-moz-animation-name, animation_name, MozAnimationName, "")
-CSS_PROP_ALIAS(-moz-animation-play-state, animation_play_state, MozAnimationPlayState, "")
-CSS_PROP_ALIAS(-moz-animation-timing-function, animation_timing_function, MozAnimationTimingFunction, "")
+CSS_PROP_ALIAS(-moz-transform-origin, transform_origin, MozTransformOrigin, "layout.css.prefixes.transforms")
+CSS_PROP_ALIAS(-moz-perspective-origin, perspective_origin, MozPerspectiveOrigin, "layout.css.prefixes.transforms")
+CSS_PROP_ALIAS(-moz-perspective, perspective, MozPerspective, "layout.css.prefixes.transforms")
+CSS_PROP_ALIAS(-moz-transform-style, transform_style, MozTransformStyle, "layout.css.prefixes.transforms")
+CSS_PROP_ALIAS(-moz-backface-visibility, backface_visibility, MozBackfaceVisibility, "layout.css.prefixes.transforms")
+CSS_PROP_ALIAS(-moz-border-image, border_image, MozBorderImage, "layout.css.prefixes.border-image")
+CSS_PROP_ALIAS(-moz-transition, transition, MozTransition, "layout.css.prefixes.transitions")
+CSS_PROP_ALIAS(-moz-transition-delay, transition_delay, MozTransitionDelay, "layout.css.prefixes.transitions")
+CSS_PROP_ALIAS(-moz-transition-duration, transition_duration, MozTransitionDuration, "layout.css.prefixes.transitions")
+CSS_PROP_ALIAS(-moz-transition-property, transition_property, MozTransitionProperty, "layout.css.prefixes.transitions")
+CSS_PROP_ALIAS(-moz-transition-timing-function, transition_timing_function, MozTransitionTimingFunction, "layout.css.prefixes.transitions")
+CSS_PROP_ALIAS(-moz-animation, animation, MozAnimation, "layout.css.prefixes.animations")
+CSS_PROP_ALIAS(-moz-animation-delay, animation_delay, MozAnimationDelay, "layout.css.prefixes.animations")
+CSS_PROP_ALIAS(-moz-animation-direction, animation_direction, MozAnimationDirection, "layout.css.prefixes.animations")
+CSS_PROP_ALIAS(-moz-animation-duration, animation_duration, MozAnimationDuration, "layout.css.prefixes.animations")
+CSS_PROP_ALIAS(-moz-animation-fill-mode, animation_fill_mode, MozAnimationFillMode, "layout.css.prefixes.animations")
+CSS_PROP_ALIAS(-moz-animation-iteration-count, animation_iteration_count, MozAnimationIterationCount, "layout.css.prefixes.animations")
+CSS_PROP_ALIAS(-moz-animation-name, animation_name, MozAnimationName, "layout.css.prefixes.animations")
+CSS_PROP_ALIAS(-moz-animation-play-state, animation_play_state, MozAnimationPlayState, "layout.css.prefixes.animations")
+CSS_PROP_ALIAS(-moz-animation-timing-function, animation_timing_function, MozAnimationTimingFunction, "layout.css.prefixes.animations")
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -3361,17 +3361,17 @@ CSS_PROP_SVGRESET(
 // The shorthands below are essentially aliases, but they require different
 // parsing rules, and are therefore implemented as shorthands.
 CSS_PROP_SHORTHAND(
     -moz-transform,
     _moz_transform,
     MozTransform,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_IS_ALIAS,
-    "")
+    "layout.css.prefixes.transforms")
 
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 // We have a few properties that are in style structs but are not stored
 // in style sheets (or nsCSS* structs).  Some fields in these property
 // definitions are bogus (e.g., they work for nsRuleData* offsets but
 // not nsCSS* offsets).  Callers that care about these bogus fields can
 // define CSS_PROP_STUB_NOT_CSS to define a replacement for these
 // entries.
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -252,27 +252,46 @@ static nscoord CalcLengthWith(const nsCS
   NS_ASSERTION(aPresContext, "Must have prescontext");
 
   if (aValue.IsFixedLengthUnit()) {
     return aValue.GetFixedLength(aPresContext);
   }
   if (aValue.IsPixelLengthUnit()) {
     return aValue.GetPixelLength();
   }
-  // Common code for all units other than pixel-based units and fixed-length
-  // units:
-  aCanStoreInRuleTree = false;
-  const nsStyleFont *styleFont =
-    aStyleFont ? aStyleFont : aStyleContext->GetStyleFont();
-  if (aFontSize == -1) {
-    // XXX Should this be styleFont->mSize instead to avoid taking minfontsize
-    // prefs into account?
-    aFontSize = styleFont->mFont.size;
+  if (aValue.IsCalcUnit()) {
+    // For properties for which lengths are the *only* units accepted in
+    // calc(), we can handle calc() here and just compute a final
+    // result.  We ensure that we don't get to this code for other
+    // properties by not calling CalcLength in those cases:  SetCoord
+    // only calls CalcLength for a calc when it is appropriate to do so.
+    CalcLengthCalcOps ops(aFontSize, aStyleFont,
+                          aStyleContext, aPresContext,
+                          aUseProvidedRootEmSize, aUseUserFontSet,
+                          aCanStoreInRuleTree);
+    return css::ComputeCalc(aValue, ops);
   }
   switch (aValue.GetUnit()) {
+    // nsPresContext::SetVisibleArea and
+    // nsPresContext::MediaFeatureValuesChanged handle dynamic changes
+    // of the basis for viewport units by rebuilding the rule tree and
+    // style context tree.  Not caching them in the rule tree wouldn't
+    // be sufficient to handle these changes because we also need a way
+    // to get rid of cached values in the style context tree without any
+    // changes in specified style.  We can either do this by not caching
+    // in the rule tree and then throwing away the style context tree
+    // for dynamic viewport size changes, or by allowing caching in the
+    // rule tree and using the existing rebuild style data path that
+    // throws away the style context and the rule tree.
+    // Thus we do cache viewport units in the rule tree.  This allows us
+    // to benefit from the performance advantages of the rule tree
+    // (e.g., faster dynamic changes on other things, like transforms)
+    // and allows us not to need an additional code path, in exchange
+    // for an increased cost to dynamic changes to the viewport size
+    // when viewport units are in use.
     case eCSSUnit_ViewportWidth: {
       aPresContext->SetUsesViewportUnits(true);
       return ScaleCoord(aValue, 0.01f * aPresContext->GetVisibleArea().width);
     }
     case eCSSUnit_ViewportHeight: {
       aPresContext->SetUsesViewportUnits(true);
       return ScaleCoord(aValue, 0.01f * aPresContext->GetVisibleArea().height);
     }
@@ -281,52 +300,79 @@ static nscoord CalcLengthWith(const nsCS
       nsSize viewportSize = aPresContext->GetVisibleArea().Size();
       return ScaleCoord(aValue, 0.01f * min(viewportSize.width, viewportSize.height));
     }
     case eCSSUnit_ViewportMax: {
       aPresContext->SetUsesViewportUnits(true);
       nsSize viewportSize = aPresContext->GetVisibleArea().Size();
       return ScaleCoord(aValue, 0.01f * max(viewportSize.width, viewportSize.height));
     }
+    // While we could deal with 'rem' units correctly by simply not
+    // caching any data that uses them in the rule tree, it's valuable
+    // to store them in the rule tree (for faster dynamic changes of
+    // other things).  And since the font size of the root element
+    // changes rarely, we instead handle dynamic changes to the root
+    // element's font size by rebuilding all style data in
+    // nsCSSFrameConstructor::RestyleElement.
     case eCSSUnit_RootEM: {
+      aPresContext->SetUsesRootEMUnits(true);
       nscoord rootFontSize;
 
       if (aUseProvidedRootEmSize) {
         // We should use the provided aFontSize as the reference length to
         // scale. This only happens when we are calculating font-size or
         // an equivalent (scriptminsize or CalcLengthWithInitialFont) on
         // the root element, in which case aFontSize is already the
         // value we want.
         rootFontSize = aFontSize;
       } else if (aStyleContext && !aStyleContext->GetParent()) {
         // This is the root element (XXX we don't really know this, but
         // nsRuleNode::SetFont makes the same assumption!), so we should
         // use GetStyleFont on this context to get the root element's
         // font size.
+        const nsStyleFont *styleFont =
+          aStyleFont ? aStyleFont : aStyleContext->GetStyleFont();
         rootFontSize = styleFont->mFont.size;
       } else {
         // This is not the root element or we are calculating something other
         // than font size, so rem is relative to the root element's font size.
         nsRefPtr<nsStyleContext> rootStyle;
-        const nsStyleFont *rootStyleFont = styleFont;
+        const nsStyleFont *rootStyleFont =
+          aStyleFont ? aStyleFont : aStyleContext->GetStyleFont();
         Element* docElement = aPresContext->Document()->GetRootElement();
 
         if (docElement) {
           rootStyle = aPresContext->StyleSet()->ResolveStyleFor(docElement,
                                                                 nullptr);
           if (rootStyle) {
             rootStyleFont = rootStyle->GetStyleFont();
           }
         }
 
         rootFontSize = rootStyleFont->mFont.size;
       }
 
       return ScaleCoord(aValue, float(rootFontSize));
     }
+    default:
+      // Fall through to the code for units that can't be stored in the
+      // rule tree because they depend on font data.
+      break;
+  }
+  // Common code for units that depend on the element's font data and
+  // thus can't be stored in the rule tree:
+  aCanStoreInRuleTree = false;
+  const nsStyleFont *styleFont =
+    aStyleFont ? aStyleFont : aStyleContext->GetStyleFont();
+  if (aFontSize == -1) {
+    // XXX Should this be styleFont->mSize instead to avoid taking minfontsize
+    // prefs into account?
+    aFontSize = styleFont->mFont.size;
+  }
+  switch (aValue.GetUnit()) {
     case eCSSUnit_EM: {
       return ScaleCoord(aValue, float(aFontSize));
       // XXX scale against font metrics height instead?
     }
     case eCSSUnit_XHeight: {
       nsRefPtr<nsFontMetrics> fm =
         GetMetricsFor(aPresContext, aStyleContext, styleFont,
                       aFontSize, aUseUserFontSet);
@@ -337,33 +383,16 @@ static nscoord CalcLengthWith(const nsCS
         GetMetricsFor(aPresContext, aStyleContext, styleFont,
                       aFontSize, aUseUserFontSet);
       gfxFloat zeroWidth = (fm->GetThebesFontGroup()->GetFontAt(0)
                             ->GetMetrics().zeroOrAveCharWidth);
 
       return ScaleCoord(aValue, ceil(aPresContext->AppUnitsPerDevPixel() *
                                      zeroWidth));
     }
-    // For properties for which lengths are the *only* units accepted in
-    // calc(), we can handle calc() here and just compute a final
-    // result.  We ensure that we don't get to this code for other
-    // properties by not calling CalcLength in those cases:  SetCoord
-    // only calls CalcLength for a calc when it is appropriate to do so.
-    case eCSSUnit_Calc:
-    case eCSSUnit_Calc_Plus:
-    case eCSSUnit_Calc_Minus:
-    case eCSSUnit_Calc_Times_L:
-    case eCSSUnit_Calc_Times_R:
-    case eCSSUnit_Calc_Divided: {
-      CalcLengthCalcOps ops(aFontSize, aStyleFont,
-                            aStyleContext, aPresContext,
-                            aUseProvidedRootEmSize, aUseUserFontSet,
-                            aCanStoreInRuleTree);
-      return css::ComputeCalc(aValue, ops);
-    }
     default:
       NS_NOTREACHED("unexpected unit");
       break;
   }
   return 0;
 }
 
 /* static */ nscoord
--- a/layout/style/test/Makefile.in
+++ b/layout/style/test/Makefile.in
@@ -145,16 +145,17 @@ MOCHITEST_FILES =	test_acid3_test46.html
 		test_unclosed_parentheses.html \
 		test_units_angle.html \
 		test_units_frequency.html \
 		test_units_length.html \
 		test_units_time.html \
 		test_value_cloning.html \
 		test_value_computation.html \
 		test_value_storage.html \
+		test_viewport_units.html \
 		test_visited_image_loading.html \
 		test_visited_image_loading_empty.html \
 		test_visited_lying.html \
 		test_visited_pref.html \
 		test_visited_reftests.html \
 		animation_utils.js \
 		css_properties.js \
 		property_database.js \
@@ -185,16 +186,17 @@ MOCHITEST_FILES =	test_acid3_test46.html
 		visited_image_loading_frame.html \
 		visited_image_loading_frame_empty.html \
 		test_load_events_on_stylesheets.html \
 		test_bug721136.html \
 		test_bug732153.html \
 		test_bug732209.html \
 		bug732209-css.sjs \
 		test_bug795520.html \
+		viewport_units_iframe.html \
 		$(NULL)
 
 ifdef MOZ_FLEXBOX
 MOCHITEST_FILES +=	\
 		file_flexbox_align_self_auto.html \
 		test_flexbox_align_self_auto.html \
 		file_flexbox_flex_grow_and_shrink.html \
 		test_flexbox_flex_grow_and_shrink.html \
copy from layout/style/test/test_rem_unit.html
copy to layout/style/test/test_viewport_units.html
--- a/layout/style/test/test_rem_unit.html
+++ b/layout/style/test/test_viewport_units.html
@@ -1,80 +1,66 @@
 <!DOCTYPE HTML>
 <html>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=478321
+https://bugzilla.mozilla.org/show_bug.cgi?id=804970
 -->
 <head>
-  <title>Test for CSS 'rem' unit</title>
+  <title>Test for dynamic changes to CSS 'vh', 'vw', 'vmin', and 'vmax' units</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=478321">Mozilla Bug 478321</a>
-<p id="display"></p>
-<p id="display2"></p>
-<div id="content" style="display: none">
-  
-</div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=804970">Mozilla Bug 804970</a>
+<iframe id="iframe" src="viewport_units_iframe.html"></iframe>
 <pre id="test">
 <script type="application/javascript">
 
-/** Test for CSS 'rem' unit **/
+/** Test for CSS vh/vw/vmin/vmax units **/
 
 function px_to_num(str)
 {
     return Number(String(str).match(/^([\d.]+)px$/)[1]);
 }
 
-function fs(elt)
+function width(elt)
 {
-    return px_to_num(getComputedStyle(elt, "").fontSize);
+    return px_to_num(elt.ownerDocument.defaultView.getComputedStyle(elt, "").width);
 }
 
-var html = document.documentElement;
-var body = document.body;
-var p = document.getElementById("display");
-var p2 = document.getElementById("display2");
-
-html.style.font = "-moz-initial";
+SimpleTest.waitForExplicitFinish();
 
-var defaultFontSize = fs(html);
+function run() {
+    var iframe = document.getElementById("iframe");
+    var idoc = iframe.contentDocument;
+    var vh = idoc.getElementById("vh");
+    var vw = idoc.getElementById("vw");
+    var vmin = idoc.getElementById("vmin");
+    var vmax = idoc.getElementById("vmax");
 
-// NOTE:  This test assumes that the default font size is an
-// integral number of pixels (which is always the case at present).
-// If that ever becomes false, the calls to "is" may need to be replaced by
-// calls to "isapprox" that allows errors of up to some small fraction
-// of a pixel.
+    iframe.style.width = "100px";
+    iframe.style.height = "250px";
+    is(width(vh), 250, "vh should be 250px");
+    is(width(vw), 100, "vw should be 100px");
+    is(width(vmin), 100, "vmin should be 100px");
+    is(width(vmax), 250, "vmax should be 250px");
 
-html.style.fontSize = "3rem";
-is(fs(html), 3 * defaultFontSize,
-   "3rem on root should triple root's font size");
-body.style.font = "-moz-initial";
-is(fs(body), defaultFontSize,
-   "-moz-initial should produce initial font size");
-body.style.fontSize = "1em";
-is(fs(body), 3 * defaultFontSize, "1em should inherit from parent");
-body.style.fontSize = "200%";
-is(fs(body), 6 * defaultFontSize, "200% should double parent");
-body.style.fontSize = "2rem";
-is(fs(body), 6 * defaultFontSize, "2rem should double root");
-p.style.font = "inherit";
-is(fs(p), 6 * defaultFontSize, "inherit should inherit from parent");
-p2.style.fontSize = "2rem";
-is(fs(p2), 6 * defaultFontSize, "2rem should double root");
-body.style.font = "-moz-initial";
-is(fs(p), defaultFontSize, "inherit should inherit from parent");
-is(fs(p2), 6 * defaultFontSize, "2rem should double root");
-body.style.fontSize = "5em";
-html.style.fontSize = "200%";
-is(fs(p), 10 * defaultFontSize, "inherit should inherit from parent");
-is(fs(p2), 4 * defaultFontSize, "2rem should double root");
+    iframe.style.width = "300px";
+    is(width(vh), 250, "vh should be 250px");
+    is(width(vw), 300, "vw should be 300px");
+    is(width(vmin), 250, "vmin should be 250px");
+    is(width(vmax), 300, "vmax should be 300px");
 
+    iframe.style.height = "200px";
+    is(width(vh), 200, "vh should be 200px");
+    is(width(vw), 300, "vw should be 300px");
+    is(width(vmin), 200, "vmin should be 200px");
+    is(width(vmax), 300, "vmax should be 300px");
 
-// Make things readable again.
-html.style.fontSize = "1em";
-body.style.fontSize = "1em";
+    SimpleTest.finish();
+}
+
+window.addEventListener("load", run, false);
 
 </script>
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/style/test/viewport_units_iframe.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<title>viewport units test</title>
+<div id="vh" style="width: 100vh"></div>
+<div id="vw" style="width: 100vw"></div>
+<div id="vmin" style="width: 100vmin"></div>
+<div id="vmax" style="width: 100vmax"></div>
--- a/layout/xul/base/public/nsXULPopupManager.h
+++ b/layout/xul/base/public/nsXULPopupManager.h
@@ -281,21 +281,22 @@ public:
   friend class nsXULMenuCommandEvent;
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSIDOMEVENTLISTENER
 
   // nsIRollupListener
-  virtual nsIContent* Rollup(uint32_t aCount, bool aGetLastRolledUp = false);
+  virtual bool Rollup(uint32_t aCount, nsIContent** aLastRolledUp);
   virtual bool ShouldRollupOnMouseWheelEvent();
   virtual bool ShouldRollupOnMouseActivate();
   virtual uint32_t GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain);
   virtual void NotifyGeometryChange() {}
+  virtual nsIWidget* GetRollupWidget();
 
   static nsXULPopupManager* sInstance;
 
   // initialize and shutdown methods called by nsLayoutStatics
   static nsresult Init();
   static void Shutdown();
 
   // returns a weak reference to the popup manager instance, could return null
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -159,58 +159,61 @@ nsXULPopupManager::Observe(nsISupports *
   }
 
   return NS_OK;
 }
 
 nsXULPopupManager*
 nsXULPopupManager::GetInstance()
 {
+  MOZ_ASSERT(sInstance);
   return sInstance;
 }
 
-nsIContent*
-nsXULPopupManager::Rollup(uint32_t aCount, bool aGetLastRolledUp)
+bool
+nsXULPopupManager::Rollup(uint32_t aCount, nsIContent** aLastRolledUp)
 {
-  nsIContent* lastRolledUpPopup = nullptr;
+  bool consume = false;
 
   nsMenuChainItem* item = GetTopVisibleMenu();
   if (item) {
-    if (aGetLastRolledUp) {
+    if (aLastRolledUp) {
       // we need to get the popup that will be closed last, so that
       // widget can keep track of it so it doesn't reopen if a mouse
       // down event is going to processed.
       // Keep going up the menu chain to get the first level menu. This will
       // be the one that closes up last. It's possible that this menu doesn't
       // end up closing because the popuphiding event was cancelled, but in
       // that case we don't need to deal with the menu reopening as it will
       // already still be open.
       nsMenuChainItem* first = item;
       while (first->GetParent())
         first = first->GetParent();
-      lastRolledUpPopup = first->Content();
+      *aLastRolledUp = first->Content();
     }
 
+    consume = item->Frame()->ConsumeOutsideClicks();
+
     // if a number of popups to close has been specified, determine the last
     // popup to close
     nsIContent* lastPopup = nullptr;
     if (aCount != UINT32_MAX) {
       nsMenuChainItem* last = item;
       while (--aCount && last->GetParent()) {
         last = last->GetParent();
       }
       if (last) {
         lastPopup = last->Content();
       }
     }
 
     HidePopup(item->Content(), true, true, false, lastPopup);
   }
 
-  return lastRolledUpPopup;
+  return consume;
 }
 
 ////////////////////////////////////////////////////////////////////////
 bool nsXULPopupManager::ShouldRollupOnMouseWheelEvent()
 {
   // should rollup only for autocomplete widgets
   // XXXndeakin this should really be something the popup has more control over
 
@@ -259,16 +262,23 @@ nsXULPopupManager::GetSubmenuWidgetChain
       }
     }
     item = parent;
   }
 
   return sameTypeCount;
 }
 
+nsIWidget*
+nsXULPopupManager::GetRollupWidget()
+{
+  nsMenuChainItem* item = GetTopVisibleMenu();
+  return item ? item->Frame()->GetWidget() : nullptr;
+}
+
 void
 nsXULPopupManager::AdjustPopupsOnWindowChange(nsPIDOMWindow* aWindow)
 {
   // When the parent window is moved, adjust any child popups. Dismissable
   // menus and panels are expected to roll up when a window is moved, so there
   // is no need to check these popups, only the noautohide popups.
   nsMenuChainItem* item = mNoHidePanels;
   while (item) {
@@ -1540,25 +1550,25 @@ nsXULPopupManager::HasContextMenu(nsMenu
 void
 nsXULPopupManager::SetCaptureState(nsIContent* aOldPopup)
 {
   nsMenuChainItem* item = GetTopVisibleMenu();
   if (item && aOldPopup == item->Content())
     return;
 
   if (mWidget) {
-    mWidget->CaptureRollupEvents(this, false, false);
+    mWidget->CaptureRollupEvents(nullptr, false);
     mWidget = nullptr;
   }
 
   if (item) {
     nsMenuPopupFrame* popup = item->Frame();
     mWidget = popup->GetWidget();
     if (mWidget) {
-      mWidget->CaptureRollupEvents(this, true, popup->ConsumeOutsideClicks());
+      mWidget->CaptureRollupEvents(nullptr, true);
       popup->AttachedDismissalListener();
     }
   }
 
   UpdateKeyboardListeners();
 }
 
 void
@@ -2097,17 +2107,17 @@ nsXULPopupManager::KeyDown(nsIDOMKeyEven
         aKeyEvent->GetShiftKey(&shift);
       bool meta=false;
       if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_META)
         aKeyEvent->GetMetaKey(&meta);
       if (!(ctrl || alt || shift || meta)) {
         // The access key just went down and no other
         // modifiers are already down.
         if (mPopups)
-          Rollup(0);
+          Rollup(0, nullptr);
         else if (mActiveMenuBar)
           mActiveMenuBar->MenuClosed();
       }
     }
   }
 
   // Since a menu was open, eat the event to keep other event
   // listeners from becoming confused.
@@ -2174,17 +2184,17 @@ nsXULPopupManager::KeyPress(nsIDOMKeyEve
   }
   else if (theChar == NS_VK_TAB
 #ifndef XP_MACOSX
            || theChar == NS_VK_F10
 #endif
   ) {
     // close popups or deactivate menubar when Tab or F10 are pressed
     if (item)
-      Rollup(0);
+      Rollup(0, nullptr);
     else if (mActiveMenuBar)
       mActiveMenuBar->MenuClosed();
   }
   else if (theChar == NS_VK_ENTER ||
            theChar == NS_VK_RETURN) {
     // If there is a popup open, check if the current item needs to be opened.
     // Otherwise, tell the active menubar, if any, to activate the menu. The
     // Enter method will return a menu if one needs to be opened as a result.
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -351,16 +351,19 @@
  * enumerations, implicit conversions of Enum values to numeric types will
  * fail.  In other compilers, Enum itself will actually be defined as a class,
  * and some implicit conversions will fail while others will succeed.
  *
  * The type argument specifies the underlying type for the enum where
  * supported, as with MOZ_ENUM_TYPE().  For simplicity, it is currently
  * mandatory.  As with MOZ_ENUM_TYPE(), it will do nothing on compilers that do
  * not support it.
+ *
+ * Note that the workaround implemented here is not compatible with enums
+ * nested inside a class.
  */
 #if defined(MOZ_HAVE_CXX11_STRONG_ENUMS)
   /* All compilers that support strong enums also support an explicit
    * underlying type, so no extra check is needed */
 #  define MOZ_BEGIN_ENUM_CLASS(Name, type) enum class Name : type {
 #  define MOZ_END_ENUM_CLASS(Name)         };
 #else
    /**
--- a/mfbt/NullPtr.h
+++ b/mfbt/NullPtr.h
@@ -15,17 +15,17 @@
 #  ifndef __has_extension
 #    define __has_extension __has_feature
 #  endif
 #  if __has_extension(cxx_nullptr)
 #    define MOZ_HAVE_CXX11_NULLPTR
 #  endif
 #elif defined(__GNUC__)
 #  if defined(_GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
-#    if (__GNUC__ * 1000 + __GNU_MINOR__) >= 4006
+#    if (__GNUC__ * 1000 + __GNUC_MINOR__) >= 4006
 #      define MOZ_HAVE_CXX11_NULLPTR
 #    endif
 #  endif
 #elif _MSC_VER >= 1600
 # define MOZ_HAVE_CXX11_NULLPTR
 #endif
 
 /**
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5632,18 +5632,19 @@ var PluginHelper = {
           if (aChecked)
             Services.perms.add(uri, "plugins", Ci.nsIPermissionManager.DENY_ACTION);
 
           // Other than that, do nothing
         }
       }
     ];
 
-    // Add a checkbox with a "Don't ask again" message
-    let options = { checkbox: Strings.browser.GetStringFromName("clickToPlayPlugins.dontAskAgain") };
+    // Add a checkbox with a "Don't ask again" message if the uri contains a
+    // host. Adding a permanent exception will fail if host is not present.
+    let options = uri.host ? { checkbox: Strings.browser.GetStringFromName("clickToPlayPlugins.dontAskAgain") } : {};
 
     NativeWindow.doorhanger.show(message, "ask-to-play-plugins", buttons, aTab.id, options);
   },
 
   playAllPlugins: function(aContentWindow) {
     let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIDOMWindowUtils);
     // XXX not sure if we should enable plugins for the parent documents...
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -353,16 +353,17 @@ pref("toolkit.telemetry.server", "https:
 pref("toolkit.telemetry.server_owner", "Mozilla");
 // Information page about telemetry (temporary ; will be about:telemetry in the end)
 pref("toolkit.telemetry.infoURL", "http://www.mozilla.com/legal/privacy/firefox.html#telemetry");
 // Determines whether full SQL strings are returned when they might contain sensitive info
 // i.e. dynamically constructed SQL strings or SQL executed by addons against addon DBs
 pref("toolkit.telemetry.debugSlowSql", false);
 
 // Identity module
+pref("toolkit.identity.enabled", false);
 pref("toolkit.identity.debug", false);
 
 // Disable remote debugging protocol logging
 pref("devtools.debugger.log", false);
 // Disable remote debugging connections
 pref("devtools.debugger.remote-enabled", false);
 pref("devtools.debugger.remote-port", 6000);
 // Force debugger server binding on the loopback interface
@@ -1604,16 +1605,22 @@ pref("layout.css.dpi", -1);
 pref("layout.css.devPixelsPerPx", "-1.0");
 
 // Is support for the the @supports rule enabled?
 pref("layout.css.supports-rule.enabled", true);
 
 // Is support for CSS Flexbox enabled?
 pref("layout.css.flexbox.enabled", false);
 
+// Are sets of prefixed properties supported?
+pref("layout.css.prefixes.border-image", true);
+pref("layout.css.prefixes.transforms", true);
+pref("layout.css.prefixes.transitions", true);
+pref("layout.css.prefixes.animations", true);
+
 // pref for which side vertical scrollbars should be on
 // 0 = end-side in UI direction
 // 1 = end-side in document/content direction
 // 2 = right
 // 3 = left
 pref("layout.scrollbar.side", 0);
 
 // pref to control browser frame rate, in Hz. A value <= 0 means choose
@@ -3792,18 +3799,16 @@ pref("memory.low_memory_notification_int
 pref("memory.ghost_window_timeout_seconds", 60);
 
 pref("social.enabled", false);
 
 // Disable idle observer fuzz, because only privileged content can access idle
 // observers (bug 780507).
 pref("dom.idle-observers-api.fuzz_time.disabled", true);
 
-pref("toolkit.identity.debug", false);
-
 // Setting that to true grant elevated privileges to apps that ask
 // for them in their manifest.
 pref("dom.mozApps.dev_mode", false);
 
 // Lowest localId for apps.
 pref("dom.mozApps.maxLocalId", 1000);
 
 // Minimum delay in milliseconds between network activity notifications (0 means
--- a/netwerk/base/public/nsIApplicationCacheService.idl
+++ b/netwerk/base/public/nsIApplicationCacheService.idl
@@ -10,27 +10,34 @@ interface nsIApplicationCache;
 interface nsIFile;
 interface nsIURI;
 interface nsILoadContext;
 
 /**
  * The application cache service manages the set of application cache
  * groups.
  */
-[scriptable, uuid(1750F671-0170-4d3f-A836-455501141E32)]
+[scriptable, uuid(9b5b2cde-d5dd-48d3-87f8-8e8b776952a8)]
 interface nsIApplicationCacheService : nsISupports
 {
     /**
      * Create group string identifying cache group according the manifest
      * URL and the given load context.
      */
     ACString buildGroupID(in nsIURI aManifestURL,
                           in nsILoadContext aLoadContext);
 
     /**
+     * Same as buildGroupID method, just doesn't require load context.
+     */
+    ACString buildGroupIDForApp(in nsIURI aManifestURL,
+                                in unsigned long aAppID,
+                                in boolean aInBrowser);
+
+    /**
      * Create a new, empty application cache for the given cache
      * group.
      */
     nsIApplicationCache createApplicationCache(in ACString group);
 
     /**
      * Create a new, empty application cache for the given cache
      * group residing in a custom directory with a custom quota.
--- a/netwerk/cache/nsApplicationCacheService.cpp
+++ b/netwerk/cache/nsApplicationCacheService.cpp
@@ -26,18 +26,44 @@ nsApplicationCacheService::nsApplication
     mCacheService = nsCacheService::GlobalInstance();
 }
 
 NS_IMETHODIMP
 nsApplicationCacheService::BuildGroupID(nsIURI *aManifestURL,
                                         nsILoadContext *aLoadContext,
                                         nsACString &_result)
 {
+    nsresult rv;
+
+    uint32_t appId = NECKO_NO_APP_ID;
+    bool isInBrowserElement = false;
+
+    if (aLoadContext) {
+        rv = aLoadContext->GetAppId(&appId);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = aLoadContext->GetIsInBrowserElement(&isInBrowserElement);
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    rv = nsOfflineCacheDevice::BuildApplicationCacheGroupID(
+        aManifestURL, appId, isInBrowserElement, _result);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsApplicationCacheService::BuildGroupIDForApp(nsIURI *aManifestURL,
+                                              uint32_t aAppId,
+                                              bool aIsInBrowser,
+                                              nsACString &_result)
+{
     nsresult rv = nsOfflineCacheDevice::BuildApplicationCacheGroupID(
-        aManifestURL, aLoadContext, _result);
+        aManifestURL, aAppId, aIsInBrowser, _result);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsApplicationCacheService::CreateApplicationCache(const nsACString &group,
                                                   nsIApplicationCache **out)
--- a/netwerk/cache/nsApplicationCacheService.h
+++ b/netwerk/cache/nsApplicationCacheService.h
@@ -16,16 +16,12 @@ public:
     nsApplicationCacheService();
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIAPPLICATIONCACHESERVICE
 
     static void AppClearDataObserverInit();
 
 private:
-    nsresult GetJARIdentifier(nsIURI *aURI,
-                              nsILoadContext *aLoadContext,
-                              nsACString &_result);
-
     nsRefPtr<nsCacheService> mCacheService;
 };
 
 #endif // _nsApplicationCacheService_h_
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -35,22 +35,16 @@
 #include "nsNetCID.h"
 #include <math.h>  // for log()
 #include "mozilla/Services.h"
 #include "nsITimer.h"
 
 
 #include "mozilla/net/NeckoCommon.h"
 
-#ifdef XP_MACOSX
-// for chflags()
-#include <sys/stat.h>
-#include <unistd.h>
-#endif
-
 using namespace mozilla;
 
 /******************************************************************************
  * nsCacheProfilePrefObserver
  *****************************************************************************/
 #define DISK_CACHE_ENABLE_PREF      "browser.cache.disk.enable"
 #define DISK_CACHE_DIR_PREF         "browser.cache.disk.parent_directory"
 #define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
@@ -727,26 +721,16 @@ nsCacheProfilePrefObserver::ReadPrefs(ns
         if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
             rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
                                         getter_AddRefs(directory));
         }
         if (directory)
             mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
     }
     if (mDiskCacheParentDirectory) {
-#ifdef XP_MACOSX
-        // ensure that this directory is not indexed by Spotlight
-        // (bug 718910). it may already exist, so we "just do it."
-        nsAutoCString cachePD;
-        if (NS_SUCCEEDED(mDiskCacheParentDirectory->GetNativePath(cachePD))) {
-            if (chflags(cachePD.get(), UF_HIDDEN)) {
-                NS_WARNING("Failed to set CacheParentDirectory to HIDDEN.");
-            }
-        }
-#endif 
         bool firstSmartSizeRun;
         rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF, 
                                  &firstSmartSizeRun); 
         if (NS_FAILED(rv)) 
             firstSmartSizeRun = false;
         if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
             // Avoid evictions: use previous cache size until smart size event
             // updates mDiskCacheCapacity
--- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp
@@ -1254,63 +1254,52 @@ AppendJARIdentifier(nsACString &_result,
     _result.Append('+');
     _result.Append(isInBrowserElement ? 't' : 'f');
 
     return NS_OK;
 }
 
 nsresult
 GetJARIdentifier(nsIURI *aURI,
-                 nsILoadContext *aLoadContext,
+                 uint32_t appId, bool isInBrowserElement,
                  nsACString &_result)
 {
     _result.Truncate();
 
-    if (!aLoadContext)
-        return NS_OK;
-
     // These lines are here for compatibility only.  We must not fill the
     // JAR identifier when this is no-app context, otherwise web content
     // offline application cache loads would not be satisfied (cache would
     // not be found).
-    bool isInBrowserElement;
-    nsresult rv = aLoadContext->GetIsInBrowserElement(&isInBrowserElement);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    uint32_t appId;
-    rv = aLoadContext->GetAppId(&appId);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     if (!isInBrowserElement && appId == NECKO_NO_APP_ID)
         return NS_OK;
 
     // This load context has some special attributes, create a jar identifier
     return AppendJARIdentifier(_result, appId, isInBrowserElement);
 }
 
 } // anon namespace
 
 // static
 nsresult
 nsOfflineCacheDevice::BuildApplicationCacheGroupID(nsIURI *aManifestURL,
-                                                   nsILoadContext *aLoadContext,
+                                                   uint32_t appId, bool isInBrowserElement,
                                                    nsACString &_result)
 {
   nsCOMPtr<nsIURI> newURI;
   nsresult rv = aManifestURL->CloneIgnoringRef(getter_AddRefs(newURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString manifestSpec;
   rv = newURI->GetAsciiSpec(manifestSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   _result.Assign(manifestSpec);
 
   nsAutoCString jarid;
-  rv = GetJARIdentifier(aManifestURL, aLoadContext, jarid);
+  rv = GetJARIdentifier(aManifestURL, appId, isInBrowserElement, jarid);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Include JAR ID, i.e. the extended origin if present.
   if (!jarid.IsEmpty())
     _result.Append(jarid);
 
   return NS_OK;
 }
@@ -2437,21 +2426,34 @@ nsOfflineCacheDevice::CanUseCache(nsIURI
   // the same origin as the manifest, according to the spec.
   // The following check is here because explicit, fallback
   // and dynamic entries might have origin different from the
   // manifest origin.
   if (!NS_SecurityCompareURIs(keyURI, groupURI,
                               GetStrictFileOriginPolicy()))
     return false;
 
+  // Get extended origin attributes
+  uint32_t appId = NECKO_NO_APP_ID;
+  bool isInBrowserElement = false;
+
+  if (loadContext) {
+      rv = loadContext->GetAppId(&appId);
+      NS_ENSURE_SUCCESS(rv, false);
+
+      rv = loadContext->GetIsInBrowserElement(&isInBrowserElement);
+      NS_ENSURE_SUCCESS(rv, false);
+  }
+
   // Check the groupID we found is equal to groupID based
   // on the load context demanding load from app cache.
   // This is check of extended origin.
   nsAutoCString demandedGroupID;
-  rv = BuildApplicationCacheGroupID(groupURI, loadContext, demandedGroupID);
+  rv = BuildApplicationCacheGroupID(groupURI, appId, isInBrowserElement,
+                                    demandedGroupID);
   NS_ENSURE_SUCCESS(rv, false);
 
   if (groupID != demandedGroupID)
     return false;
 
   return true;
 }
 
--- a/netwerk/cache/nsDiskCacheDeviceSQL.h
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.h
@@ -128,17 +128,17 @@ public:
                                      const nsACString &       key,
                                      bool *                 isOwned);
 
   nsresult                ClearKeysOwnedByDomain(const char *clientID,
                                                  const nsACString &ownerDomain);
   nsresult                EvictUnownedEntries(const char *clientID);
 
   static nsresult         BuildApplicationCacheGroupID(nsIURI *aManifestURL,
-                                                       nsILoadContext *aLoadContext,
+                                                       uint32_t appId, bool isInBrowserElement,
                                                        nsACString &_result);
 
   nsresult                ActivateCache(const nsCSubstring &group,
                                         const nsCSubstring &clientID);
   bool                    IsActiveCache(const nsCSubstring &group,
                                         const nsCSubstring &clientID);
   nsresult                CreateApplicationCache(const nsACString &group,
                                                  nsIApplicationCache **out);
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -985,19 +985,20 @@ nsHostResolver::ThreadFunc(void *arg)
                                     Telemetry::DNS_LOOKUP_TIME :
                                     Telemetry::DNS_RENEWAL_TIME,
                                   millis);
         }
         else {
             status = NS_ERROR_UNKNOWN_HOST;
             Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
         }
-        
+
+        // OnLookupComplete may release "rec", log before we lose it.
+        LOG(("Lookup completed for host [%s].\n", rec->host));
         resolver->OnLookupComplete(rec, status, ai);
-        LOG(("Lookup completed for host [%s].\n", rec->host));
     }
     NS_RELEASE(resolver);
     LOG(("DNS lookup thread ending execution.\n"));
 }
 
 //----------------------------------------------------------------------------
 
 nsresult
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2535,16 +2535,21 @@ nsHttpChannel::OpenOfflineCacheEntryForW
     NS_PRECONDITION(!mOfflineCacheEntry, "cache entry already open");
 
     bool offline = gIOService->IsOffline();
     if (offline) {
         // only put things in the offline cache while online
         return NS_OK;
     }
 
+    if (mLoadFlags & INHIBIT_CACHING) {
+        // respect demand not to cache
+        return NS_OK;
+    }
+
     if (mRequestHead.Method() != nsHttp::Get) {
         // only cache complete documents offline
         return NS_OK;
     }
 
     // Don't cache byte range requests which are subranges, only cache 0-
     // byte range requests.
     if (IsSubRangeRequest(mRequestHead))
--- a/security/manager/boot/src/Makefile.in
+++ b/security/manager/boot/src/Makefile.in
@@ -21,15 +21,16 @@ FAIL_ON_WARNINGS = 1
 CPPSRCS = \
 	nsEntropyCollector.cpp \
 	nsSecureBrowserUIImpl.cpp \
 	nsBOOTModule.cpp \
 	nsSecurityWarningDialogs.cpp \
 	nsStrictTransportSecurityService.cpp \
 	$(NULL)
 
+DEFINES += -D__STDC_CONSTANT_MACROS
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES	+= \
 		-I$(DIST)/public/nss \
 		$(NULL)
 
--- a/security/manager/boot/src/nsSTSPreloadList.errors
+++ b/security/manager/boot/src/nsSTSPreloadList.errors
@@ -36,50 +36,52 @@ irccloud.com: did not receive HSTS heade
 jitsi.org: did not receive HSTS header
 jottit.com: could not connect to host
 kyps.net: did not receive HSTS header
 lastpass.com: max-age too low: 8640000
 ledgerscope.net: max-age too low: 86400
 linx.net: could not connect to host
 lists.mayfirst.org: did not receive HSTS header
 login.persona.org: max-age too low: 2592000
-lookout.com: did not receive HSTS header
 mail.google.com: did not receive HSTS header
 market.android.com: did not receive HSTS header
 mydigipass.com: did not receive HSTS header
-mylookout.com: did not receive HSTS header
 neonisi.com: could not connect to host
+openshift.redhat.com: did not receive HSTS header
 ottospora.nl: could not connect to host
 packagist.org: max-age too low: 2592000
 plus.google.com: did not receive HSTS header
 profiles.google.com: did not receive HSTS header
+rhcloud.com: could not connect to host
 romab.com: max-age too low: 2628000
 script.google.com: did not receive HSTS header
 shops.neonisi.com: could not connect to host
 simon.butcher.name: max-age too low: 2629743
 sites.google.com: did not receive HSTS header
 sol.io: could not connect to host
 spreadsheets.google.com: did not receive HSTS header
 squareup.com: max-age too low: 1296000
 ssl.google-analytics.com: did not receive HSTS header
 sunshinepress.org: could not connect to host
 talk.google.com: did not receive HSTS header
 talkgadget.google.com: did not receive HSTS header
 torproject.org: did not receive HSTS header
 uprotect.it: could not connect to host
 www.developer.mydigipass.com: did not receive HSTS header
 www.dropcam.com: max-age too low: 2592000
+www.elanex.biz: did not receive HSTS header
 www.entropia.de: max-age too low: 2678402
 www.gmail.com: did not receive HSTS header
 www.googlemail.com: did not receive HSTS header
 www.greplin.com: did not receive HSTS header
 www.irccloud.com: did not receive HSTS header
 www.jitsi.org: did not receive HSTS header
 www.kyps.net: did not receive HSTS header
 www.lastpass.com: did not receive HSTS header
 www.ledgerscope.net: max-age too low: 86400
 www.logentries.com: did not receive HSTS header
 www.makeyourlaws.org: did not receive HSTS header
 www.moneybookers.com: did not receive HSTS header
 www.neonisi.com: could not connect to host
 www.paycheckrecords.com: did not receive HSTS header
 www.paypal.com: max-age too low: 14400
 www.sandbox.mydigipass.com: did not receive HSTS header
+www.twitter.com: did not receive HSTS header
--- a/security/manager/boot/src/nsSTSPreloadList.inc
+++ b/security/manager/boot/src/nsSTSPreloadList.inc
@@ -2,16 +2,19 @@
  * 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/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsStrictTransportSecurityService.cpp, you shouldn't be #including it.     */
 /*****************************************************************************/
 
+#include "mozilla/StandardInteger.h"
+const PRTime gPreloadListExpirationTime = INT64_C(1362156597190000);
+
 class nsSTSPreload
 {
   public:
     const char *mHost;
     const bool mIncludeSubdomains;
 };
 
 static const nsSTSPreload kSTSPreloadList[] = {
@@ -29,37 +32,41 @@ static const nsSTSPreload kSTSPreloadLis
   { "csawctf.poly.edu", true },
   { "developer.mydigipass.com", false },
   { "dm.lookout.com", false },
   { "dm.mylookout.com", false },
   { "ebanking.indovinabank.com.vn", false },
   { "factor.cc", false },
   { "id.mayfirst.org", false },
   { "intercom.io", false },
+  { "itriskltd.com", true },
   { "keyerror.com", true },
   { "logentries.com", false },
   { "login.sapo.pt", true },
+  { "lookout.com", false },
   { "luneta.nearbuysystems.com", false },
   { "makeyourlaws.org", false },
   { "mattmccutchen.net", true },
   { "members.mayfirst.org", false },
+  { "mylookout.com", false },
   { "neg9.org", false },
   { "passwd.io", true },
   { "piratenlogin.de", true },
   { "pixi.me", true },
   { "riseup.net", true },
   { "sandbox.mydigipass.com", false },
+  { "stocktrade.de", false },
   { "stripe.com", true },
   { "support.mayfirst.org", false },
   { "surfeasy.com", false },
+  { "twitter.com", false },
   { "ubertt.org", true },
   { "www.apollo-auto.com", true },
   { "www.braintreepayments.com", false },
   { "www.cueup.com", false },
-  { "www.elanex.biz", false },
   { "www.intercom.io", false },
   { "www.lookout.com", false },
   { "www.mydigipass.com", false },
   { "www.mylookout.com", false },
   { "www.noisebridge.net", false },
   { "www.surfeasy.com", false },
   { "www.torproject.org", false },
 };
--- a/security/manager/boot/src/nsStrictTransportSecurityService.cpp
+++ b/security/manager/boot/src/nsStrictTransportSecurityService.cpp
@@ -369,26 +369,33 @@ int STSPreloadCompare(const void *key, c
 }
 
 // Returns the preload list entry for the given host, if it exists.
 // Only does exact host matching - the user must decide how to use the returned
 // data. May return null.
 const nsSTSPreload *
 nsStrictTransportSecurityService::GetPreloadListEntry(const char *aHost)
 {
-  if (mUsePreloadList) {
+  PRTime currentTime = PR_Now();
+  int32_t timeOffset = 0;
+  nsresult rv = mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds",
+                                             &timeOffset);
+  if (NS_SUCCEEDED(rv)) {
+    currentTime += (PRTime(timeOffset) * PR_USEC_PER_SEC);
+  }
+
+  if (mUsePreloadList && currentTime < gPreloadListExpirationTime) {
     return (const nsSTSPreload *) bsearch(aHost,
                                           kSTSPreloadList,
                                           PR_ARRAY_SIZE(kSTSPreloadList),
                                           sizeof(nsSTSPreload),
                                           STSPreloadCompare);
   }
-  else {
-    return nullptr;
-  }
+
+  return nullptr;
 }
 
 NS_IMETHODIMP
 nsStrictTransportSecurityService::IsStsURI(nsIURI* aURI, bool* aResult)
 {
   // Should be called on the main thread (or via proxy) since the permission
   // manager is used and it's not threadsafe.
   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sts_preloadlist_selfdestruct.js
@@ -0,0 +1,24 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function run_test() {
+  let STSService = Cc["@mozilla.org/stsservice;1"]
+                     .getService(Ci.nsIStrictTransportSecurityService);
+
+  // check that a host on the preload list is identified as an sts host
+  do_check_true(STSService.isStsHost("alpha.irccloud.com"));
+
+  // now simulate that it's 19 weeks later than it actually is
+  let offsetSeconds = 19 * 7 * 24 * 60 * 60;
+  Services.prefs.setIntPref("test.currentTimeOffsetSeconds", offsetSeconds);
+
+  // check that the preloaded host is no longer considered sts
+  do_check_false(STSService.isStsHost("alpha.irccloud.com"));
+
+  // just make sure we can get everything back to normal
+  Services.prefs.clearUserPref("test.currentTimeOffsetSeconds");
+  do_check_true(STSService.isStsHost("alpha.irccloud.com"));
+}
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -8,8 +8,9 @@ skip-if = os == "android"
 [test_hash_algorithms.js]
 # Bug 676972: test hangs consistently on Android
 skip-if = os == "android"
 [test_hmac.js]
 # Bug 676972: test hangs consistently on Android
 skip-if = os == "android"
 [test_bug627234.js]
 [test_sts_preloadlist.js]
+[test_sts_preloadlist_selfdestruct.js]
--- a/security/manager/tools/getHSTSPreloadList.js
+++ b/security/manager/tools/getHSTSPreloadList.js
@@ -22,25 +22,27 @@ resHandler.setSubstitution("app", mozDir
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource:///modules/XPCOMUtils.jsm");
 
 const SOURCE = "https://src.chromium.org/viewvc/chrome/trunk/src/net/base/transport_security_state_static.json";
 const OUTPUT = "nsSTSPreloadList.inc";
 const ERROR_OUTPUT = "nsSTSPreloadList.errors";
 const MINIMUM_REQUIRED_MAX_AGE = 60 * 60 * 24 * 7 * 18;
-const PREFIX = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
+const HEADER = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
 " * License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
 " * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" +
 "\n" +
 "/*****************************************************************************/\n" +
 "/* This is an automatically generated file. If you're not                    */\n" +
 "/* nsStrictTransportSecurityService.cpp, you shouldn't be #including it.     */\n" +
 "/*****************************************************************************/\n" +
 "\n" +
+"#include \"mozilla/StandardInteger.h\"\n";
+const PREFIX = "\n" +
 "class nsSTSPreload\n" +
 "{\n" +
 "  public:\n" +
 "    const char *mHost;\n" +
 "    const bool mIncludeSubdomains;\n" +
 "};\n" +
 "\n" +
 "static const nsSTSPreload kSTSPreloadList[] = {\n";
@@ -166,22 +168,36 @@ function getHSTSStatus(host, resultList)
 function compareHSTSStatus(a, b) {
   return (a.hostname > b.hostname ? 1 : (a.hostname < b.hostname ? -1 : 0));
 }
 
 function writeTo(string, fos) {
   fos.write(string, string.length);
 }
 
+// Determines and returns a string representing a declaration of when this
+// preload list should no longer be used.
+// This is the current time plus MINIMUM_REQUIRED_MAX_AGE.
+function getExpirationTimeString() {
+  var now = new Date();
+  var nowMillis = now.getTime();
+  // MINIMUM_REQUIRED_MAX_AGE is in seconds, so convert to milliseconds
+  var expirationMillis = nowMillis + (MINIMUM_REQUIRED_MAX_AGE * 1000);
+  var expirationMicros = expirationMillis * 1000;
+  return "const PRTime gPreloadListExpirationTime = INT64_C(" + expirationMicros + ");\n";
+}
+
 function output(sortedStatuses) {
   try {
     var file = FileUtils.getFile("CurWorkD", [OUTPUT]);
     var errorFile = FileUtils.getFile("CurWorkD", [ERROR_OUTPUT]);
     var fos = FileUtils.openSafeFileOutputStream(file);
     var eos = FileUtils.openSafeFileOutputStream(errorFile);
+    writeTo(HEADER, fos);
+    writeTo(getExpirationTimeString(), fos);
     writeTo(PREFIX, fos);
     for (var status of hstsStatuses) {
       if (status.maxAge >= MINIMUM_REQUIRED_MAX_AGE) {
         writeTo("  { \"" + status.hostname + "\", " +
                  (status.includeSubdomains ? "true" : "false") + " },\n", fos);
         dump("INFO: " + status.hostname + " ON the preload list\n");
       }
       else {
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -451,16 +451,17 @@ class Marionette(object):
         return self._send_message('getLogs', 'value')
 
     def add_perf_data(self, suite, name, value):
         return self._send_message('addPerfData', 'ok', suite=suite, name=name, value=value)
 
     def get_perf_data(self):
         return self._send_message('getPerfData', 'value')
 
-    def import_script(self, file):
-        f = open(file, "r")
-        js = f.read()
+    def import_script(self, js_file):
+        js = ''
+        with open(js_file, 'r') as f:
+            js = f.read()
         return self._send_message('importScript', 'ok', script=js)
 
     @property
     def application_cache(self):
         return ApplicationCache(self)
--- a/testing/marionette/client/setup.py
+++ b/testing/marionette/client/setup.py
@@ -1,12 +1,12 @@
 import os
 from setuptools import setup, find_packages
 
-version = '0.5.1'
+version = '0.5.2'
 
 # get documentation from the README
 try:
     here = os.path.dirname(os.path.abspath(__file__))
     description = file(os.path.join(here, 'README.md')).read()
 except (OSError, IOError):
     description = ''
 
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -201,21 +201,23 @@ function _parseModifiers(aEvent)
  *
  * aEvent is an object which may contain the properties:
  *   shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
  *
  * If the type is specified, an mouse event of that type is fired. Otherwise,
  * a mousedown followed by a mouse up is performed.
  *
  * aWindow is optional, and defaults to the current window object.
+ *
+ * Returns whether the event had preventDefault() called on it.
  */
 function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
 {
   var rect = aTarget.getBoundingClientRect();
-  synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
+  return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
        aEvent, aWindow);
 }
 function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
 {
   var rect = aTarget.getBoundingClientRect();
   synthesizeTouchAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
        aEvent, aWindow);
 }
@@ -229,30 +231,33 @@ function synthesizeTouch(aTarget, aOffse
  * If the type is specified, an mouse event of that type is fired. Otherwise,
  * a mousedown followed by a mouse up is performed.
  *
  * aWindow is optional, and defaults to the current window object.
  */
 function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
 {
   var utils = _getDOMWindowUtils(aWindow);
+  var defaultPrevented = false;
 
   if (utils) {
     var button = aEvent.button || 0;
     var clickCount = aEvent.clickCount || 1;
     var modifiers = _parseModifiers(aEvent);
 
     if (("type" in aEvent) && aEvent.type) {
-      utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
+      defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
     }
     else {
       utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers);
       utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers);
     }
   }
+
+  return defaultPrevented;
 }
 function synthesizeTouchAtPoint(left, top, aEvent, aWindow)
 {
   var utils = _getDOMWindowUtils(aWindow);
 
   if (utils) {
     var id = aEvent.id || 0;
     var rx = aEvent.rx || 1;
--- a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
+++ b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
@@ -290,16 +290,19 @@ let test_read_write = maketest("read_wri
       yield fileSource.close();
       yield fileDest.close();
       test.info("Files are closed");
     }
 
     stat = yield OS.File.stat(pathDest);
     test.is(stat.size, size, "Both files have the same size");
     yield reference_compare_files(pathSource, pathDest, test);
+
+    // Cleanup.
+    OS.File.remove(pathDest);
   });
 });
 
 /**
  * Test OS.File.prototype.{writeAtomic}
  */
 let test_read_write_all = maketest("read_write_all", function read_write_all(test) {
   return Task.spawn(function() {
@@ -363,16 +366,19 @@ let test_read_write_all = maketest("read
     // Check that writeAtomic fails if there is no tmpPath.
     // FIXME: Remove this as part of bug 793660
     try {
       yield OS.File.writeAtomic(pathDest, contents, {});
       test.fail("Without a tmpPath, writeAtomic should have failed");
     } catch (err) {
       test.ok(true, "Without a tmpPath, writeAtomic has failed as expected");
     }
+
+    // Cleanup.
+    OS.File.remove(pathDest);
   });
 });
 
 /**
  * Test file.{getPosition, setPosition}
  */
 let test_position = maketest("position", function position(test) {
   return Task.spawn(function() {
@@ -423,16 +429,17 @@ let test_copy = maketest("copy", functio
     test.info("First compare complete");
 
     let pathDest2 = OS.Path.join(OS.Constants.Path.tmpDir,
       "osfile async test 3.tmp");
     yield OS.File.move(pathDest, pathDest2);
     test.info("Move complete");
     yield reference_compare_files(pathSource, pathDest2, test);
     test.info("Second compare complete");
+    OS.File.remove(pathDest2);
 
     try {
       let field = yield OS.File.open(pathDest);
       test.fail("I should not have been able to open " + pathDest);
       file.close();
     } catch (err) {
       test.ok(err, "Could not open a file after it has been moved away " + err);
       test.ok(err instanceof OS.File.Error, "Error is an OS.File.Error");
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
@@ -197,18 +197,21 @@ function compare_files(test, sourcePath,
     source.close();
     dest.close();
   }
   ok(true, test + ": Comparison complete");
 }
 
 function test_readall_writeall_file()
 {
-  let src_file_name = "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js";
-  let tmp_file_name = "test_osfile_front.tmp";
+  let src_file_name =
+    OS.Path.join("chrome", "toolkit", "components", "osfile", "tests", "mochi",
+                 "worker_test_osfile_front.js");
+  let tmp_file_name =
+    OS.Path.join(OS.Constants.Path.tmpDir, "test_osfile_front.tmp");
   ok(true, "Starting test_readall_writeall_file");
 
   // read, ArrayBuffer
 
   let source = OS.File.open(src_file_name);
   let dest = OS.File.open(tmp_file_name, {write: true, trunc:true});
   let size = source.stat().size;
 
@@ -221,17 +224,16 @@ function test_readall_writeall_file()
   ok(true, "test_readall_writeall_file: copy complete (manual allocation)");
   source.close();
   dest.close();
 
   compare_files("test_readall_writeall_file (manual allocation)", src_file_name, tmp_file_name);
   OS.File.remove(tmp_file_name);
 
   // read, C buffer
-
   source = OS.File.open(src_file_name);
   dest = OS.File.open(tmp_file_name, {write: true, trunc:true});
   buf = new ArrayBuffer(size);
   let ptr = OS.Shared.Type.voidptr_t.implementation(buf);
   readResult = source.readTo(ptr, {bytes: size});
   is(readResult, size, "test_readall_writeall_file: read the right number of bytes (C buffer)");
 
   dest.write(ptr, {bytes: size});
@@ -314,17 +316,16 @@ function test_readall_writeall_file()
   exn = null;
   try {
     let stat = OS.File.stat(tmp_file_name + ".tmp");
   } catch (x) {
     exn = x;
   }
   ok(!!exn, "readAll + writeAtomic cleaned up after itself");
 
-
   // File.writeAtomic on top of existing file
   // Remove content and set arbitrary size, to avoid potential false negatives
   dest = OS.File.open(tmp_file_name, {write: true, trunc:true});
   dest.setPosition(1234);
   dest.close();
 
   OS.File.writeAtomic(tmp_file_name, readResult,
     {tmpPath: tmp_file_name + ".tmp"});
@@ -351,24 +352,29 @@ function test_readall_writeall_file()
   exn = null;
   try {
     OS.File.writeAtomic(tmp_file_name, readResult.buffer,
       {bytes: readResult.length});
   } catch (x) {
     exn = x;
   }
   ok(!!exn && exn instanceof TypeError, "writeAtomic fails if tmpPath is not provided");
+
+  // Cleanup.
+  OS.File.remove(tmp_file_name);
 }
 
 /**
  * Test that copying a file using |copy| works.
  */
 function test_copy_existing_file()
 {
-  let src_file_name = "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js";
+  let src_file_name =
+    OS.Path.join("chrome", "toolkit", "components", "osfile", "tests", "mochi",
+                 "worker_test_osfile_front.js");
   let tmp_file_name = "test_osfile_front.tmp";
   ok(true, "Starting test_copy_existing");
   OS.File.copy(src_file_name, tmp_file_name);
 
   ok(true, "test_copy_existing: Copy complete");
   compare_files("test_copy_existing", src_file_name, tmp_file_name);
 
   // Create a bogus file with arbitrary content, then attempt to overwrite
@@ -386,29 +392,30 @@ function test_copy_existing_file()
   let exn;
   try {
     OS.File.copy(src_file_name, tmp_file_name, {noOverwrite: true});
   } catch(x) {
     exn = x;
   }
   ok(!!exn, "test_copy_existing: noOverwrite prevents overwriting existing files");
 
-
   ok(true, "test_copy_existing: Cleaning up");
   OS.File.remove(tmp_file_name);
 }
 
 /**
  * Test that moving a file works.
  */
 function test_move_file()
 {
   ok(true, "test_move_file: Starting");
   // 1. Copy file into a temporary file
-  let src_file_name = "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js";
+  let src_file_name =
+    OS.Path.join("chrome", "toolkit", "components", "osfile", "tests", "mochi",
+                 "worker_test_osfile_front.js");
   let tmp_file_name = "test_osfile_front.tmp";
   let tmp2_file_name = "test_osfile_front.tmp2";
   OS.File.copy(src_file_name, tmp_file_name);
 
   ok(true, "test_move_file: Copy complete");
 
   // 2. Move
   OS.File.move(tmp_file_name, tmp2_file_name);
@@ -426,17 +433,16 @@ function test_move_file()
     exn = x;
   }
   ok(!!exn, "test_move_file: Original file has been removed");
 
   ok(true, "test_move_file: Cleaning up");
   OS.File.remove(tmp2_file_name);
 }
 
-
 function test_iter_dir()
 {
   ok(true, "test_iter_dir: Starting");
 
   // Create a file, to be sure that it exists
   let tmp_file_name = "test_osfile_front.tmp";
   let tmp_file = OS.File.open(tmp_file_name, {write: true, trunc:true});
   tmp_file.close();
@@ -553,18 +559,19 @@ function test_iter_dir()
 function test_position() {
   ok(true, "test_position: Starting");
 
   ok("POS_START" in OS.File, "test_position: POS_START exists");
   ok("POS_CURRENT" in OS.File, "test_position: POS_CURRENT exists");
   ok("POS_END" in OS.File, "test_position: POS_END exists");
 
   let ARBITRARY_POSITION = 321;
-  let src_file_name = "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js";
-
+  let src_file_name =
+    OS.Path.join("chrome", "toolkit", "components", "osfile", "tests", "mochi",
+                 "worker_test_osfile_front.js");
 
   let file = OS.File.open(src_file_name);
   is(file.getPosition(), 0, "test_position: Initial position is 0");
 
   let size = 0 + file.stat().size; // Hack: We can remove this 0 + once 776259 has landed
 
   file.setPosition(ARBITRARY_POSITION, OS.File.POS_START);
   is(file.getPosition(), ARBITRARY_POSITION, "test_position: Setting position from start");
--- a/toolkit/content/WindowDraggingUtils.jsm
+++ b/toolkit/content/WindowDraggingUtils.jsm
@@ -1,18 +1,24 @@
 /* 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/. */
 
+#ifdef XP_WIN
+#define USE_HITTEST
+#elifdef MOZ_WIDGET_COCOA
+#define USE_HITTEST
+#endif
+
 let EXPORTED_SYMBOLS = [ "WindowDraggingElement" ];
 
 function WindowDraggingElement(elem) {
   this._elem = elem;
   this._window = elem.ownerDocument.defaultView;
-#ifdef XP_WIN
+#ifdef USE_HITTEST
   if (!this.isPanel())
     this._elem.addEventListener("MozMouseHittest", this, false);
   else
 #endif
   this._elem.addEventListener("mousedown", this, false);
 }
 
 WindowDraggingElement.prototype = {
@@ -49,17 +55,17 @@ WindowDraggingElement.prototype = {
     return true;
   },
   isPanel : function() {
     return this._elem instanceof Components.interfaces.nsIDOMXULElement &&
            this._elem.localName == "panel";
   },
   handleEvent: function(aEvent) {
     let isPanel = this.isPanel();
-#ifdef XP_WIN
+#ifdef USE_HITTEST
     if (!isPanel) {
       if (this.shouldDrag(aEvent))
         aEvent.preventDefault();
       return;
     }
 #endif
 
     switch (aEvent.type) {
--- a/toolkit/content/tests/chrome/window_titlebar.xul
+++ b/toolkit/content/tests/chrome/window_titlebar.xul
@@ -47,17 +47,16 @@
       <label id="panellabelfloating" value="Titlebar"/>
     </titlebar>
   </panel>
 
   <button id="button" label="OK"/>
   <statusbar id="statusbar">
     <statusbarpanel>
       <label id="statuslabel" value="Status"/>
-      <label id="statuslabelnodrag" value="No Drag" onmousedown="event.preventDefault()"/>
     </statusbarpanel>
   </statusbar>
 
 <script>
 <![CDATA[
 
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 
@@ -101,36 +100,28 @@ function waitForWindowMove(element, x, y
   }
 }
 
 function test_titlebar()
 {
   var titlebar = document.getElementById("titlebar");
   var label = document.getElementById("label");
 
-  // on Mac, the window can also be moved with the statusbar
+  // On Mac, the window can also be moved with the statusbar, but this works
+  // via the MozMouseHittest event, not via mouse events.
   if (navigator.platform.indexOf("Mac") >= 0) {
-    var statuslabel = document.getElementById("statuslabel");
-    var statuslabelnodrag = document.getElementById("statuslabelnodrag");
-
-    origoldx = window.screenX;
-    origoldy = window.screenY;
+    var preventDefaulted;
 
-    synthesizeMouse(statuslabel, 2, 2, { type: "mousedown" });
-    synthesizeMouse(statuslabel, 22, 22, { type: "mousemove" });
-    SimpleTest.is(window.screenX, origoldx + 20, "move window with statusbar horizontal");
-    SimpleTest.is(window.screenY, origoldy + 20, "move window with statusbar vertical");
-    synthesizeMouse(statuslabel, 22, 22, { type: "mouseup" });
+    var statuslabel = document.getElementById("statuslabel");
+    preventDefaulted = synthesizeMouse(statuslabel, 2, 2, { type: "MozMouseHittest" });
+    SimpleTest.ok(preventDefaulted, "MozMouseHittest should have been defaultPrevented over statusbar");
 
-    // event was cancelled so the drag should not have occurred
-    synthesizeMouse(statuslabelnodrag, 2, 2, { type: "mousedown" });
-    synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mousemove" });
-    SimpleTest.is(window.screenX, origoldx + 20, "move window with statusbar cancelled mousedown horizontal");
-    SimpleTest.is(window.screenY, origoldy + 20, "move window with statusbar cancelled mousedown vertical");
-    synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mouseup" });
+    var button = document.getElementById("button");
+    preventDefaulted = synthesizeMouse(button, 2, 2, { type: "MozMouseHittest" });
+    SimpleTest.ok(!preventDefaulted, "MozMouseHittest should NOT have been defaultPrevented over button");
   }
 
   origoldx = window.screenX;
   origoldy = window.screenY;
 
   var mousedownListener = function (event) mouseDownTarget = event.originalTarget;
   window.addEventListener("mousedown", mousedownListener, false);
   synthesizeMouse(label, 2, 2, { type: "mousedown" });
--- a/toolkit/devtools/debugger/nsJSInspector.cpp
+++ b/toolkit/devtools/debugger/nsJSInspector.cpp
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsJSInspector.h"
 #include "nsIXPConnect.h"
 #include "nsIJSContextStack.h"
 #include "nsThreadUtils.h"
 #include "jsapi.h"
-#include "jsgc.h"
 #include "jsfriendapi.h"
 #include "jsdbgapi.h"
 #include "mozilla/ModuleUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsMemory.h"
 
 #define JSINSPECTOR_CONTRACTID \
   "@mozilla.org/jsinspector;1"
--- a/toolkit/devtools/webconsole/test/test_file_uri.html
+++ b/toolkit/devtools/webconsole/test/test_file_uri.html
@@ -65,17 +65,17 @@ function onAttach(aState, aResponse)
 
 function onFileActivity(aType, aPacket)
 {
   is(aPacket.from, gState.actor, "fileActivity actor");
 
   gState.dbgClient.removeListener("fileActivity", onFileActivity);
 
   info("aPacket.uri: " + aPacket.uri);
-  ok(/bug798764\.html$/.test(aPacket.uri), "file URI match");
+  ok(/\bbug798764\b.*\.html$/.test(aPacket.uri), "file URI match");
 
   testEnd();
 }
 
 function testEnd()
 {
   if (gTmpFile) {
     SimpleTest.executeSoon(function() {
--- a/toolkit/identity/Makefile.in
+++ b/toolkit/identity/Makefile.in
@@ -30,16 +30,17 @@ CPPSRCS = \
   $(NULL)
 
 EXTRA_JS_MODULES = \
 	Identity.jsm \
 	IdentityProvider.jsm \
 	IdentityStore.jsm \
 	jwcrypto.jsm \
 	LogUtils.jsm \
+	MinimalIdentity.jsm \
 	RelyingParty.jsm \
 	Sandbox.jsm \
 	$(NULL)
 
 ifdef ENABLE_TESTS
     DIRS += tests
 endif
 
new file mode 100644
--- /dev/null
+++ b/toolkit/identity/MinimalIdentity.jsm
@@ -0,0 +1,376 @@
+/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This alternate implementation of IdentityService provides just the
+ * channels for navigator.id, leaving the certificate storage to a
+ * server-provided app.
+ *
+ * On b2g, the messages identity-controller-watch, -request, and
+ * -logout, are observed by the component SignInToWebsite.jsm.
+ */
+
+"use strict";
+
+const EXPORTED_SYMBOLS = ["IdentityService"];
+
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/identity/LogUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this,
+                                  "jwcrypto",
+                                  "resource://gre/modules/identity/jwcrypto.jsm");
+
+function log(...aMessageArgs) {
+  Logger.log.apply(Logger, ["minimal core"].concat(aMessageArgs));
+}
+function reportError(...aMessageArgs) {
+  Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs));
+}
+
+function IDService() {
+  Services.obs.addObserver(this, "quit-application-granted", false);
+  // Services.obs.addObserver(this, "identity-auth-complete", false);
+
+  // simplify, it's one object
+  this.RP = this;
+  this.IDP = this;
+
+  // keep track of flows
+  this._rpFlows = {};
+  this._authFlows = {};
+  this._provFlows = {};
+}
+
+IDService.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
+
+  observe: function observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "quit-application-granted":
+        Services.obs.removeObserver(this, "quit-application-granted");
+        // Services.obs.removeObserver(this, "identity-auth-complete");
+        break;
+    }
+  },
+
+  /**
+   * Parse an email into username and domain if it is valid, else return null
+   */
+  parseEmail: function parseEmail(email) {
+    var match = email.match(/^([^@]+)@([^@^/]+.[a-z]+)$/);
+    if (match) {
+      return {
+        username: match[1],
+        domain: match[2]
+      };
+    }
+    return null;
+  },
+
+  /**
+   * Register a listener for a given windowID as a result of a call to
+   * navigator.id.watch().
+   *
+   * @param aCaller
+   *        (Object)  an object that represents the caller document, and
+   *                  is expected to have properties:
+   *                  - id (unique, e.g. uuid)
+   *                  - loggedInEmail (string or null)
+   *                  - origin (string)
+   *
+   *                  and a bunch of callbacks
+   *                  - doReady()
+   *                  - doLogin()
+   *                  - doLogout()
+   *                  - doError()
+   *                  - doCancel()
+   *
+   */
+  watch: function watch(aRpCaller) {
+    log("watch: caller keys:", Object.keys(aRpCaller));
+    log("watch: rpcaller:", aRpCaller);
+    // store the caller structure and notify the UI observers
+
+    // note that, unfortunately, what here is loggedInEmail is called
+    // loggedInUser in the native API.
+    this._rpFlows[aRpCaller.id] = aRpCaller;
+    let options = {rpId: aRpCaller.id,
+                   origin: aRpCaller.origin,
+                   loggedInUser: aRpCaller.loggedInEmail || null};
+    log("sending identity-controller-watch:", options);
+    Services.obs.notifyObservers({wrappedJSObject: options},"identity-controller-watch", null);
+  },
+
+  /**
+   * Initiate a login with user interaction as a result of a call to
+   * navigator.id.request().
+   *
+   * @param aRPId
+   *        (integer)  the id of the doc object obtained in .watch()
+   *
+   * @param aOptions
+   *        (Object)  options including privacyPolicy, termsOfService
+   */
+  request: function request(aRPId, aOptions) {
+    log("request: rpId:", aRPId);
+    let rp = this._rpFlows[aRPId];
+
+    // Notify UX to display identity picker.
+    // Pass the doc id to UX so it can pass it back to us later.
+    let options = {rpId: aRPId, origin: rp.origin};
+    Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-request", null);
+  },
+
+  /**
+   * Invoked when a user wishes to logout of a site (for instance, when clicking
+   * on an in-content logout button).
+   *
+   * @param aRpCallerId
+   *        (integer)  the id of the doc object obtained in .watch()
+   *
+   */
+  logout: function logout(aRpCallerId) {
+    log("logout: RP caller id:", aRpCallerId);
+    let rp = this._rpFlows[aRpCallerId];
+
+    let options = {rpId: aRpCallerId, origin: rp.origin};
+    Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-logout", null);
+  },
+
+  /*
+   * once the UI-and-display-logic components have received
+   * notifications, they call back with direct invocation of the
+   * following functions (doLogin, doLogout, or doReady)
+   */
+
+  doLogin: function doLogin(aRpCallerId, aAssertion) {
+    let rp = this._rpFlows[aRpCallerId];
+    if (!rp)
+      return;
+
+    rp.doLogin(aAssertion);
+  },
+
+  doLogout: function doLogout(aRpCallerId) {
+    let rp = this._rpFlows[aRpCallerId];
+    if (!rp)
+      return;
+
+    rp.doLogout();
+  },
+
+  doReady: function doReady(aRpCallerId) {
+    let rp = this._rpFlows[aRpCallerId];
+    if (!rp)
+      return;
+
+    rp.doReady();
+  },
+
+
+  /*
+   * XXX Bug 804229: Implement Identity Provider Functions
+   *
+   * Stubs for Identity Provider functions follow
+   */
+
+  /**
+   * the provisioning iframe sandbox has called navigator.id.beginProvisioning()
+   *
+   * @param aCaller
+   *        (object)  the iframe sandbox caller with all callbacks and
+   *                  other information.  Callbacks include:
+   *                  - doBeginProvisioningCallback(id, duration_s)
+   *                  - doGenKeyPairCallback(pk)
+   */
+  beginProvisioning: function beginProvisioning(aCaller) {
+  },
+
+  /**
+   * the provisioning iframe sandbox has called
+   * navigator.id.raiseProvisioningFailure()
+   *
+   * @param aProvId
+   *        (int)  the identifier of the provisioning flow tied to that sandbox
+   * @param aReason
+   */
+  raiseProvisioningFailure: function raiseProvisioningFailure(aProvId, aReason) {
+    reportError("Provisioning failure", aReason);
+  },
+
+  /**
+   * When navigator.id.genKeyPair is called from provisioning iframe sandbox.
+   * Generates a keypair for the current user being provisioned.
+   *
+   * @param aProvId
+   *        (int)  the identifier of the provisioning caller tied to that sandbox
+   *
+   * It is an error to call genKeypair without receiving the callback for
+   * the beginProvisioning() call first.
+   */
+  genKeyPair: function genKeyPair(aProvId) {
+  },
+
+  /**
+   * When navigator.id.registerCertificate is called from provisioning iframe
+   * sandbox.
+   *
+   * Sets the certificate for the user for which a certificate was requested
+   * via a preceding call to beginProvisioning (and genKeypair).
+   *
+   * @param aProvId
+   *        (integer) the identifier of the provisioning caller tied to that
+   *                  sandbox
+   *
+   * @param aCert
+   *        (String)  A JWT representing the signed certificate for the user
+   *                  being provisioned, provided by the IdP.
+   */
+  registerCertificate: function registerCertificate(aProvId, aCert) {
+  },
+
+  /**
+   * The authentication frame has called navigator.id.beginAuthentication
+   *
+   * IMPORTANT: the aCaller is *always* non-null, even if this is called from
+   * a regular content page. We have to make sure, on every DOM call, that
+   * aCaller is an expected authentication-flow identifier. If not, we throw
+   * an error or something.
+   *
+   * @param aCaller
+   *        (object)  the authentication caller
+   *
+   */
+  beginAuthentication: function beginAuthentication(aCaller) {
+  },
+
+  /**
+   * The auth frame has called navigator.id.completeAuthentication
+   *
+   * @param aAuthId
+   *        (int)  the identifier of the authentication caller tied to that sandbox
+   *
+   */
+  completeAuthentication: function completeAuthentication(aAuthId) {
+  },
+
+  /**
+   * The auth frame has called navigator.id.cancelAuthentication
+   *
+   * @param aAuthId
+   *        (int)  the identifier of the authentication caller
+   *
+   */
+  cancelAuthentication: function cancelAuthentication(aAuthId) {
+  },
+
+  // methods for chrome and add-ons
+
+  /**
+   * Discover the IdP for an identity
+   *
+   * @param aIdentity
+   *        (string) the email we're logging in with
+   *
+   * @param aCallback
+   *        (function) callback to invoke on completion
+   *                   with first-positional parameter the error.
+   */
+  _discoverIdentityProvider: function _discoverIdentityProvider(aIdentity, aCallback) {
+    // XXX bug 767610 - validate email address call
+    // When that is available, we can remove this custom parser
+    var parsedEmail = this.parseEmail(aIdentity);
+    if (parsedEmail === null) {
+      return aCallback("Could not parse email: " + aIdentity);
+    }
+    log("_discoverIdentityProvider: identity:", aIdentity, "domain:", parsedEmail.domain);
+
+    this._fetchWellKnownFile(parsedEmail.domain, function fetchedWellKnown(err, idpParams) {
+      // idpParams includes the pk, authorization url, and
+      // provisioning url.
+
+      // XXX bug 769861 follow any authority delegations
+      // if no well-known at any point in the delegation
+      // fall back to browserid.org as IdP
+      return aCallback(err, idpParams);
+    });
+  },
+
+  /**
+   * Fetch the well-known file from the domain.
+   *
+   * @param aDomain
+   *
+   * @param aScheme
+   *        (string) (optional) Protocol to use.  Default is https.
+   *                 This is necessary because we are unable to test
+   *                 https.
+   *
+   * @param aCallback
+   *
+   */
+  _fetchWellKnownFile: function _fetchWellKnownFile(aDomain, aCallback, aScheme='https') {
+    // XXX bug 769854 make tests https and remove aScheme option
+    let url = aScheme + '://' + aDomain + "/.well-known/browserid";
+    log("_fetchWellKnownFile:", url);
+
+    // this appears to be a more successful way to get at xmlhttprequest (which supposedly will close with a window
+    let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+                .createInstance(Ci.nsIXMLHttpRequest);
+
+    // XXX bug 769865 gracefully handle being off-line
+    // XXX bug 769866 decide on how to handle redirects
+    req.open("GET", url, true);
+    req.responseType = "json";
+    req.mozBackgroundRequest = true;
+    req.onload = function _fetchWellKnownFile_onload() {
+      if (req.status < 200 || req.status >= 400) {
+        log("_fetchWellKnownFile", url, ": server returned status:", req.status);
+        return aCallback("Error");
+      }
+      try {
+        let idpParams = req.response;
+
+        // Verify that the IdP returned a valid configuration
+        if (! (idpParams.provisioning &&
+            idpParams.authentication &&
+            idpParams['public-key'])) {
+          let errStr= "Invalid well-known file from: " + aDomain;
+          log("_fetchWellKnownFile:", errStr);
+          return aCallback(errStr);
+        }
+
+        let callbackObj = {
+          domain: aDomain,
+          idpParams: idpParams,
+        };
+        log("_fetchWellKnownFile result: ", callbackObj);
+        // Yay.  Valid IdP configuration for the domain.
+        return aCallback(null, callbackObj);
+
+      } catch (err) {
+        reportError("_fetchWellKnownFile", "Bad configuration from", aDomain, err);
+        return aCallback(err.toString());
+      }
+    };
+    req.onerror = function _fetchWellKnownFile_onerror() {
+      log("_fetchWellKnownFile", "ERROR:", req.status, req.statusText);
+      log("ERROR: _fetchWellKnownFile:", err);
+      return aCallback("Error");
+    };
+    req.send(null);
+  },
+
+};
+
+let IdentityService = new IDService();
--- a/toolkit/identity/tests/mochitest/Makefile.in
+++ b/toolkit/identity/tests/mochitest/Makefile.in
@@ -5,16 +5,11 @@
 DEPTH          = @DEPTH@
 topsrcdir      = @top_srcdir@
 srcdir         = @srcdir@
 VPATH          = @srcdir@
 relativesrcdir = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MOCHITEST_FILES = \
-		head_identity.js \
-		test_authentication.html \
-		test_provisioning.html \
-		test_relying_party.html \
-		$(NULL)
+# XXX bug 805602 update and restore mochitests
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/toolkit/identity/tests/mochitest/head_identity.js
+++ /dev/null
@@ -1,221 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/** Helper functions for identity mochitests **/
-/** Please keep functions in-sync with unit/head_identity.js **/
-
-"use strict";
-
-const Cc = SpecialPowers.Cc;
-const Ci = SpecialPowers.Ci;
-const Cu = SpecialPowers.Cu;
-
-const TEST_URL = "http://mochi.test:8888";
-const TEST_URL2 = "https://myfavoritebaconinacan.com";
-const TEST_USER = "user@example.com";
-const TEST_PRIVKEY = "fake-privkey";
-const TEST_CERT = "fake-cert";
-const TEST_IDPPARAMS = {
-  domain: "example.com",
-  idpParams: {
-    authentication: "/foo/authenticate.html",
-    provisioning: "/foo/provision.html"
-  },
-};
-
-const Services = Cu.import("resource://gre/modules/Services.jsm").Services;
-
-// Set the debug pref before loading other modules
-SpecialPowers.setBoolPref("toolkit.identity.debug", true);
-SpecialPowers.setBoolPref("dom.identity.enabled", true);
-
-// Shutdown the UX if it exists so that it won't interfere with tests by also responding to
-// observer notifications.
-try {
-  const SignInToWebsiteUX = Cu.import("resource:///modules/SignInToWebsite.jsm").SignInToWebsiteUX;
-  if (SignInToWebsiteUX) {
-    SignInToWebsiteUX.uninit();
-  }
-} catch (ex) {
-  // The module doesn't exist
-}
-
-const jwcrypto = Cu.import("resource://gre/modules/identity/jwcrypto.jsm").jwcrypto;
-const IdentityStore = Cu.import("resource://gre/modules/identity/IdentityStore.jsm").IdentityStore;
-const RelyingParty = Cu.import("resource://gre/modules/identity/RelyingParty.jsm").RelyingParty;
-const XPCOMUtils = Cu.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;
-const IDService = Cu.import("resource://gre/modules/identity/Identity.jsm").IdentityService;
-const IdentityProvider = Cu.import("resource://gre/modules/identity/IdentityProvider.jsm").IdentityProvider;
-
-const identity = navigator.id || navigator.mozId;
-
-function do_check_null(aVal, aMsg) {
-  is(aVal, null, aMsg);
-}
-
-function do_timeout(aDelay, aFunc) {
-  setTimeout(aFunc, aDelay);
-}
-
-function get_idstore() {
-  return IdentityStore;
-}
-
-// create a mock "watch" object, which the Identity Service
-function mock_watch(aIdentity, aDoFunc) {
-  function partial(fn) {
-    let args = Array.prototype.slice.call(arguments, 1);
-    return function() {
-      return fn.apply(this, args.concat(Array.prototype.slice.call(arguments)));
-    };
-  }
-
-  let mockedWatch = {};
-  mockedWatch.loggedInEmail = aIdentity;
-  mockedWatch['do'] = aDoFunc;
-  mockedWatch.onready = partial(aDoFunc, 'ready');
-  mockedWatch.onlogin = partial(aDoFunc, 'login');
-  mockedWatch.onlogout = partial(aDoFunc, 'logout');
-
-  return mockedWatch;
-}
-
-// mimicking callback funtionality for ease of testing
-// this observer auto-removes itself after the observe function
-// is called, so this is meant to observe only ONE event.
-function makeObserver(aObserveTopic, aObserveFunc) {
-  function observe(aSubject, aTopic, aData) {
-    if (aTopic == aObserveTopic) {
-
-      Services.obs.removeObserver(this, aObserveTopic);
-      try {
-        aObserveFunc(SpecialPowers.wrap(aSubject), aTopic, aData);
-      } catch (ex) {
-        ok(false, ex);
-      }
-    }
-  }
-
-  Services.obs.addObserver(observe, aObserveTopic, false);
-}
-
-// set up the ID service with an identity with keypair and all
-// when ready, invoke callback with the identity
-function setup_test_identity(identity, cert, cb) {
-  // set up the store so that we're supposed to be logged in
-  let store = get_idstore();
-
-  function keyGenerated(err, kpo) {
-    store.addIdentity(identity, kpo, cert);
-    cb();
-  };
-
-  jwcrypto.generateKeyPair("DS160", keyGenerated);
-}
-
-// takes a list of functions and returns a function that
-// when called the first time, calls the first func,
-// then the next time the second, etc.
-function call_sequentially() {
-  let numCalls = 0;
-  let funcs = arguments;
-
-  return function() {
-    if (!funcs[numCalls]) {
-      let argString = Array.prototype.slice.call(arguments).join(",");
-      ok(false, "Too many calls: " + argString);
-      return;
-    }
-    funcs[numCalls].apply(funcs[numCalls], arguments);
-    numCalls += 1;
-  };
-}
-
-/*
- * Setup a provisioning workflow with appropriate callbacks
- *
- * identity is the email we're provisioning.
- *
- * afterSetupCallback is required.
- *
- * doneProvisioningCallback is optional, if the caller
- * wants to be notified when the whole provisioning workflow is done
- *
- * frameCallbacks is optional, contains the callbacks that the sandbox
- * frame would provide in response to DOM calls.
- */
-function setup_provisioning(identity, afterSetupCallback, doneProvisioningCallback, callerCallbacks) {
-  IDService.reset();
-
-  let util = SpecialPowers.getDOMWindowUtils(window);
-
-  let provId = util.outerWindowID;
-  IDService.IDP._provisionFlows[provId] = {
-    identity : identity,
-    idpParams: TEST_IDPPARAMS,
-    callback: function(err) {
-      if (doneProvisioningCallback)
-        doneProvisioningCallback(err);
-    },
-    sandbox: {
-      // Emulate the free() method on the iframe sandbox
-      free: function() {}
-    }
-  };
-
-  let caller = {};
-  caller.id = callerCallbacks.id = provId;
-  caller.doBeginProvisioningCallback = function(id, duration_s) {
-    if (callerCallbacks && callerCallbacks.beginProvisioningCallback)
-      callerCallbacks.beginProvisioningCallback(id, duration_s);
-  };
-  caller.doGenKeyPairCallback = function(pk) {
-    if (callerCallbacks && callerCallbacks.genKeyPairCallback)
-      callerCallbacks.genKeyPairCallback(pk);
-  };
-
-  afterSetupCallback(callerCallbacks);
-}
-
-function resetState() {
-  IDService.reset();
-}
-
-function cleanup() {
-  resetState();
-  SpecialPowers.clearUserPref("toolkit.identity.debug");
-  SpecialPowers.clearUserPref("dom.identity.enabled");
-  // Re-init the UX that we uninit
-  try {
-    const SignInToWebsiteUX = Cu.import("resource:///modules/SignInToWebsite.jsm").SignInToWebsiteUX;
-    if (SignInToWebsiteUX) {
-      SignInToWebsiteUX.init();
-    }
-  } catch (ex) {
-    // The module doesn't exist
-  }
-}
-
-var TESTS = [];
-
-function run_next_test() {
-  if (!identity) {
-    todo(false, "DOM API is not available. Skipping tests.");
-    cleanup();
-    SimpleTest.finish();
-    return;
-  }
-  if (TESTS.length) {
-    let test = TESTS.shift();
-    info(test.name);
-    try {
-      test();
-    } catch (ex) {
-      ok(false, ex);
-    }
-  } else {
-    cleanup();
-    info("all done");
-    SimpleTest.finish();
-  }
-}
deleted file mode 100644
--- a/toolkit/identity/tests/mochitest/test_authentication.html
+++ /dev/null
@@ -1,161 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-Test of Identity Provider (IDP) Authentication using the DOM APIs
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test of Identity Provider (IDP) Authentication using the DOM APIs</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript;version=1.8" src="head_identity.js"></script>
-</head>
-<body onload="run_next_test()">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=753238">Test of Identity Provider (IDP) Authentication using the DOM APIs</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script type="application/javascript;version=1.8">
-
-/** Test of Identity Provider (IDP) Authentication using the DOM APIs **/
-/** Most tests are ported from test_authentication.js */
-
-"use strict";
-
-SimpleTest.waitForExplicitFinish();
-
-const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm")
-                      .DOMIdentity;
-let outerWinId = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
-
-function run_next_auth_test() {
-  // Reset the DOM state then run the next test
-  let provContext = IdentityProvider._provisionFlows[outerWinId];
-  if (provContext && provContext.caller) {
-    makeObserver("identity-DOM-state-reset", function() {
-      SimpleTest.executeSoon(run_next_test);
-    });
-    DOMIdentity._resetFrameState(provContext.caller);
-  } else {
-    SimpleTest.executeSoon(run_next_test);
-  }
-}
-
-function test_begin_authentication_flow() {
-  let _provId = null;
-
-  // set up a watch, to be consistent
-  let mockedDoc = mock_watch(null, function(action, params) {});
-  identity.watch(mockedDoc);
-
-  // The identity-auth notification is sent up to the UX from the
-  // _doAuthentication function.  Be ready to receive it and call
-  // beginAuthentication
-  makeObserver("identity-auth", function (aSubject, aTopic, aData) {
-    isnot(aSubject, null);
-
-    is(aSubject.wrappedJSObject.provId, _provId);
-
-    run_next_auth_test();
-  });
-  setup_provisioning(
-    TEST_USER,
-    function(caller) {
-      _provId = caller.id;
-      identity.beginProvisioning(caller.beginProvisioningCallback);
-    }, function() {},
-    {
-      beginProvisioningCallback: function(email, duration_s) {
-        // let's say this user needs to authenticate
-        IDService.IDP._doAuthentication(_provId, TEST_IDPPARAMS);
-      }
-    });
-}
-
-function test_complete_authentication_flow() {
-  let _provId = null;
-  let _authId = null;
-  let id = TEST_USER;
-
-  let callbacksFired = false;
-  let topicObserved = false;
-
-  // The result of authentication should be a successful login
-  IDService.reset();
-
-  let mockedDoc = mock_watch(null, call_sequentially(
-    function(action, params) {
-      is(action, 'ready');
-      is(params, undefined);
-    }
-  ));
-
-  identity.watch(mockedDoc);
-
-  // A mock calling context
-  let authCaller = {
-    doBeginAuthenticationCallback: function doBeginAuthCallback(aIdentity) {
-      is(aIdentity, TEST_USER);
-      identity.completeAuthentication();
-    },
-
-    doError: function(err) {
-      ok(false, "doError called: " + err);
-    },
-  };
-
-  makeObserver("identity-auth-complete", function (aSubject, aTopic, aData) {
-    info("identity-auth-complete");
-    is(aSubject.wrappedJSObject.identity, TEST_USER);
-
-    run_next_test();
-  });
-
-  // Create a provisioning flow for our auth flow to attach to
-  setup_provisioning(
-    TEST_USER,
-    function(provFlow) {
-      _provId = provFlow.id;
-      info("after setup: " + _provId);
-      identity.beginProvisioning(provFlow.beginProvisioningCallback);
-    },
-    function() {
-      info("doneProvisioningCallback");
-
-      // let's say this user needs to authenticate
-      IDService.IDP._doAuthentication(_provId, TEST_IDPPARAMS);
-
-      // test_begin_authentication_flow verifies that the right
-      // message is sent to the UI.  So that works.  Moving on,
-      // the UI calls setAuthenticationFlow ...
-      _authId = outerWinId;
-      IDService.IDP.setAuthenticationFlow(_authId, _provId);
-      IDService.IDP._provisionFlows[_provId].rpId = outerWinId;
-
-      // ... then the UI calls beginAuthentication ...
-      authCaller.id = _authId;
-      IDService.IDP._provisionFlows[_provId].caller = authCaller;
-      identity.beginAuthentication(function bac() {
-        info("beginAuthentication callback");
-        identity.completeAuthentication();
-      });
-    },
-    {
-      beginProvisioningCallback: function(email, duration_s) {
-        info("beginProvisioningCallback");
-
-        identity.raiseProvisioningFailure("Prov. failed");
-      }
-    }
-  );
-}
-
-TESTS.push(test_begin_authentication_flow);
-TESTS.push(test_complete_authentication_flow);
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/identity/tests/mochitest/test_provisioning.html
+++ /dev/null
@@ -1,285 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-Test of Identity Provider (IDP) Provisioning using the DOM APIs
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test of Identity Provider (IDP) Provisioning using the DOM APIs</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript;version=1.8" src="head_identity.js"></script>
-</head>
-<body onload="run_next_test()">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=753238">Test of Identity Provider (IDP) Provisioning using the DOM APIs</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script type="application/javascript;version=1.8">
-
-/** Test of Identity Provider (IDP) Provisioning using the DOM APIs **/
-/** Most tests are ported from test_provisioning.js */
-
-"use strict";
-
-SimpleTest.waitForExplicitFinish();
-
-const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm")
-                      .DOMIdentity;
-let outerWinId = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
-
-function check_provision_flow_done(provId) {
-  do_check_null(IdentityProvider._provisionFlows[provId]);
-}
-
-/**
- * Allow specifying aProvFlow so we can reset after the _provisionFlows is cleaned up.
- */
-function run_next_prov_test(aProvFlow) {
-  // Reset the DOM state then run the next test
-  let provContext = aProvFlow || IdentityProvider._provisionFlows[outerWinId];
-  if (provContext && provContext.caller) {
-    makeObserver("identity-DOM-state-reset", function() {
-      SimpleTest.executeSoon(run_next_test);
-    });
-    DOMIdentity._resetFrameState(provContext.caller);
-  } else {
-    SimpleTest.executeSoon(run_next_test);
-  }
-}
-
-function test_begin_provisioning() {
-  setup_provisioning(
-    TEST_USER,
-    function(caller) {
-      // call .beginProvisioning()
-      // TODO: should probably throw outside of a prov. sandbox?
-      identity.beginProvisioning(caller.beginProvisioningCallback);
-    }, function() {},
-    {
-      beginProvisioningCallback: function(email, duration_s) {
-        is(email, TEST_USER);
-        ok(duration_s > 0);
-        ok(duration_s <= (24 * 3600));
-
-        run_next_prov_test();
-      }
-    });
-}
-
-function test_raise_provisioning_failure() {
-  let _callerId = null;
-
-  setup_provisioning(
-    TEST_USER,
-    function(caller) {
-      // call .beginProvisioning()
-      _callerId = caller.id;
-      identity.beginProvisioning(caller.beginProvisioningCallback);
-    }, function(err) {
-      // this should be invoked with a populated error
-      isnot(err, null);
-      ok(err.indexOf("can't authenticate this email") > -1);
-
-      run_next_prov_test();
-    },
-    {
-      beginProvisioningCallback: function(email, duration_s) {
-        // raise the failure as if we can't provision this email
-        identity.raiseProvisioningFailure("can't authenticate this email");
-      }
-  });
-}
-
-function test_genkeypair_before_begin_provisioning() {
-  setup_provisioning(
-    TEST_USER,
-    function(caller) {
-      try {
-        // call genKeyPair without beginProvisioning
-        identity.genKeyPair(caller.genKeyPairCallback);
-      } catch (ex) {
-        ok(ex, "Caught exception for calling genKeyPair without beginProvisioning: " + ex);
-        run_next_prov_test();
-      }
-    },
-    // expect this to be called with an error
-    function(err) {
-      ok(false, "Shoudn't reach here as DOM code should have caught the problem");
-
-      run_next_prov_test();
-    },
-    {
-      // this should not be called at all!
-      genKeyPairCallback: function(pk) {
-        // a test that will surely fail because we shouldn't be here.
-        ok(false);
-
-        run_next_prov_test();
-      }
-    }
-  );
-}
-
-function test_genkeypair() {
-  let _callerId = null;
-
-  function gkpCallback(kp) {
-    isnot(kp, null);
-
-    // yay!
-    run_next_prov_test();
-  }
-
-  setup_provisioning(
-    TEST_USER,
-    function(caller) {
-      _callerId = caller.id;
-      identity.beginProvisioning(caller.beginProvisioningCallback);
-    },
-    function(err) {
-      // should not be called!
-      ok(false);
-
-      run_next_prov_test();
-    },
-    {
-      beginProvisioningCallback: function(email, time_s) {
-        identity.genKeyPair(gkpCallback);
-      },
-      genKeyPairCallback: gkpCallback,
-    }
-  );
-}
-
-// we've already ensured that genkeypair can't be called
-// before beginProvisioning, so this test should be enough
-// to ensure full sequential call of the 3 APIs.
-function test_register_certificate_before_genkeypair() {
-  let _callerID = null;
-
-  setup_provisioning(
-    TEST_USER,
-    function(caller) {
-      // do the right thing for beginProvisioning
-      _callerID = caller.id;
-      identity.beginProvisioning(caller.beginProvisioningCallback);
-    },
-    // expect this to be called with an error
-    function(err) {
-      ok(false, "Shoudn't reach here as DOM code should have caught the problem");
-
-      run_next_prov_test();
-    },
-    {
-      beginProvisioningCallback: function(email, duration_s) {
-        try {
-          // now we try to register cert but no keygen has been done
-          identity.registerCertificate("fake-cert");
-        } catch (ex) {
-          ok(ex, "Caught exception for calling genKeyPair without beginProvisioning: " + ex);
-          run_next_prov_test();
-        }
-
-      }
-    }
-  );
-}
-
-function test_register_certificate() {
-  let _callerId = null;
-  let provFlow = null;
-
-  function gkpCallback(pk) {
-    // Hold on to the provFlow so we have access to .caller to cleanup later
-    provFlow = IdentityProvider._provisionFlows[outerWinId];
-
-    identity.registerCertificate("fake-cert-42");
-  }
-
-  setup_provisioning(
-    TEST_USER,
-    function(caller) {
-      _callerId = caller.id;
-      identity.beginProvisioning(caller.beginProvisioningCallback);
-    },
-    function(err) {
-      // we should be cool!
-      do_check_null(err);
-
-      // check that the cert is there
-      let identity = get_idstore().fetchIdentity(TEST_USER);
-      isnot(identity,null);
-      is(identity.cert, "fake-cert-42");
-
-      SimpleTest.executeSoon(function check_done() {
-        // cleanup will happen after the callback is called
-        check_provision_flow_done(_callerId);
-
-        run_next_prov_test(provFlow);
-      });
-    },
-    {
-      beginProvisioningCallback: function(email, duration_s) {
-        identity.genKeyPair(gkpCallback);
-      },
-      genKeyPairCallback: gkpCallback,
-    }
-  );
-}
-
-function test_get_assertion_after_provision() {
-  let _callerId = null;
-  let provFlow = null;
-
-  function gkpCallback(pk) {
-    // Hold on to the provFlow so we have access to .caller to cleanup later
-    provFlow = IdentityProvider._provisionFlows[outerWinId];
-    identity.registerCertificate("fake-cert-42");
-  }
-
-  setup_provisioning(
-    TEST_USER,
-    function(caller) {
-      _callerId = caller.id;
-      identity.beginProvisioning(caller.beginProvisioningCallback);
-    },
-    function(err) {
-      // we should be cool!
-      do_check_null(err);
-
-      // check that the cert is there
-      let identity = get_idstore().fetchIdentity(TEST_USER);
-      isnot(identity,null);
-      is(identity.cert, "fake-cert-42");
-
-      SimpleTest.executeSoon(function check_done() {
-        // cleanup will happen after the callback is called
-        check_provision_flow_done(_callerId);
-
-        run_next_prov_test(provFlow);
-      });
-    },
-    {
-      beginProvisioningCallback: function(email, duration_s) {
-        identity.genKeyPair(gkpCallback);
-      },
-      genKeyPairCallback: gkpCallback,
-    }
-  );
-}
-
-TESTS.push(test_genkeypair_before_begin_provisioning);
-TESTS.push(test_begin_provisioning);
-TESTS.push(test_raise_provisioning_failure);
-TESTS.push(test_register_certificate_before_genkeypair);
-TESTS.push(test_genkeypair);
-TESTS.push(test_register_certificate);
-TESTS.push(test_get_assertion_after_provision);
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/identity/tests/mochitest/test_relying_party.html
+++ /dev/null
@@ -1,229 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-Test of Relying Party (RP) using the DOM APIs
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test of Relying Party (RP) using the DOM APIs</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript;version=1.8" src="head_identity.js"></script>
-</head>
-<body onload="run_next_test()">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=753238">Test of Relying Party (RP) using the DOM APIs</a>
-<p id="display"></p>
-<div id="content">
-  <button id='request'>request</button>
-</div>
-<pre id="test">
-<script type="application/javascript;version=1.8">
-
-/** Test of Relying Party (RP) using the DOM APIs **/
-/** Most tests are ported from test_relying_party.js */
-
-"use strict";
-
-SimpleTest.waitForExplicitFinish();
-
-const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm")
-                      .DOMIdentity;
-let outerWinId = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
-
-// Reset the DOM state then run the next test
-function run_next_rp_test() {
-  let rpContext = RelyingParty._rpFlows[outerWinId];
-  if (rpContext) {
-    makeObserver("identity-DOM-state-reset", function() {
-      SimpleTest.executeSoon(run_next_test);
-    });
-    DOMIdentity._resetFrameState(rpContext);
-  } else {
-    SimpleTest.executeSoon(run_next_test);
-  }
-}
-
-function test_watch_loggedin_ready() {
-  resetState();
-
-  let id = TEST_USER;
-  setup_test_identity(id, TEST_CERT, function() {
-    let store = get_idstore();
-
-    // set it up so we're supposed to be logged in to TEST_URL
-    store.setLoginState(TEST_URL, true, id);
-    identity.watch(mock_watch(id, function(action, params) {
-      is(action, 'ready');
-      is(params, undefined);
-
-      run_next_rp_test();
-    }));
-  });
-}
-
-function test_watch_loggedin_login() {
-  resetState();
-
-  let id = TEST_USER;
-  setup_test_identity(id, TEST_CERT, function() {
-    let store = get_idstore();
-
-    // set it up so we're supposed to be logged in to TEST_URL
-    store.setLoginState(TEST_URL, true, id);
-
-    // check for first a login() call, then a ready() call
-    identity.watch(mock_watch(null, call_sequentially(
-      function(action, params) {
-        is(action, 'login');
-        isnot(params, null);
-      },
-      function(action, params) {
-        is(action, 'ready');
-        do_check_null(params);
-
-        run_next_rp_test();
-      }
-    )));
-  });
-}
-
-function test_watch_loggedin_logout() {
-  resetState();
-
-  let id = TEST_USER;
-  let other_id = "otherid@foo.com";
-  setup_test_identity(other_id, TEST_CERT, function() {
-    setup_test_identity(id, TEST_CERT, function() {
-      let store = get_idstore();
-
-      // set it up so we're supposed to be logged in to TEST_URL
-      // with id, not other_id
-      store.setLoginState(TEST_URL, true, id);
-
-      // this should cause a login with an assertion for id,
-      // not for other_id
-      identity.watch(mock_watch(other_id, call_sequentially(
-        function(action, params) {
-          is(action, 'login');
-          isnot(params, null);
-        },
-        function(action, params) {
-          is(action, 'ready');
-          do_check_null(params);
-
-          run_next_rp_test();
-        }
-      )));
-    });
-  });
-}
-
-function test_watch_notloggedin_ready() {
-  resetState();
-
-  identity.watch(mock_watch(null, function(action, params) {
-    is(action, 'ready');
-    is(params, undefined);
-
-    run_next_rp_test();
-  }));
-}
-
-function test_watch_notloggedin_logout() {
-  resetState();
-
-  identity.watch(mock_watch(TEST_USER, call_sequentially(
-    function(action, params) {
-      is(action, 'logout');
-      is(params, undefined);
-
-      let store = get_idstore();
-      do_check_null(store.getLoginState(TEST_URL));
-    },
-    function(action, params) {
-      is(action, 'ready');
-      is(params, undefined);
-      run_next_rp_test();
-    }
-  )));
-}
-
-function test_request() {
-  // set up a watch, to be consistent
-  let mockedDoc = mock_watch(null, function(action, params) {
-    // We're not checking anything here at the moment.
-  });
-
-  identity.watch(mockedDoc);
-
-  // be ready for the UX identity-request notification
-  makeObserver("identity-request", function (aSubject, aTopic, aData) {
-    isnot(aSubject, null);
-
-    run_next_rp_test();
-  });
-
-  var button = document.getElementById('request');
-  button.addEventListener('click', function requestHandler() {
-    button.removeEventListener('click', requestHandler);
-    identity.request();
-  });
-
-  synthesizeMouseAtCenter(button, {});
-}
-
-function test_logout() {
-  resetState();
-
-  let id = TEST_USER;
-  setup_test_identity(id, TEST_CERT, function() {
-    let store = get_idstore();
-
-    // set it up so we're supposed to be logged in to TEST_URL
-    store.setLoginState(TEST_URL, true, id);
-
-    let doLogout;
-    let mockedDoc = mock_watch(id, call_sequentially(
-      function(action, params) {
-        is(action, 'ready');
-        is(params, undefined);
-
-        SimpleTest.executeSoon(doLogout);
-      },
-      function(action, params) {
-        is(action, 'logout');
-        is(params, undefined);
-      },
-      function(action, params) {
-        is(action, 'ready');
-        is(params, undefined);
-
-        run_next_rp_test();
-      }));
-
-    doLogout = function() {
-      makeObserver("identity-login-state-changed", function (aSubject, aTopic, aData) {
-        isnot(aSubject.wrappedJSObject.rpId, null, "Check rpId is not null");
-        is(aData, null, "Check identity changed to nobody");
-
-        ok(!store.getLoginState(TEST_URL).isLoggedIn, "Check isLoggedIn is false");
-        is(store.getLoginState(TEST_URL).email, TEST_USER, "Check notification email");
-      });
-      identity.logout();
-    };
-
-    identity.watch(mockedDoc);
-  });
-}
-
-
-TESTS = TESTS.concat([test_watch_loggedin_ready, test_watch_loggedin_login, test_watch_loggedin_logout]);
-TESTS = TESTS.concat([test_watch_notloggedin_ready, test_watch_notloggedin_logout]);
-TESTS.push(test_request);
-TESTS.push(test_logout);
-
-</script>
-</pre>
-</body>
-</html>
--- a/toolkit/identity/tests/unit/test_identity.js
+++ b/toolkit/identity/tests/unit/test_identity.js
@@ -41,18 +41,18 @@ function test_select_identity() {
   IDService.reset();
 
   let id = "ishtar@mockmyid.com";
   setup_test_identity(id, TEST_CERT, function() {
     let gotAssertion = false;
     let mockedDoc = mock_doc(null, TEST_URL, call_sequentially(
       function(action, params) {
         // ready emitted from first watch() call
-	do_check_eq(action, 'ready');
-	do_check_null(params);
+        do_check_eq(action, 'ready');
+        do_check_null(params);
       },
       // first the login call
       function(action, params) {
         do_check_eq(action, 'login');
         do_check_neq(params, null);
 
         // XXX - check that the assertion is for the right email
 
new file mode 100644
--- /dev/null
+++ b/toolkit/identity/tests/unit/test_minimalidentity.js
@@ -0,0 +1,95 @@
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
+                                  "resource://gre/modules/identity/MinimalIdentity.jsm",
+                                  "IdentityService");
+
+Cu.import("resource://gre/modules/identity/LogUtils.jsm");
+
+function log(...aMessageArgs) {
+  Logger.log.apply(Logger, ["test_minimalidentity"].concat(aMessageArgs));
+}
+
+function test_overall() {
+  do_check_neq(MinimalIDService, null);
+  run_next_test();
+}
+
+function test_mock_doc() {
+  do_test_pending();
+  let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {
+    do_check_eq(action, 'coffee');
+    do_test_finished();
+    run_next_test();
+  });
+
+  mockedDoc.doCoffee();
+}
+
+/*
+ * Test that the "identity-controller-watch" signal is emitted correctly
+ */
+function test_watch() {
+  do_test_pending();
+
+  let mockedDoc = mock_doc(null, TEST_URL);
+  makeObserver("identity-controller-watch", function (aSubject, aTopic, aData) {
+    do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
+    do_check_eq(aSubject.wrappedJSObject.origin, TEST_URL);
+    do_test_finished();
+    run_next_test();
+   });
+
+  MinimalIDService.RP.watch(mockedDoc);
+}
+
+/*
+ * Test that the "identity-controller-request" signal is emitted correctly
+ */
+function test_request() {
+  do_test_pending();
+
+  let mockedDoc = mock_doc(null, TEST_URL);
+  makeObserver("identity-controller-request", function (aSubject, aTopic, aData) {
+    do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
+    do_check_eq(aSubject.wrappedJSObject.origin, TEST_URL);
+    do_test_finished();
+    run_next_test();
+  });
+
+  MinimalIDService.RP.watch(mockedDoc);
+  MinimalIDService.RP.request(mockedDoc.id, {});
+}
+
+/*
+ * Test that the "identity-controller-logout" signal is emitted correctly
+ */
+function test_logout() {
+  do_test_pending();
+
+  let mockedDoc = mock_doc(null, TEST_URL);
+  makeObserver("identity-controller-logout", function (aSubject, aTopic, aData) {
+    do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
+    do_test_finished();
+    run_next_test();
+  });
+
+  MinimalIDService.RP.watch(mockedDoc);
+  MinimalIDService.RP.logout(mockedDoc.id, {});
+}
+
+
+
+let TESTS = [
+  test_overall,
+  test_mock_doc,
+  test_watch,
+  test_request,
+  test_logout
+];
+
+TESTS.forEach(add_test);
+
+function run_test() {
+  run_next_test();
+}
--- a/toolkit/identity/tests/unit/xpcshell.ini
+++ b/toolkit/identity/tests/unit/xpcshell.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 head = head_identity.js
 tail = tail_identity.js
 
 # Test load modules first so syntax failures are caught early.
 [test_load_modules.js]
+[test_minimalidentity.js]
+
 [test_log_utils.js]
-
 [test_authentication.js]
 [test_crypto_service.js]
 [test_identity.js]
 [test_jwcrypto.js]
 [test_observer_topics.js]
 [test_provisioning.js]
 [test_relying_party.js]
 [test_store.js]
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -39,16 +39,19 @@
 #include <stdlib.h>
 
 #ifdef XP_WIN
 #include <windows.h>
 #include <shlobj.h>
 #endif
 #ifdef XP_MACOSX
 #include "nsILocalFileMac.h"
+// for chflags()
+#include <sys/stat.h>
+#include <unistd.h>
 #endif
 #ifdef XP_UNIX
 #include <ctype.h>
 #endif
 #ifdef XP_OS2
 #define INCL_DOS
 #include <os2.h>
 #endif
@@ -127,16 +130,40 @@ nsXREDirProvider::SetProfile(nsIFile* aD
   rv = EnsureDirectoryExists(aDir);
   if (NS_FAILED(rv))
     return rv;
 
   rv = EnsureDirectoryExists(aLocalDir);
   if (NS_FAILED(rv))
     return rv;
 
+#ifdef XP_MACOSX
+  bool same;
+  if (NS_SUCCEEDED(aDir->Equals(aLocalDir, &same)) && !same) {
+    // Ensure that the cache directory is not indexed by Spotlight
+    // (bug 718910).  At least on OS X, the cache directory (under
+    // ~/Library/Caches/) is always the "local" user profile
+    // directory.  This is confusing, since *both* user profile
+    // directories are "local" (they both exist under the user's
+    // home directory).  But this usage dates back at least as far
+    // as the patch for bug 291033, where "local" seems to mean
+    // "suitable for temporary storage".  Don't hide the cache
+    // directory if by some chance it and the "non-local" profile
+    // directory are the same -- there are bad side effects from
+    // hiding a profile directory under /Library/Application Support/
+    // (see bug 801883).
+    nsAutoCString cacheDir;
+    if (NS_SUCCEEDED(aLocalDir->GetNativePath(cacheDir))) {
+      if (chflags(cacheDir.get(), UF_HIDDEN)) {
+        NS_WARNING("Failed to set Cache directory to HIDDEN.");
+      }
+    }
+  }
+#endif
+
   mProfileDir = aDir;
   mProfileLocalDir = aLocalDir;
   return NS_OK;
 }
 
 NS_IMPL_QUERY_INTERFACE3(nsXREDirProvider,
                          nsIDirectoryServiceProvider,
                          nsIDirectoryServiceProvider2,
--- a/uriloader/prefetch/OfflineCacheUpdateChild.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateChild.cpp
@@ -77,16 +77,18 @@ OfflineCacheUpdateChild::RefcountHitZero
 //-----------------------------------------------------------------------------
 // OfflineCacheUpdateChild <public>
 //-----------------------------------------------------------------------------
 
 OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow* aWindow)
     : mState(STATE_UNINITIALIZED)
     , mIsUpgrade(false)
     , mIPCActivated(false)
+    , mInBrowser(false)
+    , mAppID(NECKO_NO_APP_ID)
     , mWindow(aWindow)
     , mByteProgress(0)
 {
 }
 
 OfflineCacheUpdateChild::~OfflineCacheUpdateChild()
 {
     LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this));
@@ -178,17 +180,18 @@ OfflineCacheUpdateChild::AssociateDocume
 // OfflineCacheUpdateChild::nsIOfflineCacheUpdate
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
                               nsIURI *aDocumentURI,
                               nsIDOMDocument *aDocument,
                               nsIFile *aCustomProfileDir,
-                              nsILoadContext *aLoadContext)
+                              uint32_t aAppID,
+                              bool aInBrowser)
 {
     nsresult rv;
 
     // Make sure the service has been initialized
     nsOfflineCacheUpdateService* service =
         nsOfflineCacheUpdateService::EnsureService();
     if (!service)
         return NS_ERROR_FAILURE;
@@ -219,33 +222,45 @@ OfflineCacheUpdateChild::Init(nsIURI *aM
 
     mDocumentURI = aDocumentURI;
 
     mState = STATE_INITIALIZED;
 
     if (aDocument)
         SetDocument(aDocument);
 
-    mLoadContext = aLoadContext;
+    mAppID = aAppID;
+    mInBrowser = aInBrowser;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI,
                                   const nsACString& clientID,
                                   nsIURI *aDocumentURI)
 {
     NS_NOTREACHED("Not expected to do partial offline cache updates"
                   " on the child process");
     // For now leaving this method, we may discover we need it.
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+OfflineCacheUpdateChild::InitForUpdateCheck(nsIURI *aManifestURI,
+                                            uint32_t aAppID,
+                                            bool aInBrowser,
+                                            nsIObserver *aObserver)
+{
+    NS_NOTREACHED("Not expected to do only update checks"
+                  " from the child process");
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain)
 {
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
 
     aUpdateDomain = mUpdateDomain;
     return NS_OK;
 }
 
@@ -411,29 +426,21 @@ OfflineCacheUpdateChild::Schedule()
     // mDocument is non-null if both:
     // 1. this update was initiated by a document that referred a manifest
     // 2. the document has not already been loaded from the application cache
     // This tells the update to cache this document even in case the manifest
     // has not been changed since the last fetch.
     // See also nsOfflineCacheUpdate::ScheduleImplicit.
     bool stickDocument = mDocument != nullptr; 
 
-    // Carry load context to the parent
-    bool isInBrowserElement = false;
-    uint32_t appId = NECKO_NO_APP_ID;
-    if (mLoadContext) {
-        mLoadContext->GetIsInBrowserElement(&isInBrowserElement);
-        mLoadContext->GetAppId(&appId);
-    }
-
     // Need to addref ourself here, because the IPC stack doesn't hold
     // a reference to us. Will be released in RecvFinish() that identifies 
     // the work has been done.
     child->SendPOfflineCacheUpdateConstructor(this, manifestURI, documentURI,
-                                              isInBrowserElement, appId,
+                                              mInBrowser, mAppID,
                                               stickDocument);
 
     mIPCActivated = true;
     this->AddRef();
 
     return NS_OK;
 }
 
--- a/uriloader/prefetch/OfflineCacheUpdateChild.h
+++ b/uriloader/prefetch/OfflineCacheUpdateChild.h
@@ -68,17 +68,19 @@ private:
     bool mSucceeded;
     bool mIPCActivated;
 
     nsCString mUpdateDomain;
     nsCOMPtr<nsIURI> mManifestURI;
     nsCOMPtr<nsIURI> mDocumentURI;
 
     nsCOMPtr<nsIObserverService> mObserverService;
-    nsCOMPtr<nsILoadContext> mLoadContext;
+
+    uint32_t mAppID;
+    bool mInBrowser;
 
     /* Clients watching this update for changes */
     nsCOMArray<nsIWeakReference> mWeakObservers;
     nsCOMArray<nsIOfflineCacheUpdateObserver> mObservers;
 
     /* Document that requested this update */
     nsCOMPtr<nsIDOMDocument> mDocument;
 
--- a/uriloader/prefetch/OfflineCacheUpdateGlue.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateGlue.cpp
@@ -88,27 +88,28 @@ OfflineCacheUpdateGlue::Schedule()
     return mUpdate->Schedule();
 }
 
 NS_IMETHODIMP
 OfflineCacheUpdateGlue::Init(nsIURI *aManifestURI, 
                              nsIURI *aDocumentURI,
                              nsIDOMDocument *aDocument,
                              nsIFile *aCustomProfileDir,
-                             nsILoadContext *aLoadContext)
+                             uint32_t aAppID,
+                             bool aInBrowser)
 {
     if (!EnsureUpdate())
         return NS_ERROR_NULL_POINTER;
 
     mDocumentURI = aDocumentURI;
 
     if (aDocument)
         SetDocument(aDocument);
 
-    return mUpdate->Init(aManifestURI, aDocumentURI, nullptr, aCustomProfileDir, aLoadContext);
+    return mUpdate->Init(aManifestURI, aDocumentURI, nullptr, aCustomProfileDir, aAppID, aInBrowser);
 }
 
 void
 OfflineCacheUpdateGlue::SetDocument(nsIDOMDocument *aDocument)
 {
     // The design is one document for one cache update on the content process.
     NS_ASSERTION(!mDocument, 
                  "Setting more then a single document on an instance of OfflineCacheUpdateGlue");
--- a/uriloader/prefetch/OfflineCacheUpdateGlue.h
+++ b/uriloader/prefetch/OfflineCacheUpdateGlue.h
@@ -25,16 +25,17 @@ namespace docshell {
 #define NS_ADJUSTED_FORWARD_NSIOFFLINECACHEUPDATE(_to) \
   NS_IMETHOD GetStatus(uint16_t *aStatus) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetStatus(aStatus); } \
   NS_IMETHOD GetPartial(bool *aPartial) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetPartial(aPartial); } \
   NS_IMETHOD GetIsUpgrade(bool *aIsUpgrade) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetIsUpgrade(aIsUpgrade); } \
   NS_IMETHOD GetUpdateDomain(nsACString & aUpdateDomain) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetUpdateDomain(aUpdateDomain); } \
   NS_IMETHOD GetManifestURI(nsIURI **aManifestURI) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetManifestURI(aManifestURI); } \
   NS_IMETHOD GetSucceeded(bool *aSucceeded) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetSucceeded(aSucceeded); } \
   NS_IMETHOD InitPartial(nsIURI *aManifestURI, const nsACString & aClientID, nsIURI *aDocumentURI) { return !_to ? NS_ERROR_NULL_POINTER : _to->InitPartial(aManifestURI, aClientID, aDocumentURI); } \
+  NS_IMETHOD InitForUpdateCheck(nsIURI *aManifestURI, uint32_t aAppID, bool aInBrowser, nsIObserver *aObserver) { return !_to ? NS_ERROR_NULL_POINTER : _to->InitForUpdateCheck(aManifestURI, aAppID, aInBrowser, aObserver); } \
   NS_IMETHOD AddDynamicURI(nsIURI *aURI) { return !_to ? NS_ERROR_NULL_POINTER : _to->AddDynamicURI(aURI); } \
   NS_IMETHOD AddObserver(nsIOfflineCacheUpdateObserver *aObserver, bool aHoldWeak) { return !_to ? NS_ERROR_NULL_POINTER : _to->AddObserver(aObserver, aHoldWeak); } \
   NS_IMETHOD RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) { return !_to ? NS_ERROR_NULL_POINTER : _to->RemoveObserver(aObserver); } \
   NS_IMETHOD GetByteProgress(uint64_t * _result) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetByteProgress(_result); }
 
 class OfflineCacheUpdateGlue MOZ_FINAL : public nsSupportsWeakReference
                                        , public nsIOfflineCacheUpdate
                                        , public nsIOfflineCacheUpdateObserver
@@ -47,17 +48,18 @@ private:
 
 public:
     NS_ADJUSTED_FORWARD_NSIOFFLINECACHEUPDATE(EnsureUpdate())
     NS_IMETHOD Schedule(void);
     NS_IMETHOD Init(nsIURI *aManifestURI, 
                     nsIURI *aDocumentURI,
                     nsIDOMDocument *aDocument,
                     nsIFile *aCustomProfileDir,
-                    nsILoadContext *aLoadContext);
+                    uint32_t aAppID,
+                    bool aInBrowser);
 
     NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER
 
     OfflineCacheUpdateGlue();
     ~OfflineCacheUpdateGlue();
 
     void SetDocument(nsIDOMDocument *aDocument);
 
--- a/uriloader/prefetch/OfflineCacheUpdateParent.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateParent.cpp
@@ -70,20 +70,16 @@ nsresult
 OfflineCacheUpdateParent::Schedule(const URIParams& aManifestURI,
                                    const URIParams& aDocumentURI,
                                    const bool& isInBrowserElement,
                                    const uint32_t& appId,
                                    const bool& stickDocument)
 {
     LOG(("OfflineCacheUpdateParent::RecvSchedule [%p]", this));
 
-    // Load context members
-    mIsInBrowserElement = isInBrowserElement;
-    mAppId = appId;
-
     nsRefPtr<nsOfflineCacheUpdate> update;
     nsCOMPtr<nsIURI> manifestURI = DeserializeURI(aManifestURI);
     if (!manifestURI)
         return NS_ERROR_FAILURE;
 
     nsOfflineCacheUpdateService* service =
         nsOfflineCacheUpdateService::EnsureService();
     if (!service)
@@ -99,23 +95,25 @@ OfflineCacheUpdateParent::Schedule(const
 
     nsCOMPtr<nsIURI> documentURI = DeserializeURI(aDocumentURI);
     if (!documentURI)
         return NS_ERROR_FAILURE;
 
     if (!NS_SecurityCompareURIs(manifestURI, documentURI, false))
         return NS_ERROR_DOM_SECURITY_ERR;
 
-    service->FindUpdate(manifestURI, this, getter_AddRefs(update));
+    service->FindUpdate(manifestURI, appId, isInBrowserElement,
+                        getter_AddRefs(update));
     if (!update) {
         update = new nsOfflineCacheUpdate();
 
         // Leave aDocument argument null. Only glues and children keep 
         // document instances.
-        rv = update->Init(manifestURI, documentURI, nullptr, nullptr, this);
+        rv = update->Init(manifestURI, documentURI, nullptr, nullptr,
+                          appId, isInBrowserElement);
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = update->Schedule();
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     update->AddObserver(this, false);
 
--- a/uriloader/prefetch/nsIOfflineCacheUpdate.idl
+++ b/uriloader/prefetch/nsIOfflineCacheUpdate.idl
@@ -10,17 +10,17 @@ interface nsIDOMWindow;
 interface nsIDOMNode;
 interface nsIDOMDocument;
 interface nsIDOMLoadStatus;
 interface nsIOfflineCacheUpdate;
 interface nsIPrincipal;
 interface nsIPrefBranch;
 interface nsIApplicationCache;
 interface nsIFile;
-interface nsILoadContext;
+interface nsIObserver;
 
 [scriptable, uuid(47360d57-8ef4-4a5d-8865-1a27a739ad1a)]
 interface nsIOfflineCacheUpdateObserver : nsISupports {
   const unsigned long STATE_ERROR = 1;
   const unsigned long STATE_CHECKING = 2;
   const unsigned long STATE_NOUPDATE = 3;
   const unsigned long STATE_OBSOLETE = 4;
   const unsigned long STATE_DOWNLOADING = 5;
@@ -58,17 +58,17 @@ interface nsIOfflineCacheUpdateObserver 
  * Each update object maintains a list of nsIDOMLoadStatus items for the
  * resources it is updating.  The list of these items will be available
  * after the object is scheduled.
  *
  * One update object will be updating at a time.  The active object will
  * load its items one by one, sending itemCompleted() to any registered
  * observers.
  */
-[scriptable, uuid(D47966B2-1EBC-45c5-8639-2937ED200281)]
+[scriptable, uuid(91b94446-5d91-4089-bed7-edfab25824a7)]
 interface nsIOfflineCacheUpdate : nsISupports {
   /**
    * Fetch the status of the running update.  This will return a value
    * defined in nsIDOMOfflineResourceList.
    */
   readonly attribute unsigned short status;
 
   /**
@@ -104,17 +104,18 @@ interface nsIOfflineCacheUpdate : nsISup
    *
    * @param aManifestURI
    *        The manifest URI to be checked.
    * @param aDocumentURI
    *        The page that is requesting the update.
    */
   void init(in nsIURI aManifestURI, in nsIURI aDocumentURI, in nsIDOMDocument aDocument,
             [optional] in nsIFile aCustomProfileDir,
-            [optional] in nsILoadContext aLoadContext);
+            [optional] in unsigned long aAppId,
+            [optional] in boolean aInBrowser);
 
   /**
    * Initialize the update for partial processing. 
    *
    * @param aManifestURI
    *        The manifest URI of the related cache.
    * @param aClientID
    *        Client  ID of the cache to store resource to. This ClientID
@@ -122,16 +123,39 @@ interface nsIOfflineCacheUpdate : nsISup
    *        the manifest URI passed in the first parameter.
    * @param aDocumentURI
    *        The page that is requesting the update. May be null 
    *        when this information is unknown.
    */
   void initPartial(in nsIURI aManifestURI, in ACString aClientID, in nsIURI aDocumentURI);
 
   /**
+   * Initialize the update to only check whether there is an update
+   * to the manifest available (if it has actually changed on the server).
+   *
+   * @param aManifestURI
+   *        The manifest URI of the related cache.
+   * @param aAppID
+   *        Local ID of an app (optional) to check the cache update for.
+   * @param aInBrowser
+   *        Whether to check for a cache populated from browser element.
+   * @param aObserver
+   *        nsIObserver implementation that receives the result.
+   *        When aTopic == "offline-cache-update-available" there is an update to
+   *        to download. Update of the app cache will lead to a new version
+   *        download.
+   *        When aTopic == "offline-cache-update-unavailable" then there is no
+   *        update available (the manifest has not changed on the server).
+   */
+  void initForUpdateCheck(in nsIURI aManifestURI,
+                          in unsigned long aAppID,
+                          in boolean aInBrowser,
+                          in nsIObserver aObserver);
+
+  /**
    * Add a dynamic URI to the offline cache as part of the update.
    *
    * @param aURI
    *        The URI to add.
    */
   void addDynamicURI(in nsIURI aURI);
 
   /**
@@ -161,17 +185,17 @@ interface nsIOfflineCacheUpdate : nsISup
   void removeObserver(in nsIOfflineCacheUpdateObserver aObserver);
 
   /**
    * Return the number of bytes downloaded so far
    */
   readonly attribute uint64_t byteProgress;
 };
 
-[scriptable, uuid(dc5de18c-197c-41d2-9584-dd7ac7494611)]
+[scriptable, uuid(cf362a31-4166-4994-8443-b68704ecdcc0)]
 interface nsIOfflineCacheUpdateService : nsISupports {
     /**
      * Constants for the offline-app permission.
      *
      * XXX: This isn't a great place for this, but it's really the only
      * private offline-app-related interface
      */
 
@@ -201,24 +225,51 @@ interface nsIOfflineCacheUpdateService :
      * be stored to a custom profile directory.  There is no coalescing of
      * manifests by manifest URL.
      */
     nsIOfflineCacheUpdate scheduleCustomProfileUpdate(in nsIURI aManifestURI,
                                                       in nsIURI aDocumentURI,
                                                       in nsIFile aProfileDir);
 
     /**
+     * Schedule a cache update for a given offline manifest using app cache
+     * bound to the given appID+inBrowser flag.  If an existing update is
+     * scheduled or running, that update will be returned. Otherwise a new
+     * update will be scheduled.
+     */
+    nsIOfflineCacheUpdate scheduleAppUpdate(in nsIURI aManifestURI,
+                                            in nsIURI aDocumentURI,
+                                            in unsigned long aAppID,
+                                            in boolean aInBrowser);
+
+    /**
      * Schedule a cache update for a manifest when the document finishes
      * loading.
      */
     void scheduleOnDocumentStop(in nsIURI aManifestURI,
                                 in nsIURI aDocumentURI,
                                 in nsIDOMDocument aDocument);
 
     /**
+     * Schedule a check to see if an update is available.
+     *
+     * This will not update or make any changes to the appcache.
+     * It only notifies the observer to indicate whether the manifest has
+     * changed on the server (or not): a changed manifest means that an
+     * update is available.
+     *
+     * For arguments see nsIOfflineCacheUpdate.initForUpdateCheck() method
+     * description.
+     */
+    void checkForUpdate(in nsIURI aManifestURI,
+                        in unsigned long aAppID,
+                        in boolean aInBrowser,
+                        in nsIObserver aObserver);
+
+    /**
      * Checks whether a principal should have access to the offline
      * cache.
      * @param aPrincipal
      *        The principal to check.
      * @param aPrefBranch
      *        The pref branch to use to check the
      *        offline-apps.allow_by_default pref.  If not specified,
      *        the pref service will be used.
--- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp
@@ -327,35 +327,44 @@ nsOfflineCacheUpdateItem::OpenChannel(ns
         // and its Run() call.  We must never open channel on this item again.
         LOG(("  %p is already running! ignoring", this));
         return NS_ERROR_ALREADY_OPENED;
     }
 
     nsresult rv = nsOfflineCacheUpdate::GetCacheKey(mURI, mCacheKey);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    uint32_t flags = nsIRequest::LOAD_BACKGROUND |
+                     nsICachingChannel::LOAD_ONLY_IF_MODIFIED |
+                     nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE;
+
+    if (mApplicationCache == mPreviousApplicationCache) {
+        // Same app cache to read from and to write to is used during
+        // an only-update-check procedure.  Here we protect the existing
+        // cache from being modified.
+        flags |= nsIRequest::INHIBIT_CACHING;
+    }
+
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        mURI,
                        nullptr, nullptr, this,
-                       nsIRequest::LOAD_BACKGROUND |
-                       nsICachingChannel::LOAD_ONLY_IF_MODIFIED |
-                       nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE);
+                       flags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
         do_QueryInterface(mChannel, &rv);
 
     // Support for nsIApplicationCacheChannel is required.
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Use the existing application cache as the cache to check.
     rv = appCacheChannel->SetApplicationCache(mPreviousApplicationCache);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // Set the new application cache as the targer for write.
+    // Set the new application cache as the target for write.
     rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // configure HTTP specific stuff
     nsCOMPtr<nsIHttpChannel> httpChannel =
         do_QueryInterface(mChannel);
     if (httpChannel) {
         httpChannel->SetReferrer(mReferrerURI);
@@ -1154,18 +1163,21 @@ NS_IMPL_ISUPPORTS3(nsOfflineCacheUpdate,
 // nsOfflineCacheUpdate <public>
 //-----------------------------------------------------------------------------
 
 nsOfflineCacheUpdate::nsOfflineCacheUpdate()
     : mState(STATE_UNINITIALIZED)
     , mOwner(nullptr)
     , mAddedItems(false)
     , mPartialUpdate(false)
+    , mOnlyCheckUpdate(false)
     , mSucceeded(true)
     , mObsolete(false)
+    , mAppID(NECKO_NO_APP_ID)
+    , mInBrowser(false)
     , mItemsInProgress(0)
     , mRescheduleCount(0)
     , mPinnedEntryRetriesCount(0)
     , mPinned(false)
 {
 }
 
 nsOfflineCacheUpdate::~nsOfflineCacheUpdate()
@@ -1185,34 +1197,20 @@ nsOfflineCacheUpdate::GetCacheKey(nsIURI
 
     rv = newURI->GetAsciiSpec(aKey);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
 nsresult
-nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
-                           nsIURI *aDocumentURI,
-                           nsIDOMDocument *aDocument,
-                           nsIFile *aCustomProfileDir,
-                           nsILoadContext *aLoadContext)
+nsOfflineCacheUpdate::InitInternal(nsIURI *aManifestURI)
 {
     nsresult rv;
 
-    // Make sure the service has been initialized
-    nsOfflineCacheUpdateService* service =
-        nsOfflineCacheUpdateService::EnsureService();
-    if (!service)
-        return NS_ERROR_FAILURE;
-
-    LOG(("nsOfflineCacheUpdate::Init [%p]", this));
-
-    mPartialUpdate = false;
-
     // Only http and https applications are supported.
     bool match;
     rv = aManifestURI->SchemeIs("http", &match);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!match) {
         rv = aManifestURI->SchemeIs("https", &match);
         NS_ENSURE_SUCCESS(rv, rv);
@@ -1220,16 +1218,42 @@ nsOfflineCacheUpdate::Init(nsIURI *aMani
             return NS_ERROR_ABORT;
     }
 
     mManifestURI = aManifestURI;
 
     rv = mManifestURI->GetAsciiHost(mUpdateDomain);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    mPartialUpdate = false;
+
+    return NS_OK;
+}
+
+nsresult
+nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
+                           nsIURI *aDocumentURI,
+                           nsIDOMDocument *aDocument,
+                           nsIFile *aCustomProfileDir,
+                           uint32_t aAppID,
+                           bool aInBrowser)
+{
+    nsresult rv;
+
+    // Make sure the service has been initialized
+    nsOfflineCacheUpdateService* service =
+        nsOfflineCacheUpdateService::EnsureService();
+    if (!service)
+        return NS_ERROR_FAILURE;
+
+    LOG(("nsOfflineCacheUpdate::Init [%p]", this));
+
+    rv = InitInternal(aManifestURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     nsCOMPtr<nsIApplicationCacheService> cacheService =
         do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mDocumentURI = aDocumentURI;
 
     if (aCustomProfileDir) {
         rv = GetCacheKey(aManifestURI, mGroupID);
@@ -1247,36 +1271,89 @@ nsOfflineCacheUpdate::Init(nsIURI *aMani
                                                         aCustomProfileDir,
                                                         kCustomProfileQuota,
                                                         getter_AddRefs(mApplicationCache));
         NS_ENSURE_SUCCESS(rv, rv);
 
         mCustomProfileDir = aCustomProfileDir;
     }
     else {
-        rv = cacheService->BuildGroupID(aManifestURI,
-                                        aLoadContext,
-                                        mGroupID);
+        rv = cacheService->BuildGroupIDForApp(aManifestURI,
+                                              aAppID, aInBrowser,
+                                              mGroupID);
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = cacheService->GetActiveCache(mGroupID,
                                           getter_AddRefs(mPreviousApplicationCache));
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = cacheService->CreateApplicationCache(mGroupID,
                                                   getter_AddRefs(mApplicationCache));
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI,
                                                              NULL,
                                                              &mPinned);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mLoadContext = aLoadContext;
+    mAppID = aAppID;
+    mInBrowser = aInBrowser;
+
+    mState = STATE_INITIALIZED;
+    return NS_OK;
+}
+
+nsresult
+nsOfflineCacheUpdate::InitForUpdateCheck(nsIURI *aManifestURI,
+                                         uint32_t aAppID,
+                                         bool aInBrowser,
+                                         nsIObserver *aObserver)
+{
+    nsresult rv;
+
+    // Make sure the service has been initialized
+    nsOfflineCacheUpdateService* service =
+        nsOfflineCacheUpdateService::EnsureService();
+    if (!service)
+        return NS_ERROR_FAILURE;
+
+    LOG(("nsOfflineCacheUpdate::InitForUpdateCheck [%p]", this));
+
+    rv = InitInternal(aManifestURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIApplicationCacheService> cacheService =
+        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = cacheService->BuildGroupIDForApp(aManifestURI,
+                                          aAppID, aInBrowser,
+                                          mGroupID);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = cacheService->GetActiveCache(mGroupID,
+                                      getter_AddRefs(mPreviousApplicationCache));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // To load the manifest properly using current app cache to satisfy and
+    // also to compare the cached content hash value we have to set 'some'
+    // app cache to write to on the channel.  Otherwise the cached version will
+    // be used and no actual network request will be made.  We use the same
+    // app cache here.  OpenChannel prevents caching in this case using
+    // INHIBIT_CACHING load flag.
+    mApplicationCache = mPreviousApplicationCache;
+
+    rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aManifestURI,
+                                                             NULL,
+                                                             &mPinned);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mUpdateAvailableObserver = aObserver;
+    mOnlyCheckUpdate = true;
 
     mState = STATE_INITIALIZED;
     return NS_OK;
 }
 
 nsresult
 nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI,
                                   const nsACString& clientID,
@@ -1383,16 +1460,45 @@ nsOfflineCacheUpdate::HandleManifest(boo
                           &mManifestItem->GetOpportunisticNamespaces());
     NS_ENSURE_SUCCESS(rv, rv);
 
     *aDoUpdate = true;
 
     return NS_OK;
 }
 
+bool
+nsOfflineCacheUpdate::CheckUpdateAvailability()
+{
+    nsresult rv;
+
+    bool succeeded;
+    rv = mManifestItem->GetRequestSucceeded(&succeeded);
+    NS_ENSURE_SUCCESS(rv, false);
+
+    if (!succeeded || !mManifestItem->ParseSucceeded()) {
+        return false;
+    }
+
+    if (!mPinned) {
+        uint16_t status;
+        rv = mManifestItem->GetStatus(&status);
+        NS_ENSURE_SUCCESS(rv, false);
+
+        // Treat these as there would be an update available,
+        // since this is indication of demand to remove this
+        // offline cache.
+        if (status == 404 || status == 410) {
+            return true;
+        }
+    }
+
+    return mManifestItem->NeedsUpdate();
+}
+
 void
 nsOfflineCacheUpdate::LoadCompleted(nsOfflineCacheUpdateItem *aItem)
 {
     nsresult rv;
 
     LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
 
     if (mState == STATE_FINISHED) {
@@ -1406,16 +1512,22 @@ nsOfflineCacheUpdate::LoadCompleted(nsOf
     if (mState == STATE_CANCELLED) {
         Finish();
         return;
     }
 
     if (mState == STATE_CHECKING) {
         // Manifest load finished.
 
+        if (mOnlyCheckUpdate) {
+            Finish();
+            NotifyUpdateAvailability(CheckUpdateAvailability());
+            return;
+        }
+
         NS_ASSERTION(mManifestItem,
                      "Must have a manifest item in STATE_CHECKING.");
         NS_ASSERTION(mManifestItem == aItem,
                      "Unexpected aItem in nsOfflineCacheUpdate::LoadCompleted");
 
         // A 404 or 410 is interpreted as an intentional removal of
         // the manifest file, rather than a transient server error.
         // Obsolete this cache group if one of these is returned.
@@ -1584,17 +1696,17 @@ nsOfflineCacheUpdate::ManifestCheckCompl
         // correct.
         FinishNoNotify();
 
         nsRefPtr<nsOfflineCacheUpdate> newUpdate =
             new nsOfflineCacheUpdate();
         // Leave aDocument argument null. Only glues and children keep
         // document instances.
         newUpdate->Init(mManifestURI, mDocumentURI, nullptr,
-                        mCustomProfileDir, mLoadContext);
+                        mCustomProfileDir, mAppID, mInBrowser);
 
         // In a rare case the manifest will not be modified on the next refetch
         // transfer all master document URIs to the new update to ensure that
         // all documents refering it will be properly cached.
         for (int32_t i = 0; i < mDocumentURIs.Count(); i++) {
             newUpdate->StickDocument(mDocumentURIs[i]);
         }
 
@@ -1827,16 +1939,34 @@ nsOfflineCacheUpdate::NotifyState(uint32
     GatherObservers(observers);
 
     for (int32_t i = 0; i < observers.Count(); i++) {
         observers[i]->UpdateStateChanged(this, state);
     }
 }
 
 void
+nsOfflineCacheUpdate::NotifyUpdateAvailability(bool updateAvailable)
+{
+    if (!mUpdateAvailableObserver)
+        return;
+
+    LOG(("nsOfflineCacheUpdate::NotifyUpdateAvailability [this=%p, avail=%d]",
+         this, updateAvailable));
+
+    const char* topic = updateAvailable
+                      ? "offline-cache-update-available"
+                      : "offline-cache-update-unavailable";
+
+    nsCOMPtr<nsIObserver> observer;
+    observer.swap(mUpdateAvailableObserver);
+    observer->Observe(mManifestURI, topic, nullptr);
+}
+
+void
 nsOfflineCacheUpdate::AssociateDocuments(nsIApplicationCache* cache)
 {
     nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
     GatherObservers(observers);
 
     for (int32_t i = 0; i < observers.Count(); i++) {
         observers[i]->ApplicationCacheAvailable(cache);
     }
@@ -1929,17 +2059,17 @@ nsOfflineCacheUpdate::ScheduleImplicit()
 
 nsresult
 nsOfflineCacheUpdate::FinishNoNotify()
 {
     LOG(("nsOfflineCacheUpdate::Finish [%p]", this));
 
     mState = STATE_FINISHED;
 
-    if (!mPartialUpdate) {
+    if (!mPartialUpdate && !mOnlyCheckUpdate) {
         if (mSucceeded) {
             nsIArray *namespaces = mManifestItem->GetNamespaces();
             nsresult rv = mApplicationCache->AddNamespaces(namespaces);
             if (NS_FAILED(rv)) {
                 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
                 mSucceeded = false;
             }
 
@@ -2075,17 +2205,17 @@ nsOfflineCacheUpdate::GetStatus(uint16_t
     }
 
     return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsOfflineCacheUpdate::GetPartial(bool *aPartial)
 {
-    *aPartial = mPartialUpdate;
+    *aPartial = mPartialUpdate || mOnlyCheckUpdate;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI)
 {
     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
 
--- a/uriloader/prefetch/nsOfflineCacheUpdate.h
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.h
@@ -32,16 +32,17 @@
 #include "nsWeakReference.h"
 #include "nsICryptoHash.h"
 #include "mozilla/Attributes.h"
 
 class nsOfflineCacheUpdate;
 
 class nsICacheEntryDescriptor;
 class nsIUTF8StringEnumerator;
+class nsILoadContext;
 
 class nsOfflineCacheUpdateItem : public nsIDOMLoadStatus
                                , public nsIStreamListener
                                , public nsIRunnable
                                , public nsIInterfaceRequestor
                                , public nsIChannelEventSink
 {
 public:
@@ -213,28 +214,31 @@ public:
 
     virtual nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate);
 
 protected:
     friend class nsOfflineCacheUpdateItem;
     void OnByteProgress(uint64_t byteIncrement);
 
 private:
+    nsresult InitInternal(nsIURI *aManifestURI);
     nsresult HandleManifest(bool *aDoUpdate);
     nsresult AddURI(nsIURI *aURI, uint32_t aItemType);
 
     nsresult ProcessNextURI();
 
     // Adds items from the previous cache witha type matching aType.
     // If namespaceFilter is non-null, only items matching the
     // specified namespaces will be added.
     nsresult AddExistingItems(uint32_t aType,
                               nsTArray<nsCString>* namespaceFilter = nullptr);
     nsresult ScheduleImplicit();
     void AssociateDocuments(nsIApplicationCache* cache);
+    bool CheckUpdateAvailability();
+    void NotifyUpdateAvailability(bool updateAvailable);
 
     void GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers);
     void NotifyState(uint32_t state);
     nsresult Finish();
     nsresult FinishNoNotify();
 
     // Find one non-pinned cache group and evict it.
     nsresult EvictOneNonPinned();
@@ -247,25 +251,30 @@ private:
         STATE_CANCELLED,
         STATE_FINISHED
     } mState;
 
     nsOfflineCacheUpdateOwner *mOwner;
 
     bool mAddedItems;
     bool mPartialUpdate;
+    bool mOnlyCheckUpdate;
     bool mSucceeded;
     bool mObsolete;
 
     nsCString mUpdateDomain;
     nsCString mGroupID;
     nsCOMPtr<nsIURI> mManifestURI;
     nsCOMPtr<nsIURI> mDocumentURI;
     nsCOMPtr<nsIFile> mCustomProfileDir;
-    nsCOMPtr<nsILoadContext> mLoadContext;
+
+    uint32_t mAppID;
+    bool mInBrowser;
+
+    nsCOMPtr<nsIObserver> mUpdateAvailableObserver;
 
     nsCOMPtr<nsIApplicationCache> mApplicationCache;
     nsCOMPtr<nsIApplicationCache> mPreviousApplicationCache;
 
     nsCOMPtr<nsIObserverService> mObserverService;
 
     nsRefPtr<nsOfflineManifestItem> mManifestItem;
 
@@ -307,24 +316,27 @@ public:
 
     nsOfflineCacheUpdateService();
     ~nsOfflineCacheUpdateService();
 
     nsresult Init();
 
     nsresult ScheduleUpdate(nsOfflineCacheUpdate *aUpdate);
     nsresult FindUpdate(nsIURI *aManifestURI,
-                        nsILoadContext *aLoadContext,
+                        uint32_t aAppID,
+                        bool aInBrowser,
                         nsOfflineCacheUpdate **aUpdate);
 
     nsresult Schedule(nsIURI *aManifestURI,
                       nsIURI *aDocumentURI,
                       nsIDOMDocument *aDocument,
                       nsIDOMWindow* aWindow,
                       nsIFile* aCustomProfileDir,
+                      uint32_t aAppID,
+                      bool aInBrowser,
                       nsIOfflineCacheUpdate **aUpdate);
 
     virtual nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate);
 
     /**
      * Returns the singleton nsOfflineCacheUpdateService without an addref, or
      * nullptr if the service couldn't be created.
      */
--- a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -76,16 +76,49 @@ public:
     AutoFreeArray(uint32_t count, char **values)
         : mCount(count), mValues(values) {};
     ~AutoFreeArray() { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mValues); }
 private:
     uint32_t mCount;
     char **mValues;
 };
 
+namespace { // anon
+
+nsresult
+GetAppIDAndInBrowserFromWindow(nsIDOMWindow *aWindow,
+                               uint32_t *aAppId,
+                               bool *aInBrowser)
+{
+    *aAppId = NECKO_NO_APP_ID;
+    *aInBrowser = false;
+
+    if (!aWindow) {
+        return NS_OK;
+    }
+
+    nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
+    nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
+    if (!loadContext) {
+        return NS_OK;
+    }
+
+    nsresult rv;
+
+    rv = loadContext->GetAppId(aAppId);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = loadContext->GetIsInBrowserElement(aInBrowser);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+}
+
+} // anon
+
 //-----------------------------------------------------------------------------
 // nsOfflineCachePendingUpdate
 //-----------------------------------------------------------------------------
 
 class nsOfflineCachePendingUpdate MOZ_FINAL : public nsIWebProgressListener
                                             , public nsSupportsWeakReference
 {
 public:
@@ -161,19 +194,26 @@ nsOfflineCachePendingUpdate::OnStateChan
         return NS_OK;
     }
 
     LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]",
          this, progressDoc.get()));
 
     // Only schedule the update if the document loaded successfully
     if (NS_SUCCEEDED(aStatus)) {
+        // Get extended origin attributes
+        uint32_t appId;
+        bool isInBrowserElement;
+        nsresult rv = GetAppIDAndInBrowserFromWindow(window, &appId, &isInBrowserElement);
+        NS_ENSURE_SUCCESS(rv, rv);
+
         nsCOMPtr<nsIOfflineCacheUpdate> update;
         mService->Schedule(mManifestURI, mDocumentURI,
-                           updateDoc, window, nullptr, getter_AddRefs(update));
+                           updateDoc, window, nullptr,
+                           appId, isInBrowserElement, getter_AddRefs(update));
     }
 
     aWebProgress->RemoveProgressListener(this);
     NS_RELEASE_THIS();
 
     return NS_OK;
 }
 
@@ -400,29 +440,30 @@ nsOfflineCacheUpdateService::GetUpdate(u
         *aUpdate = nullptr;
     }
 
     return NS_OK;
 }
 
 nsresult
 nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI,
-                                        nsILoadContext *aLoadContext,
+                                        uint32_t aAppID,
+                                        bool aInBrowser,
                                         nsOfflineCacheUpdate **aUpdate)
 {
     nsresult rv;
 
     nsCOMPtr<nsIApplicationCacheService> cacheService =
         do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoCString groupID;
-    rv = cacheService->BuildGroupID(aManifestURI,
-                                    aLoadContext,
-                                    groupID);
+    rv = cacheService->BuildGroupIDForApp(aManifestURI,
+                                          aAppID, aInBrowser,
+                                          groupID);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsRefPtr<nsOfflineCacheUpdate> update;
     for (uint32_t i = 0; i < mUpdates.Length(); i++) {
         update = mUpdates[i];
 
         bool partial;
         rv = update->GetPartial(&partial);
@@ -443,65 +484,102 @@ nsOfflineCacheUpdateService::FindUpdate(
 }
 
 nsresult
 nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
                                       nsIURI *aDocumentURI,
                                       nsIDOMDocument *aDocument,
                                       nsIDOMWindow* aWindow,
                                       nsIFile* aCustomProfileDir,
+                                      uint32_t aAppID,
+                                      bool aInBrowser,
                                       nsIOfflineCacheUpdate **aUpdate)
 {
     nsCOMPtr<nsIOfflineCacheUpdate> update;
     if (GeckoProcessType_Default != XRE_GetProcessType()) {
         update = new OfflineCacheUpdateChild(aWindow);
     }
     else {
         update = new OfflineCacheUpdateGlue();
     }
 
     nsresult rv;
 
-    nsCOMPtr<nsILoadContext> loadContext;
-    if (aWindow) {
-        nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
-        loadContext = do_QueryInterface(webNav);
-    }
-
     rv = update->Init(aManifestURI, aDocumentURI, aDocument,
-                      aCustomProfileDir, loadContext);
+                      aCustomProfileDir, aAppID, aInBrowser);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = update->Schedule();
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ADDREF(*aUpdate = update);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI,
                                             nsIURI *aDocumentURI,
                                             nsIDOMWindow *aWindow,
                                             nsIOfflineCacheUpdate **aUpdate)
 {
-    return Schedule(aManifestURI, aDocumentURI, nullptr, aWindow, nullptr, aUpdate);
+    // Get extended origin attributes
+    uint32_t appId;
+    bool isInBrowser;
+    nsresult rv = GetAppIDAndInBrowserFromWindow(aWindow, &appId, &isInBrowser);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return Schedule(aManifestURI, aDocumentURI, nullptr, aWindow, nullptr,
+                    appId, isInBrowser, aUpdate);
 }
 
 NS_IMETHODIMP
 nsOfflineCacheUpdateService::ScheduleCustomProfileUpdate(nsIURI *aManifestURI,
                                                          nsIURI *aDocumentURI,
                                                          nsIFile *aProfileDir,
                                                          nsIOfflineCacheUpdate **aUpdate)
 {
     // The profile directory is mandatory
     NS_ENSURE_ARG(aProfileDir);
 
-    return Schedule(aManifestURI, aDocumentURI, nullptr, nullptr, aProfileDir, aUpdate);
+    return Schedule(aManifestURI, aDocumentURI, nullptr, nullptr, aProfileDir,
+                    NECKO_NO_APP_ID, false, aUpdate);
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::ScheduleAppUpdate(nsIURI *aManifestURI,
+                                               nsIURI *aDocumentURI,
+                                               uint32_t aAppID, bool aInBrowser,
+                                               nsIOfflineCacheUpdate **aUpdate)
+{
+    return Schedule(aManifestURI, aDocumentURI, nullptr, nullptr, nullptr,
+                    aAppID, aInBrowser, aUpdate);
+}
+
+NS_IMETHODIMP nsOfflineCacheUpdateService::CheckForUpdate(nsIURI *aManifestURI,
+                                                          uint32_t aAppID,
+                                                          bool aInBrowser,
+                                                          nsIObserver *aObserver)
+{
+    if (GeckoProcessType_Default != XRE_GetProcessType()) {
+        // Not intended to support this on child processes
+        return NS_ERROR_NOT_IMPLEMENTED;
+    }
+
+    nsCOMPtr<nsIOfflineCacheUpdate> update = new OfflineCacheUpdateGlue();
+
+    nsresult rv;
+
+    rv = update->InitForUpdateCheck(aManifestURI, aAppID, aInBrowser, aObserver);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = update->Schedule();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsOfflineCacheUpdateService::nsIObserver
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsOfflineCacheUpdateService::Observe(nsISupports     *aSubject,
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -117,18 +117,17 @@ public:
     NS_IMETHOD GetHasTransparentBackground(bool& aTransparent) { aTransparent = false; return NS_OK; }
     NS_IMETHOD HideWindowChrome(bool aShouldHide) { return NS_ERROR_NOT_IMPLEMENTED; }
     virtual void* GetNativeData(uint32_t aDataType);
     NS_IMETHOD SetTitle(const nsAString& aTitle) { return NS_OK; }
     NS_IMETHOD SetIcon(const nsAString& aIconSpec) { return NS_OK; }
     NS_IMETHOD EnableDragDrop(bool aEnable) { return NS_OK; }
     NS_IMETHOD CaptureMouse(bool aCapture) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD CaptureRollupEvents(nsIRollupListener *aListener,
-                                   bool aDoCapture,
-                                   bool aConsumeRollupEvent) { return NS_ERROR_NOT_IMPLEMENTED; }
+                                   bool aDoCapture) { return NS_ERROR_NOT_IMPLEMENTED; }
 
     NS_IMETHOD GetAttention(int32_t aCycleCount) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD BeginResizeDrag(nsGUIEvent* aEvent, int32_t aHorizontal, int32_t aVertical) { return NS_ERROR_NOT_IMPLEMENTED; }
 
     NS_IMETHOD ResetInputState();
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction);
     NS_IMETHOD_(InputContext) GetInputContext();
--- a/widget/cocoa/nsAppShell.mm
+++ b/widget/cocoa/nsAppShell.mm
@@ -33,20 +33,16 @@
 #include "TextInputHandler.h"
 #include "mozilla/HangMonitor.h"
 #include "sampler.h"
 
 #include "npapi.h"
 
 using namespace mozilla::widget;
 
-// defined in nsChildView.mm
-extern nsIRollupListener * gRollupListener;
-extern nsIWidget         * gRollupWidget;
-
 // defined in nsCocoaWindow.mm
 extern int32_t             gXULModalLevel;
 
 static bool gAppShellMethodsSwizzled = false;
 // List of current Cocoa app-modal windows (nested if more than one).
 nsCocoaAppModalWindowList *gCocoaAppModalWindowList = NULL;
 
 // Push a Cocoa app-modal window onto the top of our list.
@@ -958,18 +954,20 @@ nsAppShell::AfterProcessNextEvent(nsIThr
 // any sort of menu.  But make sure we don't do this for notifications we
 // send ourselves (whose 'sender' will be @"org.mozilla.gecko.PopupWindow").
 - (void)beginMenuTracking:(NSNotification*)aNotification
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   NSString *sender = [aNotification object];
   if (!sender || ![sender isEqualToString:@"org.mozilla.gecko.PopupWindow"]) {
-    if (gRollupListener && gRollupWidget)
-      gRollupListener->Rollup(0);
+    nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
+    nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
+    if (rollupWidget)
+      rollupListener->Rollup(0, nullptr);
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 @end
 
 // We hook beginModalSessionForWindow: and endModalSession: in order to
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -178,16 +178,20 @@ typedef NSInteger NSEventGestureAxis;
 // automatically firing momentum scroll events.
 @interface NSEvent (ScrollPhase)
 // Leopard and SnowLeopard
 - (long long)_scrollPhase;
 // Lion and above
 - (NSEventPhase)momentumPhase;
 @end
 
+@protocol EventRedirection
+  - (NSView*)targetView;
+@end
+
 @interface ChildView : NSView<
 #ifdef ACCESSIBILITY
                               mozAccessible,
 #endif
                               mozView, NSTextInput>
 {
 @private
   // the nsChildView that created the view. It retains this NSView, so
@@ -262,25 +266,29 @@ typedef NSInteger NSEventGestureAxis;
 
   // Whether this uses off-main-thread compositing.
   BOOL mUsingOMTCompositor;
 }
 
 // class initialization
 + (void)initialize;
 
++ (void)registerViewForDraggedTypes:(NSView*)aView;
+
 // these are sent to the first responder when the window key status changes
 - (void)viewsWindowDidBecomeKey;
 - (void)viewsWindowDidResignKey;
 
 // Stop NSView hierarchy being changed during [ChildView drawRect:]
 - (void)delayedTearDown;
 
 - (void)sendFocusEvent:(uint32_t)eventType;
 
+- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent;
+
 - (void)handleMouseMoved:(NSEvent*)aEvent;
 
 - (void)drawRect:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext;
 
 - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent
                             enter:(BOOL)aEnter
                              type:(nsMouseEvent::exitType)aType;
 
@@ -414,17 +422,17 @@ public:
   NS_IMETHOD              DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus);
 
   virtual bool            GetShouldAccelerate();
   virtual bool            UseOffMainThreadCompositing();
 
   NS_IMETHOD        SetCursor(nsCursor aCursor);
   NS_IMETHOD        SetCursor(imgIContainer* aCursor, uint32_t aHotspotX, uint32_t aHotspotY);
 
-  NS_IMETHOD        CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture, bool aConsumeRollupEvent);
+  NS_IMETHOD        CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture);
   NS_IMETHOD        SetTitle(const nsAString& title);
 
   NS_IMETHOD        GetAttention(int32_t aCycleCount);
 
   virtual bool HasPendingInputEvent();
 
   NS_IMETHOD        ActivateNativeMenuItemAt(const nsAString& indexString);
   NS_IMETHOD        ForceUpdateNativeMenuAt(const nsAString& indexString);
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -88,36 +88,30 @@ extern "C" {
   CG_EXTERN void CGContextResetCTM(CGContextRef);
   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
   CG_EXTERN void CGContextResetClip(CGContextRef);
 }
 
 // defined in nsMenuBarX.mm
 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
 
-// these are defined in nsCocoaWindow.mm
-extern bool gConsumeRollupEvent;
-
 bool gChildViewMethodsSwizzled = false;
 
 extern nsISupportsArray *gDraggedTransferables;
 
 ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
 NSEvent* ChildViewMouseTracker::sLastMouseMoveEvent = nil;
 NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
 NSPoint ChildViewMouseTracker::sLastScrollEventScreenLocation = NSZeroPoint;
 
 #ifdef INVALIDATE_DEBUGGING
 static void blinkRect(Rect* r);
 static void blinkRgn(RgnHandle rgn);
 #endif
 
-nsIRollupListener * gRollupListener = nullptr;
-nsIWidget         * gRollupWidget   = nullptr;
-
 bool gUserCancelledDrag = false;
 
 uint32_t nsChildView::sLastInputEventCount = 0;
 
 @interface ChildView(Private)
 
 // sets up our view, attaching it to its owning gecko view
 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
@@ -811,17 +805,17 @@ NS_IMETHODIMP nsChildView::Move(int32_t 
   mBounds.x = aX;
   mBounds.y = aY;
 
   [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
 
   if (mVisible)
     [mView setNeedsDisplay:YES];
 
-  NotifyRollupGeometryChange(gRollupListener);
+  NotifyRollupGeometryChange();
   ReportMoveEvent();
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 NS_IMETHODIMP nsChildView::Resize(int32_t aWidth, int32_t aHeight, bool aRepaint)
@@ -834,17 +828,17 @@ NS_IMETHODIMP nsChildView::Resize(int32_
   mBounds.width  = aWidth;
   mBounds.height = aHeight;
 
   [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
 
   if (mVisible && aRepaint)
     [mView setNeedsDisplay:YES];
 
-  NotifyRollupGeometryChange(gRollupListener);
+  NotifyRollupGeometryChange();
   ReportSizeEvent();
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 NS_IMETHODIMP nsChildView::Resize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, bool aRepaint)
@@ -865,17 +859,17 @@ NS_IMETHODIMP nsChildView::Resize(int32_
     mBounds.height = aHeight;
   }
 
   [mView setFrame:DevPixelsToCocoaPoints(mBounds)];
 
   if (mVisible && aRepaint)
     [mView setNeedsDisplay:YES];
 
-  NotifyRollupGeometryChange(gRollupListener);
+  NotifyRollupGeometryChange();
   if (isMoving) {
     ReportMoveEvent();
     if (mOnDestroyCalled)
       return NS_OK;
   }
   if (isResizing)
     ReportSizeEvent();
 
@@ -1485,19 +1479,18 @@ nsIntPoint nsChildView::WidgetToScreenOf
   FlipCocoaScreenCoordinate(origin);
 
   // convert to device pixels
   return CocoaPointsToDevPixels(origin);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntPoint(0,0));
 }
 
-NS_IMETHODIMP nsChildView::CaptureRollupEvents(nsIRollupListener * aListener, 
-                                               bool aDoCapture, 
-                                               bool aConsumeRollupEvent)
+NS_IMETHODIMP nsChildView::CaptureRollupEvents(nsIRollupListener * aListener,
+                                               bool aDoCapture)
 {
   // this never gets called, only top-level windows can be rollup widgets
   return NS_OK;
 }
 
 NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title)
 {
   // child views don't have titles
@@ -1907,16 +1900,30 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
     [sendTypes release];
     [returnTypes release];
 
     initialized = YES;
   }
 }
 
++ (void)registerViewForDraggedTypes:(NSView*)aView
+{
+  [aView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
+                                                           NSStringPboardType,
+                                                           NSHTMLPboardType,
+                                                           NSURLPboardType,
+                                                           NSFilesPromisePboardType,
+                                                           kWildcardPboardType,
+                                                           kCorePboardType_url,
+                                                           kCorePboardType_urld,
+                                                           kCorePboardType_urln,
+                                                           nil]];
+}
+
 // initWithFrame:geckoChild:
 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ((self = [super initWithFrame:inFrame])) {
     mGeckoChild = inChild;
     mIsPluginView = NO;
@@ -1954,27 +1961,18 @@ NSEvent* gLastDragMouseDownEvent = nil;
     [self setFocusRingType:NSFocusRingTypeNone];
 
 #ifdef __LP64__
     mSwipeAnimationCancelled = nil;
 #endif
   }
   
   // register for things we'll take from other applications
-  PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
-  [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
-                                                          NSStringPboardType,
-                                                          NSHTMLPboardType,
-                                                          NSURLPboardType,
-                                                          NSFilesPromisePboardType,
-                                                          kWildcardPboardType,
-                                                          kCorePboardType_url,
-                                                          kCorePboardType_urld,
-                                                          kCorePboardType_urln,
-                                                          nil]];
+  [ChildView registerViewForDraggedTypes:self];
+
   [[NSNotificationCenter defaultCenter] addObserver:self
                                            selector:@selector(windowBecameMain:)
                                                name:NSWindowDidBecomeMainNotification
                                              object:nil];
   [[NSNotificationCenter defaultCenter] addObserver:self
                                            selector:@selector(windowResignedMain:)
                                                name:NSWindowDidResignMainNotification
                                              object:nil];
@@ -2334,17 +2332,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
   }
   [super scrollRect:aRect by:offset];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (BOOL)mouseDownCanMoveWindow
 {
-  return NO;
+  return [[self window] isMovableByWindowBackground];
 }
 
 - (void)lockFocus
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   [super lockFocus];
 
@@ -2646,24 +2644,26 @@ NSEvent* gLastDragMouseDownEvent = nil;
 // such and let the OS (and other programs) know when it opens and closes
 // (this is how the OS knows to close other programs' context menus when
 // ours open).  We send the initial notification here, but others are sent
 // in nsCocoaWindow::Show().
 - (void)maybeInitContextMenuTracking
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  if (!gRollupWidget)
-    return;
-
 #ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
   return;
 #endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */
 
-  NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
+  nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
+  nsCOMPtr<nsIWidget> widget = rollupListener->GetRollupWidget();
+  if (!widget)
+    return;
+
+  NSWindow *popupWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
   if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]])
     return;
 
   [[NSDistributedNotificationCenter defaultCenter]
     postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification"
                   object:@"org.mozilla.gecko.PopupWindow"];
   [(PopupWindow*)popupWindow setIsContextMenu:YES];
 
@@ -2673,58 +2673,57 @@ NSEvent* gLastDragMouseDownEvent = nil;
 // Returns true if the event should no longer be processed, false otherwise.
 // This does not return whether or not anything was rolled up.
 - (BOOL)maybeRollup:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   BOOL consumeEvent = NO;
 
-  if (gRollupWidget && gRollupListener) {
-    NSWindow* currentPopup = static_cast<NSWindow*>(gRollupWidget->GetNativeData(NS_NATIVE_WINDOW));
+  nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
+  nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
+  if (rollupWidget) {
+    NSWindow* currentPopup = static_cast<NSWindow*>(rollupWidget->GetNativeData(NS_NATIVE_WINDOW));
     if (!nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) {
       // event is not over the rollup window, default is to roll up
       bool shouldRollup = true;
 
       // check to see if scroll events should roll up the popup
       if ([theEvent type] == NSScrollWheel) {
-        shouldRollup = gRollupListener->ShouldRollupOnMouseWheelEvent();
+        shouldRollup = rollupListener->ShouldRollupOnMouseWheelEvent();
         // always consume scroll events that aren't over the popup
         consumeEvent = YES;
       }
 
       // if we're dealing with menus, we probably have submenus and
       // we don't want to rollup if the click is in a parent menu of
       // the current submenu
       uint32_t popupsToRollup = UINT32_MAX;
-      if (gRollupListener) {
-        nsAutoTArray<nsIWidget*, 5> widgetChain;
-        uint32_t sameTypeCount = gRollupListener->GetSubmenuWidgetChain(&widgetChain);
-        for (uint32_t i = 0; i < widgetChain.Length(); i++) {
-          nsIWidget* widget = widgetChain[i];
-          NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
-          if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
-            // don't roll up if the mouse event occurred within a menu of the
-            // same type. If the mouse event occurred in a menu higher than
-            // that, roll up, but pass the number of popups to Rollup so
-            // that only those of the same type close up.
-            if (i < sameTypeCount) {
-              shouldRollup = false;
-            }
-            else {
-              popupsToRollup = sameTypeCount;
-            }
-            break;
+      nsAutoTArray<nsIWidget*, 5> widgetChain;
+      uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
+      for (uint32_t i = 0; i < widgetChain.Length(); i++) {
+        nsIWidget* widget = widgetChain[i];
+        NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
+        if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) {
+          // don't roll up if the mouse event occurred within a menu of the
+          // same type. If the mouse event occurred in a menu higher than
+          // that, roll up, but pass the number of popups to Rollup so
+          // that only those of the same type close up.
+          if (i < sameTypeCount) {
+            shouldRollup = false;
           }
+          else {
+            popupsToRollup = sameTypeCount;
+          }
+          break;
         }
       }
 
       if (shouldRollup) {
-        gRollupListener->Rollup(popupsToRollup);
-        consumeEvent = (BOOL)gConsumeRollupEvent;
+        consumeEvent = (BOOL)rollupListener->Rollup(popupsToRollup, nullptr);
       }
     }
   }
 
   return consumeEvent;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
 }
@@ -3279,16 +3278,35 @@ NSEvent* gLastDragMouseDownEvent = nil;
   }
 
   event.exit = aType;
 
   nsEventStatus status; // ignored
   mGeckoChild->DispatchEvent(&event, status);
 }
 
+- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent
+{
+  if (!theEvent || !mGeckoChild) {
+    return;
+  }
+
+  nsCocoaWindow* windowWidget = mGeckoChild->GetXULWindowWidget();
+  if (!windowWidget) {
+    return;
+  }
+
+  // We assume later on that sending a hit test event won't cause widget destruction.
+  nsMouseEvent hitTestEvent(true, NS_MOUSE_MOZHITTEST, mGeckoChild, nsMouseEvent::eReal);
+  [self convertCocoaMouseEvent:theEvent toGeckoEvent:&hitTestEvent];
+  bool result = mGeckoChild->DispatchWindowEvent(hitTestEvent);
+
+  [windowWidget->GetCocoaWindow() setMovableByWindowBackground:result];
+}
+
 - (void)handleMouseMoved:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (!mGeckoChild)
     return;
 
   nsMouseEvent geckoEvent(true, NS_MOUSE_MOVE, mGeckoChild, nsMouseEvent::eReal);
@@ -4803,16 +4821,21 @@ ChildView*
 ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
 {
   NSWindow* window = sWindowUnderMouse;
   if (!window)
     return nil;
 
   NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
   NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
+
+  while([view conformsToProtocol:@protocol(EventRedirection)]) {
+    view = [(id<EventRedirection>)view targetView];
+  }
+
   if (![view isKindOfClass:[ChildView class]])
     return nil;
 
   ChildView* childView = (ChildView*)view;
   // If childView is being destroyed return nil.
   if (![childView widget])
     return nil;
   return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil;
@@ -4901,17 +4924,17 @@ ChildViewMouseTracker::WindowAcceptsEven
 // triggers bmo bugs 431902 and 476393.  So here we make sure that a
 // ToolbarWindow's contentView always returns NO from the
 // mouseDownCanMoveWindow method.
 - (BOOL)nsChildView_NSView_mouseDownCanMoveWindow
 {
   NSWindow *ourWindow = [self window];
   NSView *contentView = [ourWindow contentView];
   if ([ourWindow isKindOfClass:[ToolbarWindow class]] && (self == contentView))
-    return NO;
+    return [ourWindow isMovableByWindowBackground];
   return [self nsChildView_NSView_mouseDownCanMoveWindow];
 }
 
 @end
 
 #ifdef __LP64__
 // When using blocks, at least on OS X 10.7, the OS sometimes calls
 // +[NSEvent removeMonitor:] more than once on a single event monitor, which
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -13,16 +13,17 @@
 #include "nsBaseWidget.h"
 #include "nsPIWidgetCocoa.h"
 #include "nsAutoPtr.h"
 #include "nsCocoaUtils.h"
 
 class nsCocoaWindow;
 class nsChildView;
 class nsMenuBarX;
+@class ChildView;
 
 // Value copied from BITMAP_MAX_AREA, used in nsNativeThemeCocoa.mm
 #define CUIDRAW_MAX_AREA 500000
 
 // If we are using an SDK older than 10.7, define bits we need that are missing
 // from it.
 #if !defined(MAC_OS_X_VERSION_10_7) || \
     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
@@ -171,26 +172,28 @@ typedef struct _nsCocoaWindowList {
 @end
 
 // NSWindow subclass for handling windows with toolbars.
 @interface ToolbarWindow : BaseWindow
 {
   TitlebarAndBackgroundColor *mColor;
   float mUnifiedToolbarHeight;
   NSColor *mBackgroundColor;
+  NSView *mTitlebarView; // strong
 }
 // Pass nil here to get the default appearance.
 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive;
 - (void)setUnifiedToolbarHeight:(float)aHeight;
 - (float)unifiedToolbarHeight;
 - (float)titlebarHeight;
 - (NSRect)titlebarRect;
 - (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync;
 - (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect;
 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState;
+- (ChildView*)mainChildView;
 @end
 
 class nsCocoaWindow : public nsBaseWidget, public nsPIWidgetCocoa
 {
 private:
   
   typedef nsBaseWidget Inherited;
 
@@ -250,17 +253,17 @@ public:
 
     NS_IMETHOD Invalidate(const nsIntRect &aRect);
     virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
     virtual LayerManager* GetLayerManager(PLayersChild* aShadowManager = nullptr,
                                           LayersBackend aBackendHint = mozilla::layers::LAYERS_NONE,
                                           LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                                           bool* aAllowRetaining = nullptr);
     NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus) ;
-    NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture, bool aConsumeRollupEvent);
+    NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture);
     NS_IMETHOD GetAttention(int32_t aCycleCount);
     virtual bool HasPendingInputEvent();
     virtual nsTransparencyMode GetTransparencyMode();
     virtual void SetTransparencyMode(nsTransparencyMode aMode);
     NS_IMETHOD SetWindowShadowStyle(int32_t aStyle);
     virtual void SetShowsToolbarButton(bool aShow);
     virtual void SetShowsFullScreenButton(bool aShow);
     virtual void SetWindowAnimationType(WindowAnimationType aType);
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -52,24 +52,20 @@ int32_t gXULModalLevel = 0;
 
 // In principle there should be only one app-modal window at any given time.
 // But sometimes, despite our best efforts, another window appears above the
 // current app-modal window.  So we need to keep a linked list of app-modal
 // windows.  (A non-sheet window that appears above an app-modal window is
 // also made app-modal.)  See nsCocoaWindow::SetModal().
 nsCocoaWindowList *gGeckoAppModalWindowList = NULL;
 
-bool gConsumeRollupEvent;
-
 // defined in nsMenuBarX.mm
 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
 
 // defined in nsChildView.mm
-extern nsIRollupListener * gRollupListener;
-extern nsIWidget         * gRollupWidget;
 extern BOOL                gSomeMenuBarPainted;
 
 extern "C" {
   // CGSPrivate.h
   typedef NSInteger CGSConnection;
   typedef NSInteger CGSWindow;
   typedef NSUInteger CGSWindowFilterRef;
   extern CGSConnection _CGSDefaultConnection(void);
@@ -85,21 +81,22 @@ extern "C" {
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsCocoaWindow, Inherited, nsPIWidgetCocoa)
 
 // A note on testing to see if your object is a sheet...
 // |mWindowType == eWindowType_sheet| is true if your gecko nsIWidget is a sheet
 // widget - whether or not the sheet is showing. |[mWindow isSheet]| will return
 // true *only when the sheet is actually showing*. Choose your test wisely.
 
-// roll up any popup windows
 static void RollUpPopups()
 {
-  if (gRollupListener && gRollupWidget)
-    gRollupListener->Rollup(0);
+  nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
+  nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
+  if (rollupWidget)
+    rollupListener->Rollup(0, nullptr);
 }
 
 nsCocoaWindow::nsCocoaWindow()
 : mParent(nullptr)
 , mWindow(nil)
 , mDelegate(nil)
 , mSheetWindowParent(nil)
 , mPopupContentView(nil)
@@ -445,16 +442,20 @@ nsresult nsCocoaWindow::CreateNativeWind
     [mWindow setHasShadow:YES];
   }
 
   [mWindow setBackgroundColor:[NSColor clearColor]];
   [mWindow setOpaque:NO];
   [mWindow setContentMinSize:NSMakeSize(60, 60)];
   [mWindow disableCursorRects];
 
+  // Make sure the window starts out not draggable by the background.
+  // We will turn it on as necessary.
+  [mWindow setMovableByWindowBackground:NO];
+
   [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow];
   mWindowMadeHere = true;
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
@@ -1744,39 +1745,31 @@ nsIntSize nsCocoaWindow::ClientToWindowS
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsIntSize(0,0));
 }
 
 nsMenuBarX* nsCocoaWindow::GetMenuBar()
 {
   return mMenuBar;
 }
 
-NS_IMETHODIMP nsCocoaWindow::CaptureRollupEvents(nsIRollupListener * aListener,
-                                                 bool aDoCapture,
-                                                 bool aConsumeRollupEvent)
+NS_IMETHODIMP nsCocoaWindow::CaptureRollupEvents(nsIRollupListener* aListener, bool aDoCapture)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   gRollupListener = nullptr;
-  NS_IF_RELEASE(gRollupWidget);
   
   if (aDoCapture) {
     gRollupListener = aListener;
-    gRollupWidget = this;
-    NS_ADDREF(this);
-
-    gConsumeRollupEvent = aConsumeRollupEvent;
 
     // Sometimes more than one popup window can be visible at the same time
     // (e.g. nested non-native context menus, or the test case (attachment
-    // 276885) for bmo bug 392389, which displays a non-native combo-box in
-    // a non-native popup window).  In these cases the "active" popup window
-    // (the one that corresponds to the current gRollupWidget) should be the
-    // topmost -- the (nested) context menu the mouse is currently over, or
-    // the combo-box's drop-down list (when it's displayed).  But (among
+    // 276885) for bmo bug 392389, which displays a non-native combo-box in a
+    // non-native popup window).  In these cases the "active" popup window should
+    // be the topmost -- the (nested) context menu the mouse is currently over,
+    // or the combo-box's drop-down list (when it's displayed).  But (among
     // windows that have the same "level") OS X makes topmost the window that
     // last received a mouse-down event, which may be incorrect (in the combo-
     // box case, it makes topmost the window containing the combo-box).  So
     // here we fiddle with a non-native popup window's level to make sure the
     // "active" one is always above any other non-native popup windows that
     // may be visible.
     if (mWindow && (mWindowType == eWindowType_popup))
       SetPopupWindowLevel();
@@ -2589,17 +2582,104 @@ static const NSString* kStateShowsToolba
     retval = [NSArray arrayWithArray:holder];
   }
 
   return retval;
 }
 
 @end
 
-// This class allows us to have a "unified toolbar" style window. It works like this:
+@interface TitlebarMouseHandlingView : NSView<EventRedirection>
+{
+  ToolbarWindow* mWindow; // weak
+  BOOL mProcessingRightMouseDown;
+}
+
+- (id)initWithWindow:(ToolbarWindow*)aWindow;
+@end
+
+@implementation TitlebarMouseHandlingView
+
+- (id)initWithWindow:(ToolbarWindow*)aWindow
+{
+  if ((self = [super initWithFrame:[aWindow titlebarRect]])) {
+    mWindow = aWindow;
+    [self setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
+    [ChildView registerViewForDraggedTypes:self];
+    mProcessingRightMouseDown = NO;
+  }
+  return self;
+}
+
+- (NSView*)targetView
+{
+  return [mWindow mainChildView];
+}
+
+- (BOOL)mouseDownCanMoveWindow
+{
+  return [mWindow isMovableByWindowBackground];
+}
+
+// We redirect many types of events to the window's mainChildView simply by
+// passing the event object to the respective handler method. We don't need any
+// coordinate transformations because event coordinates are relative to the
+// window.
+// We only need to handle event types whose target NSView is determined by the
+// event's position. We don't need to handle key events and NSMouseMoved events
+// because those are only sent to the window's first responder. This view
+// doesn't override acceptsFirstResponder, so it will never receive those kinds
+// of events.
+
+- (void)mouseMoved:(NSEvent*)aEvent            { [[self targetView] mouseMoved:aEvent]; }
+- (void)mouseDown:(NSEvent*)aEvent             { [[self targetView] mouseDown:aEvent]; }
+- (void)mouseUp:(NSEvent*)aEvent               { [[self targetView] mouseUp:aEvent]; }
+- (void)mouseDragged:(NSEvent*)aEvent          { [[self targetView] mouseDragged:aEvent]; }
+- (void)rightMouseDown:(NSEvent*)aEvent
+{
+  // To avoid recursion...
<