Merge mozilla-central into holly
authorMike Conley <mconley@mozilla.com>
Sun, 08 Dec 2013 23:11:56 -0500
changeset 168634 1c599e4e665025063bc6219f45bddb64233f1a68
parent 168541 806bdab53ee18ba5dd1e86ee002bc7ab0c7a441e (current diff)
parent 168633 551efcc4de6f1c965179975e3b12ab9e9e4c208a (diff)
child 168639 f37f87abf269848b375790c0d012393726aef14f
push idunknown
push userunknown
push dateunknown
milestone28.0a1
Merge mozilla-central into holly
CLOBBER
accessible/src/base/nsAccessibilityService.cpp
accessible/tests/mochitest/testTextboxes.js
accessible/tests/mochitest/test_textboxes.html
accessible/tests/mochitest/test_textboxes.xul
browser/components/nsBrowserGlue.js
browser/metro/locales/en-US/chrome/crashprompt.properties
gfx/src/nsThemeConstants.h
layout/base/nsDisplayList.cpp
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
modules/libpref/src/init/all.js
widget/cocoa/nsNativeThemeCocoa.mm
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 937317 required a clobber on Windows landing, backout probably will too
+Bug 908695 required a clobber on Windows because bug 928195
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -1305,17 +1305,17 @@ nsAccessibilityService::CreateAccessible
 
   } else if (role.EqualsLiteral("xul:tabs")) {
     accessible = new XULTabsAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:text")) {
     accessible = new XULLabelAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:textbox")) {
-    accessible = new XULTextFieldAccessible(aContent, aDoc);
+    accessible = new EnumRoleAccessible(aContent, aDoc, roles::SECTION);
 
   } else if (role.EqualsLiteral("xul:thumb")) {
     accessible = new XULThumbAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:tree")) {
     accessible = CreateAccessibleForXULTree(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:treecolumns")) {
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -1047,108 +1047,86 @@ Accessible::TakeFocus()
   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(focusContent));
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   if (fm)
     fm->SetFocus(element, 0);
 
   return NS_OK;
 }
 
-ENameValueFlag
-Accessible::GetHTMLName(nsString& aLabel)
+void
+Accessible::XULElmName(DocAccessible* aDocument,
+                       nsIContent* aElm, nsString& aName)
 {
-  Accessible* labelAcc = nullptr;
-  HTMLLabelIterator iter(Document(), this);
-  while ((labelAcc = iter.Next())) {
-    nsTextEquivUtils::AppendTextEquivFromContent(this, labelAcc->GetContent(),
-                                                 &aLabel);
-    aLabel.CompressWhitespace();
-  }
-
-  if (!aLabel.IsEmpty())
-    return eNameOK;
-
-  nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
-  return aLabel.IsEmpty() ? eNameOK : eNameFromSubtree;
-}
-
-/**
-  * 3 main cases for XUL Controls to be labeled
-  *   1 - control contains label="foo"
-  *   2 - control has, as a child, a label element
-  *        - label has either value="foo" or children
-  *   3 - non-child label contains control="controlID"
-  *        - label has either value="foo" or children
-  * Once a label is found, the search is discontinued, so a control
-  *  that has a label child as well as having a label external to
-  *  the control that uses the control="controlID" syntax will use
-  *  the child label for its Name.
-  */
-ENameValueFlag
-Accessible::GetXULName(nsString& aName)
-{
+  /**
+   * 3 main cases for XUL Controls to be labeled
+   *   1 - control contains label="foo"
+   *   2 - control has, as a child, a label element
+   *        - label has either value="foo" or children
+   *   3 - non-child label contains control="controlID"
+   *        - label has either value="foo" or children
+   * Once a label is found, the search is discontinued, so a control
+   *  that has a label child as well as having a label external to
+   *  the control that uses the control="controlID" syntax will use
+   *  the child label for its Name.
+   */
+
   // CASE #1 (via label attribute) -- great majority of the cases
-  nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl =
-    do_QueryInterface(mContent);
+  nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl = do_QueryInterface(aElm);
   if (labeledEl) {
     labeledEl->GetLabel(aName);
   } else {
-    nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl =
-      do_QueryInterface(mContent);
+    nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl = do_QueryInterface(aElm);
     if (itemEl) {
       itemEl->GetLabel(aName);
     } else {
-      nsCOMPtr<nsIDOMXULSelectControlElement> select =
-        do_QueryInterface(mContent);
+      nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
       // Use label if this is not a select control element which 
       // uses label attribute to indicate which option is selected
       if (!select) {
-        nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(mContent));
+        nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aElm));
         if (xulEl)
           xulEl->GetAttribute(NS_LITERAL_STRING("label"), aName);
       }
     }
   }
 
   // CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
   if (aName.IsEmpty()) {
     Accessible* labelAcc = nullptr;
-    XULLabelIterator iter(Document(), mContent);
+    XULLabelIterator iter(aDocument, aElm);
     while ((labelAcc = iter.Next())) {
       nsCOMPtr<nsIDOMXULLabelElement> xulLabel =
         do_QueryInterface(labelAcc->GetContent());
       // Check if label's value attribute is used
       if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(aName)) && aName.IsEmpty()) {
         // If no value attribute, a non-empty label must contain
         // children that define its text -- possibly using HTML
         nsTextEquivUtils::
-          AppendTextEquivFromContent(this, labelAcc->GetContent(), &aName);
+          AppendTextEquivFromContent(labelAcc, labelAcc->GetContent(), &aName);
       }
     }
   }
 
   aName.CompressWhitespace();
   if (!aName.IsEmpty())
-    return eNameOK;
+    return;
 
   // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
-  nsIContent *bindingParent = mContent->GetBindingParent();
-  nsIContent *parent = bindingParent? bindingParent->GetParent() :
-                                      mContent->GetParent();
+  nsIContent *bindingParent = aElm->GetBindingParent();
+  nsIContent* parent =
+    bindingParent? bindingParent->GetParent() : aElm->GetParent();
   while (parent) {
     if (parent->Tag() == nsGkAtoms::toolbaritem &&
         parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
       aName.CompressWhitespace();
-      return eNameOK;
+      return;
     }
     parent = parent->GetParent();
   }
