Merge m-c to m-i
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 08 Dec 2013 08:30:45 -0800
changeset 174145 243a32ad97ed9eaa7987c4f25f797dde9503d1ed
parent 174144 d5a9a9520d8a35f7c9f13cccc1221c4b8f5732bf (current diff)
parent 174130 2827800c529aee3ea948dfd9674fd50b91481c59 (diff)
child 174146 6b48e28ecea63f7dc070d34bd9d165bb450ec418
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to m-i
browser/metro/locales/en-US/chrome/crashprompt.properties
--- a/b2g/components/ContentPermissionPrompt.js
+++ b/b2g/components/ContentPermissionPrompt.js
@@ -1,28 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict"
 
 function debug(str) {
-  //dump("-*- ContentPermissionPrompt: " + s + "\n");
+  //dump("-*- ContentPermissionPrompt: " + str + "\n");
 }
 
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 const Cc = Components.classes;
 
-const PROMPT_FOR_UNKNOWN    = ["geolocation", "desktop-notification",
-                               "audio-capture"];
+const PROMPT_FOR_UNKNOWN = ["audio-capture",
+                            "desktop-notification",
+                            "geolocation",
+                            "video-capture"];
 // Due to privary issue, permission requests like GetUserMedia should prompt
 // every time instead of providing session persistence.
-const PERMISSION_NO_SESSION = ["audio-capture"];
+const PERMISSION_NO_SESSION = ["audio-capture", "video-capture"];
+const ALLOW_MULTIPLE_REQUESTS = ["audio-capture", "video-capture"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Webapps.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
 Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
@@ -36,206 +39,314 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "@mozilla.org/permissionSettings;1",
                                    "nsIDOMPermissionSettings");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "AudioManager",
                                    "@mozilla.org/telephony/audiomanager;1",
                                    "nsIAudioManager");
 
-function rememberPermission(aPermission, aPrincipal, aSession)
+/**
+ * aTypesInfo is an array of {permission, access, action, deny} which keeps
+ * the information of each permission. This arrary is initialized in
+ * ContentPermissionPrompt.prompt and used among functions.
+ *
+ * aTypesInfo[].permission : permission name
+ * aTypesInfo[].access     : permission name + request.access
+ * aTypesInfo[].action     : the default action of this permission
+ * aTypesInfo[].deny       : true if security manager denied this app's origin
+ *                           principal.
+ * Note:
+ *   aTypesInfo[].permission will be sent to prompt only when
+ *   aTypesInfo[].action is PROMPT_ACTION and aTypesInfo[].deny is false.
+ */
+function rememberPermission(aTypesInfo, aPrincipal, aSession)
 {
   function convertPermToAllow(aPerm, aPrincipal)
   {
     let type =
       permissionManager.testExactPermissionFromPrincipal(aPrincipal, aPerm);
     if (type == Ci.nsIPermissionManager.PROMPT_ACTION ||
         (type == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
-        PROMPT_FOR_UNKNOWN.indexOf(aPermission) >= 0)) {
+        PROMPT_FOR_UNKNOWN.indexOf(aPerm) >= 0)) {
+      debug("add " + aPerm + " to permission manager with ALLOW_ACTION");
       if (!aSession) {
         permissionManager.addFromPrincipal(aPrincipal,
                                            aPerm,
                                            Ci.nsIPermissionManager.ALLOW_ACTION);
-      } else if (PERMISSION_NO_SESSION.indexOf(aPermission) < 0) {
+      } else if (PERMISSION_NO_SESSION.indexOf(aPerm) < 0) {
         permissionManager.addFromPrincipal(aPrincipal,
                                            aPerm,
                                            Ci.nsIPermissionManager.ALLOW_ACTION,
                                            Ci.nsIPermissionManager.EXPIRE_SESSION, 0);
       }
     }
   }
 
