Merge m-c to b-i
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 08 Dec 2013 08:32:36 -0800
changeset 174199 dcfd4bbf76a2f9b06a4e555a3f715a42bf907150
parent 174198 3e9af8f5053cf1fd447292fb09fe14d53c61b7c8 (current diff)
parent 174130 2827800c529aee3ea948dfd9674fd50b91481c59 (diff)
child 174200 c9634bdaec6728c8fda8eb9c1a8a7e5d5e590c18
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b-i
accessible/tests/mochitest/testTextboxes.js
accessible/tests/mochitest/test_textboxes.html
accessible/tests/mochitest/test_textboxes.xul
browser/metro/locales/en-US/chrome/crashprompt.properties
--- 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/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -66,16 +66,17 @@ const COMPAT = {
   // The indent of a console group in pixels.
   GROUP_INDENT: 12,
 };
 
 // A map from the console API call levels to the Web Console severities.
 const CONSOLE_API_LEVELS_TO_SEVERITIES = {
   error: "error",
   exception: "error",
+  assert: "error",
   warn: "warning",
   info: "info",
   log: "log",
   trace: "log",
   debug: "log",
   dir: "log",
   group: "log",
   groupCollapsed: "log",
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -55,16 +55,17 @@ support-files =
   test-bug-821877-csperrors.html^headers^
   test-bug-837351-security-errors.html
   test-bug-846918-hsts-invalid-headers.html
   test-bug-846918-hsts-invalid-headers.html^headers^
   test-bug-859170-longstring-hang.html
   test-bug-869003-iframe.html
   test-bug-869003-top-window.html
   test-closures.html
+  test-console-assert.html
   test-console-extras.html
   test-console-replaced-api.html
   test-console.html
   test-consoleiframes.html
   test-data.json
   test-data.json^headers^
   test-duplicate-error.html
   test-encoding-ISO-8859-1.html
@@ -130,16 +131,17 @@ support-files =
 [browser_netpanel_longstring_expand.js]
 [browser_output_breaks_after_console_dir_uninspectable.js]
 [browser_output_longstring_expand.js]
 [browser_repeated_messages_accuracy.js]
 [browser_result_format_as_string.js]
 [browser_warn_user_about_replaced_api.js]
 [browser_webconsole_abbreviate_source_url.js]
 [browser_webconsole_allow_mixedcontent_securityerrors.js]
+[browser_webconsole_assert.js]
 [browser_webconsole_basic_net_logging.js]
 [browser_webconsole_block_mixedcontent_securityerrors.js]
 [browser_webconsole_bug_579412_input_focus.js]
 [browser_webconsole_bug_580001_closing_after_completion.js]
 [browser_webconsole_bug_580030_errors_after_page_reload.js]
 [browser_webconsole_bug_580454_timestamp_l10n.js]
 [browser_webconsole_bug_582201_duplicate_errors.js]
 [browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js]
@@ -236,8 +238,9 @@ support-files =
 [browser_webconsole_property_provider.js]
 [browser_webconsole_scratchpad_panel_link.js]
 [browser_webconsole_split.js]
 [browser_webconsole_view_source.js]
 [browser_webconsole_reflow.js]
 [browser_webconsole_log_file_filter.js]
 [browser_webconsole_expandable_timestamps.js]
 [browser_webconsole_autocomplete_in_debugger_stackframe.js]
+[browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_assert.js
@@ -0,0 +1,50 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that console.assert() works as expected (i.e. outputs only on falsy
+// asserts). See bug 760193.
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-assert.html";
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
+}
+
+function consoleOpened(hud) {
+  waitForMessages({
+    webconsole: hud,
+    messages: [{
+      text: "start",
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_LOG,
+    },
+    {
+      text: "false assert",
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_ERROR,
+    },
+    {
+      text: "falsy assert",
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_ERROR,
+    },
+    {
+      text: "end",
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_LOG,
+    }],
+  }).then(() => {
+    let nodes = hud.outputNode.querySelectorAll(".message");
+    is(nodes.length, 4, "only four messages are displayed, no output from the true assert");
+    finishTest();
+  });
+
+  let button = content.document.querySelector("button");
+  ok(button, "we have the button");
+  EventUtils.sendMouseEvent({ type: "click" }, button, content);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js
@@ -0,0 +1,42 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Test that the autocomplete popup closes on switching tabs. See bug 900448.
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>bug 900448 - autocomplete popup closes on tab switch";
+
+let popup = null;
+
+registerCleanupFunction(function() {
+  popup = null;
+});
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
+}
+
+function consoleOpened(HUD) {
+  popup = HUD.jsterm.autocompletePopup;
+
+  popup._panel.addEventListener("popupshown", function popupOpened() {
+    popup._panel.removeEventListener("popupshown", popupOpened, false);
+    addTab("data:text/html;charset=utf-8,<p>testing autocomplete closes");
+    gBrowser.selectedBrowser.addEventListener("load", tab2Loaded, true);
+  }, false);
+
+  HUD.jsterm.setInputValue("sc");
+  EventUtils.synthesizeKey("r", {});
+}
+
+function tab2Loaded() {
+  gBrowser.selectedBrowser.removeEventListener("load", tab2Loaded, true);
+  ok(!popup.isOpen, "Popup closes on tab switch");
+  gBrowser.removeCurrentTab();
+  finishTest();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-console-assert.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+  <head>
+    <!--
+      Any copyright is dedicated to the Public Domain.
+      http://creativecommons.org/publicdomain/zero/1.0/
+    -->
+    <meta charset="utf-8">
+    <title>console.assert() test</title>
+    <script type="text/javascript">
+      function test() {
+        console.log("start");
+        console.assert(false, "false assert");
+        console.assert(0, "falsy assert");
+        console.assert(true, "true assert");
+        console.log("end");
+      }
+    </script>
+  </head>
+  <body>
+    <p>test console.assert()</p>
+    <button onclick="test();">test console.assert()</button>
+  </body>
+</html>
--- a/browser/devtools/webconsole/test/test-console-extras.html
+++ b/browser/devtools/webconsole/test/test-console-extras.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html dir="ltr" xml:lang="en-US" lang="en-US"><head>
     <meta charset="utf-8">
     <title>Console extended API test</title>
     <script type="text/javascript">
       function test() {
         console.log("start");
-        console.assert()
         console.clear()
         console.dirxml()
         console.profile()
         console.profileEnd()
         console.count()
         console.table()
         console.log("end");
       }
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -113,16 +113,17 @@ const MESSAGE_PREFERENCE_KEYS = [
   [ "secerror",   "secwarn",    null,     null,          ],  // Security
 ];
 
 // A mapping from the console API log event levels to the Web Console
 // severities.
 const LEVELS = {
   error: SEVERITY_ERROR,
   exception: SEVERITY_ERROR,
+  assert: SEVERITY_ERROR,
   warn: SEVERITY_WARNING,
   info: SEVERITY_INFO,
   log: SEVERITY_LOG,
   trace: SEVERITY_LOG,
   debug: SEVERITY_LOG,
   dir: SEVERITY_LOG,
   group: SEVERITY_LOG,
   groupCollapsed: SEVERITY_LOG,
@@ -740,17 +741,17 @@ WebConsoleFrame.prototype = {
     }
     this._updateCharSize();
   },
 
   /**
    * Calculates the width and height of a single character of the input box.
    * This will be used in opening the popup at the correct offset.
    *
-   * @private 
+   * @private
    */
   _updateCharSize: function WCF__updateCharSize()
   {
     let doc = this.document;
     let tempLabel = doc.createElementNS(XHTML_NS, "span");
     let style = tempLabel.style;
     style.position = "fixed";
     style.padding = "0";
@@ -1166,16 +1167,17 @@ WebConsoleFrame.prototype = {
     });
 
     switch (level) {
       case "log":
       case "info":
       case "warn":
       case "error":
       case "exception":
+      case "assert":
       case "debug": {
         let msg = new Messages.ConsoleGeneric(aMessage);
         node = msg.init(this.output).render().element;
         break;
       }
       case "dir": {
         body = { arguments: args };
         let clipboardArray = [];
@@ -2907,16 +2909,17 @@ function JSTerm(aWebConsoleFrame)
   // This is reset to this.history.length when this.execute() is invoked.
   this.historyPlaceHolder = 0;
   this._objectActorsInVariablesViews = new Map();
 
   this._keyPress = this._keyPress.bind(this);
   this._inputEventHandler = this._inputEventHandler.bind(this);
   this._focusEventHandler = this._focusEventHandler.bind(this);
   this._onKeypressInVariablesView = this._onKeypressInVariablesView.bind(this);
+  this._blurEventHandler = this._blurEventHandler.bind(this);
 
   EventEmitter.decorate(this);
 }
 
 JSTerm.prototype = {
   SELECTED_FRAME: -1,
 
   /**
@@ -3028,37 +3031,37 @@ JSTerm.prototype = {
   COMPLETE_BACKWARD: 1,
   COMPLETE_HINT_ONLY: 2,
 
   /**
    * Initialize the JSTerminal UI.
    */
   init: function JST_init()
   {
-    let chromeDocument = this.hud.owner.chromeWindow.document;
     let autocompleteOptions = {
       onSelect: this.onAutocompleteSelect.bind(this),
       onClick: this.acceptProposedCompletion.bind(this),
       panelId: "webConsole_autocompletePopup",
       listBoxId: "webConsole_autocompletePopupListBox",
       position: "before_start",
       theme: "auto",
       direction: "ltr",
       autoSelect: true
     };
-    this.autocompletePopup = new AutocompletePopup(chromeDocument,
+    this.autocompletePopup = new AutocompletePopup(this.hud.document,
                                                    autocompleteOptions);
 
     let doc = this.hud.document;
     this.completeNode = doc.querySelector(".jsterm-complete-node");
     this.inputNode = doc.querySelector(".jsterm-input-node");
     this.inputNode.addEventListener("keypress", this._keyPress, false);
     this.inputNode.addEventListener("input", this._inputEventHandler, false);
     this.inputNode.addEventListener("keyup", this._inputEventHandler, false);
     this.inputNode.addEventListener("focus", this._focusEventHandler, false);
+    this.hud.window.addEventListener("blur", this._blurEventHandler, false);
 
     this.lastInputValue && this.setInputValue(this.lastInputValue);
   },
 
   /**
    * The JavaScript evaluation response handler.
    *
    * @private
@@ -3738,16 +3741,27 @@ JSTerm.prototype = {
       this.resizeInput();
       this.complete(this.COMPLETE_HINT_ONLY);
       this.lastInputValue = this.inputNode.value;
       this._inputChanged = true;
     }
   },
 
   /**
+   * The window "blur" event handler.
+   * @private
+   */
+  _blurEventHandler: function JST__blurEventHandler()
+  {
+    if (this.autocompletePopup) {
+      this.clearCompletion();
+    }
+  },
+
+  /**
    * The inputNode "keypress" event handler.
    *
    * @private
    * @param nsIDOMEvent aEvent
    */
   _keyPress: function JST__keyPress(aEvent)
   {
     let inputNode = this.inputNode;
@@ -4368,16 +4382,17 @@ JSTerm.prototype = {
     if (popup) {
       popup.parentNode.removeChild(popup);
     }
 
     this.inputNode.removeEventListener("keypress", this._keyPress, false);
     this.inputNode.removeEventListener("input", this._inputEventHandler, false);
     this.inputNode.removeEventListener("keyup", this._inputEventHandler, false);
     this.inputNode.removeEventListener("focus", this._focusEventHandler, false);
+    this.hud.window.removeEventListener("blur", this._blurEventHandler, false);
 
     this.hud = null;
   },
 };
 
 /**
  * Utils: a collection of globally used functions.
  */
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -80,16 +80,17 @@ var BrowserUI = {
 
   init: function() {
     // start the debugger now so we can use it on the startup code as well
     if (Services.prefs.getBoolPref(debugServerStateChanged)) {
       this.runDebugServer();
     }
     Services.prefs.addObserver(debugServerStateChanged, this, false);
     Services.prefs.addObserver(debugServerPortChanged, this, false);
+    Services.prefs.addObserver("app.crashreporter.autosubmit", this, false);
 
     Services.obs.addObserver(this, "handle-xul-text-link", false);
 
     // listen content messages
     messageManager.addMessageListener("DOMTitleChanged", this);
     messageManager.addMessageListener("DOMWillOpenModalDialog", this);
     messageManager.addMessageListener("DOMWindowClose", this);
 
@@ -259,69 +260,21 @@ var BrowserUI = {
     return Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime).lastRunCrashID;
   },
 
   startupCrashCheck: function startupCrashCheck() {
 #ifdef MOZ_CRASHREPORTER
     if (!CrashReporter.enabled) {
       return;
     }
-    let lastCrashID = this.lastCrashID;
-
-    if (!lastCrashID || !lastCrashID.length) {
-      return;
-    }
-
-    let shouldReport = Services.prefs.getBoolPref("app.crashreporter.autosubmit");
-    let didPrompt = Services.prefs.getBoolPref("app.crashreporter.prompted");
-
-    if (!shouldReport && !didPrompt) {
-      let crashBundle = Services.strings.createBundle("chrome://browser/locale/crashprompt.properties");
-      let title = crashBundle.GetStringFromName("crashprompt.dialog.title");
-      let acceptbutton = crashBundle.GetStringFromName("crashprompt.dialog.acceptbutton");
-      let refusebutton = crashBundle.GetStringFromName("crashprompt.dialog.refusebutton");
-      let bodyText = crashBundle.GetStringFromName("crashprompt.dialog.statement1");
 
-      let buttonPressed =
-            Services.prompt.confirmEx(
-                null,
-                title,
-                bodyText,
-                Ci.nsIPrompt.BUTTON_POS_0 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING
-              + Ci.nsIPrompt.BUTTON_POS_1 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING
-              + Ci.nsIPrompt.BUTTON_POS_1_DEFAULT,
-                acceptbutton,
-                refusebutton,
-                null,
-                null,
-                { value: false });
-
-      Services.prefs.setBoolPref("app.crashreporter.prompted", true);
+    // Ensure that CrashReporter state matches pref
+    CrashReporter.submitReports = Services.prefs.getBoolPref("app.crashreporter.autosubmit");
 
-      if (buttonPressed == 0) {
-        Services.prefs.setBoolPref('app.crashreporter.autosubmit', true);
-        BrowserUI.crashReportingPrefChanged(true);
-        shouldReport = true;
-      } else {
-        Services.prefs.setBoolPref('app.crashreporter.autosubmit', false);
-        BrowserUI.crashReportingPrefChanged(false);
-      }
-    }
-
-    // We've already prompted, return if the user doesn't want to report.
-    if (!shouldReport) {
-      return;
-    }
-
-    Util.dumpLn("Submitting last crash id:", lastCrashID);
-    try {
-      this.CrashSubmit.submit(lastCrashID);
-    } catch (ex) {
-      Util.dumpLn(ex);
-    }
+    BrowserUI.submitLastCrashReportOrShowPrompt();
 #endif
   },
 
 
   /*********************************
    * Navigation
    */
 
@@ -627,21 +580,58 @@ var BrowserUI = {
               this.runDebugServer();
             } else {
               this.stopDebugServer();
             }
             break;
           case debugServerPortChanged:
             this.changeDebugPort(Services.prefs.getIntPref(aData));
             break;
+          case "app.crashreporter.autosubmit":
+#ifdef MOZ_CRASHREPORTER
+            CrashReporter.submitReports = Services.prefs.getBoolPref(aData);
+
+            // The user explicitly set the autosubmit option, so there is no
+            // need to prompt them about crash reporting in the future
+            Services.prefs.setBoolPref("app.crashreporter.prompted", true);
+
+            BrowserUI.submitLastCrashReportOrShowPrompt;
+#endif
+            break;
+
+
         }
         break;
     }
   },
 
+  submitLastCrashReportOrShowPrompt: function() {
+#ifdef MOZ_CRASHREPORTER
+    let lastCrashID = this.lastCrashID;
+    if (lastCrashID && lastCrashID.length) {
+      if (Services.prefs.getBoolPref("app.crashreporter.autosubmit")) {
+        Util.dumpLn("Submitting last crash id:", lastCrashID);
+        let params = {};
+        if (!Services.prefs.getBoolPref("app.crashreporter.submitURLs")) {
+          params['extraExtraKeyVals'] = { URL: '' };
+        }
+        try {
+          this.CrashSubmit.submit(lastCrashID, params);
+        } catch (ex) {
+          Util.dumpLn(ex);
+        }
+      } else if (!Services.prefs.getBoolPref("app.crashreporter.prompted")) {
+        BrowserUI.addAndShowTab("about:crashprompt", null);
+      }
+    }
+#endif
+  },
+
+
+
   /*********************************
    * Internal utils
    */
 
   _titleChanged: function(aBrowser) {
     let url = this.getDisplayURI(aBrowser);
 
     let tabCaption;
@@ -1103,20 +1093,16 @@ var BrowserUI = {
                           null,
                           { value: false });
 
     // Clicking 'Clear' will call onSanitize().
     if (buttonPressed === 0) {
       SanitizeUI.onSanitize();
     }
   },
-
-  crashReportingPrefChanged: function crashReportingPrefChanged(aState) {
-    CrashReporter.submitReports = aState;
-  }
 };
 
 var PanelUI = {
   get _panels() { return document.getElementById("panel-items"); },
 
   get isVisible() {
     return !Elements.panelUI.hidden;
   },
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -667,17 +667,23 @@ Desktop browser's sync prefs.
               <cssthrobber id="clearprivacythrobber"/>
             </hbox>
             <description id="clear-notification-done">&clearPrivateData.done;</description>
           </deck>
         </hbox>
       </settings>
       <setting pref="signon.rememberSignons" title="&optionsHeader.privacy.passwords.label;" type="bool"/>
       <settings id="prefs-reporting" label="&optionsHeader.reporting.title;">
-        <setting pref="app.crashreporter.autosubmit" type="bool" title="&optionsHeader.reporting.crashes.label;" oncommand="BrowserUI.crashReportingPrefChanged(this.value);"/>
+        <setting pref="app.crashreporter.autosubmit"
+                 type="bool"
+                 title="&optionsHeader.reporting.crashes.label;" />
+        <setting pref="app.crashreporter.submitURLs"
+                 id="prefs-reporting-submitURLs"
+                 type="bool"
+                 title="&optionsHeader.reporting.crashes.submitURLs;" />
       </settings>
       <settings id="prefs-telemetry" label="&optionsHeader.telemetry.title;">
         <setting pref="toolkit.telemetry.enabled" type="bool" title="&optionsHeader.telemetry.label;"/>
       </settings>
       <settings id="prefs-dnt" label="&doNotTrack.title;">
         <setting id="prefs-dnt-value" pref="privacy.donottrackheader.value" type="radio" >
           <radiogroup id="prefs-dnt-options">
             <radio id="prefs-dnt-notrack" class="flyoutpanel-hack"
--- a/browser/metro/base/content/flyoutpanels/PrefsFlyoutPanel.js
+++ b/browser/metro/base/content/flyoutpanels/PrefsFlyoutPanel.js
@@ -39,16 +39,22 @@ let PrefsFlyoutPanel = {
       this._hasShown = true;
 
       Services.prefs.addObserver("privacy.donottrackheader.value",
                                  this._prefObserver,
                                  false);
       Services.prefs.addObserver("privacy.donottrackheader.enabled",
                                  this._prefObserver,
                                  false);
+      Services.prefs.addObserver("app.crashreporter.autosubmit",
+                                 this._prefObserver,
+                                 false);
+      Services.prefs.addObserver("app.crashreporter.submitURLs",
+                                 this._prefObserver,
+                                 false);
     }
 
     this._topmostElement.show();
   },
 
   _prefObserver: function(subject, topic, data) {
     let value = -1;
     try {
@@ -76,11 +82,34 @@ let PrefsFlyoutPanel = {
 
       case "privacy.donottrackheader.enabled":
         // If someone or something modifies this pref, we should update the
         // other pref as well so our UI doesn't give false information
         if (((1 == value) || (0 == value)) && !isEnabled) {
           Services.prefs.setIntPref('privacy.donottrackheader.value', -1);
         }
         break;
+
+      case "app.crashreporter.autosubmit":
+        let autosubmit = Services.prefs.getBoolPref("app.crashreporter.autosubmit");
+        if (!autosubmit) {
+          // If the user has selected not to submit crash reports, the UI
+          // should reflect also that URLs will not be included.
+          // TODO: Ideally we would grey out the text and the toggle for
+          // the "include URLs" pref, but the |setting| binding doesn't
+          // appear to support enabling/disabling. In the meantime, we just
+          // set the "include URLs" pref to false if the "send crash reports"
+          // pref has been set to false.
+          Services.prefs.setBoolPref('app.crashreporter.submitURLs', false);
+        }
+        break;
+
+      case "app.crashreporter.submitURLs":
+        let submitURLs = Services.prefs.getBoolPref("app.crashreporter.submitURLs");
+        if (submitURLs) {
+          // If the user has selected to submit URLs, they are implicitly also
+          // selecting to submit crash reports. Let's update the autosubmit pref
+          Services.prefs.setBoolPref('app.crashreporter.autosubmit', true);
+        }
+      break;
     }
   },
 };
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/components.manifest
+++ b/browser/metro/components/components.manifest
@@ -1,16 +1,19 @@
 # AboutRedirector.js
 component {433d2d75-5923-49b0-854d-f37267b03dc7} AboutRedirector.js
 contract @mozilla.org/network/protocol/about;1?what=start {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=empty {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=firefox {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=rights {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=certerror {433d2d75-5923-49b0-854d-f37267b03dc7}
 contract @mozilla.org/network/protocol/about;1?what=home {433d2d75-5923-49b0-854d-f37267b03dc7}
+#ifdef MOZ_CRASHREPORTER
+contract @mozilla.org/network/protocol/about;1?what=crashprompt {433d2d75-5923-49b0-854d-f37267b03dc7}
+#endif
 #ifdef MOZ_SAFE_BROWSING
 contract @mozilla.org/network/protocol/about;1?what=blocked {433d2d75-5923-49b0-854d-f37267b03dc7}
 #endif
 
 # DirectoryProvider.js
 component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js
 contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}
 category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1
new file mode 100644
--- /dev/null
+++ b/browser/metro/locales/en-US/chrome/crashprompt.dtd
@@ -0,0 +1,16 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!ENTITY crashprompt.title           "Would you like to send Mozilla crash reports?">
+<!ENTITY crashprompt.message         "We are sorry, &brandShortName; just recovered from a crash. Sending crash reports will help Mozilla make &brandShortName; more stable and secure. You can always change your preference in Settings/Options.">
+<!ENTITY crashprompt.detailsLink     "What's in a crash report?">
+<!ENTITY crashprompt.detailedMessage.firstParagraph "A crash report contains some details about the crash, your machine, as well as a snapshot of the state of your browser when it crashed.">
+<!ENTITY crashprompt.detailedMessage.secondParagraph "This may include things like open pages, text typed into forms and the content of open messages, recent browsing history, or geolocation shared with a site.">
+<!ENTITY crashprompt.detailedMessage.thirdParagraph "We use crash reports to try to fix problems and improve our products. We handle your information as we describe in our ">
+<!ENTITY crashprompt.privacyPolicyLink "&brandShortName; privacy policy">
+<!ENTITY crashprompt.includeURLLabel "Include the address of the page I was on">
+<!ENTITY crashprompt.acceptbutton    "Send reports">
+<!ENTITY crashprompt.refusebutton    "Don't send">
+<!ENTITY crashprompt.privacyPolicy.title "&brandShortName; privacy policy">
deleted file mode 100644
--- a/browser/metro/locales/en-US/chrome/crashprompt.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# LOCALIZATION NOTE (crashprompt.messagebody2): %1$S - short brand name, %2$S - vendor name
-crashprompt.messagebody2=We are sorry, %1$S just recovered from a crash. Sending crash reports will help %2$S make %1$S more stable and secure. You can always change your preference in Settings/Options.
-crashprompt.dialog.title=Would you like to send Mozilla crash reports?
-crashprompt.dialog.privacyLink=Privacy statement of crash-reporting feature
-crashprompt.dialog.acceptbutton=Send reports
-crashprompt.dialog.refusebutton=Don't send
-crashprompt.dialog.title2=Privacy statement of crash-reporting feature
-crashprompt.dialog.statement1=Firefox has a crash-reporting feature that sends a report to Mozilla when Firefox crashes. Mozilla uses information in crash reports to diagnose and correct problems in Firefox that cause crashes. Though this feature starts automatically after Firefox crashes, it does not send information to Mozilla until you explicitly authorize it to do so. By default, this feature sends a variety of Non-Personal Information to Mozilla, including the stack trace (a detailed description of which parts of the Firefox code were active at the time of the crash) and the type of computer you are using. Additional information is collected by the crash reporting feature. Which crash reporting feature is used and what additional information collected by Firefox depends on which version of Firefox you are using.
-crashprompt.dialog.statement2=For current versions of Firefox, “Firefox Crash Reporter” is Firefox’s crash reporting feature. With this feature, you may have the option to include Personal Information (including your email address), Potentially Personal Information (including your IP address and the URL of the site you were visiting when Firefox crashed), and a comment. Firefox Crash Reporter also sends a list of all add-ons that you were using at the time of the crash, the time since (i) the last crash, (ii) the last install, and (iii) the start-up of the program. Mozilla only makes Non-Personal Information (i.e., generic information about your computer, the stack trace, and any comment given by the user) available in the public reports available online at http://crash-stats.mozilla.com/.
--- a/browser/metro/locales/en-US/chrome/preferences.dtd
+++ b/browser/metro/locales/en-US/chrome/preferences.dtd
@@ -30,10 +30,11 @@
 <!ENTITY doNotTrack.title                                        "Do Not Track">
 <!ENTITY doNotTrack.options.doNotTrack                           "Tell websites that I do not want to be tracked">
 <!ENTITY doNotTrack.options.doTrack                              "Tell websites that I want to be tracked">
 <!ENTITY doNotTrack.options.default                              "Do not tell websites anything about my tracking preferences">
 <!ENTITY doNotTrack.learnMoreLink                                "Learn more&#x2026;">
 
 <!ENTITY optionsHeader.reporting.title                           "Crash Reporter">
 <!ENTITY optionsHeader.reporting.crashes.label                   "&brandShortName; submits crash reports to help Mozilla make your browser more stable and secure">
+<!ENTITY optionsHeader.reporting.crashes.submitURLs             "Include the address of the page I was on">
 <!ENTITY optionsHeader.telemetry.title                            "Telemetry">
 <!ENTITY optionsHeader.telemetry.label                            "Shares usage statistics about &brandShortName; with Mozilla to help us make your browser better">
--- a/browser/metro/locales/jar.mn
+++ b/browser/metro/locales/jar.mn
@@ -17,17 +17,19 @@
   locale/browser/preferences.dtd          (%chrome/preferences.dtd)
   locale/browser/aboutPanel.dtd           (%chrome/aboutPanel.dtd)
   locale/browser/searchPanel.dtd          (%chrome/searchPanel.dtd)
   locale/browser/checkbox.dtd             (%chrome/checkbox.dtd)
   locale/browser/sync.dtd                 (%chrome/sync.dtd)
   locale/browser/sync.properties          (%chrome/sync.properties)
   locale/browser/passwordmgr.properties   (%chrome/passwordmgr.properties)
   locale/browser/phishing.dtd             (%chrome/phishing.dtd)
-  locale/browser/crashprompt.properties   (%chrome/crashprompt.properties)
+#ifdef MOZ_CRASHREPORTER
+  locale/browser/crashprompt.dtd          (%chrome/crashprompt.dtd)
+#endif
   locale/browser/aboutAddons.dtd          (%chrome/aboutAddons.dtd)
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %locale/browser/
   locale/browser/bookmarks.json           (bookmarks.json)
 
 #
 # Browser jar resources
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -13,19 +13,21 @@ pref("devtools.chrome.enabled", true);
 #else
 pref("devtools.errorconsole.enabled", false);
 pref("devtools.chrome.enabled", false);
 #endif
 
 // Automatically submit crash reports
 #ifdef RELEASE_BUILD
 pref("app.crashreporter.autosubmit", false);
+pref("app.crashreporter.submitURLs", false);
 #else
 // For Nightly and Aurora we turn this on by default
 pref("app.crashreporter.autosubmit", true);
+pref("app.crashreporter.submitURLs", false);
 #endif
 // Has the user been prompted about crash reporting?
 pref("app.crashreporter.prompted", false);
 
 // Debug prefs, see input.js
 pref("metro.debug.colorizeInputOverlay", false);
 pref("metro.debug.selection.displayRanges", false);
 pref("metro.debug.selection.dumpRanges", false);
new file mode 100644
--- /dev/null
+++ b/browser/metro/theme/crashprompt.css
@@ -0,0 +1,134 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+%filter substitution
+%include defines.inc
+
+body {
+  background-color: #F4F4F4;
+  margin-left: 16%;
+  margin-right: 23%;
+  margin-top: 115px;
+}
+
+a {
+  text-decoration: none;
+}
+
+#detailsLink {
+  color: black;
+  display: table-row;
+}
+
+#detailsGrid {
+  display: table;
+}
+
+#detailsContainer {
+  display: table-row;
+}
+
+#detailsLinkLabel {
+  display: table-cell;
+}
+
+.detailsSpacer {
+  display: table-cell;
+  padding-right: 8px;
+}
+
+#crashpromptDetailedMessage {
+  font-size: @metro_font_snormal@;
+  display: table-cell;
+}
+
+#crashPromptTitle {
+  color: #737980;
+  font-weight: normal;
+}
+
+.hidden {
+  display: none !important;
+}
+
+#crashPromptButtonContainer {
+  margin-top: 40px;
+}
+
+#lightboxBackdrop {
+  position: fixed;
+  top: 0%;
+  left: 0%;
+  width: 100%;
+  height: 100%;
+  z-index: 1001;
+  background-color: rgba(0,0,0,0.7);
+}
+
+#lightbox {
+  position: fixed;
+  top: 20%;
+  left: 15%;
+  width: 70%;
+  height: 60%;
+  padding-bottom: 1%;
+  padding-top: 1%;
+  background-color: white;
+  z-index: 1002;
+}
+
+#privacyPolicyTitle {
+  margin-left: 10%;
+}
+
+#privacyPolicyBody {
+  margin-left: 10%;
+  margin-right: 10%;
+  padding-right: 20px;
+  max-height: 75%;
+  overflow: auto;
+}
+
+#lightboxCloseButton {
+  background-image: -moz-image-rect(url("chrome://browser/skin/images/infobar-close.png"), 0, 40, 40, 0);
+  width: 40px;
+  height: 40px;
+  float: right;
+  margin-right: 30px;
+  margin-top: 10px;
+}
+
+button {
+  color: #FFFFFF;
+}
+
+button:not([disabled]):hover {
+  color: #FFFFFF;
+}
+
+#refuseButton {
+  background: #847F7C;
+}
+
+#refuseButton:hover {
+  background: #B0B0B0;
+}
+
+hr {
+  border: 1px solid #C7C7C7;
+}
+
+#privacyPolicyLink {
+  color: #649FEF;
+}
+
+.button-default {
+  background: #649FEF;
+  border-color: #9DCFF2;
+}
+
+.button-default:not([disabled]):hover {
+  background: #2990D9;
+  border-color: #8DBAD9;
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..efff2fd985b133fccd735534b338307b6a266f43
GIT binary patch
literal 696
zc$@*a0!RIcP)<h;3K|Lk000e1NJLTq000vJ000mO1^@s6^u$DB0007iNkl<ZI1z=E
zO=}ZD7{{M^*==5`wFrWONRQH^;Ms5Bsb2gpMy(!1#EW1l6g*q>BzP1LUc`ckK?{`z
z8`GpA+4tF<cW0BPM%s|)vNO-@??2D~+1WvG|3d|s=bf)Ddj=FWpsA%8fkFso1+^9_
zW0CMQ@v)3qAfYBD1-L}(HK+?hs#HT20rfqDiNzYv(n5w91x6zugyZshZ8^_akkO?=
zdn?zkZ4iua&w}UWwK4Daf9Si|R#J2{JbGxY4d+2;tvfCr1X06Q2*MbuW|+wV%Zn#x
zy*w|+3jQn!2Ypm%i$n00Yqw}09==mbeVPYPuPd!Tw>zD;0<4HCM7k!|6qO+-1hU>B
zdhE*1rh!>wYk~EL(IWzgQCKKS9VgaOaa!jp$1F?Z{j4Zn%o3Q+v!d9Dhw+{gD2r3Y
zOC18YRsehql3WD>5+z~&>%!8xn;7HE)A00IYyGW#@cV((IAVS!1^>GM_@c3GGz>IB
z15n`sWt5m?7zXRkhAaJaxIFI%(YjSMp@y-NEBR7qd@#4y%-F8tW!b?X-DP>cd0Gk}
z7kn#9<EEl{mdJ|Ru%%$c1B}GnZWiF9fyG^!Y3@EUq}^WUmDZ*?EohAW*=hgT(g@Nl
z$2lxgS#o3kSgZt2>{c)GvV3cB6a>LNw~On?C{!2*4FD9<j57BlIeX2Kz2o(9Ws}Lc
z+?FcK*I?5yioXgjUY6@z$jxCt-YN6RLYG74pV*u5<DMPQ{@JB6Wl65NO7^<F=k9R6
z**oYx)mp}7-dzp?uH4WQdlNC_4(Vh(dLkFF_8P<LDuGmaKqOs4K8~z#x09zveRB3-
e`;P0?|L|YQWuGX^4JH!+0000<MNUMnLSTZ@OGk46
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..790791bb8a6e03f433970e4afc3e0bf6da38c8ee
GIT binary patch
literal 1839
zc$@(*2hjM5P)<h;3K|Lk000e1NJLTq001Tc001Be1^@s6eq;)>000K|Nkl<ZSP8vY
zOOG5^6+ZV-)eq04Z4EXgwgquEk@6E*kX3#G5fVuJ0R90&f+5IG*klLF1~%~q8v#O5
zA|;C?8$`s0Ac2X5V#%JKvHMxoU0q%G&3CJ(I^&sfVrD#4t8`1%_n!0J@4M&RQ<u^9
zt(52@C)&h4$9;wOs-*~R!2&ILP@GXqFv1lyo=<VH)Q5Vwa)GtZ1xD7h%7wh$aYWh^
zEpg9r54~K2DmO%rr_#4`VR=ggQn(XNEyIIsv$kc7Foj_CiNG<L)}SgG0Lb9E@{F8k
zJaL=k9j~0r+ZF&fZ3GWsV5+QYP_7*kPa86%PNpPf5K;ukM6eaN4r~d4lgSA=(?l)=
zNglvb&xCXgKb>+LFx}7&wpVp2_n1I80xJ@BenSXJX@^lr6Okk?RH+9r3QQ(ah`<xD
z=Qw!)<FI>@%gaD%WJ`GFy|V(a0=FF^%Y87GQY|*PId98FMsrMnx42Wec>cv}Z*tDR
zxAJ-EB(6_A#)t6kf9~A*A-QM40xPhN1lt=Aei1?#o!a^d4M5I(FdzS<Ue~~cD8zzB
z<mIz6Un;$QLc6CPrj))h*)7J6Ax@p%DufMZJN=yxtaTqWfBwH(=lx&q?d<$%?GKm`
zkuj$rza%kQ68~^Cy#YyQ>rQ9<5CWAChxu#F;wfT4ev6nmbpZ^?jUgCgUMUu{QMpua
zo-v-a`K79UH7|-jlcM$?lWxR>HMaD#W$L&wPLr^VnB9B0_l|Y`uj|og^x%B>ba!v>
zcVwMY5_wH0h{n|p0AO350<y459Z3V!!eXN^!^z~0Nb?zCBG}f$>GZla+;YhcRI@x)
zE-T~qwB-S;^yD|aXjP7v!fMG3vt$;<e5b6+x6TS!rK;Z++3YS$gN{C<Zvj%sD^KG)
zkaBk!Xk+3s%B)B^@K;7)M|EDR-3Oyv&bm*ZHaJA#XZQOL-nJ+agd$!0gzt<}8?lhO
z1jeSH*Sk68EjPm-T#?bB8Psa-Ne!ntqLkEA1ZOAH?1pe|$$g@98^GW*`ldXGj6)wW
zaeC=r$YU2h+00W>I@toP0A^&WtnjLs%|2gh^_!;!tWx@BHk;pxwxcrKE=}|qeb*Qw
z7GM(e(T7AYe~T!aG1|(hqGzasDk6Brr4ii9z~_1K*@fOS-)5YB?KA)(;oY6x-Je<)
zO4J5R69lBJ^h{Tl&Rw*WL8E*9K)?P`;E*ZMHtw~g`4^H#nn8D@eVk?2L9ICjFgM9(
zuLoro+<K**GF+8r?y=ypvWfmyOBmUTnCiWf2$jrnl7ePIm5#LM+3erCQa4Wu413}4
zvn>C+6__ZN32G=Ug0$okdr(5!{#m_6vIs9;sI(ZpdYjXQYn-0j5>)n+O_8Lo)oUff
zbWmHK<D6~2@Xf1l3(lYaUjTX^`gi;N9~f<?QqV$Iu9Ci0&5=^6+cLB}cC})+Uq$5u
z^>FPoh6*FTh^*C;f>zKQX^wQq5RLQ;W6SBRxQ=m%o+rEDC1l0?4P>i12lG)vkuXTO
zu+%MJ=TQk8t7~BOi%8=^NkMZRrBTv4N)t7mPXD2`xz!l>s@53u=ka*@iE}8fV$PzD
z78Rl}N;n<>*eVJe2^)KBDc3KKNU9)d{)nU~x6fa@dif^f{Ax4qxE8t$?%lut{mS^E
zAYK}2mU8js5;k@oWejhRND8`ZNlDo{k{~H5J<sOl>rtl0&~YuQhF>~IlPDp=S))Wa
zAR)HGxNK^Lj($cQ0aiOWC~2e{v`6}D8KR`AsK%qgC)(OS90w@AL$UJSa5VZD^VpXV
zz}6D>AmLyI3cliX*v=6tn+`{#G)jsOyB2FD)^zaY)$VrM=dWD3d>ehAZN|kGQ8?Lq
zF!*j!6ax`Nr30ccv1(p!VDp(+T44K$Y#oTggX81#@r>2I_H$burJ%i*bg_z3i3o+m
zQkT<3S-y7I=(18bilurez^smaz1Q<4l5>p09@u(rz8_>qC2VXx7FbR1T2jzHhQa+P
zjeA_}kYu&H^Wa0&d4FsSI_KZ--`{;lkgHIl)cfH&kwzO)Li*UpD6D-n0}r*2m9pt~
zL{chcuX4%itGy+?DU+?<g$rN*#@727lME->E5qSvz>_Z4B<cXHtK83!vo0T-NE<(g
z+s88YMaO+fBmFTMqBP0d!r)V13x#?rB-ON>4wlCJ82N2`oQ?YNouER;KC$m=FT`?q
z0tp+xU&&bPShKeMb0GW<CUS`b9E#0zoHn}R;K@*J6el@6bo}ZBX9p8$t=3g7;meQ^
z8EJppX+Rv;J`u1ua4qRe7Xfexfcr;RIQt-u<EyS*t>hACBq-HsBcAB2j+E-`{?B3f
dvM4+Z?!T1d0iF!w!7Bg&002ovPDHLkV1j@2edYiF
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..07444ca41abf6fb663206e984202d5c99128f8e7
GIT binary patch
literal 744
zc$@*~0vG*>P)<h;3K|Lk000e1NJLTq000mG000vR1^@s6nP-j900087Nkl<ZI1!bU
zy-yTD7{;IZ*xlQ`-CH?DLQny*P)V#zOsp*Y7w9Zaj314St*4eoDJ_)PV4)=XC#Z>)
ziH!*=iNOyL6+!Q~z1`cHof+p4!y%Hun@o0QlK1yK?`GbYp!T$c)Nx{3;ikk0h0t}l
z1+Zur?0T+ItC>#N^QNGk<S>Z*;Uj~S@vhQHn|pEKkf?&a0g|c*9OW2$L<s@elx;6a
zr8Zp^oJ6C8`#&0(MwaHc=DsdGVIXNCNUP<RGJp%<<A2>yCA8xdh2NoDkvIrS*GC2i
z?}br(l)9$JM_P&U#qu9FW1HMi<4UGM+-`fEvlD&2)rSN9{bxPsJ(LBz%3&TM%Y5wH
z=c4wEk!!GJ5yJTC^A#67s`kX^x*@h^$94G0IF3&$YG!h&6XtQ^n=(Y(>bc86?<r6e
zgcvB=o!q@=j2+us-Th#HU*9FS(7n`?aTubElzA8(Wm_h8L#!Jw@cj!1hlU<lr^i`A
zqmZQ~B!#mrz+V_;^l(p4b@IT-z~#d8%V3i42+#qo{u}OGJUrAt9ut@Hzx3^X?R%6$
z%DeC9zTa#cp?3t(O24Z&>Q{gMSb4>jY(Y82wg6T&&qO}4F#l!37ExE+B#8*x2$kgE
z#t_Z*RwXE<UN6<_*XzyZ8^vkDTRcsqSWU+CwBn@DMnbE90ai6lb8+X}m(P=?;7!A`
zR+iBWqA3vP5;&zXHEK8!j<RpwHp_YxO1)fKZroUDHr^Mosw&M|?UGgH60@@k<73(i
z7+f{r%%#$HqwP4@8`DU${Pz69!sAXiYTJ#O>u2yP0^V9TPHmhG1Xo@}oq2F*c<BD}
zucg|LBwwTif=iRHBt?#JMq(NYQ){a0Mpt(nfs{$w#D}+^&U7YHse2-Yor(0u*1A!(
aoADb$woK3=hEs$90000<MNUMnLSTZY25uYx
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a3f2f27940b375b8f0dc0e61b08d04b28698602a
GIT binary patch
literal 1976
zc$@*a2S@mcP)<h;3K|Lk000e1NJLTq001BW001Tk1^@s6r6ZZ?000MnNkl<ZSP8vY
zOK&7s6+ZV>RsC|?Za=U+o{T3TjqnDs10hXVffP0fg#}B5*yIlofrLSt2m}J85fT!~
z2*Cgg15y@5f(-)_8^Qu1O@Pfr3AQuSPTT!*SKqoXzT0k3dM0DXW5&5sb(dXTb-wRA
zk2>cv`s&{mE%yXbjT7}a4VD;{1*5#fD6WzXxaNs+91Tyce+AMDp7!r}T7Nyz#y4@D
zQNSAg{xLt~edB|wm@W(9L<eA|S>SvGfRPM1@tlNj5459n#ZzO?(K4*kRZq`t23p?;
zjGYYxSV!ho)_EbV4(jPESAI4%t5zIWSv)MoLuaOtX|3E(r>kBOElBEI5Y);A&0OYm
z`=g9DHX;Tb_<W27<0W1nmW9iLa-1iE^Up0#&)r(PwERV>TuE79QY15179%hb$P`gR
z^?3y~pkPDLIzhFo9BS}GAg2cb015Y5Av`&msub+tQO2fAS@QkMYilo8ry6UHWLXAR
zjzCmmOO`}d)xESXSwe$t+;dqzsK&@6r&2>sQ=gk66(Ql$kxnMnlbnC*Lrb&2n;vG@
zx3};8*1BMPA`7LBD7X?q<))?;?@`@Qsu(q<5F2;kf?J4<-BZN|_fQ}tMkG)mI2tK1
z2?C*2DP^UvtzBCE>GW*vBf?R|vQQqmhiaKi>*@cah76SRK#B+JL|{rz8lOBDLVRqo
zF?(~WT6t+}`|V!=h_oV?N+?lOpoSf*k&{JijMXT;uNpk)m~MuLlyiwBoHY9IIYKKf
zXX*1FzI5^Bsm9DYVzy$Th_n)cTFWd?vua6=V?#sD9I=6HiJ20E^e|f=hz&`pU_oXq
z1jhqsCjvME#09@H*O<LARjvNw&7HSz2&07+N-E_FLqUYB#A`b6UR0y81>=`2vJHjo
z7CjsrxJZ;)a4Q_n+34>hLnnPhHkGEym!H14`t$n2{Kq*D6-P-~3RRL8nNrb*Y785S
z2FI%*)F+>>A^?(rlw!fBjoF#6oMianYbcmAaLco`a;4TE=C3oPiVJ~DQVazQ>Lx5x
z1775~6CqEH8JxsqyG=Cz+9YE|Z~=&&X^A9NUs_vPxj8#G_X)0K*^z%&4I|Sk0_L!a
z4p2p7xnJP4K0gMKp2_HYZh{&YX6jSdpITXZp`=tp1OSn)lrVvG6{#hj)Q3({y)KSG
zoZ9E_v3ATqAR$YV<nybm%fFtRYg`pvmK<{#YJBRTeJuIYR7esP*lzMC4&VqEyt!Df
zRepGJb@f}-smi=_JWYVjQ;Le~Bvs3BL+$(k905tBe0F(p@i(hW3!m{Q-T}rvN`HNb
zH!1p$b5r05K>SAKX_U*^4=%4h^`p6JwiqgcdzPd5QIlCPdCU2nOCdfxzqs&+wZ(-m
z!~#fV`N0A>qQwH;SN_<S&(V*c<hOP1k9+U5zR}WpOK691u;Bv%fPS@==la@KbLUm-
zWX=okdBD4&JUD*<#=2LV@9ciBe^4|9F~dk$p5(4Wb(DpgKQ{$@pnJW8gYWL{Hvi&`
zABsfi!i7SRwgMoiKX6p-J$V2a4uUD{D?815*PU_u&RdQ2hjFiw_=OlU)6No0u7fwz
z!We)?s67EKd<a|ZZtpv-z5TyAk5)&(s1-#KTp_eaKRq_y={UR(;DY9)0*08UGfg*u
z@3by9x0=nLgrewq7qk#)U7arQDnQ}SEt(TUePvOCVVuCe>FM@v>;gO{p9CqP(Hf#s
zKIU@Xzp>x#U)$f`|K}lV0&7+j(iFlfBZkg&eAQC9@2Cai-Im8R20PDq+N8rQ7|IY(
zrNBP`+36OZV6@i1d~4_KOTyTJ2V(?SVGw8|)(715{DGxX4^qdL%weS48nLG3I8%N)
zj*|d@g22cGCX|N@@Z<Mi-uZvG+TCmIZs$K9z9x|l5kdvV1tSz2vqq$s@x0arj2jQK
zrp<TfNXb$p48gee-ZFI}0L;HS*TWlI+jnmY=W>rALl3A6W($wDT>4;oISjk#>-hSA
zG;1WveU_4;TV<p@lM;P{g;bAT&bfc=wmL8Nd)+r;kYO6sl1fG47$SUN&<fj*?}9ZZ
zhSoNGZN?+~b0PYAlJ%qo$*J(HKtG*uD}^_c7vp&#c97@!k9T(NZd!**mY|)9L5Atf
ztDX@WtkYRUcq}X{VEr0=Jr7^s!DtqJjgB8{lJ%qw*`}(mB^|sOs;E%Bn7rtm|7)|^
z`F3~E-vMJxEzEFdY=P7ou^x_D6Y^R&$E=ZJ>(89XdeTOW9nXh}3z@bWu^2A`aJw+}
z#W(-^)+@}0ATTm%1{aZW*hVliai*Kr#^v<!Sw93I_npi->=B@KK~L}e?{~YcukYPE
z*rPc1t5_$j7X#*aVjOC^Z4*jqXV;N!`$Jzx)_(?TY}ZUg_@trZ&at!cr);!KGTNfa
zK^5<d6k9a<V5COOk{GBlR@#FS+I{V*iY*%2Ma_u5j$?TA^~TuO$C-Nc|KWo8AS?*b
zCR8Z5gok5b^+=A<iknoibRwn7DK@DPT-~@yh5OFP`tZ4jpZ^0oW;qB`k8FDY0000<
KMNUMnLSTZQWUfd6
--- a/browser/metro/theme/jar.mn
+++ b/browser/metro/theme/jar.mn
@@ -5,16 +5,19 @@
 
 
 chrome.jar:
 % skin browser classic/1.0 %skin/
   skin/aboutPage.css                        (aboutPage.css)
   skin/aboutAddons.css                      (aboutAddons.css)
   skin/about.css                            (about.css)
 * skin/flyoutpanel.css                      (flyoutpanel.css)
+#ifdef MOZ_CRASHREPORTER
+* skin/crashprompt.css                      (crashprompt.css)
+#endif
 * skin/circularprogress.css                 (circularprogress.css)
 * skin/cssthrobber.css                      (cssthrobber.css)
 * skin/browser.css                          (browser.css)
 * skin/content.css                          (content.css)
   skin/config.css                           (config.css)
 * skin/platform.css                         (platform.css)
 * skin/tiles.css                            (tiles.css)
   skin/touchcontrols.css                    (touchcontrols.css)
@@ -98,16 +101,18 @@ chrome.jar:
   skin/images/arrowup-16.png                (images/arrowup-16.png)
   skin/images/arrowdown-16.png              (images/arrowdown-16.png)
   skin/images/arrowleftdark-16.png          (images/arrowleftdark-16.png)
   skin/images/arrowrightdark-16.png         (images/arrowrightdark-16.png)
   skin/images/arrowupdark-16.png            (images/arrowupdark-16.png)
   skin/images/arrowdowndark-16.png          (images/arrowdowndark-16.png)
   skin/images/popup-bg-hdpi.png             (images/popup-bg-hdpi.png)
   skin/images/popup-selected-item-hdpi.png  (images/popup-selected-item-hdpi.png)
+  skin/images/arrowbox-horiz-blue-filled.png (images/arrowbox-horiz-blue-filled.png)
+  skin/images/arrowbox-down-blue-filled.png (images/arrowbox-down-blue-filled.png)
   skin/images/favicon-default-32.png        (images/favicon-default-32.png)
   skin/images/errorpage-warning.png         (images/errorpage-warning.png)
   skin/images/errorpage-warning.png         (images/errorpage-warning.png)
   skin/images/errorpage-larry-white.png     (images/errorpage-larry-white.png)
   skin/images/errorpage-larry-black.png     (images/errorpage-larry-black.png)
   skin/images/alert-downloads-30.png        (images/alert-downloads-30.png)
   skin/images/search-glass-30.png           (images/search-glass-30.png)
   skin/images/play-hdpi.png                 (images/play-hdpi.png)
--- a/browser/themes/shared/devtools/toolbars.inc.css
+++ b/browser/themes/shared/devtools/toolbars.inc.css
@@ -457,23 +457,21 @@
 #command-button-splitconsole[checked=true] {
   -moz-image-region: rect(0px, 64px, 16px, 48px);
 }
 
 /* Tabs */
 
 .devtools-tabbar {
   -moz-appearance: none;
-  background-image: url("background-noise-toolbar.png"),
-                    linear-gradient(#303840, #2d3640);
-  border-color: #060a0d;
+  background: #252c33;
+  border-color: #434a51;
   border-style: solid;
   border-width: 1px 0;
-  box-shadow: 0 1px 0 hsla(204,45%,98%,.05) inset,
-              0 -1px 0 hsla(206,37%,4%,.1) inset;
+  box-shadow: 0 -2px 0 rgba(0,0,0,.25) inset;
   min-height: 32px;
   padding: 0;
 }
 
 #toolbox-tabs {
   margin: 0;
 }
 
@@ -485,25 +483,28 @@
 .devtools-tab {
   -moz-appearance: none;
   min-width: 32px;
   min-height: 32px;
   max-width: 127px;
   color: #b6babf;
   margin: 0;
   padding: 0;
-  background-image: linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1));
-  background-size: 1px 100%;
-  background-repeat: no-repeat;
-  background-position: left, right;
-  border-right: 1px solid hsla(206,37%,4%,.45);
+  border-left: 1px solid #42484f;
   -moz-box-align: center;
 }
 