-
-  nsTextEquivUtils::GetNameFromSubtree(this, aName);
-  return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
 }
 
 nsresult
 Accessible::HandleAccEvent(AccEvent* aEvent)
 {
   NS_ENSURE_ARG_POINTER(aEvent);
 
   nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
@@ -2473,21 +2451,40 @@ Accessible::ARIAName(nsString& aName)
     aName.CompressWhitespace();
   }
 }
 
 // Accessible protected
 ENameValueFlag
 Accessible::NativeName(nsString& aName)
 {
-  if (mContent->IsHTML())
-    return GetHTMLName(aName);
-
-  if (mContent->IsXUL())
-    return GetXULName(aName);
+  if (mContent->IsHTML()) {
+    Accessible* label = nullptr;
+    HTMLLabelIterator iter(Document(), this);
+    while ((label = iter.Next())) {
+      nsTextEquivUtils::AppendTextEquivFromContent(this, label->GetContent(),
+                                                   &aName);
+      aName.CompressWhitespace();
+    }
+
+    if (!aName.IsEmpty())
+      return eNameOK;
+
+    nsTextEquivUtils::GetNameFromSubtree(this, aName);
+    return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
+  }
+
+  if (mContent->IsXUL()) {
+    XULElmName(mDoc, mContent, aName);
+    if (!aName.IsEmpty())
+      return eNameOK;
+
+    nsTextEquivUtils::GetNameFromSubtree(this, aName);
+    return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
+  }
 
   if (mContent->IsSVG()) {
     // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
     // for processing, the user agent shall choose the first one.
     for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
          childElm = childElm->GetNextSibling()) {
       if (childElm->IsSVG(nsGkAtoms::title)) {
         nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -874,20 +874,20 @@ protected:
   // Name helpers
 
   /**
    * Returns the accessible name specified by ARIA.
    */
   void ARIAName(nsString& aName);
 
   /**
-   * Compute the name of HTML/XUL node.
+   * Return the name for XUL element.
    */
-  mozilla::a11y::ENameValueFlag GetHTMLName(nsString& aName);
-  mozilla::a11y::ENameValueFlag GetXULName(nsString& aName);
+  static void XULElmName(DocAccessible* aDocument,
+                         nsIContent* aElm, nsString& aName);
 
   // helper method to verify frames
   static nsresult GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut);
 
   /**
    * Return an accessible for the given DOM node, or if that node isn't
    * accessible, return the accessible for the next DOM node which has one
    * (based on forward depth first search).
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -323,26 +323,20 @@ HTMLTextFieldAccessible::NativeAttribute
 
 ENameValueFlag
 HTMLTextFieldAccessible::NativeName(nsString& aName)
 {
   ENameValueFlag nameFlag = Accessible::NativeName(aName);
   if (!aName.IsEmpty())
     return nameFlag;
 
-  if (mContent->GetBindingParent()) {
-    // XXX: bug 459640
-    // There's a binding parent.
-    // This means we're part of another control, so use parent accessible for name.
-    // This ensures that a textbox inside of a XUL widget gets
-    // an accessible name.
-    Accessible* parent = Parent();
-    if (parent)
-      parent->GetName(aName);
-  }
+  // If part of compound of XUL widget then grab a name from XUL widget element.
+  nsIContent* widgetElm = XULWidgetElm();
+  if (widgetElm)
+    XULElmName(mDoc, widgetElm, aName);
 
   if (!aName.IsEmpty())
     return eNameOK;
 
   // text inputs and textareas might have useful placeholder text
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, aName);
   return eNameOK;
 }
@@ -364,18 +358,23 @@ HTMLTextFieldAccessible::Value(nsString&
   if (input)
     input->GetValue(aValue);
 }
 
 void
 HTMLTextFieldAccessible::ApplyARIAState(uint64_t* aState) const
 {
   HyperTextAccessibleWrap::ApplyARIAState(aState);
+  aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState);
 
-  aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState);
+  // If part of compound of XUL widget then pick up ARIA stuff from XUL widget
+  // element.
+  nsIContent* widgetElm = XULWidgetElm();
+  if (widgetElm)
+    aria::MapToState(aria::eARIAAutoComplete, widgetElm->AsElement(), aState);
 }
 
 uint64_t
 HTMLTextFieldAccessible::NativeState()
 {
   uint64_t state = HyperTextAccessibleWrap::NativeState();
 
   // can be focusable, focused, protected. readonly, unavailable, selected
@@ -403,19 +402,18 @@ HTMLTextFieldAccessible::NativeState()
     state |= states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION;
     return state;
   }
 
   // Expose autocomplete state if it has associated autocomplete list.
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::list))
     return state | states::SUPPORTS_AUTOCOMPLETION | states::HASPOPUP;
 
-  // No parent can mean a fake widget created for XUL textbox. If accessible
-  // is unattached from tree then we don't care.
-  if (mParent && Preferences::GetBool("browser.formfill.enable")) {
+  // Ordinal XUL textboxes don't support autocomplete.
+  if (!XULWidgetElm() && Preferences::GetBool("browser.formfill.enable")) {
     // Check to see if autocompletion is allowed on this input. We don't expose
     // it for password fields even though the entire password can be remembered
     // for a page if the user asks it to be. However, the kind of autocomplete
     // we're talking here is based on what the user types, where a popup of
     // possible choices comes up.
     nsAutoString autocomplete;
     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete,
                       autocomplete);
--- a/accessible/src/html/HTMLFormControlAccessible.h
+++ b/accessible/src/html/HTMLFormControlAccessible.h
@@ -138,16 +138,21 @@ public:
 
   // Widgets
   virtual bool IsWidget() const;
   virtual Accessible* ContainerWidget() const;
 
 protected:
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
+
+  /**
+   * Return a XUL widget element this input is part of.
+   */
+  nsIContent* XULWidgetElm() const { return mContent->GetBindingParent(); }
 };
 
 
 /**
  * Accessible for input@type="file" element.
  */
 class HTMLFileInputAccessible : public HyperTextAccessibleWrap
 {
--- a/accessible/src/xul/XULFormControlAccessible.cpp
+++ b/accessible/src/xul/XULFormControlAccessible.cpp
@@ -631,197 +631,8 @@ XULToolbarSeparatorAccessible::NativeRol
   return roles::SEPARATOR;
 }
 
 uint64_t
 XULToolbarSeparatorAccessible::NativeState()
 {
   return 0;
 }
-
-////////////////////////////////////////////////////////////////////////////////
-// XULTextFieldAccessible
-////////////////////////////////////////////////////////////////////////////////
-
-XULTextFieldAccessible::
- XULTextFieldAccessible(nsIContent* aContent, DocAccessible* aDoc) :
- HyperTextAccessibleWrap(aContent, aDoc)
-{
-}
-
-NS_IMPL_ISUPPORTS_INHERITED2(XULTextFieldAccessible,
-                             Accessible,
-                             nsIAccessibleText,
-                             nsIAccessibleEditableText)
-
-////////////////////////////////////////////////////////////////////////////////
-// XULTextFieldAccessible: nsIAccessible
-
-void
-XULTextFieldAccessible::Value(nsString& aValue)
-{
-  aValue.Truncate();
-  if (NativeRole() == roles::PASSWORD_TEXT) // Don't return password text!
-    return;
-
-  nsCOMPtr<nsIDOMXULTextBoxElement> textBox(do_QueryInterface(mContent));
-  if (textBox) {
-    textBox->GetValue(aValue);
-    return;
-  }
-
-  nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
-  if (menuList)
-    menuList->GetLabel(aValue);
-}
-
-void
-XULTextFieldAccessible::ApplyARIAState(uint64_t* aState) const
-{
-  HyperTextAccessibleWrap::ApplyARIAState(aState);
-
-  aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState);
-}
-
-uint64_t
-XULTextFieldAccessible::NativeState()
-{
-  uint64_t state = HyperTextAccessibleWrap::NativeState();
-
-  nsCOMPtr<nsIContent> inputField(GetInputField());
-  NS_ENSURE_TRUE(inputField, state);
-
-  // Create a temporary accessible from the HTML text field to get
-  // the accessible state from. Doesn't add to cache into document cache.
-  nsRefPtr<HTMLTextFieldAccessible> tempAccessible =
-    new HTMLTextFieldAccessible(inputField, mDoc);
-  if (tempAccessible)
-    return state | tempAccessible->NativeState();
-  return state;
-}
-
-role
-XULTextFieldAccessible::NativeRole()
-{
-  if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                            nsGkAtoms::password, eIgnoreCase))
-    return roles::PASSWORD_TEXT;
-  
-  return roles::ENTRY;
-}
-
-/**
-  * Only one actions available
-  */
-uint8_t
-XULTextFieldAccessible::ActionCount()
-{
-  return 1;
-}
-
-/**
-  * Return the name of our only action
-  */
-NS_IMETHODIMP
-XULTextFieldAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
-{
-  if (aIndex == eAction_Click) {
-    aName.AssignLiteral("activate"); 
-    return NS_OK;
-  }
-  return NS_ERROR_INVALID_ARG;
-}
-
-/**
-  * Tell the button to do its action
-  */
-NS_IMETHODIMP
-XULTextFieldAccessible::DoAction(uint8_t index)
-{
-  if (index == 0) {
-    nsCOMPtr<nsIDOMXULElement> element(do_QueryInterface(mContent));
-    if (element)
-    {
-      element->Focus();
-      return NS_OK;
-    }
-    return NS_ERROR_FAILURE;
-  }
-  return NS_ERROR_INVALID_ARG;
-}
-
-bool
-XULTextFieldAccessible::CanHaveAnonChildren()
-{
-  return false;
-}
-
-bool
-XULTextFieldAccessible::IsAcceptableChild(Accessible* aPossibleChild) const
-{
-  // XXX: entry shouldn't contain anything but text leafs. Currently it may
-  // contain a trailing fake HTML br element added for layout needs. We don't
-  // need to expose it since it'd be confusing for AT.
-  return aPossibleChild->IsTextLeaf();
-}
-
-already_AddRefed<nsIEditor>
-XULTextFieldAccessible::GetEditor() const
-{
-  nsCOMPtr<nsIContent> inputField = GetInputField();
-  nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(inputField));
-  if (!editableElt)
-    return nullptr;
-
-  nsCOMPtr<nsIEditor> editor;
-  editableElt->GetEditor(getter_AddRefs(editor));
-  return editor.forget();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// XULTextFieldAccessible: Accessible protected
-
-void
-XULTextFieldAccessible::CacheChildren()
-{
-  NS_ENSURE_TRUE_VOID(mDoc);
-  // Create child accessibles for native anonymous content of underlying HTML
-  // input element.
-  nsCOMPtr<nsIContent> inputContent(GetInputField());
-  if (!inputContent)
-    return;
-
-  TreeWalker walker(this, inputContent);
-  while (Accessible* child = walker.NextChild())
-    AppendChild(child);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// XULTextFieldAccessible: HyperTextAccessible protected
-
-already_AddRefed<nsFrameSelection>
-XULTextFieldAccessible::FrameSelection() const
-{
-  nsCOMPtr<nsIContent> inputContent(GetInputField());
-  NS_ASSERTION(inputContent, "No input content");
-  if (!inputContent)
-    return nullptr;
-
-  nsIFrame* frame = inputContent->GetPrimaryFrame();
-  return frame ? frame->GetFrameSelection() : nullptr;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// XULTextFieldAccessible protected
-
-already_AddRefed<nsIContent>
-XULTextFieldAccessible::GetInputField() const
-{
-  nsCOMPtr<nsIDOMNode> inputFieldDOMNode;
-  nsCOMPtr<nsIDOMXULTextBoxElement> textBox = do_QueryInterface(mContent);
-  if (textBox)
-    textBox->GetInputField(getter_AddRefs(inputFieldDOMNode));
-
-  NS_ASSERTION(inputFieldDOMNode, "No input field for XULTextFieldAccessible");
-
-  nsCOMPtr<nsIContent> inputField = do_QueryInterface(inputFieldDOMNode);
-  return inputField.forget();
-}
--- a/accessible/src/xul/XULFormControlAccessible.h
+++ b/accessible/src/xul/XULFormControlAccessible.h
@@ -210,54 +210,13 @@ public:
   XULToolbarSeparatorAccessible(nsIContent* aContent,
                                 DocAccessible* aDoc);
 
   // Accessible
   virtual mozilla::a11y::role NativeRole();
   virtual uint64_t NativeState();
 };
 
-/**
- * Used for XUL textbox element.
- */
-class XULTextFieldAccessible : public HyperTextAccessibleWrap
-{
-public:
-  enum { eAction_Click = 0 };
-
-  XULTextFieldAccessible(nsIContent* aContent, DocAccessible* aDoc);
-
-  NS_DECL_ISUPPORTS_INHERITED
-
-  // nsIAccessible
-  NS_IMETHOD GetActionName(uint8_t aIndex, nsAString& aName);
-  NS_IMETHOD DoAction(uint8_t index);
-
-  // HyperTextAccessible
-  virtual already_AddRefed<nsIEditor> GetEditor() const;
-
-  // Accessible
-  virtual void Value(nsString& aValue);
-  virtual void ApplyARIAState(uint64_t* aState) const;
-  virtual mozilla::a11y::role NativeRole();
-  virtual uint64_t NativeState();
-  virtual bool CanHaveAnonChildren();
-  virtual bool IsAcceptableChild(Accessible* aPossibleChild) const MOZ_OVERRIDE;
-
-  // ActionAccessible
-  virtual uint8_t ActionCount();
-
-protected:
-  // Accessible
-  virtual void CacheChildren();
-
-  // HyperTextAccessible
-  virtual already_AddRefed<nsFrameSelection> FrameSelection() const;
-
-  // nsXULTextFieldAccessible
-  already_AddRefed<nsIContent> GetInputField() const;
-};
-
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 
--- a/accessible/src/xul/XULListboxAccessible.cpp
+++ b/accessible/src/xul/XULListboxAccessible.cpp
@@ -630,17 +630,17 @@ XULListitemAccessible::NativeName(nsStri
   if (childContent) {
     if (childContent->NodeInfo()->Equals(nsGkAtoms::listcell,
                                          kNameSpaceID_XUL)) {
       childContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
       return eNameOK;
     }
   }
 
-  return GetXULName(aName);
+  return Accessible::NativeName(aName);
 }
 
 role
 XULListitemAccessible::NativeRole()
 {
   Accessible* list = GetListAccessible();
   if (!list) {
     NS_ERROR("No list accessible for listitem accessible!");
--- a/accessible/tests/mochitest/a11y.ini
+++ b/accessible/tests/mochitest/a11y.ini
@@ -16,21 +16,18 @@ support-files =
   name.js
   pivot.js
   relations.js
   role.js
   selectable.js
   states.js
   table.js
   value.js
-  testTextboxes.js
   text.js
   treeview.css
   treeview.js
 
 [test_aria_token_attrs.html]
 [test_bug420863.html]
 [test_descr.html]
 [test_nsIAccessibleDocument.html]
 [test_nsIAccessibleImage.html]
 [test_OuterDocAccessible.html]
-[test_textboxes.html]
-[test_textboxes.xul]
--- a/accessible/tests/mochitest/actions.js
+++ b/accessible/tests/mochitest/actions.js
@@ -86,16 +86,33 @@ function testActions(aArray)
     var invoker = new actionInvoker(accOrElmOrID, actionIndex, actionName,
                                     eventSeq);
     gActionsQueue.push(invoker);
   }
 
   gActionsQueue.invoke();
 }
 
+/**
+ * Test action names and descriptions.
+ */
+function testActionNames(aID, aActions)
+{
+  var actions = (typeof aActions == "string") ?
+    [ aActions ] : (aActions || []);
+
+  var acc = getAccessible(aID);
+  is(acc.actionCount, actions.length, "Wong number of actions.");
+  for (var i = 0; i < actions.length; i++ ) {
+    is(acc.getActionName(i), actions[i], "Wrong action name at " + i + " index.");
+    is(acc.getActionDescription(0), gActionDescrMap[actions[i]],
+       "Wrong action description at " + i + "index.");
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Private
 
 var gActionsQueue = null;
 
 function actionInvoker(aAccOrElmOrId, aActionIndex, aActionName, aEventSeq)
 {
   this.invoke = function actionInvoker_invoke()
@@ -146,8 +163,25 @@ function checkerOfActionInvoker(aType, a
   }
 
   this.check = function check(aEvent)
   {
     if (aActionObj && "checkOnClickEvent" in aActionObj)
       aActionObj.checkOnClickEvent(aEvent);
   }
 }
+
+var gActionDescrMap =
+{
+  jump: "Jump",
+  press: "Press",
+  check: "Check",
+  uncheck: "Uncheck",
+  select: "Select",
+  open: "Open",
+  close: "Close",
+  switch: "Switch",
+  click: "Click",
+  collapse: "Collapse",
+  expand: "Expand",
+  activate: "Activate",
+  cycle: "Cycle"
+};
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -324,16 +324,24 @@ function getTabDocAccessible(aAccOrElmOr
  */
 function getApplicationAccessible()
 {
   return gAccRetrieval.getApplicationAccessible().
     QueryInterface(nsIAccessibleApplication);
 }
 
 /**
+ * A version of accessible tree testing, doesn't fail if tree is not complete.
+ */
+function testElm(aID, aTreeObj)
+{
+  testAccessibleTree(aID, aTreeObj, kSkipTreeFullCheck);
+}
+
+/**
  * Flags used for testAccessibleTree
  */
 const kSkipTreeFullCheck = 1;
 
 /**
  * Compare expected and actual accessibles trees.
  *
  * @param  aAccOrElmOrID  [in] accessible identifier
@@ -365,21 +373,17 @@ function testAccessibleTree(aAccOrElmOrI
   }
 
   // Test accessible properties.
   for (var prop in accTree) {
     var msg = "Wrong value of property '" + prop + "' for " + prettyName(acc) + ".";
 
     switch (prop) {
     case "actions": {
-      var actions = (typeof accTree.actions == "string") ?
-        [ accTree.actions ] : (accTree.actions || []);
-      is(acc.actionCount, actions.length, "Wong number of actions.");
-      for (var i = 0; i < actions.length; i++ )
-        is(acc.getActionName(i), actions[i], "Wrong action name at " + i + " index.");
+      testActionNames(acc, accTree.actions);
       break;
     }
 
     case "attributes":
       testAttrs(acc, accTree[prop], true);
       break;
 
     case "absentAttributes":
--- a/accessible/tests/mochitest/elm/test_HTMLSpec.html
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -6,32 +6,29 @@
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
+          src="../actions.js"></script>
+  <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
   <script type="application/javascript"
           src="../attributes.js"></script>
   <script type="application/javascript"
           src="../relations.js"></script>
   <script type="application/javascript"
           src="../name.js"></script>
 
   <script type="application/javascript">
-    function testElm(aID, aTreeObj)
-    {
-      testAccessibleTree(aID, aTreeObj, kSkipTreeFullCheck);
-    }
-
     function doTest()
     {
       //////////////////////////////////////////////////////////////////////////
       // HTML:a@href
 
       var obj = {
         role: ROLE_LINK,
         states: STATE_LINKED,
--- a/accessible/tests/mochitest/events/test_caretmove.xul
+++ b/accessible/tests/mochitest/events/test_caretmove.xul
@@ -23,30 +23,30 @@
 
     //gA11yEventDumpID = "eventdump"; // debug stuff
     //gA11yEventDumpToConsole = true;
 
     var gQueue = null;
 
     function doTests()
     {
-
       if (MAC) {
         todo(false, "Make these tests pass on OSX (bug 650294)");
         SimpleTest.finish();
         return;
       }
 
       gQueue = new eventQueue(EVENT_TEXT_CARET_MOVED);
 
       var id = "textbox";
-      gQueue.push(new synthFocus(id, new caretMoveChecker(5, id)));
-      gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, id)));
-      gQueue.push(new synthHomeKey(id, new caretMoveChecker(0, id)));
-      gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
+      var input = getNode(id).inputField;
+      gQueue.push(new synthFocus(id, new caretMoveChecker(5, input)));
+      gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, input)));
+      gQueue.push(new synthHomeKey(id, new caretMoveChecker(0, input)));
+      gQueue.push(new synthRightKey(id, new caretMoveChecker(1, input)));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 
--- a/accessible/tests/mochitest/events/test_focus_general.xul
+++ b/accessible/tests/mochitest/events/test_focus_general.xul
@@ -33,18 +33,20 @@
     //gA11yEventDumpToConsole = true; // debug stuff
 
     var gQueue = null;
     function doTests()
     {
       // Test focus events.
       gQueue = new eventQueue();
 
-      gQueue.push(new synthFocus("textbox"));
-      gQueue.push(new synthFocus("textbox_multiline"));
+      gQueue.push(new synthFocus("textbox",
+                                 new focusChecker(getNode("textbox").inputField)));
+      gQueue.push(new synthFocus("textbox_multiline",
+                                 new focusChecker(getNode("textbox_multiline").inputField)));
       gQueue.push(new synthFocus("scale"));
       gQueue.push(new synthFocusOnFrame("editabledoc"));
       gQueue.push(new synthFocus("radioclothes",
                                  new focusChecker("radiosweater")));
       gQueue.push(new synthDownKey("radiosweater",
                                    new focusChecker("radiojacket")));
       gQueue.push(new synthFocus("checkbox"));
       gQueue.push(new synthFocus("button"));
--- a/accessible/tests/mochitest/events/test_focus_listcontrols.xul
+++ b/accessible/tests/mochitest/events/test_focus_listcontrols.xul
@@ -17,17 +17,17 @@
           src="../role.js" />
   <script type="application/javascript"
           src="../states.js" />
   <script type="application/javascript"
           src="../events.js" />
 
   <script type="application/javascript">
     //gA11yEventDumpID = "eventdump"; // debug stuff
-    //gA11yEventDumpToConsole = true; // debug stuff
+    gA11yEventDumpToConsole = true; // debug stuff
 
     var gQueue = null;
     function doTests()
     {
       // Test focus events.
       gQueue = new eventQueue();
 
       gQueue.push(new synthFocus("listbox", new focusChecker("lb_item1")));
--- a/accessible/tests/mochitest/events/test_focus_tabbox.xul
+++ b/accessible/tests/mochitest/events/test_focus_tabbox.xul
@@ -31,20 +31,21 @@
         todo(false, "Tests disabled because of imminent failure.");
         SimpleTest.finish();
         return;
       }
 
       // Test focus events.
       gQueue = new eventQueue();
 
+      var textbox = getNode("textbox").inputField;
       gQueue.push(new synthClick("tab1", new focusChecker("tab1")));
       gQueue.push(new synthTab("tab1", new focusChecker("checkbox1")));
       gQueue.push(new synthKey("tab1", "VK_TAB", { ctrlKey: true },
-                               new focusChecker("textbox")));
+                               new focusChecker(textbox)));
       gQueue.push(new synthKey("tab2", "VK_TAB", { ctrlKey: true },
                                new focusChecker("tab3")));
       gQueue.push(new synthKey("tab3", "VK_TAB", { ctrlKey: true },
                                new focusChecker("tab1")));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
--- a/accessible/tests/mochitest/name/test_general.xul
+++ b/accessible/tests/mochitest/name/test_general.xul
@@ -28,17 +28,17 @@
       // Simple label provided via ARIA
       testName("btn_simple_aria_label", "I am a button");
 
       // aria-label and aria-labelledby, expect aria-labelledby
       testName("btn_both_aria_labels", "text I am a button, two");
 
       //////////////////////////////////////////////////////////////////////////
       // aria-labelledby
-      
+
       // Single relation. The value of 'aria-labelledby' contains the ID of
       // an element. Gets the name from text node of that element.
       testName("btn_labelledby_text", "text");
 
       // Multiple relations. The value of 'aria-labelledby' contains the IDs
       // of elements. Gets the name from text nodes of those elements.
       testName("btn_labelledby_texts", "text1 text2");
 
--- a/accessible/tests/mochitest/states/test_textbox.xul
+++ b/accessible/tests/mochitest/states/test_textbox.xul
@@ -13,94 +13,99 @@
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../states.js" />
 
   <script type="application/javascript">
   <![CDATA[
+    function getInput(aID)
+    {
+      return getNode(aID).inputField;
+    }
+
     function doTest()
     {
       //////////////////////////////////////////////////////////////////////////
       // Ordinary textbox
-      testStates("textbox",
+      testStates(getInput("textbox"),
                  STATE_FOCUSABLE,
                  EXT_STATE_EDITABLE,
                  STATE_PROTECTED | STATE_UNAVAILABLE,
                  EXT_STATE_SUPPORTS_AUTOCOMPLETION,
                  "ordinary textbox");
 
       //////////////////////////////////////////////////////////////////////////
       // Password textbox
-      testStates("password",
+      testStates(getInput("password"),
                  STATE_FOCUSABLE | STATE_PROTECTED,
                  EXT_STATE_EDITABLE,
                  STATE_UNAVAILABLE,
                  EXT_STATE_SUPPORTS_AUTOCOMPLETION,
                  "password textbox");
 
       //////////////////////////////////////////////////////////////////////////
       // Textarea
-      testStates("textarea",
+      testStates(getInput("textarea"),
                  STATE_FOCUSABLE,
                  EXT_STATE_EDITABLE | EXT_STATE_MULTI_LINE,
                  STATE_PROTECTED | STATE_UNAVAILABLE,
                  EXT_STATE_SUPPORTS_AUTOCOMPLETION,
                  "multiline textbox");
 
       //////////////////////////////////////////////////////////////////////////
       // Readonly textbox
-      testStates("readonly_textbox",
+      testStates(getInput("readonly_textbox"),
                  STATE_FOCUSABLE | STATE_READONLY,
                  EXT_STATE_EDITABLE,
                  STATE_PROTECTED | STATE_UNAVAILABLE,
                  EXT_STATE_SUPPORTS_AUTOCOMPLETION,
                  "readonly textbox");
 
       //////////////////////////////////////////////////////////////////////////
       // Disabled textbox
-      testStates("disabled_textbox",
+      testStates(getInput("disabled_textbox"),
                  STATE_UNAVAILABLE,
                  EXT_STATE_EDITABLE,
                  STATE_FOCUSABLE | STATE_PROTECTED,
                  EXT_STATE_SUPPORTS_AUTOCOMPLETION,
                  "readonly textbox");
 
       //////////////////////////////////////////////////////////////////////////
       // Readonly textarea
-      testStates("readonly_textarea",
+      testStates(getInput("readonly_textarea"),
                  STATE_FOCUSABLE | STATE_READONLY,
                  EXT_STATE_EDITABLE | EXT_STATE_MULTI_LINE,
                  STATE_PROTECTED | STATE_UNAVAILABLE,
                  EXT_STATE_SUPPORTS_AUTOCOMPLETION,
                  "readonly multiline textbox");
 
       //////////////////////////////////////////////////////////////////////////
       // Disabled textarea
-      testStates("disabled_textarea",
+      testStates(getInput("disabled_textarea"),
                  STATE_UNAVAILABLE,
                  EXT_STATE_EDITABLE| EXT_STATE_MULTI_LINE,
                  STATE_PROTECTED | STATE_FOCUSABLE,
                  EXT_STATE_SUPPORTS_AUTOCOMPLETION,
                  "readonly multiline textbox");
 
       //////////////////////////////////////////////////////////////////////////
       // Search textbox without search button, searches as you type and filters
       // a separate control.
-      testStates("searchbox",
+      testStates(getInput("searchbox"),
                  STATE_FOCUSABLE,
                  EXT_STATE_EDITABLE | EXT_STATE_SUPPORTS_AUTOCOMPLETION,
                  STATE_PROTECTED | STATE_UNAVAILABLE,
                  0,
                  "searchbox");
 
       //////////////////////////////////////////////////////////////////////////
       // Search textbox with search button, does not support autoCompletion.
-      testStates("searchfield",
+      testStates(getInput("searchfield"),
                  STATE_FOCUSABLE,
                  EXT_STATE_EDITABLE,
                  STATE_PROTECTED | STATE_UNAVAILABLE,
                  EXT_STATE_SUPPORTS_AUTOCOMPLETION,
                  "searchfield");
 
       SimpleTest.finish();
     }
deleted file mode 100644
--- a/accessible/tests/mochitest/testTextboxes.js
+++ /dev/null
@@ -1,33 +0,0 @@
-function testValue(aID, aAcc, aValue, aRole)
-{
-  is(aAcc.value, aValue, "Wrong value for " + aID + "!");
-}
-
-function testAction(aID, aAcc, aActionCount, aActionName, aActionDescription)
-{
-  var actionCount = aAcc.actionCount;
-  is(actionCount, aActionCount, "Wrong number of actions for " + aID + "!");
-
-  if (actionCount != 0) {
-    // Test first action. Normally only 1 should be present.
-    is(aAcc.getActionName(0), aActionName,
-       "Wrong name of action for " + aID + "!");
-    is(aAcc.getActionDescription(0), aActionDescription,
-       "Wrong description of action for " + aID + "!");
-  }
-}
-
-function testThis(aID, aName, aValue, aDescription, aRole,
-                  aActionCount, aActionName, aActionDescription)
-{
-  var acc = getAccessible(aID);
-  if (!acc)
-    return;
-
-  is(acc.name, aName, "Wrong name for " + aID + "!");
-  testValue(aID, acc, aValue, aRole);
-  is(acc.description, aDescription, "Wrong description for " + aID + "!");
-  testRole(aID, aRole);
-
-  testAction(aID, acc, aActionCount, aActionName, aActionDescription);
-}
deleted file mode 100644
--- a/accessible/tests/mochitest/test_textboxes.html
+++ /dev/null
@@ -1,164 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=442648
--->
-<head>
-  <title>nsIAccessible textboxes chrome tests</title>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-
-  <script type="application/javascript"
-          src="common.js"></script>
-  <script type="application/javascript"
-          src="role.js"></script>
-  <script type="application/javascript"
-          src="states.js"></script>
-
-  <script type="application/javascript"
-          src="testTextboxes.js"></script>
-
-  <script type="application/javascript">
-    function doTest()
-    {
-      //////////////////////////////////////////////////////////////////////////
-      // normal textbox without content and with no proper label
-      testThis("unlabelled_Textbox", // ID
-               null, // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // normal textbox without content and with a proper label
-      testThis("labelled_textbox", // ID
-               "Second textbox:", // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // normal textbox with content and with a proper label
-      testThis("prefilled_textbox", // ID
-               "Textbox with predefined value:", // name
-               "I have some text", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // password textbox with a proper label
-      testThis("password_textbox", // ID
-               "Enter some password here:", // name
-               "", // value
-               "", // description
-               ROLE_PASSWORD_TEXT, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // textarea without content and label
-      testThis("unlabelled_Textarea", // ID
-               null, // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // textarea without content and with proper label
-      testThis("labelled_textarea", // ID
-               "Labelled textarea:", // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // textarea with content and with proper label
-      testThis("prefilled_textarea", // ID
-               "Pre-filled textarea:", // name
-               "    I also have some text.\n    ", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // readonly textbox with content and with proper label
-      testThis("readonly_textbox", // ID
-               "The following is a read-only textbox:", // name
-               "You cannot change me.", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // readonly textarea with content and with proper label
-      testThis("readonly_textarea", // ID
-               "This textarea is readonly, too:", // name
-               "    You cannot change me, either.\n    ", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      SimpleTest.finish();
-    }
-
-    SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
-  </script>
-</head>
-<body>
-
-  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=442648">Mozilla Bug 442648</a>
-  <p id="display"></p>
-  <div id="content" style="display: none"></div>
-  <pre id="test">
-  </pre>
-  <form action="test.php" method="post">
-    Text before input without labelling it:
-    <input type="text" name="unlabelled_Textbox" id="unlabelled_Textbox" size="50"/>
-    <br><label for="labelled_textbox">Second textbox:</label>
-    <input type="text" name="labelled_Textbox" id="labelled_textbox"/>
-    <br><label for="prefilled_textbox">Textbox with predefined value:</label>
-    <input type="text" name="prefilled_Textbox" id="prefilled_textbox" value="I have some text" size="80"/>
-    <br><label for="password_textbox">Enter some password here:</label>
-    <input type="password" name="password_Textbox" id="password_textbox"/>
-    <br>Textarea without label:<br>
-    <textarea id="unlabelled_Textarea" name="unlabelled_Textarea" cols="80" rows="5"></textarea>
-    <br><label for="labelled_textarea">Labelled textarea:</label><br>
-    <textarea id="labelled_textarea" name="labelled_Textarea" cols="80" rows="5"></textarea>
-    <br><label for="prefilled_textarea">Pre-filled textarea:</label><br>
-    <textarea id="prefilled_textarea" name="prefilled_Textarea" cols="80" rows="5">
-    I also have some text.
-    </textarea>
-    <br><label for="readonly_textbox">The following is a read-only textbox:</label>
-    <input type="text" readonly="true" name="readonly_Textbox" id="readonly_textbox" value="You cannot change me."/>
-    <br><label for="readonly_textarea">This textarea is readonly, too:</label><br>
-    <textarea name="readonly_Textarea" id="readonly_textarea" readonly="true" cols="80" rows="5">
-    You cannot change me, either.
-    </textarea>
-  </form>
-</body>
-</html>
deleted file mode 100644
--- a/accessible/tests/mochitest/test_textboxes.xul
+++ /dev/null
@@ -1,217 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
-                 type="text/css"?>
-
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        title="nsIAccessible XUL textboxes chrome tests">
-
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
-
-  <script type="application/javascript"
-          src="common.js" />
-  <script type="application/javascript"
-          src="role.js" />
-  <script type="application/javascript"
-          src="states.js" />
-
-  <script type="application/javascript"
-          src="testTextboxes.js" />
-
-  <script type="application/javascript">
-  <![CDATA[
-    function doTest()
-    {
-      //////////////////////////////////////////////////////////////////////////
-      // normal textbox without content and with no proper label
-      testThis("unlabelled_Textbox", // ID
-               null, // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // normal textbox without content and with a proper label
-      testThis("labelled_textbox", // ID
-               "Second textbox:", // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // normal textbox with content and with a proper label
-      testThis("prefilled_textbox", // ID
-               "Textbox with predefined value:", // name
-               "I have some text", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // password textbox with a proper label
-      testThis("password_textbox", // ID
-               "Enter some password here:", // name
-               "", // value
-               "", // description
-               ROLE_PASSWORD_TEXT, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // textarea without content and label
-      testThis("unlabelled_Textarea", // ID
-               null, // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // textarea without content and with proper label
-      testThis("labelled_textarea", // ID
-               "Labelled textarea:", // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // textarea with content and with proper label
-      testThis("prefilled_textarea", // ID
-               "Pre-filled textarea:", // name
-               "I also have some text.", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // readonly textbox with content and with proper label
-      testThis("readonly_textbox", // ID
-               "The following is a read-only textbox:", // name
-               "You cannot change me.", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // readonly textarea with content and with proper label
-      testThis("readonly_textarea", // ID
-               "This textarea is readonly, too:", // name
-               "You cannot change me, either.", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // Search textbox without search button, searches as you type and filters
-      // a separate control.
-      testThis("search-box", // ID
-               "Search History:", // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-
-      //////////////////////////////////////////////////////////////////////////
-      // Search textbox with search button, does not support autoCompletion.
-      testThis("searchfield", // ID
-               "Search all add-ons", // name
-               "", // value
-               "", // description
-               ROLE_ENTRY, // role
-               1, // actionCount
-               "activate",  // ActionName
-               "Activate"); // ActionDescription
-      testStates("searchfield", 0, 0, 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
-
-      SimpleTest.finish();
-    }
-
-    SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
-  ]]>
-  </script>
-
-  <body xmlns="http://www.w3.org/1999/xhtml">
-    <a target="_blank"
-       href="https://bugzilla.mozilla.org/show_bug.cgi?id=442648">
-      Mozilla Bug 442648
-    </a>
-    <p id="display"></p>
-    <div id="content" style="display: none"></div>
-    <pre id="test">
-    </pre>
-  </body>
-
-  <vbox>
-    <hbox>
-      <label value="Text before input without labelling it:"/>
-      <textbox id="unlabelled_Textbox" size="50"/>
-    </hbox>
-    <hbox>
-      <label control="labelled_textbox">Second textbox:</label>
-      <textbox id="labelled_textbox"/>
-    </hbox>
-    <hbox>
-      <label control="prefilled_textbox">Textbox with predefined value:</label>
-      <textbox id="prefilled_textbox" value="I have some text" size="80"/>
-    </hbox>
-    <hbox>
-      <label control="password_textbox">Enter some password here:</label>
-      <textbox type="password" id="password_textbox"/>
-    </hbox>
-    <vbox>
-      <label value="Textarea without label:"/>
-      <textbox multiline="true" id="unlabelled_Textarea" cols="80" rows="5"/>
-    </vbox>
-    <vbox>
-      <label control="labelled_textarea">Labelled textarea:</label>
-      <textbox multiline="true" id="labelled_textarea" cols="80" rows="5"/>
-    </vbox>
-    <vbox>
-      <label control="prefilled_textarea">Pre-filled textarea:</label>
-      <textbox multiline="true" id="prefilled_textarea" cols="80" rows="5"
-               value="I also have some text."/>
-    </vbox>
-    <hbox>
-      <label control="readonly_textbox">The following is a read-only textbox:</label>
-      <textbox readonly="true" id="readonly_textbox"
-               value="You cannot change me."/>
-    </hbox>
-    <vbox>
-      <label control="readonly_textarea">This textarea is readonly, too:</label>
-      <textbox multiline="true" id="readonly_textarea" readonly="true" cols="80"
-               rows="5" value="You cannot change me, either."/>
-    </vbox>
-    <hbox>
-      <label value="Search History:" accesskey="S" 
-             control="search-box"/>
-	  <textbox id="search-box" flex="1" type="search"
-	           results="historyTree"/>
-    </hbox>
-    <textbox id="searchfield" placeholder="Search all add-ons"
-             type="search" searchbutton="true"/>
-  </vbox>
-</window>
--- a/accessible/tests/mochitest/text/test_general.xul
+++ b/accessible/tests/mochitest/text/test_general.xul
@@ -38,17 +38,17 @@
 
       testCharAfterOffset(ids, 0, "e", 1, 2);
       testCharBeforeOffset(ids, 1, "H", 0, 1);
       testCharAtOffset(ids, 1, "e", 1, 2);
 
       //////////////////////////////////////////////////////////////////////////
       // XUL textbox
 
-      testTextAtOffset([ "tbox1" ], BOUNDARY_LINE_START,
+      testTextAtOffset([ getNode("tbox1").inputField ], BOUNDARY_LINE_START,
                        [ [ 0, 4, "test", 0, 4 ] ]);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   ]]>
--- a/accessible/tests/mochitest/tree/test_txtctrl.xul
+++ b/accessible/tests/mochitest/tree/test_txtctrl.xul
@@ -18,74 +18,78 @@
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     function doTest()
     {
-      ////////////////////
-      // textbox
-      ////////////////////
-      var accTree = {
-        role: ROLE_ENTRY,
-        children: [
-          {
-            role: ROLE_TEXT_LEAF,
-            children: []
-          }
-        ]
-      };
+      //////////////////////////////////////////////////////////////////////////
+      // textboxes
+
+      var accTree =
+        { SECTION: [
+          { ENTRY: [ { TEXT_LEAF: [] } ] },
+          { MENUPOPUP: [] }
+        ] };
 
       // default textbox
-      testAccessibleTree("txc1", accTree);
+      testAccessibleTree("txc", accTree);
 
-      // number textbox
-      testAccessibleTree("txc2", accTree);
+      // multiline
+      testAccessibleTree("txc_multiline", accTree);
 
+      //////////////////////////////////////////////////////////////////////////
       // search textbox
-      testAccessibleTree("txc3", accTree);
+
+      if (MAC) {
+        accTree =
+          { SECTION: [
+            { ENTRY: [ { TEXT_LEAF: [] } ] },
+            { MENUPOPUP: [] }
+          ] };
+      } else {
+        accTree =
+          { SECTION: [
+            { ENTRY: [ { TEXT_LEAF: [] } ] },
+            { PUSHBUTTON: [] },
+            { MENUPOPUP: [] }
+          ] };
+      }
 
-      // timed textbox
-      testAccessibleTree("txc4_deprecated", accTree);
+      testAccessibleTree("txc_search", accTree);
+
+      //////////////////////////////////////////////////////////////////////////
+      // number textbox
 
-      ////////////////////
+      accTree =
+        { SECTION: [
+          { ENTRY: [ { TEXT_LEAF: [] } ] },
+          { MENUPOPUP: [] },
+          { PUSHBUTTON: [] },
+          { PUSHBUTTON: [] }
+        ] };
+
+      testAccessibleTree("txc_number", accTree);
+
+      //////////////////////////////////////////////////////////////////////////
       // password textbox
-      ////////////////////
-      accTree = {
-        role: ROLE_PASSWORD_TEXT,
-        children: [
-          {
-            role: ROLE_TEXT_LEAF,
-            children: []
-          }
-        ]
-      };
-
-      testAccessibleTree("txc5", accTree);
 
-      ////////////////////
-      // multiline textbox
-      ////////////////////
-      accTree = {
-        role: ROLE_ENTRY,
-        children: [
-          {
-            role: ROLE_TEXT_LEAF,
-            children: []
-          }
-        ]
-      };
+      accTree =
+        { SECTION: [
+          { PASSWORD_TEXT: [ { TEXT_LEAF: [] } ] },
+          { MENUPOPUP: [] }
+        ] };
 
-      testAccessibleTree("txc6", accTree);
+      testAccessibleTree("txc_password", accTree);
 
-      ////////////////////
+      //////////////////////////////////////////////////////////////////////////
       // autocomplete textbox
-      ////////////////////
+
       accTree = {
         // textbox
         role: ROLE_AUTOCOMPLETE,
         children: [
           {
             // html:input
             role: ROLE_ENTRY,
             children: [
@@ -99,24 +103,24 @@
           {
             // xul:menupopup
             role: ROLE_COMBOBOX_LIST,
             children: []
           }
         ]
       };
 
-      function test_txc7() {
-        testAccessibleTree("txc7", accTree);
+      function test_AutocompleteControl() {
+        testAccessibleTree("txc_autocomplete", accTree);
         SimpleTest.finish();
       }
 
       // XPFE and Toolkit autocomplete widgets differ.
-      var txc7 = document.getElementById("txc7");
-      if ("clearResults" in txc7) {
+      var txc = document.getElementById("txc_autocomplete");
+      if ("clearResults" in txc) {
         SimpleTest.ok(true, "Testing (Old) XPFE autocomplete widget.");
 
         // Popup is always created. (See code below.)
 
         accTree.children.push(
           {
             // xul:panel
             role: ROLE_COMBOBOX_LIST,
@@ -136,24 +140,24 @@
                       }
                     ]
                   }
                 ]
               }
             ]
           }
         );
-        test_txc7();
+        test_AutocompleteControl();
 
       } else {
         SimpleTest.ok(true, "Testing (New) Toolkit autocomplete widget.");
 
         // Dumb access to trigger popup lazy creation.
-        waitForEvent(EVENT_REORDER, txc7, test_txc7);
-        txc7.popup;
+        waitForEvent(EVENT_REORDER, txc, test_AutocompleteControl);
+        txc.popup;
 
         accTree.children.push(
           {
             role: ROLE_LIST,
             children: [
               {
                 role: ROLE_LIST,
                 children: [
@@ -184,21 +188,18 @@
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
     <vbox flex="1">
-      <textbox id="txc1" value="hello"/>
-      <textbox id="txc2" type="number" value="44"/>
-      <textbox id="txc3" type="search" value="hello"/>
-      <!-- This textbox triggers "Warning: Timed textboxes are deprecated. Consider using type="search" instead.".
-           Yet let's test it too as long as it's (still) supported. -->
-      <textbox id="txc4_deprecated" type="timed" value="hello"/>
-      <textbox id="txc5" type="password" value="hello"/>
-      <textbox id="txc6" multiline="true" value="hello"/>
-      <textbox id="txc7" type="autocomplete" value="hello"/>
+      <textbox id="txc" value="hello"/>
+      <textbox id="txc_search" type="search" value="hello"/>
+      <textbox id="txc_number" type="number" value="44"/>
+      <textbox id="txc_password" type="password" value="hello"/>
+      <textbox id="txc_multiline" multiline="true" value="hello"/>
+      <textbox id="txc_autocomplete" type="autocomplete" value="hello"/>
     </vbox>
   </hbox>
 
 </window>
--- a/b2g/chrome/content/content.css
+++ b/b2g/chrome/content/content.css
@@ -279,16 +279,17 @@ button:active,
 input:active,
 option:active,
 select:active,
 label:active,
 textarea:active {
   background-color: rgba(141, 184, 216, 0.5);
 }
 
+input[type=number] > div > div, /* work around bug 946184 */
 input[type=number]::-moz-number-spin-box {
   display: none;
 }
 
 %ifdef MOZ_WIDGET_GONK
 /* This binding only provide key shortcuts that we can't use on devices */
 input,
 textarea {
--- 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/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2016,60 +2016,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/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -107,16 +107,19 @@ BINPATH = $(_BINPATH)
 DEFINES += -DAPPNAME=$(_APPNAME)
 else
 # Every other platform just winds up in dist/bin
 BINPATH = bin
 endif
 DEFINES += -DBINPATH=$(BINPATH)
 
 DEFINES += -DMOZ_ICU_VERSION=$(MOZ_ICU_VERSION)
+ifdef MOZ_NATIVE_ICU
+DEFINES += -DMOZ_NATIVE_ICU
+endif
 ifdef MOZ_SHARED_ICU
 DEFINES += -DMOZ_SHARED_ICU
 endif
 
 libs::
 	$(MAKE) -C $(DEPTH)/browser/locales langpack
 
 ifeq (WINNT,$(OS_ARCH))
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -96,16 +96,17 @@
 @BINPATH@/msvcp120.dll
 @BINPATH@/msvcr120.dll
 #ifdef MOZ_METRO
 @BINPATH@/vccorlib120.dll
 #endif
 #endif
 #endif
 #endif
+#ifndef MOZ_NATIVE_ICU
 #ifdef MOZ_SHARED_ICU
 #ifdef XP_WIN
 #ifdef MOZ_DEBUG
 @BINPATH@/icudtd@MOZ_ICU_VERSION@.dll
 @BINPATH@/icuind@MOZ_ICU_VERSION@.dll
 @BINPATH@/icuucd@MOZ_ICU_VERSION@.dll
 #else
 @BINPATH@/icudt@MOZ_ICU_VERSION@.dll
@@ -117,16 +118,17 @@
 @BINPATH@/libicui18n.@MOZ_ICU_VERSION@.dylib
 @BINPATH@/libicuuc.@MOZ_ICU_VERSION@.dylib
 #elif defined(XP_UNIX)
 @BINPATH@/libicudata.so.@MOZ_ICU_VERSION@
 @BINPATH@/libicui18n.so.@MOZ_ICU_VERSION@
 @BINPATH@/libicuuc.so.@MOZ_ICU_VERSION@
 #endif
 #endif
+#endif
 
 [browser]
 ; [Base Browser Files]
 #ifndef XP_UNIX
 @BINPATH@/@MOZ_APP_NAME@.exe
 #else
 @BINPATH@/@MOZ_APP_NAME@-bin
 @BINPATH@/@MOZ_APP_NAME@
--- 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/downloads.js
+++ b/browser/metro/base/content/downloads.js
@@ -13,18 +13,17 @@ var MetroDownloadsView = {
    * downloads if it starts before other downloads have completed.
    */
   _downloadCount: 0,
   _downloadsInProgress: 0,
   _lastDownload: null,
   _inited: false,
   _progressAlert: null,
   _lastSec: Infinity,
-  _notificationBox: null,
-  _progressNotification: null,
+
   _progressNotificationInfo: new Map(),
   _runDownloadBooleanMap: new Map(),
 
   get manager() {
     return Cc["@mozilla.org/download-manager;1"]
              .getService(Ci.nsIDownloadManager);
   },
 
@@ -51,38 +50,83 @@ var MetroDownloadsView = {
 
     this._inited = true;
 
     Services.obs.addObserver(this, "dl-start", true);
     Services.obs.addObserver(this, "dl-done", true);
     Services.obs.addObserver(this, "dl-run", true);
     Services.obs.addObserver(this, "dl-failed", true);
 
-    this._notificationBox = Browser.getNotificationBox();
 
     this._progress = new DownloadProgressListener(this);
     this.manager.addListener(this._progress);
 
+    Elements.tabList.addEventListener("TabClose", this, false);
+
     this._downloadProgressIndicator = document.getElementById("download-progress");
 
     if (this.manager.activeDownloadCount) {
       setTimeout (this._restartWithActiveDownloads.bind(this), 0);
     }
   },
 
   uninit: function dh_uninit() {
     if (this._inited) {
       Services.obs.removeObserver(this, "dl-start");
       Services.obs.removeObserver(this, "dl-done");
       Services.obs.removeObserver(this, "dl-run");
       Services.obs.removeObserver(this, "dl-failed");
+      if (Elements && Elements.tabList)
+        Elements.tabList.removeEventListener("TabClose", this);
     }
   },
 
-  _restartWithActiveDownloads: function() {
+  get _notificationBox() {
+    return Browser.getNotificationBox(Browser.selectedBrowser);
+  },
+
+  get _notificationBoxes() {
+    let currentBox = this._notificationBox;
+    let boxes = [
+      currentBox
+    ];
+    for (let { linkedBrowser } of Elements.tabList.children) {
+      if (linkedBrowser !== Browser.selectedBrowser) {
+        let notificationBox = Browser.getNotificationBox(linkedBrowser);
+        if (notificationBox)
+          boxes.push(notificationBox);
+      }
+    }
+    return boxes;
+  },
+
+  get _progressNotification() {
+    let notn = this._getNotificationWithValue("download-progress");
+    let currentBox = this._notificationBox;
+    // move the progress notification if attached to a different browser
+    if (notn && notn.parentNode !== currentBox) {
+      notn.parentNode.removeNotification(notn);
+      currentBox.insertBefore(notn, currentBox.firstChild);
+    }
+    return notn;
+  },
+
+  _getNotificationWithValue: function(aValue) {
+    let notn;
+    let allNotificationBoxes = this._notificationBoxes;
+    for(let box of allNotificationBoxes) {
+      notn = box.getNotificationWithValue(aValue);
+      if (notn) {
+        break;
+      }
+    }
+    return notn;
+  },
+
+   _restartWithActiveDownloads: function() {
     let activeDownloads = this.manager.activeDownloads;
 
     while (activeDownloads.hasMoreElements()) {
       let dl = activeDownloads.getNext();
       switch (dl.state) {
         case 0: // Downloading
         case 5: // Queued
           this.watchDownload(dl);
@@ -135,19 +179,19 @@ var MetroDownloadsView = {
         file.remove(false);
       this.manager.cancelDownload(aDownload.id);
 
       // If cancelling was successful, stop tracking the download.
       this._progressNotificationInfo.delete(aDownload.guid);
       this._runDownloadBooleanMap.delete(aDownload.targetFile.path);
       this._downloadCount--;
       this._downloadsInProgress--;
-      if (this._downloadsInProgress <= 0) {
-        this._notificationBox.removeNotification(this._progressNotification);
-        this._progressNotification = null;
+      let notn = this._progressNotification;
+      if (notn && this._downloadsInProgress <= 0) {
+        this._notificationBox.removeNotification(notn);
       }
     } catch (ex) {
       Util.dumpLn("Failed to cancel download, with id: "+aDownload.id+", download target URI spec: " + fileURI.spec);
       Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec));
     }
   },
 
   // Cancels all downloads.
@@ -379,74 +423,77 @@ var MetroDownloadsView = {
       this._progressNotificationInfo.set(aDownload.guid, {});
     }
     let infoObj = this._progressNotificationInfo.get(aDownload.guid);
     infoObj.download = aDownload;
     this._progressNotificationInfo.set(aDownload.guid, infoObj);
   },
 
   onDownloadButton: function dv_onDownloadButton() {
-    if (this._downloadsInProgress) {
-      if (!this._removeNotification("download-progress")) {
-        this.updateInfobar();
-      }
-    } else if (this._downloadCount) {
-      if (!this._removeNotification("download-complete")) {
-        this._showDownloadCompleteNotification();
-      }
+    let progressNotification = this._getNotificationWithValue("download-progress");
+    let wasProgressVisible = (progressNotification &&
+                              progressNotification.parentNode == this._notificationBox);
+    let completeNotification = this._getNotificationWithValue("download-complete");
+    let wasCompleteVisible = (completeNotification &&
+                              completeNotification.parentNode == this._notificationBox);
+
+    this._removeNotification("download-complete");
+    this._removeNotification("download-progress");
+
+    if (this._downloadsInProgress && !wasProgressVisible) {
+      this.updateInfobar();
+    } else if (this._downloadCount && !wasCompleteVisible) {
+      this._showDownloadCompleteNotification();
     }
   },
 
   _removeNotification: function (aValue) {
-    let notification = this._notificationBox.getNotificationWithValue(aValue);
-    if (!notification) {
-      return false;
-    }
-    this._notificationBox.removeNotification(notification);
-    return true;
+    let notification = this._getNotificationWithValue(aValue);
+    return notification &&
+           notification.parentNode.removeNotification(notification);
   },
 
   updateInfobar: function dv_updateInfobar() {
     let message = this._computeDownloadProgressString();
     this._updateCircularProgressMeter();
 
-    if (this._progressNotification == null ||
-        !this._notificationBox.getNotificationWithValue("download-progress")) {
+    let notn = this._progressNotification;
+    if (!notn) {
       let cancelButtonText =
               Strings.browser.GetStringFromName("downloadCancel");
 
       let buttons = [
         {
           isDefault: false,
           label: cancelButtonText,
           accessKey: "",
           callback: function() {
             MetroDownloadsView.cancelDownloads();
             MetroDownloadsView._downloadProgressIndicator.reset();
           }
         }
       ];
 
-      this._progressNotification =
-        this.showNotification("download-progress", message, buttons,
-        this._notificationBox.PRIORITY_WARNING_LOW);
+      notn = this.showNotification("download-progress", message, buttons,
+             this._notificationBox.PRIORITY_WARNING_LOW);
 
       ContextUI.displayNavbar();
     } else {
-      this._progressNotification.label = message;
+      notn.label = message;
     }
   },
 
   updateDownload: function dv_updateDownload(aDownload) {
-    if (this._progressNotification != null) {
-      this._saveDownloadData(aDownload);
-      this._progressNotification.label =
+    this._saveDownloadData(aDownload);
+    let notn = this._progressNotification;
+    if (notn) {
+      notn.label =
         this._computeDownloadProgressString(aDownload);
-      this._updateCircularProgressMeter();
     }
+    this._updateCircularProgressMeter();
   },
 
   watchDownload: function dv_watchDownload(aDownload) {
     this._saveDownloadData(aDownload);
     this._downloadCount++;
     this._downloadsInProgress++;
     if (!this._progressNotificationInfo.get(aDownload.guid)) {
       this._progressNotificationInfo.set(aDownload.guid, {});
@@ -481,27 +528,44 @@ var MetroDownloadsView = {
         }
 
         this._runDownloadBooleanMap.delete(download.targetFile.path);
         if (this._downloadsInProgress == 0) {
           if (this._downloadCount > 1 || !runAfterDownload) {
             this._showDownloadCompleteToast();
             this._showDownloadCompleteNotification();
           }
-          this._notificationBox.removeNotification(this._progressNotification);
-          this._progressNotification = null;
+          let notn = this._progressNotification;
+          if (notn)
+            this._notificationBox.removeNotification(notn);
         }
         break;
       case "dl-failed":
         download = aSubject.QueryInterface(Ci.nsIDownload);
         this._showDownloadFailedNotification(download);
         break;
     }
   },
 
+  handleEvent: function(aEvent) {
+    switch (aEvent.type) {
+      case 'TabClose': {
+        let browser = aEvent.originalTarget.linkedBrowser;
+        dump("DownloadNotificationsView handleEvent, got TabClose event for browser: "+browser+"\n");
+        let notn = this._getNotificationWithValue("download-progress");
+        if (notn && notn.defaultView == browser.contentWindow) {
+          let nextTab = Browser.getNextTab(aEvent.originalTarget);
+          let box = Browser.getNotificationBox(nextTab.linkedBrowser);
+          box.insertBefore(notn, box.firstChild);
+        }
+        break;
+      }
+    }
+  },
+
   QueryInterface: function (aIID) {
     if (!aIID.equals(Ci.nsIObserver) &&
         !aIID.equals(Ci.nsISupportsWeakReference) &&
         !aIID.equals(Ci.nsISupports))
       throw Components.results.NS_ERROR_NO_INTERFACE;
     return this;
   }
 };
--- 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);
+  -moz-border-start: 1px solid #42484f;
   -moz-box-align: center;
 }
 
+.devtools-tab:first-child {
+  -moz-border-start-width: 0;
+}
+
+.devtools-tab:last-child {
+  -moz-border-end: 1px solid #42484f;
+}
+
 .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 */
 }
@@ -516,77 +517,56 @@
   white-space: nowrap;
 }
 
 .devtools-tab:hover > image {
   opacity: 0.8;
 }
 
 .devtools-tab:active > image,
-.devtools-tab[selected=true] > label {
+.devtools-tab[selected=true] > image {
   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) {
+  -moz-padding-start: 1px;
+}
+
+.devtools-tab[selected=true]:last-child {
+  -moz-padding-end: 1px;
+}
+
+.devtools-tab[selected=true] + .devtools-tab {
+  -moz-border-start-width: 0;
+  -moz-padding-start: 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/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -17,15 +17,15 @@ AC_DEFUN([MOZ_CONFIG_ICU], [
     fi
 
     version=`sed -n 's/^[[:space:]]*#[[:space:]]*define[[:space:]][[:space:]]*U_ICU_VERSION_MAJOR_NUM[[:space:]][[:space:]]*\([0-9][0-9]*\)[[:space:]]*$/\1/p' "$icudir/common/unicode/uvernum.h"`
     if test x"$version" = x; then
        AC_MSG_ERROR([cannot determine icu version number from uvernum.h header file $lineno])
     fi
     MOZ_ICU_VERSION="$version"
 
-    if test -n "${JS_SHARED_LIBRARY}${MOZ_NATIVE_ICU}"; then
+    if test -z "${JS_STANDALONE}" -a -n "${JS_SHARED_LIBRARY}${MOZ_NATIVE_ICU}"; then
         MOZ_SHARED_ICU=1
     fi
 
     AC_SUBST(MOZ_ICU_VERSION)
     AC_SUBST(MOZ_SHARED_ICU)
 ])
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -108,18 +108,18 @@ namespace dom {
 
 class Link;
 class UndoManager;
 class DOMRect;
 class DOMRectList;
 
 // IID for the dom::Element interface
 #define NS_ELEMENT_IID \
-{ 0xec962aa7, 0x53ee, 0x46ff, \
-  { 0x90, 0x34, 0x68, 0xea, 0x79, 0x9d, 0x7d, 0xf7 } }
+{ 0xf7c18f0f, 0xa8fd, 0x4a95, \
+  { 0x91, 0x72, 0xd3, 0xa7, 0x4a, 0xb8, 0xc4, 0xbe } }
 
 class Element : public FragmentOrElement
 {
 public:
 #ifdef MOZILLA_INTERNAL_API
   Element(already_AddRefed<nsINodeInfo> aNodeInfo) :
     FragmentOrElement(aNodeInfo),
     mState(NS_EVENT_STATE_MOZ_READONLY)
@@ -380,26 +380,27 @@ private:
 
   void NotifyStateChange(nsEventStates aStates);
 
   void NotifyStyleStateChange(nsEventStates aStates);
 
   // Style state computed from element's state and style locks.
   nsEventStates StyleStateFromLocks() const;
 
+protected:
   // Methods for the ESM to manage state bits.  These will handle
   // setting up script blockers when they notify, so no need to do it
   // in the callers unless desired.
-  void AddStates(nsEventStates aStates) {
+  virtual void AddStates(nsEventStates aStates) {
     NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES),
                     "Should only be adding ESM-managed states here");
     AddStatesSilently(aStates);
     NotifyStateChange(aStates);
   }
-  void RemoveStates(nsEventStates aStates) {
+  virtual void RemoveStates(nsEventStates aStates) {
     NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES),
                     "Should only be removing ESM-managed states here");
     RemoveStatesSilently(aStates);
     NotifyStateChange(aStates);
   }
 public:
   virtual void UpdateEditableState(bool aNotify) MOZ_OVERRIDE;
 
--- 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/canvas/src/WebGLFramebuffer.cpp
+++ b/content/canvas/src/WebGLFramebuffer.cpp
@@ -499,17 +499,17 @@ WebGLFramebuffer::CheckAndInitializeAtta
 
 bool WebGLFramebuffer::CheckColorAttachementNumber(GLenum attachment, const char * functionName) const
 {
     const char* const errorFormating = "%s: attachment: invalid enum value 0x%x";
 
     if (mContext->IsExtensionEnabled(WebGLContext::WEBGL_draw_buffers))
     {
         if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
-            attachment > GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mGLMaxColorAttachments))
+            attachment >= GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mGLMaxColorAttachments))
         {
             mContext->ErrorInvalidEnum(errorFormating, functionName, attachment);
             return false;
         }
     }
     else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
     {
         if (attachment > LOCAL_GL_COLOR_ATTACHMENT0 &&
--- a/content/events/src/moz.build
+++ b/content/events/src/moz.build
@@ -81,16 +81,17 @@ FINAL_LIBRARY = 'gklayout'
 LOCAL_INCLUDES += [
     '/content/base/src',
     '/content/html/content/src',
     '/content/xml/content/src',
     '/content/xul/content/src',
     '/dom/base',
     '/dom/settings',
     '/dom/src/storage',
+    '/js/xpconnect/wrappers',
     '/layout/generic',
     '/layout/xul',
     '/layout/xul/tree/',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     LOCAL_INCLUDES += [
         '/dom/wifi',
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
+#include "AccessCheck.h"
 #include "ipc/IPCMessageUtils.h"
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsDOMEvent.h"
 #include "nsEventStateManager.h"
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsIPresShell.h"
@@ -28,16 +29,24 @@
 #include "nsDOMEventTargetHelper.h"
 #include "nsPIWindowRoot.h"
 #include "nsGlobalWindow.h"
 #include "nsDeviceContext.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
+namespace mozilla {
+namespace dom {
+namespace workers {
+extern bool IsCurrentThreadRunningChromeWorker();
+} // namespace workers
+} // namespace dom
+} // namespace mozilla
+
 static char *sPopupAllowedEvents;
 
 
 nsDOMEvent::nsDOMEvent(mozilla::dom::EventTarget* aOwner,
                        nsPresContext* aPresContext, WidgetEvent* aEvent)
 {
   ConstructorInit(aOwner, aPresContext, aEvent);
 }
@@ -212,16 +221,24 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
     }
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+bool
+nsDOMEvent::IsChrome(JSContext* aCx) const
+{
+  return mIsMainThreadEvent ?
+    xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx)) :
+    mozilla::dom::workers::IsCurrentThreadRunningChromeWorker();
+}
+
 // nsIDOMEventInterface
 NS_METHOD nsDOMEvent::GetType(nsAString& aType)
 {
   if (!mIsMainThreadEvent || !mEvent->typeString.IsEmpty()) {
     aType = mEvent->typeString;
     return NS_OK;
   }
   const char* name = GetEventName(mEvent->message);
@@ -431,35 +448,50 @@ nsDOMEvent::GetIsTrusted(bool *aIsTruste
 {
   *aIsTrusted = IsTrusted();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMEvent::PreventDefault()
 {
-  if (mEvent->mFlags.mCancelable) {
-    mEvent->mFlags.mDefaultPrevented = true;
+  // This method is called only from C++ code which must handle default action
+  // of this event.  So, pass true always.
+  PreventDefaultInternal(true);
+  return NS_OK;
+}
+
+void
+nsDOMEvent::PreventDefault(JSContext* aCx)
+{
+  MOZ_ASSERT(aCx, "JS context must be specified");
 
-    // Need to set an extra flag for drag events.
-    if (mEvent->eventStructType == NS_DRAG_EVENT && IsTrusted()) {
-      nsCOMPtr<nsINode> node = do_QueryInterface(mEvent->currentTarget);
-      if (!node) {
-        nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mEvent->currentTarget);
-        if (win) {
-          node = win->GetExtantDoc();
-        }
-      }
-      if (node && !nsContentUtils::IsChromeDoc(node->OwnerDoc())) {
-        mEvent->mFlags.mDefaultPreventedByContent = true;
-      }
-    }
+  // Note that at handling default action, another event may be dispatched.
+  // Then, JS in content mey be call preventDefault()
+  // even in the event is in system event group.  Therefore, don't refer
+  // mInSystemGroup here.
+  PreventDefaultInternal(IsChrome(aCx));
+}
+
+void
+nsDOMEvent::PreventDefaultInternal(bool aCalledByDefaultHandler)
+{
+  if (!mEvent->mFlags.mCancelable) {
+    return;
   }
 
-  return NS_OK;
+  mEvent->mFlags.mDefaultPrevented = true;
+
+  // Note that even if preventDefault() has already been called by chrome,
+  // a call of preventDefault() by content needs to overwrite
+  // mDefaultPreventedByContent to true because in such case, defaultPrevented
+  // must be true when web apps check it after they call preventDefault().
+  if (!aCalledByDefaultHandler) {
+    mEvent->mFlags.mDefaultPreventedByContent = true;
+  }
 }
 
 void
 nsDOMEvent::SetEventType(const nsAString& aEventTypeArg)
 {
   if (mIsMainThreadEvent) {
     mEvent->userType =
       nsContentUtils::GetEventIdAndAtom(aEventTypeArg, mEvent->eventStructType,
@@ -1139,38 +1171,62 @@ const char* nsDOMEvent::GetEventName(uin
   // create and that are not user defined events since this function and
   // SetEventType are incomplete.  (But fixing that requires fixing the
   // arrays in nsEventListenerManager too, since the events for which
   // this is a problem generally *are* created by nsDOMEvent.)
   return nullptr;
 }
 
 bool
+nsDOMEvent::DefaultPrevented(JSContext* aCx) const
+{
+  MOZ_ASSERT(aCx, "JS context must be specified");
+
+  NS_ENSURE_TRUE(mEvent, false);
+
+  // If preventDefault() has never been called, just return false.
+  if (!mEvent->mFlags.mDefaultPrevented) {
+    return false;
+  }
+
+  // If preventDefault() has been called by content, return true.  Otherwise,
+  // i.e., preventDefault() has been called by chrome, return true only when
+  // this is called by chrome.
+  return mEvent->mFlags.mDefaultPreventedByContent || IsChrome(aCx);
+}
+
+bool
 nsDOMEvent::GetPreventDefault() const
 {
   if (mOwner) {
     if (nsIDocument* doc = mOwner->GetExtantDoc()) {
       doc->WarnOnceAbout(nsIDocument::eGetPreventDefault);
     }
   }
+  // GetPreventDefault() is legacy and Gecko specific method.  Although,
+  // the result should be same as defaultPrevented, we don't need to break
+  // backward compatibility of legacy method.  Let's behave traditionally.
   return DefaultPrevented();
 }
 
 NS_IMETHODIMP
 nsDOMEvent::GetPreventDefault(bool* aReturn)
 {
   NS_ENSURE_ARG_POINTER(aReturn);
   *aReturn = GetPreventDefault();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMEvent::GetDefaultPrevented(bool* aReturn)
 {
   NS_ENSURE_ARG_POINTER(aReturn);
+  // This method must be called by only event handlers implemented by C++.
+  // Then, the handlers must handle default action.  So, this method don't need
+  // to check if preventDefault() has been called by content or chrome.
   *aReturn = DefaultPrevented();
   return NS_OK;
 }
 
 NS_IMETHODIMP_(void)
 nsDOMEvent::Serialize(IPC::Message* aMsg, bool aSerializeInterfaceType)
 {
   if (aSerializeInterfaceType) {
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -148,19 +148,30 @@ public:
   bool Cancelable() const
   {
     return mEvent->mFlags.mCancelable;
   }
 
   // xpidl implementation
   // void PreventDefault();
 
+  // You MUST NOT call PreventDefaultJ(JSContext*) from C++ code.  A call of
+  // this method always sets Event.defaultPrevented true for web contents.
+  // If default action handler calls this, web applications meet wrong
+  // defaultPrevented value.
+  void PreventDefault(JSContext* aCx);
+
+  // You MUST NOT call DefaultPrevented(JSContext*) from C++ code.  This may
+  // return false even if PreventDefault() has been called.
+  // See comments in its implementation for the detail.
+  bool DefaultPrevented(JSContext* aCx) const;
+
   bool DefaultPrevented() const
   {
-    return mEvent && mEvent->mFlags.mDefaultPrevented;
+    return mEvent->mFlags.mDefaultPrevented;
   }
 
   bool MultipleActionsPrevented() const
   {
     return mEvent->mFlags.mMultipleActionsPrevented;
   }
 
   bool IsTrusted() const
@@ -185,16 +196,30 @@ public:
   bool GetPreventDefault() const;
 
 protected:
 
   // Internal helper functions
   void SetEventType(const nsAString& aEventTypeArg);
   already_AddRefed<nsIContent> GetTargetFromFrame();
 
+  /**
+   * IsChrome() returns true if aCx is chrome context or the event is created
+   * in chrome's thread.  Otherwise, false.
+   */
+  bool IsChrome(JSContext* aCx) const;
+
+  /**
+   * @param aCalledByDefaultHandler     Should be true when this is called by
+   *                                    C++ or Chrome.  Otherwise, e.g., called
+   *                                    by a call of Event.preventDefault() in
+   *                                    content script, false.
+   */
+  void PreventDefaultInternal(bool aCalledByDefaultHandler);
+
   mozilla::WidgetEvent*       mEvent;
   nsRefPtr<nsPresContext>     mPresContext;
   nsCOMPtr<mozilla::dom::EventTarget> mExplicitOriginalTarget;
   nsCOMPtr<nsPIDOMWindow>     mOwner; // nsPIDOMWindow for now.
   bool                        mEventIsInternal;
   bool                        mPrivateDataDuplicated;
   bool                        mIsMainThreadEvent;
 };
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -2605,134 +2605,140 @@ nsEventStateManager::DispatchLegacyMouse
   // Send the legacy events in following order:
   // 1. Vertical scroll
   // 2. Vertical pixel scroll (even if #1 isn't consumed)
   // 3. Horizontal scroll (even if #1 and/or #2 are consumed)
   // 4. Horizontal pixel scroll (even if #3 isn't consumed)
 
   nsWeakFrame targetFrame(aTargetFrame);
 
-  nsEventStatus statusX = *aStatus;
-  nsEventStatus statusY = *aStatus;
+  MOZ_ASSERT(*aStatus != nsEventStatus_eConsumeNoDefault &&
+             !aEvent->mFlags.mDefaultPrevented,
+             "If you make legacy events dispatched for default prevented wheel "
+             "event, you need to initialize stateX and stateY");
+  EventState stateX, stateY;
   if (scrollDeltaY) {
-    SendLineScrollEvent(aTargetFrame, aEvent, &statusY,
+    SendLineScrollEvent(aTargetFrame, aEvent, stateY,
                         scrollDeltaY, DELTA_DIRECTION_Y);
     if (!targetFrame.IsAlive()) {
       *aStatus = nsEventStatus_eConsumeNoDefault;
       return;
     }
   }
 
   if (pixelDeltaY) {
-    SendPixelScrollEvent(aTargetFrame, aEvent, &statusY,
+    SendPixelScrollEvent(aTargetFrame, aEvent, stateY,
                          pixelDeltaY, DELTA_DIRECTION_Y);
     if (!targetFrame.IsAlive()) {
       *aStatus = nsEventStatus_eConsumeNoDefault;
       return;
     }
   }
 
   if (scrollDeltaX) {
-    SendLineScrollEvent(aTargetFrame, aEvent, &statusX,
+    SendLineScrollEvent(aTargetFrame, aEvent, stateX,
                         scrollDeltaX, DELTA_DIRECTION_X);
     if (!targetFrame.IsAlive()) {
       *aStatus = nsEventStatus_eConsumeNoDefault;
       return;
     }
   }
 
   if (pixelDeltaX) {
-    SendPixelScrollEvent(aTargetFrame, aEvent, &statusX,
+    SendPixelScrollEvent(aTargetFrame, aEvent, stateX,
                          pixelDeltaX, DELTA_DIRECTION_X);
     if (!targetFrame.IsAlive()) {
       *aStatus = nsEventStatus_eConsumeNoDefault;
       return;
     }
   }
 
-  if (statusY == nsEventStatus_eConsumeNoDefault ||
-      statusX == nsEventStatus_eConsumeNoDefault) {
+  if (stateY.mDefaultPrevented || stateX.mDefaultPrevented) {
     *aStatus = nsEventStatus_eConsumeNoDefault;
-    return;
-  }
-  if (statusY == nsEventStatus_eConsumeDoDefault ||
-      statusX == nsEventStatus_eConsumeDoDefault) {
-    *aStatus = nsEventStatus_eConsumeDoDefault;
+    aEvent->mFlags.mDefaultPrevented = true;
+    aEvent->mFlags.mDefaultPreventedByContent |=
+      stateY.mDefaultPreventedByContent || stateX.mDefaultPreventedByContent;
   }
 }
 
 void
 nsEventStateManager::SendLineScrollEvent(nsIFrame* aTargetFrame,
                                          WidgetWheelEvent* aEvent,
-                                         nsEventStatus* aStatus,
+                                         EventState& aState,
                                          int32_t aDelta,
                                          DeltaDirection aDeltaDirection)
 {
   nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
   if (!targetContent)
     targetContent = GetFocusedContent();
   if (!targetContent)
     return;
 
   while (targetContent->IsNodeOfType(nsINode::eTEXT)) {
     targetContent = targetContent->GetParent();
   }
 
   WidgetMouseScrollEvent event(aEvent->mFlags.mIsTrusted, NS_MOUSE_SCROLL,
                                aEvent->widget);
-  if (*aStatus == nsEventStatus_eConsumeNoDefault) {
-    event.mFlags.mDefaultPrevented = true;
-  }
+  event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
+  event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   event.refPoint = aEvent->refPoint;
   event.widget = aEvent->widget;
   event.time = aEvent->time;
   event.modifiers = aEvent->modifiers;
   event.buttons = aEvent->buttons;
   event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   event.delta = aDelta;
   event.inputSource = aEvent->inputSource;
 
+  nsEventStatus status = nsEventStatus_eIgnore;
   nsEventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
-                              &event, nullptr, aStatus);
+                              &event, nullptr, &status);
+  aState.mDefaultPrevented =
+    event.mFlags.mDefaultPrevented || status == nsEventStatus_eConsumeNoDefault;
+  aState.mDefaultPreventedByContent = event.mFlags.mDefaultPreventedByContent;
 }
 
 void
 nsEventStateManager::SendPixelScrollEvent(nsIFrame* aTargetFrame,
                                           WidgetWheelEvent* aEvent,
-                                          nsEventStatus* aStatus,
+                                          EventState& aState,
                                           int32_t aPixelDelta,
                                           DeltaDirection aDeltaDirection)
 {
   nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
   if (!targetContent) {
     targetContent = GetFocusedContent();
     if (!targetContent)
       return;
   }
 
   while (targetContent->IsNodeOfType(nsINode::eTEXT)) {
     targetContent = targetContent->GetParent();
   }
 
   WidgetMouseScrollEvent event(aEvent->mFlags.mIsTrusted, NS_MOUSE_PIXEL_SCROLL,
                                aEvent->widget);
-  if (*aStatus == nsEventStatus_eConsumeNoDefault) {
-    event.mFlags.mDefaultPrevented = true;
-  }
+  event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
+  event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   event.refPoint = aEvent->refPoint;
   event.widget = aEvent->widget;
   event.time = aEvent->time;
   event.modifiers = aEvent->modifiers;
   event.buttons = aEvent->buttons;
   event.isHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   event.delta = aPixelDelta;
   event.inputSource = aEvent->inputSource;
 
+  nsEventStatus status = nsEventStatus_eIgnore;
   nsEventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(),
-                              &event, nullptr, aStatus);
+                              &event, nullptr, &status);
+  aState.mDefaultPrevented =
+    event.mFlags.mDefaultPrevented || status == nsEventStatus_eConsumeNoDefault;
+  aState.mDefaultPreventedByContent = event.mFlags.mDefaultPreventedByContent;
 }
 
 nsIScrollableFrame*
 nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
                                          WidgetWheelEvent* aEvent,
                                          ComputeScrollTargetOptions aOptions)
 {
   return ComputeScrollTarget(aTargetFrame, aEvent->deltaX, aEvent->deltaY,
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -476,49 +476,62 @@ protected:
    * This is clearer than using bool.
    */
   enum DeltaDirection
   {
     DELTA_DIRECTION_X = 0,
     DELTA_DIRECTION_Y
   };
 
+  struct MOZ_STACK_CLASS EventState
+  {
+    bool mDefaultPrevented;
+    bool mDefaultPreventedByContent;
+
+    EventState() :
+      mDefaultPrevented(false), mDefaultPreventedByContent(false)
+    {
+    }
+  };
+
   /**
    * SendLineScrollEvent() dispatches a DOMMouseScroll event for the
    * WidgetWheelEvent.  This method shouldn't be called for non-trusted
    * wheel event because it's not necessary for compatiblity.
    *
    * @param aTargetFrame        The event target of wheel event.
    * @param aEvent              The original Wheel event.
-   * @param aStatus             The event status, must not be
-   *                            nsEventStatus_eConsumeNoDefault.
+   * @param aState              The event which should be set to the dispatching
+   *                            event.  This also returns the dispatched event
+   *                            state.
    * @param aDelta              The delta value of the event.
    * @param aDeltaDirection     The X/Y direction of dispatching event.
    */
   void SendLineScrollEvent(nsIFrame* aTargetFrame,
                            mozilla::WidgetWheelEvent* aEvent,
-                           nsEventStatus* aStatus,
+                           EventState& aState,
                            int32_t aDelta,
                            DeltaDirection aDeltaDirection);
 
   /**
    * SendPixelScrollEvent() dispatches a MozMousePixelScroll event for the
    * WidgetWheelEvent.  This method shouldn't be called for non-trusted
    * wheel event because it's not necessary for compatiblity.
    *
    * @param aTargetFrame        The event target of wheel event.
    * @param aEvent              The original Wheel event.
-   * @param aStatus             The event status, must not be
-   *                            nsEventStatus_eConsumeNoDefault.
+   * @param aState              The event which should be set to the dispatching
+   *                            event.  This also returns the dispatched event
+   *                            state.
    * @param aPixelDelta         The delta value of the event.
    * @param aDeltaDirection     The X/Y direction of dispatching event.
    */
   void SendPixelScrollEvent(nsIFrame* aTargetFrame,
                             mozilla::WidgetWheelEvent* aEvent,
-                            nsEventStatus* aStatus,
+                            EventState& aState,
                             int32_t aPixelDelta,
                             DeltaDirection aDeltaDirection);
 
   /**
    * ComputeScrollTarget() returns the scrollable frame which should be
    * scrolled.
    *
    * @param aTargetFrame        The event target of the wheel event.
--- a/content/events/test/chrome.ini
+++ b/content/events/test/chrome.ini
@@ -10,9 +10,10 @@ support-files =
 [test_bug336682.js]
 [test_bug336682_2.xul]
 [test_bug415498.xul]
 [test_bug586961.xul]
 [test_bug591249.xul]
 [test_bug602962.xul]
 [test_bug617528.xul]
 [test_bug679494.xul]
+[test_bug930374-chrome.html]
 [test_eventctors.xul]
--- a/content/events/test/mochitest.ini
+++ b/content/events/test/mochitest.ini
@@ -78,16 +78,17 @@ skip-if = true # Disabled due to timeout
 [test_bug689564.html]
 [test_bug698929.html]
 [test_bug741666.html]
 [test_bug742376.html]
 [test_bug812744.html]
 [test_bug847597.html]
 [test_bug855741.html]
 [test_bug864040.html]
+[test_bug930374-content.html]
 skip-if = toolkit == "gonk"
 [test_clickevent_on_input.html]
 [test_continuous_wheel_events.html]
 [test_dblclick_explicit_original_target.html]
 [test_dom_keyboard_event.html]
 [test_dom_mouse_event.html]
 [test_dom_wheel_event.html]
 [test_draggableprop.html]
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_bug930374-chrome.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=930374
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 930374</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=930374">Mozilla Bug 930374</a>
+<div id="display">
+  <input id="input-text">
+</div>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+  <script type="application/javascript">
+    SimpleTest.waitForExplicitFinish();
+
+    var gKeyPress = null;
+    function onKeyPress(aEvent)
+    {
+      gKeyPress = aEvent;
+      is(aEvent.target, document.getElementById("input-text"), "input element should have focus");
+      ok(!aEvent.defaultPrevented, "keypress event should be consumed before keypress event handler");
+    }
+
+    function runTests()
+    {
+      document.addEventListener("keypress", onKeyPress, false);
+      var input = document.getElementById("input-text");
+      input.focus();
+
+      input.addEventListener("input", function (aEvent) {
+          input.removeEventListener("input", arguments.callee, false);
+          ok(gKeyPress,
+             "Test1: keypress event must be fired before an input event");
+          ok(gKeyPress.defaultPrevented,
+             "Test1: keypress event's defaultPrevented should be true in chrome even if it's consumed by default action handler of editor");
+          setTimeout(function () {
+            ok(gKeyPress.defaultPrevented,
+               "Test2: keypress event's defaultPrevented should be true after event dispatching finished");
+            SimpleTest.finish();
+          }, 0);
+        }, false);
+
+      sendChar("a");
+    }
+
+    SimpleTest.waitForFocus(runTests);
+  </script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_bug930374-content.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=930374
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 930374</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=930374">Mozilla Bug 930374</a>
+<div id="display">
+  <input id="input-text">
+</div>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+  <script type="application/javascript">
+    SimpleTest.waitForExplicitFinish();
+
+    var gKeyPress = null;
+    function onKeyPress(aEvent)
+    {
+      gKeyPress = aEvent;
+      is(aEvent.target, document.getElementById("input-text"), "input element should have focus");
+      ok(!aEvent.defaultPrevented, "keypress event should be consumed before keypress event handler");
+    }
+
+    function runTests()
+    {
+      document.addEventListener("keypress", onKeyPress, false);
+      var input = document.getElementById("input-text");
+      input.focus();
+
+      input.addEventListener("input", function (aEvent) {
+          input.removeEventListener("input", arguments.callee, false);
+          ok(gKeyPress,
+             "Test1: keypress event must be fired before an input event");
+          ok(!gKeyPress.defaultPrevented,
+             "Test1: keypress event's defaultPrevented should be false even though it's consumed by the default action handler of editor");
+          gKeyPress.preventDefault();
+          ok(gKeyPress.defaultPrevented,
+             "Test1: keypress event's defaultPrevented should become true because of a call of preventDefault()");
+        }, false);
+
+      sendChar("a");
+      gKeyPress = null;
+
+      input.addEventListener("input", function (aEvent) {
+          input.removeEventListener("input", arguments.callee, false);
+          ok(gKeyPress,
+             "Test2: keypress event must be fired before an input event");
+          ok(!gKeyPress.defaultPrevented,
+             "Test2: keypress event's defaultPrevented should be false even though it's consumed by the default action handler of editor");
+          setTimeout(function () {
+            ok(!gKeyPress.defaultPrevented,
+               "Test2: keypress event's defaultPrevented should not become true after event dispatching finished");
+            SimpleTest.finish();
+          }, 0);
+        }, false);
+
+      sendChar("b");
+    }
+
+    SimpleTest.waitForFocus(runTests);
+  </script>
+</pre>
+</body>
+</html>
--- a/content/html/content/reftests/autofocus/reftest.list
+++ b/content/html/content/reftests/autofocus/reftest.list
@@ -1,11 +1,12 @@
+default-preferences pref(dom.forms.number,true)
 needs-focus == input-load.html input-ref.html
 needs-focus == input-create.html input-ref.html
-fails-if(Android) needs-focus == input-number.html input-number-ref.html # bug 940764
+needs-focus == input-number.html input-number-ref.html
 needs-focus == button-load.html button-ref.html
 needs-focus == button-create.html button-ref.html
 needs-focus == textarea-load.html textarea-ref.html
 needs-focus == textarea-create.html textarea-ref.html
 needs-focus == select-load.html select-ref.html
 needs-focus == select-create.html select-ref.html
 needs-focus == autofocus-after-load.html autofocus-after-load-ref.html
 needs-focus == autofocus-leaves-iframe.html autofocus-leaves-iframe-ref.html
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -609,16 +609,18 @@ private:
   // We'd prefer this member to be bool, but we don't support Atomic<bool>.
   mozilla::Atomic<uint32_t> mCanceled;
 };
 
 
 NS_IMETHODIMP
 HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
 {
+  mInput->PickerClosed();
+
   if (aResult == nsIFilePicker::returnCancel) {
     return NS_OK;
   }
 
   mInput->CancelDirectoryPickerScanIfRunning();
 
   int16_t mode;
   mFilePicker->GetMode(&mode);
@@ -791,16 +793,18 @@ nsColorPickerShownCallback::Done(const n
    * When Done() is called, we might be at the end of a serie of Update() calls
    * in which case mValueChanged is set to true and a change event will have to
    * be fired but we might also be in a one shot Done() call situation in which
    * case we should fire a change event iif the value actually changed.
    * UpdateInternal(bool) is taking care of that logic for us.
    */
   nsresult rv = NS_OK;
 
+  mInput->PickerClosed();
+
   if (!aColor.IsEmpty()) {
     UpdateInternal(aColor, false);
   }
 
   if (mValueChanged) {
     rv = nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
                                               static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
                                               NS_LITERAL_STRING("change"), true,
@@ -834,16 +838,21 @@ HTMLInputElement::IsPopupBlocked() const
   uint32_t permission;
   pm->TestPermission(OwnerDoc()->NodePrincipal(), &permission);
   return permission == nsIPopupWindowManager::DENY_POPUP;
 }
 
 nsresult
 HTMLInputElement::InitColorPicker()
 {
+  if (mPickerRunning) {
+    NS_WARNING("Just one nsIColorPicker is allowed");
+    return NS_ERROR_FAILURE;
+  }
+
   nsCOMPtr<nsIDocument> doc = OwnerDoc();
 
   nsCOMPtr<nsPIDOMWindow> win = doc->GetWindow();
   if (!win) {
     return NS_ERROR_FAILURE;
   }
 
   if (IsPopupBlocked()) {
@@ -864,22 +873,32 @@ HTMLInputElement::InitColorPicker()
   nsAutoString initialValue;
   GetValueInternal(initialValue);
   nsresult rv = colorPicker->Init(win, title, initialValue);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIColorPickerShownCallback> callback =
     new nsColorPickerShownCallback(this, colorPicker);
 
-  return colorPicker->Open(callback);
+  rv = colorPicker->Open(callback);
+  if (NS_SUCCEEDED(rv)) {
+    mPickerRunning = true;
+  }
+
+  return rv;
 }
 
 nsresult
 HTMLInputElement::InitFilePicker(FilePickerType aType)
 {
+  if (mPickerRunning) {
+    NS_WARNING("Just one nsIFilePicker is allowed");
+    return NS_ERROR_FAILURE;
+  }
+
   // Get parent nsPIDOMWindow object.
   nsCOMPtr<nsIDocument> doc = OwnerDoc();
 
   nsCOMPtr<nsPIDOMWindow> win = doc->GetWindow();
   if (!win) {
     return NS_ERROR_FAILURE;
   }
 
@@ -950,20 +969,26 @@ HTMLInputElement::InitFilePicker(FilePic
     if (oldFiles.Length() == 1) {
       nsAutoString leafName;
       oldFiles[0]->GetName(leafName);
       if (!leafName.IsEmpty()) {
         filePicker->SetDefaultString(leafName);
       }
     }
 
-    return filePicker->Open(callback);
+    rv = filePicker->Open(callback);
+    if (NS_SUCCEEDED(rv)) {
+      mPickerRunning = true;
+    }
+
+    return rv;
   }
 
   HTMLInputElement::gUploadLastDir->FetchDirectoryAndDisplayPicker(doc, filePicker, callback);
+  mPickerRunning = true;
   return NS_OK;
 }
 
 #define CPS_PREF_NAME NS_LITERAL_STRING("browser.upload.lastDir")
 
 NS_IMPL_ISUPPORTS2(UploadLastDir, nsIObserver, nsISupportsWeakReference)
 
 void
@@ -1089,16 +1114,18 @@ HTMLInputElement::HTMLInputElement(alrea
   , mIndeterminate(false)
   , mInhibitRestoration(aFromParser & FROM_PARSER_FRAGMENT)
   , mCanShowValidUI(true)
   , mCanShowInvalidUI(true)
   , mHasRange(false)
   , mIsDraggingRange(false)
   , mProgressTimerIsActive(false)
   , mNumberControlSpinnerIsSpinning(false)
+  , mNumberControlSpinnerSpinsUp(false)
+  , mPickerRunning(false)
 {
   // We are in a type=text so we now we currenty need a nsTextEditorState.
   mInputData.mState = new nsTextEditorState(this);
 
   if (!gUploadLastDir)
     HTMLInputElement::InitUploadLastDir();
 
   // Set up our default state.  By default we're enabled (since we're
@@ -3803,21 +3830,20 @@ HTMLInputElement::PostHandleEvent(nsEven
       // control and as a result aVisitor.mEventStatus will already have been
       // set to nsEventStatus_eConsumeNoDefault. However, we know that
       // whenever the up/down arrow keys cause the value of the number
       // control to change the string in the text control will change, and
       // the cursor will be moved to the end of the text control, overwriting
       // the editor's handling of up/down keypress events. For that reason we
       // just ignore aVisitor.mEventStatus here and go ahead and handle the
       // event to increase/decrease the value of the number control.
-      // XXX we still need to allow script to call preventDefault() on the
-      // event, but right now we can't tell the difference between the editor
-      // on script doing that (bug 930374).
-      StepNumberControlForUserEvent(keyEvent->keyCode == NS_VK_UP ? 1 : -1);
-      aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
+      if (!aVisitor.mEvent->mFlags.mDefaultPreventedByContent) {
+        StepNumberControlForUserEvent(keyEvent->keyCode == NS_VK_UP ? 1 : -1);
+        aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
+      }
     } else if (nsEventStatus_eIgnore == aVisitor.mEventStatus) {
       switch (aVisitor.mEvent->message) {
 
         case NS_FOCUS_CONTENT:
         {
           // see if we should select the contents of the textbox. This happens
           // for text and password fields when the field was focused by the
           // keyboard or a navigation, the platform allows it, and it wasn't
@@ -5700,16 +5726,48 @@ HTMLInputElement::IntrinsicState() const
               GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW))
                ? NS_EVENT_STATE_OUTOFRANGE
                : NS_EVENT_STATE_INRANGE;
   }
 
   return state;
 }
 
+void
+HTMLInputElement::AddStates(nsEventStates aStates)
+{
+  if (mType == NS_FORM_INPUT_TEXT) {
+    nsEventStates focusStates(aStates & (NS_EVENT_STATE_FOCUS |
+                                         NS_EVENT_STATE_FOCUSRING));
+    if (!focusStates.IsEmpty()) {
+      HTMLInputElement* ownerNumberControl = GetOwnerNumberControl();
+      if (ownerNumberControl) {
+        ownerNumberControl->AddStates(focusStates);
+      }
+    }
+  }
+  nsGenericHTMLFormElementWithState::AddStates(aStates);                          
+}
+
+void
+HTMLInputElement::RemoveStates(nsEventStates aStates)
+{
+  if (mType == NS_FORM_INPUT_TEXT) {
+    nsEventStates focusStates(aStates & (NS_EVENT_STATE_FOCUS |
+                                         NS_EVENT_STATE_FOCUSRING));
+    if (!focusStates.IsEmpty()) {
+      HTMLInputElement* ownerNumberControl = GetOwnerNumberControl();
+      if (ownerNumberControl) {
+        ownerNumberControl->RemoveStates(focusStates);
+      }
+    }
+  }
+  nsGenericHTMLFormElementWithState::RemoveStates(aStates);
+}
+
 bool
 HTMLInputElement::RestoreState(nsPresState* aState)
 {
   bool restoredCheckedState = false;
 
   nsCOMPtr<HTMLInputElementState> inputState
     (do_QueryInterface(aState->GetStateProperty()));
 
@@ -7173,16 +7231,22 @@ HTMLInputElement::UpdateHasRange()
 
   Decimal maximum = GetMaximum();
   if (!maximum.isNaN()) {
     mHasRange = true;
     return;
   }
 }
 
+void
+HTMLInputElement::PickerClosed()
+{
+  mPickerRunning = false;
+}
+
 JSObject*
 HTMLInputElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return HTMLInputElementBinding::Wrap(aCx, aScope, this);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/HTMLInputElement.h
+++ b/content/html/content/src/HTMLInputElement.h
@@ -161,16 +161,22 @@ public:
                               bool aCompileEventHandlers) MOZ_OVERRIDE;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) MOZ_OVERRIDE;
 
   virtual void DoneCreatingElement() MOZ_OVERRIDE;
 
   virtual nsEventStates IntrinsicState() const MOZ_OVERRIDE;
 
+  // Element
+private:
+  virtual void AddStates(nsEventStates aStates);
+  virtual void RemoveStates(nsEventStates aStates);
+public:
+
   // nsITextControlElement
   NS_IMETHOD SetValueChanged(bool aValueChanged) MOZ_OVERRIDE;
   NS_IMETHOD_(bool) IsSingleLineTextControl() const MOZ_OVERRIDE;
   NS_IMETHOD_(bool) IsTextArea() const MOZ_OVERRIDE;
   NS_IMETHOD_(bool) IsPlainTextControl() const MOZ_OVERRIDE;
   NS_IMETHOD_(bool) IsPasswordTextControl() const MOZ_OVERRIDE;
   NS_IMETHOD_(int32_t) GetCols() MOZ_OVERRIDE;
   NS_IMETHOD_(int32_t) GetWrapCols() MOZ_OVERRIDE;
@@ -198,16 +204,19 @@ public:
   const nsTArray<nsCOMPtr<nsIDOMFile> >& GetFilesInternal() const
   {
     return mFiles;
   }
 
   void SetFiles(const nsTArray<nsCOMPtr<nsIDOMFile> >& aFiles, bool aSetValueChanged);
   void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged);
 
+  // Called when a nsIFilePicker or a nsIColorPicker terminate.
+  void PickerClosed();
+
   void SetCheckedChangedInternal(bool aCheckedChanged);
   bool GetCheckedChanged() const {
     return mCheckedChanged;
   }
   void AddedToRadioGroup();
   void WillRemoveFromRadioGroup();
 
  /**
@@ -1262,16 +1271,17 @@ protected:
   bool                     mInhibitRestoration  : 1;
   bool                     mCanShowValidUI      : 1;
   bool                     mCanShowInvalidUI    : 1;
   bool                     mHasRange            : 1;
   bool                     mIsDraggingRange     : 1;
   bool                     mProgressTimerIsActive : 1;
   bool                     mNumberControlSpinnerIsSpinning : 1;
   bool                     mNumberControlSpinnerSpinsUp : 1;
+  bool                     mPickerRunning : 1;
 
 private:
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     nsRuleData* aData);
 
   /**
    * Returns true if this input's type will fire a DOM "change" event when it
    * loses focus if its value has changed since it gained focus.
--- a/content/html/content/test/forms/test_input_number_key_events.html
+++ b/content/html/content/test/forms/test_input_number_key_events.html
@@ -172,15 +172,25 @@ function test() {
     expectedVal = expectedValAfterKeyEvent(key, elem);
     synthesizeKey(key, {});
     is(elem.value, expectedVal, "Test " + key + " for number control with value set to the minimum (" + oldVal + ")");
 
     // Same again:
     expectedVal = expectedValAfterKeyEvent(key, elem);
     synthesizeKey(key, {});
     is(elem.value, expectedVal, "Test repeat of " + key + " for number control");
+
+    // Test preventDefault():
+    elem.addEventListener("keypress", function(evt) {
+      evt.preventDefault();
+      elem.removeEventListener("keypress", arguments.callee, false);
+    }, false);
+    oldVal = elem.value = 0;
+    expectedVal = 0;
+    synthesizeKey(key, {});
+    is(elem.value, expectedVal, "Test " + key + " for number control where scripted preventDefault() should prevent the value changing");
   }
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -156,18 +156,16 @@ AudioStream::~AudioStream()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
 #endif
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   PrefChanged(PREF_CUBEB_LATENCY, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
-
-  InitPreferredSampleRate();
 }
 
 /*static*/ void AudioStream::ShutdownLibrary()
 {
   Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
 
   StaticMutexAutoLock lock(sMutex);
@@ -262,26 +260,38 @@ int64_t AudioStream::GetWritten()
       cubeb_get_max_channel_count(cubebContext,
                                   &maxNumberOfChannels) == CUBEB_OK) {
     return static_cast<int>(maxNumberOfChannels);
   }
 
   return 0;
 }
 
-/*static */ void AudioStream::InitPreferredSampleRate()
+/*static*/ int AudioStream::PreferredSampleRate()
 {
+  const int fallbackSampleRate = 44100;
+  StaticMutexAutoLock lock(sMutex);
+  if (sPreferredSampleRate != 0) {
+    return sPreferredSampleRate;
+  }
+
+  cubeb* cubebContext = GetCubebContextUnlocked();
+  if (!cubebContext) {
+    sPreferredSampleRate = fallbackSampleRate;
+  }
   // Get the preferred samplerate for this platform, or fallback to something
   // sensible if we fail. We cache the value, because this might be accessed
   // often, and the complexity of the function call below depends on the
   // backend used.
-  if (cubeb_get_preferred_sample_rate(GetCubebContext(),
+  if (cubeb_get_preferred_sample_rate(cubebContext,
                                       &sPreferredSampleRate) != CUBEB_OK) {
-    sPreferredSampleRate = 44100;
+    sPreferredSampleRate = fallbackSampleRate;
   }
+
+  return sPreferredSampleRate;
 }
 
 static void SetUint16LE(uint8_t* aDest, uint16_t aValue)
 {
   aDest[0] = aValue & 0xFF;
   aDest[1] = aValue >> 8;
 }
 
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -174,23 +174,19 @@ public:
 
   // Shutdown Audio Library. Some Audio backends require shutting down the
   // library after using it.
   static void ShutdownLibrary();
 
   // Returns the maximum number of channels supported by the audio hardware.
   static int MaxNumberOfChannels();
 
-  static void InitPreferredSampleRate();
   // Returns the samplerate the systems prefer, because it is the
   // samplerate the hardware/mixer supports.
-  static int PreferredSampleRate() {
-    MOZ_ASSERT(sPreferredSampleRate);
-    return sPreferredSampleRate;
-  }
+  static int PreferredSampleRate();
 
   AudioStream();
   ~AudioStream();
 
   enum LatencyRequest {
     HighLatency,
     LowLatency
   };
@@ -372,24 +368,24 @@ private:
                  // cubeb, after which StateCallback will indicate drain
                  // completion.
     DRAINED,     // StateCallback has indicated that the drain is complete.
     ERRORED      // Stream disabled due to an internal error.
   };
 
   StreamState mState;
 
+  // This mutex protects the static members below.
+  static StaticMutex sMutex;
+  static cubeb* sCubebContext;
+
   // Prefered samplerate, in Hz (characteristic of the
   // hardware/mixer/platform/API used).
   static uint32_t sPreferredSampleRate;
 
-  // This mutex protects the static members below
-  static StaticMutex sMutex;
-  static cubeb* sCubebContext;
-
   static double sVolumeScale;
   static uint32_t sCubebLatency;
   static bool sCubebLatencyPrefSet;
 };
 
 } // namespace mozilla
 
 #endif
--- 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/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -380,16 +380,17 @@ DOMInterfaces = {
         'classList', 'attributes', 'children', 'firstElementChild',
         'lastElementChild', 'previousElementSibling', 'nextElementSibling',
         'getAttributeNode', 'getAttributeNodeNS', 'querySelector'
     ]
 },
 
 'Event': {
     'nativeType': 'nsDOMEvent',
+    'implicitJSContext': [ 'defaultPrevented', 'preventDefault' ],
 },
 
 'EventTarget': {
     'hasXPConnectImpls': True,
     'concrete': False,
     'jsImplParent': 'nsDOMEventTargetHelper'
 },
 
--- 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/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/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
--- 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/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -22,17 +22,17 @@ dictionary RTCStats {
   DOMHighResTimeStamp timestamp;
   RTCStatsType type;
   DOMString id;
 };
 
 dictionary RTCRTPStreamStats : RTCStats {
   DOMString ssrc;
   DOMString remoteId;
-  boolean isRemote;
+  boolean isRemote = false;
   DOMString mediaTrackId;
   DOMString transportId;
   DOMString codecId;
 };
 
 dictionary RTCInboundRTPStreamStats : RTCRTPStreamStats {
   unsigned long packetsReceived;
   unsigned long long bytesReceived;
@@ -119,17 +119,17 @@ dictionary RTCCodecStats : RTCStats {
 };
 
 callback RTCStatsReportCallback = void (RTCStatsReport obj);
 
 // This is the internal representation of the report in this implementation
 // to be received from c++
 
 dictionary RTCStatsReportInternal {
-  DOMString                           pcid;
+  DOMString                           pcid = "";
   sequence<RTCRTPStreamStats>         rtpStreamStats;
   sequence<RTCInboundRTPStreamStats>  inboundRTPStreamStats;
   sequence<RTCOutboundRTPStreamStats> outboundRTPStreamStats;
   sequence<RTCMediaStreamTrackStats>  mediaStreamTrackStats;
   sequence<RTCMediaStreamStats>       mediaStreamStats;
   sequence<RTCTransportStats>         transportStats;
   sequence<RTCIceComponentStats>      iceComponentStats;
   sequence<RTCIceCandidatePairStats>  iceCandidatePairStats;
--- a/editor/libeditor/html/tests/chrome.ini
+++ b/editor/libeditor/html/tests/chrome.ini
@@ -4,8 +4,9 @@ support-files = green.png
 [test_bug366682.html]
 [test_bug489202.xul]
 [test_bug490879.xul]
 [test_bug607584.xul]
 [test_bug616590.xul]
 [test_bug635636.html]
 [test_bug780908.xul]
 [test_contenteditable_text_input_handling.html]
+[test_htmleditor_keyevent_handling.html]
--- a/editor/libeditor/html/tests/mochitest.ini
+++ b/editor/libeditor/html/tests/mochitest.ini
@@ -125,13 +125,12 @@ support-files =
 [test_bug780035.html]
 [test_bug787432.html]
 [test_bug790475.html]
 [test_bug796839.html]
 [test_bug832025.html]
 [test_bug857487.html]
 [test_contenteditable_focus.html]
 [test_dom_input_event_on_htmleditor.html]
-[test_htmleditor_keyevent_handling.html]
 [test_keypress_untrusted_event.html]
 [test_root_element_replacement.html]
 [test_select_all_without_body.html]
 [test_spellcheck_pref.html]
--- a/editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
+++ b/editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
@@ -1,17 +1,17 @@
 <html>
 <head>
   <title>Test for key event handler of HTML editor</title>
   <script type="text/javascript"
-          src="/tests/SimpleTest/SimpleTest.js"></script>
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript"
-          src="/tests/SimpleTest/EventUtils.js"></script>
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css"
-          href="/tests/SimpleTest/test.css" />
+          href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 </head>
 <body>
 <div id="display">
   <div id="htmlEditor" contenteditable="true"><br></div>
 </div>
 <div id="content" style="display: none">
   
 </div>
--- a/editor/libeditor/text/tests/Makefile.in
+++ b/editor/libeditor/text/tests/Makefile.in
@@ -1,13 +1,13 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # disables the key handling test on gtk because gtk overrides some key events
 # on our editor, and the combinations depend on the system.
 ifndef MOZ_WIDGET_GTK
-MOCHITEST_FILES += \
+MOCHITEST_CHROME_FILES += \
 		test_texteditor_keyevent_handling.html \
 		$(NULL)
 endif
 
--- a/editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
+++ b/editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
@@ -1,17 +1,17 @@
 <html>
 <head>
   <title>Test for key event handler of text editor</title>
   <script type="text/javascript"
-          src="/tests/SimpleTest/SimpleTest.js"></script>
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript"
-          src="/tests/SimpleTest/EventUtils.js"></script>
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css"
-          href="/tests/SimpleTest/test.css" />
+          href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 </head>
 <body>
 <div id="display">
   <input type="text" id="inputField">
   <input type="password" id="passwordField">
   <textarea id="textarea"></textarea>
 </div>
 <div id="content" style="display: none">
--- a/gfx/src/nsThemeConstants.h
+++ b/gfx/src/nsThemeConstants.h
@@ -132,16 +132,19 @@
 #define NS_THEME_SPINNER_UP_BUTTON                         73
 
 // The down button of a spin control
 #define NS_THEME_SPINNER_DOWN_BUTTON                       74
 
 // The textfield of a spin control
 #define NS_THEME_SPINNER_TEXTFIELD                         75
 
+// For HTML's <input type=number>
+#define NS_THEME_NUMBER_INPUT                              76
+
 // A scrollbar.
 #define NS_THEME_SCROLLBAR                                 80
 
 // A small scrollbar.
 #define NS_THEME_SCROLLBAR_SMALL                           81
 
 // A scrollbar button (up/down/left/right)
 #define NS_THEME_SCROLLBAR_BUTTON_UP                       82
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -16,16 +16,17 @@
 #include "Layers.h"
 #include "TestLayers.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using ::testing::_;
 using ::testing::NiceMock; 
+using ::testing::AtLeast;
 
 class MockContentController : public GeckoContentController {
 public:
   MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
   MOCK_METHOD2(HandleDoubleTap, void(const CSSIntPoint&, int32_t));
   MOCK_METHOD2(HandleSingleTap, void(const CSSIntPoint&, int32_t));
   MOCK_METHOD2(HandleLongTap, void(const CSSIntPoint&, int32_t));
   MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize));
@@ -164,17 +165,17 @@ TEST(AsyncPanZoomController, Pinch) {
   fm.mViewport = CSSRect(0, 0, 980, 480);
   fm.mCompositionBounds = ScreenIntRect(200, 200, 100, 200);
   fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
   fm.mScrollOffset = CSSPoint(300, 300);
   fm.mZoom = CSSToScreenScale(2.0);
   apzc->SetFrameMetrics(fm);
   // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
 
-  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(2);
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   ApzcPinch(apzc, 250, 300, 1.25);
 
   // the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
   fm = apzc->GetFrameMetrics();
   EXPECT_EQ(fm.mZoom.scale, 2.5f);
   EXPECT_EQ(fm.mScrollOffset.x, 305);
@@ -204,17 +205,17 @@ TEST(AsyncPanZoomController, Overzoom) {
   fm.mViewport = CSSRect(0, 0, 100, 100);
   fm.mCompositionBounds = ScreenIntRect(0, 0, 100, 100);
   fm.mScrollableRect = CSSRect(0, 0, 125, 150);
   fm.mScrollOffset = CSSPoint(10, 0);
   fm.mZoom = CSSToScreenScale(1.0);
   apzc->SetFrameMetrics(fm);
   // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
 
-  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(1);
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   ApzcPinch(apzc, 50, 50, 0.5);
 
   fm = apzc->GetFrameMetrics();
   EXPECT_EQ(fm.mZoom.scale, 0.8f);
   // bug 936721 - PGO builds introduce rounding error so
   // use a fuzzy match instead
@@ -348,17 +349,17 @@ TEST(AsyncPanZoomController, Pan) {
 
   nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
   nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
 
-  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(4);
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   int time = 0;
   int touchStart = 50;
   int touchEnd = 10;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
@@ -381,17 +382,17 @@ TEST(AsyncPanZoomController, Fling) {
 
   nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
   nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
 
-  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(2);
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   int time = 0;
   int touchStart = 50;
   int touchEnd = 10;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
@@ -411,17 +412,17 @@ TEST(AsyncPanZoomController, OverScrollP
 
   nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
   nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
 
-  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(4);
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   // Pan sufficiently to hit overscroll behavior
   int time = 0;
   int touchStart = 500;
   int touchEnd = 10;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
@@ -654,17 +655,17 @@ TEST(APZCTreeManager, HitTesting2) {
   EXPECT_EQ(gfxPoint(75, 75), transformToGecko.Transform(gfxPoint(37.5, 75)));
 
   // Pan the root layer upward by 50 pixels.
   // This causes layers[1] to scroll out of view, and an async transform
   // of -50 to be set on the root layer.
   int time = 0;
   // Silence GMock warnings about "uninteresting mock function calls".
   EXPECT_CALL(*mcc, PostDelayedTask(_,_)).Times(1);
-  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(2);
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
   ApzcPan(apzcroot, manager, time, 100, 50);
 
   // Hit where layers[3] used to be. It should now hit the root.
   hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko);
   EXPECT_EQ(apzcroot, hit.get());
   // transformToApzc doesn't unapply the root's own async transform
   EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75)));
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -1518,16 +1518,17 @@ class HashTable : private AllocPolicy
     // Note: |l| may be a reference to a piece of |u|, so this function
     // must take care not to use |l| after moving |u|.
     template <class U>
     bool relookupOrAdd(AddPtr& p, const Lookup &l, U &&u)
     {
         p.mutationCount = mutationCount;
         {
             mozilla::ReentrancyGuard g(*this);
+            JS_ASSERT(prepareHash(l) == p.keyHash); // l has not been destroyed
             p.entry_ = &lookup(l, p.keyHash, sCollisionBit);
         }
         return p.found() || add(p, mozilla::Forward<U>(u));
     }
 
     void remove(Ptr p)
     {
         JS_ASSERT(table);
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -120,16 +120,17 @@ jsconfig_DEST = $(DIST)/include
 jsconfig_TARGET := export
 
 # Ensure that this happens before using $(MOZ_PSEUDO_DERECURSE)
 include $(topsrcdir)/config/config.mk
 
 # Ensure that this happens before including rules.mk
 ifdef ENABLE_INTL_API
 ifndef MOZ_NATIVE_ICU
+ifdef MOZ_SHARED_ICU
 ifeq ($(OS_ARCH),WINNT)
   # Library names: On Windows, ICU uses modified library names for static
   # and debug libraries.
   ifdef MOZ_DEBUG
     ICU_LIB_SUFFIX=d
   endif
   ifdef JS_SHARED_LIBRARY
     ICU_FILES := $(foreach libname,$(ICU_LIB_NAMES),intl/icu/target/lib/$(libname)$(ICU_LIB_SUFFIX)$(MOZ_ICU_VERSION).dll)
@@ -151,16 +152,17 @@ endif
 ifdef ICU_FILES
   ICU_DEST := $(DIST)/bin
   INSTALL_TARGETS += ICU
   $(ICU_FILES): buildicu
   ICU_TARGET := $(if $(MOZ_PSEUDO_DERECURSE),compile,export)
 endif
 endif
 endif
+endif
 
 include $(topsrcdir)/config/rules.mk
 
 .PHONY: buildffi buildicu
 buildffi buildicu:
 $(if $(MOZ_PSEUDO_DERECURSE),compile,export):: buildffi buildicu
 
 ifdef JS_HAS_CTYPES
--- a/js/src/build/autoconf/icu.m4
+++ b/js/src/build/autoconf/icu.m4
@@ -17,15 +17,15 @@ AC_DEFUN([MOZ_CONFIG_ICU], [
     fi
 
     version=`sed -n 's/^[[:space:]]*#[[:space:]]*define[[:space:]][[:space:]]*U_ICU_VERSION_MAJOR_NUM[[:space:]][[:space:]]*\([0-9][0-9]*\)[[:space:]]*$/\1/p' "$icudir/common/unicode/uvernum.h"`
     if test x"$version" = x; then
        AC_MSG_ERROR([cannot determine icu version number from uvernum.h header file $lineno])
     fi
     MOZ_ICU_VERSION="$version"
 
-    if test -n "${JS_SHARED_LIBRARY}${MOZ_NATIVE_ICU}"; then
+    if test -z "${JS_STANDALONE}" -a -n "${JS_SHARED_LIBRARY}${MOZ_NATIVE_ICU}"; then
         MOZ_SHARED_ICU=1
     fi
 
     AC_SUBST(MOZ_ICU_VERSION)
     AC_SUBST(MOZ_SHARED_ICU)
 ])
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1114,21 +1114,21 @@ SetJitCompilerOption(JSContext *cx, unsi
 
     JS_SetGlobalJitCompilerOption(cx, opt, uint32_t(number));
 
     args.rval().setBoolean(true);
     return true;
 }
 
 static bool
-SetIonAssertGraphCoherency(JSContext *cx, unsigned argc, jsval *vp)
+SetIonCheckGraphCoherency(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 #ifdef JS_ION
-    jit::js_IonOptions.assertGraphConsistency = ToBoolean(args.get(0));
+    jit::js_IonOptions.checkGraphConsistency = ToBoolean(args.get(0));
 #endif
     args.rval().setUndefined();
     return true;
 }
 
 class CloneBufferObject : public JSObject {
     static const JSPropertySpec props_[2];
     static const size_t DATA_SLOT   = 0;
@@ -1572,18 +1572,18 @@ static const JSFunctionSpecWithHelp Test
     JS_FN_HELP("bailout", testingFunc_bailout, 0, 0,
 "bailout()",
 "  Force a bailout out of ionmonkey (if running in ionmonkey)."),
 
     JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0,
 "setCompilerOption(<option>, <number>)",
 "  Set a compiler option indexed in JSCompileOption enum to a number.\n"),
 
-    JS_FN_HELP("setIonAssertGraphCoherency", SetIonAssertGraphCoherency, 1, 0,
-"setIonAssertGraphCoherency(bool)",
+    JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
+"setIonCheckGraphCoherency(bool)",
 "  Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
 "  are valuable and should be generally enabled, however they can be very expensive for large\n"
 "  (asm.js) programs."),
 
     JS_FN_HELP("serialize", Serialize, 1, 0,
 "serialize(data, [transferables])",
 "  Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
 "  clone buffer object."),
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1130,17 +1130,16 @@ if test "$GNU_CC"; then
     # -Wdeclaration-after-statement - MSVC doesn't like these
     # -Werror=return-type - catches missing returns, zero false positives
     # -Wtype-limits - catches overflow bugs, few false positives
     # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
     # -Wsign-compare - catches comparison of signed and unsigned types
     #
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -Wpointer-arith -Wdeclaration-after-statement"
     MOZ_C_SUPPORTS_WARNING(-W, error=return-type, ac_c_has_werror_return_type)
-    MOZ_C_SUPPORTS_WARNING(-W, type-limits, ac_c_has_wtype_limits)
     MOZ_C_SUPPORTS_WARNING(-W, empty-body, ac_c_has_wempty_body)
     MOZ_C_SUPPORTS_WARNING(-W, sign-compare, ac_c_has_sign_compare)
 
     # Turn off the following warnings that -Wall turns on:
     # -Wno-unused - lots of violations in third-party code
     #
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-unused"
 
@@ -4208,55 +4207,55 @@ if test -n "$ENABLE_INTL_API"; then
     AC_DEFINE(ENABLE_INTL_API)
 
     MOZ_CONFIG_ICU()
 
     if test -z "$MOZ_NATIVE_ICU"; then
         case "$OS_TARGET" in
             WINNT)
                 ICU_LIB_NAMES="icuin icuuc icudt"
-                if test "$DISABLE_SHARED_JS" != "1"; then
+                if test -n "$MOZ_SHARED_ICU"; then
                     DBG_SUFFIX=
                     if test -n "$MOZ_DEBUG"; then
                         DBG_SUFFIX=d
                     fi
                     MOZ_ICU_LIBS='$(foreach lib,$(ICU_LIB_NAMES),$(DEPTH)/intl/icu/target/lib/$(LIB_PREFIX)$(lib)$(DBG_SUFFIX).$(LIB_SUFFIX))'
                 fi
                 ;;
             Darwin)
                 ICU_LIB_NAMES="icui18n icuuc icudata"
-                if test "$DISABLE_SHARED_JS" != "1"; then
+                if test -n "$MOZ_SHARED_ICU"; then
                    MOZ_ICU_LIBS='$(foreach lib,$(ICU_LIB_NAMES),$(DEPTH)/intl/icu/target/lib/$(DLL_PREFIX)$(lib).$(MOZ_ICU_VERSION)$(DLL_SUFFIX))'
                 fi
                 ;;
             Linux|DragonFly|FreeBSD|NetBSD|OpenBSD)
                 ICU_LIB_NAMES="icui18n icuuc icudata"
-                if test "$DISABLE_SHARED_JS" != "1"; then
+                if test -n "$MOZ_SHARED_ICU"; then
                    MOZ_ICU_LIBS='$(foreach lib,$(ICU_LIB_NAMES),$(DEPTH)/intl/icu/target/lib/$(DLL_PREFIX)$(lib)$(DLL_SUFFIX).$(MOZ_ICU_VERSION))'
                 fi
                 ;;
             *)
                 AC_MSG_ERROR([ECMAScript Internationalization API is not yet supported on this platform])
         esac
-        if test "$DISABLE_SHARED_JS" = "1"; then
+        if test -z "$MOZ_SHARED_ICU"; then
             MOZ_ICU_LIBS='$(call EXPAND_LIBNAME_PATH,$(ICU_LIB_NAMES),$(DEPTH)/intl/icu/target/lib)'
         fi
     fi
 fi
 
 AC_SUBST(DBG_SUFFIX)
 AC_SUBST(ENABLE_INTL_API)
 AC_SUBST(ICU_LIB_NAMES)
 AC_SUBST(MOZ_ICU_LIBS)
 AC_SUBST(MOZ_NATIVE_ICU)
 
 dnl Settings for ICU
 if test -n "$ENABLE_INTL_API" -a -z "$MOZ_NATIVE_ICU"; then
     dnl We build ICU as a static library for non-shared js builds and as a shared library for shared js builds.
-    if test "$DISABLE_SHARED_JS" = "1"; then
+    if test -z "$MOZ_SHARED_ICU"; then
         AC_DEFINE(U_STATIC_IMPLEMENTATION)
     else
         AC_DEFINE(U_COMBINED_IMPLEMENTATION)
     fi
 
     dnl Source files that use ICU should have control over which parts of the ICU
     dnl namespace they want to use.
     AC_DEFINE(U_USING_ICU_NAMESPACE,0)
@@ -4342,17 +4341,17 @@ if test -n "$ENABLE_INTL_API" -a -z "$MO
         ICU_CROSS_BUILD_OPT="--with-cross-build=$ICU_HOST_PATH"
         ICU_TARGET_OPT="--build=$build --host=$target"
     else
         # CROSS_COMPILE isn't set build and target are i386 and x86-64.
         # So we must set target for --build and --host.
         ICU_TARGET_OPT="--build=$target --host=$target"
     fi
 
-    if test "$DISABLE_SHARED_JS" = "1"; then
+    if test -z "$MOZ_SHARED_ICU"; then
         # To reduce library size, use static linking
         ICU_LINK_OPTS="--enable-static --disable-shared"
     else
         ICU_LINK_OPTS="--disable-static --enable-shared"
     fi
     # Force the ICU static libraries to be position independent code
     ICU_CFLAGS="$DSO_PIC_CFLAGS $CFLAGS"
     ICU_CXXFLAGS="$DSO_PIC_CFLAGS $CXXFLAGS"
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -673,23 +673,30 @@ EnclosingStaticScope(BytecodeEmitter *bc
     return bce->sc->asFunctionBox()->function();
 }
 
 // Push a block scope statement and link blockObj into bce->blockChain.
 static bool
 PushBlockScopeBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
                   ptrdiff_t top)
 {
+    uint32_t parent = UINT32_MAX;
+    if (bce->blockChain) {
+        StmtInfoBCE *stmt = bce->topScopeStmt;
+        for (; stmt->blockObj != bce->blockChain; stmt = stmt->down) {}
+        parent = stmt->blockScopeIndex;
+    }
+
     StaticBlockObject &blockObj = objbox->object->as<StaticBlockObject>();
 
     PushStatementBCE(bce, stmt, STMT_BLOCK, top);
 
     unsigned scopeObjectIndex = bce->objectList.add(objbox);
     stmt->blockScopeIndex = bce->blockScopeList.length();
-    if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset()))
+    if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
         return false;
 
     blockObj.initEnclosingStaticScope(EnclosingStaticScope(bce));
     FinishPushBlockScope(bce, stmt, blockObj);
 
     return true;
 }
 
@@ -809,33 +816,28 @@ EmitUnaliasedVarOp(ExclusiveContext *cx,
     return true;
 }
 
 static bool
 EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ScopeCoordinate sc, BytecodeEmitter *bce)
 {
     JS_ASSERT(JOF_OPTYPE(op) == JOF_SCOPECOORD);
 
-    uint32_t maybeBlockIndex = UINT32_MAX;
-    if (bce->blockChain)
-        maybeBlockIndex = bce->objectList.indexOf(bce->blockChain);
-
-    unsigned n = 2 * sizeof(uint16_t) + sizeof(uint32_t);
+    unsigned n = 2 * sizeof(uint16_t);
     JS_ASSERT(int(n) + 1 /* op */ == js_CodeSpec[op].length);
 
     ptrdiff_t off = EmitN(cx, bce, op, n);
     if (off < 0)
         return false;
 
     jsbytecode *pc = bce->code(off);
     SET_UINT16(pc, sc.hops);
     pc += sizeof(uint16_t);
     SET_UINT16(pc, sc.slot);
     pc += sizeof(uint16_t);
-    SET_UINT32_INDEX(pc, maybeBlockIndex);
     CheckTypeSet(cx, bce, op);
     return true;
 }
 
 static unsigned
 ClonedBlockDepth(BytecodeEmitter *bce)
 {
     unsigned clonedBlockDepth = 0;
@@ -6874,23 +6876,24 @@ CGTryNoteList::finish(TryNoteArray *arra
 {
     JS_ASSERT(length() == array->length);
 
     for (unsigned i = 0; i < length(); i++)
         array->vector[i] = list[i];
 }
 
 bool
-CGBlockScopeList::append(uint32_t scopeObject, uint32_t offset)
+CGBlockScopeList::append(uint32_t scopeObject, uint32_t offset, uint32_t parent)
 {
     BlockScopeNote note;
     mozilla::PodZero(&note);
 
     note.index = scopeObject;
     note.start = offset;
+    note.parent = parent;
 
     return list.append(note);
 }
 
 void
 CGBlockScopeList::recordEnd(uint32_t index, uint32_t offset)
 {
     JS_ASSERT(index < length());
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -56,17 +56,17 @@ struct CGTryNoteList {
     size_t length() const { return list.length(); }
     void finish(TryNoteArray *array);
 };
 
 struct CGBlockScopeList {
     Vector<BlockScopeNote> list;
     CGBlockScopeList(ExclusiveContext *cx) : list(cx) {}
 
-    bool append(uint32_t scopeObject, uint32_t offset);
+    bool append(uint32_t scopeObject, uint32_t offset, uint32_t parent);
     void recordEnd(uint32_t index, uint32_t offset);
     size_t length() const { return list.length(); }
     void finish(BlockScopeArray *array);
 };
 
 struct StmtInfoBCE;
 
 // Use zero inline elements because these go on the stack and affect how many
--- a/js/src/jit-test/tests/asm.js/testBullet.js
+++ b/js/src/jit-test/tests/asm.js/testBullet.js
@@ -4,15 +4,15 @@
 
 if (!isAsmJSCompilationAvailable())
     quit();
 
 // Note: if you get some failure in this test, it probably has to do with
 // bullet.js and not the nestedShell() call, so try first commenting out
 // nestedShell() (and the loadedFromCache assertion) to see if the error
 // reproduces.
-var code = "setIonAssertGraphCoherency(false); load('" + libdir + "bullet.js'); runBullet()";
+var code = "setIonCheckGraphCoherency(false); load('" + libdir + "bullet.js'); runBullet()";
 nestedShell("--js-cache", "--execute=" + code);
-setIonAssertGraphCoherency(false);
+setIonCheckGraphCoherency(false);
 load(libdir + 'bullet.js');
 var results = runBullet();
 assertEq(results.asmJSValidated, true);
 assertEq(results.loadedFromCache, true);
--- a/js/src/jit-test/tests/asm.js/testZOOB.js
+++ b/js/src/jit-test/tests/asm.js/testZOOB.js
@@ -1,13 +1,13 @@
 load(libdir + "asm.js");
 
 // constants
 var buf = new ArrayBuffer(4096);
-setIonAssertGraphCoherency(false);
+setIonCheckGraphCoherency(false);
 
 // An unshifted literal constant byte index in the range 0 to 2^31-1 inclusive should give a link failure.
 assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0x7fffffff]|0 } return f'), this, null, buf);
 assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x1fffffff]|0 } return f'), this, null, buf);
 
 
 // An unshifted literal constant byte index outside the range 0 to 2^31-1 inclusive should cause an error compiling.
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x20000000]|0 } return f');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug940635.js
@@ -0,0 +1,7 @@
+function f(y) {
+    return (y > 0) == y;
+}
+assertEq(f(0), true);
+assertEq(f(0), true);
+assertEq(f(null), false);
+assertEq(f(null), false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug946969.js
@@ -0,0 +1,6 @@
+function f() {
+    return Math.abs(~(Math.tan()));
+}
+
+for (var i=0; i<1000; i++)
+    assertEq(f(i), 1);
--- a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js
+++ b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js
@@ -1,14 +1,14 @@
 // |jit-test| debug
 setDebug(true);
 x = "notset";
 function main() {
   /* The JSOP_STOP in main. */
-  a = { valueOf: function () { trap(main, 95, "success()"); } };
+  a = { valueOf: function () { trap(main, 91, "success()"); } };
   b = "";
   eval();
   a + b;
   x = "failure";
 }
 function success() { x = "success"; }
 
 main();
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -5502,17 +5502,17 @@ LoadJSContextFromActivation(MacroAssembl
 static void
 AssertStackAlignment(MacroAssembler &masm)
 {
     JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0);
 #ifdef DEBUG
     Label ok;
     JS_ASSERT(IsPowerOfTwo(StackAlignment));
     masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
-    masm.breakpoint();
+    masm.assume_unreachable("Stack should be aligned.");
     masm.bind(&ok);
 #endif
 }
 
 template <class VectorT>
 static unsigned
 StackArgBytes(const VectorT &argTypes)
 {
@@ -5957,17 +5957,16 @@ GenerateOOLConvert(ModuleCompiler &m, Re
                             MIRType_Pointer }; // argv
     MIRTypeVector callArgTypes(m.cx());
     callArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray));
 
     // Reserve space for a call to InvokeFromAsmJS_* and an array of values
     // passed to this FFI call.
     unsigned arraySize = sizeof(Value);
     unsigned stackDec = StackDecrementForCall(masm, callArgTypes, arraySize);
-    masm.setFramePushed(0);
     masm.reserveStack(stackDec);
 
     // Store value
     unsigned offsetToArgv = StackArgBytes(callArgTypes);
     masm.storeValue(JSReturnOperand, Address(StackPointer, offsetToArgv));
 
     // Store real arguments
     ABIArgMIRTypeIter i(callArgTypes);
@@ -5991,16 +5990,17 @@ GenerateOOLConvert(ModuleCompiler &m, Re
     } else {
         masm.computeEffectiveAddress(argv, scratch);
         masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase()));
     }
     i++;
     JS_ASSERT(i.done());
 
     // Call
+    AssertStackAlignment(masm);
     switch (retType.which()) {
       case RetType::Signed:
           masm.call(AsmJSImm_CoerceInPlace_ToInt32);
           masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
           masm.unboxInt32(Address(StackPointer, offsetToArgv), ReturnReg);
           break;
       case RetType::Double:
           masm.call(AsmJSImm_CoerceInPlace_ToNumber);
@@ -6133,16 +6133,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
 
 #ifdef DEBUG
     masm.branchTestMagicValue(Assembler::Equal, JSReturnOperand, JS_ION_ERROR, throwLabel);
     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, &ionFailed);
 #else
     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
 #endif
 
+    uint32_t oolConvertFramePushed = masm.framePushed();
     switch (exit.sig().retType().which()) {
       case RetType::Void:
         break;
       case RetType::Signed:
         masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert,
                                  /* -0 check */ false);
         break;
       case RetType::Double:
@@ -6155,23 +6156,25 @@ GenerateFFIIonExit(ModuleCompiler &m, co
 
     masm.bind(&done);
     masm.PopRegsInMask(restoreSet);
     masm.ret();
 
     // oolConvert
     if (oolConvert.used()) {
         masm.bind(&oolConvert);
+        masm.setFramePushed(oolConvertFramePushed);
         GenerateOOLConvert(m, exit.sig().retType(), throwLabel);
+        masm.setFramePushed(0);
         masm.jump(&done);
     }
 
 #ifdef DEBUG
     masm.bind(&ionFailed);
-    masm.breakpoint();
+    masm.assume_unreachable("AsmJS to IonMonkey call failed.");
 #endif
 }
 
 // See "asm.js FFI calls" comment above.
 static void
 GenerateFFIExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit, unsigned exitIndex,
                 Label *throwLabel)
 {
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2360,16 +2360,20 @@ bool
 BaselineCompiler::emit_JSOP_CALLARG()
 {
     return emit_JSOP_GETARG();
 }
 
 bool
 BaselineCompiler::emit_JSOP_SETARG()
 {
+    // Ionmonkey can't inline functions with SETARG with magic arguments.
+    if (!script->argsObjAliasesFormals() && script->argumentsAliasesFormals())
+        script->uninlineable = true;
+
     modifiesArguments_ = true;
 
     uint32_t arg = GET_SLOTNO(pc);
     return emitFormalArgAccess(arg, /* get = */ false);
 }
 
 bool
 BaselineCompiler::emitCall()
@@ -2499,16 +2503,18 @@ BaselineCompiler::emit_JSOP_THROW()
     pushArg(R0);
 
     return callVM(ThrowInfo);
 }
 
 bool
 BaselineCompiler::emit_JSOP_TRY()
 {
+    // Ionmonkey can't inline function with JSOP_TRY.
+    script->uninlineable = true;
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_FINALLY()
 {
     // JSOP_FINALLY has a def count of 2, but these values are already on the
     // stack (they're pushed by JSOP_GOSUB). Update the compiler's stack state.
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1090,17 +1090,17 @@ ICProfiler_PushFunction::Compiler::gener
     Register scratch = R0.scratchReg();
     Register scratch2 = R1.scratchReg();
 
     // Profiling should be enabled if we ever reach here.
 #ifdef DEBUG
     Label spsEnabled;
     uint32_t *enabledAddr = cx->runtime()->spsProfiler.addressOfEnabled();
     masm.branch32(Assembler::NotEqual, AbsoluteAddress(enabledAddr), Imm32(0), &spsEnabled);
-    masm.breakpoint();
+    masm.assume_unreachable("Profiling should have been enabled.");
     masm.bind(&spsEnabled);
 #endif
 
     // Push SPS entry.
     masm.spsPushFrame(&cx->runtime()->spsProfiler,
                       Address(BaselineStubReg, ICProfiler_PushFunction::offsetOfStr()),
                       Address(BaselineStubReg, ICProfiler_PushFunction::offsetOfScript()),
                       scratch,
@@ -5128,17 +5128,17 @@ ICSetElem_Dense::Compiler::generateStubC
     masm.branchTest32(Assembler::Zero, elementsFlags,
                       Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
                       &dontConvertDoubles);
     // Note that double arrays are only created by IonMonkey, so if we have no
     // floating-point support Ion is disabled and there should be no double arrays.
     if (cx->runtime()->jitSupportsFloatingPoint)
         masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &dontConvertDoubles);
     else
-        masm.breakpoint();
+        masm.assume_unreachable("There shouldn't be double arrays when there is no FP support.");
     masm.bind(&dontConvertDoubles);
 
     // Don't overwrite R0 becuase |obj| might overlap with it, and it's needed
     // for post-write barrier later.
     ValueOperand tmpVal = regs.takeAnyValue();
     masm.loadValue(valueAddr, tmpVal);
     EmitPreBarrier(masm, element, MIRType_Value);
     masm.storeValue(tmpVal, element);
@@ -5316,17 +5316,17 @@ ICSetElemDenseAddCompiler::generateStubC
     masm.branchTest32(Assembler::Zero, elementsFlags,
                       Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
                       &dontConvertDoubles);
     // Note that double arrays are only created by IonMonkey, so if we have no
     // floating-point support Ion is disabled and there should be no double arrays.
     if (cx->runtime()->jitSupportsFloatingPoint)
         masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &dontConvertDoubles);
     else
-        masm.breakpoint();
+        masm.assume_unreachable("There shouldn't be double arrays when there is no FP support.");
     masm.bind(&dontConvertDoubles);
 
     // Write the value.  No need for pre-barrier since we're not overwriting an old value.
     ValueOperand tmpVal = regs.takeAnyValue();
     BaseIndex element(scratchReg, key, TimesEight);
     masm.loadValue(valueAddr, tmpVal);
     masm.storeValue(tmpVal, element);
     regs.add(key);
@@ -8279,17 +8279,17 @@ ICCall_Fallback::Compiler::generateStubC
     Label skipThisReplace;
     masm.load16ZeroExtend(Address(BaselineStubReg, ICStub::offsetOfExtra()), scratch);
     masm.branchTest32(Assembler::Zero, scratch, Imm32(ICCall_Fallback::CONSTRUCTING_FLAG),
                       &skipThisReplace);
     masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
     masm.moveValue(R1, R0);
 #ifdef DEBUG
     masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
-    masm.breakpoint();
+    masm.assume_unreachable("Failed to return object in constructing call.");
 #endif
     masm.bind(&skipThisReplace);
 
     // At this point, BaselineStubReg points to the ICCall_Fallback stub, which is NOT
     // a MonitoredStub, but rather a MonitoredFallbackStub.  To use EmitEnterTypeMonitorIC,
     // first load the ICTypeMonitor_Fallback stub into BaselineStubReg.  Then, use
     // EmitEnterTypeMonitorIC with a custom struct offset.
     masm.loadPtr(Address(BaselineStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
@@ -8388,17 +8388,17 @@ ICCallScriptedCompiler::generateStubCode
         masm.push(masm.extractObject(R1, ExtractTemp0));
         if (!callVM(CreateThisInfoBaseline, masm))
             return false;
 
         // Return of CreateThis must be an object.
 #ifdef DEBUG
         Label createdThisIsObject;
         masm.branchTestObject(Assembler::Equal, JSReturnOperand, &createdThisIsObject);
-        masm.breakpoint();
+        masm.assume_unreachable("The return of CreateThis must be an object.");
         masm.bind(&createdThisIsObject);
 #endif
 
         // Reset the register set from here on in.
         JS_ASSERT(JSReturnOperand == R0);
         regs = availableGeneralRegs(0);
         regs.take(R0);
         regs.take(ArgumentsRectifierReg);
@@ -8529,17 +8529,17 @@ ICCallScriptedCompiler::generateStubCode
         // STUB_FRAME_SIZE + sizeof(ThisVal) + sizeof(size_t) + sizeof(void *) + sizoef(size_t)
         // for: stub frame, this value, actual argc, callee, and descriptor
         masm.lshiftPtr(Imm32(1), scratchReg);
         BaseIndex reloadThisSlot(BaselineStackReg, scratchReg, TimesEight,
                                  STUB_FRAME_SIZE + sizeof(Value) + 3*sizeof(size_t));
         masm.loadValue(reloadThisSlot, JSReturnOperand);
 #ifdef DEBUG
         masm.branchTestObject(Assembler::Equal, JSReturnOperand, &skipThisReplace);
-        masm.breakpoint();
+        masm.assume_unreachable("Return of constructing call should be an object.");
 #endif
         masm.bind(&skipThisReplace);
     }
 
     leaveStubFrame(masm, true);
 
     // Enter type monitor IC to type-check result.
     EmitEnterTypeMonitorIC(masm);
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -221,18 +221,30 @@ CanUseInt32Compare(ICStub::Kind kind)
 
 MCompare::CompareType
 BaselineInspector::expectedCompareType(jsbytecode *pc)
 {
     ICStub *first = monomorphicStub(pc), *second = nullptr;
     if (!first && !dimorphicStub(pc, &first, &second))
         return MCompare::Compare_Unknown;
 
-    if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind())))
+    if (CanUseInt32Compare(first->kind()) && (!second || CanUseInt32Compare(second->kind()))) {
+        ICCompare_Int32WithBoolean *coerce =
+            first->isCompare_Int32WithBoolean()
+            ? first->toCompare_Int32WithBoolean()
+            : ((second && second->isCompare_Int32WithBoolean())
+               ? second->toCompare_Int32WithBoolean()
+               : nullptr);
+        if (coerce) {
+            return coerce->lhsIsInt32()
+                   ? MCompare::Compare_Int32MaybeCoerceRHS
+                   : MCompare::Compare_Int32MaybeCoerceLHS;
+        }
         return MCompare::Compare_Int32;
+    }
 
     if (CanUseDoubleCompare(first->kind()) && (!second || CanUseDoubleCompare(second->kind()))) {
         ICCompare_NumberWithUndefined *coerce =
             first->isCompare_NumberWithUndefined()
             ? first->toCompare_NumberWithUndefined()
             : (second && second->isCompare_NumberWithUndefined())
               ? second->toCompare_NumberWithUndefined()
               : nullptr;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -196,17 +196,18 @@ CodeGenerator::visitValueToInt32(LValueT
             stringRejoin = nullptr;
         }
 
         masm.truncateValueToInt32(operand, input, stringEntry, stringRejoin, oolDouble->entry(),
                                   stringReg, temp, output, &fails);
         masm.bind(oolDouble->rejoin());
     } else {
         masm.convertValueToInt32(operand, input, temp, output, &fails,
-                                 lir->mirNormal()->canBeNegativeZero());
+                                 lir->mirNormal()->canBeNegativeZero(),
+                                 lir->mirNormal()->conversion());
     }
 
     return bailoutFrom(&fails, lir->snapshot());
 }
 
 bool
 CodeGenerator::visitValueToDouble(LValueToDouble *lir)
 {
@@ -2493,17 +2494,17 @@ CodeGenerator::generateArgumentsChecks(b
     if (miss.used()) {
         if (bailout) {
             if (!bailoutFrom(&miss, graph.entrySnapshot()))
                 return false;
         } else {
             Label success;
             masm.jump(&success);
             masm.bind(&miss);
-            masm.breakpoint();
+            masm.assume_unreachable("Argument check fail.");
             masm.bind(&success);
         }
     }
 
     masm.freeStack(frameSize());
 
     return true;
 }
@@ -3667,17 +3668,17 @@ CodeGenerator::visitGetArgumentsObjectAr
     ValueOperand out = ToOutValue(lir);
 
     masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
     Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
     masm.loadValue(argAddr, out);
 #ifdef DEBUG
     Label success;
     masm.branchTestMagic(Assembler::NotEqual, out, &success);
-    masm.breakpoint();
+    masm.assume_unreachable("Result from ArgumentObject shouldn't be MIRType_Magic.");
     masm.bind(&success);
 #endif
     return true;
 }
 
 bool
 CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir)
 {
@@ -3686,17 +3687,17 @@ CodeGenerator::visitSetArgumentsObjectAr
     ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex);
 
     masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
     Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
     emitPreBarrier(argAddr, MIRType_Value);
 #ifdef DEBUG
     Label success;
     masm.branchTestMagic(Assembler::NotEqual, argAddr, &success);