-  // Expand the permission to see if we have multiple access properties to convert
-  let access = PermissionsTable[aPermission].access;
-  if (access) {
-    for (let idx in access) {
-      convertPermToAllow(aPermission + "-" + access[idx], aPrincipal);
+  for (let i in aTypesInfo) {
+    // Expand the permission to see if we have multiple access properties
+    // to convert
+    let perm = aTypesInfo[i].permission;
+    let access = PermissionsTable[perm].access;
+    if (access) {
+      for (let idx in access) {
+        convertPermToAllow(perm + "-" + access[idx], aPrincipal);
+      }
+    } else {
+      convertPermToAllow(perm, aPrincipal);
     }
-  } else {
-    convertPermToAllow(aPermission, aPrincipal);
   }
 }
 
 function ContentPermissionPrompt() {}
 
 ContentPermissionPrompt.prototype = {
 
-  handleExistingPermission: function handleExistingPermission(request) {
-    let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
-                                                                   request.type;
-    let result = Services.perms.testExactPermissionFromPrincipal(request.principal, access);
-    if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
+  handleExistingPermission: function handleExistingPermission(request,
+                                                              typesInfo) {
+    typesInfo.forEach(function(type) {
+      type.action =
+        Services.perms.testExactPermissionFromPrincipal(request.principal,
+                                                        type.access);
+      if (type.action == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
+          PROMPT_FOR_UNKNOWN.indexOf(type.access) >= 0) {
+        type.action = Ci.nsIPermissionManager.PROMPT_ACTION;
+      }
+    });
+
+    // If all permissions are allowed already, call allow() without prompting.
+    let checkAllowPermission = function(type) {
+      if (type.action == Ci.nsIPermissionManager.ALLOW_ACTION) {
+        return true;
+      }
+      return false;
+    }
+    if (typesInfo.every(checkAllowPermission)) {
+      debug("all permission requests are allowed");
       request.allow();
       return true;
     }
-    if (result == Ci.nsIPermissionManager.DENY_ACTION ||
-        result == Ci.nsIPermissionManager.UNKNOWN_ACTION && PROMPT_FOR_UNKNOWN.indexOf(access) < 0) {
+
+    // If all permissions are DENY_ACTION or UNKNOWN_ACTION, call cancel()
+    // without prompting.
+    let checkDenyPermission = function(type) {
+      if (type.action == Ci.nsIPermissionManager.DENY_ACTION ||
+          type.action == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
+        return true;
+      }
+      return false;
+    }
+    if (typesInfo.every(checkDenyPermission)) {
+      debug("all permission requests are denied");
       request.cancel();
       return true;
     }
     return false;
   },
 
-  handledByApp: function handledByApp(request) {
+  // multiple requests should be audio and video
+  checkMultipleRequest: function checkMultipleRequest(typesInfo) {
+    if (typesInfo.length == 1) {
+      return true;
+    } else if (typesInfo.length > 1) {
+      let checkIfAllowMultiRequest = function(type) {
+        return (ALLOW_MULTIPLE_REQUESTS.indexOf(type.access) !== -1);
+      }
+      if (typesInfo.every(checkIfAllowMultiRequest)) {
+        debug("legal multiple requests");
+        return true;
+      }
+    }
+
+    return false;
+  },
+
+  handledByApp: function handledByApp(request, typesInfo) {
     if (request.principal.appId == Ci.nsIScriptSecurityManager.NO_APP_ID ||
         request.principal.appId == Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID) {
       // This should not really happen
       request.cancel();
       return true;
     }
 
     let appsService = Cc["@mozilla.org/AppsService;1"]
                         .getService(Ci.nsIAppsService);
     let app = appsService.getAppByLocalId(request.principal.appId);
 
-    let url = Services.io.newURI(app.origin, null, null);
-    let principal = secMan.getAppCodebasePrincipal(url, request.principal.appId,
-                                                   /*mozbrowser*/false);
-    let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
-                                                                   request.type;
-    let result = Services.perms.testExactPermissionFromPrincipal(principal, access);
+    // Check each permission if it's denied by permission manager with app's
+    // URL.
+    let notDenyAppPrincipal = function(type) {
+      let url = Services.io.newURI(app.origin, null, null);
+      let principal = secMan.getAppCodebasePrincipal(url,
+                                                     request.principal.appId,
+                                                     /*mozbrowser*/false);
+      let result = Services.perms.testExactPermissionFromPrincipal(principal,
+                                                                   type.access);
 
-    if (result == Ci.nsIPermissionManager.ALLOW_ACTION ||
-        result == Ci.nsIPermissionManager.PROMPT_ACTION) {
-      return false;
+      if (result == Ci.nsIPermissionManager.ALLOW_ACTION ||
+          result == Ci.nsIPermissionManager.PROMPT_ACTION) {
+        type.deny = false;
+      }
+      return !type.deny;
+    }
+    if (typesInfo.filter(notDenyAppPrincipal).length === 0) {
+      request.cancel();
+      return true;
     }
 
-    request.cancel();
-    return true;
+    return false;
   },
 
-  handledByPermissionType: function handledByPermissionType(request) {
-    return permissionSpecificChecker.hasOwnProperty(request.type)
-             ? permissionSpecificChecker[request.type](request)
-             : false;
+  handledByPermissionType: function handledByPermissionType(request, typesInfo) {
+    for (let i in typesInfo) {
+      if (permissionSpecificChecker.hasOwnProperty(typesInfo[i].permission) &&
+          permissionSpecificChecker[typesInfo[i].permission](request)) {
+        return true;
+      }
+    }
+
+    return false;
   },
 
   _id: 0,
   prompt: function(request) {
     if (secMan.isSystemPrincipal(request.principal)) {
       request.allow();
-      return true;
+      return;
     }
 
-    if (this.handledByApp(request) ||
-        this.handledByPermissionType(request)) {
+    // Initialize the typesInfo and set the default value.
+    let typesInfo = [];
+    let perms = request.types.QueryInterface(Ci.nsIArray);
+    for (let idx = 0; idx < perms.length; idx++) {
+      let perm = perms.queryElementAt(idx, Ci.nsIContentPermissionType);
+      let tmp = {
+        permission: perm.type,
+        access: (perm.access && perm.access !== "unused") ?
+                  perm.type + "-" + perm.access : perm.type,
+        deny: true,
+        action: Ci.nsIPermissionManager.UNKNOWN_ACTION
+      };
+      typesInfo.push(tmp);
+    }
+    if (typesInfo.length == 0) {
+      request.cancel();
+      return;
+    }
+
+    if(!this.checkMultipleRequest(typesInfo)) {
+      request.cancel();
+      return;
+    }
+
+    if (this.handledByApp(request, typesInfo) ||
+        this.handledByPermissionType(request, typesInfo)) {
       return;
     }
 
     // returns true if the request was handled
-    if (this.handleExistingPermission(request))
+    if (this.handleExistingPermission(request, typesInfo)) {
        return;
+    }
+
+    // prompt PROMPT_ACTION request only.
+    typesInfo.forEach(function(aType, aIndex) {
+      if (aType.action != Ci.nsIPermissionManager.PROMPT_ACTION || aType.deny) {
+        typesInfo.splice(aIndex);
+      }
+    });
 
     let frame = request.element;
     let requestId = this._id++;
 
     if (!frame) {
-      this.delegatePrompt(request, requestId);
+      this.delegatePrompt(request, requestId, typesInfo);
       return;
     }
 
     frame = frame.wrappedJSObject;
     var cancelRequest = function() {
       frame.removeEventListener("mozbrowservisibilitychange", onVisibilityChange);
       request.cancel();
     }
 
     var self = this;
     var onVisibilityChange = function(evt) {
       if (evt.detail.visible === true)
         return;
 
-      self.cancelPrompt(request, requestId);
+      self.cancelPrompt(request, requestId, typesInfo);
       cancelRequest();
     }
 
     // If the request was initiated from a hidden iframe
     // we don't forward it to content and cancel it right away
     let domRequest = frame.getVisible();
     domRequest.onsuccess = function gv_success(evt) {
       if (!evt.target.result) {
         cancelRequest();
         return;
       }
 
       // Monitor the frame visibility and cancel the request if the frame goes
       // away but the request is still here.
       frame.addEventListener("mozbrowservisibilitychange", onVisibilityChange);
 
-      self.delegatePrompt(request, requestId, function onCallback() {
+      self.delegatePrompt(request, requestId, typesInfo, function onCallback() {
         frame.removeEventListener("mozbrowservisibilitychange", onVisibilityChange);
       });
     };
 
     // Something went wrong. Let's cancel the request just in case.
     domRequest.onerror = function gv_error() {
       cancelRequest();
     }
   },
 
-  cancelPrompt: function(request, requestId) {
-    this.sendToBrowserWindow("cancel-permission-prompt", request, requestId);
+  cancelPrompt: function(request, requestId, typesInfo) {
+    this.sendToBrowserWindow("cancel-permission-prompt", request, requestId,
+                             typesInfo);
   },
 
-  delegatePrompt: function(request, requestId, callback) {
-    let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
-                                                                   request.type;
-    let principal = request.principal;
+  delegatePrompt: function(request, requestId, typesInfo, callback) {
 
-    this._permission = access;
-    this._uri = principal.URI.spec;
-    this._origin = principal.origin;
-
-    this.sendToBrowserWindow("permission-prompt", request, requestId, function(type, remember) {
+    this.sendToBrowserWindow("permission-prompt", request, requestId, typesInfo,
+                             function(type, remember) {
       if (type == "permission-allow") {
-        rememberPermission(request.type, principal, !remember);
+        rememberPermission(typesInfo, request.principal, !remember);
         if (callback) {
           callback();
         }
         request.allow();
         return;
       }
 
-      if (remember) {
-        Services.perms.addFromPrincipal(principal, access,
-                                        Ci.nsIPermissionManager.DENY_ACTION);
-      } else {
-        Services.perms.addFromPrincipal(principal, access,
-                                        Ci.nsIPermissionManager.DENY_ACTION,
-                                        Ci.nsIPermissionManager.EXPIRE_SESSION, 0);
+      let addDenyPermission = function(type) {
+        debug("add " + type.permission +
+              " to permission manager with DENY_ACTION");
+        if (remember) {
+          Services.perms.addFromPrincipal(request.principal, type.access,
+                                          Ci.nsIPermissionManager.DENY_ACTION);
+        } else if (PERMISSION_NO_SESSION.indexOf(aPerm) < 0) {
+          Services.perms.addFromPrincipal(request.principal, type.access,
+                                          Ci.nsIPermissionManager.DENY_ACTION,
+                                          Ci.nsIPermissionManager.EXPIRE_SESSION,
+                                          0);
+        }
       }
+      typesInfo.forEach(addDenyPermission);
 
       if (callback) {
         callback();
       }
       request.cancel();
     });
   },
 
-  sendToBrowserWindow: function(type, request, requestId, callback) {
+  sendToBrowserWindow: function(type, request, requestId, typesInfo, callback) {
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     let content = browser.getContentWindow();
     if (!content)
       return;
 
     if (callback) {
       content.addEventListener("mozContentEvent", function contentEvent(evt) {
         let detail = evt.detail;
@@ -248,20 +359,25 @@ ContentPermissionPrompt.prototype = {
     }
 
     let principal = request.principal;
     let isApp = principal.appStatus != Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED;
     let remember = (principal.appStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
                     principal.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED)
                     ? true
                     : request.remember;
+    let permissions = {};
+    for (let i in typesInfo) {
+      debug("prompt " + typesInfo[i].permission);
+      permissions[typesInfo[i].permission] = [];
+    }
 
     let details = {
       type: type,
-      permission: request.type,
+      permissions: permissions,
       id: requestId,
       origin: principal.origin,
       isApp: isApp,
       remember: remember
     };
 
     if (!isApp) {
       browser.shell.sendChromeEvent(details);
@@ -284,11 +400,10 @@ ContentPermissionPrompt.prototype = {
       request.cancel();
       return true;
     } else {
       return false;
     }
   };
 })();
 
-
 //module initialization
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]);
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "961def304c067c112f9f3c149431dba70887c5e0", 
+    "revision": "7343851c97c278a338434d2632098a263e19bbb6", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -51,12 +51,14 @@ MOZ_EXTENSION_MANAGER=1
 MOZ_TIME_MANAGER=1
 
 MOZ_B2G_CERTDATA=1
 MOZ_PAY=1
 MOZ_TOOLKIT_SEARCH=
 MOZ_PLACES=
 MOZ_B2G=1
 
-#MOZ_NUWA_PROCESS=1
+if test "$OS_TARGET" = "Android"; then
+MOZ_NUWA_PROCESS=1
+fi
 MOZ_FOLD_LIBS=1
 
 MOZ_JSDOWNLOADS=1
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2025,60 +2025,68 @@ ContentPermissionPrompt.prototype = {
     }
 
     this._showPrompt(aRequest, message, "pointerLock", actions, "pointerLock",
                      "pointerLock-notification-icon", null);
   },
 
   prompt: function CPP_prompt(request) {
 
+    // Only allow exactly one permission rquest here.
+    let types = request.types.QueryInterface(Ci.nsIArray);
+    if (types.length != 1) {
+      request.cancel();
+      return;
+    }
+    let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
+
     const kFeatureKeys = { "geolocation" : "geo",
                            "desktop-notification" : "desktop-notification",
                            "pointerLock" : "pointerLock",
                          };
 
     // Make sure that we support the request.
-    if (!(request.type in kFeatureKeys)) {
+    if (!(perm.type in kFeatureKeys)) {
         return;
     }
 
     var requestingPrincipal = request.principal;
     var requestingURI = requestingPrincipal.URI;
 
     // Ignore requests from non-nsIStandardURLs
     if (!(requestingURI instanceof Ci.nsIStandardURL))
       return;
 
     var autoAllow = false;
-    var permissionKey = kFeatureKeys[request.type];
+    var permissionKey = kFeatureKeys[perm.type];
     var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey);
 
     if (result == Ci.nsIPermissionManager.DENY_ACTION) {
       request.cancel();
       return;
     }
 
     if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
       autoAllow = true;
       // For pointerLock, we still want to show a warning prompt.
-      if (request.type != "pointerLock") {
+      if (perm.type != "pointerLock") {
         request.allow();
         return;
       }
     }
 
     var browser = this._getBrowserForRequest(request);
     var chromeWin = browser.ownerDocument.defaultView;
     if (!chromeWin.PopupNotifications)
       // Ignore requests from browsers hosted in windows that don't support
       // PopupNotifications.
       return;
 
     // Show the prompt.
-    switch (request.type) {
+    switch (perm.type) {
     case "geolocation":
       this._promptGeo(request);
       break;
     case "desktop-notification":
       this._promptWebNotifications(request);
       break;
     case "pointerLock":
       this._promptPointerLock(request, autoAllow);
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -66,16 +66,17 @@ const COMPAT = {
   // The indent of a console group in pixels.
   GROUP_INDENT: 12,
 };
 
 // A map from the console API call levels to the Web Console severities.
 const CONSOLE_API_LEVELS_TO_SEVERITIES = {
   error: "error",
   exception: "error",
+  assert: "error",
   warn: "warning",
   info: "info",
   log: "log",
   trace: "log",
   debug: "log",
   dir: "log",
   group: "log",
   groupCollapsed: "log",
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -55,16 +55,17 @@ support-files =
   test-bug-821877-csperrors.html^headers^
   test-bug-837351-security-errors.html
   test-bug-846918-hsts-invalid-headers.html
   test-bug-846918-hsts-invalid-headers.html^headers^
   test-bug-859170-longstring-hang.html
   test-bug-869003-iframe.html
   test-bug-869003-top-window.html
   test-closures.html
+  test-console-assert.html
   test-console-extras.html
   test-console-replaced-api.html
   test-console.html
   test-consoleiframes.html
   test-data.json
   test-data.json^headers^
   test-duplicate-error.html
   test-encoding-ISO-8859-1.html
@@ -130,16 +131,17 @@ support-files =
 [browser_netpanel_longstring_expand.js]
 [browser_output_breaks_after_console_dir_uninspectable.js]
 [browser_output_longstring_expand.js]
 [browser_repeated_messages_accuracy.js]
 [browser_result_format_as_string.js]
 [browser_warn_user_about_replaced_api.js]
 [browser_webconsole_abbreviate_source_url.js]
 [browser_webconsole_allow_mixedcontent_securityerrors.js]
+[browser_webconsole_assert.js]
 [browser_webconsole_basic_net_logging.js]
 [browser_webconsole_block_mixedcontent_securityerrors.js]
 [browser_webconsole_bug_579412_input_focus.js]
 [browser_webconsole_bug_580001_closing_after_completion.js]
 [browser_webconsole_bug_580030_errors_after_page_reload.js]
 [browser_webconsole_bug_580454_timestamp_l10n.js]
 [browser_webconsole_bug_582201_duplicate_errors.js]
 [browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js]
@@ -236,8 +238,9 @@ support-files =
 [browser_webconsole_property_provider.js]
 [browser_webconsole_scratchpad_panel_link.js]
 [browser_webconsole_split.js]
 [browser_webconsole_view_source.js]
 [browser_webconsole_reflow.js]
 [browser_webconsole_log_file_filter.js]
 [browser_webconsole_expandable_timestamps.js]
 [browser_webconsole_autocomplete_in_debugger_stackframe.js]
+[browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_assert.js
@@ -0,0 +1,50 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that console.assert() works as expected (i.e. outputs only on falsy
+// asserts). See bug 760193.
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-assert.html";
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
+}
+
+function consoleOpened(hud) {
+  waitForMessages({
+    webconsole: hud,
+    messages: [{
+      text: "start",
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_LOG,
+    },
+    {
+      text: "false assert",
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_ERROR,
+    },
+    {
+      text: "falsy assert",
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_ERROR,
+    },
+    {
+      text: "end",
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_LOG,
+    }],
+  }).then(() => {
+    let nodes = hud.outputNode.querySelectorAll(".message");
+    is(nodes.length, 4, "only four messages are displayed, no output from the true assert");
+    finishTest();
+  });
+
+  let button = content.document.querySelector("button");
+  ok(button, "we have the button");
+  EventUtils.sendMouseEvent({ type: "click" }, button, content);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js
@@ -0,0 +1,42 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+// Test that the autocomplete popup closes on switching tabs. See bug 900448.
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>bug 900448 - autocomplete popup closes on tab switch";
+
+let popup = null;
+
+registerCleanupFunction(function() {
+  popup = null;
+});
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
+}
+
+function consoleOpened(HUD) {
+  popup = HUD.jsterm.autocompletePopup;
+
+  popup._panel.addEventListener("popupshown", function popupOpened() {
+    popup._panel.removeEventListener("popupshown", popupOpened, false);
+    addTab("data:text/html;charset=utf-8,<p>testing autocomplete closes");
+    gBrowser.selectedBrowser.addEventListener("load", tab2Loaded, true);
+  }, false);
+
+  HUD.jsterm.setInputValue("sc");
+  EventUtils.synthesizeKey("r", {});
+}
+
+function tab2Loaded() {
+  gBrowser.selectedBrowser.removeEventListener("load", tab2Loaded, true);
+  ok(!popup.isOpen, "Popup closes on tab switch");
+  gBrowser.removeCurrentTab();
+  finishTest();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-console-assert.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+  <head>
+    <!--
+      Any copyright is dedicated to the Public Domain.
+      http://creativecommons.org/publicdomain/zero/1.0/
+    -->
+    <meta charset="utf-8">
+    <title>console.assert() test</title>
+    <script type="text/javascript">
+      function test() {
+        console.log("start");
+        console.assert(false, "false assert");
+        console.assert(0, "falsy assert");
+        console.assert(true, "true assert");
+        console.log("end");
+      }
+    </script>
+  </head>
+  <body>
+    <p>test console.assert()</p>
+    <button onclick="test();">test console.assert()</button>
+  </body>
+</html>
--- a/browser/devtools/webconsole/test/test-console-extras.html
+++ b/browser/devtools/webconsole/test/test-console-extras.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html dir="ltr" xml:lang="en-US" lang="en-US"><head>
     <meta charset="utf-8">
     <title>Console extended API test</title>
     <script type="text/javascript">
       function test() {
         console.log("start");
-        console.assert()
         console.clear()
         console.dirxml()
         console.profile()
         console.profileEnd()
         console.count()
         console.table()
         console.log("end");
       }
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -113,16 +113,17 @@ const MESSAGE_PREFERENCE_KEYS = [
   [ "secerror",   "secwarn",    null,     null,          ],  // Security
 ];
 
 // A mapping from the console API log event levels to the Web Console
 // severities.
 const LEVELS = {
   error: SEVERITY_ERROR,
   exception: SEVERITY_ERROR,
+  assert: SEVERITY_ERROR,
   warn: SEVERITY_WARNING,
   info: SEVERITY_INFO,
   log: SEVERITY_LOG,
   trace: SEVERITY_LOG,
   debug: SEVERITY_LOG,
   dir: SEVERITY_LOG,
   group: SEVERITY_LOG,
   groupCollapsed: SEVERITY_LOG,
@@ -740,17 +741,17 @@ WebConsoleFrame.prototype = {
     }
     this._updateCharSize();
   },
 
   /**
    * Calculates the width and height of a single character of the input box.
    * This will be used in opening the popup at the correct offset.
    *
-   * @private 
+   * @private
    */
   _updateCharSize: function WCF__updateCharSize()
   {
     let doc = this.document;
     let tempLabel = doc.createElementNS(XHTML_NS, "span");
     let style = tempLabel.style;
     style.position = "fixed";
     style.padding = "0";
@@ -1166,16 +1167,17 @@ WebConsoleFrame.prototype = {
     });
 
     switch (level) {
       case "log":
       case "info":
       case "warn":
       case "error":
       case "exception":
+      case "assert":
       case "debug": {
         let msg = new Messages.ConsoleGeneric(aMessage);
         node = msg.init(this.output).render().element;
         break;
       }
       case "dir": {
         body = { arguments: args };
         let clipboardArray = [];
@@ -2907,16 +2909,17 @@ function JSTerm(aWebConsoleFrame)
   // This is reset to this.history.length when this.execute() is invoked.
   this.historyPlaceHolder = 0;
   this._objectActorsInVariablesViews = new Map();
 
   this._keyPress = this._keyPress.bind(this);
   this._inputEventHandler = this._inputEventHandler.bind(this);
   this._focusEventHandler = this._focusEventHandler.bind(this);
   this._onKeypressInVariablesView = this._onKeypressInVariablesView.bind(this);
+  this._blurEventHandler = this._blurEventHandler.bind(this);
 
   EventEmitter.decorate(this);
 }
 
 JSTerm.prototype = {
   SELECTED_FRAME: -1,
 
   /**
@@ -3028,37 +3031,37 @@ JSTerm.prototype = {
   COMPLETE_BACKWARD: 1,
   COMPLETE_HINT_ONLY: 2,
 
   /**
    * Initialize the JSTerminal UI.
    */
   init: function JST_init()
   {
-    let chromeDocument = this.hud.owner.chromeWindow.document;
     let autocompleteOptions = {
       onSelect: this.onAutocompleteSelect.bind(this),
       onClick: this.acceptProposedCompletion.bind(this),
       panelId: "webConsole_autocompletePopup",
       listBoxId: "webConsole_autocompletePopupListBox",
       position: "before_start",
       theme: "auto",
       direction: "ltr",
       autoSelect: true
     };
-    this.autocompletePopup = new AutocompletePopup(chromeDocument,
+    this.autocompletePopup = new AutocompletePopup(this.hud.document,
                                                    autocompleteOptions);
 
     let doc = this.hud.document;
     this.completeNode = doc.querySelector(".jsterm-complete-node");
     this.inputNode = doc.querySelector(".jsterm-input-node");
     this.inputNode.addEventListener("keypress", this._keyPress, false);
     this.inputNode.addEventListener("input", this._inputEventHandler, false);
     this.inputNode.addEventListener("keyup", this._inputEventHandler, false);
     this.inputNode.addEventListener("focus", this._focusEventHandler, false);
+    this.hud.window.addEventListener("blur", this._blurEventHandler, false);
 
     this.lastInputValue && this.setInputValue(this.lastInputValue);
   },
 
   /**
    * The JavaScript evaluation response handler.
    *
    * @private
@@ -3738,16 +3741,27 @@ JSTerm.prototype = {
       this.resizeInput();
       this.complete(this.COMPLETE_HINT_ONLY);
       this.lastInputValue = this.inputNode.value;
       this._inputChanged = true;
     }
   },
 
   /**
+   * The window "blur" event handler.
+   * @private
+   */
+  _blurEventHandler: function JST__blurEventHandler()
+  {
+    if (this.autocompletePopup) {
+      this.clearCompletion();
+    }
+  },
+
+  /**
    * The inputNode "keypress" event handler.
    *
    * @private
    * @param nsIDOMEvent aEvent
    */
   _keyPress: function JST__keyPress(aEvent)
   {
     let inputNode = this.inputNode;
@@ -4368,16 +4382,17 @@ JSTerm.prototype = {
     if (popup) {
       popup.parentNode.removeChild(popup);
     }
 
     this.inputNode.removeEventListener("keypress", this._keyPress, false);
     this.inputNode.removeEventListener("input", this._inputEventHandler, false);
     this.inputNode.removeEventListener("keyup", this._inputEventHandler, false);
     this.inputNode.removeEventListener("focus", this._focusEventHandler, false);
+    this.hud.window.removeEventListener("blur", this._blurEventHandler, false);
 
     this.hud = null;
   },
 };
 
 /**
  * Utils: a collection of globally used functions.
  */
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -80,16 +80,17 @@ var BrowserUI = {
 
   init: function() {
     // start the debugger now so we can use it on the startup code as well
     if (Services.prefs.getBoolPref(debugServerStateChanged)) {
       this.runDebugServer();
     }
     Services.prefs.addObserver(debugServerStateChanged, this, false);
     Services.prefs.addObserver(debugServerPortChanged, this, false);
+    Services.prefs.addObserver("app.crashreporter.autosubmit", this, false);
 
     Services.obs.addObserver(this, "handle-xul-text-link", false);
 
     // listen content messages
     messageManager.addMessageListener("DOMTitleChanged", this);
     messageManager.addMessageListener("DOMWillOpenModalDialog", this);
     messageManager.addMessageListener("DOMWindowClose", this);
 
@@ -259,69 +260,21 @@ var BrowserUI = {
     return Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime).lastRunCrashID;
   },
 
   startupCrashCheck: function startupCrashCheck() {
 #ifdef MOZ_CRASHREPORTER
     if (!CrashReporter.enabled) {
       return;
     }
-    let lastCrashID = this.lastCrashID;
-
-    if (!lastCrashID || !lastCrashID.length) {
-      return;
-    }
-
-    let shouldReport = Services.prefs.getBoolPref("app.crashreporter.autosubmit");
-    let didPrompt = Services.prefs.getBoolPref("app.crashreporter.prompted");
-
-    if (!shouldReport && !didPrompt) {
-      let crashBundle = Services.strings.createBundle("chrome://browser/locale/crashprompt.properties");
-      let title = crashBundle.GetStringFromName("crashprompt.dialog.title");
-      let acceptbutton = crashBundle.GetStringFromName("crashprompt.dialog.acceptbutton");
-      let refusebutton = crashBundle.GetStringFromName("crashprompt.dialog.refusebutton");
-      let bodyText = crashBundle.GetStringFromName("crashprompt.dialog.statement1");
 
-      let buttonPressed =
-            Services.prompt.confirmEx(
-                null,
-                title,
-                bodyText,
-                Ci.nsIPrompt.BUTTON_POS_0 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING
-              + Ci.nsIPrompt.BUTTON_POS_1 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING
-              + Ci.nsIPrompt.BUTTON_POS_1_DEFAULT,
-                acceptbutton,
-                refusebutton,
-                null,
-                null,
-                { value: false });
-
-      Services.prefs.setBoolPref("app.crashreporter.prompted", true);
+    // Ensure that CrashReporter state matches pref
+    CrashReporter.submitReports = Services.prefs.getBoolPref("app.crashreporter.autosubmit");
 
-      if (buttonPressed == 0) {
-        Services.prefs.setBoolPref('app.crashreporter.autosubmit', true);
-        BrowserUI.crashReportingPrefChanged(true);
-        shouldReport = true;
-      } else {
-        Services.prefs.setBoolPref('app.crashreporter.autosubmit', false);
-        BrowserUI.crashReportingPrefChanged(false);
-      }
-    }
-
-    // We've already prompted, return if the user doesn't want to report.
-    if (!shouldReport) {
-      return;
-    }
-
-    Util.dumpLn("Submitting last crash id:", lastCrashID);
-    try {
-      this.CrashSubmit.submit(lastCrashID);
-    } catch (ex) {
-      Util.dumpLn(ex);
-    }
+    BrowserUI.submitLastCrashReportOrShowPrompt();
 #endif
   },
 
 
   /*********************************
    * Navigation
    */
 
@@ -627,21 +580,58 @@ var BrowserUI = {
               this.runDebugServer();
             } else {
               this.stopDebugServer();
             }
             break;
           case debugServerPortChanged:
             this.changeDebugPort(Services.prefs.getIntPref(aData));
             break;
+          case "app.crashreporter.autosubmit":
+#ifdef MOZ_CRASHREPORTER
+            CrashReporter.submitReports = Services.prefs.getBoolPref(aData);
+
+            // The user explicitly set the autosubmit option, so there is no
+            // need to prompt them about crash reporting in the future
+            Services.prefs.setBoolPref("app.crashreporter.prompted", true);
+
+            BrowserUI.submitLastCrashReportOrShowPrompt;
+#endif
+            break;
+
+
         }
         break;
     }
   },
 
+  submitLastCrashReportOrShowPrompt: function() {
+#ifdef MOZ_CRASHREPORTER
+    let lastCrashID = this.lastCrashID;
+    if (lastCrashID && lastCrashID.length) {
+      if (Services.prefs.getBoolPref("app.crashreporter.autosubmit")) {
+        Util.dumpLn("Submitting last crash id:", lastCrashID);
+        let params = {};
+        if (!Services.prefs.getBoolPref("app.crashreporter.submitURLs")) {
+          params['extraExtraKeyVals'] = { URL: '' };
+        }
+        try {
+          this.CrashSubmit.submit(lastCrashID, params);
+        } catch (ex) {
+          Util.dumpLn(ex);
+        }
+      } else if (!Services.prefs.getBoolPref("app.crashreporter.prompted")) {
+        BrowserUI.addAndShowTab("about:crashprompt", null);
+      }
+    }
+#endif
+  },
+
+
+
   /*********************************
    * Internal utils
    */
 
   _titleChanged: function(aBrowser) {
     let url = this.getDisplayURI(aBrowser);
 
     let tabCaption;
@@ -1103,20 +1093,16 @@ var BrowserUI = {
                           null,
                           { value: false });
 
     // Clicking 'Clear' will call onSanitize().
     if (buttonPressed === 0) {
       SanitizeUI.onSanitize();
     }
   },
-
-  crashReportingPrefChanged: function crashReportingPrefChanged(aState) {
-    CrashReporter.submitReports = aState;
-  }
 };
 
 var PanelUI = {
   get _panels() { return document.getElementById("panel-items"); },
 
   get isVisible() {
     return !Elements.panelUI.hidden;
   },
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -667,17 +667,23 @@ Desktop browser's sync prefs.
               <cssthrobber id="clearprivacythrobber"/>
             </hbox>
             <description id="clear-notification-done">&clearPrivateData.done;</description>
           </deck>
         </hbox>
       </settings>
       <setting pref="signon.rememberSignons" title="&optionsHeader.privacy.passwords.label;" type="bool"/>
       <settings id="prefs-reporting" label="&optionsHeader.reporting.title;">
-        <setting pref="app.crashreporter.autosubmit" type="bool" title="&optionsHeader.reporting.crashes.label;" oncommand="BrowserUI.crashReportingPrefChanged(this.value);"/>
+        <setting pref="app.crashreporter.autosubmit"
+                 type="bool"
+                 title="&optionsHeader.reporting.crashes.label;" />
+        <setting pref="app.crashreporter.submitURLs"
+                 id="prefs-reporting-submitURLs"
+                 type="bool"
+                 title="&optionsHeader.reporting.crashes.submitURLs;" />
       </settings>
       <settings id="prefs-telemetry" label="&optionsHeader.telemetry.title;">
         <setting pref="toolkit.telemetry.enabled" type="bool" title="&optionsHeader.telemetry.label;"/>
       </settings>
       <settings id="prefs-dnt" label="&doNotTrack.title;">
         <setting id="prefs-dnt-value" pref="privacy.donottrackheader.value" type="radio" >
           <radiogroup id="prefs-dnt-options">
             <radio id="prefs-dnt-notrack" class="flyoutpanel-hack"
--- a/browser/metro/base/content/flyoutpanels/PrefsFlyoutPanel.js
+++ b/browser/metro/base/content/flyoutpanels/PrefsFlyoutPanel.js
@@ -39,16 +39,22 @@ let PrefsFlyoutPanel = {
       this._hasShown = true;
 
       Services.prefs.addObserver("privacy.donottrackheader.value",
                                  this._prefObserver,
                                  false);
       Services.prefs.addObserver("privacy.donottrackheader.enabled",
                                  this._prefObserver,
                                  false);
+      Services.prefs.addObserver("app.crashreporter.autosubmit",
+                                 this._prefObserver,
+                                 false);
+      Services.prefs.addObserver("app.crashreporter.submitURLs",
+                                 this._prefObserver,
+                                 false);
     }
 
     this._topmostElement.show();
   },
 
   _prefObserver: function(subject, topic, data) {
     let value = -1;
     try {
@@ -76,11 +82,34 @@ let PrefsFlyoutPanel = {
 
       case "privacy.donottrackheader.enabled":
         // If someone or something modifies this pref, we should update the
         // other pref as well so our UI doesn't give false information
         if (((1 == value) || (0 == value)) && !isEnabled) {
           Services.prefs.setIntPref('privacy.donottrackheader.value', -1);
         }
         break;
+
+      case "app.crashreporter.autosubmit":
+        let autosubmit = Services.prefs.getBoolPref("app.crashreporter.autosubmit");
+        if (!autosubmit) {
+          // If the user has selected not to submit crash reports, the UI
+          // should reflect also that URLs will not be included.
+          // TODO: Ideally we would grey out the text and the toggle for
+          // the "include URLs" pref, but the |setting| binding doesn't
+          // appear to support enabling/disabling. In the meantime, we just
+          // set the "include URLs" pref to false if the "send crash reports"
+          // pref has been set to false.
+          Services.prefs.setBoolPref('app.crashreporter.submitURLs', false);
+        }
+        break;
+
+      case "app.crashreporter.submitURLs":
+        let submitURLs = Services.prefs.getBoolPref("app.crashreporter.submitURLs");
+        if (submitURLs) {
+          // If the user has selected to submit URLs, they are implicitly also
+          // selecting to submit crash reports. Let's update the autosubmit pref
+          Services.prefs.setBoolPref('app.crashreporter.autosubmit', true);
+        }
+      break;
     }
   },
 };
--- a/browser/metro/base/content/helperui/IndexedDB.js
+++ b/browser/metro/base/content/helperui/IndexedDB.js
@@ -39,33 +39,35 @@ let IndexedDB = {
     } else if (topic == this._quotaCancel) {
       payload.permission = Ci.nsIPermissionManager.UNKNOWN_ACTION;
       browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload);
       // XXX Need to actually save this?
       return;
     }
 
     let prompt = Cc["@mozilla.org/content-permission/prompt;1"].createInstance(Ci.nsIContentPermissionPrompt);
+    let types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+    types.appendElement({type: type, access: "unused"}, false);
 
     // If the user waits a long time before responding, we default to UNKNOWN_ACTION.
     let timeoutId = setTimeout(function() {
       payload.permission = Ci.nsIPermissionManager.UNKNOWN_ACTION;
       browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload);
       timeoutId = null;
     }, 30000);
  
     function checkTimeout() {
       if (timeoutId === null) return true;
       clearTimeout(timeoutId);
       timeoutId = null;
       return false;
     }
 
     prompt.prompt({
-      type: type,
+      types: types,
       uri: Services.io.newURI(payload.location, null, null),
       window: null,
       element: aMessage.target,
 
       cancel: function() {
         if (checkTimeout()) return;
         payload.permission = Ci.nsIPermissionManager.DENY_ACTION;
         browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload);
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/content/pages/crashprompt.xhtml
@@ -0,0 +1,392 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html [
+  <!ENTITY % htmlDTD
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "DTD/xhtml1-strict.dtd">
+  %htmlDTD;
+  <!ENTITY % globalDTD
+    SYSTEM "chrome://global/locale/global.dtd">
+  %globalDTD;
+  <!ENTITY % brandDTD
+    SYSTEM "chrome://branding/locale/brand.dtd">
+  %brandDTD;
+  <!ENTITY % crashPromptDTD
+    SYSTEM "chrome://browser/locale/crashprompt.dtd">
+  %crashPromptDTD;
+]>
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <base href="http://www.mozilla.org/en-US/legal/privacy/firefox.html"></base>
+    <meta name="viewport" content="width=device-width; user-scalable=false;" />
+    <title>&crashprompt.title;</title>
+    <link rel="stylesheet" href="chrome://browser/skin/platform.css" type="text/css" media="all" />
+    <link rel="stylesheet" href="chrome://browser/skin/browser.css" type="text/css" media="all" />
+    <link rel="stylesheet" href="chrome://browser/content/browser.css" type="text/css" media="all" />
+    <link rel="stylesheet" href="chrome://browser/skin/crashprompt.css" type="text/css" media="all" />
+    <!--TODO: Do we need a favicon?-->
+
+    <script type="application/javascript"><![CDATA[
+      Components.utils.import("resource://gre/modules/Services.jsm");
+
+      function toggleDetailsVisible() {
+        var arrow = document.getElementById("detailsArrow");
+        detailsArrow.classList.toggle('hidden');
+
+        var arrowExpanded = document.getElementById("detailsArrowExpanded");
+        detailsArrowExpanded.classList.toggle('hidden');
+
+        var details = document.getElementById("detailsContainer");
+        details.classList.toggle('hidden');
+      }
+
+      function togglePrivacyDetails() {
+        var privacyDetails = document.getElementById("lightboxBackdrop");
+        privacyDetails.classList.toggle('hidden');
+      }
+
+      function onChoseSendReports() {
+        Services.prefs.setBoolPref("app.crashreporter.prompted", true);
+        Services.prefs.setBoolPref("app.crashreporter.autosubmit", true);
+
+        var includeURLCheckbox = document.getElementById("crashpromptIncludeURLCheckbox");
+        if (includeURLCheckbox.checked) {
+          Services.prefs.setBoolPref("app.crashreporter.submitURLs", true);
+        } else {
+          Services.prefs.setBoolPref("app.crashreporter.submitURLs", false);
+        }
+
+        // TODO: How can we access BrowserUI from here?
+        // BrowserUI.startupCrashCheck();
+        window.close();
+      }
+
+      function onChoseDoNotSend() {
+        Services.prefs.setBoolPref("app.crashreporter.prompted", true);
+        Services.prefs.setBoolPref("app.crashreporter.autosubmit", false);
+        window.close();
+      }
+
+
+      function onLoad() {
+        document.getElementById("crashpromptIncludeURLCheckbox").checked =
+          Services.prefs.getBoolPref("app.crashreporter.submitURLs");
+      }
+    ]]></script>
+
+  </head>
+
+  <body id="crashPromptPage" dir="&locale.dir;" onload="onLoad();">
+    <div id="crashPromptContainer">
+      <h1 id="crashPromptTitle">&crashprompt.title;</h1>
+      <hr id="crashPromptSeparator" />
+      <p>&crashprompt.message;</p>
+      <p id="detailsGrid">
+        <a href="javascript:toggleDetailsVisible();"
+           id="detailsLink">
+          <img id="detailsArrow"
+               src="chrome://browser/skin/images/arrowbox-horiz-blue-filled.png"
+               class="detailsSpacer" />
+          <img id="detailsArrowExpanded"
+               src="chrome://browser/skin/images/arrowbox-down-blue-filled.png"
+               class="hidden detailsSpacer" />
+          <label id="detailsLinkLabel">&crashprompt.detailsLink;</label>
+        </a>
+        <div id="detailsContainer"
+             class="hidden">
+          <div id="detailsSpacer" class="detailsSpacer" />
+          <label id="crashpromptDetailedMessage">
+            &crashprompt.detailedMessage.firstParagraph;<br />
+            &crashprompt.detailedMessage.secondParagraph;<br />
+            &crashprompt.detailedMessage.thirdParagraph;
+            <a id="privacyPolicyLink"
+               href="javascript:togglePrivacyDetails();">
+              &crashprompt.privacyPolicyLink;
+            </a>
+          </label>
+        </div>
+      </p>
+      <div id="crashpromptURLCheckboxContainer">
+        <label id="crashpromptIncludeURLLabel">
+          <input type="checkbox"
+                 id="crashpromptIncludeURLCheckbox" />
+          &crashprompt.includeURLLabel;
+        </label>
+      </div>
+      <div id="crashPromptButtonContainer">
+        <button id="sendReportsButton"
+                class="button-default"
+                onclick="onChoseSendReports();">
+          &crashprompt.acceptbutton;
+        </button>
+        <button id="refuseButton"
+                onclick="onChoseDoNotSend();">
+          &crashprompt.refusebutton;
+        </button>
+      </div>
+    </div>
+
+    <div id="lightboxBackdrop" class="hidden" onclick="javascript:togglePrivacyDetails();">
+      <div id="lightbox" onclick="event.stopPropagation();">
+        <div id="lightboxCloseButton"
+             onclick="togglePrivacyDetails();" />
+        <h2 id="privacyPolicyTitle">&crashprompt.privacyPolicy.title;</h2>
+        <div id="privacyPolicyBody"  onclick="event.stopPropagation();">
+          <div id="content">
+            <p>
+              This privacy policy explains how Mozilla Corporation (“Mozilla”), a wholly-owned subsidiary of the non-profit Mozilla Foundation, collects and uses information about users of the official Mozilla Firefox® web browser (“Firefox”). It does not apply to other Mozilla websites, products, or services.
+            </p>
+
+            <h3>Overview</h3>
+
+            <p>In this privacy policy, we address the following:</p>
+            <ul>
+              <li>Definitions of the types of information</li>
+              <li>What Firefox Sends to Websites</li>
+              <li>Feature-by-Feature Description of Data Practices</li>
+              <li>What Mozilla Does to Secure Data</li>
+              <li>Government and Court Demands for Information</li>
+              <li>Overview of Other Situations Involving Possibility of Data Disclosures</li>
+              <li>Mozilla’s Approach to Data Retention</li>
+              <li>How Mozilla Discloses Changes to this Policy</li>
+              <li>How to Contact Mozilla about this Policy</li>
+              <li>Appendix of Practices relating to Prior Versions of Firefox</li>
+            </ul>
+
+            <h3>Types of Information</h3>
+            <p><em>"Personal Information"</em> is information that you provide to us that personally identifies you, such as your name, phone number, or email address. Except as described below, Mozilla does not collect or require end-users of Firefox to provide Personal Information.</p>
+
+            <p><em>"Non-Personal Information"</em> is information that cannot be directly associated with a specific person or entity. Non-Personal Information includes but is not limited to your computer’s configuration and the version of Firefox you use.</p>
+
+            <p><em>"Potentially Personal Information"</em> is information that is Non-Personal Information in and of itself but that could be used in conjunction with other information to personally identify you. For example, Uniform Resource Locators (“URLs”) (the addresses of web pages) or Internet Protocol (“IP”) addresses (the addresses of computers on the Internet), which are Non-Personal Information in and of themselves, could be Personal Information when combined with Internet service provider (“ISP”) records.</p>
+
+            <p><em>“Aggregate Data”</em> is information that is recorded about users and collected into groups so that it no longer reflects or references an individually identifiable user.</p>
+
+            <h3>Information Firefox Sends to Websites and ISPs</h3>
+            <p>Like other web browsers, Firefox sends Non-Personal and Potentially Personal Information to the websites you visit when requested by the website. This may include, e.g. the type of browser you are using, the type of device you are using (desktop, mobile, touch screen), your language preference, the referring site, and your IP address. If you are viewing a video, the buffering functionality of Firefox may allow the server hosting the video to determine which sections of the video you have actually played. This information may be logged by the websites you visit and the Internet Service Provider you are using. What information is logged and how that information is used depends on the policies of each of the websites you visit and the ISPs you use.</p>
+
+            <p>Each website determines its own privacy practices for the distribution and use of this Non- Personal Information and Potentially Personal Information. If you are concerned about how a website will use this information, check out its privacy policy. To find out more about how Mozilla uses this information on its own websites, see the <a href="/en-US/privacy-policy.html">Mozilla Privacy Policy</a>.</p>
+
+            <h3>Cookies</h3>
+            <p>A cookie is information stored on your computer by a website you visit. Cookies often store your settings for a website, such as your preferred language or location. When you return to the site, Firefox sends back the cookies that belong to the site. This allows the site to present you with information customized to fit your needs. Cookies can store a wide range of information, including personally identifiable information (such as your name, home address, e-mail address, or telephone number). Because of their ability to store Personal Information, or references to such information, cookies can allow websites to track the online movements of particular individuals.</p>
+
+            <p>Firefox itself does not set any cookies on behalf of Mozilla.</p>
+
+            <p>By default, the activities of storing and sending cookies are invisible to you. However, you can change your Firefox settings to allow you to approve or deny cookie storage requests, delete stored cookies automatically when you close Firefox, and more. An <a href="//support.mozilla.org/en-US/kb/Cookies">article in our Firefox Knowledge Base</a> gives you information about changing these preferences.</p>
+
+            <h3>Interactive Product Features</h3>
+            <p><strong>Add-ons Features.</strong> One thing that makes Firefox so flexible is the ability for you to add various add-ons, extensions, and themes to Firefox, thereby creating a custom browser that fits your needs. The following features show how Firefox provides the ability both to obtain additional add- ons easily and to protect against potentially harmful add-ons.</p>
+
+            <dl>
+              <dt style="text-decoration: underline;">Get Add-ons Page</dt>
+              <dd>
+                <p>Firefox offers a Get Add-ons page of the Add-ons Manager that features popular add-ons and displays personalized recommendations based on the add-ons you already have installed. This page can be accessed by clicking (or tapping on a mobile device) on the “Get Add-ons” tab of the Firefox Add-ons Manager. To display the personalized recommendations, Firefox sends certain information to Mozilla, including the list of add-ons you have installed, Firefox version information, and your IP address. This communication only happens when the Get Add-ons area is open and can be turned off at any time by following <a href="http://blog.mozilla.com/addons/how-to-opt-out-of-add-on-metadata-updates/">these instructions</a>.</p>
+              </dd>
+
+              <dt style="text-decoration: underline;">Add-on Information and Searches</dt>
+              <dd>
+                <p>In order to keep the information displayed to you about your installed add-ons up to date, Firefox communicates with Mozilla once a day to update add-on descriptions, home pages, download counts, screenshots, and ratings. This communication includes the list of add-ons you have installed, Firefox version information, how long it took Firefox to start up, and your IP address. You can turn off this functionality at any time by following <a href="http://blog.mozilla.com/addons/how-to-opt-out-of-add-on-metadata-updates/">these instructions</a>.</p>
+                <p>If you enter keywords into the search field for the Add-ons Manager, those keywords will be sent to Mozilla in order to perform the search, along with Potentially Personal Information (such as IP address) normally transferred to perform such functionality.</p>
+              </dd>
+            </dl>
+
+            <p><strong>Automated Update Service.</strong> Firefox’s automatic update feature periodically checks to see if an updated version of Firefox and installed add-ons are available from Mozilla.</p>
+
+            <p>This feature sends Non-Personal Information to Mozilla, including the version of Firefox you are using, build ID and target, update channel, your language preference, and your operating system. This feature also sends Potentially Personal Information to Mozilla in the form of your IP address and a cookie that contains a unique numeric value to distinguish individual Firefox installs. Mozilla uses this information to provide you with updated versions of Firefox and to understand the usage patterns of Firefox users. We use this information to improve our products and services and to support decision making regarding feature and capacity planning.</p>
+
+            <p>Mozilla does not collect or track any Personal Information or any information about the websites you visit, and Mozilla does not release the raw information we obtain from these Firefox features to the public. We may release reports containing Aggregate Data so that our global community can make better product and design decisions. To prevent Mozilla from obtaining this information, you can turn this feature off in Firefox’s preferences. An <a href="//support.mozilla.org/en-US/kb/Firefox%20makes%20unrequested%20connections#Auto_update_checking">article in our Firefox Knowledge Base</a> gives you information about changing your preferences in non-mobile versions of Firefox.</p>
+
+            <p><strong>Blocklist Feature.</strong> Firefox also offers a Blocklist feature. With this feature, once a day Firefox does a regularly scheduled, automatic check to see if you have any harmful add-ons or plug-ins installed. If so, this feature disables add-ons or plug-ins that Mozilla has determined contain known vulnerabilities or major user-facing issues or fatal bugs (e.g., Firefox crashes on startup or something causes an endless loop). You may view the <a href="/en-US/blocklist/">current list of Blocklisted items</a>. This feature sends Non-Personal Information to Mozilla, including the version of Firefox you are using, operating system version, build ID and target, update channel, and your language preference. This feature also sends Potentially Personal Information to Mozilla in the form of your IP address and a cookie. In addition, Mozilla also uses this feature to analyze Firefox usage patterns so we may improve our products and services, including planning features and capacity. Currently there is no basic user interface to disable the Blocklist feature. An <a href="http://support.mozilla.org/en-US/kb/Firefox+makes+unrequested+connections#Extension_blocklist_updating">article in our Firefox Knowledge Base</a> explains how you may disable the Blocklist feature. Disabling the Blocklist feature is not recommended as it may result in using extensions known to be untrustworthy.</p>
+
+            <p><strong>Crash-Reporting Feature</strong> (not applicable to Firefox for mobile). Firefox has a crash-reporting feature that sends a report to Mozilla when Firefox crashes. Mozilla uses the information in the crash reports to diagnose and correct the problems in Firefox that caused the crash. Though this feature starts automatically after Firefox crashes, it does not send information to Mozilla until you explicitly authorize it to do so. By default, this feature sends a variety of Non-Personal Information to Mozilla, including the stack trace (a detailed description of which parts of the Firefox code were active at the time of the crash) and the type of computer you are using. Additional information is collected by the crash reporting feature. Which crash reporting feature is used and what additional information collected by Firefox depends on which version of Firefox you’re using. For pre-3.x versions of Firefox, please see the end of this privacy policy.</p>
+
+            <dl>
+              <dt style="text-decoration: underline;">Firefox 3.0 to present</dt>
+              <dd>
+                <p>For the current versions of Firefox, “Firefox Crash Reporter” is Firefox’s crash reporting feature. With this feature, you have the option to include Personal Information (including your email address), Potentially Personal Information (including your IP address and the URL of the site you were visiting when Firefox crashed), and a comment. Firefox Crash Reporter also sends a list of all add-ons that you were using at the time of the crash, the time since (i) the last crash, (ii) the last install, and (iii) the start-up of the program. For Firefox 3.0.0 – 3.0.5, Firefox Crash Reporter also collects Potentially Personal Information to Mozilla in the form of a unique alphanumeric value to distinguish individual Firefox installs. This value is not assigned to users of Firefox 3.0.6 and subsequent versions. Mozilla only makes Non-Personal Information (i.e., generic information about your computer, the stack trace, and any comment given by the user) available in the public reports available online at <a href="http://crash-stats.mozilla.com/">http://crash-stats.mozilla.com/</a>.</p>
+              </dd>
+            </dl>
+
+            <p><strong>Location-Aware Feature.</strong> Beginning with Firefox 3.5 and all versions of Firefox for mobile, Firefox offers a <a href="/en-US/firefox/geolocation/">Location-Aware Feature</a>, parts of which may be provided by <a href="/en-US/legal/privacy/firefox-third-party.html">third-party service providers</a>.</p>
+
+            <dl>
+              <dt style="text-decoration: underline;">You Elect to Use the Location-Aware Feature</dt>
+              <dd>
+                <p>This feature remains inoperative until you visit a website that requests your location and you choose to opt in to the feature. If you elect not to, nothing happens. Each time you visit such a website, Firefox asks you if you want it to provide the site with your current location. Additionally, you may elect to have Firefox remember your choice to allow or not allow the feature for each site. Any such election is domain specific. You are able to opt out at any time of having Firefox remember your choice, just like any other preference setting.</p>
+              </dd>
+              <dt style="text-decoration: underline;">What Information Firefox Collects</dt>
+              <dd>
+                <p>If you choose to allow it, the Firefox Location-Aware Feature first collects one or more of the following relevant location markers: (i) location provided by a GPS device built into or attached to your computer or device and/or geolocation services provided by the operating system; (ii) the wifi routers closest to you; (iii) cell ids of the cell phone broadcast towers closest to you; (iv) the signal strength of nearby wireless access points and/or cell phone broadcast towers; and/or (v) your computer or device’s IP address. Next, it attempts to determine your location using these location markers. Any information Firefox uses, receives or sends as part of this Location-Aware Feature is not received by any Mozilla servers or by Mozilla. Firefox does not track or remember your location. Firefox does remember a random client identifier, the temporary ID assigned by our third party provider to process your request, for two weeks.</p>
+              </dd>
+              <dt style="text-decoration: underline;">Transmission of Geolocation Information to Third Parties</dt>
+              <dd>
+                <p>If your computer or device has a GPS unit or your operating system provides geolocation services and you have elected to use the location aware feature, Firefox will send your location information directly to the requesting website. If not, Firefox will send the other information described above, plus your user agent information (e.g., version of Firefox you’re using) and a temporary client identifier, to a third party geolocation services provider. That provider can determine your approximate location from such data (e.g., convert a set of WiFi signal strengths into latitude and longitude). This information is sent by Firefox over an encrypted connection and no cookies are used. Neither the domain name nor the URL of the site you’re visiting is sent to our service providers. Our providers estimate your location and return it to Firefox. Firefox provides your location information to the webpage that made the request.</p>
+              </dd>
+              <dt style="text-decoration: underline;">Restrictions on How Third Party Providers Use the Location Information Received</dt>
+              <dd>
+                <p>Our policy is to require third-party providers to enter licensing agreements with Mozilla, which prohibit them from releasing Personal or Potentially Personal Information to the public. We only permit our third party providers to use this information in conjunction with the service(s) they are providing to us. They are required to ensure that any information collected on our behalf is anonymized and aggregated before they are permitted to use such information to develop new features or products and services, or to improve the overall quality of any of their products and services. For example, this means that they are required to ensure that your IP address and unique identifier of your client will be stripped out before being used by any of our third party provider’s other products or features. For more information on how our geolocation services providers use information sent by Firefox, please see the privacy policy links in our list of <a href="/en-US/legal/privacy/firefox-third-party.html">third-party service providers</a>.</p>
+              </dd>
+              <dt style="text-decoration: underline;">Third Party Privacy Policies</dt>
+              <dd>
+                <p>Please carefully consider any website or service provider's privacy practices before agreeing to share your location.</p>
+                <ul>
+                  <li><em>Requesting Websites</em>. For information on the use of location data sent back to the website, please see that website’s privacy policy.</li>
+                  <li><em>Location-Aware Service Providers</em>. For information on how our service providers use the location data sent by Firefox, see the privacy policies linked from our list of <a href="/en-US/legal/privacy/firefox-third-party.html">third-party service providers</a>.</li>
+                </ul>
+              </dd>
+            </dl>
+
+            <p><strong>Panorama Feature</strong> (not applicable to Firefox for mobile).  Starting with Firefox 4, Firefox provides Panorama, which manages your tab experience. Your tab usage and names are not sent to Mozilla, but rather reside locally on your device. The first time you run Panorama, a video may be presented to you explaining Panorama. The video or web page is hosted by Mozilla so Mozilla receives your IP address and date and time of receiving the video.</p>
+
+            <p><strong>Firefox Sync Feature</strong>. The Firefox Sync feature is included in versions of Firefox beginning with Version 4. Firefox Sync allows you to synchronize certain data between your computers, mobile phones, and other devices that have the Firefox browser installed, by utilizing the Firefox Sync Services. (You can also use Sync with a syncing service hosted on a non-Mozilla server set up by yourself or a third party, but in that case this policy doesn’t apply to your use of such syncing service.) Examples of data you can synchronize include browsing history, form history, bookmarks, saved passwords, preferences, and open tabs. This data (“Firefox Sync User Data”) is stored on, manipulated, and transmitted to and from Mozilla’s servers by means of your use of the Firefox Sync Services. Firefox Sync User Data is encrypted on your computer before it is sent to Mozilla’s servers, so it is not available to Mozilla in a readable form. Mozilla uses SSL/TLS technology to ensure your Firefox Sync User Data is encrypted during transit.</p>
+
+            <p>In order to utilize the Sync functionality you must register for the Firefox Sync Services. During registration you will need to provide your email address and create a username and password (collectively “Account Data”). Your Account Data will be encrypted using SSL/TLS for transit. Your password will be stored on our servers in an encrypted form called a hash. This form of encryption disguises your password on the server, but still allows us to authenticate you when you sign into the Firefox Sync Services. Certain versions of Sync also ask you to create a secret phrase. The secret phrase is stored on your computer and is not sent to the Firefox Sync servers or to Mozilla. Mozilla does not collect any other Personal Information through Firefox Sync.</p>
+
+            <p>Mozilla receives and uses the following Non-Personal and Potentially Personal Information for the purpose of providing and improving the Firefox Sync Services: IP address, username, date and time of accessing the Firefox Sync Services, user agent string information such as the type of client OS and Firefox version in use, and aggregated operational data such as access log data and how many bookmarks, history, or tabs users have collectively created and synced.</p>
+
+            <p>The Firefox Sync Services also receive the host names you have given your devices that you are syncing. These names are used to label your tabs within Firefox Sync. If you don’t want to share your devices’ names, you should consider naming your devices with fanciful names rather than your actual name. The information is transmitted using SSL. Currently, you can opt out of having your devices named in Firefox Sync by visiting the Firefox preferences pane or about:config.</p>
+
+            <p>You can disconnect from the Firefox Sync Services and have your Account Data and Firefox Sync User Data removed from our servers at any time. On your computer, go to the “Tools” menu, highlight “Sync,” and click “Disconnect.” Then go to <a href="https://services.mozilla.com/delete-account/">https://services.mozilla.com/delete-account/</a>, and submit the form to request deletion of your Account Data and Firefox Sync User Data from our servers. If you are transferring a synced device to another party (such as if you are sharing, reselling, or donating your laptop or phone) you may wish to disconnect and then remove your Sync data from your device to avoid sharing it with the device recipient.</p>
+
+            <p><strong>Personas Feature</strong>. Firefox’s Personas feature is a theme that lets you personalize the look of your browser.</p>
+
+            <dl>
+              <dt style="text-decoration: underline;">Applying Personas</dt>
+              <dd>
+                <p>When you apply a Persona to your browser, Mozilla collects your IP address, the date and time you applied the Persona to your browser, and the url you used to make the application as well as the url you were visiting immediately before that (known as the “referrer” url).</p>
+              </dd>
+              <dt style="text-decoration: underline;">Creating a Custom Persona</dt>
+              <dd>
+                <p>If you are creating a Custom Persona for your own use, Mozilla does not collect any Personal Information.</p>
+              </dd>
+              <dt style="text-decoration: underline;">Contributing a Design to the Personas Gallery</dt>
+              <dd>
+                <p>The Personas gallery is where you can browse all the available designs. If you contribute a design or image (each a “Persona”) to the Personas gallery, Mozilla collects the following Personal Information: (1) your username and (2) your email address. Your username will be used to attribute your Persona to you and will be publicly available on the Personas gallery. You do not have to provide your real name; you can use a nickname or avatar. Mozilla will not make your email address publicly available without your consent or share it with any third parties other than Mozilla’s service providers. Mozilla will use your email address only to contact you regarding your design or to provide any additional information that you elect or opt in to receive.</p>
+              </dd>
+              <dt style="text-decoration: underline;">Personas’ Interactive Product Features</dt>
+              <dd>
+                <p>After you have selected your Persona, it is stored on your computer. Once per day the Personas service checks to see if your selected Persona has been updated. This feature sends the same information that web browsers typically transfer with any HTTP requests including user agent and your IP address.</p>
+                <p>We use this information to improve our products and services and to support decision making regarding feature and capacity planning. Mozilla is an open organization that believes in sharing as much information as possible about its products, its operations, and its associations. Accordingly, we may release public reports containing Aggregate Data so that our global community and Personas partners may make better product and design decisions. For example, we think it is good for users of Personas to know which are the most popular Personas and Personas designers to know how many times their Persona was downloaded.</p>
+              </dd>
+            </dl>
+
+            <p><strong>Report Web Forgery Feature</strong> (not applicable to Firefox for mobile). Firefox’s Report Web Forgery feature lets you report suspected web forgeries to Mozilla’s third party service provider(s) for the web forgery protection feature when you encounter a suspected malicious “phishing” or fraudulent website that is impersonating a legitimate website. This feature sends your comments about the suspected fraudulent website to our third-party provider(s), as well as the same information that the browser sends when you visit a website. Our policy is to require each of our third-party providers to enter into written agreements with Mozilla that prohibit them from releasing Potentially Personal Information to the public. Our policy is to only permit these third party providers to use this information in conjunction with the web forgery protection service they are providing. In addition, we require each third-party provider to maintain its own privacy policy that is linked to the online form where you report a potential web forgery. To prevent the third party provider from obtaining this information, don’t use this feature to report a web forgery. (Also see <em>“Protection Against Suspected Forgery and Attack Sites Features”</em> below.)</p>
+
+            <p id="telemetry"><strong>Usage Statistics</strong> (also known as Telemetry). Beginning with version 7, Firefox includes functionality that sends Mozilla usage, performance, and responsiveness statistics about user interface features, memory, hardware configuration along with IP address.</p>
+
+            <p>
+              This feature is turned off by default in general release versions of Firefox and Firefox Beta. In order to enable Aurora and Nightly testers to provide more efficient feedback, Usage Statistics are enabled by default on Aurora and Nightly. In either case, if this functionality is enabled, users can disable it in Firefox's Options/Preferences by simply deselecting the "Submit performance data" item.
+            </p>
+
+            <p>
+              Usage statistics are transmitted using SSL (a method of protecting data in transit) and help us improve future versions of Firefox. Once sent to Mozilla, usage statistics are stored in an aggregate form and made available to a broad range of developers, including both Mozilla employees and public contributors.
+            </p>
+
+            <p><strong>Snippets </strong>Firefox's default home page (&lt;about:home&gt;) loads small bits of information that we think will be useful to you. We call these "snippets". You can see them right below the search bar.</p>
+
+            <p>Once per day, Firefox gets new snippets from Mozilla. Whenever you see a snippet, that snippet makes a request to Mozilla, so that we can count how often snippets are viewed.  This request to Mozilla sends the name of the snippet and is securely transmitted over HTTPS. Aside from this and standard log data collected as a part of any web request, snippets in Firefox do not send other information to Mozilla.</p>
+
+            <p>To help display relevant snippets, your Firefox browser sends Mozilla a monthly request to look up your location at a country level using your IP address. We then send that country level information back to your Firefox browser, where it's stored locally. Your Firefox browser (not our web servers) will then pick snippets to show you based on the locally stored country information.</p>
+
+            <p>Aside from providing you with the snippets service, we use the information from these requests to estimate what fraction of Firefox users click on each snippet (broken down into general categories like Firefox version and geographic area). We will not use any of the information in this request to attempt to identify you or your web browsing activity on other websites, and we aggregate the info we receive into these general categories as soon as is practical.</p>
+
+            <p><strong>Feedback Button and Test Pilot for Beta Users</strong>. If you installed a Firefox 4 beta and then got the general release version of Firefox 4 by clicking an “update” button, then your Firefox still has the feedback features you received as a beta user. If you installed a beta version of Firefox 4 for computers and then got the general release version of Firefox 4 by clicking a “download” button, you still have the Test Pilot add-on as an extension to Firefox. The privacy policy for Feedback is <a href="/en-US/firefox/beta/feedbackprivacypolicy/">here</a> and for Test Pilot is <a href="https://testpilot.mozillalabs.com/privacy.php">here</a>. If you decide to remove Test Pilot or Feedback from Firefox on your computer, you can do so under the Tools menu in Firefox by selecting add-ons in the drop down menu and then uninstall. If you decide to remove Beta Tester Tools from Firefox on your mobile device, you can do so under the Add-ons Manager pane in the Browser Controls window by selecting the add-on in the extension list and clicking "Disable."</p>
+
+            <p id="health-report"><strong>Firefox Health Report</strong>. Firefox Health Report (“FHR”) is a Firefox feature designed to provide you with insights about your browser’s stability and performance. FHR does this by collecting data and sharing it with us in a way designed to minimize our ability to identify your browser. Our systems aggregate your data with that of other Firefox users and then send it back to your browser so you can see how your Firefox performance compares to others. FHR does not send us sites from your browsing history.</p>
+
+            <p>We use the data sent through FHR to provide users with FHR’s functionality, such as helping you analyze and address performance issues with your browser. We also use what we learn from the FHR data in the aggregate to make Firefox and our other products better. We may disclose aggregated FHR data openly in order to help further our mission of promoting openness, innovation and opportunity on the Web. We aim to be transparent about FHR and you can learn more about the exact data being sent to us in the feature itself. If you wish, you can choose not to share your FHR data with us in Firefox settings or the feature itself. <a href="https://support.mozilla.org/kb/firefox-health-report-understand-your-browser-perf">Learn more about FHR</a>.</p>
+
+            <h3>Security</h3>
+            <p>Mozilla is committed to protecting your personal information from unauthorized access, alteration, disclosure, or destruction. We undertake a range of security measures including physical access restraints, technical security monitoring, and internal security reviews of the environment. We also have policies in place to prohibit employees from viewing personal information without business justification. Additionally, it is our policy to ensure that Mozilla employees and contractors are bound by confidentiality obligations.</p>
+
+            <p>Beginning with Firefox 2.0, Mozilla has additional security features, some of which are provided by <a href="/en-US/legal/privacy/firefox-third-party.html">third-party service providers</a>.</p>
+
+            <p>The security features available depend on the version of Firefox you are using. Please see the end of this privacy policy for older versions of Firefox.</p>
+
+            <p><strong>Secure Website Certificate Verification</strong>. When you visit a secure website, Firefox may check with any status provider mentioned in the certificate to validate that website’s certificate. Firefox sends only the certificate identification to the certificate provider, not the exact URL you are visiting. Sending these verification requests to third parties is sometimes necessary to ensure your connection to a site is secure; to help maintain your security, Firefox may block access to the site if it can't verify your connection using the third party. If the certificate is no longer valid, you will receive an error page that states why the certificate is not valid and you will not be able to access that website. The technical name for this process is OCSP or On-line Certificate Status Protocol. You may disable online certificate verification in Firefox's preferences under the encryption tab. If you do this, none of the information discussed here will be sent to any third party certificate provider. An <a href="http://support.mozilla.org/en-US/kb/Options%20window%20-%20Advanced%20panel?bl=n&amp;s=OCSP#Certificates">article in our Firefox Knowledge Base</a> gives you information about changing your preferences. However, if you choose to disable the online verification feature, Firefox will not be able to confirm the identity of the website you are visiting, which may put you at greater risk of having your private information intercepted. In this case, Firefox will also not show the identity of the website in the URL bar.</p>
+
+
+            <p><strong>Protection Against Suspected Forgery and Attack Sites Features</strong> (not applicable to Firefox for mobile). The Firefox forgery and attack protection feature displays a warning if the website you are visiting is suspected of impersonating a legitimate website (commonly referred to as a phishing or forgery website) or a site that infiltrates or damages a computer system without your informed consent, including, without limitation, any computer viruses, worms, trojan horses, spyware, computer contaminant and/or other malicious and unwanted software (commonly called an attack site or malware). By default, Firefox checks the web pages that you visit against a blacklist that is downloaded to your hard drive at regularly scheduled intervals (e.g., approximately twice per hour), the rate of frequency may change from time to time. The blacklist does not include the full URL of each suspicious site. Instead, each URL is hashed (obscured so it can't be read) and then broken into portions. Only a portion of each hashed URL is included on the blacklist on your hard drive. If there is a match, Firefox will check with its third party provider to ensure that the website is still on the blacklist. The information sent between Firefox and its third party provider(s) are hashed URLs. In fact, multiple hashed URLs are sent with the real hash so that the third party provider(s) will not know what site you are visiting. If there is a match, Firefox displays either a “Reported Web Forgery” or “Reported Attack Site” alert, as applicable.</p>
+
+            <p>You may completely turn off the forgery and/or attack site protection features in Firefox’s preferences. If you do this, none of the information discussed here will be downloaded to your hard drive or sent to any third party service provider. An <a href="http://support.mozilla.org/en-US/kb/Options%20window%20-%20Security%20panel?bl=n&amp;s=suspected%20web%20forgery">article in our Firefox Knowledge Base</a> gives you information about changing your preferences.</p>
+
+            <p>Each time Firefox checks in with a third party provider to download a new blacklist, Non-Personal Information and Potentially Personal Information, such as the information that the browser sends every time you visit a website as well as the version number of the blacklist on your system, is sent to a third party provider. In order to safeguard your privacy, Firefox will not transmit the complete URL of web pages that you visit to anyone other than Mozilla and its service providers. While it is possible that a third party service provider may determine the actual URL from the hashed URL sent, Mozilla’s policy is to require its third party service providers to enter into a written agreement with Mozilla not to use any data or other information about or from users of Firefox for purposes other than to provide and maintain their service. In addition, Mozilla’s policy is to prohibit these third party service providers from correlating any Firefox user data with any other data collected through other products, services or web properties of that provider. These third party service providers may post about additional notices regarding their applicable privacy policies. (For example, see <a href="http://code.google.com/apis/safebrowsing/firefox3_privacy.html">Google Safe Browsing Service in Mozilla Firefox Version 3</a>.)</p>
+
+            <p>Please note that we’re not yelling at you in this paragraph. Our lawyers have advised us that we need to make sure this information is conspicuous so you’ll read it. <b>The forgery and attack site protection feature is provided “as is” and for your information as advice and guidance only. Mozilla and its contributors, licensors and partners do not guarantee that these protection features will prevent you from being deceived by a malicious website and we strongly recommend that you continue to be vigilant while online, particularly when following links sent to you in e-mail.</b></p>
+
+            <h3>Legal Process and Other Disclosures</h3>
+
+            <p>Consistent with our privacy commitments, we will scrutinize third party requests for information about you for compliance with the law, including those coming from governmental agencies or civil litigants.  We may access, use, preserve or disclose information about you only when we have a good faith belief that it is reasonably necessary to do so to satisfy the applicable law, regulation, legal process or lawful governmental request of any country, or to protect the rights, property or safety of Mozilla, its users or the public.  We will provide notice of legal process or governmental requests unless prohibited to do so by law or the circumstances warrant otherwise.</p>
+
+
+            <h3>What and When We Share with Third Parties</h3>
+
+            <p>Mozilla’s policy is to make Personal Information, such as your name and email address, and Potentially Personal Information, such as the URL of the site you last visited, only available to its employees, contractors, and selected contributors who signed confidentiality agreements that prohibit them from using or disclosing such information other than for approved Mozilla purposes.</p>
+
+            <p>We also work with third parties who provide infrastructure or back-end services (like content delivery networks, bandwidth providers, and services of an administrative nature). We may share Personal Information about you with such third parties for the purpose of enabling these third parties to provide such services.</p>
+
+            <p>Additionally, Mozilla may need to transfer Personal Information to an affiliate or successor in the event of a change of our corporate structure or status, such as in the event of a restructuring, sale, or bankruptcy.</p>
+
+            <h3>Transfer of Data to the U.S.</h3>
+
+            <p>Mozilla is a global organization and operates in different countries. Privacy laws and common practices vary from country to country. Some countries may provide for less legal protection of your personal data; others may provide more legal protection. By using Firefox, you consent to the transfer of the information collected, as outlined by this Policy, to Mozilla or its third party service providers in the United States, the Netherlands, and other places where our distributed, third party content delivery network exists (which is in several countries around the world), which countries may provide a lesser level of data security than in your country of residence.</p>
+
+            <h3>Data Retention</h3>
+
+            <p>We will retain any information collected for the period necessary to fulfill the purposes outlined in this Policy unless a longer retention period is required by law and/or regulations.</p>
+
+            <h3>Privacy Policy Changes</h3>
+
+            <p>Mozilla may change the Firefox Privacy Policy from time to time. Any and all changes will be reflected on this page. Substantive changes may also be announced through the standard mechanisms by which Mozilla communicates with its users and community, such as Mozilla's "announce" <a href="https://lists.mozilla.org/listinfo/announce">mailing list</a> and <a href="https://lists.mozilla.org/listinfo/announce">newsgroup</a>. It is your responsibility to ensure that you understand the terms of this Privacy Policy. You should periodically check this page for any changes to the current policy.</p>
+
+            <h3>For More Information</h3>
+            <p>You may request access, correction, or deletion of Personal Information or Potentially Personal Information, as permitted by law. We will seek to comply with such requests, provided that we have sufficient information to identify the Personal Information or Potentially Personal Information related to you.</p>
+
+            <p>Any such requests or other questions or concerns regarding this Policy and Mozilla's data protection practices should be addressed to:</p>
+
+            <p>
+              Mozilla Corporation<br/>
+              Attn:  Legal Notices - Privacy<br/>
+              650 Castro Street, Suite 300<br/>
+              Mountain View, CA 94041-2072<br/>
+              Phone: +1-650-903-0800<br/>
+              E-mail: <a href="mailto:privacy@mozilla.com">privacy@mozilla.com</a>
+            </p>
+
+            <h3>Appendix for Pre-Firefox 4.0</h3>
+
+            <dl>
+              <dt>Report Broken Website Feature. -3.6.x.</dt>
+              <dd>
+                <p>Firefox’s Report Broken Website feature lets you notify Mozilla when a website you visit improperly displays or incorrectly functions. The feature sends the URL of the broken website to Mozilla. You may also choose to send your email address and a description of the problem. This feature also sends your IP address and a variety of Non-Personal Information to Mozilla, including but not limited to the version of Firefox you are using and your language preference. Except for your email and IP address, Mozilla makes all of this information public. This feature does not send information to Mozilla until you explicitly authorize Firefox to do so. To prevent this public release of Personal and Potentially Personal Information, don’t report a website if the website’s URL contains your Personal and Potentially Personal Information, and don’t include Personal Information in your description of the problem. To prevent the release of any information, don’t use this feature to report a broken website.</p>
+              </dd>
+
+              <dt>Crash-Reporting Feature for Firefox 1.0-2.x.</dt>
+              <dd>
+                <p>For these earlier versions of Firefox, “Talkback” is Firefox’s crash reporting feature. Talkback also gives you the option to provide your Personal Information and Potentially Personal Information (including your name, email address, and the url you were visiting) and Potentially Personal Information (including your computer’s name, IP address, and the processes you were running at the time of the crash). You can selectively disable the sending of this information. Additionally, you have the option to include the URL of the site you were visiting when Firefox crashed, a comment, and your email address in the report. Mozilla only makes Non-Personal Information and Potentially Personal Information in the public reports available online at <a href="http://talkback-public.mozilla.org/">http://talkback-public.mozilla.org/</a>.</p>
+              </dd>
+
+              <dt>Security for Firefox 2.0 to 2.x.</dt>
+              <dd>
+                <p><strong>Protection Against Suspected Forgery Sites</strong>. The Firefox web forgery protection feature displays a warning if the website you are visiting is suspected of impersonating a legitimate website. Firefox lets you select various levels of protection, and different information is transmitted by Firefox depending on the level you choose.</p>
+
+                <p>By default, Firefox checks the web pages that you visit against a list of suspected web forgeries (a “blacklist”) that is downloaded to your hard drive at regularly scheduled intervals (e.g., approximately twice per hour), the rate of frequency may change from time to time. If there is a match, Firefox displays a “Suspected Web Forgery” alert. Each time Firefox checks in with the third party provider to download a new blacklist, Non-Personal Information and Potentially Personal Information, such as the information that the browser sends every time you visit a website as well as the version number of the blacklist on your system, is sent to the third party provider. In order to safeguard your privacy, Firefox will not transmit the URL of web pages that you visit in this default mode to anyone other than Mozilla and its service providers.</p>
+
+                <p>You may completely turn off the web forgery protection feature in Firefox’s preferences. If you do this, none of the information discussed here will be downloaded to your hard drive or sent to any third party service provider.</p>
+
+                <p>Each time Firefox checks in with the third party provider to download a new blacklist, Non-Personal Information and Potentially Personal Information, such as the information that the browser sends every time you visit a website as well as the version number of the blacklist on your system, is sent to the third party provider. In order to safeguard your privacy, Firefox will not transmit the complete URL of web pages that you visit to anyone other than Mozilla and its service providers. While it is possible that a third party service provider may determine the actual URL from the hashed URL sent, Mozilla’s policy is to require its third party service providers to enter into a written agreement with Mozilla not to use any data or other information about or from users of Firefox for purposes other than to provide and maintain their service. In addition, Mozilla’s policy is to prohibit its third party service providers from correlating any Firefox user data with any other data collected through other products, services or web properties of that provider. These third party service providers may inform you about additional notices regarding their applicable privacy policies.</p>
+              </dd>
+            </dl>
+          </div><!-- end #content div -->
+
+
+        </div>
+      </div>
+    </div>
+  </body>
+</html>
--- a/browser/metro/base/jar.mn
+++ b/browser/metro/base/jar.mn
@@ -6,16 +6,19 @@
 chrome.jar:
 % content browser %content/
 
   content/aboutCertError.xhtml                 (content/pages/aboutCertError.xhtml)
   content/aboutRights.xhtml                    (content/pages/aboutRights.xhtml)
   content/blockedSite.xhtml                    (content/pages/blockedSite.xhtml)
   content/netError.xhtml                       (content/pages/netError.xhtml)
   content/aboutAddons.xhtml                    (content/pages/aboutAddons.xhtml)
+#ifdef MOZ_CRASHREPORTER
+  content/crashprompt.xhtml                    (content/pages/crashprompt.xhtml)
+#endif
 
   content/bindings/bindings.xml                (content/bindings/bindings.xml)
   content/bindings/tabs.xml                    (content/bindings/tabs.xml)
   content/bindings/toggleswitch.xml            (content/bindings/toggleswitch.xml)
   content/bindings/browser.xml                 (content/bindings/browser.xml)
   content/bindings/browser.js                  (content/bindings/browser.js)
   content/bindings/console.xml                 (content/bindings/console.xml)
   content/bindings/dialog.xml                  (content/bindings/dialog.xml)
--- a/browser/metro/base/tests/mochitest/browser_crashprompt.js
+++ b/browser/metro/base/tests/mochitest/browser_crashprompt.js
@@ -6,18 +6,17 @@
 "use strict";
 
 const Ci = Components.interfaces;
 const Cm = Components.manager;
 const Cc = Components.classes;
 
 const CONTRACT_ID = "@mozilla.org/xre/runtime;1";
 
-var gAppInfoClassID, gIncOldFactory;
-
+var gAppInfoClassID, gIncOldFactory, gOldCrashSubmit, gCrashesSubmitted;
 var gMockAppInfoQueried = false;
 
 function MockAppInfo() {
 }
 
 var newFactory = {
   createInstance: function(aOuter, aIID) {
     if (aOuter)
@@ -25,131 +24,197 @@ var newFactory = {
     return new MockAppInfo().QueryInterface(aIID);
   },
   lockFactory: function(aLock) {
     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
   },
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
 };
 
-let oldPrompt;
 function initMockAppInfo() {
   var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
   gAppInfoClassID = registrar.contractIDToCID(CONTRACT_ID);
   gIncOldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
   registrar.unregisterFactory(gAppInfoClassID, gIncOldFactory);
   var components = [MockAppInfo];
   registrar.registerFactory(gAppInfoClassID, "", CONTRACT_ID, newFactory);
   gIncOldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
 
-  oldPrompt = Services.prompt;
+  gCrashesSubmitted = 0;
+  gOldCrashSubmit = BrowserUI.CrashSubmit;
+  BrowserUI.CrashSubmit = {
+    submit: function() {
+      gCrashesSubmitted++;
+    }
+  };
 }
 
 function cleanupMockAppInfo() {
   if (gIncOldFactory) {
     var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
     registrar.unregisterFactory(gAppInfoClassID, newFactory);
     registrar.registerFactory(gAppInfoClassID, "", CONTRACT_ID, gIncOldFactory);
   }
   gIncOldFactory = null;
-
-  Services.prompt = oldPrompt;
+  BrowserUI.CrashSubmit = gOldCrashSubmit;
 }
 
 MockAppInfo.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULRuntime]),
   get lastRunCrashID() {
     gMockAppInfoQueried = true;
     return this.crashid;
   },
 }
 
+function GetAutoSubmitPref() {
+  return Services.prefs.getBoolPref("app.crashreporter.autosubmit");
+}
+
+function SetAutoSubmitPref(aValue) {
+  Services.prefs.setBoolPref('app.crashreporter.autosubmit', aValue);
+}
+
+function GetPromptedPref() {
+  return Services.prefs.getBoolPref("app.crashreporter.prompted");
+}
+
+function SetPromptedPref(aValue) {
+  Services.prefs.setBoolPref('app.crashreporter.prompted', aValue);
+}
+
 gTests.push({
-  desc: "Test Crash Prompt",
+  desc: "Pressing 'cancel' button on the crash reporter prompt",
   setUp: initMockAppInfo,
   tearDown: cleanupMockAppInfo,
 
-  get autosubmitPref() {
-    return Services.prefs.getBoolPref("app.crashreporter.autosubmit");
-  },
-  set autosubmitPref(aValue) {
-    Services.prefs.setBoolPref('app.crashreporter.autosubmit', aValue);
-  },
-  get promptedPref() {
-    return Services.prefs.getBoolPref("app.crashreporter.prompted");
-  },
-  set promptedPref(aValue) {
-    Services.prefs.setBoolPref('app.crashreporter.prompted', aValue);
-  },
+  run: function() {
+    MockAppInfo.crashid = "testid";
+    SetAutoSubmitPref(false);
+    SetPromptedPref(false);
+
+    BrowserUI.startupCrashCheck();
+    yield waitForCondition2(function () {
+      return Browser.selectedBrowser.currentURI.spec == "about:crashprompt";
+    }, "Loading crash prompt");
+
+    yield Browser.selectedTab.pageShowPromise;
+    ok(true, "Loaded crash prompt");
+
+    EventUtils.sendMouseEvent({type: "click"},
+                              "refuseButton",
+                              Browser.selectedBrowser
+                                     .contentDocument
+                                     .defaultView);
+    yield waitForCondition2(function () {
+      return Browser.selectedBrowser.currentURI.spec == "about:blank";
+    }, "Crash prompt dismissed");
+
+    ok(!GetAutoSubmitPref(), "auto-submit pref should still be false");
+    ok(GetPromptedPref(), "prompted pref should now be true");
+    is(gCrashesSubmitted, 0, "did not submit crash report");
+  }
+});
+
+gTests.push({
+  desc: "Pressing 'Send Reports' button on the crash reporter prompt",
+  setUp: initMockAppInfo,
+  tearDown: cleanupMockAppInfo,
 
   run: function() {
     MockAppInfo.crashid = "testid";
+    SetAutoSubmitPref(false);
+    SetPromptedPref(false);
 
-    // For the first set of tests, we want to be able to simulate that a
-    // specific button of the crash reporter prompt was pressed
-    Services.prompt = {
-      confirmEx: function() {
-        return this.retVal;
-      }
-    };
+    BrowserUI.startupCrashCheck();
+    yield waitForCondition2(function () {
+      return Browser.selectedBrowser.currentURI.spec == "about:crashprompt";
+    }, "Loading crash prompt");
+
+    yield Browser.selectedTab.pageShowPromise;
+    ok(true, "Loaded crash prompt");
 
-    // Test 1:
-    // Pressing cancel button on the crash reporter prompt
+    EventUtils.sendMouseEvent({type: "click"},
+                              "sendReportsButton",
+                              Browser.selectedBrowser
+                                     .contentDocument
+                                     .defaultView);
+    yield waitForCondition2(function () {
+      return Browser.selectedBrowser.currentURI.spec == "about:blank";
+    }, "Crash prompt dismissed");
 
-    // Set the crash reporter prefs to indicate that the prompt should appear
-    this.autosubmitPref = false;
-    this.promptedPref = false;
+    ok(GetAutoSubmitPref(), "auto-submit pref should now be true");
+    ok(GetPromptedPref(), "prompted pref should now be true");
+    // TODO: We don't submit a crash report when the user selects
+    // 'Send Reports' but eventually we want to.
+    // is(gCrashesSubmitted, 1, "submitted 1 crash report");
+  }
+});
 
-    // We will simulate that "button 1" (which should be the cancel button)
-    // was pressed
-    Services.prompt.retVal = 1;
+gTests.push({
+  desc: "Already prompted, crash reporting disabled",
+  setUp: initMockAppInfo,
+  tearDown: cleanupMockAppInfo,
+
+  run: function() {
+    MockAppInfo.crashid = "testid";
+    SetAutoSubmitPref(false);
+    SetPromptedPref(true);
 
     BrowserUI.startupCrashCheck();
-    ok(!this.autosubmitPref, "auto submit disabled?");
-    ok(this.promptedPref, "prompted should be true");
+    is(Browser.selectedBrowser.currentURI.spec,
+       "about:blank",
+       "Not loading crash prompt");
 
-
-    // Test 2:
-    // Pressing 'ok' button on the crash reporter prompt
+    ok(!GetAutoSubmitPref(), "auto-submit pref should still be false");
+    ok(GetPromptedPref(), "prompted pref should still be true");
+    is(gCrashesSubmitted, 0, "did not submit crash report");
+  }
+});
 
-    // Set the crash reporter prefs to indicate that the prompt should appear
-    this.autosubmitPref = false;
-    this.promptedPref = false;
+gTests.push({
+  desc: "Already prompted, crash reporting enabled",
+  setUp: initMockAppInfo,
+  tearDown: cleanupMockAppInfo,
 
-    // should query on the first call to startupCrashCheck
-    gMockAppInfoQueried = false;
-
-    // We will simulate that "button 0" (which should be the OK button)
-    // was pressed
-    Services.prompt.retVal = 0;
+  run: function() {
+    MockAppInfo.crashid = "testid";
+    SetAutoSubmitPref(true);
+    SetPromptedPref(true);
 
     BrowserUI.startupCrashCheck();
-    ok(gMockAppInfoQueried, "id queried");
-    ok(this.autosubmitPref, "auto submit enabled?");
-    ok(this.promptedPref, "prompted should be true");
-
+    is(Browser.selectedBrowser.currentURI.spec,
+       "about:blank",
+       "Not loading crash prompt");
 
-    // For the remaining tests, attempting to launch the crash reporter
-    // prompt would be incorrect behavior
-    Services.prompt.confirmEx = function() {
-      ok(false, "Should not attempt to launch crash reporter prompt");
-    };
+    ok(GetAutoSubmitPref(), "auto-submit pref should still be true");
+    ok(GetPromptedPref(), "prompted pref should still be true");
+    is(gCrashesSubmitted, 1, "submitted 1 crash report");
+  }
+});
+
+gTests.push({
+  desc: "Crash reporting enabled, not prompted",
+  setUp: initMockAppInfo,
+  tearDown: cleanupMockAppInfo,
 
-    // Test 3:
-    // Prompt should not appear if pref indicates that user has already
-    // been prompted
-    this.autosubmitPref = false;
-    this.promptedPref = true;
+  run: function() {
+    MockAppInfo.crashid = "testid";
+    SetAutoSubmitPref(true);
+    SetPromptedPref(false);
+
     BrowserUI.startupCrashCheck();
+    is(Browser.selectedBrowser.currentURI.spec,
+       "about:blank",
+       "Not loading crash prompt");
 
-    // Test 4:
-    // Prompt should not appear if pref indicates that autosubmit is on
-    this.autosubmitPref = true;
-    this.promptedPref = false;
-    BrowserUI.startupCrashCheck();
+    ok(GetAutoSubmitPref(), "auto-submit pref should still be true");
+    // We don't check the "prompted" pref; it's equally correct for
+    // the pref to be true or false at this point
+    is(gCrashesSubmitted, 1, "submitted 1 crash report");
   }
 });
 
 function test() {
   if (!CrashReporter.enabled) {
     info("crash reporter prompt tests didn't run, CrashReporter.enabled is false.");
     return;
   }
--- a/browser/metro/components/AboutRedirector.js
+++ b/browser/metro/components/AboutRedirector.js
@@ -1,8 +1,9 @@
+ // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
 /* 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/. */
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
@@ -34,17 +35,23 @@ let modules = {
   },
   certerror: {
     uri: "chrome://browser/content/aboutCertError.xhtml",
     privileged: true
   },
   home: {
     uri: "about:start",
     privileged: true
-  }
+  },
+#ifdef MOZ_CRASHREPORTER
+  crashprompt: {
+    uri: "chrome://browser/content/crashprompt.xhtml",
+    privileged: true
+  },
+#endif
 }
 
 function AboutGeneric() {}
 
 AboutGeneric.prototype = {
   classID: Components.ID("{433d2d75-5923-49b0-854d-f37267b03dc7}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
 
@@ -59,17 +66,17 @@ AboutGeneric.prototype = {
 
   newChannel: function(aURI) {
     let moduleInfo = this._getModuleInfo(aURI);
 
     var ios = Cc["@mozilla.org/network/io-service;1"].
               getService(Ci.nsIIOService);
 
     var channel = ios.newChannel(moduleInfo.uri, null, null);
-    
+
     if (!moduleInfo.privileged) {
       // Setting the owner to null means that we'll go through the normal
       // path in GetChannelPrincipal and create a codebase principal based
       // on the channel's originalURI
       channel.owner = null;
     }
 
     channel.originalURI = aURI;
--- a/browser/metro/components/ContentPermissionPrompt.js
+++ b/browser/metro/components/ContentPermissionPrompt.js
@@ -51,78 +51,86 @@ ContentPermissionPrompt.prototype = {
       let requestingWindow = request.window.top;
       let windowID = request.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
       let browser = chromeWin.Browser.getBrowserForWindowId(windowID);
       return chromeWin.getNotificationBox(browser);
     }
     return chromeWin.Browser.getNotificationBox(request.element);
   },
 
-  handleExistingPermission: function handleExistingPermission(request) {
-    let result = Services.perms.testExactPermissionFromPrincipal(request.principal, request.type);
+  handleExistingPermission: function handleExistingPermission(request, type) {
+    let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type);
     if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
       request.allow();
       return true;
     }
     if (result == Ci.nsIPermissionManager.DENY_ACTION) {
       request.cancel();
       return true;
     }
     return false;
   },
 
   prompt: function(request) {
+    // Only allow exactly one permission rquest here.
+    let types = request.types.QueryInterface(Ci.nsIArray);
+    if (types.length != 1) {
+      request.cancel();
+      return;
+    }
+    let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
+
     // returns true if the request was handled
-    if (this.handleExistingPermission(request))
+    if (this.handleExistingPermission(request, perm.type))
        return;
 
     let pm = Services.perms;
     let notificationBox = this.getNotificationBoxForRequest(request);
     let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
     
-    let notification = notificationBox.getNotificationWithValue(request.type);
+    let notification = notificationBox.getNotificationWithValue(perm.type);
     if (notification)
       return;
 
-    let entityName = kEntities[request.type];
-    let icon = kIcons[request.type] || "";
+    let entityName = kEntities[perm.type];
+    let icon = kIcons[perm.type] || "";
 
     let buttons = [{
       label: browserBundle.GetStringFromName(entityName + ".allow"),
       accessKey: "",
       callback: function(notification) {
         request.allow();
       }
     },
     {
       label: browserBundle.GetStringFromName("contentPermissions.alwaysForSite"),
       accessKey: "",
       callback: function(notification) {
-        Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION);
+        Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION);
         request.allow();
       }
     },
     {
       label: browserBundle.GetStringFromName("contentPermissions.neverForSite"),
       accessKey: "",
       callback: function(notification) {
-        Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.DENY_ACTION);
+        Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.DENY_ACTION);
         request.cancel();
       }
     }];
 
     let message = browserBundle.formatStringFromName(entityName + ".wantsTo",
                                                      [request.principal.URI.host], 1);
     let newBar = notificationBox.appendNotification(message,
-                                                    request.type,
+                                                    perm.type,
                                                     icon,
                                                     notificationBox.PRIORITY_WARNING_MEDIUM,
                                                     buttons);
 
-    if (request.type == "geolocation") {
+    if (perm.type == "geolocation") {
       // Add the "learn more" link.
       let link = newBar.ownerDocument.createElement("label");
       link.setAttribute("value", browserBundle.GetStringFromName("geolocation.learnMore"));
       link.setAttribute("class", "text-link notification-link");
       newBar.insertBefore(link, newBar.firstChild);
 
       let win = this.getChromeWindowForRequest(request);
       link.addEventListener("click", function() {
--- a/browser/metro/components/components.manifest
+++ b/browser/metro/components/components.manifest
@@ -1,16 +1,19 @@
 # AboutRedirector.js
 component {433d2d75-5923-49b0-854d-f37267b03dc7} AboutRedirector.js
 contract @mozilla.org/network/protocol/about;1?what=start {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=empty {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=firefox {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=rights {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=certerror {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=home {433d2d75-5923-49b0-854d-f37267b03dc7}
+#ifdef MOZ_CRASHREPORTER
+contract @mozilla.org/network/protocol/about;1?what=crashprompt {433d2d75-5923-49b0-854d-f37267b03dc7}
+#endif
 #ifdef MOZ_SAFE_BROWSING
 contract @mozilla.org/network/protocol/about;1?what=blocked {433d2d75-5923-49b0-854d-f37267b03dc7}
 #endif
 
 # DirectoryProvider.js
 component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js
 contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}
 category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1
new file mode 100644
--- /dev/null
+++ b/browser/metro/locales/en-US/chrome/crashprompt.dtd
@@ -0,0 +1,16 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!ENTITY crashprompt.title           "Would you like to send Mozilla crash reports?">
+<!ENTITY crashprompt.message         "We are sorry, &brandShortName; just recovered from a crash. Sending crash reports will help Mozilla make &brandShortName; more stable and secure. You can always change your preference in Settings/Options.">
+<!ENTITY crashprompt.detailsLink     "What's in a crash report?">
+<!ENTITY crashprompt.detailedMessage.firstParagraph "A crash report contains some details about the crash, your machine, as well as a snapshot of the state of your browser when it crashed.">
+<!ENTITY crashprompt.detailedMessage.secondParagraph "This may include things like open pages, text typed into forms and the content of open messages, recent browsing history, or geolocation shared with a site.">
+<!ENTITY crashprompt.detailedMessage.thirdParagraph "We use crash reports to try to fix problems and improve our products. We handle your information as we describe in our ">
+<!ENTITY crashprompt.privacyPolicyLink "&brandShortName; privacy policy">
+<!ENTITY crashprompt.includeURLLabel "Include the address of the page I was on">
+<!ENTITY crashprompt.acceptbutton    "Send reports">
+<!ENTITY crashprompt.refusebutton    "Don't send">
+<!ENTITY crashprompt.privacyPolicy.title "&brandShortName; privacy policy">
deleted file mode 100644
--- a/browser/metro/locales/en-US/chrome/crashprompt.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# LOCALIZATION NOTE (crashprompt.messagebody2): %1$S - short brand name, %2$S - vendor name
-crashprompt.messagebody2=We are sorry, %1$S just recovered from a crash. Sending crash reports will help %2$S make %1$S more stable and secure. You can always change your preference in Settings/Options.
-crashprompt.dialog.title=Would you like to send Mozilla crash reports?
-crashprompt.dialog.privacyLink=Privacy statement of crash-reporting feature
-crashprompt.dialog.acceptbutton=Send reports
-crashprompt.dialog.refusebutton=Don't send
-crashprompt.dialog.title2=Privacy statement of crash-reporting feature
-crashprompt.dialog.statement1=Firefox has a crash-reporting feature that sends a report to Mozilla when Firefox crashes. Mozilla uses information in crash reports to diagnose and correct problems in Firefox that cause crashes. Though this feature starts automatically after Firefox crashes, it does not send information to Mozilla until you explicitly authorize it to do so. By default, this feature sends a variety of Non-Personal Information to Mozilla, including the stack trace (a detailed description of which parts of the Firefox code were active at the time of the crash) and the type of computer you are using. Additional information is collected by the crash reporting feature. Which crash reporting feature is used and what additional information collected by Firefox depends on which version of Firefox you are using.
-crashprompt.dialog.statement2=For current versions of Firefox, “Firefox Crash Reporter” is Firefox’s crash reporting feature. With this feature, you may have the option to include Personal Information (including your email address), Potentially Personal Information (including your IP address and the URL of the site you were visiting when Firefox crashed), and a comment. Firefox Crash Reporter also sends a list of all add-ons that you were using at the time of the crash, the time since (i) the last crash, (ii) the last install, and (iii) the start-up of the program. Mozilla only makes Non-Personal Information (i.e., generic information about your computer, the stack trace, and any comment given by the user) available in the public reports available online at http://crash-stats.mozilla.com/.
--- a/browser/metro/locales/en-US/chrome/preferences.dtd
+++ b/browser/metro/locales/en-US/chrome/preferences.dtd
@@ -30,10 +30,11 @@
 <!ENTITY doNotTrack.title                                        "Do Not Track">
 <!ENTITY doNotTrack.options.doNotTrack                           "Tell websites that I do not want to be tracked">
 <!ENTITY doNotTrack.options.doTrack                              "Tell websites that I want to be tracked">
 <!ENTITY doNotTrack.options.default                              "Do not tell websites anything about my tracking preferences">
 <!ENTITY doNotTrack.learnMoreLink                                "Learn more&#x2026;">
 
 <!ENTITY optionsHeader.reporting.title                           "Crash Reporter">
 <!ENTITY optionsHeader.reporting.crashes.label                   "&brandShortName; submits crash reports to help Mozilla make your browser more stable and secure">
+<!ENTITY optionsHeader.reporting.crashes.submitURLs             "Include the address of the page I was on">
 <!ENTITY optionsHeader.telemetry.title                            "Telemetry">
 <!ENTITY optionsHeader.telemetry.label                            "Shares usage statistics about &brandShortName; with Mozilla to help us make your browser better">
--- a/browser/metro/locales/jar.mn
+++ b/browser/metro/locales/jar.mn
@@ -17,17 +17,19 @@
   locale/browser/preferences.dtd          (%chrome/preferences.dtd)
   locale/browser/aboutPanel.dtd           (%chrome/aboutPanel.dtd)
   locale/browser/searchPanel.dtd          (%chrome/searchPanel.dtd)
   locale/browser/checkbox.dtd             (%chrome/checkbox.dtd)
   locale/browser/sync.dtd                 (%chrome/sync.dtd)
   locale/browser/sync.properties          (%chrome/sync.properties)
   locale/browser/passwordmgr.properties   (%chrome/passwordmgr.properties)
   locale/browser/phishing.dtd             (%chrome/phishing.dtd)
-  locale/browser/crashprompt.properties   (%chrome/crashprompt.properties)
+#ifdef MOZ_CRASHREPORTER
+  locale/browser/crashprompt.dtd          (%chrome/crashprompt.dtd)
+#endif
   locale/browser/aboutAddons.dtd          (%chrome/aboutAddons.dtd)
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %locale/browser/
   locale/browser/bookmarks.json           (bookmarks.json)
 
 #
 # Browser jar resources
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -13,19 +13,21 @@ pref("devtools.chrome.enabled", true);
 #else
 pref("devtools.errorconsole.enabled", false);
 pref("devtools.chrome.enabled", false);
 #endif
 
 // Automatically submit crash reports
 #ifdef RELEASE_BUILD
 pref("app.crashreporter.autosubmit", false);
+pref("app.crashreporter.submitURLs", false);
 #else
 // For Nightly and Aurora we turn this on by default
 pref("app.crashreporter.autosubmit", true);
+pref("app.crashreporter.submitURLs", false);
 #endif
 // Has the user been prompted about crash reporting?
 pref("app.crashreporter.prompted", false);
 
 // Debug prefs, see input.js
 pref("metro.debug.colorizeInputOverlay", false);
 pref("metro.debug.selection.displayRanges", false);
 pref("metro.debug.selection.dumpRanges", false);
new file mode 100644
--- /dev/null
+++ b/browser/metro/theme/crashprompt.css
@@ -0,0 +1,134 @@
+/* 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/. */
+
+%filter substitution
+%include defines.inc
+
+body {
+  background-color: #F4F4F4;
+  margin-left: 16%;
+  margin-right: 23%;
+  margin-top: 115px;
+}
+
+a {
+  text-decoration: none;
+}
+
+#detailsLink {
+  color: black;
+  display: table-row;
+}
+
+#detailsGrid {
+  display: table;
+}
+
+#detailsContainer {
+  display: table-row;
+}
+
+#detailsLinkLabel {
+  display: table-cell;
+}
+
+.detailsSpacer {
+  display: table-cell;
+  padding-right: 8px;
+}
+
+#crashpromptDetailedMessage {
+  font-size: @metro_font_snormal@;
+  display: table-cell;
+}
+
+#crashPromptTitle {
+  color: #737980;
+  font-weight: normal;
+}
+
+.hidden {
+  display: none !important;
+}
+
+#crashPromptButtonContainer {
+  margin-top: 40px;
+}
+
+#lightboxBackdrop {
+  position: fixed;
+  top: 0%;
+  left: 0%;
+  width: 100%;
+  height: 100%;
+  z-index: 1001;
+  background-color: rgba(0,0,0,0.7);
+}
+
+#lightbox {
+  position: fixed;
+  top: 20%;
+  left: 15%;
+  width: 70%;
+  height: 60%;
+  padding-bottom: 1%;
+  padding-top: 1%;
+  background-color: white;
+  z-index: 1002;
+}
+
+#privacyPolicyTitle {
+  margin-left: 10%;
+}
+
+#privacyPolicyBody {
+  margin-left: 10%;
+  margin-right: 10%;
+  padding-right: 20px;
+  max-height: 75%;
+  overflow: auto;
+}
+
+#lightboxCloseButton {
+  background-image: -moz-image-rect(url("chrome://browser/skin/images/infobar-close.png"), 0, 40, 40, 0);
+  width: 40px;
+  height: 40px;
+  float: right;
+  margin-right: 30px;
+  margin-top: 10px;
+}
+
+button {
+  color: #FFFFFF;
+}
+
+button:not([disabled]):hover {
+  color: #FFFFFF;
+}
+
+#refuseButton {
+  background: #847F7C;
+}
+
+#refuseButton:hover {
+  background: #B0B0B0;
+}
+
+hr {
+  border: 1px solid #C7C7C7;
+}
+
+#privacyPolicyLink {
+  color: #649FEF;
+}
+
+.button-default {
+  background: #649FEF;
+  border-color: #9DCFF2;
+}
+
+.button-default:not([disabled]):hover {
+  background: #2990D9;
+  border-color: #8DBAD9;
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..efff2fd985b133fccd735534b338307b6a266f43
GIT binary patch
literal 696
zc$@*a0!RIcP)<h;3K|Lk000e1NJLTq000vJ000mO1^@s6^u$DB0007iNkl<ZI1z=E
zO=}ZD7{{M^*==5`wFrWONRQH^;Ms5Bsb2gpMy(!1#EW1l6g*q>BzP1LUc`ckK?{`z
z8`GpA+4tF<cW0BPM%s|)vNO-@??2D~+1WvG|3d|s=bf)Ddj=FWpsA%8fkFso1+^9_
zW0CMQ@v)3qAfYBD1-L}(HK+?hs#HT20rfqDiNzYv(n5w91x6zugyZshZ8^_akkO?=
zdn?zkZ4iua&w}UWwK4Daf9Si|R#J2{JbGxY4d+2;tvfCr1X06Q2*MbuW|+wV%Zn#x
zy*w|+3jQn!2Ypm%i$n00Yqw}09==mbeVPYPuPd!Tw>zD;0<4HCM7k!|6qO+-1hU>B
zdhE*1rh!>wYk~EL(IWzgQCKKS9VgaOaa!jp$1F?Z{j4Zn%o3Q+v!d9Dhw+{gD2r3Y
zOC18YRsehql3WD>5+z~&>%!8xn;7HE)A00IYyGW#@cV((IAVS!1^>GM_@c3GGz>IB
z15n`sWt5m?7zXRkhAaJaxIFI%(YjSMp@y-NEBR7qd@#4y%-F8tW!b?X-DP>cd0Gk}
z7kn#9<EEl{mdJ|Ru%%$c1B}GnZWiF9fyG^!Y3@EUq}^WUmDZ*?EohAW*=hgT(g@Nl
z$2lxgS#o3kSgZt2>{c)GvV3cB6a>LNw~On?C{!2*4FD9<j57BlIeX2Kz2o(9Ws}Lc
z+?FcK*I?5yioXgjUY6@z$jxCt-YN6RLYG74pV*u5<DMPQ{@JB6Wl65NO7^<F=k9R6
z**oYx)mp}7-dzp?uH4WQdlNC_4(Vh(dLkFF_8P<LDuGmaKqOs4K8~z#x09zveRB3-
e`;P0?|L|YQWuGX^4JH!+0000<MNUMnLSTZ@OGk46
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..790791bb8a6e03f433970e4afc3e0bf6da38c8ee
GIT binary patch
literal 1839
zc$@(*2hjM5P)<h;3K|Lk000e1NJLTq001Tc001Be1^@s6eq;)>000K|Nkl<ZSP8vY
zOOG5^6+ZV-)eq04Z4EXgwgquEk@6E*kX3#G5fVuJ0R90&f+5IG*klLF1~%~q8v#O5
zA|;C?8$`s0Ac2X5V#%JKvHMxoU0q%G&3CJ(I^&sfVrD#4t8`1%_n!0J@4M&RQ<u^9
zt(52@C)&h4$9;wOs-*~R!2&ILP@GXqFv1lyo=<VH)Q5Vwa)GtZ1xD7h%7wh$aYWh^
zEpg9r54~K2DmO%rr_#4`VR=ggQn(XNEyIIsv$kc7Foj_CiNG<L)}SgG0Lb9E@{F8k
zJaL=k9j~0r+ZF&fZ3GWsV5+QYP_7*kPa86%PNpPf5K;ukM6eaN4r~d4lgSA=(?l)=
zNglvb&xCXgKb>+LFx}7&wpVp2_n1I80xJ@BenSXJX@^lr6Okk?RH+9r3QQ(ah`<xD
z=Qw!)<FI>@%gaD%WJ`GFy|V(a0=FF^%Y87GQY|*PId98FMsrMnx42Wec>cv}Z*tDR
zxAJ-EB(6_A#)t6kf9~A*A-QM40xPhN1lt=Aei1?#o!a^d4M5I(FdzS<Ue~~cD8zzB
z<mIz6Un;$QLc6CPrj))h*)7J6Ax@p%DufMZJN=yxtaTqWfBwH(=lx&q?d<$%?GKm`
zkuj$rza%kQ68~^Cy#YyQ>rQ9<5CWAChxu#F;wfT4ev6nmbpZ^?jUgCgUMUu{QMpua
zo-v-a`K79UH7|-jlcM$?lWxR>HMaD#W$L&wPLr^VnB9B0_l|Y`uj|og^x%B>ba!v>
zcVwMY5_wH0h{n|p0AO350<y459Z3V!!eXN^!^z~0Nb?zCBG}f$>GZla+;YhcRI@x)
zE-T~qwB-S;^yD|aXjP7v!fMG3vt$;<e5b6+x6TS!rK;Z++3YS$gN{C<Zvj%sD^KG)
zkaBk!Xk+3s%B)B^@K;7)M|EDR-3Oyv&bm*ZHaJA#XZQOL-nJ+agd$!0gzt<}8?lhO
z1jeSH*Sk68EjPm-T#?bB8Psa-Ne!ntqLkEA1ZOAH?1pe|$$g@98^GW*`ldXGj6)wW
zaeC=r$YU2h+00W>I@toP0A^&WtnjLs%|2gh^_!;!tWx@BHk;pxwxcrKE=}|qeb*Qw
z7GM(e(T7AYe~T!aG1|(hqGzasDk6Brr4ii9z~_1K*@fOS-)5YB?KA)(;oY6x-Je<)
zO4J5R69lBJ^h{Tl&Rw*WL8E*9K)?P`;E*ZMHtw~g`4^H#nn8D@eVk?2L9ICjFgM9(
zuLoro+<K**GF+8r?y=ypvWfmyOBmUTnCiWf2$jrnl7ePIm5#LM+3erCQa4Wu413}4
zvn>C+6__ZN32G=Ug0$okdr(5!{#m_6vIs9;sI(ZpdYjXQYn-0j5>)n+O_8Lo)oUff
zbWmHK<D6~2@Xf1l3(lYaUjTX^`gi;N9~f<?QqV$Iu9Ci0&5=^6+cLB}cC})+Uq$5u
z^>FPoh6*FTh^*C;f>zKQX^wQq5RLQ;W6SBRxQ=m%o+rEDC1l0?4P>i12lG)vkuXTO
zu+%MJ=TQk8t7~BOi%8=^NkMZRrBTv4N)t7mPXD2`xz!l>s@53u=ka*@iE}8fV$PzD
z78Rl}N;n<>*eVJe2^)KBDc3KKNU9)d{)nU~x6fa@dif^f{Ax4qxE8t$?%lut{mS^E
zAYK}2mU8js5;k@oWejhRND8`ZNlDo{k{~H5J<sOl>rtl0&~YuQhF>~IlPDp=S))Wa
zAR)HGxNK^Lj($cQ0aiOWC~2e{v`6}D8KR`AsK%qgC)(OS90w@AL$UJSa5VZD^VpXV
zz}6D>AmLyI3cliX*v=6tn+`{#G)jsOyB2FD)^zaY)$VrM=dWD3d>ehAZN|kGQ8?Lq
zF!*j!6ax`Nr30ccv1(p!VDp(+T44K$Y#oTggX81#@r>2I_H$burJ%i*bg_z3i3o+m
zQkT<3S-y7I=(18bilurez^smaz1Q<4l5>p09@u(rz8_>qC2VXx7FbR1T2jzHhQa+P
zjeA_}kYu&H^Wa0&d4FsSI_KZ--`{;lkgHIl)cfH&kwzO)Li*UpD6D-n0}r*2m9pt~
zL{chcuX4%itGy+?DU+?<g$rN*#@727lME->E5qSvz>_Z4B<cXHtK83!vo0T-NE<(g
z+s88YMaO+fBmFTMqBP0d!r)V13x#?rB-ON>4wlCJ82N2`oQ?YNouER;KC$m=FT`?q
z0tp+xU&&bPShKeMb0GW<CUS`b9E#0zoHn}R;K@*J6el@6bo}ZBX9p8$t=3g7;meQ^
z8EJppX+Rv;J`u1ua4qRe7Xfexfcr;RIQt-u<EyS*t>hACBq-HsBcAB2j+E-`{?B3f
dvM4+Z?!T1d0iF!w!7Bg&002ovPDHLkV1j@2edYiF
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..07444ca41abf6fb663206e984202d5c99128f8e7
GIT binary patch
literal 744
zc$@*~0vG*>P)<h;3K|Lk000e1NJLTq000mG000vR1^@s6nP-j900087Nkl<ZI1!bU
zy-yTD7{;IZ*xlQ`-CH?DLQny*P)V#zOsp*Y7w9Zaj314St*4eoDJ_)PV4)=XC#Z>)
ziH!*=iNOyL6+!Q~z1`cHof+p4!y%Hun@o0QlK1yK?`GbYp!T$c)Nx{3;ikk0h0t}l
z1+Zur?0T+ItC>#N^QNGk<S>Z*;Uj~S@vhQHn|pEKkf?&a0g|c*9OW2$L<s@elx;6a
zr8Zp^oJ6C8`#&0(MwaHc=DsdGVIXNCNUP<RGJp%<<A2>yCA8xdh2NoDkvIrS*GC2i
z?}br(l)9$JM_P&U#qu9FW1HMi<4UGM+-`fEvlD&2)rSN9{bxPsJ(LBz%3&TM%Y5wH
z=c4wEk!!GJ5yJTC^A#67s`kX^x*@h^$94G0IF3&$YG!h&6XtQ^n=(Y(>bc86?<r6e
zgcvB=o!q@=j2+us-Th#HU*9FS(7n`?aTubElzA8(Wm_h8L#!Jw@cj!1hlU<lr^i`A
zqmZQ~B!#mrz+V_;^l(p4b@IT-z~#d8%V3i42+#qo{u}OGJUrAt9ut@Hzx3^X?R%6$
z%DeC9zTa#cp?3t(O24Z&>Q{gMSb4>jY(Y82wg6T&&qO}4F#l!37ExE+B#8*x2$kgE
z#t_Z*RwXE<UN6<_*XzyZ8^vkDTRcsqSWU+CwBn@DMnbE90ai6lb8+X}m(P=?;7!A`
zR+iBWqA3vP5;&zXHEK8!j<RpwHp_YxO1)fKZroUDHr^Mosw&M|?UGgH60@@k<73(i
z7+f{r%%#$HqwP4@8`DU${Pz69!sAXiYTJ#O>u2yP0^V9TPHmhG1Xo@}oq2F*c<BD}
zucg|LBwwTif=iRHBt?#JMq(NYQ){a0Mpt(nfs{$w#D}+^&U7YHse2-Yor(0u*1A!(
aoADb$woK3=hEs$90000<MNUMnLSTZY25uYx
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a3f2f27940b375b8f0dc0e61b08d04b28698602a
GIT binary patch
literal 1976
zc$@*a2S@mcP)<h;3K|Lk000e1NJLTq001BW001Tk1^@s6r6ZZ?000MnNkl<ZSP8vY
zOK&7s6+ZV>RsC|?Za=U+o{T3TjqnDs10hXVffP0fg#}B5*yIlofrLSt2m}J85fT!~
z2*Cgg15y@5f(-)_8^Qu1O@Pfr3AQuSPTT!*SKqoXzT0k3dM0DXW5&5sb(dXTb-wRA
zk2>cv`s&{mE%yXbjT7}a4VD;{1*5#fD6WzXxaNs+91Tyce+AMDp7!r}T7Nyz#y4@D
zQNSAg{xLt~edB|wm@W(9L<eA|S>SvGfRPM1@tlNj5459n#ZzO?(K4*kRZq`t23p?;
zjGYYxSV!ho)_EbV4(jPESAI4%t5zIWSv)MoLuaOtX|3E(r>kBOElBEI5Y);A&0OYm
z`=g9DHX;Tb_<W27<0W1nmW9iLa-1iE^Up0#&)r(PwERV>TuE79QY15179%hb$P`gR
z^?3y~pkPDLIzhFo9BS}GAg2cb015Y5Av`&msub+tQO2fAS@QkMYilo8ry6UHWLXAR
zjzCmmOO`}d)xESXSwe$t+;dqzsK&@6r&2>sQ=gk66(Ql$kxnMnlbnC*Lrb&2n;vG@
zx3};8*1BMPA`7LBD7X?q<))?;?@`@Qsu(q<5F2;kf?J4<-BZN|_fQ}tMkG)mI2tK1
z2?C*2DP^UvtzBCE>GW*vBf?R|vQQqmhiaKi>*@cah76SRK#B+JL|{rz8lOBDLVRqo
zF?(~WT6t+}`|V!=h_oV?N+?lOpoSf*k&{JijMXT;uNpk)m~MuLlyiwBoHY9IIYKKf
zXX*1FzI5^Bsm9DYVzy$Th_n)cTFWd?vua6=V?#sD9I=6HiJ20E^e|f=hz&`pU_oXq
z1jhqsCjvME#09@H*O<LARjvNw&7HSz2&07+N-E_FLqUYB#A`b6UR0y81>=`2vJHjo
z7CjsrxJZ;)a4Q_n+34>hLnnPhHkGEym!H14`t$n2{Kq*D6-P-~3RRL8nNrb*Y785S
z2FI%*)F+>>A^?(rlw!fBjoF#6oMianYbcmAaLco`a;4TE=C3oPiVJ~DQVazQ>Lx5x
z1775~6CqEH8JxsqyG=Cz+9YE|Z~=&&X^A9NUs_vPxj8#G_X)0K*^z%&4I|Sk0_L!a
z4p2p7xnJP4K0gMKp2_HYZh{&YX6jSdpITXZp`=tp1OSn)lrVvG6{#hj)Q3({y)KSG
zoZ9E_v3ATqAR$YV<nybm%fFtRYg`pvmK<{#YJBRTeJuIYR7esP*lzMC4&VqEyt!Df
zRepGJb@f}-smi=_JWYVjQ;Le~Bvs3BL+$(k905tBe0F(p@i(hW3!m{Q-T}rvN`HNb
zH!1p$b5r05K>SAKX_U*^4=%4h^`p6JwiqgcdzPd5QIlCPdCU2nOCdfxzqs&+wZ(-m
z!~#fV`N0A>qQwH;SN_<S&(V*c<hOP1k9+U5zR}WpOK691u;Bv%fPS@==la@KbLUm-
zWX=okdBD4&JUD*<#=2LV@9ciBe^4|9F~dk$p5(4Wb(DpgKQ{$@pnJW8gYWL{Hvi&`
zABsfi!i7SRwgMoiKX6p-J$V2a4uUD{D?815*PU_u&RdQ2hjFiw_=OlU)6No0u7fwz
z!We)?s67EKd<a|ZZtpv-z5TyAk5)&(s1-#KTp_eaKRq_y={UR(;DY9)0*08UGfg*u
z@3by9x0=nLgrewq7qk#)U7arQDnQ}SEt(TUePvOCVVuCe>FM@v>;gO{p9CqP(Hf#s
zKIU@Xzp>x#U)$f`|K}lV0&7+j(iFlfBZkg&eAQC9@2Cai-Im8R20PDq+N8rQ7|IY(
zrNBP`+36OZV6@i1d~4_KOTyTJ2V(?SVGw8|)(715{DGxX4^qdL%weS48nLG3I8%N)
zj*|d@g22cGCX|N@@Z<Mi-uZvG+TCmIZs$K9z9x|l5kdvV1tSz2vqq$s@x0arj2jQK
zrp<TfNXb$p48gee-ZFI}0L;HS*TWlI+jnmY=W>rALl3A6W($wDT>4;oISjk#>-hSA
zG;1WveU_4;TV<p@lM;P{g;bAT&bfc=wmL8Nd)+r;kYO6sl1fG47$SUN&<fj*?}9ZZ
zhSoNGZN?+~b0PYAlJ%qo$*J(HKtG*uD}^_c7vp&#c97@!k9T(NZd!**mY|)9L5Atf
ztDX@WtkYRUcq}X{VEr0=Jr7^s!DtqJjgB8{lJ%qw*`}(mB^|sOs;E%Bn7rtm|7)|^
z`F3~E-vMJxEzEFdY=P7ou^x_D6Y^R&$E=ZJ>(89XdeTOW9nXh}3z@bWu^2A`aJw+}
z#W(-^)+@}0ATTm%1{aZW*hVliai*Kr#^v<!Sw93I_npi->=B@KK~L}e?{~YcukYPE
z*rPc1t5_$j7X#*aVjOC^Z4*jqXV;N!`$Jzx)_(?TY}ZUg_@trZ&at!cr);!KGTNfa
zK^5<d6k9a<V5COOk{GBlR@#FS+I{V*iY*%2Ma_u5j$?TA^~TuO$C-Nc|KWo8AS?*b
zCR8Z5gok5b^+=A<iknoibRwn7DK@DPT-~@yh5OFP`tZ4jpZ^0oW;qB`k8FDY0000<
KMNUMnLSTZQWUfd6
--- a/browser/metro/theme/jar.mn
+++ b/browser/metro/theme/jar.mn
@@ -5,16 +5,19 @@
 
 
 chrome.jar:
 % skin browser classic/1.0 %skin/
   skin/aboutPage.css                        (aboutPage.css)
   skin/aboutAddons.css                      (aboutAddons.css)
   skin/about.css                            (about.css)
 * skin/flyoutpanel.css                      (flyoutpanel.css)
+#ifdef MOZ_CRASHREPORTER
+* skin/crashprompt.css                      (crashprompt.css)
+#endif
 * skin/circularprogress.css                 (circularprogress.css)
 * skin/cssthrobber.css                      (cssthrobber.css)
 * skin/browser.css                          (browser.css)
 * skin/content.css                          (content.css)
   skin/config.css                           (config.css)
 * skin/platform.css                         (platform.css)
 * skin/tiles.css                            (tiles.css)
   skin/touchcontrols.css                    (touchcontrols.css)
@@ -98,16 +101,18 @@ chrome.jar:
   skin/images/arrowup-16.png                (images/arrowup-16.png)
   skin/images/arrowdown-16.png              (images/arrowdown-16.png)
   skin/images/arrowleftdark-16.png          (images/arrowleftdark-16.png)
   skin/images/arrowrightdark-16.png         (images/arrowrightdark-16.png)
   skin/images/arrowupdark-16.png            (images/arrowupdark-16.png)
   skin/images/arrowdowndark-16.png          (images/arrowdowndark-16.png)
   skin/images/popup-bg-hdpi.png             (images/popup-bg-hdpi.png)
   skin/images/popup-selected-item-hdpi.png  (images/popup-selected-item-hdpi.png)
+  skin/images/arrowbox-horiz-blue-filled.png (images/arrowbox-horiz-blue-filled.png)
+  skin/images/arrowbox-down-blue-filled.png (images/arrowbox-down-blue-filled.png)
   skin/images/favicon-default-32.png        (images/favicon-default-32.png)
   skin/images/errorpage-warning.png         (images/errorpage-warning.png)
   skin/images/errorpage-warning.png         (images/errorpage-warning.png)
   skin/images/errorpage-larry-white.png     (images/errorpage-larry-white.png)
   skin/images/errorpage-larry-black.png     (images/errorpage-larry-black.png)
   skin/images/alert-downloads-30.png        (images/alert-downloads-30.png)
   skin/images/search-glass-30.png           (images/search-glass-30.png)
   skin/images/play-hdpi.png                 (images/play-hdpi.png)
--- a/browser/themes/shared/devtools/toolbars.inc.css
+++ b/browser/themes/shared/devtools/toolbars.inc.css
@@ -457,23 +457,21 @@
 #command-button-splitconsole[checked=true] {
   -moz-image-region: rect(0px, 64px, 16px, 48px);
 }
 
 /* Tabs */
 
 .devtools-tabbar {
   -moz-appearance: none;
-  background-image: url("background-noise-toolbar.png"),
-                    linear-gradient(#303840, #2d3640);
-  border-color: #060a0d;
+  background: #252c33;
+  border-color: #434a51;
   border-style: solid;
   border-width: 1px 0;
-  box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset,
-              0 -1px 0 hsla(206,37%,4%,.1) inset;
+  box-shadow: 0 -2px 0 rgba(0,0,0,.25) inset;
   min-height: 32px;
   padding: 0;
 }
 
 #toolbox-tabs {
   margin: 0;
 }
 
@@ -485,25 +483,28 @@
 .devtools-tab {
   -moz-appearance: none;
   min-width: 32px;
   min-height: 32px;
   max-width: 127px;
   color: #b6babf;
   margin: 0;
   padding: 0;
-  background-image: linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1));
-  background-size: 1px 100%;
-  background-repeat: no-repeat;
-  background-position: left, right;
-  border-right: 1px solid hsla(206,37%,4%,.45);
+  border-left: 1px solid #42484f;
   -moz-box-align: center;
 }
 