+.devtools-tab:first-child {
+  border-left-width: 0;
+}
+
+.devtools-tab:last-child {
+  border-right: 1px solid #5a6169;
+}
+
 .devtools-tab > image {
   border: none;
   -moz-margin-end: 0;
   -moz-margin-start: 4px;
   opacity: 0.6;
   max-height: 16px;
   width: 16px; /* Prevents collapse during theme switching */
 }
@@ -521,72 +522,47 @@
 }
 
 .devtools-tab:active > image,
 .devtools-tab[selected=true] > label {
   opacity: 1;
 }
 
 .devtools-tab:hover {
-  background-image: linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(206,37%,4%,.1), hsla(206,37%,4%,.2));
-  background-size: 1px 100%,
-                   1px 100%,
-                   100%;
-  background-repeat: no-repeat,
-                     no-repeat,
-                     repeat-x;
-  background-position: left, right;
+  background-color: hsla(206,37%,4%,.2);
   color: #ced3d9;
 }
+
 .devtools-tab:hover:active {
-  background-color: hsla(206,37%,4%,.2);
+  background-color: hsla(206,37%,4%,.4);
   color: #f5f7fa;
 }
 
-.devtools-tab[selected=true] {
+#toolbox-tabs .devtools-tab[selected=true] {
   color: #f5f7fa;
-  background-image: radial-gradient(farthest-corner at center top, #9fdfff, hsla(200,100%,70%,.3)),
-                    radial-gradient(farthest-side at center top, hsla(200,100%,70%,.4), hsla(200,100%,70%,0)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(204,45%,98%,.02), hsla(204,45%,98%,.04)),
-                    linear-gradient(hsla(206,37%,4%,.2), hsla(206,37%,4%,.3));
-  background-size: 100% 1px,
-                   100% 5px,
-                   1px 100%,
-                   1px 100%,
-                   100%;
-  background-repeat: no-repeat,
-                     no-repeat,
-                     no-repeat,
-                     no-repeat,
-                     repeat-x;
-  background-position: top right, top left, left, right;
-  box-shadow: 1px -1px 0 hsla(206,37%,4%,.2) inset;
+  background-color: #1a4666;
+  border-width: 0;
+  box-shadow: 0 2px 0 #d7f1ff inset,
+              0 8px 3px -5px #2b82bf inset,
+              0 -2px 0 rgba(0,0,0,.2) inset;
+}
+
+.devtools-tab[selected=true]:not(:first-child) {
+  padding-left: 1px;
+}
+
+.devtools-tab[selected=true] + .devtools-tab {
+  border-left-width: 0;
+  padding-left: 1px;
 }
 
 .devtools-tab:not([selected=true]).highlighted {
   color: #f5f7fa;
-  background-image: radial-gradient(farthest-corner at center top, #c0ff40, hsla(80,100%,63%,.5) 70%, hsla(80,100%,63%,.3) 97%),
-                    radial-gradient(farthest-side at center top, hsla(80,100%,35%,.5), hsla(80,100%,35%,0)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(204,45%,98%,.05), hsla(204,45%,98%,.1)),
-                    linear-gradient(hsla(99,100%,14%,.2), hsla(99,100%,14%,.2));
-  background-size: 100% 1px,
-                   100% 5px,
-                   1px 100%,
-                   1px 100%,
-                   100%;
-  background-repeat: no-repeat,
-                     no-repeat,
-                     no-repeat,
-                     no-repeat,
-                     repeat-x;
-  background-position: top right, top left, left, right;
+  background-color: hsla(99,100%,14%,.2);
+  box-shadow: 0 2px 0 #7bc107 inset;
 }
 
 .devtools-tab:not(.highlighted) > .highlighted-icon,
 .devtools-tab[selected=true] > .highlighted-icon,
 .devtools-tab:not([selected=true]).highlighted > .default-icon {
   visibility: collapse;
 }
 
--- a/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/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;
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/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)
 {
@@ -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;
 }
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -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/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1558,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
@@ -1169,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);
@@ -1209,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/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -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_Boolean && rhs == MIRType_Boolean))
     {
-        compareType_ = Compare_Int32;
+        compareType_ = Compare_Int32MaybeCoerceBoth;
         return;
     }
 
     // Loose/relational cross-integer/boolean comparisons may be treated as Int32.
     if (!strictEq &&
         (lhs == MIRType_Int32 || lhs == MIRType_Boolean) &&
         (rhs == MIRType_Int32 || rhs == MIRType_Boolean))
     {
-        compareType_ = Compare_Int32;
+        compareType_ = Compare_Int32MaybeCoerceBoth;
         return;
     }
 
     // Numeric comparisons against a double coerce to double.
     if (IsNumberType(lhs) && IsNumberType(rhs)) {
         compareType_ = Compare_Double;
         return;
     }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2187,16 +2187,19 @@ class MCompare
         // String    compared to Boolean
         // Object    compared to Boolean
         // Value     compared to Boolean
         Compare_Boolean,
 
         // Int32   compared to Int32
         // Boolean compared to Boolean
         Compare_Int32,