-    masm.breakpoint();
+    masm.assume_unreachable("Result in ArgumentObject shouldn't be MIRType_Magic.");
     masm.bind(&success);
 #endif
     masm.storeValue(value, argAddr);
     return true;
 }
 
 bool
 CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir)
@@ -3834,17 +3835,17 @@ CodeGenerator::visitMinMaxI(LMinMaxI *in
 bool
 CodeGenerator::visitAbsI(LAbsI *ins)
 {
     Register input = ToRegister(ins->input());
     Label positive;
 
     JS_ASSERT(input == ToRegister(ins->output()));
     masm.test32(input, input);
-    masm.j(Assembler::GreaterThanOrEqual, &positive);
+    masm.j(Assembler::NotSigned, &positive);
     masm.neg32(input);
     if (ins->snapshot() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
         return false;
     masm.bind(&positive);
 
     return true;
 }
 
@@ -4515,17 +4516,17 @@ static void
 CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch)
 {
     // Copy |len| jschars from |from| to |to|. Assumes len > 0 (checked below in
     // debug builds), and when done |to| must point to the next available char.
 
 #ifdef DEBUG
     Label ok;
     masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
-    masm.breakpoint();
+    masm.assume_unreachable("Length should be greater than 0.");
     masm.bind(&ok);
 #endif
 
     JS_STATIC_ASSERT(sizeof(jschar) == 2);
 
     Label start;
     masm.bind(&start);
     masm.load16ZeroExtend(Address(from, 0), scratch);
@@ -5913,16 +5914,30 @@ CodeGenerator::link(JSContext *cx, types
 
     ionScript->setMethod(code);
     ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
 
     // If SPS is enabled, mark IonScript as having been instrumented with SPS
     if (sps_.enabled())
         ionScript->setHasSPSInstrumentation();
 
+    // We finished the new IonScript. Invalidate the current active IonScript,
+    // so we can replace it with this new (probably higher optimized) version.
+    if (HasIonScript(script, executionMode)) {
+        JS_ASSERT(GetIonScript(script, executionMode)->isRecompiling());
+        // Do a normal invalidate, except don't cancel offThread compilations,
+        // since that will cancel this compilation too.
+        if (!Invalidate(cx, script, SequentialExecution,
+                        /* resetUses */ false, /* cancelOffThread*/ false))
+        {
+            js_free(ionScript);
+            return false;
+        }
+    }
+
     SetIonScript(script, executionMode, ionScript);
 
     // In parallel execution mode, when we first compile a script, we
     // don't know that its potential callees are compiled, so set a
     // flag warning that the callees may not be fully compiled.
     if (callTargets.length() != 0)
         ionScript->setHasUncompiledCallTarget();
 
@@ -7683,17 +7698,17 @@ CodeGenerator::visitAsmJSCall(LAsmJSCall
         masm.freeStack(mir->spIncrement());
 
    JS_ASSERT((AlignmentAtPrologue +  masm.framePushed()) % StackAlignment == 0);
 
 #ifdef DEBUG
     Label ok;
     JS_ASSERT(IsPowerOfTwo(StackAlignment));
     masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
-    masm.breakpoint();
+    masm.assume_unreachable("Stack should be aligned.");
     masm.bind(&ok);
 #endif
 
     MAsmJSCall::Callee callee = mir->callee();
     switch (callee.which()) {
       case MAsmJSCall::Callee::Internal:
         masm.call(callee.internal());
         break;
@@ -7762,25 +7777,25 @@ CodeGenerator::visitAsmJSCheckOverRecurs
 
 bool
 CodeGenerator::emitAssertRangeI(const Range *r, Register input)
 {
     // Check the lower bound.
     if (r->hasInt32LowerBound() && r->lower() > INT32_MIN) {
         Label success;
         masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), &success);
-        masm.breakpoint();
+        masm.assume_unreachable("Integer input should be equal or higher than Lowerbound.");
         masm.bind(&success);
     }
 
     // Check the upper bound.
     if (r->hasInt32UpperBound() && r->upper() < INT32_MAX) {
         Label success;
         masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()), &success);
-        masm.breakpoint();
+        masm.assume_unreachable("Integer input should be lower or equal than Upperbound.");
         masm.bind(&success);
     }
 
     // For r->canHaveFractionalPart() and r->exponent(), there's nothing to check, because
     // if we ended up in the integer range checking code, the value is already
     // in an integer register in the integer range.
 
     return true;
@@ -7791,67 +7806,67 @@ CodeGenerator::emitAssertRangeD(const Ra
 {
     // Check the lower bound.
     if (r->hasInt32LowerBound()) {
         Label success;
         masm.loadConstantDouble(r->lower(), temp);
         if (r->canBeNaN())
             masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
         masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, &success);
-        masm.breakpoint();
+        masm.assume_unreachable("Double input should be equal or higher than Lowerbound.");
         masm.bind(&success);
     }
     // Check the upper bound.
     if (r->hasInt32UpperBound()) {
         Label success;
         masm.loadConstantDouble(r->upper(), temp);
         if (r->canBeNaN())
             masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
         masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success);
-        masm.breakpoint();
+        masm.assume_unreachable("Double input should be lower or equal than Upperbound.");
         masm.bind(&success);
     }
 
     // This code does not yet check r->canHaveFractionalPart(). This would require new
     // assembler interfaces to make rounding instructions available.
 
     if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() && r->exponent() < DoubleExponentBias) {
         // Check the bounds implied by the maximum exponent.
         Label exponentLoOk;
         masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp);
         masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk);
         masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &exponentLoOk);