+.devtools-tab:first-child {
+  border-left-width: 0;
+}
+
+.devtools-tab:last-child {
+  border-right: 1px solid #5a6169;
+}
+
 .devtools-tab > image {
   border: none;
   -moz-margin-end: 0;
   -moz-margin-start: 4px;
   opacity: 0.6;
   max-height: 16px;
   width: 16px; /* Prevents collapse during theme switching */
 }
@@ -521,72 +522,47 @@
 }
 
 .devtools-tab:active > image,
 .devtools-tab[selected=true] > label {
   opacity: 1;
 }
 
 .devtools-tab:hover {
-  background-image: linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(206,37%,4%,.1), hsla(206,37%,4%,.2));
-  background-size: 1px 100%,
-                   1px 100%,
-                   100%;
-  background-repeat: no-repeat,
-                     no-repeat,
-                     repeat-x;
-  background-position: left, right;
+  background-color: hsla(206,37%,4%,.2);
   color: #ced3d9;
 }
+
 .devtools-tab:hover:active {
-  background-color: hsla(206,37%,4%,.2);
+  background-color: hsla(206,37%,4%,.4);
   color: #f5f7fa;
 }
 
-.devtools-tab[selected=true] {
+#toolbox-tabs .devtools-tab[selected=true] {
   color: #f5f7fa;
-  background-image: radial-gradient(farthest-corner at center top, #9fdfff, hsla(200,100%,70%,.3)),
-                    radial-gradient(farthest-side at center top, hsla(200,100%,70%,.4), hsla(200,100%,70%,0)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(204,45%,98%,.02), hsla(204,45%,98%,.04)),
-                    linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.3));
-  background-size: 100% 1px,
-                   100% 5px,
-                   1px 100%,
-                   1px 100%,
-                   100%;
-  background-repeat: no-repeat,
-                     no-repeat,
-                     no-repeat,
-                     no-repeat,
-                     repeat-x;
-  background-position: top right, top left, left, right;
-  box-shadow: 1px -1px 0 hsla(206,37%,4%,.2) inset;
+  background-color: #1a4666;
+  border-width: 0;
+  box-shadow: 0 2px 0 #d7f1ff inset,
+              0 8px 3px -5px #2b82bf inset,
+              0 -2px 0 rgba(0,0,0,.2) inset;
+}
+
+.devtools-tab[selected=true]:not(:first-child) {
+  padding-left: 1px;
+}
+
+.devtools-tab[selected=true] + .devtools-tab {
+  border-left-width: 0;
+  padding-left: 1px;
 }
 
 .devtools-tab:not([selected=true]).highlighted {
   color: #f5f7fa;
-  background-image: radial-gradient(farthest-corner at center top, #c0ff40, hsla(80,100%,63%,.5) 70%, hsla(80,100%,63%,.3) 97%),
-                    radial-gradient(farthest-side at center top, hsla(80,100%,35%,.5), hsla(80,100%,35%,0)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(99,100%,14%,.2), hsla(99,100%,14%,.2));
-  background-size: 100% 1px,
-                   100% 5px,
-                   1px 100%,
-                   1px 100%,
-                   100%;
-  background-repeat: no-repeat,
-                     no-repeat,
-                     no-repeat,
-                     no-repeat,
-                     repeat-x;
-  background-position: top right, top left, left, right;
+  background-color: hsla(99,100%,14%,.2);
+  box-shadow: 0 2px 0 #7bc107 inset;
 }
 
 .devtools-tab:not(.highlighted) > .highlighted-icon,
 .devtools-tab[selected=true] > .highlighted-icon,
 .devtools-tab:not([selected=true]).highlighted > .default-icon {
   visibility: collapse;
 }
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -212,16 +212,18 @@
 #include "mozilla/css/Rule.h"
 #include "nsIDOMLocation.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsISecurityConsoleMessage.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "mozilla/dom/XPathEvaluator.h"
 #include "nsIDocumentEncoder.h"
 #include "nsIStructuredCloneContainer.h"
+#include "nsIMutableArray.h"
+#include "nsContentPermissionHelper.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
@@ -10724,27 +10726,21 @@ public:
   bool mUserInputOrChromeCaller;
 };
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsPointerLockPermissionRequest,
                              nsRunnable,
                              nsIContentPermissionRequest)
 
 NS_IMETHODIMP