+        Compare_Int32MaybeCoerceBoth,
+        Compare_Int32MaybeCoerceLHS,
+        Compare_Int32MaybeCoerceRHS,
 
         // Int32 compared as unsigneds
         Compare_UInt32,
 
         // Double compared to Double
         Compare_Double,
 
         Compare_DoubleMaybeCoerceLHS,
@@ -2253,16 +2256,22 @@ class MCompare
     bool tryFold(bool *result);
     bool evaluateConstantOperands(bool *result);
     MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
 
     void infer(BaselineInspector *inspector, jsbytecode *pc);
     CompareType compareType() const {
         return compareType_;
     }
+    bool isInt32Comparison() const {
+        return compareType() == Compare_Int32 ||
+               compareType() == Compare_Int32MaybeCoerceBoth ||
+               compareType() == Compare_Int32MaybeCoerceLHS ||
+               compareType() == Compare_Int32MaybeCoerceRHS;
+    }
     bool isDoubleComparison() const {
         return compareType() == Compare_Double ||
                compareType() == Compare_DoubleMaybeCoerceLHS ||
                compareType() == Compare_DoubleMaybeCoerceRHS;
     }
     bool isFloat32Comparison() const {
         return compareType() == Compare_Float32;
     }
@@ -3036,30 +3045,34 @@ class MAsmJSUnsignedToFloat32
 // Converts a primitive (either typed or untyped) to an int32. If the input is
 // not primitive at runtime, a bailout occurs. If the input cannot be converted
 // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
 class MToInt32
   : public MUnaryInstruction,
     public ToInt32Policy
 {
     bool canBeNegativeZero_;
-
-    MToInt32(MDefinition *def)
+    MacroAssembler::IntConversionInputKind conversion_;
+
+    MToInt32(MDefinition *def, MacroAssembler::IntConversionInputKind conversion)
       : MUnaryInstruction(def),
-        canBeNegativeZero_(true)
+        canBeNegativeZero_(true),
+        conversion_(conversion)
     {
         setResultType(MIRType_Int32);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(ToInt32)
-    static MToInt32 *New(TempAllocator &alloc, MDefinition *def)
-    {
-        return new(alloc) MToInt32(def);
+    static MToInt32 *New(TempAllocator &alloc, MDefinition *def,
+                         MacroAssembler::IntConversionInputKind conversion =
+                             MacroAssembler::IntConversion_Any)
+    {
+        return new(alloc) MToInt32(def, conversion);
     }
 
     MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers);
 
     // this only has backwards information flow.
     void analyzeEdgeCasesBackward();
 
     bool canBeNegativeZero() const {
@@ -3068,16 +3081,20 @@ class MToInt32
     void setCanBeNegativeZero(bool negativeZero) {
         canBeNegativeZero_ = negativeZero;
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
+    MacroAssembler::IntConversionInputKind conversion() const {
+        return conversion_;
+    }
+
     bool congruentTo(MDefinition *ins) const {
         return congruentIfOperandsEqual(ins);
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     void computeRange(TempAllocator &alloc);
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -136,17 +136,17 @@ ComparePolicy::adjustInputs(TempAllocato
 
     // Compare_Boolean specialization is done for "Anything === Bool"
     // If the LHS is boolean, we set the specialization to Compare_Int32.
     // This matches other comparisons of the form bool === bool and
     // generated code of Compare_Int32 is more efficient.
     if (compare->compareType() == MCompare::Compare_Boolean &&
         def->getOperand(0)->type() == MIRType_Boolean)
     {
-       compare->setCompareType(MCompare::Compare_Int32);
+       compare->setCompareType(MCompare::Compare_Int32MaybeCoerceBoth);
     }
 
     // Compare_Boolean specialization is done for "Anything === Bool"
     // As of previous line Anything can't be Boolean
     if (compare->compareType() == MCompare::Compare_Boolean) {
         // Unbox rhs that is definitely Boolean
         MDefinition *rhs = def->getOperand(1);
         if (rhs->type() != MIRType_Boolean) {
@@ -237,19 +237,38 @@ ComparePolicy::adjustInputs(TempAllocato
             if (in->type() == MIRType_Null ||
                 (in->type() == MIRType_Boolean && convert == MToFloat32::NumbersOnly))
             {
                 in = boxAt(alloc, def, in);
             }
             replace = MToFloat32::New(alloc, in, convert);
             break;
           }
-          case MIRType_Int32:
-            replace = MToInt32::New(alloc, in);
+          case MIRType_Int32: {
+            MacroAssembler::IntConversionInputKind convert = MacroAssembler::IntConversion_NumbersOnly;
+            if (compare->compareType() == MCompare::Compare_Int32MaybeCoerceBoth ||
+                (compare->compareType() == MCompare::Compare_Int32MaybeCoerceLHS && i == 0) ||
+                (compare->compareType() == MCompare::Compare_Int32MaybeCoerceRHS && i == 1))
+            {
+                convert = MacroAssembler::IntConversion_Any;
+            }
+            if (convert == MacroAssembler::IntConversion_NumbersOnly) {
+                if (in->type() != MIRType_Int32 && in->type() != MIRType_Value)
+                    in = boxAt(alloc, def, in);
+            } else {
+                if (in->type() == MIRType_Undefined ||
+                    in->type() == MIRType_String ||
+                    in->type() == MIRType_Object)
+                {
+                    in = boxAt(alloc, def, in);
+                }
+            }
+            replace = MToInt32::New(alloc, in, convert);
             break;
+          }
           case MIRType_Object:
             replace = MUnbox::New(alloc, in, MIRType_Object, MUnbox::Infallible);
             break;
           case MIRType_String:
             replace = MUnbox::New(alloc, in, MIRType_String, MUnbox::Infallible);
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Unknown compare specialization");
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1155,17 +1155,17 @@ class Assembler
         Below = CC,
         BelowOrEqual = LS,
         GreaterThan = GT,
         GreaterThanOrEqual = GE,
         LessThan = LT,
         LessThanOrEqual = LE,
         Overflow = VS,
         Signed = MI,
-        Unsigned = PL,
+        NotSigned = PL,
         Zero = EQ,
         NonZero = NE,
         Always  = AL,
 
         VFP_NotEqualOrUnordered = NE,
         VFP_Equal = EQ,
         VFP_Unordered = VS,
         VFP_NotUnordered = VC,
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1119,19 +1119,19 @@ CodeGeneratorARM::emitTableSwitchDispatc
     // unhandled case is the default case (both out of range high and out of range low)
     // I then insert a branch to default case into the extra slot, which ensures
     // we don't attempt to execute the address table.
     Label *defaultcase = mir->getDefault()->lir()->label();
 
     int32_t cases = mir->numCases();
     // Lower value with low value
     masm.ma_sub(index, Imm32(mir->low()), index, SetCond);
-    masm.ma_rsb(index, Imm32(cases - 1), index, SetCond, Assembler::Unsigned);
+    masm.ma_rsb(index, Imm32(cases - 1), index, SetCond, Assembler::NotSigned);
     AutoForbidPools afp(&masm);
-    masm.ma_ldr(DTRAddr(pc, DtrRegImmShift(index, LSL, 2)), pc, Offset, Assembler::Unsigned);
+    masm.ma_ldr(DTRAddr(pc, DtrRegImmShift(index, LSL, 2)), pc, Offset, Assembler::NotSigned);
     masm.ma_b(defaultcase);
 
     // To fill in the CodeLabels for the case entries, we need to first
     // generate the case entries (we don't yet know their offsets in the
     // instruction stream).
     OutOfLineTableSwitch *ool = new(alloc()) OutOfLineTableSwitch(alloc(), mir);
     for (int32_t i = 0; i < cases; i++) {
         CodeLabel cl;
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -953,17 +953,17 @@ MacroAssemblerARM::ma_mod_mask(Register 
 
     // Extract the bottom bits into lr.
     ma_and(Imm32(mask), ScratchRegister, secondScratchReg_);
     // Add those bits to the accumulator.
     ma_add(secondScratchReg_, dest, dest);
     // Do a trial subtraction, this is the same operation as cmp, but we store the dest
     ma_sub(dest, Imm32(mask), secondScratchReg_, SetCond);
     // If (sum - C) > 0, store sum - C back into sum, thus performing a modulus.
-    ma_mov(secondScratchReg_, dest, NoSetCond, Unsigned);
+    ma_mov(secondScratchReg_, dest, NoSetCond, NotSigned);
     // Get rid of the bits that we extracted before, and set the condition codes
     as_mov(ScratchRegister, lsr(ScratchRegister, shift), SetCond);
     // If the shift produced zero, finish, otherwise, continue in the loop.
     ma_b(&head, NonZero);
     // Check the hold to see if we need to negate the result.  Hold can only be 1 or -1,
     // so this will never set the 0 flag.
     ma_cmp(hold, Imm32(0));
     // If the hold was non-zero, negate the result to be in line with what JS wants
@@ -3879,17 +3879,17 @@ MacroAssemblerARMCompat::floor(FloatRegi
     // the result will still be a negative number
     ma_rsb(output, Imm32(0), output, SetCond);
     // Flip the negated input back to its original value.
     ma_vneg(input, input);
     // If the result looks non-negative, then this value didn't actually fit into
     // the int range, and special handling is required.
     // zero is also caught by this case, but floor of a negative number
     // should never be zero.
-    ma_b(bail, Unsigned);
+    ma_b(bail, NotSigned);
 
     bind(&fin);
 }
 
 void
 MacroAssemblerARMCompat::floorf(FloatRegister input, Register output, Label *bail)
 {
     Label handleZero;
@@ -3931,17 +3931,17 @@ MacroAssemblerARMCompat::floorf(FloatReg
     // the result will still be a negative number
     ma_rsb(output, Imm32(0), output, SetCond);
     // Flip the negated input back to its original value.
     ma_vneg_f32(input, input);
     // If the result looks non-negative, then this value didn't actually fit into
     // the int range, and special handling is required.
     // zero is also caught by this case, but floor of a negative number
     // should never be zero.
-    ma_b(bail, Unsigned);
+    ma_b(bail, NotSigned);
 
     bind(&fin);
 }
 
 CodeOffsetLabel
 MacroAssemblerARMCompat::toggledJump(Label *label)
 {
     // Emit a B that can be toggled to a CMP. See ToggleToJmp(), ToggleToCmp().
@@ -4018,17 +4018,17 @@ MacroAssemblerARMCompat::round(FloatRegi
     ma_sub(output, Imm32(1), output, NoSetCond, Equal);
     // Negate the output.  Since INT_MIN < -INT_MAX, even after adding 1,
     // the result will still be a negative number
     ma_rsb(output, Imm32(0), output, SetCond);
 
     // If the result looks non-negative, then this value didn't actually fit into
     // the int range, and special handling is required, or it was zero, which means
     // the result is actually -0.0 which also requires special handling.
-    ma_b(bail, Unsigned);
+    ma_b(bail, NotSigned);
 
     bind(&fin);
 }
 
 CodeOffsetJump
 MacroAssemblerARMCompat::jumpWithPatch(RepatchLabel *label, Condition cond)
 {
     ARMBuffer::PoolEntry pe;
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -450,17 +450,17 @@ JitRuntime::generateArgumentsRectifier(J
     // Push arguments, |nargs| + 1 times (to include |this|).
     {
         Label copyLoopTop;
         masm.bind(&copyLoopTop);
         masm.ma_dataTransferN(IsLoad, 64, true, r3, Imm32(-8), r4, PostIndex);
         masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex);
 
         masm.ma_sub(r8, Imm32(1), r8, SetCond);
-        masm.ma_b(&copyLoopTop, Assembler::Unsigned);
+        masm.ma_b(&copyLoopTop, Assembler::NotSigned);
     }
 
     // translate the framesize from values into bytes
     masm.ma_add(r6, Imm32(1), r6);
     masm.ma_lsl(Imm32(3), r6, r6);
 
     // Construct sizeDescriptor.
     masm.makeFrameDescriptor(r6, IonFrame_Rectifier);
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -1944,39 +1944,37 @@ nsLineLayout::VerticalAlignFrames(PerSpa
       nscoord revisedBaselineY = baselineY - offset;
       pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
       pfd->mVerticalAlign = VALIGN_OTHER;
     }
 
     // Update minY/maxY for frames that we just placed. Do not factor
     // text into the equation.
     if (pfd->mVerticalAlign == VALIGN_OTHER) {
-      // Text frames do not contribute to the min/max Y values for the
-      // line (instead their parent frame's font-size contributes).
+      // Text frames and bullets do not contribute to the min/max Y values for
+      // the line (instead their parent frame's font-size contributes).
       // XXXrbs -- relax this restriction because it causes text frames
       //           to jam together when 'font-size-adjust' is enabled
       //           and layout is using dynamic font heights (bug 20394)
       //        -- Note #1: With this code enabled and with the fact that we are not
       //           using Em[Ascent|Descent] as nsDimensions for text metrics in
       //           GFX mean that the discussion in bug 13072 cannot hold.
       //        -- Note #2: We still don't want empty-text frames to interfere.
       //           For example in quirks mode, avoiding empty text frames prevents
       //           "tall" lines around elements like <hr> since the rules of <hr>
       //           in quirks.css have pseudo text contents with LF in them.
-#if 0
-      if (!pfd->GetFlag(PFD_ISTEXTFRAME)) {
-#else
-      // Only consider non empty text frames when line-height=normal
       bool canUpdate = !pfd->GetFlag(PFD_ISTEXTFRAME);
-      if (!canUpdate && pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)) {
+      if ((!canUpdate && pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)) ||
+          (canUpdate && (pfd->GetFlag(PFD_ISBULLET) ||
+                         nsGkAtoms::bulletFrame == frame->GetType()))) {
+        // Only consider bullet / non-empty text frames when line-height:normal.
         canUpdate =
           frame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal;
       }
       if (canUpdate) {
-#endif
         nscoord yTop, yBottom;
         if (frameSpan) {
           // For spans that were are now placing, use their position
           // plus their already computed min-Y and max-Y values for
           // computing yTop and yBottom.
           yTop = pfd->mBounds.y + frameSpan->mMinY;
           yBottom = pfd->mBounds.y + frameSpan->mMaxY;
         }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/942017-ref.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+    <meta charset="utf-8">
+    <title>Bug 942017</title>
+
+    <style type="text/css">
+@font-face {
+  font-family: DejaVuSansMono;
+  src: url(../fonts/DejaVuSansMono.woff),url(DejaVuSansMono.woff);
+}
+
+  html,body {
+      color:black; background-color:white; font-size:24px; font-family:DejaVuSansMono; padding:20px; margin:0;
+  }
+
+  div {
+    float: left;
+    padding: 1em 2em;
+    outline: 2px solid black;
+    background: black;
+  }
+
+  div.a { line-height: 0.5em; }
+  div.b { line-height: 2em; }
+  div.i l { margin-left:2.84ch; }
+
+  l { display:block; outline:1px solid green; width:1ch; direction:rtl; white-space:nowrap; }
+  x { display:inline-block; width:2.84ch; height:1px; vertical-align:top; }
+    </style>
+</head>
+<body>
+        <div class="a">
+          <l>X<x></x></l>
+          <l>X<x></x></l>
+        </div>
+
+<br clear="all">
+
+        <div class="b">
+          <l>X<x></x></l>
+          <l>X<x></x></l>
+        </div>
+
+<br clear="all">
+
+        <div class="a i">
+          <l>X<x></x></l>
+          <l>X<x></x></l>
+        </div>
+
+<br clear="all">
+
+        <div class="b i">
+          <l>X<x></x></l>
+          <l>X<x></x></l>
+        </div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/942017.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+    <meta charset="utf-8">
+    <title>Bug 942017</title>
+
+    <style type="text/css">
+@font-face {
+  font-family: DejaVuSansMono;
+  src: url(../fonts/DejaVuSansMono.woff),url(DejaVuSansMono.woff);
+}
+
+  html,body {
+      color:black; background-color:white; font-size:24px; font-family:DejaVuSansMono; padding:20px; margin:0;
+  }
+
+  div {
+    float: left;
+    padding: 1em 2em;
+    outline: 2px solid black;
+    background: black;
+    list-style-type: decimal;
+  }
+
+  div.a { line-height: 0.5em; }
+  div.b { line-height: 2em; }
+  div.i { list-style-position: inside; }
+
+  li { outline:1px solid green; padding:0; margin:0; letter-spacing:0; }
+
+    </style>
+</head>
+<body>
+
+        <div class="a">
+          <li>X</li>
+          <li>X</li>
+        </div>
+
+<br clear="all">
+
+        <div class="b">
+          <li>X</li>
+          <li>X</li>
+        </div>
+
+<br clear="all">
+
+        <div class="a i">
+          <li>X</li>
+          <li>X</li>
+        </div>
+
+<br clear="all">
+
+        <div class="b i">
+          <li>X</li>
+          <li>X</li>
+        </div>
+
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1780,9 +1780,10 @@ fuzzy(1,10000) fuzzy-if(Android&&Android
 fuzzy-if(Android,8,400) == 906199-1.html 906199-1-ref.html
 == 921716-1.html 921716-1-ref.html
 fuzzy-if(cocoaWidget,1,40) == 928607-1.html 928607-1-ref.html
 == 931464-1.html 931464-1-ref.html
 == 931853.html 931853-ref.html
 == 931853-quirks.html 931853-quirks-ref.html
 == 936670-1.svg 936670-1-ref.svg
 == 941940-1.html 941940-1-ref.html
+== 942017.html 942017-ref.html
 fails-if(winWidget&&!d2d) == 942672-1.html 942672-1-ref.html
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -431,25 +431,31 @@ int nr_ice_component_initialize(struct n
       }
       cand=TAILQ_NEXT(cand,entry_comp);
     }
     _status=0;
  abort:
     return(_status);
   }
 
+static int nr_ice_any_peer_paired(nr_ice_candidate* cand) {
+  nr_ice_peer_ctx* pctx=STAILQ_FIRST(&cand->ctx->peers);
+  while(pctx && pctx->state == NR_ICE_PEER_STATE_UNPAIRED){
+    /* Is it worth actually looking through the check lists? Probably not. */
+    pctx=STAILQ_NEXT(pctx,entry);
+  }
+  return pctx != NULL;
+}
+
 /*
   Compare this newly initialized candidate against the other initialized
   candidates and discard the lower-priority one if they are redundant.
 
    This algorithm combined with the other algorithms, favors
    host > srflx > relay
-
-   This actually won't prune relayed in the very rare
-   case that relayed is the same. Not relevant in practice.
  */
 int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned)
   {
     nr_ice_candidate *c2, *tmp = NULL;
 
     *was_pruned = 0;
     c2 = TAILQ_FIRST(&comp->candidates);
     while(c2){
@@ -458,22 +464,23 @@ int nr_ice_component_maybe_prune_candida
          !nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) &&
          !nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
 
         if((c1->type == c2->type) ||
            (c1->type==HOST && c2->type == SERVER_REFLEXIVE) ||
            (c2->type==HOST && c1->type == SERVER_REFLEXIVE)){
 
           /*
-             These are redundant. Remove the lower pri one.
+             These are redundant. Remove the lower pri one, or if pairing has
+             already occurred, remove the newest one.
 
              Since this algorithmis run whenever a new candidate
              is initialized, there should at most one duplicate.
            */
-          if (c1->priority < c2->priority) {
+          if ((c1->priority <= c2->priority) || nr_ice_any_peer_paired(c2)) {
             tmp = c1;
             *was_pruned = 1;
           }
           else {
             tmp = c2;
           }
           break;
         }
--- a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
@@ -331,26 +331,29 @@ int nr_ice_peer_ctx_pair_candidates(nr_i
 
     r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): peer (%s) pairing candidates",pctx->ctx->label,pctx->label);
 
     if(STAILQ_EMPTY(&pctx->peer_streams)) {
         r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) received no media stream attributes",pctx->ctx->label,pctx->label);
         ABORT(R_FAILED);
     }
 
+    /* Set this first; if we fail partway through, we do not want to end
+     * up in UNPAIRED after creating some pairs. */
+    pctx->state = NR_ICE_PEER_STATE_PAIRED;
+
     stream=STAILQ_FIRST(&pctx->peer_streams);
     while(stream){
       if(r=nr_ice_media_stream_pair_candidates(pctx, stream->local_stream,
         stream))
         ABORT(r);
 
       stream=STAILQ_NEXT(stream,entry);
     }
 
-    pctx->state = NR_ICE_PEER_STATE_PAIRED;
 
     _status=0;
   abort:
     return(_status);
   }
 
 
 int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand)
--- a/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c
@@ -375,16 +375,38 @@ nr_turn_client_ctx_destroy(nr_turn_clien
     r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): destroy", ctx->label);
 
   /* Cancel frees the rest of our data */
   RFREE(ctx->label);
   ctx->label = 0;
 
   nr_turn_client_cancel(ctx);
 
+  RFREE(ctx->username);
+  ctx->username = 0;
+  r_data_destroy(&ctx->password);
+  RFREE(ctx->nonce);
+  ctx->nonce = 0;
+  RFREE(ctx->realm);
+  ctx->realm = 0;
+
+  /* Destroy the STUN client ctxs */
+  while (!STAILQ_EMPTY(&ctx->stun_ctxs)) {
+    nr_turn_stun_ctx *stun = STAILQ_FIRST(&ctx->stun_ctxs);
+    STAILQ_REMOVE_HEAD(&ctx->stun_ctxs, entry);
+    nr_turn_stun_ctx_destroy(&stun);
+  }
+
+  /* Destroy the permissions */
+  while (!STAILQ_EMPTY(&ctx->permissions)) {
+    nr_turn_permission *perm = STAILQ_FIRST(&ctx->permissions);
+    STAILQ_REMOVE_HEAD(&ctx->permissions, entry);
+    nr_turn_permission_destroy(&perm);
+  }
+
   RFREE(ctx);
 
   return(0);
 }
 
 static int nr_turn_client_connect(nr_turn_client_ctx *ctx)
 {
   int r,_status;
@@ -452,16 +474,18 @@ static void nr_turn_client_connected_cb(
 abort:
   if (_status) {
     nr_turn_client_failed(ctx);
   }
 }
 
 int nr_turn_client_cancel(nr_turn_client_ctx *ctx)
 {
+  nr_turn_stun_ctx *stun = 0;
+
   if (ctx->state == NR_TURN_CLIENT_STATE_CANCELLED ||
       ctx->state == NR_TURN_CLIENT_STATE_FAILED)
     return 0;
 
   if (ctx->label)
     r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): cancelling", ctx->label);
 
   /* If we are waiting for connect, we need to stop
@@ -477,39 +501,21 @@ int nr_turn_client_cancel(nr_turn_client
          error, we shouldn't cancel. */
       r_log(NR_LOG_TURN, LOG_ERR, "TURN: Couldn't get internal fd");
     }
     else {
       NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
     }
   }
 
-  /* Setting these values to 0 isn't strictly necessary, but
-     it protects us in case we double cancel and for
-     some reason bungle the states above in future.*/
-  RFREE(ctx->username);
-  ctx->username = 0;
-  r_data_destroy(&ctx->password);
-  RFREE(ctx->nonce);
-  ctx->nonce = 0;
-  RFREE(ctx->realm);
-  ctx->realm = 0;
-
-  /* Destroy the STUN client ctxs */
-  while (!STAILQ_EMPTY(&ctx->stun_ctxs)) {
-    nr_turn_stun_ctx *stun = STAILQ_FIRST(&ctx->stun_ctxs);
-    STAILQ_REMOVE_HEAD(&ctx->stun_ctxs, entry);
-    nr_turn_stun_ctx_destroy(&stun);
-  }
-
-  /* Destroy the permissions */
-  while (!STAILQ_EMPTY(&ctx->permissions)) {
-    nr_turn_permission *perm = STAILQ_FIRST(&ctx->permissions);
-    STAILQ_REMOVE_HEAD(&ctx->permissions, entry);
-    nr_turn_permission_destroy(&perm);
+  /* Cancel the STUN client ctxs */
+  stun = STAILQ_FIRST(&ctx->stun_ctxs);
+  while (stun) {
+    nr_stun_client_cancel(stun->stun);
+    stun = STAILQ_NEXT(stun, entry);
   }
 
   /* Cancel the timers, if not already cancelled */
   NR_async_timer_cancel(ctx->connected_timer_handle);
   NR_async_timer_cancel(ctx->refresh_timer_handle);
 
   ctx->state = NR_TURN_CLIENT_STATE_CANCELLED;
 
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -324,44 +324,48 @@ nsresult MediaPipeline::SendPacket(Trans
 
     MOZ_MTLOG(ML_ERROR, "Failed write on stream");
     return NS_BASE_STREAM_CLOSED;
   }
 
   return NS_OK;
 }
 
-void MediaPipeline::increment_rtp_packets_sent() {
+void MediaPipeline::increment_rtp_packets_sent(int32_t bytes) {
   ++rtp_packets_sent_;
+  rtp_bytes_sent_ += bytes;
 
   if (!(rtp_packets_sent_ % 100)) {
     MOZ_MTLOG(ML_INFO, "RTP sent packet count for " << description_
               << " Pipeline " << static_cast<void *>(this)
               << " Flow : " << static_cast<void *>(rtp_transport_)
-              << ": " << rtp_packets_sent_);
+              << ": " << rtp_packets_sent_
+              << " (" << rtp_bytes_sent_ << " bytes)");
   }
 }
 
 void MediaPipeline::increment_rtcp_packets_sent() {
   ++rtcp_packets_sent_;
   if (!(rtcp_packets_sent_ % 100)) {
     MOZ_MTLOG(ML_INFO, "RTCP sent packet count for " << description_
               << " Pipeline " << static_cast<void *>(this)
               << " Flow : " << static_cast<void *>(rtcp_transport_)
               << ": " << rtcp_packets_sent_);
   }
 }
 
-void MediaPipeline::increment_rtp_packets_received() {
+void MediaPipeline::increment_rtp_packets_received(int32_t bytes) {
   ++rtp_packets_received_;
+  rtp_bytes_received_ += bytes;
   if (!(rtp_packets_received_ % 100)) {
     MOZ_MTLOG(ML_INFO, "RTP received packet count for " << description_
               << " Pipeline " << static_cast<void *>(this)
               << " Flow : " << static_cast<void *>(rtp_transport_)
-              << ": " << rtp_packets_received_);
+              << ": " << rtp_packets_received_
+              << " (" << rtp_bytes_received_ << " bytes)");
   }
 }
 
 void MediaPipeline::increment_rtcp_packets_received() {
   ++rtcp_packets_received_;
   if (!(rtcp_packets_received_ % 100)) {
     MOZ_MTLOG(ML_INFO, "RTCP received packet count for " << description_
               << " Pipeline " << static_cast<void *>(this)
@@ -398,39 +402,40 @@ void MediaPipeline::RtpPacketReceived(Tr
   if (direction_ == TRANSMIT) {
     // Discard any media that is being transmitted to us
     // This will be unnecessary when we have SSRC filtering.
     return;
   }
 
   // TODO(ekr@rtfm.com): filter for DTLS here and in RtcpPacketReceived
   // TODO(ekr@rtfm.com): filter on SSRC for bundle
-  increment_rtp_packets_received();
 
   // Make a copy rather than cast away constness
   ScopedDeletePtr<unsigned char> inner_data(
       new unsigned char[len]);
   memcpy(inner_data, data, len);
-  int out_len;
+  int out_len = 0;
   nsresult res = rtp_recv_srtp_->UnprotectRtp(inner_data,
                                               len, len, &out_len);
   if (!NS_SUCCEEDED(res)) {
     char tmp[16];
 
     PR_snprintf(tmp, sizeof(tmp), "%.2x %.2x %.2x %.2x",
                 inner_data[0],
                 inner_data[1],
                 inner_data[2],
                 inner_data[3]);
 
     MOZ_MTLOG(ML_NOTICE, "Error unprotecting RTP in " << description_
               << "len= " << len << "[" << tmp << "...]");
 
     return;
   }
+  increment_rtp_packets_received(out_len);
+
   (void)conduit_->ReceivedRTPPacket(inner_data, out_len);  // Ignore error codes
 }
 
 void MediaPipeline::RtcpPacketReceived(TransportLayer *layer,
                                        const unsigned char *data,
                                        size_t len) {
   if (!transport_->pipeline()) {
     MOZ_MTLOG(ML_DEBUG, "Discarding incoming packet; transport disconnected");
@@ -607,17 +612,17 @@ nsresult MediaPipeline::PipelineTranspor
   int out_len;
   nsresult res = pipeline_->rtp_send_srtp_->ProtectRtp(inner_data,
                                                        data->len(),
                                                        max_len,
                                                        &out_len);
   if (!NS_SUCCEEDED(res))
     return res;
 
-  pipeline_->increment_rtp_packets_sent();
+  pipeline_->increment_rtp_packets_sent(out_len);
   return pipeline_->SendPacket(pipeline_->rtp_transport_, inner_data,
                                out_len);
 }
 
 nsresult MediaPipeline::PipelineTransport::SendRtcpPacket(
     const void *data, int len) {
 
     nsAutoPtr<DataBuffer> buf(new DataBuffer(static_cast<const uint8_t *>(data),
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -87,16 +87,18 @@ class MediaPipeline : public sigslot::ha
         rtp_send_srtp_(),
         rtcp_send_srtp_(),
         rtp_recv_srtp_(),
         rtcp_recv_srtp_(),
         rtp_packets_sent_(0),
         rtcp_packets_sent_(0),
         rtp_packets_received_(0),
         rtcp_packets_received_(0),
+        rtp_bytes_sent_(0),
+        rtp_bytes_received_(0),
         pc_(pc),
         description_() {
       // To indicate rtcp-mux rtcp_transport should be nullptr.
       // Therefore it's an error to send in the same flow for
       // both rtp and rtcp.
       MOZ_ASSERT(rtp_transport_ != rtcp_transport_);
 
       if (!rtcp_transport_) {
@@ -118,27 +120,30 @@ class MediaPipeline : public sigslot::ha
     if (stream_) {
       DetachMediaStream();
     }
   }
 
   virtual nsresult Init();
 
   virtual Direction direction() const { return direction_; }
+  virtual TrackID trackid() const { return track_id_; }
 
   bool IsDoingRtcpMux() const {
     return (rtp_transport_ == rtcp_transport_);
   }
 
-  int rtp_packets_sent() const { return rtp_packets_sent_; }
-  int rtcp_packets_sent() const { return rtcp_packets_sent_; }
-  int rtp_packets_received() const { return rtp_packets_received_; }
-  int rtcp_packets_received() const { return rtcp_packets_received_; }
+  int32_t rtp_packets_sent() const { return rtp_packets_sent_; }
+  int64_t rtp_bytes_sent() const { return rtp_bytes_sent_; }
+  int32_t rtcp_packets_sent() const { return rtcp_packets_sent_; }
+  int32_t rtp_packets_received() const { return rtp_packets_received_; }
+  int64_t rtp_bytes_received() const { return rtp_bytes_received_; }
+  int32_t rtcp_packets_received() const { return rtcp_packets_received_; }
 
-  MediaSessionConduit *Conduit() { return conduit_; }
+  MediaSessionConduit *Conduit() const { return conduit_; }
 
   // Thread counting
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline)
 
  protected:
   virtual void DetachMediaStream() {}
 
   // Separate class to allow ref counting
@@ -162,19 +167,19 @@ class MediaPipeline : public sigslot::ha
     MediaPipeline *pipeline_;  // Raw pointer to avoid cycles
     nsCOMPtr<nsIEventTarget> sts_thread_;
   };
   friend class PipelineTransport;
 
   virtual nsresult TransportFailed_s(TransportFlow *flow);  // The transport is down
   virtual nsresult TransportReady_s(TransportFlow *flow);   // The transport is ready
 
-  void increment_rtp_packets_sent();
+  void increment_rtp_packets_sent(int bytes);
   void increment_rtcp_packets_sent();
-  void increment_rtp_packets_received();
+  void increment_rtp_packets_received(int bytes);
   void increment_rtcp_packets_received();
 
   virtual nsresult SendPacket(TransportFlow *flow, const void* data, int len);
 
   // Process slots on transports
   void StateChange(TransportFlow *flow, TransportLayer::State);
   void RtpPacketReceived(TransportLayer *layer, const unsigned char *data,
                          size_t len);
@@ -211,20 +216,22 @@ class MediaPipeline : public sigslot::ha
   RefPtr<SrtpFlow> rtp_send_srtp_;
   RefPtr<SrtpFlow> rtcp_send_srtp_;
   RefPtr<SrtpFlow> rtp_recv_srtp_;
   RefPtr<SrtpFlow> rtcp_recv_srtp_;
 
   // Written only on STS thread. May be read on other
   // threads but since there is no mutex, the values
   // will only be approximate.
-  int rtp_packets_sent_;
-  int rtcp_packets_sent_;
-  int rtp_packets_received_;
-  int rtcp_packets_received_;
+  int32_t rtp_packets_sent_;
+  int32_t rtcp_packets_sent_;
+  int32_t rtp_packets_received_;
+  int32_t rtcp_packets_received_;
+  int64_t rtp_bytes_sent_;
+  int64_t rtp_bytes_received_;
 
   // Written on Init. Read on STS thread.
   std::string pc_;
   std::string description_;
 
  private:
   nsresult Init_s();
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -58,16 +58,18 @@
 #include "nsIDOMDataChannel.h"
 #include "mozilla/dom/RTCConfigurationBinding.h"
 #include "mozilla/dom/RTCStatsReportBinding.h"
 #include "mozilla/dom/RTCPeerConnectionBinding.h"
 #include "mozilla/dom/PeerConnectionImplBinding.h"
 #include "mozilla/dom/DataChannelBinding.h"
 #include "MediaStreamList.h"
 #include "MediaStreamTrack.h"
+#include "AudioStreamTrack.h"
+#include "VideoStreamTrack.h"
 #include "nsIScriptGlobalObject.h"
 #include "DOMMediaStream.h"
 #include "rlogringbuffer.h"
 #endif
 
 #ifndef USE_FAKE_MEDIA_STREAMS
 #include "MediaSegment.h"
 #endif
@@ -1791,36 +1793,61 @@ PeerConnectionImpl::IceGatheringStateCha
                              &PeerConnectionObserver::OnStateChange,
                              PCObserverStateType::IceGatheringState,
                              rv, static_cast<JSCompartment*>(nullptr)),
                 NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 #ifdef MOZILLA_INTERNAL_API
-void PeerConnectionImpl::GetStats_s(
-    uint32_t trackId,
-    bool internalStats,
-    DOMHighResTimeStamp now) {
+class RTCStatsReportInternalConstruct : public RTCStatsReportInternal {
+public:
+  RTCStatsReportInternalConstruct(const nsString &pcid, DOMHighResTimeStamp now) {
+    mPcid = pcid;
+    mInboundRTPStreamStats.Construct();
+    mOutboundRTPStreamStats.Construct();
+    mMediaStreamTrackStats.Construct();
+    mMediaStreamStats.Construct();
+    mTransportStats.Construct();
+    mIceComponentStats.Construct();
+    mIceCandidatePairStats.Construct();
+    mIceCandidateStats.Construct();
+    mCodecStats.Construct();
+  }
+};
 
-  nsresult result = NS_OK;
-  nsAutoPtr<RTCStatsReportInternal> report(new RTCStatsReportInternal);
-  if (!report) {
-    result = NS_ERROR_FAILURE;
-  }
+nsresult PeerConnectionImpl::GetStatsImpl_s(
+    TrackID trackId,
+    bool internalStats,
+    DOMHighResTimeStamp now,
+    RTCStatsReportInternal *report) {
+  if (mMedia) {
+    nsresult rv;
+
+    // Gather stats from media pipeline (can't touch stream itself on STS)
 
-  report->mPcid.Construct(NS_ConvertASCIItoUTF16(mHandle.c_str()));
-  if (mMedia) {
-    RefPtr<NrIceMediaStream> mediaStream(
-        mMedia->ice_media_stream(trackId));
+    for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) {
+      rv = mMedia->GetLocalStream(i)->GetPipelineStats(now, trackId,
+          &report->mInboundRTPStreamStats.Value(),
+          &report->mOutboundRTPStreamStats.Value());
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) {
+      rv = mMedia->GetRemoteStream(i)->GetPipelineStats(now, trackId,
+          &report->mInboundRTPStreamStats.Value(),
+          &report->mOutboundRTPStreamStats.Value());
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    // Gather stats from ICE
+
+    RefPtr<NrIceMediaStream> mediaStream(mMedia->ice_media_stream(trackId));
     if (mediaStream) {
       std::vector<NrIceCandidatePair> candPairs;
       mediaStream->GetCandidatePairs(&candPairs);
-      report->mIceCandidatePairStats.Construct();
-      report->mIceCandidateStats.Construct();
       NS_ConvertASCIItoUTF16 componentId(mediaStream->name().c_str());
       for (auto p = candPairs.begin(); p != candPairs.end(); ++p) {
         NS_ConvertASCIItoUTF16 codeword(p->codeword.c_str());
         NS_ConvertASCIItoUTF16 localCodeword(p->local.codeword.c_str());
         NS_ConvertASCIItoUTF16 remoteCodeword(p->remote.codeword.c_str());
         // Only expose candidate-pair statistics to chrome, until we've thought
         // through the implications of exposing it to content.
 
@@ -1864,29 +1891,42 @@ void PeerConnectionImpl::GetStats_s(
           remote.mIpAddress.Construct(
               NS_ConvertASCIItoUTF16(p->remote.cand_addr.host.c_str()));
           remote.mPortNumber.Construct(p->remote.cand_addr.port);
           report->mIceCandidateStats.Value().AppendElement(remote);
         }
       }
     }
   }
+  return NS_OK;
+}
+
+void PeerConnectionImpl::GetStats_s(
+    TrackID trackId,
+    bool internalStats,
+    DOMHighResTimeStamp now) {
+
+  nsAutoPtr<RTCStatsReportInternal> report(new RTCStatsReportInternalConstruct(
+      NS_ConvertASCIItoUTF16(mHandle.c_str()), now));
+
+  nsresult rv = report ? GetStatsImpl_s(trackId, internalStats, now, report)
+                       : NS_ERROR_UNEXPECTED;
 
   nsRefPtr<PeerConnectionImpl> pc(this);
   RUN_ON_THREAD(mThread,
                 WrapRunnable(pc,
                              &PeerConnectionImpl::OnStatsReport_m,
                              trackId,
-                             result,
+                             rv,
                              report),
                 NS_DISPATCH_NORMAL);
 }
 
 void PeerConnectionImpl::OnStatsReport_m(
-    uint32_t trackId,
+    TrackID trackId,
     nsresult result,
     nsAutoPtr<RTCStatsReportInternal> report) {
   nsRefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
   if (pco) {
     JSErrorResult rv;
     if (NS_SUCCEEDED(result)) {
       pco->OnGetStatsSuccess(*report, rv);
     } else {
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -20,16 +20,17 @@
 #include "nricectx.h"
 #include "nricemediastream.h"
 #include "nsComponentManagerUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsIThread.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/PeerConnectionImplEnumsBinding.h"
+#include "StreamBuffer.h"
 
 #ifdef MOZILLA_INTERNAL_API
 #include "mozilla/TimeStamp.h"
 #include "mozilla/net/DataChannel.h"
 #include "VideoUtils.h"
 #include "VideoSegment.h"
 #include "nsNSSShutDown.h"
 #endif
@@ -523,22 +524,27 @@ private:
   // ICE callbacks run on the right thread.
   nsresult IceConnectionStateChange_m(
       mozilla::dom::PCImplIceConnectionState aState);
   nsresult IceGatheringStateChange_m(
       mozilla::dom::PCImplIceGatheringState aState);
 
 #ifdef MOZILLA_INTERNAL_API
   // Fills in an RTCStatsReportInternal. Must be run on STS.
-  void GetStats_s(uint32_t trackId,
+  void GetStats_s(mozilla::TrackID trackId,
                   bool internalStats,
                   DOMHighResTimeStamp now);
 
+  nsresult GetStatsImpl_s(mozilla::TrackID trackId,
+                          bool internalStats,
+                          DOMHighResTimeStamp now,
+                          mozilla::dom::RTCStatsReportInternal *report);
+
   // Sends an RTCStatsReport to JS. Must run on main thread.
-  void OnStatsReport_m(uint32_t trackId,
+  void OnStatsReport_m(mozilla::TrackID trackId,
                        nsresult result,
                        nsAutoPtr<mozilla::dom::RTCStatsReportInternal> report);
 
   // Fetches logs matching pattern from RLogRingBuffer. Must be run on STS.
   void GetLogging_s(const std::string& pattern);
 
   // Sends logging to JS. Must run on main thread.
   void OnGetLogging_m(const std::string& pattern,
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -15,19 +15,21 @@
 #include "AudioConduit.h"
 #include "VideoConduit.h"
 #include "runnable_utils.h"
 
 #ifdef MOZILLA_INTERNAL_API
 #include "MediaStreamList.h"
 #include "nsIScriptGlobalObject.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/RTCStatsReportBinding.h"
 #endif
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 namespace sipcc {
 
 static const char* logTag = "PeerConnectionMedia";
 static const mozilla::TrackID TRACK_AUDIO = 0;
 static const mozilla::TrackID TRACK_VIDEO = 1;
 
 /* If the ExpectAudio hint is on we will add a track at the default first
@@ -460,16 +462,68 @@ SourceStreamInfo::GetPipeline(int aTrack
 
   if (it == mPipelines.end()) {
     return nullptr;
   }
 
   return it->second;
 }
 
+// This methods gathers statistics for the getStats API.
+// aTrack == 0 means gather stats for all tracks.
+
+nsresult
+SourceStreamInfo::GetPipelineStats(DOMHighResTimeStamp now, int aTrack,
+                                   Sequence<RTCInboundRTPStreamStats > *inbound,
+                                   Sequence<RTCOutboundRTPStreamStats > *outbound)
+{
+#ifdef MOZILLA_INTERNAL_API
+  ASSERT_ON_THREAD(mParent->GetSTSThread());
+  // walk through all the MediaPipelines and gather stats
+  for (std::map<int, RefPtr<MediaPipeline> >::iterator it = mPipelines.begin();
+       it != mPipelines.end();
+       ++it) {
+    if (!aTrack || aTrack == it->first) {
+      const MediaPipeline &mp = *it->second;
+      nsString idstr = (mp.Conduit()->type() == MediaSessionConduit::AUDIO) ?
+          NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_");
+      idstr.AppendInt(mp.trackid());
+
+      switch (mp.direction()) {
+        case MediaPipeline::TRANSMIT: {
+          RTCOutboundRTPStreamStats s;
+          s.mTimestamp.Construct(now);
+          s.mId.Construct(NS_LITERAL_STRING("outbound_rtp_") + idstr);
+          s.mType.Construct(RTCStatsType::Outboundrtp);
+          // TODO: Get SSRC
+          // int channel = mp.Conduit()->GetChannel();
+          s.mSsrc.Construct(NS_LITERAL_STRING("123457"));
+          s.mPacketsSent.Construct(mp.rtp_packets_sent());
+          s.mBytesSent.Construct(mp.rtp_bytes_sent());
+          outbound->AppendElement(s);
+          break;
+        }
+        case MediaPipeline::RECEIVE: {
+          RTCInboundRTPStreamStats s;
+          s.mTimestamp.Construct(now);
+          s.mId.Construct(NS_LITERAL_STRING("inbound_rtp_") + idstr);
+          s.mType.Construct(RTCStatsType::Inboundrtp);
+          s.mSsrc.Construct(NS_LITERAL_STRING("123457"));
+          s.mPacketsReceived.Construct(mp.rtp_packets_received());
+          s.mBytesReceived.Construct(mp.rtp_bytes_received());
+          inbound->AppendElement(s);
+          break;
+        }
+      }
+    }
+  }
+#endif
+  return NS_OK;
+}
+
 void
 LocalSourceStreamInfo::StorePipeline(int aTrack,
   mozilla::RefPtr<mozilla::MediaPipeline> aPipeline)
 {
   MOZ_ASSERT(mPipelines.find(aTrack) == mPipelines.end());
   if (mPipelines.find(aTrack) != mPipelines.end()) {
     CSFLogError(logTag, "%s: Storing duplicate track", __FUNCTION__);
     return;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -24,21 +24,25 @@
 
 #include "AudioSegment.h"
 
 #ifdef MOZILLA_INTERNAL_API
 #include "Layers.h"
 #include "VideoUtils.h"
 #include "ImageLayers.h"
 #include "VideoSegment.h"
-#else
+#endif
+
 namespace mozilla {
-  class DataChannel;
+class DataChannel;
+namespace dom {
+class RTCInboundRTPStreamStats;
+class RTCOutboundRTPStreamStats;
 }
-#endif
+}
 
 #include "nricectx.h"
 #include "nriceresolver.h"
 #include "nricemediastream.h"
 #include "MediaPipeline.h"
 
 namespace sipcc {
 
@@ -176,17 +180,19 @@ public:
   SourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
                   PeerConnectionMedia *aParent)
       : mMediaStream(aMediaStream),
         mParent(aParent) {
     MOZ_ASSERT(mMediaStream);
   }
 
   mozilla::RefPtr<mozilla::MediaPipeline> GetPipeline(int aTrack);
-
+  nsresult GetPipelineStats(DOMHighResTimeStamp now, int aTrack,
+    mozilla::dom::Sequence<mozilla::dom::RTCInboundRTPStreamStats > *inbound,
+    mozilla::dom::Sequence<mozilla::dom::RTCOutboundRTPStreamStats > *outbound);
 protected:
   std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> > mPipelines;
   nsRefPtr<DOMMediaStream> mMediaStream;
   PeerConnectionMedia *mParent;
 };
 
 // TODO(ekr@rtfm.com): Refactor {Local,Remote}SourceStreamInfo
 // bug 837539.
--- a/media/webrtc/signaling/test/mediapipeline_unittest.cpp
+++ b/media/webrtc/signaling/test/mediapipeline_unittest.cpp
@@ -320,17 +320,18 @@ class MediaPipelineTest : public ::testi
 
     p2_.Start();
     p1_.Start();
 
     // wait for some RTP/RTCP tx and rx to happen
     PR_Sleep(10000);
 
     ASSERT_GE(p1_.GetAudioRtpCount(), 40);
-    ASSERT_GE(p2_.GetAudioRtpCount(), 40);
+// TODO: Fix to not fail or crash (Bug 947663)
+//    ASSERT_GE(p2_.GetAudioRtpCount(), 40);
     ASSERT_GE(p1_.GetAudioRtcpCount(), 1);
     ASSERT_GE(p2_.GetAudioRtcpCount(), 1);
 
     p1_.Stop();
     p2_.Stop();
   }
 
 protected:
--- a/xpcom/glue/nsBaseHashtable.h
+++ b/xpcom/glue/nsBaseHashtable.h
@@ -53,16 +53,18 @@ class nsBaseHashtable :
   protected nsTHashtable< nsBaseHashtableET<KeyClass,DataType> >
 {
   typedef mozilla::fallible_t fallible_t;
 
 public:
   typedef typename KeyClass::KeyType KeyType;
   typedef nsBaseHashtableET<KeyClass,DataType> EntryType;
 
+  using nsTHashtable<EntryType>::Contains;
+
   nsBaseHashtable()
   {
   }
   explicit nsBaseHashtable(uint32_t aInitSize)
     : nsTHashtable<EntryType>(aInitSize)
   {
   }