-        masm.breakpoint();
+        masm.assume_unreachable("Check for exponent failed.");
         masm.bind(&exponentLoOk);
 
         Label exponentHiOk;
         masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp);
         masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk);
         masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, &exponentHiOk);
-        masm.breakpoint();
+        masm.assume_unreachable("Check for exponent failed.");
         masm.bind(&exponentHiOk);
     } else if (!r->hasInt32Bounds() && !r->canBeNaN()) {
         // If we think the value can't be NaN, check that it isn't.
         Label notnan;
         masm.branchDouble(Assembler::DoubleOrdered, input, input, &notnan);
-        masm.breakpoint();
+        masm.assume_unreachable("Input shouldn't be NaN.");
         masm.bind(&notnan);
 
         // If we think the value also can't be an infinity, check that it isn't.
         if (!r->canBeInfiniteOrNaN()) {
             Label notposinf;
             masm.loadConstantDouble(PositiveInfinity(), temp);
             masm.branchDouble(Assembler::DoubleLessThan, input, temp, &notposinf);
-            masm.breakpoint();
+            masm.assume_unreachable("Input shouldn't be +Inf.");
             masm.bind(&notposinf);
 
             Label notneginf;
             masm.loadConstantDouble(NegativeInfinity(), temp);
             masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, &notneginf);
-            masm.breakpoint();
+            masm.assume_unreachable("Input shouldn't be -Inf.");
             masm.bind(&notneginf);
         }
     }
 
     return true;
 }
 
 bool