-nsPointerLockPermissionRequest::GetType(nsACString& aType)
-{
-  aType = "pointerLock";
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPointerLockPermissionRequest::GetAccess(nsACString& aAccess)
-{
-  aAccess = "unused";
-  return NS_OK;
+nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes)
+{
+  return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"),
+                               NS_LITERAL_CSTRING("unused"),
+                               aTypes);
 }
 
 NS_IMETHODIMP
 nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
 {
   nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
   if (d) {
     NS_ADDREF(*aPrincipal = d->NodePrincipal());
--- a/content/media/webrtc/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MEDIAENGINE_H_
 #define MEDIAENGINE_H_
 
+#include "mozilla/RefPtr.h"
 #include "nsIDOMFile.h"
 #include "DOMMediaStream.h"
 #include "MediaStreamGraph.h"
 
 namespace mozilla {
 
 /**
  * Abstract interface for managing audio and video devices. Each platform
@@ -30,17 +31,17 @@ enum MediaEngineState {
 };
 
 // We only support 1 audio and 1 video track for now.
 enum {
   kVideoTrack = 1,
   kAudioTrack = 2
 };
 
-class MediaEngine
+class MediaEngine : public RefCounted<MediaEngine>
 {
 public:
   virtual ~MediaEngine() {}
 
   static const int DEFAULT_VIDEO_FPS = 30;
   static const int DEFAULT_VIDEO_MIN_FPS = 10;
   static const int DEFAULT_VIDEO_WIDTH = 640;
   static const int DEFAULT_VIDEO_HEIGHT = 480;
--- a/content/media/webrtc/MediaEngineWebRTC.cpp
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -96,17 +96,17 @@ MediaEngineWebRTC::EnumerateVideoDevices
     }
 
     nsRefPtr<MediaEngineWebRTCVideoSource> vSource;
     NS_ConvertUTF8toUTF16 uuid(cameraName);
     if (mVideoSources.Get(uuid, getter_AddRefs(vSource))) {
       // We've already seen this device, just append.
       aVSources->AppendElement(vSource.get());
     } else {
-      vSource = new MediaEngineWebRTCVideoSource(mCameraManager, i, mWindowId);
+      vSource = new MediaEngineWebRTCVideoSource(mCameraManager, i);
       mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
       aVSources->AppendElement(vSource);
     }
   }
 
   return;
 #else
   webrtc::ViEBase* ptrViEBase;
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -47,16 +47,17 @@
 #include "webrtc/video_engine/include/vie_capture.h"
 #ifdef MOZ_B2G_CAMERA
 #include "CameraPreviewMediaStream.h"
 #include "DOMCameraManager.h"
 #include "GonkCameraControl.h"
 #include "ImageContainer.h"
 #include "nsGlobalWindow.h"
 #include "prprf.h"
+#include "nsProxyRelease.h"
 #endif
 
 #include "NullTransport.h"
 
 namespace mozilla {
 
 #ifdef MOZ_B2G_CAMERA
 class CameraAllocateRunnable;
@@ -68,17 +69,17 @@ class GetCameraNameRunnable;
  *
  * On B2G platform, member data may accessed from different thread after construction:
  *
  * MediaThread:
  *   mState, mImage, mWidth, mHeight, mCapability, mPrefs, mDeviceName, mUniqueId, mInitDone,
  *   mSources, mImageContainer, mSources, mState, mImage, mLastCapture
  *
  * MainThread:
- *   mDOMCameraControl, mCaptureIndex, mCameraThread, mWindowId, mCameraManager,
+ *   mDOMCameraControl, mCaptureIndex, mCameraThread, mCameraManager,
  *   mNativeCameraControl, mPreviewStream, mState, mLastCapture, mWidth, mHeight
  *
  * Where mWidth, mHeight, mImage are protected by mMonitor
  *       mState, mLastCapture is protected by mCallbackMonitor
  * Other variable is accessed only from single thread
  */
 class MediaEngineWebRTCVideoSource : public MediaEngineVideoSource
                                    , public nsRunnable
@@ -91,21 +92,20 @@ class MediaEngineWebRTCVideoSource : pub
                                    , public CameraPreviewFrameCallback
 #else
                                    , public webrtc::ExternalRenderer
 #endif
 {
 public:
 #ifdef MOZ_B2G_CAMERA
   MediaEngineWebRTCVideoSource(nsDOMCameraManager* aCameraManager,
-    int aIndex, uint64_t aWindowId)
+    int aIndex)
     : mCameraManager(aCameraManager)
     , mNativeCameraControl(nullptr)
     , mPreviewStream(nullptr)
-    , mWindowId(aWindowId)
     , mCallbackMonitor("WebRTCCamera.CallbackMonitor")
     , mCaptureIndex(aIndex)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
     , mInitDone(false)
     , mInSnapshotMode(false)
     , mSnapshotPath(nullptr)
@@ -218,17 +218,16 @@ private:
   // We need raw pointer here since such DOM-object should not addref/release on
   // any thread other than main thread, but we must use this object for now. To
   // avoid any bad thing do to addref/release DOM-object on other thread, we use
   // raw-pointer for now.
   nsDOMCameraManager* mCameraManager;
   nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
   nsRefPtr<nsGonkCameraControl> mNativeCameraControl;
   nsRefPtr<DOMCameraPreview> mPreviewStream;
-  uint64_t mWindowId;
   mozilla::ReentrantMonitor mCallbackMonitor; // Monitor for camera callback handling
   nsRefPtr<nsIThread> mCameraThread;
   nsRefPtr<nsIDOMFile> mLastCapture;
 #else
   webrtc::VideoEngine* mVideoEngine; // Weak reference, don't free.
   webrtc::ViEBase* mViEBase;
   webrtc::ViECapture* mViECapture;
   webrtc::ViERender* mViERender;
@@ -347,25 +346,24 @@ private:
 
   NullTransport *mNullTransport;
 };
 
 class MediaEngineWebRTC : public MediaEngine
 {
 public:
 #ifdef MOZ_B2G_CAMERA
-  MediaEngineWebRTC(nsDOMCameraManager* aCameraManager, uint64_t aWindowId)
+  MediaEngineWebRTC(nsDOMCameraManager* aCameraManager)
     : mMutex("mozilla::MediaEngineWebRTC")
     , mVideoEngine(nullptr)
     , mVoiceEngine(nullptr)
     , mVideoEngineInit(false)
     , mAudioEngineInit(false)
+    , mHasTabVideoSource(false)
     , mCameraManager(aCameraManager)
-    , mWindowId(aWindowId)
-    , mHasTabVideoSource(false)
   {
     AsyncLatencyLogger::Get(true)->AddRef();
     mLoadMonitor = new LoadMonitor();
     mLoadMonitor->Init(mLoadMonitor);
   }
 #else
   MediaEngineWebRTC(MediaEnginePrefs &aPrefs);
 #endif
@@ -396,25 +394,26 @@ private:
   bool mHasTabVideoSource;
 
   // Store devices we've already seen in a hashtable for quick return.
   // Maps UUID to MediaEngineSource (one set for audio, one for video).
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCVideoSource > mVideoSources;
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
 
 #ifdef MOZ_B2G_CAMERA
+  // XXX Should use nsMainThreadPtrHandle/etc
+
   // MediaEngine hold this DOM object, and the MediaEngine is hold by Navigator
   // Their life time is always much longer than this object. Use a raw-pointer
   // here should be safe.
   // We need raw pointer here since such DOM-object should not addref/release on
   // any thread other than main thread, but we must use this object for now. To
   // avoid any bad thing do to addref/release DOM-object on other thread, we use
   // raw-pointer for now.
   nsDOMCameraManager* mCameraManager;
-  uint64_t mWindowId;
 #endif
 
    nsRefPtr<LoadMonitor> mLoadMonitor;
 };
 
 }
 
 #endif /* NSMEDIAENGINEWEBRTC_H_ */
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -307,17 +307,16 @@ MediaEngineWebRTCVideoSource::Deallocate
   }
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
 {
   LOG((__FUNCTION__));
-  int error = 0;
   if (!mInitDone || !aStream) {
     return NS_ERROR_FAILURE;
   }
 
   mSources.AppendElement(aStream);
 
   aStream->AddTrack(aID, USECS_PER_S, 0, new VideoSegment());
   aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
@@ -336,17 +335,17 @@ MediaEngineWebRTCVideoSource::Start(Sour
                                        &MediaEngineWebRTCVideoSource::StartImpl,
                                        mCapability));
   mCallbackMonitor.Wait();
   if (mState != kStarted) {
     return NS_ERROR_FAILURE;
   }
 #else
   mState = kStarted;
-  error = mViERender->AddRenderer(mCaptureIndex, webrtc::kVideoI420, (webrtc::ExternalRenderer*)this);
+  int error = mViERender->AddRenderer(mCaptureIndex, webrtc::kVideoI420, (webrtc::ExternalRenderer*)this);
   if (error == -1) {
     return NS_ERROR_FAILURE;
   }
 
   error = mViERender->StartRender(mCaptureIndex);
   if (error == -1) {
     return NS_ERROR_FAILURE;
   }
@@ -487,30 +486,28 @@ MediaEngineWebRTCVideoSource::Shutdown()
 
 #ifdef MOZ_B2G_CAMERA
 
 // All these functions must be run on MainThread!
 void
 MediaEngineWebRTCVideoSource::AllocImpl() {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mDOMCameraControl = new nsDOMCameraControl(mCaptureIndex,
-                                             mCameraThread,
-                                             this,
-                                             this,
-                                             nsGlobalWindow::GetInnerWindowWithId(mWindowId));
-  mCameraManager->Register(mDOMCameraControl);
+  ErrorResult rv;
+  mDOMCameraControl = mCameraManager->GetCameraControl(mCaptureIndex,
+                                                       this, this, rv);
 }
 
 void
 MediaEngineWebRTCVideoSource::DeallocImpl() {
   MOZ_ASSERT(NS_IsMainThread());
 
   mNativeCameraControl->ReleaseHardware(this, this);
   mNativeCameraControl = nullptr;
+  mDOMCameraControl = nullptr;
 }
 
 void
 MediaEngineWebRTCVideoSource::StartImpl(webrtc::CaptureCapability aCapability) {
   MOZ_ASSERT(NS_IsMainThread());
 
   idl::CameraSize size;
   size.width = aCapability.width;
--- a/dom/apps/src/PermissionsTable.jsm
+++ b/dom/apps/src/PermissionsTable.jsm
@@ -318,16 +318,21 @@ this.PermissionsTable =  { geolocation: 
                              privileged: ALLOW_ACTION,
                              certified: ALLOW_ACTION
                            },
                            "downloads": {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
+                           "video-capture": {
+                             app: PROMPT_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: PROMPT_ACTION
+                           },
                          };
 
 /**
  * Append access modes to the permission name as suffixes.
  *   e.g. permission name 'contacts' with ['read', 'write'] =
  *   ['contacts-read', contacts-write']
  * @param string aPermName
  * @param array aAccess
--- a/dom/base/ConsoleAPI.js
+++ b/dom/base/ConsoleAPI.js
@@ -127,32 +127,39 @@ ConsoleAPI.prototype = {
         let consoleEvent = {
           action: "profileEnd",
           arguments: arguments
         };
         consoleEvent.wrappedJSObject = consoleEvent;
         Services.obs.notifyObservers(consoleEvent, "console-api-profiler",
                                      null);  
       },
+      assert: function CA_assert() {
+        let args = Array.prototype.slice.call(arguments);
+        if(!args.shift()) {
+          self.queueCall("assert", args);
+        }
+      },
       __exposedProps__: {
         log: "r",
         info: "r",
         warn: "r",
         error: "r",
         exception: "r",
         debug: "r",
         trace: "r",
         dir: "r",
         group: "r",
         groupCollapsed: "r",
         groupEnd: "r",
         time: "r",
         timeEnd: "r",
         profile: "r",
-        profileEnd: "r"
+        profileEnd: "r",
+        assert: "r"
       }
     };
 
     // We need to return an actual content object here, instead of a wrapped
     // chrome object. This allows things like console.log.bind() to work.
     let contentObj = Cu.createObjectIn(aWindow);
     function genPropDesc(fun) {
       return { enumerable: true, configurable: true, writable: true,
@@ -169,16 +176,17 @@ ConsoleAPI.prototype = {
       dir: genPropDesc('dir'),
       group: genPropDesc('group'),
       groupCollapsed: genPropDesc('groupCollapsed'),
       groupEnd: genPropDesc('groupEnd'),
       time: genPropDesc('time'),
       timeEnd: genPropDesc('timeEnd'),
       profile: genPropDesc('profile'),
       profileEnd: genPropDesc('profileEnd'),
+      assert: genPropDesc('assert'),
       __noSuchMethod__: { enumerable: true, configurable: true, writable: true,
                           value: function() {} },
       __mozillaConsole__: { value: true }
     };
 
     Object.defineProperties(contentObj, properties);
     Cu.makeObjectPropsNormal(contentObj);
 
@@ -290,16 +298,17 @@ ConsoleAPI.prototype = {
 
     switch (method) {
       case "log":
       case "info":
       case "warn":
       case "error":
       case "exception":
       case "debug":
+      case "assert":
         consoleEvent.arguments = this.processArguments(args);
         break;
       case "trace":
         consoleEvent.stacktrace = meta.stack;
         break;
       case "group":
       case "groupCollapsed":
       case "groupEnd":
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -1,49 +1,183 @@
 /* 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 MOZ_WIDGET_GONK
 #include "GonkPermission.h"
 #include "mozilla/dom/ContentParent.h"
 #endif // MOZ_WIDGET_GONK
-#include "nsContentPermissionHelper.h"
-#include "nsIContentPermissionPrompt.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMElement.h"
 #include "nsIPrincipal.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/PContentPermission.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/unused.h"
 #include "nsComponentManagerUtils.h"
+#include "nsArrayUtils.h"
+#include "nsIMutableArray.h"
+#include "nsContentPermissionHelper.h"
 
 using mozilla::unused;          // <snicker>
 using namespace mozilla::dom;
 using namespace mozilla;
 
+namespace mozilla {
+namespace dom {
+
+class ContentPermissionRequestParent : public PContentPermissionRequestParent
+{
+ public:
+  ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
+                                 Element* element,
+                                 const IPC::Principal& principal);
+  virtual ~ContentPermissionRequestParent();
+
+  bool IsBeingDestroyed();
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<Element> mElement;
+  nsCOMPtr<nsContentPermissionRequestProxy> mProxy;
+  nsTArray<PermissionRequest> mRequests;
+
+ private:
+  virtual bool Recvprompt();
+  virtual void ActorDestroy(ActorDestroyReason why);
+};
+
+ContentPermissionRequestParent::ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
+                                                               Element* aElement,
+                                                               const IPC::Principal& aPrincipal)
+{
+  MOZ_COUNT_CTOR(ContentPermissionRequestParent);
+
+  mPrincipal = aPrincipal;
+  mElement   = aElement;
+  mRequests  = aRequests;
+}
+
+ContentPermissionRequestParent::~ContentPermissionRequestParent()
+{
+  MOZ_COUNT_DTOR(ContentPermissionRequestParent);
+}
+
+bool
+ContentPermissionRequestParent::Recvprompt()
+{
+  mProxy = new nsContentPermissionRequestProxy();
+  NS_ASSERTION(mProxy, "Alloc of request proxy failed");
+  if (NS_FAILED(mProxy->Init(mRequests, this))) {
+    mProxy->Cancel();
+  }
+  return true;
+}
+
+void
+ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why)
+{
+  if (mProxy) {
+    mProxy->OnParentDestroyed();
+  }
+}
+
+bool
+ContentPermissionRequestParent::IsBeingDestroyed()
+{
+  // When TabParent::Destroy() is called, we are being destroyed. It's unsafe
+  // to send out any message now.
+  TabParent* tabParent = static_cast<TabParent*>(Manager());
+  return tabParent->IsDestroyed();
+}
+
+NS_IMPL_ISUPPORTS1(ContentPermissionType, nsIContentPermissionType)
+
+ContentPermissionType::ContentPermissionType(const nsACString& aType,
+                                             const nsACString& aAccess)
+{
+  mType = aType;
+  mAccess = aAccess;
+}
+
+ContentPermissionType::~ContentPermissionType()
+{
+}
+
+NS_IMETHODIMP
+ContentPermissionType::GetType(nsACString& aType)
+{
+  aType = mType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentPermissionType::GetAccess(nsACString& aAccess)
+{
+  aAccess = mAccess;
+  return NS_OK;
+}
+
+uint32_t
+ConvertPermissionRequestToArray(nsTArray<PermissionRequest>& aSrcArray,
+                                nsIMutableArray* aDesArray)
+{
+  uint32_t len = aSrcArray.Length();
+  for (uint32_t i = 0; i < len; i++) {
+    nsRefPtr<ContentPermissionType> cpt =
+      new ContentPermissionType(aSrcArray[i].type(), aSrcArray[i].access());
+    aDesArray->AppendElement(cpt, false);
+  }
+  return len;
+}
+
+nsresult
+CreatePermissionArray(const nsACString& aType,
+                      const nsACString& aAccess,
+                      nsIArray** aTypesArray)
+{
+  nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  nsRefPtr<ContentPermissionType> permType = new ContentPermissionType(aType,
+                                                                       aAccess);
+  types->AppendElement(permType, false);
+  types.forget(aTypesArray);
+
+  return NS_OK;
+}
+
+PContentPermissionRequestParent*
+CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
+                                     Element* element,
+                                     const IPC::Principal& principal)
+{
+  return new ContentPermissionRequestParent(aRequests, element, principal);
+}
+
+} // namespace dom
+} // namespace mozilla
+
 nsContentPermissionRequestProxy::nsContentPermissionRequestProxy()
 {
   MOZ_COUNT_CTOR(nsContentPermissionRequestProxy);
 }
 
 nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy()
 {
   MOZ_COUNT_DTOR(nsContentPermissionRequestProxy);
 }
 
 nsresult
-nsContentPermissionRequestProxy::Init(const nsACString & type,
-                                      const nsACString & access,
+nsContentPermissionRequestProxy::Init(const nsTArray<PermissionRequest>& requests,
                                       ContentPermissionRequestParent* parent)
 {
   NS_ASSERTION(parent, "null parent");
   mParent = parent;
-  mType   = type;
-  mAccess = access;
+  mPermissionRequests = requests;
 
   nsCOMPtr<nsIContentPermissionPrompt> prompt = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
   if (!prompt) {
     return NS_ERROR_FAILURE;
   }
 
   prompt->Prompt(this);
   return NS_OK;
@@ -53,27 +187,24 @@ void
 nsContentPermissionRequestProxy::OnParentDestroyed()
 {
   mParent = nullptr;
 }
 
 NS_IMPL_ISUPPORTS1(nsContentPermissionRequestProxy, nsIContentPermissionRequest)
 
 NS_IMETHODIMP
-nsContentPermissionRequestProxy::GetType(nsACString & aType)
+nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes)
 {
-  aType = mType;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContentPermissionRequestProxy::GetAccess(nsACString & aAccess)
-{
-  aAccess = mAccess;
-  return NS_OK;
+  nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (ConvertPermissionRequestToArray(mPermissionRequests, types)) {
+    types.forget(aTypes);
+    return NS_OK;
+  }
+  return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsContentPermissionRequestProxy::GetWindow(nsIDOMWindow * *aRequestingWindow)
 {
   NS_ENSURE_ARG_POINTER(aRequestingWindow);
   *aRequestingWindow = nullptr; // ipc doesn't have a window
   return NS_OK;
@@ -131,71 +262,27 @@ nsContentPermissionRequestProxy::Allow()
 
   // Don't send out the delete message when the managing protocol (PBrowser) is
   // being destroyed and PContentPermissionRequest will soon be.
   if (mParent->IsBeingDestroyed()) {
     return NS_ERROR_FAILURE;
   }
 
 #ifdef MOZ_WIDGET_GONK
-  if (mType.Equals("audio-capture")) {
-    GonkPermissionService::GetInstance()->addGrantInfo(
-      "android.permission.RECORD_AUDIO",
-      static_cast<TabParent*>(mParent->Manager())->Manager()->Pid());
+  uint32_t len = mPermissionRequests.Length();
+  for (uint32_t i = 0; i < len; i++) {
+    if (mPermissionRequests[i].type().Equals("audio-capture")) {
+      GonkPermissionService::GetInstance()->addGrantInfo(
+        "android.permission.RECORD_AUDIO",
+        static_cast<TabParent*>(mParent->Manager())->Manager()->Pid());
+    }
+    if (mPermissionRequests[i].type().Equals("video-capture")) {
+      GonkPermissionService::GetInstance()->addGrantInfo(
+        "android.permission.CAMERA",
+        static_cast<TabParent*>(mParent->Manager())->Manager()->Pid());
+    }
   }
 #endif
 
   unused << ContentPermissionRequestParent::Send__delete__(mParent, true);
   mParent = nullptr;
   return NS_OK;
 }
-
-namespace mozilla {
-namespace dom {
-
-ContentPermissionRequestParent::ContentPermissionRequestParent(const nsACString& aType,
-                                                               const nsACString& aAccess,
-                                                               Element* aElement,
-                                                               const IPC::Principal& aPrincipal)
-{
-  MOZ_COUNT_CTOR(ContentPermissionRequestParent);
-
-  mPrincipal = aPrincipal;
-  mElement   = aElement;
-  mType      = aType;
-  mAccess    = aAccess;
-}
-
-ContentPermissionRequestParent::~ContentPermissionRequestParent()
-{
-  MOZ_COUNT_DTOR(ContentPermissionRequestParent);
-}
-
-bool
-ContentPermissionRequestParent::Recvprompt()
-{
-  mProxy = new nsContentPermissionRequestProxy();
-  NS_ASSERTION(mProxy, "Alloc of request proxy failed");
-  if (NS_FAILED(mProxy->Init(mType, mAccess, this))) {
-    mProxy->Cancel();
-  }
-  return true;
-}
-
-void
-ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why)
-{
-  if (mProxy) {
-    mProxy->OnParentDestroyed();
-  }
-}
-
-bool
-ContentPermissionRequestParent::IsBeingDestroyed()
-{
-  // When TabParent::Destroy() is called, we are being destroyed. It's unsafe
-  // to send out any message now.
-  TabParent* tabParent = static_cast<TabParent*>(Manager());
-  return tabParent->IsDestroyed();
-}
-
-} // namespace dom
-} // namespace mozilla
--- a/dom/base/nsContentPermissionHelper.h
+++ b/dom/base/nsContentPermissionHelper.h
@@ -1,65 +1,80 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsContentPermissionHelper_h
 #define nsContentPermissionHelper_h
 
 #include "nsIContentPermissionPrompt.h"
-#include "nsString.h"
-
-#include "mozilla/dom/PermissionMessageUtils.h"
-#include "mozilla/dom/PContentPermissionRequestParent.h"
+#include "nsTArray.h"
+#include "nsIMutableArray.h"
 
 class nsContentPermissionRequestProxy;
 
+// Forward declare IPC::Principal here which is defined in
+// PermissionMessageUtils.h. Include this file will transitively includes
+// "windows.h" and it defines
+//   #define CreateEvent CreateEventW
+//   #define LoadImage LoadImageW
+// That will mess up windows build.
+namespace IPC {
+class Principal;
+}
+
 namespace mozilla {
 namespace dom {
 
 class Element;
+class PermissionRequest;
+class ContentPermissionRequestParent;
+class PContentPermissionRequestParent;
 
-class ContentPermissionRequestParent : public PContentPermissionRequestParent
+class ContentPermissionType : public nsIContentPermissionType
 {
- public:
-  ContentPermissionRequestParent(const nsACString& type,
-                                 const nsACString& access,
-                                 Element* element,
-                                 const IPC::Principal& principal);
-  virtual ~ContentPermissionRequestParent();
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICONTENTPERMISSIONTYPE
 
-  bool IsBeingDestroyed();
+  ContentPermissionType(const nsACString& aType, const nsACString& aAccess);
+  virtual ~ContentPermissionType();
 
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsCOMPtr<Element> mElement;
-  nsCOMPtr<nsContentPermissionRequestProxy> mProxy;
+protected:
   nsCString mType;
   nsCString mAccess;
+};
 
- private:
-  virtual bool Recvprompt();
-  virtual void ActorDestroy(ActorDestroyReason why);
-};
+uint32_t ConvertPermissionRequestToArray(nsTArray<PermissionRequest>& aSrcArray,
+                                         nsIMutableArray* aDesArray);
+
+nsresult CreatePermissionArray(const nsACString& aType,
+                               const nsACString& aAccess,
+                               nsIArray** aTypesArray);
+
+PContentPermissionRequestParent*
+CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
+                                     Element* element,
+                                     const IPC::Principal& principal);
 
 } // namespace dom
 } // namespace mozilla
 
 class nsContentPermissionRequestProxy : public nsIContentPermissionRequest
 {
  public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICONTENTPERMISSIONREQUEST
+
   nsContentPermissionRequestProxy();
   virtual ~nsContentPermissionRequestProxy();
 
-  nsresult Init(const nsACString& type, const nsACString& access, mozilla::dom::ContentPermissionRequestParent* parent);
+  nsresult Init(const nsTArray<mozilla::dom::PermissionRequest>& requests,
+                mozilla::dom::ContentPermissionRequestParent* parent);
   void OnParentDestroyed();
 
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSICONTENTPERMISSIONREQUEST
-
  private:
   // Non-owning pointer to the ContentPermissionRequestParent object which owns this proxy.
   mozilla::dom::ContentPermissionRequestParent* mParent;
-  nsCString mType;
-  nsCString mAccess;
+  nsTArray<mozilla::dom::PermissionRequest> mPermissionRequests;
 };
+
 #endif // nsContentPermissionHelper_h
-
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -100,43 +100,56 @@ nsDOMCameraManager::CreateInstance(nsPID
     new nsDOMCameraManager(aWindow);
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   obs->AddObserver(cameraManager, "xpcom-shutdown", true);
 
   return cameraManager.forget();
 }
 
+nsDOMCameraControl*
+nsDOMCameraManager::GetCameraControl(uint32_t aDeviceNum,
+                                     nsICameraGetCameraCallback* onSuccess,
+                                     nsICameraErrorCallback* onError,
+                                     ErrorResult& aRv)
+{
+  aRv = NS_OK;
+
+  // reuse the same camera thread to conserve resources
+  if (!mCameraThread) {
+    aRv = NS_NewThread(getter_AddRefs(mCameraThread));
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+  }
+
+  // Creating this object will trigger the onSuccess handler
+  nsDOMCameraControl* cameraControl =  new nsDOMCameraControl(aDeviceNum, mCameraThread,
+                                                              onSuccess, onError, mWindow);
+  if (cameraControl) {
+    Register(cameraControl);
+  }
+  return cameraControl;
+}
+
 void
 nsDOMCameraManager::GetCamera(const CameraSelector& aOptions,
                               nsICameraGetCameraCallback* onSuccess,
                               const Optional<nsICameraErrorCallback*>& onError,
                               ErrorResult& aRv)
 {
   uint32_t cameraId = 0;  // back (or forward-facing) camera by default
   if (aOptions.mCamera.EqualsLiteral("front")) {
     cameraId = 1;
   }
 
-  // reuse the same camera thread to conserve resources
-  if (!mCameraThread) {
-    aRv = NS_NewThread(getter_AddRefs(mCameraThread));
-    if (aRv.Failed()) {
-      return;
-    }
-  }
-
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
-  // Creating this object will trigger the onSuccess handler
   nsRefPtr<nsDOMCameraControl> cameraControl =
-    new nsDOMCameraControl(cameraId, mCameraThread,
-                           onSuccess, onError.WasPassed() ? onError.Value() : nullptr, mWindow);
-
-  Register(cameraControl);
+    GetCameraControl(cameraId, onSuccess, onError.WasPassed() ? onError.Value() : nullptr, aRv);
 }
 
 void
 nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl)
 {
   DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%llx\n", aDOMCameraControl, mWindowId);
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/camera/DOMCameraManager.h
+++ b/dom/camera/DOMCameraManager.h
@@ -44,16 +44,21 @@ public:
                                                          nsIObserver)
   NS_DECL_NSIOBSERVER
 
   static bool CheckPermission(nsPIDOMWindow* aWindow);
   static already_AddRefed<nsDOMCameraManager>
     CreateInstance(nsPIDOMWindow* aWindow);
   static bool IsWindowStillActive(uint64_t aWindowId);
 
+  // Build us an nsDOMCameraControl
+  mozilla::nsDOMCameraControl* GetCameraControl(uint32_t aDeviceNum,
+                                                nsICameraGetCameraCallback* onSuccess,
+                                                nsICameraErrorCallback* onError,
+                                                mozilla::ErrorResult& aRv);
   void Register(mozilla::nsDOMCameraControl* aDOMCameraControl);
   void OnNavigation(uint64_t aWindowId);
 
   nsresult GetNumberOfCameras(int32_t& aDeviceCount);
   nsresult GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName);
 
   // WebIDL
   void GetCamera(const mozilla::dom::CameraSelector& aOptions,
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -44,16 +44,17 @@
 #include "nsIObserverService.h"
 #include "GeneratedEvents.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIPermissionManager.h"
 #include "nsIStringBundle.h"
 #include "nsIDocument.h"
 #include <algorithm>
+#include "nsContentPermissionHelper.h"
 
 #include "mozilla/dom/DeviceStorageBinding.h"
 
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolume.h"
@@ -1728,27 +1729,24 @@ nsDOMDeviceStorageCursor::~nsDOMDeviceSt
 
 void
 nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType)
 {
   aType = mFile->mStorageType;
 }
 
 NS_IMETHODIMP
-nsDOMDeviceStorageCursor::GetType(nsACString & aType)
+nsDOMDeviceStorageCursor::GetTypes(nsIArray** aTypes)
 {
-  return DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType,
-                                                        aType);
-}
-
-NS_IMETHODIMP
-nsDOMDeviceStorageCursor::GetAccess(nsACString & aAccess)
-{
-  aAccess = NS_LITERAL_CSTRING("read");
-  return NS_OK;
+  nsCString type;
+  nsresult rv =
+    DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return CreatePermissionArray(type, NS_LITERAL_CSTRING("read"), aTypes);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorageCursor::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
 {
   NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
   return NS_OK;
 }
@@ -2246,51 +2244,50 @@ public:
         return rv;
       }
       nsCString access;
       rv = DeviceStorageTypeChecker::GetAccessForRequest(
         DeviceStorageRequestType(mRequestType), access);
       if (NS_FAILED(rv)) {
         return rv;
       }
+      nsTArray<PermissionRequest> permArray;
+      permArray.AppendElement(PermissionRequest(type, access));
       child->SendPContentPermissionRequestConstructor(
-        this, type, access, IPC::Principal(mPrincipal));
+        this, permArray, IPC::Principal(mPrincipal));
 
       Sendprompt();
       return NS_OK;
     }
 
     nsCOMPtr<nsIContentPermissionPrompt> prompt
       = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
     if (prompt) {
       prompt->Prompt(this);
     }
     return NS_OK;
   }
 
-  NS_IMETHOD GetType(nsACString & aType)
+  NS_IMETHODIMP GetTypes(nsIArray** aTypes)
   {
     nsCString type;
-    nsresult rv
-      = DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType,
-                                                       aType);
+    nsresult rv =
+      DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
     if (NS_FAILED(rv)) {
       return rv;
     }
-    return NS_OK;
-  }
-
-  NS_IMETHOD GetAccess(nsACString & aAccess)
-  {
-    nsresult rv = DeviceStorageTypeChecker::GetAccessForRequest(
-      DeviceStorageRequestType(mRequestType), aAccess);
+
+    nsCString access;
+    rv = DeviceStorageTypeChecker::GetAccessForRequest(
+      DeviceStorageRequestType(mRequestType), access);
     if (NS_FAILED(rv)) {
       return rv;
     }
-    return NS_OK;
+
+    return CreatePermissionArray(type, access, aTypes);
   }
 
   NS_IMETHOD GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
   {
     NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
     return NS_OK;
   }
 
@@ -3294,18 +3291,20 @@ nsDOMDeviceStorage::EnumerateInternal(co
     // Corresponding release occurs in DeallocPContentPermissionRequest.
     r->AddRef();
 
     nsCString type;
     aRv = DeviceStorageTypeChecker::GetPermissionForType(mStorageType, type);
     if (aRv.Failed()) {
       return nullptr;
     }
-    child->SendPContentPermissionRequestConstructor(r, type,
-                                                    NS_LITERAL_CSTRING("read"),
+    nsTArray<PermissionRequest> permArray;
+    permArray.AppendElement(PermissionRequest(type, NS_LITERAL_CSTRING("read")));
+    child->SendPContentPermissionRequestConstructor(r,
+                                                    permArray,
                                                     IPC::Principal(mPrincipal));
 
     r->Sendprompt();
 
     return cursor.forget();
   }
 
   nsCOMPtr<nsIContentPermissionPrompt> prompt
--- a/dom/interfaces/base/nsIContentPermissionPrompt.idl
+++ b/dom/interfaces/base/nsIContentPermissionPrompt.idl
@@ -2,38 +2,50 @@
  * 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 "nsISupports.idl"
 
 interface nsIPrincipal;
 interface nsIDOMWindow;
 interface nsIDOMElement;
+interface nsIArray;
 
 /**
- * Interface allows access to a content to request
- * permission to perform a privileged operation such as
- * geolocation.
+ *  Interface provides the request type and its access.
  */
-[scriptable, uuid(1de67000-2de8-11e2-81c1-0800200c9a66)]
-interface nsIContentPermissionRequest : nsISupports {
-
+[scriptable, builtinclass, uuid(384b6cc4-a66b-4bea-98e0-eb10562a9ba4)]
+interface nsIContentPermissionType : nsISupports {
   /**
    *  The type of the permission request, such as
    *  "geolocation".
    */
   readonly attribute ACString type;
 
   /**
    *  The access of the permission request, such as
    *  "read".
    */
   readonly attribute ACString access;
+};
 
+/**
+ * Interface allows access to a content to request
+ * permission to perform a privileged operation such as
+ * geolocation.
+ */
+[scriptable, uuid(69a39d88-d1c4-4ba9-9b19-bafc7a1bb783)]
+interface nsIContentPermissionRequest : nsISupports {
   /**
+   *  The array will include the request types. Elements of this array are
+   *  nsIContentPermissionType object.
+   */
+  readonly attribute nsIArray types;
+
+  /*
    *  The principal of the permission request.
    */
   readonly attribute nsIPrincipal principal;
 
   /**
    *  The window or element that the permission request was
    *  originated in.  Typically the element will be non-null
    *  in when using out of process content.  window or
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1343,46 +1343,52 @@ OnFinishNuwaPreparation ()
 static void
 PreloadSlowThings()
 {
     // This fetches and creates all the built-in stylesheets.
     nsLayoutStylesheetCache::UserContentSheet();
 
     TabChild::PreloadSlowThings();
 
-#ifdef MOZ_NUWA_PROCESS
-    // After preload of slow things, start freezing threads.
-    if (IsNuwaProcess()) {
-        // Perform GC before freezing the Nuwa process to reduce memory usage.
-        ContentChild::GetSingleton()->RecvGarbageCollect();
-
-        MessageLoop::current()->
-                PostTask(FROM_HERE,
-                         NewRunnableFunction(OnFinishNuwaPreparation));
-    }
-#endif
 }
 
 bool
 ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID,
                           const nsCString& name, const nsCString& UAName)
 {
     mAppInfo.version.Assign(version);
     mAppInfo.buildID.Assign(buildID);
     mAppInfo.name.Assign(name);
     mAppInfo.UAName.Assign(UAName);
+
+    if (!Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) {
+        return true;
+    }
+
     // If we're part of the mozbrowser machinery, go ahead and start
     // preloading things.  We can only do this for mozbrowser because
     // PreloadSlowThings() may set the docshell of the first TabChild
     // inactive, and we can only safely restore it to active from
     // BrowserElementChild.js.
-    if ((mIsForApp || mIsForBrowser) &&
-        Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) {
+    if ((mIsForApp || mIsForBrowser)
+#ifdef MOZ_NUWA_PROCESS
+        && !IsNuwaProcess()
+#endif
+       ) {
         PreloadSlowThings();
     }
+
+#ifdef MOZ_NUWA_PROCESS
+    if (IsNuwaProcess()) {
+        ContentChild::GetSingleton()->RecvGarbageCollect();
+        MessageLoop::current()->PostTask(
+            FROM_HERE, NewRunnableFunction(OnFinishNuwaPreparation));
+    }
+#endif
+
     return true;
 }
 
 bool
 ContentChild::RecvLastPrivateDocShellDestroyed()
 {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1244,103 +1244,23 @@ ContentParent::ContentParent(mozIApplica
     std::vector<std::string> extraArgs;
     if (aIsNuwaProcess) {
         extraArgs.push_back("-nuwa");
     }
     mSubprocess->LaunchAndWaitForProcessHandle(extraArgs);
 
     Open(mSubprocess->GetChannel(), mSubprocess->GetOwnedChildProcessHandle());
 
-    // Set the subprocess's priority.  We do this early on because we're likely
-    // /lowering/ the process's CPU and memory priority, which it has inherited
-    // from this process.
-    //
-    // This call can cause us to send IPC messages to the child process, so it
-    // must come after the Open() call above.
-    ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);
-
-    // NB: internally, this will send an IPC message to the child
-    // process to get it to create the CompositorChild.  This
-    // message goes through the regular IPC queue for this
-    // channel, so delivery will happen-before any other messages
-    // we send.  The CompositorChild must be created before any
-    // PBrowsers are created, because they rely on the Compositor
-    // already being around.  (Creation is async, so can't happen
-    // on demand.)
-    bool useOffMainThreadCompositing = !!CompositorParent::CompositorLoop();
-    if (useOffMainThreadCompositing) {
-        DebugOnly<bool> opened = PCompositor::Open(this);
-        MOZ_ASSERT(opened);
-
-        if (Preferences::GetBool("layers.async-video.enabled",false)) {
-            opened = PImageBridge::Open(this);
-            MOZ_ASSERT(opened);
-        }
-    }
-
-    nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
-    nsChromeRegistryChrome* chromeRegistry =
-        static_cast<nsChromeRegistryChrome*>(registrySvc.get());
-    chromeRegistry->SendRegisteredChrome(this);
-    mMessageManager = nsFrameMessageManager::NewProcessMessageManager(this);
-
-    if (gAppData) {
-        nsCString version(gAppData->version);
-        nsCString buildID(gAppData->buildID);
-        nsCString name(gAppData->name);
-        nsCString UAName(gAppData->UAName);
-
-        // Sending all information to content process.
-        unused << SendAppInfo(version, buildID, name, UAName);
-    }
-
-    nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
-    if (sheetService) {
-        // This looks like a lot of work, but in a normal browser session we just
-        // send two loads.
-
-        nsCOMArray<nsIStyleSheet>& agentSheets = *sheetService->AgentStyleSheets();
-        for (uint32_t i = 0; i < agentSheets.Length(); i++) {
-            URIParams uri;
-            SerializeURI(agentSheets[i]->GetSheetURI(), uri);
-            unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET);
-        }
-
-        nsCOMArray<nsIStyleSheet>& userSheets = *sheetService->UserStyleSheets();
-        for (uint32_t i = 0; i < userSheets.Length(); i++) {
-            URIParams uri;
-            SerializeURI(userSheets[i]->GetSheetURI(), uri);
-            unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET);
-        }
-
-        nsCOMArray<nsIStyleSheet>& authorSheets = *sheetService->AuthorStyleSheets();
-        for (uint32_t i = 0; i < authorSheets.Length(); i++) {
-            URIParams uri;
-            SerializeURI(authorSheets[i]->GetSheetURI(), uri);
-            unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET);
-        }
-    }
-
-#ifdef MOZ_CONTENT_SANDBOX
-    // Bug 921817.  We enable the sandbox in RecvSetProcessPrivileges,
-    // which is where a preallocated process drops unnecessary privileges,
-    // but a non-preallocated process will already have changed its
-    // uid/gid/etc immediately after forking.  Thus, we send this message,
-    // which is otherwise a no-op, to sandbox it at an appropriate point
-    // during startup.
-    if (aOSPrivileges != base::PRIVILEGES_INHERIT) {
-        if (!SendSetProcessPrivileges(base::PRIVILEGES_INHERIT)) {
-            KillHard();
-        }
-    }
-#endif
+    InitInternal(aInitialPriority,
+                 true, /* Setup off-main thread compositing */
+                 true  /* Send registered chrome */);
 }
 
 #ifdef MOZ_NUWA_PROCESS
-static const FileDescriptor*
+static const mozilla::ipc::FileDescriptor*
 FindFdProtocolFdMapping(const nsTArray<ProtocolFdMapping>& aFds,
                         ProtocolId aProtoId)
 {
     for (unsigned int i = 0; i < aFds.Length(); i++) {
         if (aFds[i].protocolId() == aProtoId) {
             return &aFds[i].fd();
         }
     }
@@ -1396,18 +1316,19 @@ ContentParent::ContentParent(ContentPare
     // memory priority, which it has inherited from this process.
     ProcessPriority priority;
     if (IsPreallocated()) {
         priority = PROCESS_PRIORITY_BACKGROUND;
     } else {
         priority = PROCESS_PRIORITY_FOREGROUND;
     }
 
-    ProcessPriorityManager::SetProcessPriority(this, priority);
-    mMessageManager = nsFrameMessageManager::NewProcessMessageManager(this);
+    InitInternal(priority,
+                 false, /* Setup Off-main thread compositing */
+                 false  /* Send registered chrome */);
 }
 #endif  // MOZ_NUWA_PROCESS
 
 ContentParent::~ContentParent()
 {
     if (mForceKillTask) {
         mForceKillTask->Cancel();
     }
@@ -1427,16 +1348,111 @@ ContentParent::~ContentParent()
         // nullptr.  But it could be that we created another ContentParent for
         // this app after we did this->ActorDestroy(), so the right check is
         // that sAppContentParents->Get(mAppManifestURL) != this.
         MOZ_ASSERT(!sAppContentParents ||
                    sAppContentParents->Get(mAppManifestURL) != this);
     }
 }
 
+void
+ContentParent::InitInternal(ProcessPriority aInitialPriority,
+                            bool aSetupOffMainThreadCompositing,
+                            bool aSendRegisteredChrome)
+{
+    // Set the subprocess's priority.  We do this early on because we're likely
+    // /lowering/ the process's CPU and memory priority, which it has inherited
+    // from this process.
+    //
+    // This call can cause us to send IPC messages to the child process, so it
+    // must come after the Open() call above.
+    ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);
+
+    if (aSetupOffMainThreadCompositing) {
+        // NB: internally, this will send an IPC message to the child
+        // process to get it to create the CompositorChild.  This
+        // message goes through the regular IPC queue for this
+        // channel, so delivery will happen-before any other messages
+        // we send.  The CompositorChild must be created before any
+        // PBrowsers are created, because they rely on the Compositor
+        // already being around.  (Creation is async, so can't happen
+        // on demand.)
+        bool useOffMainThreadCompositing = !!CompositorParent::CompositorLoop();
+        if (useOffMainThreadCompositing) {
+            DebugOnly<bool> opened = PCompositor::Open(this);
+            MOZ_ASSERT(opened);
+
+            if (Preferences::GetBool("layers.async-video.enabled",false)) {
+                opened = PImageBridge::Open(this);
+                MOZ_ASSERT(opened);
+            }
+        }
+    }
+
+    if (aSendRegisteredChrome) {
+        nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
+        nsChromeRegistryChrome* chromeRegistry =
+            static_cast<nsChromeRegistryChrome*>(registrySvc.get());
+        chromeRegistry->SendRegisteredChrome(this);
+    }
+
+    mMessageManager = nsFrameMessageManager::NewProcessMessageManager(this);
+
+    if (gAppData) {
+        nsCString version(gAppData->version);
+        nsCString buildID(gAppData->buildID);
+        nsCString name(gAppData->name);
+        nsCString UAName(gAppData->UAName);
+
+        // Sending all information to content process.
+        unused << SendAppInfo(version, buildID, name, UAName);
+    }
+
+    nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
+    if (sheetService) {
+        // This looks like a lot of work, but in a normal browser session we just
+        // send two loads.
+
+        nsCOMArray<nsIStyleSheet>& agentSheets = *sheetService->AgentStyleSheets();
+        for (uint32_t i = 0; i < agentSheets.Length(); i++) {
+            URIParams uri;
+            SerializeURI(agentSheets[i]->GetSheetURI(), uri);
+            unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET);
+        }
+
+        nsCOMArray<nsIStyleSheet>& userSheets = *sheetService->UserStyleSheets();
+        for (uint32_t i = 0; i < userSheets.Length(); i++) {
+            URIParams uri;
+            SerializeURI(userSheets[i]->GetSheetURI(), uri);
+            unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET);
+        }
+
+        nsCOMArray<nsIStyleSheet>& authorSheets = *sheetService->AuthorStyleSheets();
+        for (uint32_t i = 0; i < authorSheets.Length(); i++) {
+            URIParams uri;
+            SerializeURI(authorSheets[i]->GetSheetURI(), uri);
+            unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET);
+        }
+    }
+
+#ifdef MOZ_CONTENT_SANDBOX
+    // Bug 921817.  We enable the sandbox in RecvSetProcessPrivileges,
+    // which is where a preallocated process drops unnecessary privileges,
+    // but a non-preallocated process will already have changed its
+    // uid/gid/etc immediately after forking.  Thus, we send this message,
+    // which is otherwise a no-op, to sandbox it at an appropriate point
+    // during startup.
+    if (mOSPrivileges != base::PRIVILEGES_INHERIT) {
+        if (!SendSetProcessPrivileges(base::PRIVILEGES_INHERIT)) {
+            KillHard();
+        }
+    }
+#endif
+}
+
 bool
 ContentParent::IsAlive()
 {
     return mIsAlive;
 }
 
 bool
 ContentParent::IsForApp()
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -260,16 +260,21 @@ private:
                   base::ProcessHandle aPid,
                   const nsTArray<ProtocolFdMapping>& aFds,
                   ChildPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT);
 #endif
 
     // The common initialization for the constructors.
     void InitializeMembers();
 