@@ -7910,15 +7925,15 @@ CodeGenerator::visitAssertRangeV(LAssert
         FloatRegister input = ToFloatRegister(ins->floatTemp1());
         FloatRegister temp = ToFloatRegister(ins->floatTemp2());
         masm.unboxDouble(value, input);
         emitAssertRangeD(r, input, temp);
         masm.jump(&done);
         masm.bind(&isNotDouble);
     }
 
-    masm.breakpoint();
+    masm.assume_unreachable("Incorrect range for Value.");
     masm.bind(&done);
     return true;
 }
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -513,16 +513,20 @@ JitCompartment::ensureIonStubsExist(JSCo
     return true;
 }
 
 void
 jit::FinishOffThreadBuilder(IonBuilder *builder)
 {
     ExecutionMode executionMode = builder->info().executionMode();
 
+    // Clear the recompiling flag if it would have failed.
+    if (builder->script()->hasIonScript())
+        builder->script()->ionScript()->clearRecompiling();
+
     // Clean up if compilation did not succeed.
     if (CompilingOffThread(builder->script(), executionMode))
         SetIonScript(builder->script(), executionMode, nullptr);
 
     // The builder is allocated into its LifoAlloc, so destroying that will
     // destroy the builder and all other data accumulated during compilation,
     // except any final codegen (which includes an assembler and needs to be
     // explicitly destroyed).
@@ -717,16 +721,17 @@ IonScript::IonScript()
     osrPc_(nullptr),
     osrEntryOffset_(0),
     skipArgCheckEntryOffset_(0),
     invalidateEpilogueOffset_(0),
     invalidateEpilogueDataOffset_(0),
     numBailouts_(0),
     hasUncompiledCallTarget_(false),
     hasSPSInstrumentation_(false),
+    recompiling_(false),
     runtimeData_(0),
     runtimeSize_(0),
     cacheIndex_(0),
     cacheEntries_(0),
     safepointIndexOffset_(0),
     safepointIndexEntries_(0),
     safepointsStart_(0),
     safepointsSize_(0),
@@ -1595,25 +1600,24 @@ TrackPropertiesForSingletonScopes(JSCont
         if (scope->is<CallObject>() && scope->hasSingletonType())
             TrackAllProperties(cx, scope);
     }
 }
 
 static AbortReason
 IonCompile(JSContext *cx, JSScript *script,
            BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing,
-           ExecutionMode executionMode)
+           ExecutionMode executionMode, bool recompile)
 {
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
-
     TrackPropertiesForSingletonScopes(cx, script, baselineFrame);
 
     LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
     if (!alloc)
         return AbortReason_Alloc;
 
     ScopedJSDeletePtr<LifoAlloc> autoDelete(alloc);
 
@@ -1658,17 +1662,17 @@ IonCompile(JSContext *cx, JSScript *scri
 
     IonBuilder *builder = alloc->new_<IonBuilder>((JSContext *) nullptr,
                                                   CompileCompartment::get(cx->compartment()),
                                                   temp, graph, constraints,
                                                   &inspector, info, baselineFrameInspector);
     if (!builder)
         return AbortReason_Alloc;
 
-    JS_ASSERT(!GetIonScript(builder->script(), executionMode));
+    JS_ASSERT(recompile == HasIonScript(builder->script(), executionMode));
     JS_ASSERT(CanIonCompile(builder->script(), executionMode));
 
     RootedScript builderScript(cx, builder->script());
     IonSpewNewFunction(graph, builderScript);
 
     mozilla::Maybe<AutoProtectHeapForCompilation> protect;
     if (js_IonOptions.checkThreadSafety &&
         cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL &&
@@ -1688,17 +1692,22 @@ IonCompile(JSContext *cx, JSScript *scri
         }
 
         IonSpew(IonSpew_Abort, "Builder failed to build.");
         return builder->abortReason();
     }
 
     // If possible, compile the script off thread.
     if (OffThreadCompilationAvailable(cx)) {
-        SetIonScript(builder->script(), executionMode, ION_COMPILING_SCRIPT);
+        if (recompile) {
+            JS_ASSERT(executionMode == SequentialExecution);
+            builderScript->ionScript()->setRecompiling();
+        } else {
+            SetIonScript(builder->script(), executionMode, ION_COMPILING_SCRIPT);
+        }
 
         if (!StartOffThreadIonCompile(cx, builder)) {
             IonSpew(IonSpew_Abort, "Unable to start off-thread ion compilation.");
             return AbortReason_Alloc;
         }
 
         // The allocator and associated data will be destroyed after being
         // processed in the finishedOffThreadCompilations list.
@@ -1857,31 +1866,34 @@ Compile(JSContext *cx, HandleScript scri
     }
 
     MethodStatus status = CheckScriptSize(cx, script);
     if (status != Method_Compiled) {
         IonSpew(IonSpew_Abort, "Aborted compilation of %s:%d", script->filename(), script->lineno);
         return status;
     }
 
+    bool recompile = false;
+
     IonScript *scriptIon = GetIonScript(script, executionMode);
     if (scriptIon) {
         if (!scriptIon->method())
             return Method_CantCompile;
         return Method_Compiled;
     }
 
     if (executionMode == SequentialExecution) {
         // Use getUseCount instead of incUseCount to avoid bumping the
         // use count twice.
         if (script->getUseCount() < UsesBeforeIonRecompile(script, osrPc ? osrPc : script->code()))
             return Method_Skipped;
     }
 
-    AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode);
+    AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode,
+                                    recompile);
     if (reason == AbortReason_Error)
         return Method_Error;
 
     if (reason == AbortReason_Disable)
         return Method_CantCompile;
 
     if (reason == AbortReason_Alloc) {
         js_ReportOutOfMemory(cx);
@@ -2053,16 +2065,35 @@ jit::CompileFunctionForBaseline(JSContex
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
 
 MethodStatus
+jit::Recompile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
+               bool constructing)
+{
+    JS_ASSERT(script->hasIonScript());
+    if (script->ionScript()->isRecompiling())
+        return Method_Compiled;
+
+    MethodStatus status =
+        Compile(cx, script, osrFrame, osrPc, constructing, SequentialExecution);
+    if (status != Method_Compiled) {
+        if (status == Method_CantCompile)
+            ForbidCompilation(cx, script);
+        return status;
+    }
+
+    return Method_Compiled;
+}
+
+MethodStatus
 jit::CanEnterInParallel(JSContext *cx, HandleScript script)
 {
     // Skip if the script has been disabled.
     //
     // Note: We return Method_Skipped in this case because the other
     // CanEnter() methods do so. However, ForkJoin.cpp detects this
     // condition differently treats it more like an error.
     if (!script->canParallelIonCompile())
@@ -2423,30 +2454,32 @@ jit::InvalidateAll(FreeOp *fop, Zone *zo
             InvalidateActivation(fop, iter.jitTop(), true);
         }
     }
 }
 
 
 void
 jit::Invalidate(types::TypeCompartment &types, FreeOp *fop,
-                const Vector<types::RecompileInfo> &invalid, bool resetUses)
+                const Vector<types::RecompileInfo> &invalid, bool resetUses,
+                bool cancelOffThread)
 {
     IonSpew(IonSpew_Invalidate, "Start invalidation.");
     AutoFlushCache afc ("Invalidate", fop->runtime()->jitRuntime());
 
     // Add an invalidation reference to all invalidated IonScripts to indicate
     // to the traversal which frames have been invalidated.
     size_t numInvalidations = 0;
     for (size_t i = 0; i < invalid.length(); i++) {
         const types::CompilerOutput &co = *invalid[i].compilerOutput(types);
         if (!co.isValid())
             continue;
 
-        CancelOffThreadIonCompile(co.script()->compartment(), co.script());
+        if (cancelOffThread)
+            CancelOffThreadIonCompile(co.script()->compartment(), co.script());
 
         if (!co.ion())
             continue;
 
         IonSpew(IonSpew_Invalidate, " Invalidate %s:%u, IonScript %p",
                 co.script()->filename(), co.script()->lineno, co.ion());
 
         // Keep the ion script alive during the invalidation and flag this
@@ -2500,23 +2533,26 @@ jit::Invalidate(types::TypeCompartment &
     }
 
     // Make sure we didn't leak references by invalidating the same IonScript
     // multiple times in the above loop.
     JS_ASSERT(!numInvalidations);
 }
 
 void
-jit::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses)
+jit::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses,
+                bool cancelOffThread)
 {
-    jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses);
+    jit::Invalidate(cx->compartment()->types, cx->runtime()->defaultFreeOp(), invalid, resetUses,
+                    cancelOffThread);
 }
 
 bool
-jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses)
+jit::Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses,
+                bool cancelOffThread)
 {
     JS_ASSERT(script->hasIonScript());
 
     Vector<types::RecompileInfo> scripts(cx);
 
     switch (mode) {
       case SequentialExecution:
         JS_ASSERT(script->hasIonScript());
@@ -2527,24 +2563,24 @@ jit::Invalidate(JSContext *cx, JSScript 
         JS_ASSERT(script->hasParallelIonScript());
         if (!scripts.append(script->parallelIonScript()->recompileInfo()))
             return false;
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
-    Invalidate(cx, scripts, resetUses);
+    Invalidate(cx, scripts, resetUses, cancelOffThread);
     return true;
 }
 
 bool
-jit::Invalidate(JSContext *cx, JSScript *script, bool resetUses)
+jit::Invalidate(JSContext *cx, JSScript *script, bool resetUses, bool cancelOffThread)
 {
-    return Invalidate(cx, script, SequentialExecution, resetUses);
+    return Invalidate(cx, script, SequentialExecution, resetUses, cancelOffThread);
 }
 
 static void
 FinishInvalidationOf(FreeOp *fop, JSScript *script, IonScript *ionScript, bool parallel)
 {
     // In all cases, nullptr out script->ion or script->parallelIon to avoid
     // re-entry.
     if (parallel)
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -89,17 +89,17 @@ struct IonOptions
     // Default: false
     bool checkThreadSafety;
 
     // Whether to perform expensive graph-consistency DEBUG-only assertions.
     // It can be useful to disable this to reduce DEBUG-compile time of large
     // asm.js programs.
     //
     // Default: true
-    bool assertGraphConsistency;
+    bool checkGraphConsistency;
 
     // Toggles whether Unreachable Code Elimination is performed.
     //
     // Default: true
     bool uce;
 
     // Toggles whether Effective Address Analysis is performed.
     //
@@ -223,17 +223,17 @@ struct IonOptions
         osr(true),
         limitScriptSize(true),
         registerAllocator(RegisterAllocator_LSRA),
         inlining(true),
         edgeCaseAnalysis(true),
         rangeAnalysis(true),
         checkRangeAnalysis(false),
         checkThreadSafety(false),
-        assertGraphConsistency(true),
+        checkGraphConsistency(true),
         uce(true),
         eaa(true),
 #ifdef CHECK_OSIPOINT_REGISTERS
         checkOsiPointRegisters(false),
 #endif
         baselineUsesBeforeCompile(10),
         usesBeforeCompile(1000),
         usesBeforeInliningFactor(.125),
@@ -325,16 +325,20 @@ MethodStatus CanEnterAtBranch(JSContext 
                               BaselineFrame *frame, jsbytecode *pc, bool isConstructing);
 MethodStatus CanEnter(JSContext *cx, RunState &state);
 MethodStatus CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame,
                                         bool isConstructing);
 MethodStatus CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs);
 
 MethodStatus CanEnterInParallel(JSContext *cx, HandleScript script);
 
+MethodStatus
+Recompile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
+          bool constructing);
+
 enum IonExecStatus
 {
     // The method call had to be aborted due to a stack limit check. This
     // error indicates that Ion never attempted to clean up frames.
     IonExec_Aborted,
 
     // The method call resulted in an error, and IonMonkey has cleaned up
     // frames.
@@ -356,20 +360,24 @@ bool SetEnterJitData(JSContext *cx, Ente
 
 IonExecStatus IonCannon(JSContext *cx, RunState &state);
 
 // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard.
 IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args);
 
 // Walk the stack and invalidate active Ion frames for the invalid scripts.
 void Invalidate(types::TypeCompartment &types, FreeOp *fop,
-                const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
-void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
-bool Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses = true);
-bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true);
+                const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
+                bool cancelOffThread = true);
+void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true,
+                bool cancelOffThread = true);
+bool Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses = true,
+                bool cancelOffThread = true);
+bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true,
+                bool cancelOffThread = true);
 
 void MarkValueFromIon(JSRuntime *rt, Value *vp);
 void MarkShapeFromIon(JSRuntime *rt, Shape **shapep);
 
 void ToggleBarriers(JS::Zone *zone, bool needs);
 
 class IonBuilder;
 class MIRGenerator;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1283,32 +1283,32 @@ AssertReversePostOrder(MIRGraph &graph)
     graph.unmarkBlocks();
 }
 #endif
 
 void
 jit::AssertGraphCoherency(MIRGraph &graph)
 {
 #ifdef DEBUG
-    if (!js_IonOptions.assertGraphConsistency)
+    if (!js_IonOptions.checkGraphConsistency)
         return;
     AssertBasicGraphCoherency(graph);
     AssertReversePostOrder(graph);
 #endif
 }
 
 void
 jit::AssertExtendedGraphCoherency(MIRGraph &graph)
 {
     // Checks the basic GraphCoherency but also other conditions that
     // do not hold immediately (such as the fact that critical edges
     // are split)
 
 #ifdef DEBUG
-    if (!js_IonOptions.assertGraphConsistency)
+    if (!js_IonOptions.checkGraphConsistency)
         return;
     AssertGraphCoherency(graph);
 
     uint32_t idx = 0;
     for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
         JS_ASSERT(block->id() == idx++);
 
         // No critical edges:
@@ -1444,17 +1444,17 @@ jit::ExtractLinearInequality(MTest *test
         return false;
 
     MCompare *compare = test->getOperand(0)->toCompare();
 
     MDefinition *lhs = compare->getOperand(0);
     MDefinition *rhs = compare->getOperand(1);
 
     // TODO: optimize Compare_UInt32
-    if (compare->compareType() != MCompare::Compare_Int32)
+    if (!compare->isInt32Comparison())
         return false;
 
     JS_ASSERT(lhs->type() == MIRType_Int32);
     JS_ASSERT(rhs->type() == MIRType_Int32);
 
     JSOp jsop = compare->jsop();
     if (direction == FALSE_BRANCH)
         jsop = analyze::NegateCompareOp(jsop);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -3430,18 +3430,17 @@ IonBuilder::jsop_try()
     if (!js_IonOptions.compileTryCatch)
         return abort("Try-catch support disabled");
 
     // Try-finally is not yet supported.
     if (analysis().hasTryFinally())
         return abort("Has try-finally");
 
     // Try-catch within inline frames is not yet supported.
-    if (isInlineBuilder())
-        return abort("try-catch within inline frame");
+    JS_ASSERT(script()->uninlineable && !isInlineBuilder());
 
     graph().setHasTryBlock();
 
     jssrcnote *sn = info().getNote(gsn, pc);
     JS_ASSERT(SN_TYPE(sn) == SRC_TRY);
 
     // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to
     // jump over the catch block.
@@ -9145,19 +9144,18 @@ IonBuilder::jsop_setarg(uint32_t arg)
     }
 
     // Otherwise, if a magic arguments is in use, and it aliases formals, and there exist
     // arguments[...] GETELEM expressions in the script, then SetFrameArgument must be used.
     // If no arguments[...] GETELEM expressions are in the script, and an argsobj is not
     // required, then it means that any aliased argument set can never be observed, and
     // the frame does not actually need to be updated with the new arg value.
     if (info().argumentsAliasesFormals()) {
-        // Try-catch within inline frames is not yet supported.
-        if (isInlineBuilder())
-            return abort("JSOP_SETARG with magic arguments in inlined function.");
+        // JSOP_SETARG with magic arguments within inline frames is not yet supported.
+        JS_ASSERT(script()->uninlineable && !isInlineBuilder());
 
         MSetFrameArgument *store = MSetFrameArgument::New(alloc(), arg, val);
         current->add(store);
         current->setArg(arg);
         return true;
     }
 
     // If this assignment is at the start of the function and is coercing
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -194,16 +194,19 @@ struct IonScript
     // Flag set when it is likely that one of our (transitive) call
     // targets is not compiled.  Used in ForkJoin.cpp to decide when
     // we should add call targets to the worklist.
     bool hasUncompiledCallTarget_;
 
     // Flag set if IonScript was compiled with SPS profiling enabled.
     bool hasSPSInstrumentation_;
 
+    // Flag for if this script is getting recompiled.
+    bool recompiling_;
+
     // Any kind of data needed by the runtime, these can be either cache
     // information or profiling info.
     uint32_t runtimeData_;
     uint32_t runtimeSize_;
 
     // State for polymorphic caches in the compiled code. All caches are stored
     // in the runtimeData buffer and indexed by the cacheIndex which give a
     // relative offset in the runtimeData array.
@@ -525,16 +528,28 @@ struct IonScript
     }
     uint32_t incrOsrPcMismatchCounter() {
         return ++osrPcMismatchCounter_;
     }
     void resetOsrPcMismatchCounter() {
         osrPcMismatchCounter_ = 0;
     }
 