+    // The common initialization logic shared by all constuctors.
+    void InitInternal(ProcessPriority aPriority,
+                      bool aSetupOffMainThreadCompositing,
+                      bool aSendRegisteredChrome);
+
     virtual ~ContentParent();
 
     void Init();
 
     // If the frame element indicates that the child process is "critical" and
     // has a pending system message, this function acquires the CPU wake lock on
     // behalf of the child.  We'll release the lock when the system message is
     // handled or after a timeout, whichever comes first.
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -11,16 +11,17 @@ include protocol PContentDialog;
 include protocol PDocumentRenderer;
 include protocol PContentPermissionRequest;
 include protocol PRenderFrame;
 include protocol POfflineCacheUpdate;
 include protocol PIndexedDB;
 include DOMTypes;
 include JavaScriptTypes;
 include URIParams;
+include PContentPermission;
 
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using struct gfxMatrix from "gfxMatrix.h";
 using struct gfxSize from "gfxPoint.h";
 using CSSRect from "Units.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
@@ -201,28 +202,26 @@ parent:
      * Nowadays this is mainly used for link locations on hover.
      */
     SetStatus(uint32_t type, nsString status);
 
     /**
      * Initiates an asynchronous request for permission for the
      * provided principal.
      *
-     * @param aType
-     *   The type of permission to request.
-     * @param aAccess
-     *   Access type. "read" for example.
+     * @param aRequests
+     *   The array of permissions to request.
      * @param aPrincipal
      *   The principal of the request.
      *
      * NOTE: The principal is untrusted in the parent process. Only
      *       principals that can live in the content process should
      *       provided.
      */
-    PContentPermissionRequest(nsCString aType, nsCString aAccess, Principal principal);
+    PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal);
 
     PContentDialog(uint32_t aType, nsCString aName, nsCString aFeatures,
                    int32_t[] aIntParams, nsString[] aStringParams);
 
     /**
      * Create a layout frame (encapsulating a remote layer tree) for
      * the page that is currently loaded in the <browser>.
      */
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PContentPermission.ipdlh
@@ -0,0 +1,14 @@
+/* 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/. */
+
+namespace mozilla {
+namespace dom {
+
+struct PermissionRequest {
+  nsCString type;
+  nsCString access;
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/ipc/PreallocatedProcessManager.cpp
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -276,16 +276,25 @@ PreallocatedProcessManagerImpl::GetSpare
 /**
  * Publish a ContentParent to spare process list.
  */
 void
 PreallocatedProcessManagerImpl::PublishSpareProcess(ContentParent* aContent)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
+    AutoJSContext cx;
+    nsCOMPtr<nsIMessageBroadcaster> ppmm =
+      do_GetService("@mozilla.org/parentprocessmessagemanager;1");
+    nsresult rv = ppmm->BroadcastAsyncMessage(
+      NS_LITERAL_STRING("TEST-ONLY:nuwa-add-new-process"),
+      JSVAL_NULL, JSVAL_NULL, cx, 1);
+  }
+
   if (!mNuwaForkWaitTasks.IsEmpty()) {
     mNuwaForkWaitTasks.ElementAt(0)->Cancel();
     mNuwaForkWaitTasks.RemoveElementAt(0);
   }
 
   mSpareProcesses.AppendElement(aContent);
 }
 
@@ -307,16 +316,24 @@ PreallocatedProcessManagerImpl::MaybeFor
 
 void
 PreallocatedProcessManagerImpl::OnNuwaReady()
 {
   NS_ASSERTION(!mIsNuwaReady, "Multiple Nuwa processes created!");
   ProcessPriorityManager::SetProcessPriority(mPreallocatedAppProcess,
                                              hal::PROCESS_PRIORITY_FOREGROUND);
   mIsNuwaReady = true;
+  if (Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
+    AutoJSContext cx;
+    nsCOMPtr<nsIMessageBroadcaster> ppmm =
+      do_GetService("@mozilla.org/parentprocessmessagemanager;1");
+    nsresult rv = ppmm->BroadcastAsyncMessage(
+      NS_LITERAL_STRING("TEST-ONLY:nuwa-ready"),
+      JSVAL_NULL, JSVAL_NULL, cx, 1);
+  }
   NuwaFork();
 }
 
 void
 PreallocatedProcessManagerImpl::OnNuwaForkTimeout()
 {
   if (!mNuwaForkWaitTasks.IsEmpty()) {
     mNuwaForkWaitTasks.RemoveElementAt(0);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1152,22 +1152,21 @@ TabChild::ArraysToParams(const Infallibl
       aParams->SetString(j, aStringParams[j].get());
     }
   }
 }
 
 #ifdef DEBUG
 PContentPermissionRequestChild*
 TabChild:: SendPContentPermissionRequestConstructor(PContentPermissionRequestChild* aActor,
-                                                    const nsCString& aType,
-                                                    const nsCString& aAccess,
+                                                    const InfallibleTArray<PermissionRequest>& aRequests,
                                                     const IPC::Principal& aPrincipal)
 {
   PCOMContentPermissionRequestChild* child = static_cast<PCOMContentPermissionRequestChild*>(aActor);
-  PContentPermissionRequestChild* request = PBrowserChild::SendPContentPermissionRequestConstructor(aActor, aType, aAccess, aPrincipal);
+  PContentPermissionRequestChild* request = PBrowserChild::SendPContentPermissionRequestConstructor(aActor, aRequests, aPrincipal);
   child->mIPCOpen = true;
   return request;
 }
 #endif /* DEBUG */
 
 void
 TabChild::DestroyWindow()
 {
@@ -2009,17 +2008,18 @@ TabChild::AllocPContentDialogChild(const
 bool
 TabChild::DeallocPContentDialogChild(PContentDialogChild* aDialog)
 {
   delete aDialog;
   return true;
 }
 
 PContentPermissionRequestChild*
-TabChild::AllocPContentPermissionRequestChild(const nsCString& aType, const nsCString& aAccess, const IPC::Principal&)
+TabChild::AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
+                                              const IPC::Principal& aPrincipal)
 {
   NS_RUNTIMEABORT("unused");
   return nullptr;
 }
 
 bool
 TabChild::DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -273,23 +273,21 @@ public:
                                InfallibleTArray<nsString>& aStringParams);
     static void ArraysToParams(const InfallibleTArray<int>& aIntParams,
                                const InfallibleTArray<nsString>& aStringParams,
                                nsIDialogParamBlock* aParams);
 
 #ifdef DEBUG
     virtual PContentPermissionRequestChild*
     SendPContentPermissionRequestConstructor(PContentPermissionRequestChild* aActor,
-                                             const nsCString& aType,
-                                             const nsCString& aAccess,
+                                             const InfallibleTArray<PermissionRequest>& aRequests,
                                              const IPC::Principal& aPrincipal);
 #endif /* DEBUG */
 
-    virtual PContentPermissionRequestChild* AllocPContentPermissionRequestChild(const nsCString& aType,
-                                                                                const nsCString& aAccess,
+    virtual PContentPermissionRequestChild* AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
                                                                                 const IPC::Principal& aPrincipal);
     virtual bool DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor);
 
     virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdateChild(
             const URIParams& manifestURI,
             const URIParams& documentURI,
             const bool& stickDocument);
     virtual bool DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* offlineCacheUpdate);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -10,16 +10,17 @@
 
 #include "AppProcessChecker.h"
 #include "IDBFactory.h"
 #include "IndexedDBParent.h"
 #include "mozIApplication.h"
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/Hal.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
@@ -574,19 +575,20 @@ TabParent::AllocPDocumentRendererParent(
 bool
 TabParent::DeallocPDocumentRendererParent(PDocumentRendererParent* actor)
 {
     delete actor;
     return true;
 }
 
 PContentPermissionRequestParent*
-TabParent::AllocPContentPermissionRequestParent(const nsCString& type, const nsCString& access, const IPC::Principal& principal)
+TabParent::AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
+                                                const IPC::Principal& aPrincipal)
 {
-  return new ContentPermissionRequestParent(type, access, mFrameElement, principal);
+  return CreateContentPermissionRequestParent(aRequests, mFrameElement, aPrincipal);
 }
 
 bool
 TabParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor)
 {
   delete actor;
   return true;
 }
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -220,17 +220,18 @@ public:
     virtual PDocumentRendererParent*
     AllocPDocumentRendererParent(const nsRect& documentRect, const gfxMatrix& transform,
                                  const nsString& bgcolor,
                                  const uint32_t& renderFlags, const bool& flushLayout,
                                  const nsIntSize& renderSize);
     virtual bool DeallocPDocumentRendererParent(PDocumentRendererParent* actor);
 
     virtual PContentPermissionRequestParent*