+    void setRecompiling() {
+        recompiling_ = true;
+    }
+
+    bool isRecompiling() const {
+        return recompiling_;
+    }
+
+    void clearRecompiling() {
+        recompiling_ = false;
+    }
+
     static void writeBarrierPre(Zone *zone, IonScript *ionScript);
 };
 
 // Execution information for a basic block which may persist after the
 // accompanying IonScript is destroyed, for use during profiling.
 struct IonBlockCounts
 {
   private:
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1200,16 +1200,53 @@ MacroAssembler::handleFailure(ExecutionM
     }
     MacroAssemblerSpecific::handleFailureWithHandler(handler);
 
     // Doesn't actually emit code, but balances the leave()
     if (sps_)
         sps_->reenter(*this, InvalidReg);
 }
 
+#ifdef DEBUG
+static inline bool
+IsCompilingAsmJS()
+{
+    // asm.js compilation pushes an IonContext with a null JSCompartment.
+    IonContext *ictx = MaybeGetIonContext();
+    return ictx && ictx->compartment == nullptr;
+}
+#endif
+
+static void assume_unreachable_(const char *output) {
+    MOZ_ReportAssertionFailure(output, __FILE__, __LINE__);
+}
+
+void
+MacroAssembler::assume_unreachable(const char *output)
+{
+#ifdef DEBUG
+    // AsmJS forbids use of ImmPtr.
+    if (!IsCompilingAsmJS()) {
+        RegisterSet regs = RegisterSet::Volatile();
+        PushRegsInMask(regs);
+
+        Register temp = regs.takeGeneral();
+
+        setupUnalignedABICall(1, temp);
+        movePtr(ImmPtr(output), temp);
+        passABIArg(temp);
+        callWithABI(JS_FUNC_TO_DATA_PTR(void *, assume_unreachable_));
+
+        PopRegsInMask(RegisterSet::Volatile());
+    }
+#endif
+
+    breakpoint();
+}
+
 static void printf0_(const char *output) {
     printf("%s", output);
 }
 
 void
 MacroAssembler::printf(const char *output)
 {
     RegisterSet regs = RegisterSet::Volatile();
@@ -1521,48 +1558,56 @@ MacroAssembler::convertDoubleToInt(Float
     }
 }
 
 void
 MacroAssembler::convertValueToInt(ValueOperand value, MDefinition *maybeInput,
                                   Label *handleStringEntry, Label *handleStringRejoin,
                                   Label *truncateDoubleSlow,
                                   Register stringReg, FloatRegister temp, Register output,
-                                  Label *fail, IntConversionBehavior behavior)
+                                  Label *fail, IntConversionBehavior behavior,
+                                  IntConversionInputKind conversion)
 {
     Register tag = splitTagForTest(value);
     bool handleStrings = (behavior == IntConversion_Truncate ||
                           behavior == IntConversion_ClampToUint8) &&
                          handleStringEntry &&
                          handleStringRejoin;
     bool zeroObjects = behavior == IntConversion_ClampToUint8;
 
+    JS_ASSERT_IF(handleStrings || zeroObjects, conversion == IntConversion_Any);
+
     Label done, isInt32, isBool, isDouble, isNull, isString;
 
     branchEqualTypeIfNeeded(MIRType_Int32, maybeInput, tag, &isInt32);
-    branchEqualTypeIfNeeded(MIRType_Boolean, maybeInput, tag, &isBool);
+    if (conversion == IntConversion_Any)
+        branchEqualTypeIfNeeded(MIRType_Boolean, maybeInput, tag, &isBool);
     branchEqualTypeIfNeeded(MIRType_Double, maybeInput, tag, &isDouble);
 
-    // If we are not truncating, we fail for anything that's not
-    // null. Otherwise we might be able to handle strings and objects.
-    switch (behavior) {
-      case IntConversion_Normal:
-      case IntConversion_NegativeZeroCheck:
-        branchTestNull(Assembler::NotEqual, tag, fail);
-        break;
+    if (conversion == IntConversion_Any) {
+        // If we are not truncating, we fail for anything that's not
+        // null. Otherwise we might be able to handle strings and objects.
+        switch (behavior) {
+          case IntConversion_Normal:
+          case IntConversion_NegativeZeroCheck:
+            branchTestNull(Assembler::NotEqual, tag, fail);
+            break;
 
-      case IntConversion_Truncate:
-      case IntConversion_ClampToUint8:
-        branchEqualTypeIfNeeded(MIRType_Null, maybeInput, tag, &isNull);
-        if (handleStrings)
-            branchEqualTypeIfNeeded(MIRType_String, maybeInput, tag, &isString);
-        if (zeroObjects)
-            branchEqualTypeIfNeeded(MIRType_Object, maybeInput, tag, &isNull);
-        branchTestUndefined(Assembler::NotEqual, tag, fail);
-        break;
+          case IntConversion_Truncate:
+          case IntConversion_ClampToUint8:
+            branchEqualTypeIfNeeded(MIRType_Null, maybeInput, tag, &isNull);
+            if (handleStrings)
+                branchEqualTypeIfNeeded(MIRType_String, maybeInput, tag, &isString);
+            if (zeroObjects)
+                branchEqualTypeIfNeeded(MIRType_Object, maybeInput, tag, &isNull);
+            branchTestUndefined(Assembler::NotEqual, tag, fail);
+            break;
+        }
+    } else {
+        jump(fail);
     }
 
     // The value is null or undefined in truncation contexts - just emit 0.
     if (isNull.used())
         bind(&isNull);
     mov(ImmWord(0), output);
     jump(&done);
 
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -1089,16 +1089,17 @@ class MacroAssembler : public MacroAssem
           case SequentialExecution: return &sequentialFailureLabel_;
           case ParallelExecution: return &parallelFailureLabel_;
           default: MOZ_ASSUME_UNREACHABLE("Unexpected execution mode");
         }
     }
 
     void finish();
 