-    AllocPContentPermissionRequestParent(const nsCString& aType, const nsCString& aAccess, const IPC::Principal& aPrincipal);
+    AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
+                                         const IPC::Principal& aPrincipal);
     virtual bool DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor);
 
     virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdateParent(
             const URIParams& aManifestURI,
             const URIParams& aDocumentURI,
             const bool& stickDocument) MOZ_OVERRIDE;
     virtual bool DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* actor);
 
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -63,16 +63,17 @@ SOURCES += [
 
 IPDL_SOURCES += [
     'DOMTypes.ipdlh',
     'PBlob.ipdl',
     'PBlobStream.ipdl',
     'PBrowser.ipdl',
     'PContent.ipdl',
     'PContentDialog.ipdl',
+    'PContentPermission.ipdlh',
     'PContentPermissionRequest.ipdl',
     'PCrashReporter.ipdl',
     'PDocumentRenderer.ipdl',
     'PMemoryReportRequest.ipdl',
     'PTabContext.ipdlh',
 ]
 
 FAIL_ON_WARNINGS = True
new file mode 100644
--- /dev/null
+++ b/dom/ipc/tests/mochitest.ini
@@ -0,0 +1,2 @@
+[test_NuwaProcessCreation.html]
+run-if = toolkit == 'gonk'
--- a/dom/ipc/tests/moz.build
+++ b/dom/ipc/tests/moz.build
@@ -1,8 +1,9 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
+MOCHITEST_MANIFESTS += ['mochitest.ini']
 
new file mode 100644
--- /dev/null
+++ b/dom/ipc/tests/test_NuwaProcessCreation.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test if Nuwa process created successfully.
+-->
+<head>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript;version=1.7">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function TestLoader() {}
+
+TestLoader.prototype = {
+  _waitingTask: 0,
+  onTestReady: null,
+  unlockTestReady: function() {
+    this._waitingTask--;
+    this._maybeLoadTest();
+  },
+  lockTestReady: function() {
+    this._waitingTask++;
+  },
+  _maybeLoadTest: function() {
+    if (this._waitingTask == 0) {
+      this.onTestReady();
+    }
+  }
+}
+
+var testLoader = new TestLoader();
+testLoader.lockTestReady();
+window.addEventListener('load', function() {
+  testLoader.unlockTestReady();
+});
+
+function setPref(pref, value) {
+  testLoader.lockTestReady();
+  if (value !== undefined && value !== null) {
+    SpecialPowers.pushPrefEnv({'set': [[pref, value]]}, function() { testLoader.unlockTestReady(); });
+  } else {
+    SpecialPowers.pushPrefEnv({'clear': [[pref]]}, function() { testLoader.unlockTestReady(); });
+  }
+}
+
+setPref('dom.ipc.processPriorityManager.testMode', true);
+setPref('dom.ipc.processPriorityManager.enabled', true);
+setPref('dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2);
+
+function runTest()
+{
+  // Shutdown preallocated process.
+  SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', false);
+  let cpmm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
+                          .getService(SpecialPowers.Ci.nsISyncMessageSender);
+  let seenNuwaReady = false;
+  let msgHandler = {
+    receiveMessage: function receiveMessage(msg) {
+      msg = SpecialPowers.wrap(msg);
+      if (msg.name == 'TEST-ONLY:nuwa-ready') {
+        ok(true, "Got nuwa-ready");
+        is(seenNuwaReady, false, "Already received nuwa ready");
+        seenNuwaReady = true;
+      } else if (msg.name == 'TEST-ONLY:nuwa-add-new-process') {
+        ok(true, "Got nuwa-add-new-process");
+        is(seenNuwaReady, true, "Receive nuwa-add-new-process before nuwa-ready");
+        testEnd();
+      }
+    }
+  };
+  let timeout = setTimeout(function() {
+    ok(false, "Nuwa process is not launched");
+    testEnd();
+  }, 60000);
+
+  function testEnd() {
+    cpmm.removeMessageListener("TEST-ONLY:nuwa-ready", msgHandler);
+    cpmm.removeMessageListener("TEST-ONLY:nuwa-add-new-process", msgHandler);
+    clearTimeout(timeout);
+    SimpleTest.finish();
+  }
+
+  cpmm.addMessageListener("TEST-ONLY:nuwa-ready", msgHandler);
+  cpmm.addMessageListener("TEST-ONLY:nuwa-add-new-process", msgHandler);
+
+
+  // Setting this pref to true should cause us to prelaunch a process.
+  SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', true);
+}
+
+testLoader.onTestReady = runTest;
+</script>
+</body>
+</html>
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -36,17 +36,17 @@
 #include "mozilla/Preferences.h"
 
 /* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
 #include "MediaEngineDefault.h"
 #if defined(MOZ_WEBRTC)
 #include "MediaEngineWebRTC.h"
 #endif
 
-#ifdef MOZ_WIDGET_GONK
+#ifdef MOZ_B2G
 #include "MediaPermissionGonk.h"
 #endif
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with MediaStream::GetCurrentTime.
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
@@ -751,17 +751,17 @@ public:
     MediaEnginePrefs &aPrefs)
     : mConstraints(aConstraints)
     , mSuccess(aSuccess)
     , mError(aError)
     , mWindowID(aWindowID)
     , mListener(aListener)
     , mPrefs(aPrefs)
     , mDeviceChosen(false)
-    , mBackendChosen(false)
+    , mBackend(nullptr)
     , mManager(MediaManager::GetInstance())
   {}
 
   /**
    * The caller can also choose to provide their own backend instead of
    * using the one provided by MediaManager::GetBackend.
    */
   GetUserMediaRunnable(
@@ -773,40 +773,37 @@ public:
     MediaEngine* aBackend)
     : mConstraints(aConstraints)
     , mSuccess(aSuccess)
     , mError(aError)
     , mWindowID(aWindowID)
     , mListener(aListener)
     , mPrefs(aPrefs)
     , mDeviceChosen(false)
-    , mBackendChosen(true)
     , mBackend(aBackend)
     , mManager(MediaManager::GetInstance())
   {}
 
   ~GetUserMediaRunnable() {
-    if (mBackendChosen) {
-      delete mBackend;
-    }
   }
 
   NS_IMETHOD
   Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
+    MediaEngine* backend = mBackend;
     // Was a backend provided?
-    if (!mBackendChosen) {
-      mBackend = mManager->GetBackend(mWindowID);
+    if (!backend) {
+      backend = mManager->GetBackend(mWindowID);
     }
 
     // Was a device provided?
     if (!mDeviceChosen) {
-      nsresult rv = SelectDevice();
+      nsresult rv = SelectDevice(backend);
       if (rv != NS_OK) {
         return rv;
       }
     }
 
     // It is an error if audio or video are requested along with picture.
     if (mConstraints.mPicture && (mConstraints.mAudio || mConstraints.mVideo)) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
@@ -868,34 +865,34 @@ public:
   SetVideoDevice(MediaDevice* aVideoDevice)
   {
     mVideoDevice = aVideoDevice;
     mDeviceChosen = true;
     return NS_OK;
   }
 
   nsresult
-  SelectDevice()
+  SelectDevice(MediaEngine* backend)
   {
     if (mConstraints.mPicture || mConstraints.mVideo) {
-      ScopedDeletePtr<SourceSet> sources (GetSources(mBackend,
+      ScopedDeletePtr<SourceSet> sources (GetSources(backend,
           mConstraints.mVideom, &MediaEngine::EnumerateVideoDevices));
 
       if (!sources->Length()) {
         NS_DispatchToMainThread(new ErrorCallbackRunnable(
           mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID));
         return NS_ERROR_FAILURE;
       }
       // Pick the first available device.
       mVideoDevice = do_QueryObject((*sources)[0]);
       LOG(("Selected video device"));
     }
 
     if (mConstraints.mAudio) {
-      ScopedDeletePtr<SourceSet> sources (GetSources(mBackend,
+      ScopedDeletePtr<SourceSet> sources (GetSources(backend,
           mConstraints.mAudiom, &MediaEngine::EnumerateAudioDevices));
 
       if (!sources->Length()) {
         NS_DispatchToMainThread(new ErrorCallbackRunnable(
           mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID));
         return NS_ERROR_FAILURE;
       }
       // Pick the first available device.
@@ -979,19 +976,18 @@ private:
   already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   uint64_t mWindowID;
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsRefPtr<MediaDevice> mAudioDevice;
   nsRefPtr<MediaDevice> mVideoDevice;
   MediaEnginePrefs mPrefs;
 
   bool mDeviceChosen;
-  bool mBackendChosen;
 
-  MediaEngine* mBackend;
+  RefPtr<MediaEngine> mBackend;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
 /**
  * Similar to GetUserMediaRunnable, but used for the chrome-only
  * GetUserMediaDevices function. Enumerates a list of audio & video devices,
  * wraps them up in nsIMediaDevice objects and returns it to the success
  * callback.
@@ -1261,20 +1257,20 @@ MediaManager::GetUserMedia(JSContext* aC
   }
 #endif
 
   static bool created = false;
   if (!created) {
     // Force MediaManager to startup before we try to access it from other threads
     // Hack: should init singleton earlier unless it's expensive (mem or CPU)
     (void) MediaManager::Get();
-#ifdef MOZ_WIDGET_GONK
+#ifdef MOZ_B2G
     // Initialize MediaPermissionManager before send out any permission request.
     (void) MediaPermissionManager::GetInstance();
-#endif //MOZ_WIDGET_GONK
+#endif //MOZ_B2G
   }
 
   // Store the WindowID in a hash table and mark as active. The entry is removed
   // when this window is closed or navigated away from.
   uint64_t windowID = aWindow->WindowID();
   nsRefPtr<GetUserMediaRunnable> gUMRunnable;
   // This is safe since we're on main-thread, and the windowlist can only
   // be invalidated from the main-thread (see OnNavigation)
@@ -1410,21 +1406,21 @@ MediaEngine*
 MediaManager::GetBackend(uint64_t aWindowId)
 {
   // Plugin backends as appropriate. The default engine also currently
   // includes picture support for Android.
   // This IS called off main-thread.
   MutexAutoLock lock(mMutex);
   if (!mBackend) {
 #if defined(MOZ_WEBRTC)
-  #ifndef MOZ_B2G_CAMERA
+#ifndef MOZ_B2G_CAMERA
     mBackend = new MediaEngineWebRTC(mPrefs);
-  #else
-    mBackend = new MediaEngineWebRTC(mCameraManager, aWindowId);
-  #endif
+#else
+    mBackend = new MediaEngineWebRTC(mCameraManager);
+#endif
 #else
     mBackend = new MediaEngineDefault();
 #endif
   }
   return mBackend;
 }
 
 void
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -508,35 +508,33 @@ private:
                const char *aData, int32_t *aVal);
   void GetPrefBool(nsIPrefBranch *aBranch, const char *aPref,
                    const char *aData, bool *aVal);
   void GetPrefs(nsIPrefBranch *aBranch, const char *aData);
 
   // Make private because we want only one instance of this class
   MediaManager();
 
-  ~MediaManager() {
-    delete mBackend;
-  }
+  ~MediaManager() {}
 
   nsresult MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo,
                                            bool* aAudio);
 
   void StopMediaStreams();
 
   // ONLY access from MainThread so we don't need to lock
   WindowTable mActiveWindows;
   nsRefPtrHashtable<nsStringHashKey, nsRunnable> mActiveCallbacks;
   // Always exists
   nsCOMPtr<nsIThread> mMediaThread;
 
   Mutex mMutex;
   // protected with mMutex:
-  MediaEngine* mBackend;
+  RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
-#ifdef MOZ_WIDGET_GONK
+#ifdef MOZ_B2G_CAMERA
   nsRefPtr<nsDOMCameraManager> mCameraManager;
 #endif
 };
 
 } // namespace mozilla
--- a/dom/media/MediaPermissionGonk.cpp
+++ b/dom/media/MediaPermissionGonk.cpp
@@ -15,24 +15,46 @@
 #include "nsTArray.h"
 #include "GetUserMediaRequest.h"
 #include "PCOMContentPermissionRequestChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "nsISupportsPrimitives.h"
 #include "nsServiceManagerUtils.h"
+#include "nsArrayUtils.h"
+#include "nsContentPermissionHelper.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 
 #define AUDIO_PERMISSION_NAME "audio-capture"
+#define VIDEO_PERMISSION_NAME "video-capture"
+
+using namespace mozilla::dom;
 
 namespace mozilla {
 
 static MediaPermissionManager *gMediaPermMgr = nullptr;
 
+static uint32_t
+ConvertArrayToPermissionRequest(nsIArray* aSrcArray,
+                                nsTArray<PermissionRequest>& aDesArray)
+{
+  uint32_t len = 0;
+  aSrcArray->GetLength(&len);
+  for (uint32_t i = 0; i < len; i++) {
+    nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
+    nsAutoCString type;
+    nsAutoCString access;
+    cpt->GetType(type);
+    cpt->GetAccess(access);
+    aDesArray.AppendElement(PermissionRequest(type, access));
+  }
+  return len;
+}
+
 // Helper function for notifying permission granted
 static nsresult
 NotifyPermissionAllow(const nsAString &aCallID, nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
 {
   nsresult rv;
   nsCOMPtr<nsISupportsArray> array;
   rv = NS_NewISupportsArray(getter_AddRefs(array));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -88,77 +110,85 @@ public:
   // It will be called when prompt dismissed.
   virtual bool Recv__delete__(const bool &allow) MOZ_OVERRIDE;
   virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
 
   already_AddRefed<nsPIDOMWindow> GetOwner();
 
 private:
   bool mAudio; // Request for audio permission
+  bool mVideo; // Request for video permission
   nsRefPtr<dom::GetUserMediaRequest> mRequest;
   nsTArray<nsCOMPtr<nsIMediaDevice> > mDevices; // candiate device list
 };
 
 // MediaPermissionRequest
 NS_IMPL_ISUPPORTS1(MediaPermissionRequest, nsIContentPermissionRequest)
 
 MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest> &aRequest,
                                                nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
   : mRequest(aRequest)
 {
   dom::MediaStreamConstraintsInternal constraints;
   mRequest->GetConstraints(constraints);
 
   mAudio = constraints.mAudio;
+  mVideo = constraints.mVideo;
 
   for (uint32_t i = 0; i < aDevices.Length(); ++i) {
     nsCOMPtr<nsIMediaDevice> device(aDevices[i]);
     nsAutoString deviceType;
     device->GetType(deviceType);
     if (mAudio && deviceType.EqualsLiteral("audio")) {
       mDevices.AppendElement(device);
     }
+    if (mVideo && deviceType.EqualsLiteral("video")) {
+      mDevices.AppendElement(device);
+    }
   }
 }
 
 // nsIContentPermissionRequest methods
 NS_IMETHODIMP
+MediaPermissionRequest::GetTypes(nsIArray** aTypes)
+{
+  nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (mAudio) {
+    nsCOMPtr<ContentPermissionType> AudioType =
+      new ContentPermissionType(NS_LITERAL_CSTRING(AUDIO_PERMISSION_NAME),
+                                NS_LITERAL_CSTRING("unused"));
+    types->AppendElement(AudioType, false);
+  }
+  if (mVideo) {
+    nsCOMPtr<ContentPermissionType> VideoType =
+      new ContentPermissionType(NS_LITERAL_CSTRING(VIDEO_PERMISSION_NAME),
+                                NS_LITERAL_CSTRING("unused"));
+    types->AppendElement(VideoType, false);
+  }
+  NS_IF_ADDREF(*aTypes = types);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 MediaPermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal)
 {
   NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mRequest->GetParentObject());
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
   NS_ADDREF(*aRequestingPrincipal = doc->NodePrincipal());
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MediaPermissionRequest::GetType(nsACString &aType)
-{
-  if (mAudio) {
-    aType = AUDIO_PERMISSION_NAME;
-    return NS_OK;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MediaPermissionRequest::GetAccess(nsACString &aAccess)
-{
-  aAccess = "unused";
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 MediaPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
 {
   NS_ENSURE_ARG_POINTER(aRequestingWindow);
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mRequest->GetParentObject());
   window.forget(aRequestingWindow);
   return NS_OK;
 }
 
@@ -273,32 +303,30 @@ MediaDeviceSuccessCallback::DoPrompt(nsR
     nsresult rv;
 
     nsCOMPtr<nsPIDOMWindow> window(req->GetOwner());
     NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
     dom::TabChild* child = dom::TabChild::GetFrom(window->GetDocShell());
     NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
 
-    nsAutoCString type;
-    rv = req->GetType(type);
+    nsCOMPtr<nsIArray> typeArray;
+    rv = req->GetTypes(getter_AddRefs(typeArray));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsAutoCString access;
-    rv = req->GetAccess(access);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsTArray<PermissionRequest> permArray;
+    ConvertArrayToPermissionRequest(typeArray, permArray);
 
     nsCOMPtr<nsIPrincipal> principal;
     rv = req->GetPrincipal(getter_AddRefs(principal));
     NS_ENSURE_SUCCESS(rv, rv);
 
     req->AddRef();
     child->SendPContentPermissionRequestConstructor(req,
-                                                    type,
-                                                    access,
+                                                    permArray,
                                                     IPC::Principal(principal));
 
     req->Sendprompt();
     return NS_OK;
   }
 
   // for chrome process
   nsCOMPtr<nsIContentPermissionPrompt> prompt =
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -9,16 +9,17 @@
 
 #include "nsISettingsService.h"
 
 #include "nsGeolocation.h"
 #include "nsDOMClassInfoID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
+#include "nsContentPermissionHelper.h"
 #include "nsCxPusher.h"
 #include "nsIDocument.h"
 #include "nsIObserverService.h"
 #include "nsPIDOMWindow.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/Preferences.h"
@@ -380,27 +381,21 @@ nsGeolocationRequest::GetPrincipal(nsIPr
 
   nsCOMPtr<nsIPrincipal> principal = mLocator->GetPrincipal();
   principal.forget(aRequestingPrincipal);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsGeolocationRequest::GetType(nsACString & aType)
+nsGeolocationRequest::GetTypes(nsIArray** aTypes)
 {
-  aType = "geolocation";
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsGeolocationRequest::GetAccess(nsACString & aAccess)
-{
-  aAccess = "unused";
-  return NS_OK;
+  return CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
+                               NS_LITERAL_CSTRING("unused"),
+                               aTypes);
 }
 
 NS_IMETHODIMP
 nsGeolocationRequest::GetWindow(nsIDOMWindow * *aRequestingWindow)
 {
   NS_ENSURE_ARG_POINTER(aRequestingWindow);
 
   nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mLocator->GetOwner());
@@ -1447,22 +1442,25 @@ Geolocation::RegisterRequestWithPrompt(n
 
     // because owner implements nsITabChild, we can assume that it is
     // the one and only TabChild.
     TabChild* child = TabChild::GetFrom(window->GetDocShell());
     if (!child) {
       return false;
     }
 
+    nsTArray<PermissionRequest> permArray;
+    permArray.AppendElement(PermissionRequest(NS_LITERAL_CSTRING("geolocation"),
+                                              NS_LITERAL_CSTRING("unused")));
+
     // Retain a reference so the object isn't deleted without IPDL's knowledge.
     // Corresponding release occurs in DeallocPContentPermissionRequest.
     request->AddRef();
     child->SendPContentPermissionRequestConstructor(request,
-                                                    NS_LITERAL_CSTRING("geolocation"),
-                                                    NS_LITERAL_CSTRING("unused"),
+                                                    permArray,
                                                     IPC::Principal(mPrincipal));
 
     request->Sendprompt();
     return true;
   }
 
   nsCOMPtr<nsIRunnable> ev  = new RequestPromptEvent(request);
   NS_DispatchToMainThread(ev);
--- a/dom/src/notification/DesktopNotification.cpp
+++ b/dom/src/notification/DesktopNotification.cpp
@@ -10,16 +10,17 @@
 #include "nsIDOMDesktopNotification.h"
 #include "TabChild.h"
 #include "mozilla/Preferences.h"
 #include "nsGlobalWindow.h"
 #include "nsIAppsService.h"
 #include "PCOMContentPermissionRequestChild.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsServiceManagerUtils.h"
+#include "PermissionMessageUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 /*
  * Simple Request
  */
 class DesktopNotificationRequest : public nsIContentPermissionRequest,
@@ -174,19 +175,22 @@ DesktopNotification::Init()
     // because owner implements nsITabChild, we can assume that it is
     // the one and only TabChild for this docshell.
     TabChild* child = TabChild::GetFrom(GetOwner()->GetDocShell());
 
     // Retain a reference so the object isn't deleted without IPDL's knowledge.
     // Corresponding release occurs in DeallocPContentPermissionRequest.
     nsRefPtr<DesktopNotificationRequest> copy = request;
 
+    nsTArray<PermissionRequest> permArray;
+    permArray.AppendElement(PermissionRequest(
+                            NS_LITERAL_CSTRING("desktop-notification"),
+                            NS_LITERAL_CSTRING("unused")));
     child->SendPContentPermissionRequestConstructor(copy.forget().get(),
-                                                    NS_LITERAL_CSTRING("desktop-notification"),
-                                                    NS_LITERAL_CSTRING("unused"),
+                                                    permArray,
                                                     IPC::Principal(mPrincipal));
 
     request->Sendprompt();
     return;
   }
 
   // otherwise, dispatch it
   NS_DispatchToMainThread(request);
@@ -348,23 +352,17 @@ NS_IMETHODIMP
 DesktopNotificationRequest::Allow()
 {
   nsresult rv = mDesktopNotification->SetAllow(true);
   mDesktopNotification = nullptr;
   return rv;
 }
 
 NS_IMETHODIMP
-DesktopNotificationRequest::GetType(nsACString & aType)
+DesktopNotificationRequest::GetTypes(nsIArray** aTypes)
 {
-  aType = "desktop-notification";
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DesktopNotificationRequest::GetAccess(nsACString & aAccess)
-{
-  aAccess = "unused";
-  return NS_OK;
+  return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
+                               NS_LITERAL_CSTRING("unused"),
+                               aTypes);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/src/notification/Notification.cpp
+++ b/dom/src/notification/Notification.cpp
@@ -19,16 +19,17 @@
 #include "nsIPermissionManager.h"
 #include "nsIUUIDGenerator.h"
 #include "nsServiceManagerUtils.h"
 #include "nsToolkitCompsCID.h"
 #include "nsGlobalWindow.h"
 #include "nsDOMJSUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
+#include "nsContentPermissionHelper.h"
 #ifdef MOZ_B2G
 #include "nsIDOMDesktopNotification.h"
 #endif
 
 namespace mozilla {
 namespace dom {
 
 class NotificationStorageCallback MOZ_FINAL : public nsINotificationStorageCallback
@@ -262,19 +263,21 @@ NotificationPermissionRequest::Run()
     if (!child) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     // Retain a reference so the object isn't deleted without IPDL's knowledge.
     // Corresponding release occurs in DeallocPContentPermissionRequest.
     AddRef();
 
-    NS_NAMED_LITERAL_CSTRING(type, "desktop-notification");
-    NS_NAMED_LITERAL_CSTRING(access, "unused");
-    child->SendPContentPermissionRequestConstructor(this, type, access,
+    nsTArray<PermissionRequest> permArray;
+    permArray.AppendElement(PermissionRequest(
+                            NS_LITERAL_CSTRING("desktop-notification"),
+                            NS_LITERAL_CSTRING("unused")));
+    child->SendPContentPermissionRequestConstructor(this, permArray,
                                                     IPC::Principal(mPrincipal));
 
     Sendprompt();
     return NS_OK;
   }
 
   nsCOMPtr<nsIContentPermissionPrompt> prompt =
     do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
@@ -337,27 +340,21 @@ nsresult
 NotificationPermissionRequest::CallCallback()
 {
   ErrorResult rv;
   mCallback->Call(mPermission, rv);
   return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
-NotificationPermissionRequest::GetAccess(nsACString& aAccess)
+NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
 {
-  aAccess.AssignLiteral("unused");
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-NotificationPermissionRequest::GetType(nsACString& aType)
-{
-  aType.AssignLiteral("desktop-notification");
-  return NS_OK;
+  return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
+                               NS_LITERAL_CSTRING("unused"),
+                               aTypes);
 }
 
 bool
 NotificationPermissionRequest::Recv__delete__(const bool& aAllow)
 {
   if (aAllow) {
     (void) Allow();
   } else {
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -127,17 +127,17 @@ function testConsoleGroup(aMessageObject
   is(messageWindow, gWindow, "found correct window by window ID");
 
   ok(aMessageObject.level == "group" ||
      aMessageObject.level == "groupCollapsed" ||
      aMessageObject.level == "groupEnd",
      "expected level received");
 
   is(aMessageObject.functionName, "testGroups", "functionName matches");
-  ok(aMessageObject.lineNumber >= 45 && aMessageObject.lineNumber <= 48,
+  ok(aMessageObject.lineNumber >= 45 && aMessageObject.lineNumber <= 49,
      "lineNumber matches");
   if (aMessageObject.level == "groupCollapsed") {
     is(aMessageObject.groupName, "a group", "groupCollapsed groupName matches");
     is(aMessageObject.arguments[0], "a", "groupCollapsed arguments[0] matches");
     is(aMessageObject.arguments[1], "group", "groupCollapsed arguments[0] matches");
   }
   else if (aMessageObject.level == "group") {
     is(aMessageObject.groupName, "b group", "group groupName matches");
@@ -253,16 +253,20 @@ function observeConsoleTest() {
   win.console.exception("arg");
   yield undefined;
 
   let obj2 = { b: 2 };
   expect("log", "omg ", obj, " foo ", 4, obj2);
   win.console.log("omg %o foo %o", obj, 4, obj2);
   yield undefined;
 
+  expect("assert", "message");
+  win.console.assert(false, "message");
+  yield undefined;
+
   startTraceTest();
   yield undefined;
 
   startLocationTest();
   yield undefined;
 }
 
 function consoleAPISanityTest() {
@@ -277,16 +281,17 @@ function consoleAPISanityTest() {
   ok(win.console.exception, "console.exception is here");
   ok(win.console.trace, "console.trace is here");
   ok(win.console.dir, "console.dir is here");
   ok(win.console.group, "console.group is here");
   ok(win.console.groupCollapsed, "console.groupCollapsed is here");
   ok(win.console.groupEnd, "console.groupEnd is here");
   ok(win.console.time, "console.time is here");
   ok(win.console.timeEnd, "console.timeEnd is here");
+  ok(win.console.assert, "console.assert is here");
 }
 
 function startTimeTest() {
   // Reset the observer function to cope with the fabricated test data.
   ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) {
     try {
       testConsoleTime(aSubject.wrappedJSObject);
     } catch (ex) {
--- a/dom/tests/browser/test-console-api.html
+++ b/dom/tests/browser/test-console-api.html
@@ -35,16 +35,17 @@
       function test() {
         var str = "Test Message."
         console.foobar(str); // if this throws, we don't execute following funcs
         console.log(str);
         console.info(str);
         console.warn(str);
         console.error(str);
         console.exception(str);
+        console.assert(false, str);
       }
 
       function testGroups() {
         console.groupCollapsed("a", "group");
         console.group("b", "group");
         console.groupEnd("b", "group");
       }
 
--- a/dom/tests/mochitest/general/test_consoleAPI.html
+++ b/dom/tests/mochitest/general/test_consoleAPI.html
@@ -30,16 +30,17 @@ function doTest() {
     "dir": "function",
     "group": "function",
     "groupCollapsed": "function",
     "groupEnd": "function",
     "time": "function",
     "timeEnd": "function",
     "profile": "function",
     "profileEnd": "function",
+    "assert": "function",
     "__noSuchMethod__": "function"
   };
 
   var foundProps = 0;
   for (var prop in console) {
     foundProps++;
     is(typeof(console[prop]), expectedProps[prop], "expect console prop " + prop + " exists");
   }
--- a/mobile/android/components/ContentPermissionPrompt.js
+++ b/mobile/android/components/ContentPermissionPrompt.js
@@ -16,28 +16,28 @@ const kEntities = { "geolocation": "geol
 
 function ContentPermissionPrompt() {}
 
 ContentPermissionPrompt.prototype = {
   classID: Components.ID("{C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
 
-  handleExistingPermission: function handleExistingPermission(request, isApp) {
-    let result = Services.perms.testExactPermissionFromPrincipal(request.principal, request.type);
+  handleExistingPermission: function handleExistingPermission(request, type, isApp) {
+    let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type);
     if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
       request.allow();
       return true;
     }
     if (result == Ci.nsIPermissionManager.DENY_ACTION) {
       request.cancel();
       return true;
     }
 
-    if (isApp && (result == Ci.nsIPermissionManager.UNKNOWN_ACTION && !!kEntities[request.type])) {
+    if (isApp && (result == Ci.nsIPermissionManager.UNKNOWN_ACTION && !!kEntities[type])) {
       request.cancel();
       return true;
     }
 
     return false;
   },
 
   getChromeWindow: function getChromeWindow(aWindow) {
@@ -57,48 +57,56 @@ ContentPermissionPrompt.prototype = {
       return this.getChromeWindow(requestingWindow).wrappedJSObject;
     }
     return request.element.ownerDocument.defaultView;
   },
 
   prompt: function(request) {
     let isApp = request.principal.appId !== Ci.nsIScriptSecurityManager.NO_APP_ID && request.principal.appId !== Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID;
 
+    // Only allow exactly one permission rquest here.
+    let types = request.types.QueryInterface(Ci.nsIArray);
+    if (types.length != 1) {
+      request.cancel();
+      return;
+    }
+    let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
+
     // Returns true if the request was handled
-    if (this.handleExistingPermission(request, isApp))
+    if (this.handleExistingPermission(request, perm.type, isApp))
        return;
 
     let chromeWin = this.getChromeForRequest(request);
     let tab = chromeWin.BrowserApp.getTabForWindow(request.window.top);
     if (!tab)
       return;
 
     let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
-    let entityName = kEntities[request.type];
+    let entityName = kEntities[perm.type];
 
     let buttons = [{
       label: browserBundle.GetStringFromName(entityName + ".allow"),
       callback: function(aChecked) {
         // If the user checked "Don't ask again", make a permanent exception
         if (aChecked) {
-          Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION);
+          Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION);
         } else if (isApp || entityName == "desktopNotification") {
           // Otherwise allow the permission for the current session (if the request comes from an app or if it's a desktop-notification request)
-          Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION);
+          Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION);
         }
 
         request.allow();
       }
     },
     {
       label: browserBundle.GetStringFromName(entityName + ".dontAllow"),
       callback: function(aChecked) {
         // If the user checked "Don't ask again", make a permanent exception
         if (aChecked)
-          Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.DENY_ACTION);
+          Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.DENY_ACTION);
 
         request.cancel();
       }
     }];
 
     let requestor = chromeWin.BrowserApp.manifest ? "'" + chromeWin.BrowserApp.manifest.name + "'" : request.principal.URI.host;
     let message = browserBundle.formatStringFromName(entityName + ".ask", [requestor], 1);
     let options = { checkbox: browserBundle.GetStringFromName(entityName + ".dontAskAgain") };
--- a/netwerk/base/src/nsPACMan.cpp
+++ b/netwerk/base/src/nsPACMan.cpp
@@ -9,16 +9,19 @@
 #include "nsIAuthPrompt.h"
 #include "nsIPromptFactory.h"
 #include "nsIHttpChannel.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsNetUtil.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsISystemProxySettings.h"
+#ifdef MOZ_NUWA_PROCESS
+#include "ipc/Nuwa.h"
+#endif
 
 //-----------------------------------------------------------------------------
 using namespace mozilla;
 using namespace mozilla::net;
 
 #if defined(PR_LOGGING)
 #endif
 #undef LOG
@@ -669,16 +672,23 @@ nsPACMan::AsyncOnChannelRedirect(nsIChan
   return NS_OK;
 }
 
 void
 nsPACMan::NamePACThread()
 {
   NS_ABORT_IF_FALSE(!NS_IsMainThread(), "wrong thread");
   PR_SetCurrentThreadName("Proxy Resolution");
+#ifdef MOZ_NUWA_PROCESS
+  if (IsNuwaProcess()) {
+    NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
+                 "NuwaMarkCurrentThread is undefined!");
+    NuwaMarkCurrentThread(nullptr, nullptr);
+  }
+#endif
 }
 
 nsresult
 nsPACMan::Init(nsISystemProxySettings *systemProxySettings)
 {
   mSystemProxySettings = systemProxySettings;
 
   nsresult rv = NS_NewThread(getter_AddRefs(mPACThread), nullptr);
--- a/testing/specialpowers/content/MockPermissionPrompt.jsm
+++ b/testing/specialpowers/content/MockPermissionPrompt.jsm
@@ -29,19 +29,28 @@ var newFactory = {
   },
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
 };
 
 this.MockPermissionPrompt = {
   init: function() {
     this.reset();
     if (!registrar.isCIDRegistered(newClassID)) {
-      oldClassID = registrar.contractIDToCID(CONTRACT_ID);
-      oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
-      registrar.unregisterFactory(oldClassID, oldFactory);
+      try {
+        oldClassID = registrar.contractIDToCID(CONTRACT_ID);
+        oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
+      } catch (ex) {
+        oldClassID = "";
+        oldFactory = null;
+        dump("TEST-INFO | can't get permission prompt registered component, " +
+            "assuming there is none");
+      }
+      if (oldFactory) {
+        registrar.unregisterFactory(oldClassID, oldFactory);
+      }
       registrar.registerFactory(newClassID, "", CONTRACT_ID, newFactory);
     }
   },
   
   reset: function() {
   },
   
   cleanup: function() {
@@ -56,24 +65,27 @@ this.MockPermissionPrompt = {
 function MockPermissionPromptInstance() { };
 MockPermissionPromptInstance.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
 
   promptResult: Ci.nsIPermissionManager.UNKNOWN_ACTION,
 
   prompt: function(request) {
 
-    this.promptResult = Services.perms.testExactPermissionFromPrincipal(request.principal,
-                                                                        request.type);
-    if (this.promptResult == Ci.nsIPermissionManager.ALLOW_ACTION) {
-      request.allow();
+    let perms = request.types.QueryInterface(Ci.nsIArray);
+    for (let idx = 0; idx < perms.length; idx++) {
+      let perm = perms.queryElementAt(idx, Ci.nsIContentPermissionType);
+      if (Services.perms.testExactPermissionFromPrincipal(
+           request.principal, perm.type) != Ci.nsIPermissionManager.ALLOW_ACTION) {
+        request.cancel();
+        return;
+      }
     }
-    else {
-      request.cancel();
-    }
+
+    request.allow();
   }
 };
 
 // Expose everything to content. We call reset() here so that all of the relevant
 // lazy expandos get added.
 MockPermissionPrompt.reset();
 function exposeAll(obj) {
   var props = {};
--- a/webapprt/ContentPermission.js
+++ b/webapprt/ContentPermission.js
@@ -25,80 +25,88 @@ ContentPermission.prototype = {
       .QueryInterface(Ci.nsIDocShellTreeItem)
       .rootTreeItem
       .QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindow)
       .QueryInterface(Ci.nsIDOMChromeWindow);
   },
 
   prompt: function(request) {
+    // Only allow exactly one permission rquest here.
+    let types = request.types.QueryInterface(Ci.nsIArray);
+    if (types.length != 1) {
+      request.cancel();
+      return;
+    }
+    let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
+
     // Reuse any remembered permission preferences
     let result =
       Services.perms.testExactPermissionFromPrincipal(request.principal,
-                                                      request.type);
+                                                      perm.type);
 
     // We used to use the name "geo" for the geolocation permission, now we're
     // using "geolocation".  We need to check both to support existing
     // installations.
     if ((result == Ci.nsIPermissionManager.UNKNOWN_ACTION ||
          result == Ci.nsIPermissionManager.PROMPT_ACTION) &&
-        request.type == "geolocation") {
+        perm.type == "geolocation") {
       let geoResult = Services.perms.testExactPermission(request.principal.URI,
                                                          "geo");
       // We override the result only if the "geo" permission was allowed or
       // denied.
       if (geoResult == Ci.nsIPermissionManager.ALLOW_ACTION ||
           geoResult == Ci.nsIPermissionManager.DENY_ACTION) {
         result = geoResult;
       }
     }
 
     if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
       request.allow();
       return;
     } else if (result == Ci.nsIPermissionManager.DENY_ACTION ||
                (result == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
-                UNKNOWN_FAIL.indexOf(request.type) >= 0)) {
+                UNKNOWN_FAIL.indexOf(perm.type) >= 0)) {
       request.cancel();
       return;
     }
 
     // Display a prompt at the top level
     let {name} = WebappRT.localeManifest;
     let requestingWindow = request.window.top;
     let chromeWin = this._getChromeWindow(requestingWindow);
     let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties");
 
     // Construct a prompt with share/don't and remember checkbox
     let remember = {value: false};
     let choice = Services.prompt.confirmEx(
       chromeWin,
-      bundle.formatStringFromName(request.type + ".title", [name], 1),
-      bundle.GetStringFromName(request.type + ".description"),
+      bundle.formatStringFromName(perm.type + ".title", [name], 1),
+      bundle.GetStringFromName(perm.type + ".description"),
       // Set both buttons to strings with the cancel button being default
       Ci.nsIPromptService.BUTTON_POS_1_DEFAULT |
         Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_0 |
         Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_1,
-      bundle.GetStringFromName(request.type + ".allow"),
-      bundle.GetStringFromName(request.type + ".deny"),
+      bundle.GetStringFromName(perm.type + ".allow"),
+      bundle.GetStringFromName(perm.type + ".deny"),
       null,
-      bundle.GetStringFromName(request.type + ".remember"),
+      bundle.GetStringFromName(perm.type + ".remember"),
       remember);
 
     let action = Ci.nsIPermissionManager.ALLOW_ACTION;
     if (choice != 0) {
       action = Ci.nsIPermissionManager.DENY_ACTION;
     }
 
     if (remember.value) {
       // Persist the choice if the user wants to remember
-      Services.perms.addFromPrincipal(request.principal, request.type, action);
+      Services.perms.addFromPrincipal(request.principal, perm.type, action);
     } else {
       // Otherwise allow the permission for the current session
-      Services.perms.addFromPrincipal(request.principal, request.type, action,
+      Services.perms.addFromPrincipal(request.principal, perm.type, action,
                                       Ci.nsIPermissionManager.EXPIRE_SESSION);
     }
 
     // Trigger the selected choice
     if (choice == 0) {
       request.allow();
     }
     else {
--- a/xpcom/threads/BackgroundHangMonitor.cpp
+++ b/xpcom/threads/BackgroundHangMonitor.cpp
@@ -6,16 +6,19 @@
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Move.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThreadHangStats.h"
 #include "mozilla/ThreadLocal.h"
+#ifdef MOZ_NUWA_PROCESS
+#include "ipc/Nuwa.h"
+#endif
 
 #include "prinrval.h"
 #include "prthread.h"
 #include "ThreadStackHelper.h"
 
 #include <algorithm>
 
 namespace mozilla {
@@ -26,16 +29,25 @@ namespace mozilla {
  */
 class BackgroundHangManager : public AtomicRefCounted<BackgroundHangManager>
 {
 private:
   // Background hang monitor thread function
   static void MonitorThread(void* aData)
   {
     PR_SetCurrentThreadName("BgHangManager");
+
+#ifdef MOZ_NUWA_PROCESS
+    if (IsNuwaProcess()) {
+      NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
+                   "NuwaMarkCurrentThread is undefined!");
+      NuwaMarkCurrentThread(nullptr, nullptr);
+    }
+#endif
+
     /* We do not hold a reference to BackgroundHangManager here
        because the monitor thread only exists as long as the
        BackgroundHangManager instance exists. We stop the monitor
        thread in the BackgroundHangManager destructor, and we can
        only get to the destructor if we don't hold a reference here. */
     static_cast<BackgroundHangManager*>(aData)->RunMonitorThread();
   }