+    void assume_unreachable(const char *output);
     void printf(const char *output);
     void printf(const char *output, Register value);
 
 #if JS_TRACE_LOGGING
     void tracelogStart(JSScript *script);
     void tracelogStop();
     void tracelogLog(TraceLogging::Type type);
 #endif
@@ -1168,31 +1169,37 @@ class MacroAssembler : public MacroAssem
 
     enum IntConversionBehavior {
         IntConversion_Normal,
         IntConversion_NegativeZeroCheck,
         IntConversion_Truncate,
         IntConversion_ClampToUint8,
     };
 
+    enum IntConversionInputKind {
+        IntConversion_NumbersOnly,
+        IntConversion_Any
+    };
+
     //
     // Functions for converting values to int.
     //
     void convertDoubleToInt(FloatRegister src, Register output, FloatRegister temp,
                             Label *truncateFail, Label *fail, IntConversionBehavior behavior);
 
     // Strings may be handled by providing labels to jump to when the behavior
     // is truncation or clamping. The subroutine, usually an OOL call, is
     // passed the unboxed string in |stringReg| and should convert it to a
     // double store into |temp|.
     void convertValueToInt(ValueOperand value, MDefinition *input,
                            Label *handleStringEntry, Label *handleStringRejoin,
                            Label *truncateDoubleSlow,
                            Register stringReg, FloatRegister temp, Register output,
-                           Label *fail, IntConversionBehavior behavior);
+                           Label *fail, IntConversionBehavior behavior,
+                           IntConversionInputKind conversion = IntConversion_Any);
     void convertValueToInt(ValueOperand value, FloatRegister temp, Register output, Label *fail,
                            IntConversionBehavior behavior)
     {
         convertValueToInt(value, nullptr, nullptr, nullptr, nullptr, InvalidReg, temp, output,
                           fail, behavior);
     }
     bool convertValueToInt(JSContext *cx, const Value &v, Register output, Label *fail,
                            IntConversionBehavior behavior);
@@ -1208,22 +1215,23 @@ class MacroAssembler : public MacroAssem
                              bool negativeZeroCheck)
     {
         convertValueToInt(value, temp, output, fail, negativeZeroCheck
                           ? IntConversion_NegativeZeroCheck
                           : IntConversion_Normal);
     }
     void convertValueToInt32(ValueOperand value, MDefinition *input,
                              FloatRegister temp, Register output, Label *fail,
-                             bool negativeZeroCheck)
+                             bool negativeZeroCheck, IntConversionInputKind conversion = IntConversion_Any)
     {
         convertValueToInt(value, input, nullptr, nullptr, nullptr, InvalidReg, temp, output, fail,
                           negativeZeroCheck
                           ? IntConversion_NegativeZeroCheck
-                          : IntConversion_Normal);
+                          : IntConversion_Normal,
+                          conversion);
     }
     bool convertValueToInt32(JSContext *cx, const Value &v, Register output, Label *fail,
                              bool negativeZeroCheck)
     {
         return convertValueToInt(cx, v, output, fail, negativeZeroCheck
                                  ? IntConversion_NegativeZeroCheck
                                  : IntConversion_Normal);
     }
--- a/js/src/jit/LinearScan.cpp
+++ b/js/src/jit/LinearScan.cpp
@@ -1123,17 +1123,17 @@ LinearScanAllocator::canCoexist(LiveInte
  * Ensure intervals appear in exactly the appropriate one of {active,inactive,
  * handled}, and that active and inactive intervals do not conflict. Handled
  * intervals are checked for conflicts in validateAllocations for performance
  * reasons.
  */
 void
 LinearScanAllocator::validateIntervals()
 {
-    if (!js_IonOptions.assertGraphConsistency)
+    if (!js_IonOptions.checkGraphConsistency)
         return;
 
     for (IntervalIterator i(active.begin()); i != active.end(); i++) {
         JS_ASSERT(i->numRanges() > 0);
         JS_ASSERT(i->covers(current->start()));
 
         for (IntervalIterator j(active.begin()); j != i; j++)
             JS_ASSERT(canCoexist(*i, *j));
@@ -1169,17 +1169,17 @@ LinearScanAllocator::validateIntervals()
 
 /*
  * This function performs a nice, expensive check that all intervals
  * in the function can coexist with every other interval.
  */
 void
 LinearScanAllocator::validateAllocations()
 {
-    if (!js_IonOptions.assertGraphConsistency)
+    if (!js_IonOptions.checkGraphConsistency)
         return;
 
     for (IntervalIterator i(handled.begin()); i != handled.end(); i++) {
         for (IntervalIterator j(handled.begin()); j != i; j++) {
             JS_ASSERT(*i != *j);
             JS_ASSERT(canCoexist(*i, *j));
         }
         LinearScanVirtualRegister *reg = &vregs[i->vreg()];
--- a/js/src/jit/LiveRangeAllocator.h
+++ b/js/src/jit/LiveRangeAllocator.h
@@ -595,17 +595,17 @@ class LiveRangeAllocator : protected Reg
         if (!fixedIntervals[reg.code()]->addRangeAtHead(from, to))
             return false;
         return fixedIntervalsUnion->addRangeAtHead(from, to);
     }
 
     void validateVirtualRegisters()
     {
 #ifdef DEBUG
-        if (!js_IonOptions.assertGraphConsistency)
+        if (!js_IonOptions.checkGraphConsistency)
             return;
 
         for (size_t i = 1; i < graph.numVirtualRegisters(); i++) {
             VirtualRegister *reg = &vregs[i];
 
             LiveInterval *prev = nullptr;
             for (size_t j = 0; j < reg->numIntervals(); j++) {
                 LiveInterval *interval = reg->getInterval(j);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -590,22 +590,22 @@ LIRGenerator::visitCallDirectEval(MCallD
         lir = new(alloc()) LCallDirectEvalS(useRegisterAtStart(scopeChain),
                                             useRegisterAtStart(string));
     } else {
         lir = new(alloc()) LCallDirectEvalV(useRegisterAtStart(scopeChain));
         if (!useBoxAtStart(lir, LCallDirectEvalV::Argument, string))
             return false;
     }
 
-    if (!useBoxAtStart(lir, (string->type() == MIRType_String
-                             ? LCallDirectEvalS::ThisValue
-                             : LCallDirectEvalV::ThisValue),
-                       thisValue))
-    {
-        return false;
+    if (string->type() == MIRType_String) {
+        if (!useBoxAtStart(lir, LCallDirectEvalS::ThisValue, thisValue))
+            return false;
+    } else {
+        if (!useBoxAtStart(lir, LCallDirectEvalV::ThisValue, thisValue))
+            return false;
     }
 
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 static JSOp
 ReorderComparison(JSOp op, MDefinition **lhsp, MDefinition **rhsp)
 {
@@ -764,30 +764,27 @@ LIRGenerator::visitTest(MTest *test)
             LAllocation rhs = useRegisterOrConstant(right);
             LCompareBAndBranch *lir = new(alloc()) LCompareBAndBranch(rhs, ifTrue, ifFalse);
             if (!useBox(lir, LCompareBAndBranch::Lhs, left))
                 return false;
             return add(lir, comp);
         }
 
         // Compare and branch Int32 or Object pointers.
-        if (comp->compareType() == MCompare::Compare_Int32 ||
+        if (comp->isInt32Comparison() ||
             comp->compareType() == MCompare::Compare_UInt32 ||
             comp->compareType() == MCompare::Compare_Object)
         {
             JSOp op = ReorderComparison(comp->jsop(), &left, &right);
             LAllocation lhs = useRegister(left);
             LAllocation rhs;
-            if (comp->compareType() == MCompare::Compare_Int32 ||
-                comp->compareType() == MCompare::Compare_UInt32)
-            {
+            if (comp->isInt32Comparison() || comp->compareType() == MCompare::Compare_UInt32)
                 rhs = useAnyOrConstant(right);
-            } else {
+            else
                 rhs = useRegister(right);
-            }
             LCompareAndBranch *lir = new(alloc()) LCompareAndBranch(op, lhs, rhs, ifTrue, ifFalse);
             return add(lir, comp);
         }
 
         // Compare and branch doubles.
         if (comp->isDoubleComparison()) {
             LAllocation lhs = useRegister(left);
             LAllocation rhs = useRegister(right);
@@ -953,24 +950,24 @@ LIRGenerator::visitCompare(MCompare *com
 
         LCompareB *lir = new(alloc()) LCompareB(useRegisterOrConstant(right));
         if (!useBox(lir, LCompareB::Lhs, left))
             return false;
         return define(lir, comp);
     }
 
     // Compare Int32 or Object pointers.
-    if (comp->compareType() == MCompare::Compare_Int32 ||
+    if (comp->isInt32Comparison() ||
         comp->compareType() == MCompare::Compare_UInt32 ||
         comp->compareType() == MCompare::Compare_Object)
     {
         JSOp op = ReorderComparison(comp->jsop(), &left, &right);
         LAllocation lhs = useRegister(left);
         LAllocation rhs;
-        if (comp->compareType() == MCompare::Compare_Int32 ||
+        if (comp->isInt32Comparison() ||
             comp->compareType() == MCompare::Compare_UInt32)
         {
             rhs = useAnyOrConstant(right);
         } else {
             rhs = useRegister(right);
         }
         return define(new(alloc()) LCompare(op, lhs, rhs), comp);
     }
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1718,16 +1718,19 @@ MCompare::inputType()
       case Compare_Undefined:
         return MIRType_Undefined;
       case Compare_Null:
         return MIRType_Null;
       case Compare_Boolean:
         return MIRType_Boolean;
       case Compare_UInt32:
       case Compare_Int32:
+      case Compare_Int32MaybeCoerceBoth:
+      case Compare_Int32MaybeCoerceLHS:
+      case Compare_Int32MaybeCoerceRHS:
         return MIRType_Int32;
       case Compare_Double:
       case Compare_DoubleMaybeCoerceLHS:
       case Compare_DoubleMaybeCoerceRHS:
         return MIRType_Double;
       case Compare_Float32:
         return MIRType_Float32;
       case Compare_String:
@@ -1804,26 +1807,26 @@ MCompare::infer(BaselineInspector *inspe
         compareType_ = Compare_UInt32;
         return;
     }
 
     // Integer to integer or boolean to boolean comparisons may be treated as Int32.
     if ((lhs == MIRType_Int32 && rhs == MIRType_Int32) ||
         (lhs == MIRType_