Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Tue, 04 Dec 2012 15:00:07 -0800
changeset 126454 42bb46d1c19f799e07eea0a49af683db1c3772cc
parent 126453 cc9fa565751545e14bb351f361ff0fd5c7085357 (current diff)
parent 124016 e8f0504ccbb9bb9e8d1b90eb1da055adff7cf73b (diff)
child 126455 69a4de108bdaf6ef9e251627f27a017d21a78321
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone20.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 s-c.
.gdbinit
layout/mathml/tests/all-presentation.xml
layout/mathml/tests/various.xml
mobile/android/base/resources/drawable-large-hdpi-v11/address_bar_back_button_bg.png
mobile/android/base/resources/drawable-large-hdpi-v11/address_bar_back_button_pressed_bg.png
mobile/android/base/resources/drawable-large-mdpi-v11/address_bar_back_button_bg.png
mobile/android/base/resources/drawable-large-mdpi-v11/address_bar_back_button_pressed_bg.png
mobile/android/base/resources/drawable-large-xhdpi-v11/address_bar_back_button_bg.png
mobile/android/base/resources/drawable-large-xhdpi-v11/address_bar_back_button_pressed_bg.png
mobile/android/base/resources/drawable/address_bar_back_button.xml
mobile/android/base/resources/drawable/address_bar_back_button_bg.xml
mobile/android/base/resources/drawable/address_bar_back_button_pressed_bg.xml
mobile/android/base/resources/drawable/address_bar_forward_button.xml
mobile/android/base/resources/drawable/address_bar_url.xml.in
--- a/Makefile.in
+++ b/Makefile.in
@@ -207,20 +207,16 @@ else
 maybe_clobber_profiledbuild:
 endif
 else
 maybe_clobber_profiledbuild:
 	$(RM) $(DIST)/bin/*.pgc
 	find $(DIST)/$(MOZ_APP_NAME) -name "*.pgc" -exec mv {} $(DIST)/bin \;
 endif
 
-# put in our default gdbinit so that the gdb debugging experience is happier.
-libs:: .gdbinit
-	$(INSTALL) $< $(DIST)/bin
-
 .PHONY: maybe_clobber_profiledbuild
 
 # Look for R_386_PC32 relocations in shared libs, these
 # break x86_64 builds and SELinux users.
 ifeq ($(OS_TARGET)_$(TARGET_XPCOM_ABI),Linux_x86-gcc3)
 scheck::
 	@relcount=`find $(DIST)/bin -name "*.so" | xargs objdump -R | grep R_386_PC32 | wc -l` && if test $$relcount -gt 0; then echo "FAILED: R_386_PC32 relocations detected in a shared library.  Did you use a system header without adding it to config/system-headers?"; exit 1; else echo "PASSED"; fi
 endif
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -139,17 +139,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // directory
     &nsGkAtoms::directory,
     roles::LIST,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    kGenericAccType,
+    Accessible::eListAccessible,
     kNoReqStates
   },
   { // document
     &nsGkAtoms::document,
     roles::DOCUMENT,
     kUseMapRole,
     eNoValue,
     eNoAction,
@@ -234,17 +234,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
   },
   { // list
     &nsGkAtoms::list,
     roles::LIST,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    kGenericAccType,
+    Accessible::eListAccessible,
     states::READONLY
   },
   { // listbox
     &nsGkAtoms::listbox,
     roles::LISTBOX,
     kUseMapRole,
     eNoValue,
     eNoAction,
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -825,16 +825,25 @@ nsAccessibilityService::GetOrCreateAcces
           nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
           if (contextRoleMap &&
               !(contextRoleMap->accTypes & Accessible::eTableAccessible))
             roleMapEntry = &nsARIAMap::gEmptyRoleMap;
 
         } else if (frame->AccessibleType() == eHTMLTableCellAccessible &&
                    aContext->ARIARoleMap() == &nsARIAMap::gEmptyRoleMap) {
           roleMapEntry = &nsARIAMap::gEmptyRoleMap;
+
+        } else if (content->Tag() == nsGkAtoms::dt ||
+                   content->Tag() == nsGkAtoms::li ||
+                   content->Tag() == nsGkAtoms::dd ||
+                   frame->AccessibleType() == eHTMLLiAccessible) {
+          nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
+          if (contextRoleMap &&
+              !(contextRoleMap->accTypes & Accessible::eListAccessible))
+            roleMapEntry = &nsARIAMap::gEmptyRoleMap;
         }
       }
     }
   }
 
   if (!newAcc) {
     // Elements may implement nsIAccessibleProvider via XBL. This allows them to
     // say what kind of accessible to create.
@@ -1232,28 +1241,40 @@ nsAccessibilityService::CreateHTMLAccess
       return accessible;
     }
 
     Accessible* accessible = new HTMLLinkAccessible(aContent, document);
     NS_ADDREF(accessible);
     return accessible;
   }
 
-  if (tag == nsGkAtoms::dt || tag == nsGkAtoms::li) {
-    // Create list item accessible unconditionally by tag name. nsBlockFrame
-    // creates the list item accessible for other elements styled as list items.
-    Accessible* accessible = new HTMLLIAccessible(aContent, document);
-    NS_ADDREF(accessible);
-    return accessible;
+  if (aContext->IsOfType(Accessible::eListAccessible)) {
+    // If list item is a child of accessible list then create an accessible for
+    // it unconditionally by tag name. nsBlockFrame creates the list item
+    // accessible for other elements styled as list items.
+    if (aContext->GetContent() == aContent->GetParent()) {
+      if (tag == nsGkAtoms::dt || tag == nsGkAtoms::li) {
+        Accessible* accessible = new HTMLLIAccessible(aContent, document);
+        NS_ADDREF(accessible);
+        return accessible;
+      }
+
+      if (tag == nsGkAtoms::dd) {
+        Accessible* accessible = new HyperTextAccessibleWrap(aContent, document);
+        NS_ADDREF(accessible);
+        return accessible;
+      }
+    }
+
+    return nullptr;
   }
 
   if (tag == nsGkAtoms::abbr ||
       tag == nsGkAtoms::acronym ||
       tag == nsGkAtoms::blockquote ||
-      tag == nsGkAtoms::dd ||
       tag == nsGkAtoms::form ||
       tag == nsGkAtoms::h1 ||
       tag == nsGkAtoms::h2 ||
       tag == nsGkAtoms::h3 ||
       tag == nsGkAtoms::h4 ||
       tag == nsGkAtoms::h5 ||
       tag == nsGkAtoms::h6 ||
       tag == nsGkAtoms::q) {
@@ -1321,17 +1342,20 @@ nsAccessibilityService::CreateAccessible
       break;
     case eHTMLImageMapAccessible:
       newAcc = new HTMLImageMapAccessible(aContent, document);
       break;
     case eHTMLLabelAccessible:
       newAcc = new HTMLLabelAccessible(aContent, document);
       break;
     case eHTMLLiAccessible:
-      newAcc = new HTMLLIAccessible(aContent, document);
+      if (aContext->IsOfType(Accessible::eListAccessible) &&
+          aContext->GetContent() == aContent->GetParent()) {
+        newAcc = new HTMLLIAccessible(aContent, document);
+      }
       break;
     case eHTMLSelectListAccessible:
       newAcc = new HTMLSelectListAccessible(aContent, document);
       break;
     case eHTMLMediaAccessible:
       newAcc = new EnumRoleAccessible(aContent, document, roles::GROUPING);
       break;
     case eHTMLObjectFrameAccessible: {
@@ -1369,18 +1393,20 @@ nsAccessibilityService::CreateAccessible
         }
       }
       break;
     }
     case eHTMLTextFieldAccessible:
       newAcc = new HTMLTextFieldAccessible(aContent, document);
       break;
     case eHyperTextAccessible:
-      newAcc = new HyperTextAccessibleWrap(aContent, document);
+      if (aContent->Tag() != nsGkAtoms::dt && aContent->Tag() != nsGkAtoms::dd)
+        newAcc = new HyperTextAccessibleWrap(aContent, document);
       break;
+
     case eImageAccessible:
       newAcc = new ImageAccessibleWrap(aContent, document);
       break;
     case eOuterDocAccessible:
       newAcc = new OuterDocAccessible(aContent, document);
       break;
     case eTextLeafAccessible:
       newAcc = new TextLeafAccessibleWrap(aContent, document);
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -785,28 +785,29 @@ public: // XXX: a small hack to make the
     eComboboxAccessible = 1 << 10,
     eDocAccessible = 1 << 11,
     eHyperTextAccessible = 1 << 12,
     eHTMLFileInputAccessible = 1 << 13,
     eHTMLListItemAccessible = 1 << 14,
     eHTMLTableRowAccessible = 1 << 15,
     eImageAccessible = 1 << 16,
     eImageMapAccessible = 1 << 17,
-    eListControlAccessible = 1 << 18,
-    eMenuButtonAccessible = 1 << 19,
-    eMenuPopupAccessible = 1 << 20,
-    eProgressAccessible = 1 << 21,
-    eRootAccessible = 1 << 22,
-    eSelectAccessible = 1 << 23,
-    eTableAccessible = 1 << 24,
-    eTableCellAccessible = 1 << 25,
-    eTableRowAccessible = 1 << 26,
-    eTextLeafAccessible = 1 << 27,
-    eXULDeckAccessible = 1 << 28,
-    eXULTreeAccessible = 1 << 29
+    eListAccessible = 1 << 18,
+    eListControlAccessible = 1 << 19,
+    eMenuButtonAccessible = 1 << 20,
+    eMenuPopupAccessible = 1 << 21,
+    eProgressAccessible = 1 << 22,
+    eRootAccessible = 1 << 23,
+    eSelectAccessible = 1 << 24,
+    eTableAccessible = 1 << 25,
+    eTableCellAccessible = 1 << 26,
+    eTableRowAccessible = 1 << 27,
+    eTextLeafAccessible = 1 << 28,
+    eXULDeckAccessible = 1 << 29,
+    eXULTreeAccessible = 1 << 30
   };
 
 protected:
 
   //////////////////////////////////////////////////////////////////////////////
   // Miscellaneous helpers
 
   /**
--- a/accessible/src/html/HTMLListAccessible.h
+++ b/accessible/src/html/HTMLListAccessible.h
@@ -17,17 +17,17 @@ class HTMLListBulletAccessible;
 
 /**
  * Used for HTML list (like HTML ul).
  */
 class HTMLListAccessible : public HyperTextAccessibleWrap
 {
 public:
   HTMLListAccessible(nsIContent* aContent, DocAccessible* aDoc) :
-    HyperTextAccessibleWrap(aContent, aDoc) { }
+    HyperTextAccessibleWrap(aContent, aDoc) { mFlags |= eListAccessible; }
   virtual ~HTMLListAccessible() { }
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // Accessible
   virtual a11y::role NativeRole();
   virtual uint64_t NativeState();
--- a/accessible/src/msaa/HTMLWin32ObjectAccessible.cpp
+++ b/accessible/src/msaa/HTMLWin32ObjectAccessible.cpp
@@ -15,17 +15,18 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLWin32ObjectOwnerAccessible::
   HTMLWin32ObjectOwnerAccessible(nsIContent* aContent,
                                  DocAccessible* aDoc, void* aHwnd) :
   AccessibleWrap(aContent, aDoc), mHwnd(aHwnd)
 {
   // Our only child is a HTMLWin32ObjectAccessible object.
-  mNativeAccessible = new HTMLWin32ObjectAccessible(mHwnd);
+  if (mHwnd)
+    mNativeAccessible = new HTMLWin32ObjectAccessible(mHwnd);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLWin32ObjectOwnerAccessible: nsAccessNode implementation
 
 void
 HTMLWin32ObjectOwnerAccessible::Shutdown()
 {
--- a/accessible/tests/mochitest/elm/test_plugin.html
+++ b/accessible/tests/mochitest/elm/test_plugin.html
@@ -16,37 +16,46 @@
           src="../states.js"></script>
 
   <script type="application/javascript">
 
     function doTest()
     {
       if (!WIN) {
         ok(true,
-           "Nothing to test because accessible plugins are supported on Windows only");
+           "It's Windows specific test. Feel free to extend the test.");
 
         SimpleTest.finish();
         return;
       }
 
-      testRole("plugin", ROLE_EMBEDDED_OBJECT);
-      testStates("plugin", STATE_UNAVAILABLE);
+      testStates("plugin-windowless", STATE_UNAVAILABLE);
+      testAccessibleTree("plugin-windowless", { EMBEDDED_OBJECT: [ ] });
+
+      testStates("plugin-windowed", 0, 0, STATE_UNAVAILABLE);
+      testAccessibleTree("plugin-windowed", { EMBEDDED_OBJECT: [ { NOTHING: [] } ] });
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
-     title="embed and object HTML tags should be given an accessible role of embedded object"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=485270">Mozilla Bug 485270</a>
+     title="Embed and object HTML tags should be given an accessible role of embedded object"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=485270">Bug 485270</a>
+  <a target="_blank"
+     title="Embedded object accessibles for inaccessible/windowless plugins should not expose a NULL child"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=816856">Bug 816856</a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <embed id="plugin" type="application/x-test" width="300" height="300"></embed>
+  <embed id="plugin-windowless" type="application/x-test"
+         width="300" height="300"></embed>
+  <embed id="plugin-windowed" type="application/x-test" wmode="window"
+         width="300" height="300"></embed>
 </body>
 </html>
--- a/accessible/tests/mochitest/tree/Makefile.in
+++ b/accessible/tests/mochitest/tree/Makefile.in
@@ -12,16 +12,17 @@ relativesrcdir  = accessible/tree
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_A11Y_FILES =\
 		dockids.html \
 		$(filter disabled-temporarily--bug-561508, test_applicationacc.xul) \
 		test_aria_globals.html \
 		test_aria_grid.html \
 		test_aria_imgmap.html \
+		test_aria_list.html \
 		test_aria_menu.html \
 		test_aria_presentation.html \
 		test_brokencontext.html \
 		test_button.xul \
 		test_canvas.html \
 		test_combobox.xul \
 		test_cssoverflow.html \
 		test_dochierarchy.html \
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_list.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>ARIA lists</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">
+    function doTest()
+    {
+      //////////////////////////////////////////////////////////////////////////
+      // list
+
+      var accTree =
+        { LIST: [
+          { LISTITEM: [
+            { TEXT_LEAF: [ ] }
+          ] }
+        ] };
+
+      testAccessibleTree("list", accTree);
+
+      //////////////////////////////////////////////////////////////////////////
+      // crazy list (mad mix of ARIA and HTML)
+
+      accTree = { // div@role="list"
+        role: ROLE_LIST,
+        children: [
+          { // li text leaf
+             role: ROLE_TEXT_LEAF,
+             name: "item1",
+             children: [ ]
+          },
+          { // li@role="listitem"
+            role: ROLE_LISTITEM,
+            children: [
+              { // text leaf
+                role: ROLE_TEXT_LEAF,
+                name: "item2",
+                children: [ ]
+              }
+            ]
+          }
+        ]
+      };
+
+      testAccessibleTree("crazy_list", accTree);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="Build the context dependent tree"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=804461">
+    Mozilla Bug 804461
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="list" role="list">
+    <div role="listitem">item1</div>
+  </div>
+
+  <div id="crazy_list" role="list">
+    <ul role="presentation">
+      <li>item1</li>
+      <li role="listitem">item2</li>
+    </ul>
+  </div>
+</body>
+</html>
--- a/accessible/tests/mochitest/tree/test_brokencontext.html
+++ b/accessible/tests/mochitest/tree/test_brokencontext.html
@@ -34,16 +34,71 @@
             { NOTHING: [ // td
               { TEXT_LEAF: [ ] }
             ] }
           ] }
       ] };
     testAccessibleTree("button_table", tree);
 
     ////////////////////////////////////////////////////////////////////////////
+    // HTML list elements outside list context.
+
+    ok(!isAccessible("presentation_ul"),
+                     "presentational ul shouldn't be accessible");
+    ok(!isAccessible("item_in_presentation_ul"),
+                     "li in presentational ul shouldn't be accessible");
+    ok(!isAccessible("styleditem_in_presentation_ul"),
+                     "list styled span in presentational ul shouldn't be accessible");
+
+    ok(!isAccessible("presentation_ol"),
+                     "presentational ol shouldn't be accessible");
+    ok(!isAccessible("item_in_presentation_ol"),
+                     "li in presentational ol shouldn't be accessible");
+
+    ok(!isAccessible("presentation_dl"),
+                     "presentational dl shouldn't be accessible");
+    ok(!isAccessible("dt_in_presentation_dl"),
+                     "dt in presentational dl shouldn't be accessible");
+    ok(!isAccessible("dd_in_presentation_dl"),
+                     "dd in presentational dl shouldn't be accessible");
+
+    tree =
+      { PUSHBUTTON: [ // ul
+        { NOTHING: [ // li
+          { STATICTEXT: [ ] },
+          { TEXT_LEAF: [ ] }
+        ] },
+        { NOTHING: [ // span styled as a list
+          { STATICTEXT: [ ] },
+          { TEXT_LEAF: [ ] }
+        ] }
+      ] };
+    testAccessibleTree("button_ul", tree);
+
+    tree =
+      { PUSHBUTTON: [ // ol
+        { NOTHING: [ // li
+          { STATICTEXT: [ ] },
+          { TEXT_LEAF: [ ] }
+        ] }
+      ] };
+    testAccessibleTree("button_ol", tree);
+
+    tree =
+      { PUSHBUTTON: [ // dl
+        { NOTHING: [ // dt
+          { TEXT_LEAF: [ ] }
+        ] },
+        { NOTHING: [ // dd
+          { TEXT_LEAF: [ ] }
+        ] }
+      ] };
+    testAccessibleTree("button_dl", tree);
+
+    ////////////////////////////////////////////////////////////////////////////
     // Styled as HTML table elements, accessible is created by tag name
 
     tree =
       { LINK: [ // a
         { TEXT_LEAF: [ ] }
       ] };
     testAccessibleTree("a_as_td", tree);
 
@@ -67,16 +122,21 @@
 </head>
 
 <body>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=706849"
      title="Create accessible by tag name as fallback if table descendant style is used out of table context">
     Mozilla Bug 706849
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=804461"
+     title="Build the context dependent tree ">
+    Mozilla Bug 804461
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <!-- HTML table elements out of table -->
   <table role="presentation">
     <tr id="tr_in_presentation_table">
@@ -87,16 +147,47 @@
 
   <table role="button" id="button_table">
     <tr id="tr_in_button_table">
       <th id="th_in_button_table">not a header</th>
       <td id="td_in_button_table">not a cell</td>
     </tr>
   </table>
 
+  <!-- HTML list elements out of list -->
+  <ul role="presentation" id="presentation_ul">
+    <li id="item_in_presentation_ul">item</li>
+    <span id="styleditem_in_presentation_ul"
+          style="display:list-item">Oranges</span>
+  </ul>
+
+  <ol role="presentation" id="presentation_ol">
+    <li id="item_in_presentation_ol">item</li>
+  </ol>
+
+  <dl role="presentation" id="presentation_dl">
+    <dt id="dt_in_presentation_dl">term</dt>
+    <dd id="dd_in_presentation_dl">definition</dd>
+  </dl>
+
+  <ul role="button" id="button_ul">
+    <li id="item_in_button_ul">item</li>
+    <span id="styleditem_in_button_ul"
+          style="display:list-item">Oranges</span>
+  </ul>
+
+  <ol role="button" id="button_ol">
+    <li id="item_in_button_ul">item</li>
+  </ol>
+
+  <dl role="button" id="button_dl">
+    <dt id="dt_in_button_dl">term</ld>
+    <dd id="dd_in_button_dl">definition</dd>
+  </dl>
+
   <!-- styled as HTML table elements -->
   <a id="a_as_td" style="display:table-cell;" href="http://www.google.com">Google</a>
   <h1 id="h1_as_td" style="display: table-cell;">h1</h1>
   <h2 id="h2_as_td" style="display: table-cell;">h2</h2>
   <h3 id="h3_as_td" style="display: table-cell;">h3</h3>
   <h4 id="h4_as_td" style="display: table-cell;">h4</h4>
   <h5 id="h5_as_td" style="display: table-cell;">h5</h5>
   <h6 id="h6_as_td" style="display: table-cell;">h6</h6>
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -18,17 +18,16 @@ XPCOMUtils.defineLazyServiceGetter(Servi
                                    "@mozilla.org/focus-manager;1",
                                    "nsIFocusManager");
 
 XPCOMUtils.defineLazyGetter(this, "domWindowUtils", function () {
   return content.QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIDOMWindowUtils);
 });
 
-const FOCUS_CHANGE_DELAY = 20;
 const RESIZE_SCROLL_DELAY = 20;
 
 let HTMLInputElement = Ci.nsIDOMHTMLInputElement;
 let HTMLTextAreaElement = Ci.nsIDOMHTMLTextAreaElement;
 let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
 let HTMLOptGroupElement = Ci.nsIDOMHTMLOptGroupElement;
 let HTMLOptionElement = Ci.nsIDOMHTMLOptionElement;
 
@@ -46,17 +45,16 @@ let FormAssistant = {
 
   ignoredInputTypes: new Set([
     'button', 'file', 'checkbox', 'radio', 'reset', 'submit', 'image'
   ]),
 
   isKeyboardOpened: false,
   selectionStart: 0,
   selectionEnd: 0,
-  blurTimeout: null,
   scrollIntoViewTimeout: null,
   _focusedElement: null,
 
   get focusedElement() {
     if (this._focusedElement && Cu.isDeadWrapper(this._focusedElement))
       this._focusedElement = null;
 
     return this._focusedElement;
@@ -90,32 +88,23 @@ let FormAssistant = {
     let focusedElement = this.focusedElement;
     let target = evt.target;
 
     switch (evt.type) {
       case "focus":
         if (this.isTextInputElement(target) && this.isIMEDisabled())
           return;
 
-        if (target && this.isFocusableElement(target)) {
-          if (this.blurTimeout) {
-            this.blurTimeout = content.clearTimeout(this.blurTimeout);
-            this.handleIMEStateDisabled();
-          }
+        if (target && this.isFocusableElement(target))
           this.handleIMEStateEnabled(target);
-        }
         break;
 
       case "blur":
-        if (this.focusedElement) {
-          this.blurTimeout = content.setTimeout(function () {
-            this.blurTimeout = null;
-            this.handleIMEStateDisabled();
-          }.bind(this), FOCUS_CHANGE_DELAY);
-        }
+        if (this.focusedElement)
+          this.handleIMEStateDisabled();
         break;
 
       case 'mousedown':
         // We only listen for this event on the currently focused element.
         // When the mouse goes down, note the cursor/selection position
         this.selectionStart = this.focusedElement.selectionStart;
         this.selectionEnd = this.focusedElement.selectionEnd;
         break;
--- a/b2g/chrome/content/payment.js
+++ b/b2g/chrome/content/payment.js
@@ -19,25 +19,35 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsIMessageSender");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
 const kClosePaymentFlowEvent = "close-payment-flow-dialog";
 
+let requestId;
+
 function paymentSuccess(aResult) {
   closePaymentFlowDialog(function notifySuccess() {
-    cpmm.sendAsyncMessage("Payment:Success", { result: aResult });
+    if (!requestId) {
+      return;
+    }
+    cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
+                                               requestId: requestId });
   });
 }
 
 function paymentFailed(aErrorMsg) {
   closePaymentFlowDialog(function notifyError() {
-    cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg });
+    if (!requestId) {
+      return;
+    }
+    cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg,
+                                              requestId: requestId });
   });
 }
 
 function closePaymentFlowDialog(aCallback) {
   // After receiving the payment provider confirmation about the
   // successful or failed payment flow, we notify the UI to close the
   // payment flow dialog and return to the caller application.
   let randomId = uuidgen.generateUUID().toString();
@@ -70,12 +80,18 @@ function closePaymentFlowDialog(aCallbac
     let glue = Cc["@mozilla.org/payment/ui-glue;1"]
                  .createInstance(Ci.nsIPaymentUIGlue);
     glue.cleanup();
   });
 
   browser.shell.sendChromeEvent(detail);
 }
 
+// We save the identifier of the DOM request, so we can dispatch the results
+// of the payment flow to the appropriate content process.
+addMessageListener("Payment:LoadShim", function receiveMessage(aMessage) {
+  requestId = aMessage.json.requestId;
+});
+
 addEventListener("DOMContentLoaded", function(e) {
   content.wrappedJSObject.paymentSuccess = paymentSuccess;
   content.wrappedJSObject.paymentFailed = paymentFailed;
 });
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -123,24 +123,24 @@ SettingsListener.observe('language.curre
 
   if (shell.hasStarted() == false) {
     shell.start();
   }
 });
 
 // =================== RIL ====================
 (function RILSettingsToPrefs() {
-  let strPrefs = ['ril.data.mmsc', 'ril.data.mmsproxy'];
+  let strPrefs = ['ril.mms.mmsc', 'ril.mms.mmsproxy'];
   strPrefs.forEach(function(key) {
     SettingsListener.observe(key, "", function(value) {
       Services.prefs.setCharPref(key, value);
     });
   });
 
-  ['ril.data.mmsport'].forEach(function(key) {
+  ['ril.mms.mmsport'].forEach(function(key) {
     SettingsListener.observe(key, null, function(value) {
       if (value != null) {
         Services.prefs.setIntPref(key, value);
       }
     });
   });
 
   SettingsListener.observe('ril.sms.strict7BitEncoding.enabled', false,
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -68,16 +68,20 @@ XPCOMUtils.defineLazyGetter(this, "libcu
   return libcutils;
 });
 #endif
 
 function getContentWindow() {
   return shell.contentBrowser.contentWindow;
 }
 
+function debug(str) {
+  dump(' -*- Shell.js: ' + str + '\n');
+}
+
 var shell = {
 
   get CrashSubmit() {
     delete this.CrashSubmit;
     Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
     return this.CrashSubmit;
   },
 
@@ -606,106 +610,133 @@ var CustomEventManager = {
 var AlertsHelper = {
   _listeners: {},
   _count: 0,
 
   handleEvent: function alert_handleEvent(detail) {
     if (!detail || !detail.id)
       return;
 
-    let listener = this._listeners[detail.id];
+    let uid = detail.id;
+    let listener = this._listeners[uid];
     if (!listener)
      return;
 
     let topic = detail.type == "desktop-notification-click" ? "alertclickcallback"
                                                             : "alertfinished";
 
-    if (detail.id.startsWith("alert")) {
-      listener.observer.observe(null, topic, listener.cookie);
-    } else {
-      listener.mm.sendAsyncMessage("app-notification-return",
-                                   { id: detail.id,
-                                     type: detail.type });
+    if (uid.startsWith("app-notif")) {
+      listener.mm.sendAsyncMessage("app-notification-return", {
+        uid: uid,
+        topic: topic,
+        target: listener.target
+      });
+    } else if (uid.startsWith("alert")) {
+      try {
+        listener.observer.observe(null, topic, listener.cookie);
+      } catch (e) { }
     }
 
     // we're done with this notification
-    if (topic === "alertfinished")
-      delete this._listeners[detail.id];
+    if (topic === "alertfinished") {
+      delete this._listeners[uid];
+    }
   },
 
   registerListener: function alert_registerListener(cookie, alertListener) {
-    let id = "alert" + this._count++;
-    this._listeners[id] = { observer: alertListener, cookie: cookie };
-    return id;
+    let uid = "alert" + this._count++;
+    this._listeners[uid] = { observer: alertListener, cookie: cookie };
+    return uid;
   },
 
-  registerAppListener: function alertRegisterAppListener(id, mm, title, text,
-                                                         manifestURL, imageURL) {
-    this._listeners[id] = {
-      mm: mm,
-      title: title,
-      text: text,
-      manifestURL: manifestURL,
-      imageURL: imageURL
-    };
+  registerAppListener: function alert_registerAppListener(uid, listener) {
+    this._listeners[uid] = listener;
+
+    let app = DOMApplicationRegistry.getAppByManifestURL(listener.manifestURL);
+    DOMApplicationRegistry.getManifestFor(app.origin, function(manifest) {
+      let helper = new ManifestHelper(manifest, app.origin);
+      let getNotificationURLFor = function(messages) {
+        if (!messages)
+          return null;
+
+        for (let i = 0; i < messages.length; i++) {
+          let message = messages[i];
+          if (message === "notification") {
+            return helper.fullLaunchPath();
+          } else if ("notification" in message) {
+            return helper.resolveFromOrigin(message["notification"]);
+          }
+        }
+      }
+
+      listener.target = getNotificationURLFor(manifest.messages);
+
+      // Bug 816944 - Support notification messages for entry_points.
+    });
   },
 
   showNotification: function alert_showNotification(imageUrl,
                                                     title,
                                                     text,
                                                     textClickable,
                                                     cookie,
-                                                    id,
+                                                    uid,
                                                     name,
                                                     manifestUrl) {
     function send(appName, appIcon) {
       shell.sendChromeEvent({
         type: "desktop-notification",
-        id: id,
+        id: uid,
         icon: imageUrl,
         title: title,
         text: text,
         appName: appName,
         appIcon: appIcon
       });
     }
 
+    if (!manifestUrl || !manifestUrl.length) {
+      send(null, null);
+    }
+
     // If we have a manifest URL, get the icon and title from the manifest
     // to prevent spoofing.
-    if (manifestUrl && manifestUrl.length) {
-      let app = DOMApplicationRegistry.getAppByManifestURL(manifestUrl);
-      DOMApplicationRegistry.getManifestFor(app.origin, function(aManifest) {
-        let helper = new ManifestHelper(aManifest, app.origin);
-        send(helper.name, helper.iconURLForSize(128));
-      });
-    } else {
-      send(null, null);
-    }
+    let app = DOMApplicationRegistry.getAppByManifestURL(manifestUrl);
+    DOMApplicationRegistry.getManifestFor(app.origin, function(aManifest) {
+      let helper = new ManifestHelper(aManifest, app.origin);
+      send(helper.name, helper.iconURLForSize(128));
+    });
   },
 
   showAlertNotification: function alert_showAlertNotification(imageUrl,
                                                               title,
                                                               text,
                                                               textClickable,
                                                               cookie,
                                                               alertListener,
                                                               name) {
-    let id = this.registerListener(null, alertListener);
+    let uid = this.registerListener(null, alertListener);
     this.showNotification(imageUrl, title, text, textClickable, cookie,
-                          id, name, null);
+                          uid, name, null);
   },
 
   receiveMessage: function alert_receiveMessage(message) {
     let data = message.data;
+    let listener = {
+      mm: message.target,
+      title: data.title,
+      text: data.text,
+      manifestURL: data.manifestURL,
+      imageURL: data.imageURL
+    }
+    this.registerAppListener(data.uid, listener);
 
-    this.registerAppListener(data.id, message.target, data.title, data.text,
-                             data.manifestURL, data.imageURL);
     this.showNotification(data.imageURL, data.title, data.text,
                           data.textClickable, null,
-                          data.id, null, data.manifestURL);
+                          data.uid, null, data.manifestURL);
   },
 }
 
 var WebappsHelper = {
   _installers: {},
   _count: 0,
 
   init: function webapps_init() {
--- a/b2g/components/AlertsService.js
+++ b/b2g/components/AlertsService.js
@@ -4,28 +4,39 @@
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cc = Components.classes;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
+                                   "@mozilla.org/system-message-internal;1",
+                                   "nsISystemMessagesInternal");
+
+XPCOMUtils.defineLazyServiceGetter(this, "uuidGenerator",
+                                   "@mozilla.org/uuid-generator;1",
+                                   "nsIUUIDGenerator");
+
 XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
   return Cc["@mozilla.org/childprocessmessagemanager;1"]
            .getService(Ci.nsIMessageSender);
 });
 
+function debug(str) {
+  dump("=*= AlertsService.js : " + str + "\n");
+}
+
 // -----------------------------------------------------------------------
 // Alerts Service
 // -----------------------------------------------------------------------
 
 function AlertsService() {
   cpmm.addMessageListener("app-notification-return", this);
-  this._id = 0;
 }
 
 AlertsService.prototype = {
   classID: Components.ID("{fe33c107-82a4-41d6-8c64-5353267e04c9}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService,
                                          Ci.nsIAppNotificationService]),
 
   // nsIAlertsService
@@ -38,49 +49,73 @@ AlertsService.prototype = {
                                                         aName) {
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     browser.AlertsHelper.showAlertNotification(aImageUrl, aTitle, aText,
                                                aTextClickable, aCookie,
                                                aAlertListener, aName);
   },
 
   // nsIAppNotificationService
-  _listeners: [],
-
-  receiveMessage: function receiveMessage(aMessage) {
-    let data = aMessage.data;
-    if (aMessage.name !== "app-notification-return" ||
-        !this._listeners[data.id]) {
-      return;
-    }
-
-    let obs = this._listeners[data.id];
-    let topic = data.type == "desktop-notification-click" ? "alertclickcallback"
-                                                          : "alertfinished";
-    obs.observe(null, topic, null);
-
-    // we're done with this notification
-    if (topic === "alertfinished")
-      delete this._listeners[data.id];
-  },
-
-  // This method is called in the content process, so we remote the call
-  // to shell.js
   showAppNotification: function showAppNotification(aImageURL,
                                                     aTitle,
                                                     aText,
                                                     aTextClickable,
                                                     aManifestURL,
                                                     aAlertListener) {
-    let id = "app-notif" + this._id++;
-    this._listeners[id] = aAlertListener;
+    let uid = "app-notif-" + uuidGenerator.generateUUID();
+
+    this._listeners[uid] = {
+      observer: aAlertListener,
+      title: aTitle,
+      text: aText,
+      manifestURL: aManifestURL,
+      imageURL: aImageURL
+    };
+
     cpmm.sendAsyncMessage("app-notification-send", {
       imageURL: aImageURL,
       title: aTitle,
       text: aText,
       textClickable: aTextClickable,
       manifestURL: aManifestURL,
-      id: id
+      uid: uid
     });
+  },
+
+  // AlertsService.js custom implementation
+  _listeners: [],
+
+  receiveMessage: function receiveMessage(aMessage) {
+    let data = aMessage.data;
+    let listener = this._listeners[data.uid];
+    if (aMessage.name !== "app-notification-return" || !listener) {
+      return;
+    }
+
+    let topic = data.topic;
+
+    try {
+      listener.observer.observe(null, topic, null);
+    } catch (e) {
+      // It seems like there is no callbacks anymore, forward the click on
+      // notification via a system message containing the title/text/icon of
+      // the notification so the app get a change to react.
+      if (data.target) {
+        gSystemMessenger.sendMessage("notification", {
+            title: listener.title,
+            body: listener.text,
+            imageURL: listener.imageURL
+          },
+          Services.io.newURI(data.target, null, null),
+          Services.io.newURI(listener.manifestURL, null, null));
+      }
+
+      cpmm.sendAsyncMessage("app-notification-sysmsg-request", listener);
+    }
+
+    // we're done with this notification
+    if (topic === "alertfinished") {
+      delete this._listeners[data.uid];
+    }
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AlertsService]);
--- a/b2g/components/PaymentGlue.js
+++ b/b2g/components/PaymentGlue.js
@@ -25,22 +25,23 @@ function debug (s) {
   //dump("-*- PaymentGlue: " + s + "\n");
 };
 
 function PaymentUI() {
 }
 
 PaymentUI.prototype = {
 
-  confirmPaymentRequest: function confirmPaymentRequest(aRequests,
+  confirmPaymentRequest: function confirmPaymentRequest(aRequestId,
+                                                        aRequests,
                                                         aSuccessCb,
                                                         aErrorCb) {
     let _error = function _error(errorMsg) {
       if (aErrorCb) {
-        aErrorCb.onresult(errorMsg);
+        aErrorCb.onresult(aRequestId, errorMsg);
       }
     };
 
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     let content = browser.getContentWindow();
     if (!content) {
       _error("NO_CONTENT_WINDOW");
       return;
@@ -61,33 +62,33 @@ PaymentUI.prototype = {
     // based on the selected payment provider.
     content.addEventListener("mozContentEvent", function handleSelection(evt) {
       let msg = evt.detail;
       if (msg.id != id) {
         return;
       }
 
       if (msg.userSelection && aSuccessCb) {
-        aSuccessCb.onresult(msg.userSelection);
+        aSuccessCb.onresult(aRequestId, msg.userSelection);
       } else if (msg.errorMsg) {
         _error(msg.errorMsg);
       }
 
       content.removeEventListener("mozContentEvent", handleSelection);
     });
 
     browser.shell.sendChromeEvent(detail);
   },
 
-  showPaymentFlow: function showPaymentFlow(aPaymentFlowInfo, aErrorCb) {
-    debug("showPaymentFlow. uri " + aPaymentFlowInfo.uri);
-
+  showPaymentFlow: function showPaymentFlow(aRequestId,
+                                            aPaymentFlowInfo,
+                                            aErrorCb) {
     let _error = function _error(errorMsg) {
       if (aErrorCb) {
-        aErrorCb.onresult(errorMsg);
+        aErrorCb.onresult(aRequestId, errorMsg);
       }
     };
 
     // We ask the UI to browse to the selected payment flow.
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     let content = browser.getContentWindow();
     if (!content) {
       _error("NO_CONTENT_WINDOW");
@@ -119,16 +120,17 @@ PaymentUI.prototype = {
         return;
       }
       let frame = evt.detail.frame;
       let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner)
                         .frameLoader;
       let mm = frameLoader.messageManager;
       try {
         mm.loadFrameScript(kPaymentShimFile, true);
+        mm.sendAsyncMessage("Payment:LoadShim", { requestId: aRequestId });
       } catch (e) {
         debug("Error loading " + kPaymentShimFile + " as a frame script: " + e);
         _error("ERROR_LOADING_PAYMENT_SHIM");
       } finally {
         content.removeEventListener("mozContentEvent", loadPaymentShim);
       }
     }).bind(this));
 
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -155,16 +155,17 @@
 @BINPATH@/components/dom_activities.xpt
 @BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_base.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_gonk.xpt
 @BINPATH@/components/dom_icc.xpt
+@BINPATH@/components/dom_cellbroadcast.xpt
 #endif
 #ifdef MOZ_B2G_FM
 @BINPATH@/components/dom_fm.xpt
 #endif
 @BINPATH@/components/dom_battery.xpt
 #ifdef MOZ_B2G_BT
 @BINPATH@/components/dom_bluetooth.xpt
 #endif
@@ -372,20 +373,16 @@
 #ifdef MOZ_GTK2
 @BINPATH@/components/nsFilePicker.manifest
 @BINPATH@/components/nsFilePicker.js
 #endif
 @BINPATH@/components/nsHelperAppDlg.manifest
 @BINPATH@/components/nsHelperAppDlg.js
 @BINPATH@/components/nsDownloadManagerUI.manifest
 @BINPATH@/components/nsDownloadManagerUI.js
-@BINPATH@/components/NetworkGeolocationProvider.manifest
-@BINPATH@/components/NetworkGeolocationProvider.js
-@BINPATH@/components/GPSDGeolocationProvider.manifest
-@BINPATH@/components/GPSDGeolocationProvider.js
 @BINPATH@/components/nsSidebar.manifest
 @BINPATH@/components/nsSidebar.js
 #ifndef MOZ_WIDGET_GONK
 @BINPATH@/components/extensions.manifest
 @BINPATH@/components/addonManager.js
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amWebInstallListener.js
 @BINPATH@/components/nsBlocklistService.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1036,17 +1036,17 @@ pref("devtools.debugger.ui.height", 250)
 pref("devtools.debugger.ui.win-x", 0);
 pref("devtools.debugger.ui.win-y", 0);
 pref("devtools.debugger.ui.win-width", 900);
 pref("devtools.debugger.ui.win-height", 400);
 pref("devtools.debugger.ui.stackframes-width", 200);
 pref("devtools.debugger.ui.variables-width", 300);
 pref("devtools.debugger.ui.panes-visible-on-startup", false);
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
-pref("devtools.debugger.ui.variables-non-enum-visible", true);
+pref("devtools.debugger.ui.variables-only-enum-visible", false);
 pref("devtools.debugger.ui.variables-searchbox-visible", false);
 
 // Enable the Tilt inspector
 pref("devtools.tilt.enabled", true);
 pref("devtools.tilt.intro_transition", true);
 pref("devtools.tilt.outro_transition", true);
 
 // Enable the Scratchpad tool.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7048,16 +7048,21 @@ let gPrivateBrowsingUI = {
       let docElement = document.documentElement;
       docElement.setAttribute("title",
         docElement.getAttribute("title_privatebrowsing"));
       docElement.setAttribute("titlemodifier",
         docElement.getAttribute("titlemodifier_privatebrowsing"));
       docElement.setAttribute("privatebrowsingmode", "temporary");
       gBrowser.updateTitlebar();
     }
+
+    if (gURLBar) {
+      // Disable switch to tab autocompletion for private windows
+      gURLBar.setAttribute("autocompletesearchparam", "");
+    }
   }
 };
 
 #else
 
 let gPrivateBrowsingUI = {
   _privateBrowsingService: null,
   _searchBarValue: null,
@@ -7305,16 +7310,25 @@ let gPrivateBrowsingUI = {
  * @param aOpenNew
  *        True to open a new tab and switch to it, if no existing tab is found.
  *        If no suitable window is found, a new one will be opened.
  * @return True if an existing tab was found, false otherwise
  */
 function switchToTabHavingURI(aURI, aOpenNew) {
   // This will switch to the tab in aWindow having aURI, if present.
   function switchIfURIInWindow(aWindow) {
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    // Only switch to the tab if neither the source and desination window are
+    // private.
+    if (PrivateBrowsingUtils.isWindowPrivate(window) ||
+        PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
+      return false;
+    }
+#endif
+
     let browsers = aWindow.gBrowser.browsers;
     for (let i = 0; i < browsers.length; i++) {
       let browser = browsers[i];
       if (browser.currentURI.equals(aURI)) {
         // Focus the matching window & tab
         aWindow.focus();
         aWindow.gBrowser.tabContainer.selectedIndex = i;
         return true;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -867,17 +867,18 @@
       </toolbarbutton>
 #endif
 
       <tabs id="tabbrowser-tabs"
             class="tabbrowser-tabs"
             tabbrowser="content"
             flex="1"
             setfocus="false"
-            tooltip="tabbrowser-tab-tooltip">
+            tooltip="tabbrowser-tab-tooltip"
+            stopwatchid="FX_TAB_CLICK_MS">
         <tab class="tabbrowser-tab" selected="true" fadein="true"/>
       </tabs>
 
       <toolbarbutton id="new-tab-button"
                      class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&tabCmd.label;"
                      command="cmd_newNavigatorTab"
                      onclick="checkForMiddleClick(this, event);"
--- a/browser/base/content/macBrowserOverlay.xul
+++ b/browser/base/content/macBrowserOverlay.xul
@@ -21,18 +21,18 @@
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
 # All JS files which are not content (only) dependent that browser.xul
 # wishes to include *must* go into the global-scripts.inc file
 # so that they can be shared by this overlay.
 #include global-scripts.inc
 
 <script type="application/javascript">
-  function OpenBrowserWindowFromDockMenu() {
-    let win = OpenBrowserWindow();
+  function OpenBrowserWindowFromDockMenu(options) {
+    let win = OpenBrowserWindow(options);
     win.addEventListener("load", function listener() {
       win.removeEventListener("load", listener);
       let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
         .getService(Ci.nsIMacDockSupport);
       dockSupport.activateApplication(true);
     });
 
     return win;
@@ -51,12 +51,15 @@
 #include browser-menubar.inc
 
 <!-- Dock menu -->
 <popupset>
   <menupopup id="menu_mac_dockmenu">
     <!-- The command cannot be cmd_newNavigator because we need to activate
          the application. -->
     <menuitem label="&newNavigatorCmd.label;" oncommand="OpenBrowserWindowFromDockMenu();" />
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    <menuitem label="&newPrivateWindow.label;" oncommand="OpenBrowserWindowFromDockMenu({private: true});" />
+#endif
   </menupopup>
 </popupset>
 
 </overlay>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -619,17 +619,23 @@
                     !(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE))
                   this.mBrowser.mIconURL = null;
 
                 let autocomplete = this.mTabBrowser._placesAutocomplete;
                 if (this.mBrowser.registeredOpenURI) {
                   autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
                   delete this.mBrowser.registeredOpenURI;
                 }
-                if (!isBlankPageURL(aLocation.spec)) {
+                if (!isBlankPageURL(aLocation.spec)
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+                    // Tabs in private windows aren't registered as "Open" so
+                    // that they don't appear as switch-to-tab candidates.
+                    && !PrivateBrowsingUtils.isWindowPrivate(window)
+#endif
+                    ) {
                   autocomplete.registerOpenPage(aLocation);
                   this.mBrowser.registeredOpenURI = aLocation;
                 }
               }
 
               if (!this.mBlank) {
                 this._callProgressListeners("onLocationChange",
                                             [aWebProgress, aRequest, aLocation,
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -195,17 +195,16 @@ endif
                  browser_clearplugindata.html \
                  browser_clearplugindata_noage.html \
                  browser_popupUI.js \
                  browser_sanitizeDialog.js \
                  browser_save_video.js \
                  bug564387.html \
                  bug564387_video1.ogv \
                  bug564387_video1.ogv^headers^ \
-                 browser_save_private_link.js \
                  bug792517.html \
                  bug792517-2.html \
                  bug792517.sjs \
                  browser_scope.js \
                  browser_selectTabAtIndex.js \
                  browser_tab_dragdrop.js \
                  browser_tab_dragdrop2.js \
                  browser_tab_dragdrop2_frame1.xul \
@@ -288,16 +287,18 @@ endif
                  browser_social_chatwindow.js \
                  social_panel.html \
                  social_share_image.png \
                  social_sidebar.html \
                  social_chat.html \
                  social_flyout.html \
                  social_window.html \
                  social_worker.js \
+                 browser_bug676619.js \
+                 download_page.html \
                  $(NULL)
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _BROWSER_FILES += \
 		browser_bug462289.js \
 		$(NULL)
 else
 _BROWSER_FILES += \
@@ -308,23 +309,26 @@ else
 # 		browser_maconly_carbon_mismatch_plugin.js \
 
 endif
 
 ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
 _BROWSER_FILES += \
                 browser_bug763468_perwindowpb.js \
                 browser_bug767836_perwindowpb.js \
+                browser_bug816527.js \
                 browser_private_browsing_window.js \
                 browser_save_link-perwindowpb.js \
+                browser_save_private_link_perwindowpb.js \
                 $(NULL)
 else
 _BROWSER_FILES += \
                 browser_bug763468.js \
                 browser_bug767836.js \
                 browser_save_link.js \
+                browser_save_private_link.js \
                 $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug676619.js
@@ -0,0 +1,109 @@
+function test () {
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function () {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    let doc = gBrowser.contentDocument;
+
+
+    function testLocation(link, url, next) {
+      var tabOpenListener = new TabOpenListener(url, function () {
+          gBrowser.removeTab(this.tab);
+      }, function () {
+        next();
+      });
+
+      doc.getElementById(link).click();
+    }
+
+    function testLink(link, name, next) {
+        addWindowListener("chrome://mozapps/content/downloads/unknownContentType.xul", function (win) {
+            is(win.document.getElementById("location").value, name, "file name should match");
+            win.close();
+            next();
+        });
+
+        doc.getElementById(link).click();
+    }
+
+    testLink("link1", "test.txt",
+      testLink.bind(null, "link2", "video.ogg",
+        testLink.bind(null, "link3", "just some video",
+          testLink.bind(null, "link4", "with-target.txt",
+            testLink.bind(null, "link5", "javascript.txt",
+              testLink.bind(null, "link6", "test.blob",
+                testLocation.bind(null, "link7", "http://example.com/",
+                  function () {
+                    gBrowser.removeCurrentTab();
+                    finish();
+                  })))))));
+
+  }, true);
+
+  content.location = "http://mochi.test:8888/browser/browser/base/content/test/download_page.html";
+}
+
+
+function addWindowListener(aURL, aCallback) {
+  Services.wm.addListener({
+    onOpenWindow: function(aXULWindow) {
+      info("window opened, waiting for focus");
+      Services.wm.removeListener(this);
+
+      var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                .getInterface(Ci.nsIDOMWindow);
+      waitForFocus(function() {
+        is(domwindow.document.location.href, aURL, "should have seen the right window open");
+        aCallback(domwindow);
+      }, domwindow);
+    },
+    onCloseWindow: function(aXULWindow) { },
+    onWindowTitleChange: function(aXULWindow, aNewTitle) { }
+  });
+}
+
+// This listens for the next opened tab and checks it is of the right url.
+// opencallback is called when the new tab is fully loaded
+// closecallback is called when the tab is closed
+function TabOpenListener(url, opencallback, closecallback) {
+  this.url = url;
+  this.opencallback = opencallback;
+  this.closecallback = closecallback;
+
+  gBrowser.tabContainer.addEventListener("TabOpen", this, false);
+}
+
+TabOpenListener.prototype = {
+  url: null,
+  opencallback: null,
+  closecallback: null,
+  tab: null,
+  browser: null,
+
+  handleEvent: function(event) {
+    if (event.type == "TabOpen") {
+      gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
+      this.tab = event.originalTarget;
+      this.browser = this.tab.linkedBrowser;
+      gBrowser.addEventListener("pageshow", this, false);
+    } else if (event.type == "pageshow") {
+      if (event.target.location.href != this.url)
+        return;
+      gBrowser.removeEventListener("pageshow", this, false);
+      this.tab.addEventListener("TabClose", this, false);
+      var url = this.browser.contentDocument.location.href;
+      is(url, this.url, "Should have opened the correct tab");
+      this.opencallback(this.tab, this.browser.contentWindow);
+    } else if (event.type == "TabClose") {
+      if (event.originalTarget != this.tab)
+        return;
+      this.tab.removeEventListener("TabClose", this, false);
+      this.opencallback = null;
+      this.tab = null;
+      this.browser = null;
+      // Let the window close complete
+      executeSoon(this.closecallback);
+      this.closecallback = null;
+    }
+  }
+};
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug816527.js
@@ -0,0 +1,118 @@
+/* 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/. */
+
+function test() {
+  waitForExplicitFinish();
+
+  let testURL = "http://example.org/browser/browser/base/content/test/dummy_page.html";
+
+  function testOnWindow(aOptions, aCallback) {
+    whenNewWindowLoaded(aOptions, function(aWin) {
+      // execute should only be called when need, like when you are opening
+      // web pages on the test. If calling executeSoon() is not necesary, then
+      // call whenNewWindowLoaded() instead of testOnWindow() on your test.
+      executeSoon(function() aCallback(aWin));
+    });
+  };
+
+  testOnWindow({}, function(aNormalWindow) {
+    testOnWindow({private: true}, function(aPrivateWindow) {
+      runTest(aNormalWindow, aPrivateWindow, function() {
+        aNormalWindow.close();
+        aPrivateWindow.close();
+        testOnWindow({}, function(aNormalWindow) {
+          testOnWindow({private: true}, function(aPrivateWindow) {
+            runTest(aPrivateWindow, aNormalWindow, function() {
+              aNormalWindow.close();
+              aPrivateWindow.close();
+              testOnWindow({private: true}, function(aPrivateWindow) {
+                runTest(aPrivateWindow, aPrivateWindow, function() {
+                  aPrivateWindow.close();
+                  testOnWindow({}, function(aNormalWindow) {
+                    runTest(aNormalWindow, aNormalWindow, function() {
+                      aNormalWindow.close();
+                      finish();
+                    });
+                  });
+                });
+              });
+            });
+          });
+        });
+      });
+    });
+  });
+
+  function runTest(aSourceWindow, aDestWindow, aCallback) {
+    // Open the base tab
+    let baseTab = aSourceWindow.gBrowser.addTab(testURL);
+    baseTab.linkedBrowser.addEventListener("load", function() {
+      // Wait for the tab to be fully loaded so matching happens correctly
+      if (baseTab.linkedBrowser.currentURI.spec == "about:blank")
+        return;
+      baseTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+      let testTab = aDestWindow.gBrowser.addTab();
+
+      waitForFocus(function() {
+        // Select the testTab
+        aDestWindow.gBrowser.selectedTab = testTab;
+
+        // Ensure that this tab has no history entries
+        ok(testTab.linkedBrowser.sessionHistory.count < 2,
+           "The test tab has 1 or less history entries");
+        // Ensure that this tab is on about:blank
+        is(testTab.linkedBrowser.currentURI.spec, "about:blank",
+           "The test tab is on about:blank");
+        // Ensure that this tab's document has no child nodes
+        ok(!testTab.linkedBrowser.contentDocument.body.hasChildNodes(),
+           "The test tab has no child nodes");
+        ok(!testTab.hasAttribute("busy"),
+           "The test tab doesn't have the busy attribute");
+
+        // Set the urlbar to include the moz-action
+        aDestWindow.gURLBar.value = "moz-action:switchtab," + testURL;
+        // Focus the urlbar so we can press enter
+        aDestWindow.gURLBar.focus();
+
+        // We want to see if the switchtab action works.  If it does, the
+        // current tab will get closed, and that's what we detect with the
+        // TabClose handler.  If pressing enter triggers a load in that tab,
+        // then the load handler will get called.  Neither of these are
+        // the desired effect here.  So if the test goes successfully, it is
+        // the timeout handler which gets called.
+        //
+        // The reason that we can't avoid the timeout here is because we are
+        // trying to test something which should not happen, so we just need
+        // to wait for a while and then check whether any bad things have
+        // happened.
+
+        function onTabClose(aEvent) {
+          aDestWindow.gBrowser.tabContainer.removeEventListener("TabClose", onTabClose, false);
+          aDestWindow.gBrowser.removeEventListener("load", onLoad, false);
+          // Should only happen when we expect success
+          ok(false, "Tab closed as expected");
+          aCallback();
+        }
+        function onLoad(aEvent) {
+          aDestWindow.gBrowser.tabContainer.removeEventListener("TabClose", onTabClose, false);
+          aDestWindow.gBrowser.removeEventListener("load", onLoad, false);
+          // Should only happen when we expect success
+          ok(false, "Tab loaded as expected");
+          aCallback();
+        }
+
+        aDestWindow.gBrowser.tabContainer.addEventListener("TabClose", onTabClose, false);
+        aDestWindow.gBrowser.addEventListener("load", onLoad, false);
+        setTimeout(function() {
+          aCallback();
+        }, 500);
+
+        // Press enter!
+        EventUtils.synthesizeKey("VK_RETURN", {});
+      }, aDestWindow);
+    }, true);
+  }
+}
+
copy from browser/base/content/test/browser_save_private_link.js
copy to browser/base/content/test/browser_save_private_link_perwindowpb.js
--- a/browser/base/content/test/browser_save_private_link.js
+++ b/browser/base/content/test/browser_save_private_link_perwindowpb.js
@@ -1,67 +1,38 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-var MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init();
-
-function checkDiskCacheFor(filename) {
-  let visitor = {
-    visitDevice: function(deviceID, deviceInfo) {
-      if (deviceID == "disk")
-        info(deviceID + " device contains " + deviceInfo.entryCount + " entries");
-      return deviceID == "disk";
-    },
-    
-    visitEntry: function(deviceID, entryInfo) {
-      info(entryInfo.key);
-      is(entryInfo.key.contains(filename), false, "web content present in disk cache");
-    }
-  };
-  cache.visitEntries(visitor);
-}
-
-var cache = Cc["@mozilla.org/network/cache-service;1"]
+/* 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/. */
+function test() {
+  // initialization
+  waitForExplicitFinish();
+  let windowsToClose = [];
+  let testURI = "http://mochi.test:8888/browser/browser/base/content/test/bug792517.html";
+  let fileName;
+  let MockFilePicker = SpecialPowers.MockFilePicker;
+  let cache = Cc["@mozilla.org/network/cache-service;1"]
               .getService(Ci.nsICacheService);
 
-function test() {
-  waitForExplicitFinish();
-  var fileName;
-
-  gPrefService.setBoolPref("browser.privatebrowsing.keep_current_session", true);
-  let pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
-  pb.privateBrowsingEnabled = true;
-
-  gBrowser.loadURI("http://mochi.test:8888/browser/browser/base/content/test/bug792517.html");
+  function checkDiskCacheFor(filename) {
+    let visitor = {
+      visitDevice: function(deviceID, deviceInfo) {
+        if (deviceID == "disk")
+          info(deviceID + " device contains " + deviceInfo.entryCount + " entries");
+        return deviceID == "disk";
+      },
 
-  registerCleanupFunction(function () {
-    pb.privateBrowsingEnabled = false;
-    gPrefService.clearUserPref("browser.privatebrowsing.keep_current_session");
-    gBrowser.addTab();
-    gBrowser.removeCurrentTab();
-  });
-
-  gBrowser.addEventListener("pageshow", function pageShown(event) {
-    if (event.target.location == "about:blank")
-      return;
-    gBrowser.removeEventListener("pageshow", pageShown);
+      visitEntry: function(deviceID, entryInfo) {
+        info(entryInfo.key);
+        is(entryInfo.key.contains(filename), false, "web content present in disk cache");
+      }
+    };
+    cache.visitEntries(visitor);
+  }
 
-    executeSoon(function () {
-      document.addEventListener("popupshown", contextMenuOpened);
-
-      var img = gBrowser.contentDocument.getElementById("img");
-      EventUtils.synthesizeMouseAtCenter(img,
-                                         { type: "contextmenu", button: 2 },
-                                         gBrowser.contentWindow);
-    });
-  });
-
-  function contextMenuOpened(event) {
+  function contextMenuOpened(aWindow, event) {
     cache.evictEntries(Ci.nsICache.STORE_ANYWHERE);
 
     event.currentTarget.removeEventListener("popupshown", contextMenuOpened);
 
     // Create the folder the image will be saved into.
     var destDir = createTemporarySaveDirectory();
     var destFile = destDir.clone();
 
@@ -78,39 +49,81 @@ function test() {
 
     registerCleanupFunction(function () {
       mockTransferRegisterer.unregister();
       MockFilePicker.cleanup();
       destDir.remove(true);
     });
 
     // Select "Save Image As" option from context menu
-    var saveVideoCommand = document.getElementById("context-saveimage");
+    var saveVideoCommand = aWindow.document.getElementById("context-saveimage");
     saveVideoCommand.doCommand();
 
     event.target.hidePopup();
   }
 
   function onTransferComplete(downloadSuccess) {
     ok(downloadSuccess, "Image file should have been downloaded successfully");
 
     // Give the request a chance to finish and create a cache entry
     executeSoon(function() {
       checkDiskCacheFor(fileName);
       finish();
     });
   }
+
+  function createTemporarySaveDirectory() {
+    var saveDir = Cc["@mozilla.org/file/directory_service;1"]
+                    .getService(Ci.nsIProperties)
+                    .get("TmpD", Ci.nsIFile);
+    saveDir.append("testsavedir");
+    if (!saveDir.exists())
+      saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+    return saveDir;
+  }
+
+  function doTest(aIsPrivateMode, aWindow, aCallback) {
+    aWindow.gBrowser.addEventListener("pageshow", function pageShown(event) {
+      if (event.target.location == "about:blank")
+        return;
+      aWindow.gBrowser.removeEventListener("pageshow", pageShown);
+
+      executeSoon(function () {
+        aWindow.document.addEventListener("popupshown",
+                                          function(e) contextMenuOpened(aWindow, e), false);
+        var img = aWindow.gBrowser.selectedBrowser.contentDocument.getElementById("img");
+        EventUtils.synthesizeMouseAtCenter(img,
+                                           { type: "contextmenu", button: 2 },
+                                           aWindow.gBrowser.contentWindow);
+      });
+    });
+
+    aWindow.gBrowser.selectedBrowser.loadURI(testURI);
+  }
+
+  function testOnWindow(aOptions, aCallback) {
+    whenNewWindowLoaded(aOptions, function(aWin) {
+      windowsToClose.push(aWin);
+      // execute should only be called when need, like when you are opening
+      // web pages on the test. If calling executeSoon() is not necesary, then
+      // call whenNewWindowLoaded() instead of testOnWindow() on your test.
+      executeSoon(function() aCallback(aWin));
+    });
+  };
+
+   // this function is called after calling finish() on the test.
+  registerCleanupFunction(function() {
+    windowsToClose.forEach(function(aWin) {
+      aWin.close();
+    });
+  });
+
+  MockFilePicker.init();
+  // then test when on private mode
+  testOnWindow({private: true}, function(aWin) {
+    doTest(true, aWin, finish);
+  });
 }
 
 Cc["@mozilla.org/moz/jssubscript-loader;1"]
   .getService(Ci.mozIJSSubScriptLoader)
   .loadSubScript("chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
                  this);
-
-function createTemporarySaveDirectory() {
-  var saveDir = Cc["@mozilla.org/file/directory_service;1"]
-                  .getService(Ci.nsIProperties)
-                  .get("TmpD", Ci.nsIFile);
-  saveDir.append("testsavedir");
-  if (!saveDir.exists())
-    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
-  return saveDir;
-}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/download_page.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=676619
+-->
+  <head>
+    <title>Test for the download attribute</title>
+
+  </head>
+  <body>
+    <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=676619">Bug 676619</a>
+    <br/>
+    <ul>
+        <li><a href="data:text/plain,Hey What are you looking for?"
+                download="test.txt" id="link1">Download "test.txt"</a></li>
+        <li><a href="http://mochi.test:8888/browser/browser/base/content/test/video.ogg"
+                download id="link2">Download "video.ogg"</a></li>
+        <li><a href="http://mochi.test:8888/browser/browser/base/content/test/video.ogg"
+                download="just some video" id="link3">Download "just some video"</a></li>
+        <li><a href="data:text/plain,test"
+                download="with-target.txt" id="link4">Download "with-target.txt"</a></li>
+        <li><a href="javascript:1+2"
+            download="javascript.txt" id="link5">Download "javascript.txt"</a></li>
+    </ul>
+    <script>
+        var li = document.createElement('li');
+        var a = document.createElement('a');
+
+        a.href = window.URL.createObjectURL(new Blob(["just text"]))    ;
+        a.download = "test.blob";
+        a.id = "link6";
+        a.textContent = 'Download "test.blob"';
+
+        li.appendChild(a);
+        document.getElementsByTagName('ul')[0].appendChild(li);
+    </script>
+    <ul>
+        <li><a href="http://example.com/"
+                download="example.com" id="link7" target="_blank">Download "example.com"</a></li>
+    <ul>
+  </body>
+</html>
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -11,19 +11,16 @@ Cc["@mozilla.org/moz/jssubscript-loader;
   .getService(Ci.mozIJSSubScriptLoader)
   .loadSubScript("chrome://browser/content/sanitize.js", tmp);
 
 let {NewTabUtils, Sanitizer} = tmp;
 
 let uri = Services.io.newURI("about:newtab", null, null);
 let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
 
-let sm = Services.domStorageManager;
-let storage = sm.getLocalStorageForPrincipal(principal, "");
-
 let gWindow = window;
 
 registerCleanupFunction(function () {
   while (gWindow.gBrowser.tabs.length > 1)
     gWindow.gBrowser.removeTab(gWindow.gBrowser.tabs[1]);
 
   Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
 });
@@ -180,17 +177,22 @@ function setPinnedLinks(aLinks) {
 
   if (typeof links == "string") {
     links = aLinks.split(/\s*,\s*/).map(function (id) {
       if (id)
         return {url: "http://example.com/#" + id, title: "site#" + id};
     });
   }
 
-  storage.setItem("pinnedLinks", JSON.stringify(links));
+  let string = Cc["@mozilla.org/supports-string;1"]
+                 .createInstance(Ci.nsISupportsString);
+  string.data = JSON.stringify(links);
+  Services.prefs.setComplexValue("browser.newtabpage.pinned",
+                                 Ci.nsISupportsString, string);
+
   NewTabUtils.pinnedLinks.resetCache();
   NewTabUtils.allPages.update();
 }
 
 /**
  * Restore the grid state.
  */
 function restore() {
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -91,12 +91,12 @@ richlistitem[type="download"]:not([selec
                                    [counter],
                                    [paused]))
                                            #downloads-indicator-progress-area
 
 {
   visibility: hidden;
 }
 
-#downloadsSummary:not([inprogress="true"]) #downloadsSummaryProgress,
-#downloadsSummary:not([inprogress="true"]) #downloadsSummaryDetails {
+#downloadsSummary:not([inprogress="true"]) > vbox > #downloadsSummaryProgress,
+#downloadsSummary:not([inprogress="true"]) > vbox > #downloadsSummaryDetails {
   display: none;
 }
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
@@ -59,22 +59,16 @@
                              .QueryInterface(Ci.nsIDocShellTreeItem)
                              .rootTreeItem
                              .QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIDOMWindow);
 
       // Focus the location bar
       mainWindow.focusAndSelectUrlBar();
 
-      function openSanitizeDialog() {
-        let browserGlue = Cc["@mozilla.org/browser/browserglue;1"].
-                          getService(Ci.nsIBrowserGlue);
-        browserGlue.sanitize(mainWindow);
-      }
-
       function setFavIcon(url) {
         var icon = document.createElement("link");
         icon.setAttribute("rel", "icon");
         icon.setAttribute("type", "image/png");
         icon.setAttribute("href", url);
         var head = document.getElementsByTagName("head")[0];
         head.insertBefore(icon, head.firstChild);
       }
--- a/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_openlocation.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_openlocation.js
@@ -14,20 +14,23 @@ function test() {
       switch (aTopic) {
         case "domwindowopened":
           let dialog = aSubject.QueryInterface(Ci.nsIDOMWindow);
           dialog.addEventListener("load", function () {
             dialog.removeEventListener("load", arguments.callee, false);
 
             let browser = aWindow.gBrowser.selectedBrowser;
             browser.addEventListener("load", function() {
+              // Ignore non-related loads (could be about:privatebrowsing for example, see bug 817932)
+              if (browser.currentURI.spec != url) {
+                return;
+              }
+
               browser.removeEventListener("load", arguments.callee, true);
 
-              is(browser.currentURI.spec, url,
-                 "The correct URL should be loaded via the open location dialog");
               executeSoon(callback);
             }, true);
 
             SimpleTest.waitForFocus(function() {
               let input = dialog.document.getElementById("dialog.input");
               is(input.value, autofilled, "The input field should be correctly auto-filled");
               input.focus();
               for (let i = 0; i < url.length; ++i)
--- a/browser/components/privatebrowsing/test/browser/perwindow/head.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/head.js
@@ -39,8 +39,17 @@ function newFileInDirectory(aDir) {
   file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_FILE);
   return file;
 }
 
 function clearHistory() {
   // simulate clearing the private data
   Services.obs.notifyObservers(null, "browser:purge-session-history", "");
 }
+
+function _initTest() {
+  // Don't use about:home as the homepage for new windows
+  Services.prefs.setIntPref("browser.startup.page", 0);
+  registerCleanupFunction(function() Services.prefs.clearUserPref("browser.startup.page"));
+}
+
+_initTest();
+
--- a/browser/components/search/test/browser_private_search_perwindowpb.js
+++ b/browser/components/search/test/browser_private_search_perwindowpb.js
@@ -1,13 +1,17 @@
 // This test performs a search in a public window, then a different
 // search in a private window, and then checks in the public window
 // whether there is an autocomplete entry for the private search.
 
 function test() {
+  // Don't use about:home as the homepage for new windows
+  Services.prefs.setIntPref("browser.startup.page", 0);
+  registerCleanupFunction(function() Services.prefs.clearUserPref("browser.startup.page"));
+
   waitForExplicitFinish();
 
   let engineURL =
     "http://mochi.test:8888/browser/browser/components/search/test/";
   let windowsToClose = [];
   registerCleanupFunction(function() {
     let engine = Services.search.getEngineByName("Bug 426329");
     Services.search.removeEngine(engine);
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -719,17 +719,19 @@ let SessionStoreInternal = {
     }
     // this window was opened by _openWindowWithState
     else if (!this._isWindowLoaded(aWindow)) {
       let followUp = this._statesToRestore[aWindow.__SS_restoreID].windows.length == 1;
       this.restoreWindow(aWindow, this._statesToRestore[aWindow.__SS_restoreID], true, followUp);
     }
     else if (this._restoreLastWindow && aWindow.toolbar.visible &&
              this._closedWindows.length
-#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+             && !PrivateBrowsingUtils.isWindowPrivate(aWindow)
+#else
              && !this._inPrivateBrowsing
 #endif
              ) {
       
       // default to the most-recently closed window
       // don't use popup windows
       let closedWindowState = null;
       let closedWindowIndex;
@@ -881,19 +883,26 @@ let SessionStoreInternal = {
       }
 
 #ifndef XP_MACOSX
       // Until we decide otherwise elsewhere, this window is part of a series
       // of closing windows to quit.
       winData._shouldRestore = true;
 #endif
 
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+      // Save the window if it has multiple tabs or a single saveable tab and
+      // it's not private.
+      if (!winData.isPrivate && (winData.tabs.length > 1 ||
+          (winData.tabs.length == 1 && this._shouldSaveTabState(winData.tabs[0])))) {
+#else
       // save the window if it has multiple tabs or a single saveable tab
       if (winData.tabs.length > 1 ||
           (winData.tabs.length == 1 && this._shouldSaveTabState(winData.tabs[0]))) {
+#endif
         // we don't want to save the busy state
         delete winData.busy;
 
         this._closedWindows.unshift(winData);
         this._capClosedWindows();
       }
 
       // clear this window from the list
--- a/browser/components/sessionstore/test/Makefile.in
+++ b/browser/components/sessionstore/test/Makefile.in
@@ -26,23 +26,21 @@ MOCHITEST_BROWSER_FILES = \
 	browser_248970_b.js \
 	browser_248970_b_sample.html \
 	browser_339445.js \
 	browser_339445_sample.html \
 	browser_345898.js \
 	browser_346337.js \
 	browser_346337_sample.html \
 	browser_350525.js \
-	browser_354894.js \
 	browser_367052.js \
 	browser_393716.js \
 	browser_394759_basic.js \
 	browser_394759_behavior.js \
 	browser_394759_purge.js \
-	browser_394759_privatebrowsing.js \
 	browser_408470.js \
 	browser_408470_sample.html \
 	browser_423132.js \
 	browser_423132_sample.html \
 	browser_447951.js \
 	browser_447951_sample.html \
 	browser_448741.js \
 	browser_454908.js \
@@ -132,16 +130,28 @@ MOCHITEST_BROWSER_FILES = \
 	browser_739531.js \
 	browser_739531_sample.html \
 	browser_739805.js \
 	$(filter disabled-for-intermittent-failures--bug-766044, browser_459906_empty.html) \
 	$(filter disabled-for-intermittent-failures--bug-766044, browser_459906_sample.html) \
 	$(filter disabled-for-intermittent-failures--bug-765389, browser_461743_sample.html) \
 	$(NULL)
 
+ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+MOCHITEST_BROWSER_FILES += \
+	browser_354894_perwindowpb.js \
+	browser_394759_perwindowpb.js \
+	$(NULL)
+else
+MOCHITEST_BROWSER_FILES += \
+	browser_354894.js \
+	browser_394759_privatebrowsing.js \
+	$(NULL)
+endif
+
 # Disabled on Windows for frequent intermittent failures
 ifneq ($(OS_ARCH), WINNT)
 MOCHITEST_FILES += \
 	browser_464620_a.js \
 	browser_464620_a.html \
 	browser_464620_b.js \
 	browser_464620_b.html \
 	browser_464620_xd.html \
copy from browser/components/sessionstore/test/browser_354894.js
copy to browser/components/sessionstore/test/browser_354894_perwindowpb.js
--- a/browser/components/sessionstore/test/browser_354894.js
+++ b/browser/components/sessionstore/test/browser_354894_perwindowpb.js
@@ -181,22 +181,27 @@ function test() {
 
     // Reset the window type
     document.documentElement.setAttribute("windowtype", oldWinType);
   }
 
   /**
    * Helper: sets the prefs and a new window with our test tabs
    */
-  function setupTestAndRun(testFn) {
+  function setupTestAndRun(aIsPrivateWindow, testFn) {
     // Prepare the prefs
     setPrefs();
 
     // Prepare a window; open it and add more tabs
-    let newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:config");
+    let options = {};
+    if (aIsPrivateWindow) {
+      options = {private: true};
+    }
+
+    let newWin = OpenBrowserWindow(options);
     newWin.addEventListener("load", function(aEvent) {
       newWin.removeEventListener("load", arguments.callee, false);
       newWin.gBrowser.addEventListener("load", function(aEvent) {
         newWin.gBrowser.removeEventListener("load", arguments.callee, true);
         TEST_URLS.forEach(function (url) {
           newWin.gBrowser.addTab(url);
         });
 
@@ -205,32 +210,32 @@ function test() {
     }, false);
   }
 
   /**
    * Test 1: Normal in-session restore
    * @note: Non-Mac only
    */
   function testOpenCloseNormal(nextFn) {
-    setupTestAndRun(function(newWin) {
+    setupTestAndRun(false, function(newWin) {
       // Close the window
       // window.close doesn't push any close events,
       // so use BrowserTryToCloseWindow
       newWin.BrowserTryToCloseWindow();
 
       // The first request to close is denied by our observer (Test 6)
       ok(!newWin.closed, "First close request was denied");
       if (!newWin.closed) {
         newWin.BrowserTryToCloseWindow();
         ok(newWin.closed, "Second close request was granted");
       }
 
       // Open a new window
       // The previously closed window should be restored
-      newWin = openDialog(location, "_blank", CHROME_FEATURES);
+      newWin = OpenBrowserWindow({});
       newWin.addEventListener("load", function() {
         this.removeEventListener("load", arguments.callee, true);
         executeSoon(function() {
           is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1,
              "Restored window in-session with otherpopup windows around");
 
           // Cleanup
           newWin.close();
@@ -242,42 +247,35 @@ function test() {
     });
   }
 
   /**
    * Test 2: PrivateBrowsing in-session restore
    * @note: Non-Mac only
    */
   function testOpenClosePrivateBrowsing(nextFn) {
-    setupTestAndRun(function(newWin) {
-      let pb = Cc["@mozilla.org/privatebrowsing;1"].
-               getService(Ci.nsIPrivateBrowsingService);
-
+    setupTestAndRun(false, function(newWin) {
       // Close the window
       newWin.BrowserTryToCloseWindow();
 
       // Enter private browsing mode
-      pb.privateBrowsingEnabled = true;
-
       // Open a new window.
       // The previously closed window should NOT be restored
-      newWin = openDialog(location, "_blank", CHROME_FEATURES);
+      newWin = OpenBrowserWindow({private: true});
       newWin.addEventListener("load", function() {
         this.removeEventListener("load", arguments.callee, true);
         executeSoon(function() {
           is(newWin.gBrowser.browsers.length, 1,
              "Did not restore in private browing mode");
 
           // Cleanup
           newWin.BrowserTryToCloseWindow();
 
           // Exit private browsing mode again
-          pb.privateBrowsingEnabled = false;
-
-          newWin = openDialog(location, "_blank", CHROME_FEATURES);
+          newWin = OpenBrowserWindow({});
           newWin.addEventListener("load", function() {
             this.removeEventListener("load", arguments.callee, true);
             executeSoon(function() {
               is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1,
                  "Restored after leaving private browsing again");
 
               newWin.close();
 
@@ -291,17 +289,17 @@ function test() {
   }
 
   /**
    * Test 3: Open some popup windows to check those aren't restored, but
    *         the browser window is
    * @note: Non-Mac only
    */
   function testOpenCloseWindowAndPopup(nextFn) {
-    setupTestAndRun(function(newWin) {
+    setupTestAndRun(false, function(newWin) {
       // open some popups
       let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[0]);
       let popup2 = openDialog(location, "popup2", POPUP_FEATURES, TEST_URLS[1]);
       popup2.addEventListener("load", function() {
         popup2.removeEventListener("load", arguments.callee, false);
         popup2.gBrowser.addEventListener("load", function() {
           popup2.gBrowser.removeEventListener("load", arguments.callee, true);
           popup2.gBrowser.addTab(TEST_URLS[0]);
@@ -309,17 +307,17 @@ function test() {
           newWin.BrowserTryToCloseWindow();
 
           // Close the popup window
           // The test is successful when not this popup window is restored
           // but instead newWin
           popup2.close();
 
           // open a new window the previously closed window should be restored to
-          newWin = openDialog(location, "_blank", CHROME_FEATURES);
+          newWin = OpenBrowserWindow({});
           newWin.addEventListener("load", function() {
             this.removeEventListener("load", arguments.callee, true);
             executeSoon(function() {
               is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1,
                  "Restored window and associated tabs in session");
 
               // Cleanup
               newWin.close();
@@ -363,17 +361,17 @@ function test() {
           is(popup.gBrowser.browsers.length, 2,
              "Did not restore to the popup window (2)");
 
           // Close the popup window
           // The test is successful when not this popup window is restored
           // but instead a new window is opened without restoring anything
           popup.close();
 
-          let newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:blank");
+          let newWin = OpenBrowserWindow({});
           newWin.addEventListener("load", function() {
             newWin.removeEventListener("load", arguments.callee, true);
             executeSoon(function() {
               isnot(newWin.gBrowser.browsers.length, 2,
                     "Did not restore the popup window");
               is(TEST_URLS.indexOf(newWin.gBrowser.browsers[0].currentURI.spec), -1,
                  "Did not restore the popup window (2)");
 
@@ -390,26 +388,26 @@ function test() {
   }
 
     /**
    * Test 5: Open some windows and do undoCloseWindow. This should prevent any
    *         restoring later in the test
    * @note: Non-Mac only
    */
   function testOpenCloseRestoreFromPopup(nextFn) {
-    setupTestAndRun(function(newWin) {
-      setupTestAndRun(function(newWin2) {
+    setupTestAndRun(false, function(newWin) {
+      setupTestAndRun(false, function(newWin2) {
         newWin.BrowserTryToCloseWindow();
         newWin2.BrowserTryToCloseWindow();
 
         browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
 
         newWin = undoCloseWindow(0);
 
-        newWin2 = openDialog(location, "_blank", CHROME_FEATURES);
+        newWin2 = OpenBrowserWindow({});
         newWin2.addEventListener("load", function() {
           newWin2.removeEventListener("load", arguments.callee, true);
           executeSoon(function() {
             is(newWin2.gBrowser.browsers.length, 1,
                "Did not restore, as undoCloseWindow() was last called");
             is(TEST_URLS.indexOf(newWin2.gBrowser.browsers[0].currentURI.spec), -1,
                "Did not restore, as undoCloseWindow() was last called (2)");
 
@@ -449,17 +447,17 @@ function test() {
 
   /**
    * Test 8: Test if closing can be denied on Mac
    *         Futhermore prepares the testNotificationCount test (Test 7)
    * @note: Mac only
    */
   function testMacNotifications(nextFn, iteration) {
     iteration = iteration || 1;
-    setupTestAndRun(function(newWin) {
+    setupTestAndRun(false, function(newWin) {
       // close the window
       // window.close doesn't push any close events,
       // so use BrowserTryToCloseWindow
       newWin.BrowserTryToCloseWindow();
       if (iteration == 1) {
         ok(!newWin.closed, "First close attempt denied");
         if (!newWin.closed) {
           newWin.BrowserTryToCloseWindow();
copy from browser/components/sessionstore/test/browser_394759_privatebrowsing.js
copy to browser/components/sessionstore/test/browser_394759_perwindowpb.js
--- a/browser/components/sessionstore/test/browser_394759_privatebrowsing.js
+++ b/browser/components/sessionstore/test/browser_394759_perwindowpb.js
@@ -1,140 +1,119 @@
 /* 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/. */
 
+/** Private Browsing Test for Bug 394759 **/
 function test() {
-  /** Private Browsing Test for Bug 394759 **/
-
   waitForExplicitFinish();
 
-  // Set interval to a large time so state won't be written while we setup env.
-  gPrefService.setIntPref("browser.sessionstore.interval", 100000);
-
-  // Set up the browser in a blank state. Popup windows in previous tests result
-  // in different states on different platforms.
-  let blankState = JSON.stringify({
-    windows: [{
-      tabs: [{ entries: [{ url: "about:blank" }] }],
-      _closedTabs: []
-    }],
-    _closedWindows: []
-  });
-  ss.setBrowserState(blankState);
-
-  // Wait for the sessionstore.js file to be written before going on.
-  // Note: we don't wait for the complete event, since if asyncCopy fails we
-  // would timeout.
-  Services.obs.addObserver(function (aSubject, aTopic, aData) {
-    Services.obs.removeObserver(arguments.callee, aTopic);
-    info("sessionstore.js is being written");
-    executeSoon(continue_test);
-  }, "sessionstore-state-write", false);
-
-  // Remove the sessionstore.js file before setting the interval to 0
-  let profilePath = Cc["@mozilla.org/file/directory_service;1"].
-                    getService(Ci.nsIProperties).
-                    get("ProfD", Ci.nsIFile);
-  let sessionStoreJS = profilePath.clone();
-  sessionStoreJS.append("sessionstore.js");
-  if (sessionStoreJS.exists())
-    sessionStoreJS.remove(false);
-  info("sessionstore.js was correctly removed: " + (!sessionStoreJS.exists()));
-
-  // Make sure that sessionstore.js can be forced to be created by setting
-  // the interval pref to 0.
-  gPrefService.setIntPref("browser.sessionstore.interval", 0);
-}
-
-function continue_test() {
-  let pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
-  // Ensure Private Browsing mode is disabled.
-  ok(!pb.privateBrowsingEnabled, "Private Browsing is disabled");
-
-  let closedWindowCount = ss.getClosedWindowCount();
-  is(closedWindowCount, 0, "Correctly set window count");
-
+  let windowsToClose = [];
+  let closedWindowCount = 0;
   // Prevent VM timers issues, cache now and increment it manually.
   let now = Date.now();
   const TESTS = [
     { url: "about:config",
       key: "bug 394759 Non-PB",
       value: "uniq" + (++now) },
     { url: "about:mozilla",
       key: "bug 394759 PB",
       value: "uniq" + (++now) },
   ];
 
-  function openWindowAndTest(aTestIndex, aRunNextTestInPBMode) {
-    info("Opening new window");
-    function onLoad(event) {
-            win.removeEventListener("load", onLoad, false);
-            info("New window has been loaded");
-            win.gBrowser.addEventListener("load", function(aEvent) {
-              win.gBrowser.removeEventListener("load", arguments.callee, true);
-              info("New window browser has been loaded");
-              executeSoon(function() {
-                // Add a tab.
-                win.gBrowser.addTab();
-
-                executeSoon(function() {
-                  // Mark the window with some unique data to be restored later on.
-                  ss.setWindowValue(win, TESTS[aTestIndex].key, TESTS[aTestIndex].value);
-
-                  win.close();
-
-                  // Ensure that we incremented # of close windows.
-                  is(ss.getClosedWindowCount(), closedWindowCount + 1,
-                     "The closed window was added to the list");
-
-                  // Ensure we added window to undo list.
-                  let data = JSON.parse(ss.getClosedWindowData())[0];
-                  ok(JSON.stringify(data).indexOf(TESTS[aTestIndex].value) > -1,
-                     "The closed window data was stored correctly");
-
-                  if (aRunNextTestInPBMode) {
-                    // Enter private browsing mode.
-                    pb.privateBrowsingEnabled = true;
-                    ok(pb.privateBrowsingEnabled, "private browsing enabled");
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("browser.sessionstore.interval");
+    windowsToClose.forEach(function(win) {
+      win.close();
+    });
+  });
 
-                    // Ensure that we have 0 undo windows when entering PB.
-                    is(ss.getClosedWindowCount(), 0,
-                       "Recently Closed Windows are removed when entering Private Browsing");
-                    is(ss.getClosedWindowData(), "[]",
-                       "Recently Closed Windows data is cleared when entering Private Browsing");
-                  }
-                  else {
-                    // Exit private browsing mode.
-                    pb.privateBrowsingEnabled = false;
-                    ok(!pb.privateBrowsingEnabled, "private browsing disabled");
-
-                    // Ensure that we still have the closed windows from before.
-                    is(ss.getClosedWindowCount(), closedWindowCount + 1,
-                       "The correct number of recently closed windows were restored " +
-                       "when exiting PB mode");
+  function testOpenCloseWindow(aIsPrivate, aTest, aCallback) {
+    whenNewWindowLoaded(aIsPrivate, function(win) {
+      win.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+        win.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+        executeSoon(function() {
+          // Mark the window with some unique data to be restored later on.
+          ss.setWindowValue(win, aTest.key, aTest.value);
+          // Close.
+          win.close();
+          aCallback();
+        });
+      }, true);
+      win.gBrowser.selectedBrowser.loadURI(aTest.url);
+    });
+  }
 
-                    let data = JSON.parse(ss.getClosedWindowData())[0];
-                    ok(JSON.stringify(data).indexOf(TESTS[aTestIndex - 1].value) > -1,
-                       "The data associated with the recently closed window was " +
-                       "restored when exiting PB mode");
-                  }
+  function testOnWindow(aIsPrivate, aValue, aCallback) {
+    whenNewWindowLoaded(aIsPrivate, function(win) {
+      windowsToClose.push(win);
+      executeSoon(function() checkClosedWindows(aIsPrivate, aValue, aCallback));
+    });
+  }
 
-                  if (aTestIndex == TESTS.length - 1) {
-                    gPrefService.clearUserPref("browser.sessionstore.interval");
-                    finish();
-                  }
-                  else {
-                    // Run next test.
-                    openWindowAndTest(aTestIndex + 1, !aRunNextTestInPBMode);
-                  }
-                });
-              });
-            }, true);
-    }
-    // Open a window.
-    var win = openDialog(location, "", "chrome,all,dialog=no", TESTS[aTestIndex].url);
-    win.addEventListener("load", onLoad, false);
+  function checkClosedWindows(aIsPrivate, aValue, aCallback) {
+    let data = JSON.parse(ss.getClosedWindowData())[0];
+    is(ss.getClosedWindowCount(), 1, "Check the closed window count");
+    ok(JSON.stringify(data).indexOf(aValue) > -1,
+       "Check the closed window data was stored correctly");
+    aCallback();
   }
 
-  openWindowAndTest(0, true);
+  function setupBlankState(aCallback) {
+    // Set interval to a large time so state won't be written while we setup
+    // environment.
+    Services.prefs.setIntPref("browser.sessionstore.interval", 100000);
+
+    // Set up the browser in a blank state. Popup windows in previous tests
+    // result in different states on different platforms.
+    let blankState = JSON.stringify({
+      windows: [{
+        tabs: [{ entries: [{ url: "about:blank" }] }],
+        _closedTabs: []
+      }],
+      _closedWindows: []
+    });
+    ss.setBrowserState(blankState);
+
+    // Wait for the sessionstore.js file to be written before going on.
+    // Note: we don't wait for the complete event, since if asyncCopy fails we
+    // would timeout.
+    Services.obs.addObserver(function (aSubject, aTopic, aData) {
+      Services.obs.removeObserver(arguments.callee, aTopic);
+      info("sessionstore.js is being written");
+
+      closedWindowCount = ss.getClosedWindowCount();
+      is(closedWindowCount, 0, "Correctly set window count");
+
+      executeSoon(aCallback);
+    }, "sessionstore-state-write", false);
+
+    // Remove the sessionstore.js file before setting the interval to 0
+    let profilePath = Services.dirsvc.get("ProfD", Ci.nsIFile);
+    let sessionStoreJS = profilePath.clone();
+    sessionStoreJS.append("sessionstore.js");
+    if (sessionStoreJS.exists())
+      sessionStoreJS.remove(false);
+    info("sessionstore.js was correctly removed: " + (!sessionStoreJS.exists()));
+
+    // Make sure that sessionstore.js can be forced to be created by setting
+    // the interval pref to 0.
+    Services.prefs.setIntPref("browser.sessionstore.interval", 0);
+  }
+
+  setupBlankState(function() {
+    testOpenCloseWindow(false, TESTS[0], function() {
+      testOpenCloseWindow(true, TESTS[1], function() {
+        testOnWindow(false, TESTS[0].value, function() {
+          testOnWindow(true, TESTS[0].value, finish);
+        });
+      });
+    });
+  });
 }
+
+function whenNewWindowLoaded(aIsPrivate, aCallback) {
+  let win = OpenBrowserWindow({private: aIsPrivate});
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    aCallback(win);
+  }, false);
+}
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -146,39 +146,41 @@ let DebuggerController = {
    * wiring event handlers as necessary.
    */
   _connect: function DC__connect() {
     function callback() {
       window.dispatchEvent("Debugger:Connected");
     }
 
     let client;
+
     // Remote debugging gets the debuggee from a RemoteTarget object.
     if (this._target && this._target.isRemote) {
+      window._isRemoteDebugger = true;
+
       client = this.client = this._target.client;
-
       this._target.on("close", this._onTabDetached);
       this._target.on("navigate", this._onTabNavigated);
 
       if (this._target.chrome) {
         let dbg = this._target.form.chromeDebugger;
         this._startChromeDebugging(client, dbg, callback);
       } else {
         this._startDebuggingTab(client, this._target.form, callback);
       }
       return;
     }
 
-    // Content debugging can connect directly to the page.
+    // Content or chrome debugging can connect directly to the debuggee.
     // TODO: convert this to use a TabTarget.
     let transport = window._isChromeDebugger
       ? debuggerSocketConnect(Prefs.remoteHost, Prefs.remotePort)
       : DebuggerServer.connectPipe();
+
     client = this.client = new DebuggerClient(transport);
-
     client.addListener("tabNavigated", this._onTabNavigated);
     client.addListener("tabDetached", this._onTabDetached);
 
     client.connect(function(aType, aTraits) {
       client.listTabs(function(aResponse) {
         if (window._isChromeDebugger) {
           let dbg = aResponse.chromeDebugger;
           this._startChromeDebugging(client, dbg, callback);
@@ -196,21 +198,22 @@ let DebuggerController = {
   _disconnect: function DC__disconnect() {
     // Return early if the client didn't even have a chance to instantiate.
     if (!this.client) {
       return;
     }
     this.client.removeListener("tabNavigated", this._onTabNavigated);
     this.client.removeListener("tabDetached", this._onTabDetached);
 
-    if (!this._target.isRemote) {
+    // When remote debugging, the connection is closed by the RemoteTarget.
+    if (!window._isRemoteDebugger) {
       this.client.close();
-      this.client = null;
     }
 
+    this.client = null;
     this.tabClient = null;
     this.activeThread = null;
   },
 
   /**
    * Called for each location change in the debugged tab.
    */
   _onTabNavigated: function DC__onTabNavigated() {
@@ -230,18 +233,17 @@ let DebuggerController = {
   /**
    * Sets up a debugging session.
    *
    * @param DebuggerClient aClient
    *        The debugger client.
    * @param object aTabGrip
    *        The remote protocol grip of the tab.
    */
-  _startDebuggingTab: function DC__startDebuggingTab
-      (aClient, aTabGrip, aCallback=function(){}) {
+  _startDebuggingTab: function DC__startDebuggingTab(aClient, aTabGrip, aCallback) {
     if (!aClient) {
       Cu.reportError("No client found!");
       return;
     }
     this.client = aClient;
 
     aClient.attachTab(aTabGrip.actor, function(aResponse, aTabClient) {
       if (!aTabClient) {
@@ -257,31 +259,32 @@ let DebuggerController = {
         }
         this.activeThread = aThreadClient;
 
         this.ThreadState.connect();
         this.StackFrames.connect();
         this.SourceScripts.connect();
         aThreadClient.resume();
 
-        aCallback();
+        if (aCallback) {
+          aCallback();
+        }
       }.bind(this));
     }.bind(this));
   },
 
   /**
    * Sets up a chrome debugging session.
    *
    * @param DebuggerClient aClient
    *        The debugger client.
    * @param object aChromeDebugger
    *        The remote protocol grip of the chrome debugger.
    */
-  _startChromeDebugging: function DC__startChromeDebugging
-      (aClient, aChromeDebugger, aCallback=function(){}) {
+  _startChromeDebugging: function DC__startChromeDebugging(aClient, aChromeDebugger, aCallback) {
     if (!aClient) {
       Cu.reportError("No client found!");
       return;
     }
     this.client = aClient;
 
     aClient.attachThread(aChromeDebugger, function(aResponse, aThreadClient) {
       if (!aThreadClient) {
@@ -290,17 +293,19 @@ let DebuggerController = {
       }
       this.activeThread = aThreadClient;
 
       this.ThreadState.connect();
       this.StackFrames.connect();
       this.SourceScripts.connect();
       aThreadClient.resume();
 
-      aCallback();
+      if (aCallback) {
+        aCallback();
+      }
     }.bind(this));
   },
 
   /**
    * Attempts to quit the current process if allowed.
    */
   _quitApp: function DC__quitApp() {
     let canceled = Cc["@mozilla.org/supports-PRBool;1"]
@@ -629,16 +634,17 @@ StackFrames.prototype = {
     // to contain all the values.
     if (this.syncedWatchExpressions && watchExpressionsEvaluation) {
       let label = L10N.getStr("watchExpressionsScopeLabel");
       let arrow = L10N.getStr("watchExpressionsSeparatorLabel");
       let scope = DebuggerView.Variables.addScope(label);
       scope.separator = arrow;
       scope.allowNameInput = true;
       scope.allowDeletion = true;
+      scope.contextMenu = "debuggerWatchExpressionsContextMenu";
       scope.switch = DebuggerView.WatchExpressions.switchExpression;
       scope.delete = DebuggerView.WatchExpressions.deleteExpression;
 
       // The evaluation hasn't thrown, so display the returned results and
       // always expand the watch expressions scope by default.
       this._fetchWatchExpressions(scope, watchExpressionsEvaluation);
       scope.expand();
     }
@@ -1168,16 +1174,18 @@ SourceScripts.prototype = {
     }
 
     // Get the source text from the active thread.
     this.activeThread.source(aSource.source).source(function(aResponse) {
       if (aResponse.error) {
         Cu.reportError("Error loading " + aUrl);
         return;
       }
+      aSource.loaded = true;
+      aSource.text = aResponse.source;
       aCallback(aSource.url, aResponse.source);
     });
   }
 };
 
 /**
  * Handles all the breakpoints in the current debugger.
  */
@@ -1593,17 +1601,17 @@ Prefs.map("Int", "height", "devtools.deb
 Prefs.map("Int", "windowX", "devtools.debugger.ui.win-x");
 Prefs.map("Int", "windowY", "devtools.debugger.ui.win-y");
 Prefs.map("Int", "windowWidth", "devtools.debugger.ui.win-width");
 Prefs.map("Int", "windowHeight", "devtools.debugger.ui.win-height");
 Prefs.map("Int", "stackframesWidth", "devtools.debugger.ui.stackframes-width");
 Prefs.map("Int", "variablesWidth", "devtools.debugger.ui.variables-width");
 Prefs.map("Bool", "panesVisibleOnStartup", "devtools.debugger.ui.panes-visible-on-startup");
 Prefs.map("Bool", "variablesSortingEnabled", "devtools.debugger.ui.variables-sorting-enabled");
-Prefs.map("Bool", "variablesNonEnumVisible", "devtools.debugger.ui.variables-non-enum-visible");
+Prefs.map("Bool", "variablesOnlyEnumVisible", "devtools.debugger.ui.variables-only-enum-visible");
 Prefs.map("Bool", "variablesSearchboxVisible", "devtools.debugger.ui.variables-searchbox-visible");
 Prefs.map("Char", "remoteHost", "devtools.debugger.remote-host");
 Prefs.map("Int", "remotePort", "devtools.debugger.remote-port");
 Prefs.map("Bool", "remoteAutoConnect", "devtools.debugger.remote-autoconnect");
 Prefs.map("Int", "remoteConnectionRetries", "devtools.debugger.remote-connection-retries");
 Prefs.map("Int", "remoteTimeout", "devtools.debugger.remote-timeout");
 
 /**
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -46,18 +46,17 @@ create({ constructor: StackFramesView, p
    *
    * @param string aFrameName
    *        Name to be displayed in the list.
    * @param string aFrameDetails
    *        Details to be displayed in the list.
    * @param number aDepth
    *        The frame depth specified by the debugger.
    */
-  addFrame:
-  function DVSF_addFrame(aFrameName, aFrameDetails, aDepth) {
+  addFrame: function DVSF_addFrame(aFrameName, aFrameDetails, aDepth) {
     // Stackframes are UI elements which benefit from visible panes.
     DebuggerView.showPanesSoon();
 
     // Append a stackframe item to this container.
     let stackframeItem = this.push(aFrameName, aFrameDetails, {
       forced: true,
       unsorted: true,
       relaxed: true,
@@ -960,16 +959,17 @@ create({ constructor: WatchExpressionsVi
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function DVWE_initialize() {
     dumpn("Initializing the WatchExpressionsView");
     this._container = new StackList(document.getElementById("expressions"));
     this._variables = document.getElementById("variables");
 
+    this._container.setAttribute("context", "debuggerWatchExpressionsContextMenu");
     this._container.permaText = L10N.getStr("addWatchExpressionText");
     this._container.itemFactory = this._createItemView;
     this._container.addEventListener("click", this._onClick, false);
 
     this._cache = [];
   },
 
   /**
@@ -1126,19 +1126,45 @@ create({ constructor: WatchExpressionsVi
     aElementNode.appendChild(inputNode);
     aElementNode.appendChild(closeNode);
     aElementNode.arrowNode = arrowNode;
     aElementNode.inputNode = inputNode;
     aElementNode.closeNode = closeNode;
   },
 
   /**
+   * Called when the add watch expression key sequence was pressed.
+   */
+  _onCmdAddExpression: function BP__onCmdAddExpression(aText) {
+    // Only add a new expression if there's no pending input.
+    if (this.getExpressions().indexOf("") == -1) {
+      this.addExpression(aText || DebuggerView.editor.getSelectedText());
+    }
+  },
+
+  /**
+   * Called when the remove all watch expressions key sequence was pressed.
+   */
+  _onCmdRemoveAllExpressions: function BP__onCmdRemoveAllExpressions() {
+    // Empty the view of all the watch expressions and clear the cache.
+    this.empty();
+    this._cache = [];
+
+    // Synchronize with the controller's watch expressions store.
+    DebuggerController.StackFrames.syncWatchExpressions();
+  },
+
+  /**
    * The click listener for this container.
    */
   _onClick: function DVWE__onClick(e) {
+    if (e.button != 0) {
+      // Only allow left-click to trigger this event.
+      return;
+    }
     let expressionItem = this.getItemForElement(e.target);
     if (!expressionItem) {
       // The container is empty or we didn't click on an actual item.
       this.addExpression();
     }
   },
 
   /**
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -179,36 +179,36 @@ ToolbarView.prototype = {
 
 /**
  * Functions handling the options UI.
  */
 function OptionsView() {
   dumpn("OptionsView was instantiated");
   this._togglePauseOnExceptions = this._togglePauseOnExceptions.bind(this);
   this._toggleShowPanesOnStartup = this._toggleShowPanesOnStartup.bind(this);
-  this._toggleShowVariablesNonEnum = this._toggleShowVariablesNonEnum.bind(this);
-  this._toggleShowVariablesSearchbox = this._toggleShowVariablesSearchbox.bind(this);
+  this._toggleShowVariablesOnlyEnum = this._toggleShowVariablesOnlyEnum.bind(this);
+  this._toggleShowVariablesFilterBox = this._toggleShowVariablesFilterBox.bind(this);
 }
 
 OptionsView.prototype = {
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function DVO_initialize() {
     dumpn("Initializing the OptionsView");
     this._button = document.getElementById("debugger-options");
     this._pauseOnExceptionsItem = document.getElementById("pause-on-exceptions");
     this._showPanesOnStartupItem = document.getElementById("show-panes-on-startup");
-    this._showVariablesNonEnumItem = document.getElementById("show-vars-nonenum");
-    this._showVariablesSearchboxItem = document.getElementById("show-vars-searchbox");
+    this._showVariablesOnlyEnumItem = document.getElementById("show-vars-only-enum");
+    this._showVariablesFilterBoxItem = document.getElementById("show-vars-filter-box");
 
     this._pauseOnExceptionsItem.setAttribute("checked", "false");
     this._showPanesOnStartupItem.setAttribute("checked", Prefs.panesVisibleOnStartup);
-    this._showVariablesNonEnumItem.setAttribute("checked", Prefs.variablesNonEnumVisible);
-    this._showVariablesSearchboxItem.setAttribute("checked", Prefs.variablesSearchboxVisible);
+    this._showVariablesOnlyEnumItem.setAttribute("checked", Prefs.variablesOnlyEnumVisible);
+    this._showVariablesFilterBoxItem.setAttribute("checked", Prefs.variablesSearchboxVisible);
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function DVO_destroy() {
     dumpn("Destroying the OptionsView");
     // Nothing to do here yet.
@@ -242,34 +242,34 @@ OptionsView.prototype = {
   _toggleShowPanesOnStartup: function DVO__toggleShowPanesOnStartup() {
     Prefs.panesVisibleOnStartup =
       this._showPanesOnStartupItem.getAttribute("checked") == "true";
   },
 
   /**
    * Listener handling the 'show non-enumerables' menuitem command.
    */
-  _toggleShowVariablesNonEnum: function DVO__toggleShowVariablesNonEnum() {
-    DebuggerView.Variables.nonEnumVisible = Prefs.variablesNonEnumVisible =
-      this._showVariablesNonEnumItem.getAttribute("checked") == "true";
+  _toggleShowVariablesOnlyEnum: function DVO__toggleShowVariablesOnlyEnum() {
+    DebuggerView.Variables.onlyEnumVisible = Prefs.variablesOnlyEnumVisible =
+      this._showVariablesOnlyEnumItem.getAttribute("checked") == "true";
   },
 
   /**
    * Listener handling the 'show variables searchbox' menuitem command.
    */
-  _toggleShowVariablesSearchbox: function DVO__toggleShowVariablesSearchbox() {
+  _toggleShowVariablesFilterBox: function DVO__toggleShowVariablesFilterBox() {
     DebuggerView.Variables.searchEnabled = Prefs.variablesSearchboxVisible =
-      this._showVariablesSearchboxItem.getAttribute("checked") == "true";
+      this._showVariablesFilterBoxItem.getAttribute("checked") == "true";
   },
 
   _button: null,
   _pauseOnExceptionsItem: null,
   _showPanesOnStartupItem: null,
-  _showVariablesNonEnumItem: null,
-  _showVariablesSearchboxItem: null
+  _showVariablesOnlyEnumItem: null,
+  _showVariablesFilterBoxItem: null
 };
 
 /**
  * Functions handling the chrome globals UI.
  */
 function ChromeGlobalsView() {
   dumpn("ChromeGlobalsView was instantiated");
   MenuContainer.call(this);
@@ -622,17 +622,17 @@ FilterView.prototype = {
     this._searchbox.removeEventListener("blur", this._onBlur, false);
   },
 
   /**
    * Sets the target container to be currently filtered.
    * @param object aView
    */
   set target(aView) {
-    var placeholder = "";
+    let placeholder = "";
     switch (aView) {
       case DebuggerView.ChromeGlobals:
         placeholder = L10N.getFormatStr("emptyChromeGlobalsFilterText", [this._fileSearchKey]);
         break;
       case DebuggerView.Sources:
         placeholder = L10N.getFormatStr("emptyFilterText", [this._fileSearchKey]);
         break;
     }
@@ -660,33 +660,33 @@ FilterView.prototype = {
         ? lineFlagIndex
         : tokenFlagIndex != -1 ? tokenFlagIndex : rawLength;
 
       let lineEnd = tokenFlagIndex != -1
         ? tokenFlagIndex
         : rawLength;
 
       file = rawValue.slice(0, fileEnd);
-      line = ~~(rawValue.slice(fileEnd + 1, lineEnd)) || -1;
+      line = ~~(rawValue.slice(fileEnd + 1, lineEnd)) || 0;
       token = rawValue.slice(lineEnd + 1);
       isGlobal = false;
       isVariable = false;
     }
     // Global searches dissalow the use of file or line flags.
     else if (globalFlagIndex == 0) {
       file = "";
-      line = -1;
+      line = 0;
       token = rawValue.slice(1);
       isGlobal = true;
       isVariable = false;
     }
     // Variable searches dissalow the use of file or line flags.
     else if (variableFlagIndex == 0) {
       file = "";
-      line = -1;
+      line = 0;
       token = rawValue.slice(1);
       isGlobal = false;
       isVariable = true;
     }
 
     return [file, line, token, isGlobal, isVariable];
   },
 
@@ -773,17 +773,17 @@ FilterView.prototype = {
    * Performs a line search if necessary.
    * (Jump to lines in the currently visible source).
    *
    * @param number aLine
    *        The source line number to jump to.
    */
   _performLineSearch: function DVF__performLineSearch(aLine) {
     // Don't search for lines if the input hasn't changed.
-    if (this._prevSearchedLine != aLine && aLine > -1) {
+    if (this._prevSearchedLine != aLine && aLine > 0) {
       DebuggerView.editor.setCaretPosition(aLine - 1);
     }
     this._prevSearchedLine = aLine;
   },
 
   /**
    * Performs a token search if necessary.
    * (Search for tokens in the currently visible source).
@@ -836,45 +836,57 @@ FilterView.prototype = {
     this._performLineSearch(line);
     this._performTokenSearch(token);
   },
 
   /**
    * The key press listener for the search container.
    */
   _onKeyPress: function DVF__onScriptsKeyPress(e) {
+    // This attribute is not implemented in Gecko at this time, see bug 680830.
+    e.char = String.fromCharCode(e.charCode);
+
     let [file, line, token, isGlobal, isVariable] = this.searchboxInfo;
-    let isDifferentToken, isReturnKey, action;
+    let isDifferentToken, isReturnKey, action = -1;
 
     if (this._prevSearchedToken != token) {
       isDifferentToken = true;
     }
-    switch (e.keyCode) {
+
+    // Meta+G and Ctrl+N focus next matches.
+    if ((e.char == "g" && e.metaKey) || e.char == "n" && e.ctrlKey) {
+      action = 0;
+    }
+    // Meta+Shift+G and Ctrl+P focus previous matches.
+    else if ((e.char == "G" && e.metaKey) || e.char == "p" && e.ctrlKey) {
+      action = 1;
+    }
+    // Return, enter down and up keys focus next or previous matches, while
+    // the escape key switches focus from the search container.
+    else switch (e.keyCode) {
       case e.DOM_VK_RETURN:
       case e.DOM_VK_ENTER:
         isReturnKey = true;
         // fall through
       case e.DOM_VK_DOWN:
         action = 0;
         break;
       case e.DOM_VK_UP:
         action = 1;
         break;
       case e.DOM_VK_ESCAPE:
         action = 2;
         break;
-      default:
-        action = -1;
     }
 
     if (action == 2) {
       DebuggerView.editor.focus();
       return;
     }
-    if (action == -1 || !token) {
+    if (action == -1 || (token.length == 0 && line == 0)) {
       return;
     }
 
     e.preventDefault();
     e.stopPropagation();
 
     // Perform a global search based on the specified operator.
     if (isGlobal) {
@@ -892,16 +904,28 @@ FilterView.prototype = {
       if (isReturnKey && isDifferentToken) {
         DebuggerView.Variables.performSearch(token);
         DebuggerView.Variables.expandFirstSearchResults();
       }
       this._prevSearchedToken = token;
       return;
     }
 
+    // Increment or decrement the specified line.
+    if (!isReturnKey && token.length == 0 && line > 0) {
+      line += action == 0 ? 1 : -1;
+      let lineCount = DebuggerView.editor.getLineCount();
+      let lineTarget = line < 1 ? 1 : line > lineCount ? lineCount : line;
+
+      DebuggerView.editor.setCaretPosition(lineTarget - 1);
+      this._searchbox.value = file + SEARCH_LINE_FLAG + lineTarget;
+      this._prevSearchedLine = lineTarget;
+      return;
+    }
+
     let editor = DebuggerView.editor;
     let offset = editor[["findNext", "findPrevious"][action]](true);
     if (offset > -1) {
       editor.setSelection(offset, offset + token.length)
     }
   },
 
   /**
@@ -977,17 +1001,17 @@ FilterView.prototype = {
   _variableOperatorLabel: null,
   _fileSearchKey: "",
   _globalSearchKey: "",
   _tokenSearchKey: "",
   _lineSearchKey: "",
   _variableSearchKey: "",
   _target: null,
   _prevSearchedFile: "",
-  _prevSearchedLine: -1,
+  _prevSearchedLine: 0,
   _prevSearchedToken: ""
 };
 
 /**
  * Preliminary setup for the DebuggerView object.
  */
 DebuggerView.Toolbar = new ToolbarView();
 DebuggerView.Options = new OptionsView();
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -43,17 +43,17 @@ let DebuggerView = {
     this.StackFrames.initialize();
     this.Breakpoints.initialize();
     this.WatchExpressions.initialize();
     this.GlobalSearch.initialize();
 
     this.Variables = new VariablesView(document.getElementById("variables"));
     this.Variables.searchPlaceholder = L10N.getStr("emptyVariablesFilterText");
     this.Variables.emptyText = L10N.getStr("emptyVariablesText");
-    this.Variables.nonEnumVisible = Prefs.variablesNonEnumVisible;
+    this.Variables.onlyEnumVisible = Prefs.variablesOnlyEnumVisible;
     this.Variables.searchEnabled = Prefs.variablesSearchboxVisible;
     this.Variables.eval = DebuggerController.StackFrames.evaluate;
     this.Variables.lazyEmpty = true;
 
     this._initializeEditor(aCallback);
   },
 
   /**
@@ -258,18 +258,16 @@ let DebuggerView = {
     // If the source is not loaded, display a placeholder text.
     if (!aSource.loaded) {
       this.editor.setMode(SourceEditor.MODES.TEXT);
       this.editor.setText(L10N.getStr("loadingText"));
       this.editor.resetUndo();
 
       // Get the source text from the active thread.
       DebuggerController.SourceScripts.getText(aSource, function(aUrl, aText) {
-        aSource.loaded = true;
-        aSource.text = aText;
         this.setEditorSource(aSource, aOptions);
       }.bind(this));
     }
     // If the source is already loaded, display it immediately.
     else {
       if (this._editorSource != aSource) {
         // Avoid setting the editor mode for very large files.
         if (aSource.text.length < SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
@@ -897,18 +895,17 @@ MenuContainer.prototype = {
   /**
    * Gets the item in the container associated with the specified element.
    *
    * @param nsIDOMNode aElement
    *        The element used to identify the item.
    * @return MenuItem
    *         The matched item, or null if nothing is found.
    */
-  getItemForElement:
-  function DVMC_getItemForElement(aElement) {
+  getItemForElement: function DVMC_getItemForElement(aElement) {
     while (aElement) {
       let item = this._itemsByElement.get(aElement);
       if (item) {
         return item;
       }
       aElement = aElement.parentNode;
     }
     return null;
@@ -1032,18 +1029,17 @@ MenuContainer.prototype = {
    * @param MenuItem aItem
    *        An object containing a label and a value property.
    * @param object aOptions [optional]
    *        Additional options or flags supported by this operation:
    *          - relaxed: true if this container should allow dupes & degenerates
    * @return MenuItem
    *         The item associated with the displayed element, null if rejected.
    */
-  _appendItem:
-  function DVMC__appendItem(aItem, aOptions = {}) {
+  _appendItem: function DVMC__appendItem(aItem, aOptions = {}) {
     if (!aOptions.relaxed && !this.isEligible(aItem)) {
       return null;
     }
 
     return this._entangleItem(aItem, this._container.appendItem(
       aItem.label, aItem.value, "", aOptions.attachment));
   },
 
@@ -1055,18 +1051,17 @@ MenuContainer.prototype = {
    * @param MenuItem aItem
    *        An object containing a label and a value property.
    * @param object aOptions [optional]
    *        Additional options or flags supported by this operation:
    *          - relaxed: true if this container should allow dupes & degenerates
    * @return MenuItem
    *         The item associated with the displayed element, null if rejected.
    */
-  _insertItemAt:
-  function DVMC__insertItemAt(aIndex, aItem, aOptions) {
+  _insertItemAt: function DVMC__insertItemAt(aIndex, aItem, aOptions) {
     if (!aOptions.relaxed && !this.isEligible(aItem)) {
       return null;
     }
 
     return this._entangleItem(aItem, this._container.insertItemAt(
       aIndex, aItem.label, aItem.value, "", aOptions.attachment));
   },
 
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -42,37 +42,45 @@
     <command id="lineSearchCommand"
              oncommand="DebuggerView.Filtering._doLineSearch()"/>
     <command id="variableSearchCommand"
              oncommand="DebuggerView.Filtering._doVariableSearch()"/>
     <command id="addBreakpointCommand"
              oncommand="DebuggerView.Breakpoints._onCmdAddBreakpoint()"/>
     <command id="addConditionalBreakpointCommand"
              oncommand="DebuggerView.Breakpoints._onCmdAddConditionalBreakpoint()"/>
+    <command id="addWatchExpressionCommand"
+             oncommand="DebuggerView.WatchExpressions._onCmdAddExpression()"/>
+    <command id="removeAllWatchExpressionsCommand"
+             oncommand="DebuggerView.WatchExpressions._onCmdRemoveAllExpressions()"/>
     <command id="togglePauseOnExceptions"
              oncommand="DebuggerView.Options._togglePauseOnExceptions()"/>
     <command id="toggleShowPanesOnStartup"
              oncommand="DebuggerView.Options._toggleShowPanesOnStartup()"/>
-    <command id="toggleShowNonEnum"
-             oncommand="DebuggerView.Options._toggleShowVariablesNonEnum()"/>
-    <command id="toggleShowVariablesSearchbox"
-             oncommand="DebuggerView.Options._toggleShowVariablesSearchbox()"/>
+    <command id="toggleShowOnlyEnum"
+             oncommand="DebuggerView.Options._toggleShowVariablesOnlyEnum()"/>
+    <command id="toggleShowVariablesFilterBox"
+             oncommand="DebuggerView.Options._toggleShowVariablesFilterBox()"/>
   </commandset>
 
   <popupset id="debuggerPopupset">
     <menupopup id="sourceEditorContextMenu"
                onpopupshowing="goUpdateSourceEditorMenuItems()">
       <menuitem id="se-dbg-cMenu-addBreakpoint"
                 label="&debuggerUI.seMenuBreak;"
                 key="addBreakpointKey"
                 command="addBreakpointCommand"/>
       <menuitem id="se-dbg-cMenu-addConditionalBreakpoint"
                 label="&debuggerUI.seMenuCondBreak;"
                 key="addConditionalBreakpointKey"
                 command="addConditionalBreakpointCommand"/>
+      <menuitem id="se-dbg-cMenu-addAsWatch"
+                label="&debuggerUI.seMenuAddWatch;"
+                key="addWatchExpressionKey"
+                command="addWatchExpressionCommand"/>
       <menuseparator/>
       <menuitem id="se-cMenu-copy"/>
       <menuseparator/>
       <menuitem id="se-cMenu-selectAll"/>
       <menuseparator/>
       <menuitem id="se-dbg-cMenu-findFile"
                 label="&debuggerUI.searchFile;"
                 accesskey="&debuggerUI.searchFile.key;"
@@ -96,40 +104,54 @@
                 command="lineSearchCommand"/>
       <menuseparator/>
       <menuitem id="se-dbg-cMenu-findVariable"
                 label="&debuggerUI.searchVariable;"
                 accesskey="&debuggerUI.searchVariable.key;"
                 key="variableSearchKey"
                 command="variableSearchCommand"/>
     </menupopup>
+
+    <menupopup id="debuggerWatchExpressionsContextMenu">
+      <menuitem id="add-watch-expression"
+                label="&debuggerUI.addWatch;"
+                accesskey="&debuggerUI.addWatch.key;"
+                key="addWatchExpressionKey"
+                command="addWatchExpressionCommand"/>
+      <menuitem id="removeAll-watch-expression"
+                label="&debuggerUI.removeAllWatch;"
+                accesskey="&debuggerUI.removeAllWatch.key;"
+                key="removeAllWatchExpressionsKey"
+                command="removeAllWatchExpressionsCommand"/>
+    </menupopup>
+
     <menupopup id="debuggerPrefsContextMenu"
                position="before_end"
                onpopupshowing="DebuggerView.Options._onPopupShowing()"
                onpopuphiding="DebuggerView.Options._onPopupHiding()">
       <menuitem id="pause-on-exceptions"
                 type="checkbox"
                 label="&debuggerUI.pauseExceptions;"
                 accesskey="&debuggerUI.pauseExceptions.key;"
                 command="togglePauseOnExceptions"/>
       <menuitem id="show-panes-on-startup"
                 type="checkbox"
                 label="&debuggerUI.showPanesOnInit;"
                 accesskey="&debuggerUI.showPanesOnInit.key;"
                 command="toggleShowPanesOnStartup"/>
-      <menuitem id="show-vars-nonenum"
+      <menuitem id="show-vars-only-enum"
                 type="checkbox"
-                label="&debuggerUI.showNonEnums;"
-                accesskey="&debuggerUI.showNonEnums.key;"
-                command="toggleShowNonEnum"/>
-      <menuitem id="show-vars-searchbox"
+                label="&debuggerUI.showOnlyEnum;"
+                accesskey="&debuggerUI.showOnlyEnum.key;"
+                command="toggleShowOnlyEnum"/>
+      <menuitem id="show-vars-filter-box"
                 type="checkbox"
-                label="&debuggerUI.showVarsSearch;"
-                accesskey="&debuggerUI.showVarsSearch.key;"
-                command="toggleShowVariablesSearchbox"/>
+                label="&debuggerUI.showVarsFilter;"
+                accesskey="&debuggerUI.showVarsFilter.key;"
+                command="toggleShowVariablesFilterBox"/>
     </menupopup>
   </popupset>
 
   <keyset id="debuggerKeys">
     <key id="resumeKey"
          keycode="&debuggerUI.stepping.resume;"
          command="resumeCommand"/>
     <key id="stepOverKey"
@@ -165,16 +187,24 @@
     <key id="addBreakpointKey"
          key="&debuggerUI.seMenuBreak.key;"
          modifiers="accel"
          command="addBreakpointCommand"/>
     <key id="addConditionalBreakpointKey"
          key="&debuggerUI.seMenuCondBreak.key;"
          modifiers="accel shift"
          command="addConditionalBreakpointCommand"/>
+    <key id="addWatchExpressionKey"
+         key="&debuggerUI.seMenuAddWatch.key;"
+         modifiers="accel shift"
+         command="addWatchExpressionCommand"/>
+    <key id="removeAllWatchExpressionsKey"
+         key="&debuggerUI.removeAllWatch.key;"
+         modifiers="accel alt"
+         command="removeAllWatchExpressionsCommand"/>
   </keyset>
 
   <vbox id="body" flex="1">
     <toolbar id="dbg-toolbar" class="devtools-toolbar">
       <hbox id="debugger-controls">
         <toolbarbutton id="resume"
                        class="devtools-toolbarbutton"
                        tabindex="0"/>
--- a/browser/devtools/debugger/test/browser_dbg_bug786070_hide_nonenums.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug786070_hide_nonenums.js
@@ -52,29 +52,29 @@ function testNonEnumProperties() {
 
         ok(nonenum.hasAttribute("open"),
           ".nonenum container should be visible.");
 
         is(nonenum.childNodes.length, 1,
           "There should be just one property in the .nonenum container.");
 
         // Uncheck 'show hidden properties'.
-        gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "false");
-        gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
+        gDebugger.DebuggerView.Options._showVariablesOnlyEnumItem.setAttribute("checked", "true");
+        gDebugger.DebuggerView.Options._toggleShowVariablesOnlyEnum();
 
         executeSoon(function() {
           ok(details.hasAttribute("open"),
             ".details container should stay visible.");
 
           ok(!nonenum.hasAttribute("open"),
             ".nonenum container should become hidden.");
 
           // Check 'show hidden properties'.
-          gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "true");
-          gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
+          gDebugger.DebuggerView.Options._showVariablesOnlyEnumItem.setAttribute("checked", "false");
+          gDebugger.DebuggerView.Options._toggleShowVariablesOnlyEnum();
 
           executeSoon(function() {
             ok(details.hasAttribute("open"),
               ".details container should stay visible.");
 
             ok(nonenum.hasAttribute("open"),
               ".nonenum container should become visible.");
 
@@ -84,29 +84,29 @@ function testNonEnumProperties() {
             executeSoon(function() {
               ok(!details.hasAttribute("open"),
                 ".details container should be hidden.");
 
               ok(!nonenum.hasAttribute("open"),
                 ".nonenum container should be hidden.");
 
               // Uncheck 'show hidden properties'.
-              gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "false");
-              gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
+              gDebugger.DebuggerView.Options._showVariablesOnlyEnumItem.setAttribute("checked", "true");
+              gDebugger.DebuggerView.Options._toggleShowVariablesOnlyEnum();
 
               executeSoon(function() {
                 ok(!details.hasAttribute("open"),
                   ".details container should stay hidden.");
 
                 ok(!nonenum.hasAttribute("open"),
                   ".nonenum container should stay hidden.");
 
                 // Check 'show hidden properties'.
-                gDebugger.DebuggerView.Options._showVariablesNonEnumItem.setAttribute("checked", "true");
-                gDebugger.DebuggerView.Options._toggleShowVariablesNonEnum();
+                gDebugger.DebuggerView.Options._showVariablesOnlyEnumItem.setAttribute("checked", "false");
+                gDebugger.DebuggerView.Options._toggleShowVariablesOnlyEnum();
 
                 executeSoon(function() {
                   gDebugger.DebuggerController.activeThread.resume(function() {
                     closeDebuggerAndFinish();
                   });
                 });
               });
             });
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
@@ -22,19 +22,40 @@ function test() {
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let testScope = gDebugger.DebuggerView.Variables.addScope("test-scope");
       let testVar = testScope.addVar("something");
       let duplVar = testScope.addVar("something");
 
+      info("Scope id: " + testScope.target.id);
+      info("Scope name: " + testScope.target.name);
+      info("Variable id: " + testVar.target.id);
+      info("Variable name: " + testVar.target.name);
+
+      ok(testScope,
+        "Should have created a scope.");
       ok(testVar,
         "Should have created a variable.");
 
+      ok(testScope.id.contains("test-scope"),
+        "Should have the correct scope id.");
+      ok(testScope.target.id.contains("test-scope"),
+        "Should have the correct scope id on the element.");
+      is(testScope.name, "test-scope",
+        "Should have the correct scope name.");
+
+      ok(testVar.id.contains("something"),
+        "Should have the correct variable id.");
+      ok(testVar.target.id.contains("something"),
+        "Should have the correct variable id on the element.");
+      is(testVar.name, "something",
+        "Should have the correct variable name.");
+
       is(duplVar, null,
         "Shouldn't be able to duplicate variables in the same scope.");
 
       is(testVar.target.querySelector(".name").getAttribute("value"), "something",
         "Any new variable should have the designated title.");
 
       is(testVar.target.querySelector(".details").childNodes.length, 0,
         "Any new variable should have a details container with no child nodes.");
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-edit-watch.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-edit-watch.js
@@ -122,33 +122,33 @@ function testFrameEval() {
         "Should have the right value type for 'document.title = 42'.");
 
       testModification(scope.get("document.title = 42").target, test1, function(scope) {
         testModification(scope.get("aArg").target, test2, function(scope) {
           testModification(scope.get("aArg = 44").target, test3, function(scope) {
             testModification(scope.get("document.title = 43").target, test4, function(scope) {
               testModification(scope.get("document.title").target, test5, function(scope) {
                 testExprDeletion(scope.get("this").target, test6, function(scope) {
-                  testExprDeletion(scope.get("ermahgerd").target, test7, function(scope) {
+                  testExprDeletion(null, test7, function(scope) {
                     resumeAndFinish();
-                  }, 44, 0, true);
+                  }, 44, 0, true, true);
                 }, 44);
               }, " \t\r\n", "\"43\"", 44, 1, true);
             }, " \t\r\ndocument.title \t\r\n", "\"43\"", 44);
           }, " \t\r\ndocument.title \t\r\n", "\"43\"", 44);
         }, "aArg = 44", 44, 44);
       }, "document.title = 43", 43, "undefined");
     }}, 0);
   }, false);
 
   addWatchExpression("this");
   addWatchExpression("ermahgerd");
   addWatchExpression("aArg");
   addWatchExpression("document.title");
-  addWatchExpression("document.title = 42");
+  addCmdWatchExpression("document.title = 42");
 
   executeSoon(function() {
     gDebuggee.ermahgerd(); // ermahgerd!!
   });
 }
 
 function testModification(aVar, aTest, aCallback, aNewValue, aNewResult, aArgResult,
                           aLocalScopeIndex = 1, aDeletionFlag = null)
@@ -241,17 +241,17 @@ function testModification(aVar, aTest, a
     executeSoon(function() {
       aTest(scope);
       aCallback(scope);
     });
   }
 }
 
 function testExprDeletion(aVar, aTest, aCallback, aArgResult,
-                          aLocalScopeIndex = 1, aFinalFlag = null)
+                          aLocalScopeIndex = 1, aFinalFlag = null, aRemoveAllFlag = null)
 {
   let testContinued = false;
   let fetchedVariables = false;
   let fetchedExpressions = false;
 
   let countV = 0;
   gDebugger.addEventListener("Debugger:FetchedVariables", function testV() {
     // We expect 2 Debugger:FetchedVariables events, one from the global
@@ -314,16 +314,21 @@ function testExprDeletion(aVar, aTest, a
 
   function performCallback(scope) {
     executeSoon(function() {
       aTest(scope);
       aCallback(scope);
     });
   }
 
+  if (aRemoveAllFlag) {
+    gWatch._onCmdRemoveAllExpressions();
+    return;
+  }
+
   EventUtils.sendMouseEvent({ type: "click" },
     aVar.querySelector(".dbg-variables-delete"),
     gDebugger);
 }
 
 function test1(scope) {
   is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, 5,
     "There should be 5 hidden nodes in the watch expressions container");
@@ -480,16 +485,21 @@ function test7(scope) {
   is(gWatch._cache.length, 0, "The watch expressions cache should be empty.");
 }
 
 function addWatchExpression(string) {
   gWatch.addExpression(string);
   gDebugger.editor.focus();
 }
 
+function addCmdWatchExpression(string) {
+  gWatch._onCmdAddExpression(string);
+  gDebugger.editor.focus();
+}
+
 function resumeAndFinish() {
   gDebugger.DebuggerController.activeThread.resume(function() {
     closeDebuggerAndFinish();
   });
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-03.js
@@ -39,43 +39,43 @@ function testSearchbox()
   ok(!gDebugger.DebuggerView.Variables._parent.querySelector(".devtools-searchinput"),
     "There searchbox element should not be found.");
 }
 
 function testPref()
 {
   is(gDebugger.Prefs.variablesSearchboxVisible, false,
     "The debugger searchbox should be preffed as hidden.");
-  isnot(gDebugger.DebuggerView.Options._showVariablesSearchboxItem.getAttribute("checked"), "true",
+  isnot(gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.getAttribute("checked"), "true",
     "The options menu item should not be checked.");
 
-  gDebugger.DebuggerView.Options._showVariablesSearchboxItem.setAttribute("checked", "true");
-  gDebugger.DebuggerView.Options._toggleShowVariablesSearchbox();
+  gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.setAttribute("checked", "true");
+  gDebugger.DebuggerView.Options._toggleShowVariablesFilterBox();
 
   executeSoon(function() {
     ok(gDebugger.DebuggerView.Variables._searchboxNode,
       "There should be a searchbox available in the variables view.");
     ok(gDebugger.DebuggerView.Variables._parent.querySelector(".devtools-searchinput"),
       "There searchbox element should be found.");
     is(gDebugger.Prefs.variablesSearchboxVisible, true,
       "The debugger searchbox should now be preffed as visible.");
-    is(gDebugger.DebuggerView.Options._showVariablesSearchboxItem.getAttribute("checked"), "true",
+    is(gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.getAttribute("checked"), "true",
       "The options menu item should now be checked.");
 
-    gDebugger.DebuggerView.Options._showVariablesSearchboxItem.setAttribute("checked", "false");
-    gDebugger.DebuggerView.Options._toggleShowVariablesSearchbox();
+    gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.setAttribute("checked", "false");
+    gDebugger.DebuggerView.Options._toggleShowVariablesFilterBox();
 
     executeSoon(function() {
       ok(!gDebugger.DebuggerView.Variables._searchboxNode,
         "There should not be a searchbox available in the variables view.");
       ok(!gDebugger.DebuggerView.Variables._parent.querySelector(".devtools-searchinput"),
         "There searchbox element should not be found.");
       is(gDebugger.Prefs.variablesSearchboxVisible, false,
         "The debugger searchbox should now be preffed as hidden.");
-      isnot(gDebugger.DebuggerView.Options._showVariablesSearchboxItem.getAttribute("checked"), "true",
+      isnot(gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.getAttribute("checked"), "true",
         "The options menu item should now be unchecked.");
 
       executeSoon(function() {
         Services.prefs.setBoolPref(
           "devtools.debugger.ui.variables-searchbox-visible", gPrevPref);
 
         closeDebuggerAndFinish();
       });
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-04.js
@@ -39,43 +39,43 @@ function testSearchbox()
   ok(gDebugger.DebuggerView.Variables._parent.querySelector(".devtools-searchinput"),
     "There searchbox element should be found.");
 }
 
 function testPref()
 {
   is(gDebugger.Prefs.variablesSearchboxVisible, true,
     "The debugger searchbox should be preffed as visible.");
-  is(gDebugger.DebuggerView.Options._showVariablesSearchboxItem.getAttribute("checked"), "true",
+  is(gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.getAttribute("checked"), "true",
     "The options menu item should be checked.");
 
-  gDebugger.DebuggerView.Options._showVariablesSearchboxItem.setAttribute("checked", "false");
-  gDebugger.DebuggerView.Options._toggleShowVariablesSearchbox();
+  gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.setAttribute("checked", "false");
+  gDebugger.DebuggerView.Options._toggleShowVariablesFilterBox();
 
   executeSoon(function() {
     ok(!gDebugger.DebuggerView.Variables._searchboxNode,
       "There should not be a searchbox available in the variables view.");
     ok(!gDebugger.DebuggerView.Variables._parent.querySelector(".devtools-searchinput"),
       "There searchbox element should not be found.");
     is(gDebugger.Prefs.variablesSearchboxVisible, false,
       "The debugger searchbox should now be preffed as hidden.");
-    isnot(gDebugger.DebuggerView.Options._showVariablesSearchboxItem.getAttribute("checked"), "true",
+    isnot(gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.getAttribute("checked"), "true",
       "The options menu item should now be unchecked.");
 
-    gDebugger.DebuggerView.Options._showVariablesSearchboxItem.setAttribute("checked", "true");
-    gDebugger.DebuggerView.Options._toggleShowVariablesSearchbox();
+    gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.setAttribute("checked", "true");
+    gDebugger.DebuggerView.Options._toggleShowVariablesFilterBox();
 
     executeSoon(function() {
       ok(gDebugger.DebuggerView.Variables._searchboxNode,
         "There should be a searchbox available in the variables view.");
       ok(gDebugger.DebuggerView.Variables._parent.querySelector(".devtools-searchinput"),
         "There searchbox element should be found.");
       is(gDebugger.Prefs.variablesSearchboxVisible, true,
         "The debugger searchbox should now be preffed as visible.");
-      is(gDebugger.DebuggerView.Options._showVariablesSearchboxItem.getAttribute("checked"), "true",
+      is(gDebugger.DebuggerView.Options._showVariablesFilterBoxItem.getAttribute("checked"), "true",
         "The options menu item should now be checked.");
 
       executeSoon(function() {
         Services.prefs.setBoolPref(
           "devtools.debugger.ui.variables-searchbox-visible", gPrevPref);
 
         closeDebuggerAndFinish();
       });
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-01.js
@@ -58,16 +58,51 @@ function testScriptSearching() {
     gSearchBox = gDebugger.DebuggerView.Filtering._searchbox;
     gMenulist = gScripts._container;
 
     write(":12");
     ok(gEditor.getCaretPosition().line == 11 &&
        gEditor.getCaretPosition().col == 0,
       "The editor didn't jump to the correct line.");
 
+    EventUtils.synthesizeKey("g", { metaKey: true });
+    ok(gEditor.getCaretPosition().line == 12 &&
+       gEditor.getCaretPosition().col == 0,
+      "The editor didn't jump to the correct line after Meta+G");
+
+    EventUtils.synthesizeKey("n", { ctrlKey: true });
+    ok(gEditor.getCaretPosition().line == 13 &&
+       gEditor.getCaretPosition().col == 0,
+      "The editor didn't jump to the correct line after Ctrl+N");
+
+    EventUtils.synthesizeKey("G", { metaKey: true, shiftKey: true });
+    ok(gEditor.getCaretPosition().line == 12 &&
+       gEditor.getCaretPosition().col == 0,
+      "The editor didn't jump to the correct line after Meta+Shift+G");
+
+    EventUtils.synthesizeKey("p", { ctrlKey: true });
+    ok(gEditor.getCaretPosition().line == 11 &&
+       gEditor.getCaretPosition().col == 0,
+      "The editor didn't jump to the correct line after Ctrl+P");
+
+    for (let i = 0; i < 100; i++) {
+      EventUtils.sendKey("DOWN");
+    }
+    ok(gEditor.getCaretPosition().line == 32 &&
+       gEditor.getCaretPosition().col == 0,
+      "The editor didn't jump to the correct line after multiple DOWN keys");
+
+    for (let i = 0; i < 100; i++) {
+      EventUtils.sendKey("UP");
+    }
+    ok(gEditor.getCaretPosition().line == 0 &&
+       gEditor.getCaretPosition().col == 0,
+      "The editor didn't jump to the correct line after multiple UP keys");
+
+
     token = "debugger";
     write("#" + token);
     ok(gEditor.getCaretPosition().line == 2 &&
        gEditor.getCaretPosition().col == 44 + token.length,
       "The editor didn't jump to the correct token. (1)");
 
     EventUtils.sendKey("DOWN");
     ok(gEditor.getCaretPosition().line == 8 &&
--- a/browser/devtools/markupview/markup-view.xhtml
+++ b/browser/devtools/markupview/markup-view.xhtml
@@ -6,17 +6,17 @@
 
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
   <link rel="stylesheet" href="chrome://browser/content/devtools/markup-view.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://browser/skin/devtools/markup-view.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://browser/skin/devtools/common.css" type="text/css"/>
 </head>
-<body class="devtools-theme-background" role="application">
+<body class="devtools-theme-background devtools-monospace" role="application">
   <div id="root"></div>
   <div id="templates" style="display:none">
     <ul>
       <li id="template-container" save="${elt}" class="container"><span save="${expander}" class="expander"></span><span save="${codeBox}" class="codebox"><ul save="${children}" class="children"></ul></span></li>
     </ul>
 
     <span id="template-element" save="${elt}" class="editor"><span>&lt;</span><span save="${tag}" class="tagname devtools-theme-tagname"></span><span save="${attrList}"></span><span save="${newAttr}" class="newattr" tabindex="0"></span>&gt;</span>
 
--- a/browser/devtools/shared/VariablesView.jsm
+++ b/browser/devtools/shared/VariablesView.jsm
@@ -134,39 +134,56 @@ VariablesView.prototype = {
       if (!this._store.size) {
         this._appendEmptyNotice();
       }
     }.bind(this), aTimeout);
   },
 
   /**
    * Specifies if enumerable properties and variables should be displayed.
+   * These variables and properties are visible by default.
    * @param boolean aFlag
    */
   set enumVisible(aFlag) {
     this._enumVisible = aFlag;
 
     for (let [, scope] in this) {
       scope._enumVisible = aFlag;
     }
   },
 
   /**
    * Specifies if non-enumerable properties and variables should be displayed.
+   * These variables and properties are visible by default.
    * @param boolean aFlag
    */
   set nonEnumVisible(aFlag) {
     this._nonEnumVisible = aFlag;
 
     for (let [, scope] in this) {
       scope._nonEnumVisible = aFlag;
     }
   },
 
   /**
+   * Specifies if only enumerable properties and variables should be displayed.
+   * Both types of these variables and properties are visible by default.
+   * @param boolean aFlag
+   */
+  set onlyEnumVisible(aFlag) {
+    if (aFlag) {
+      this.enumVisible = true;
+      this.nonEnumVisible = false;
+    } else {
+      this.enumVisible = true;
+      this.nonEnumVisible = true;
+    }
+  },
+
+  /**
    * Enables variable and property searching in this view.
    */
   enableSearch: function VV_enableSearch() {
     // If searching was already enabled, no need to re-enable it again.
     if (this._searchboxContainer) {
       return;
     }
     let document = this.document;
@@ -526,17 +543,21 @@ Scope.prototype = {
     if (this.oncollapse) {
       this.oncollapse(this);
     }
   },
 
   /**
    * Toggles between the scope's collapsed and expanded state.
    */
-  toggle: function S_toggle() {
+  toggle: function S_toggle(e) {
+    if (e && e.button != 0) {
+      // Only allow left-click to trigger this event.
+      return;
+    }
     this._wasToggled = true;
     this.expanded ^= 1;
 
     // Make sure the scope and its contents are visibile.
     for (let [, variable] in this) {
       variable.header = true;
       variable._match = true;
     }
@@ -653,16 +674,21 @@ Scope.prototype = {
 
   /**
    * Specifies if removing variables or properties values is allowed.
    * This flag applies non-recursively to the current scope.
    */
   allowDeletion: false,
 
   /**
+   * Specifies the context menu attribute set on variables and properties.
+   */
+  contextMenu: "",
+
+  /**
    * Gets the id associated with this item.
    * @return string
    */
   get id() this._idString,
 
   /**
    * Gets the name associated with this item.
    * @return string
@@ -699,17 +725,17 @@ Scope.prototype = {
    *        The scope's name.
    * @param string aClassName
    *        A custom class name for this scope.
    */
   _createScope: function S__createScope(aName, aClassName) {
     let document = this.document;
 
     let element = this._target = document.createElement("vbox");
-    element.id = this._id;
+    element.id = this._idString;
     element.className = aClassName;
 
     let arrow = this._arrow = document.createElement("hbox");
     arrow.className = "arrow";
 
     let name = this._name = document.createElement("label");
     name.className = "name plain";
     name.setAttribute("value", aName);
@@ -1206,16 +1232,19 @@ create({ constructor: Variable, proto: S
       valueLabel.hidden = true;
     }
     if (this.ownerView.allowDeletion) {
       let closeNode = this._closeNode = document.createElement("toolbarbutton");
       closeNode.className = "dbg-variables-delete plain devtools-closebutton";
       closeNode.addEventListener("click", this._onClose, false);
       this._title.appendChild(closeNode);
     }
+    if (this.ownerView.contextMenu) {
+      this._title.setAttribute("context", this.ownerView.contextMenu);
+    }
   },
 
   /**
    * Prepares a tooltip for this variable.
    */
   _prepareTooltip: function V__prepareTooltip() {
     this._target.addEventListener("mouseover", this._displayTooltip, false);
   },
@@ -1368,17 +1397,21 @@ create({ constructor: Variable, proto: S
     this._locked = false;
     this.twisty = this._prevExpandable;
     this.expanded = this._prevExpanded;
   },
 
   /**
    * Makes this variable's name editable.
    */
-  _activateNameInput: function V__activateNameInput() {
+  _activateNameInput: function V__activateNameInput(e) {
+    if (e && e.button != 0) {
+      // Only allow left-click to trigger this event.
+      return;
+    }
     if (!this.ownerView.allowNameInput || !this.switch) {
       return;
     }
     this._activateInput(this._name, "element-name-input", {
       onKeypress: this._onNameInputKeyPress,
       onBlur: this._deactivateNameInput
     });
     this._separatorLabel.hidden = true;
@@ -1395,17 +1428,21 @@ create({ constructor: Variable, proto: S
     });
     this._separatorLabel.hidden = false;
     this._valueLabel.hidden = false;
   },
 
   /**
    * Makes this variable's value editable.
    */
-  _activateValueInput: function V__activateValueInput() {
+  _activateValueInput: function V__activateValueInput(e) {
+    if (e && e.button != 0) {
+      // Only allow left-click to trigger this event.
+      return;
+    }
     if (!this.ownerView.allowValueInput || !this.eval) {
       return;
     }
     this._activateInput(this._valueLabel, "element-value-input", {
       onKeypress: this._onValueInputKeyPress,
       onBlur: this._deactivateValueInput
     });
   },
--- a/browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_private_perwindowpb.js
@@ -14,33 +14,41 @@ function test() {
     finish();
   }
 
   function doTest(aWindow) {
     aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
       aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
       cache.evictEntries(Ci.nsICache.STORE_ANYWHERE);
       launchStyleEditorChromeFromWindow(aWindow, function(aChrome) {
-        aChrome.addChromeListener({
-          onEditorAdded: function(aChrome, aEditor) {
-            if (aEditor.isLoaded) {
-              checkCache();
-            } else {
-              aEditor.addActionListener({
-                onLoad: checkCache
-              });
-            }
-          }
-        });
+        if (aChrome.isContentAttached) {
+          onEditorAdded(aChrome, aChrome.editors[0]);
+        } else {
+          aChrome.addChromeListener({
+            onEditorAdded: onEditorAdded
+          });
+        }
       });
     }, true);
 
     aWindow.gBrowser.selectedBrowser.loadURI(testURI);
   }
 
+  function onEditorAdded(aChrome, aEditor) {
+    aChrome.removeChromeListener(this);
+
+    if (aEditor.isLoaded) {
+      checkCache();
+    } else {
+      aEditor.addActionListener({
+        onLoad: checkCache
+      });
+    }
+  }
+
   function testOnWindow(options, callback) {
     let win = OpenBrowserWindow(options);
     win.addEventListener("load", function onLoad() {
       win.removeEventListener("load", onLoad, false);
       windowsToClose.push(win);
       executeSoon(function() callback(win));
     }, false);
   };
--- a/browser/devtools/styleeditor/test/head.js
+++ b/browser/devtools/styleeditor/test/head.js
@@ -24,54 +24,44 @@ function cleanup()
   gChromeWindow = null;
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 }
 
 function launchStyleEditorChrome(aCallback, aSheet, aLine, aCol)
 {
-  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  launchStyleEditorChromeFromWindow(window, aCallback, aSheet, aLine, aCol);
+}
 
-  let panel = gDevTools.getPanelForTarget("styleeditor", target);
+function launchStyleEditorChromeFromWindow(aWindow, aCallback, aSheet, aLine, aCol)
+{
+  let target = TargetFactory.forTab(aWindow.gBrowser.selectedTab);
+
+  let panel = aWindow.gDevTools.getPanelForTarget("styleeditor", target);
   if (panel && panel.isReady) {
     gChromeWindow = panel._panelWin;
     gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
     if (aSheet) {
       panel.selectStyleSheet(aSheet, aLine, aCol);
     }
     aCallback(gChromeWindow.styleEditorChrome);
   } else {
-    let toolbox = gDevTools.openToolboxForTab(target, "styleeditor");
+    let toolbox = aWindow.gDevTools.openToolboxForTab(target, "styleeditor");
     toolbox.once("styleeditor-ready", function(event, panel) {
       gChromeWindow = panel._panelWin;
       gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
       if (aSheet) {
         panel.selectStyleSheet(aSheet, aLine, aCol);
       }
       aCallback(gChromeWindow.styleEditorChrome);
     });
   }
 }
 
-function launchStyleEditorChromeFromWindow(aWindow, aCallback, aSheet, aLine, aCol)
-{
-  gChromeWindow = aWindow.StyleEditor.openChrome(aSheet, aLine, aCol);
-  if (gChromeWindow.document.readyState != "complete") {
-    gChromeWindow.addEventListener("load", function onChromeLoad() {
-      gChromeWindow.removeEventListener("load", onChromeLoad, true);
-      gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
-      aCallback(gChromeWindow.styleEditorChrome);
-    }, true);
-  } else {
-    gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
-    aCallback(gChromeWindow.styleEditorChrome);
-  }
-}
-
 function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback, aSheet, aLine, aCol)
 {
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     launchStyleEditorChrome(aCallback, aSheet, aLine, aCol);
   }, true);
 }
--- a/browser/devtools/tilt/CmdTilt.jsm
+++ b/browser/devtools/tilt/CmdTilt.jsm
@@ -25,40 +25,35 @@ gcli.addCommand({
  */
 gcli.addCommand({
   name: 'tilt open',
   description: gcli.lookup("tiltOpenDesc"),
   manual: gcli.lookup("tiltOpenManual"),
   exec: function(args, context) {
     let chromeWindow = context.environment.chromeDocument.defaultView;
     let Tilt = TiltManager.getTiltForBrowser(chromeWindow);
-    Tilt.initializeForCurrentTab();
+    if (!Tilt.currentInstance) {
+      Tilt.toggle();
+    }
   }
 });
 
 
 /**
  * 'tilt toggle' command
  */
 gcli.addCommand({
   name: "tilt toggle",
   buttonId: "command-button-tilt",
   buttonClass: "command-button  devtools-toolbarbutton",
   hidden: true,
   exec: function(args, context) {
     let chromeWindow = context.environment.chromeDocument.defaultView;
-
-    if (TiltManager._instances.has(chromeWindow)) {
-      let Tilt = TiltManager.getTiltForBrowser(chromeWindow);
-      Tilt.destroy(Tilt.currentWindowId);
-    }
-    else {
-      let Tilt = TiltManager.getTiltForBrowser(chromeWindow);
-      Tilt.initializeForCurrentTab();
-    }
+    let Tilt = TiltManager.getTiltForBrowser(chromeWindow);
+    Tilt.toggle();
   }
 });
 
 
 /**
  * 'tilt translate' command
  */
 gcli.addCommand({
--- a/browser/devtools/tilt/Tilt.jsm
+++ b/browser/devtools/tilt/Tilt.jsm
@@ -88,19 +88,19 @@ this.Tilt = function Tilt(aWindow)
   this.NOTIFICATIONS = TILT_NOTIFICATIONS;
 
   this.setup();
 }
 
 Tilt.prototype = {
 
   /**
-   * Initializes a visualizer for the current tab.
+   * Initializes a visualizer for the current tab or closes it if already open.
    */
-  initializeForCurrentTab: function T_initializeForCurrentTab()
+  toggle: function T_toggle()
   {
     let contentWindow = this.chromeWindow.gBrowser.selectedBrowser.contentWindow;
     let id = this.currentWindowId;
     let self = this;
 
     contentWindow.addEventListener("beforeunload", function onUnload() {
       contentWindow.removeEventListener("beforeunload", onUnload, false);
       self.destroy(id, true);
--- a/browser/devtools/tilt/test/head.js
+++ b/browser/devtools/tilt/test/head.js
@@ -146,17 +146,17 @@ function createTilt(callbacks, close, su
 
   handleFailure(suddenDeath);
 
   Services.prefs.setBoolPref("webgl.verbose", true);
   TiltUtils.Output.suppressAlerts = true;
 
   info("Attempting to start Tilt.");
   Services.obs.addObserver(onTiltOpen, INITIALIZING, false);
-  Tilt.initializeForCurrentTab();
+  Tilt.toggle();
 
   function onTiltOpen() {
     info("Tilt was opened.");
     Services.obs.removeObserver(onTiltOpen, INITIALIZING);
 
     executeSoon(function() {
       if ("function" === typeof callbacks.onTiltOpen) {
         info("Calling 'onTiltOpen'.");
--- a/browser/locales/en-US/chrome/browser/aboutPrivateBrowsing.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutPrivateBrowsing.dtd
@@ -5,21 +5,16 @@
 <!ENTITY privatebrowsingpage.title                 "Private Browsing">
 <!ENTITY privatebrowsingpage.title.normal          "Would you like to start Private Browsing?">
 
 <!ENTITY privatebrowsingpage.issueDesc                 "&brandShortName; won't remember any history for this session.">
 <!ENTITY privatebrowsingpage.issueDesc.normal          "&brandShortName; is not currently in Private Browsing mode.">
 
 <!ENTITY privatebrowsingpage.description               "In a Private Browsing session, &brandShortName; won't keep any browser history, search history, download history, web form history, cookies, or temporary internet files.  However, files you download and bookmarks you make will be kept.">
 
-<!-- LOCALIZATION NOTE (privatebrowsingpage.clearRecentHistoryBefore): include a trailing space as needed -->
-<!-- LOCALIZATION NOTE (privatebrowsingpage.clearRecentHistoryAfter): include a starting space as needed -->
-<!ENTITY privatebrowsingpage.clearRecentHistoryBefore  "You may want to start by also ">
-<!ENTITY privatebrowsingpage.clearRecentHistoryInner   "clearing your recent history">
-<!ENTITY privatebrowsingpage.clearRecentHistoryAfter   ".">
 
 <!ENTITY privatebrowsingpage.startPrivateBrowsing.label "Start Private Browsing">
 <!ENTITY privatebrowsingpage.startPrivateBrowsing.accesskey "P">
 
 <!-- LOCALIZATION NOTE (privatebrowsingpage.howToStop2): please leave &basePBMenu.label; intact in the translation -->
 <!-- LOCALIZATION NOTE (privatebrowsingpage.howToStart2): please leave &basePBMenu.label; intact in the translation -->
 <!ENTITY privatebrowsingpage.howToStop2                "To stop Private Browsing, select &basePBMenu.label; &gt; &privateBrowsingCmd.stop.label;, or close &brandShortName;.">
 <!ENTITY privatebrowsingpage.howToStart2               "To start Private Browsing, you can also select &basePBMenu.label; &gt; &privateBrowsingCmd.start.label;.">
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
@@ -48,26 +48,27 @@
 <!ENTITY debuggerUI.pauseExceptions     "Pause on exceptions">
 <!ENTITY debuggerUI.pauseExceptions.key "E">
 
 <!-- LOCALIZATION NOTE (debuggerUI.showPanesOnInit): This is the label for the
   -  checkbox that toggles visibility of panes when opening the debugger. -->
 <!ENTITY debuggerUI.showPanesOnInit     "Show panes on startup">
 <!ENTITY debuggerUI.showPanesOnInit.key "S">
 
-<!-- LOCALIZATION NOTE (debuggerUI.showVarsSearch): This is the label for the
-  -  checkbox that toggles visibility of a designated variables searchbox. -->
-<!ENTITY debuggerUI.showVarsSearch      "Show variables searchbox">
-<!ENTITY debuggerUI.showVarsSearch.key  "V">
+<!-- LOCALIZATION NOTE (debuggerUI.showVarsFilter): This is the label for the
+  -  checkbox that toggles visibility of a designated variables filter box. -->
+<!ENTITY debuggerUI.showVarsFilter      "Show variables filter box">
+<!ENTITY debuggerUI.showVarsFilter.key  "V">
 
-<!-- LOCALIZATION NOTE (debuggerUI.showNonEnums): This is the label for the
+<!-- LOCALIZATION NOTE (debuggerUI.showOnlyEnum): This is the label for the
   -  checkbox that toggles visibility of hidden (non-enumerable) variables and
-  -  properties in stack views. -->
-<!ENTITY debuggerUI.showNonEnums        "Show hidden properties">
-<!ENTITY debuggerUI.showNonEnums.key    "P">
+  -  properties in stack views. The "enumerable" flag is a state of a property
+  -  defined in JavaScript. When in doubt, leave untranslated. -->
+<!ENTITY debuggerUI.showOnlyEnum        "Show only enumerable properties">
+<!ENTITY debuggerUI.showOnlyEnum.key    "P">
 
 <!-- LOCALIZATION NOTE (debuggerUI.searchPanelTitle): This is the text that
   -  appears in the filter panel popup as a description. -->
 <!ENTITY debuggerUI.searchPanelTitle    "Operators">
 
 <!-- LOCALIZATION NOTE (debuggerUI.searchFile): This is the text that appears
   -  in the source editor's context menu for the scripts search operation. -->
 <!ENTITY debuggerUI.searchFile          "Filter scripts">
@@ -103,16 +104,30 @@
 <!ENTITY debuggerUI.seMenuBreak.key     "B">
 
 <!-- LOCALIZATION NOTE (debuggerUI.seMenuCondBreak): This is the text that
   -  appears in the source editor context menu for adding a conditional
   -  breakpoint. -->
 <!ENTITY debuggerUI.seMenuCondBreak     "Add conditional breakpoint">
 <!ENTITY debuggerUI.seMenuCondBreak.key "B">
 
+<!-- LOCALIZATION NOTE (debuggerUI.seMenuAddWatch): This is the text that
+  -  appears in the source editor context menu for adding an expression. -->
+<!ENTITY debuggerUI.seMenuAddWatch      "Selection to watch expression">
+<!ENTITY debuggerUI.seMenuAddWatch.key  "E">
+
+<!-- LOCALIZATION NOTE (debuggerUI.addWatch): This is the text that
+  -  appears in the watch expressions context menu for adding an expression. -->
+<!ENTITY debuggerUI.addWatch            "Add watch expression">
+<!ENTITY debuggerUI.addWatch.key        "E">
+
+<!-- LOCALIZATION NOTE (debuggerUI.removeWatch): This is the text that
+  -  appears in the watch expressions context menu for removing all expressions. -->
+<!ENTITY debuggerUI.removeAllWatch      "Remove all watch expressions">
+<!ENTITY debuggerUI.removeAllWatch.key  "E">
+
 <!-- LOCALIZATION NOTE (debuggerUI.stepping): These are the keycodes that
   -  control the stepping commands in the debugger (continue, step over,
   -  step in and step out). -->
 <!ENTITY debuggerUI.stepping.resume     "VK_F6">
 <!ENTITY debuggerUI.stepping.stepOver   "VK_F7">
 <!ENTITY debuggerUI.stepping.stepIn     "VK_F8">
 <!ENTITY debuggerUI.stepping.stepOut    "VK_F8">
-
--- a/browser/modules/NewTabUtils.jsm
+++ b/browser/modules/NewTabUtils.jsm
@@ -14,88 +14,201 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
   "resource:///modules/PageThumbs.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+  "resource://gre/modules/FileUtils.jsm");
+
 XPCOMUtils.defineLazyGetter(this, "gPrincipal", function () {
   let uri = Services.io.newURI("about:newtab", null, null);
   return Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
 });
 
+XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function () {
+  return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
+});
+
+XPCOMUtils.defineLazyGetter(this, "gUnicodeConverter", function () {
+  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                    .createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = 'utf8';
+  return converter;
+});
+
 // The preference that tells whether this feature is enabled.
 const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
 
 // The preference that tells the number of rows of the newtab grid.
 const PREF_NEWTAB_ROWS = "browser.newtabpage.rows";
 
 // The preference that tells the number of columns of the newtab grid.
 const PREF_NEWTAB_COLUMNS = "browser.newtabpage.columns";
 
 // The maximum number of results we want to retrieve from history.
 const HISTORY_RESULTS_LIMIT = 100;
 
 // The gather telemetry topic.
 const TOPIC_GATHER_TELEMETRY = "gather-telemetry";
 
 /**
+ * Calculate the MD5 hash for a string.
+ * @param aValue
+ *        The string to convert.
+ * @return The base64 representation of the MD5 hash.
+ */
+function toHash(aValue) {
+  let value = gUnicodeConverter.convertToByteArray(aValue);
+  gCryptoHash.init(gCryptoHash.MD5);
+  gCryptoHash.update(value, value.length);
+  return gCryptoHash.finish(true);
+}
+
+/**
  * Singleton that provides storage functionality.
  */
-let Storage = {
+XPCOMUtils.defineLazyGetter(this, "Storage", function() {
+  return new LinksStorage();
+});
+
+function LinksStorage() {
+  // Handle migration of data across versions.
+  try {
+    if (this._storedVersion < this._version) {
+      // This is either an upgrade, or version information is missing.
+      if (this._storedVersion < 1) {
+        this._migrateToV1();
+      }
+      // Add further migration steps here.
+    }
+    else {
+      // This is a downgrade.  Since we cannot predict future, upgrades should
+      // be backwards compatible.  We will set the version to the old value
+      // regardless, so, on next upgrade, the migration steps will run again.
+      // For this reason, they should also be able to run multiple times, even
+      // on top of an already up-to-date storage.
+    }
+  } catch (ex) {
+    // Something went wrong in the update process, we can't recover from here,
+    // so just clear the storage and start from scratch (dataloss!).
+    Components.utils.reportError(
+      "Unable to migrate the newTab storage to the current version. "+
+      "Restarting from scratch.\n" + ex);
+    this.clear();
+  }
+
+  // Set the version to the current one.
+  this._storedVersion = this._version;
+}
+
+LinksStorage.prototype = {
+  get _version() 1,
+
+  get _prefs() Object.freeze({
+    pinnedLinks: "browser.newtabpage.pinned",
+    blockedLinks: "browser.newtabpage.blocked",
+  }),
+
+  get _storedVersion() {
+    if (this.__storedVersion === undefined) {
+      try {
+        this.__storedVersion =
+          Services.prefs.getIntPref("browser.newtabpage.storageVersion");
+      } catch (ex) {
+        this.__storedVersion = 0;
+      }
+    }
+    return this.__storedVersion;
+  },
+  set _storedVersion(aValue) {
+    Services.prefs.setIntPref("browser.newtabpage.storageVersion", aValue);
+    this.__storedVersion = aValue;
+    return aValue;
+  },
+
   /**
-   * The dom storage instance used to persist data belonging to the New Tab Page.
+   * V1 changes storage from chromeappsstore.sqlite to prefs.
    */
-  get domStorage() {
-    let sm = Services.domStorageManager;
-    let storage = sm.getLocalStorageForPrincipal(gPrincipal, "");
-
-    // Cache this value, overwrite the getter.
-    let descriptor = {value: storage, enumerable: true};
-    Object.defineProperty(this, "domStorage", descriptor);
-
-    return storage;
+  _migrateToV1: function Storage__migrateToV1() {
+    // Import data from the old chromeappsstore.sqlite file, if exists.
+    let file = FileUtils.getFile("ProfD", ["chromeappsstore.sqlite"]);
+    if (!file.exists())
+      return;
+    let db = Services.storage.openUnsharedDatabase(file);
+    let stmt = db.createStatement(
+      "SELECT key, value FROM webappsstore2 WHERE scope = 'batwen.:about'");
+    try {
+      while (stmt.executeStep()) {
+        let key = stmt.row.key;
+        let value = JSON.parse(stmt.row.value);
+        switch (key) {
+          case "pinnedLinks":
+            this.set(key, value);
+            break;
+          case "blockedLinks":
+            // Convert urls to hashes.
+            let hashes = {};
+            for (let url in value) {
+              hashes[toHash(url)] = 1;
+            }
+            this.set(key, hashes);
+            break;
+          default:
+            // Ignore unknown keys.
+            break;
+        }
+      }
+    } finally {
+      stmt.finalize();
+      db.close();
+    }
   },
 
   /**
    * Gets the value for a given key from the storage.
    * @param aKey The storage key (a string).
    * @param aDefault A default value if the key doesn't exist.
    * @return The value for the given key.
    */
   get: function Storage_get(aKey, aDefault) {
     let value;
-
     try {
-      value = JSON.parse(this.domStorage.getItem(aKey));
+      let prefValue = Services.prefs.getComplexValue(this._prefs[aKey],
+                                                     Ci.nsISupportsString).data;
+      value = JSON.parse(prefValue);
     } catch (e) {}
-
     return value || aDefault;
   },
 
   /**
    * Sets the storage value for a given key.
    * @param aKey The storage key (a string).
    * @param aValue The value to set.
    */
   set: function Storage_set(aKey, aValue) {
-    this.domStorage.setItem(aKey, JSON.stringify(aValue));
+    // Page titles may contain unicode, thus use complex values.
+    let string = Cc["@mozilla.org/supports-string;1"]
+                   .createInstance(Ci.nsISupportsString);
+    string.data = JSON.stringify(aValue);
+    Services.prefs.setComplexValue(this._prefs[aKey], Ci.nsISupportsString,
+                                   string);
   },
 
   /**
    * Clears the storage and removes all values.
    */
   clear: function Storage_clear() {
-    this.domStorage.clear();
-  },
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference])
+    for each (let pref in this._prefs) {
+      Services.prefs.clearUserPref(pref);
+    }
+  }
 };
 
 
 /**
  * Singleton that serves as a registry for all open 'New Tab Page's.
  */
 let AllPages = {
   /**
@@ -350,47 +463,47 @@ let BlockedLinks = {
     return this._links;
   },
 
   /**
    * Blocks a given link.
    * @param aLink The link to block.
    */
   block: function BlockedLinks_block(aLink) {
-    this.links[aLink.url] = 1;
+    this.links[toHash(aLink.url)] = 1;
     this.save();
 
     // Make sure we unpin blocked links.
     PinnedLinks.unpin(aLink);
   },
 
   /**
    * Unblocks a given link.
    * @param aLink The link to unblock.
    */
   unblock: function BlockedLinks_unblock(aLink) {
     if (this.isBlocked(aLink)) {
-      delete this.links[aLink.url];
+      delete this.links[toHash(aLink.url)];
       this.save();
     }
   },
 
   /**
    * Saves the current list of blocked links.
    */
   save: function BlockedLinks_save() {
     Storage.set("blockedLinks", this.links);
   },
 
   /**
    * Returns whether a given link is blocked.
    * @param aLink The link to check.
    */
   isBlocked: function BlockedLinks_isBlocked(aLink) {
-    return (aLink.url in this.links);
+    return (toHash(aLink.url) in this.links);
   },
 
   /**
    * Checks whether the list of blocked links is empty.
    * @return Whether the list is empty.
    */
   isEmpty: function BlockedLinks_isEmpty() {
     return Object.keys(this.links).length == 0;
--- a/browser/modules/test/Makefile.in
+++ b/browser/modules/test/Makefile.in
@@ -4,16 +4,19 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
+
+XPCSHELL_TESTS = unit
+
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
                  browser_NetworkPrioritizer.js \
                  browser_TelemetryTimestamps.js \
                  # bug 793906 - temporarily disabling desktop UI while working on b2g
                  # browser_SignInToWebsite.js \
                  $(NULL)
--- a/browser/modules/test/browser_TelemetryTimestamps.js
+++ b/browser/modules/test/browser_TelemetryTimestamps.js
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function getSimpleMeasurementsFromTelemetryPing() {
-  const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
-  let str = Cc['@mozilla.org/supports-string;1'].createInstance(Ci.nsISupportsString);
-  TelemetryPing.observe(str, "get-payload", "");
+  const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
+  let ping = TelemetryPing.getPayload();
 
-  return JSON.parse(str.data).simpleMeasurements;
+  return ping.simpleMeasurements;
 }
 
 function test() {
   // Test the module logic
   let tmp = {};
   Cu.import("resource:///modules/TelemetryTimestamps.jsm", tmp);
   let TelemetryTimestamps = tmp.TelemetryTimestamps;
   let now = Date.now();
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..15d309df50acc58e8544c23e43d5dedadf659308
GIT binary patch
literal 262144
zc%1FhL2ndg7y#he6)BqZ;)QUrlWm|7>o!mirU|J976U0LwIo$z$n197PP(1h?9Q?X
zwo5&E;p9K?WIXsQy!a11c;e=v|H0|D6-qQfdRT<#nS7i1=KbFH{pOqf;?{hU$KguW
z-iY#WI(QhAO2H>#7zE`XgCICjjJNkkxfrG3iKp-XLs8?y$7e=&@A~fr{mtSB00000
z000000000000000000000000000000004mhiKIMo@`H)e_GpqW$M-sotQFr`kH5V$
znD*j&)M|A)dDf0Eymfh@Hn&&{Z{NIj>vk=?cJoSYDSRd&{Oo3UDCz8A!DLvhIJX^?
zMovx?y=L>M-i!~eJ2WZpcUqjgG+#ShFWf5_F4mS72UYj)?nce-!P8FM=(gixgNwCK
zYYUTM)=T5|Aa-taYbPjgjRjj{{ce9@?;7?O00000004mDYrHe;9RL6T0000yas9@y
zcK`qY006I(@BaK~W!QTF000000000000000hG}c3JU8q;4A0h1>GHok49>hrYA}1G
z&cW|TNPl%~Uw?FEWMcgLs1($ryceg{nW&z1^H!3k@$!6<u6MqEQ0catm6^&~p0{SE
zrdG0cccW9?$nGc2W^_ME<7(Dkow}REy+NqjT5HX&T)Z${nXKeV-i(XXt4TXv$?k=h
zx=C}n6Fz?UQ@Ef1RgoWF9h9tWPCnDB)r`_OKWH^*^HkB#2hs4xUbM3L&F%~RaJ2Jy
zfxk}S*XNz;$fIOr;`H{H&zx$#nKjn;kM+U9OZIxb>Ppmz>shv5ZDbo$m6`LC2f^j(
z<@(^^TDB4Y6X+C?jVNhWSF>!j84nih&ZlNOYj>lI=f{(@p4}^!ZwBx7mx6NtRM7vp
zf2#k>-gUGi0ssI20000000000000000000000000000000002swetS4iScqh%6oBI
zor&sMH*Y0r8ZXZ$>3Zk*Z)3+MP9F}}n^|N1ApTcT;pdlYIR2b?|Bs;hQU%YxTz>!n
z000000000000000000000000000000000000C?jaE5`9+j1=R<{u}@R0000000000
z00000000000000000000000000000000000000000000000000000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
T000000000000000c*Fb+Ewct~
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/unit/test_newtab-migrate-v1.js
@@ -0,0 +1,98 @@
+/* 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;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource:///modules/NewTabUtils.jsm");
+Cu.import("resource://gre/modules/commonjs/promise/core.js");
+Cu.import("resource://gre/modules/Services.jsm");
+
+/**
+ * Asynchronously load test data from chromeappstore.sqlite.
+ *
+ * @param aDBFile
+ *        the database file to load
+ * @return {Promise} resolved when the load is complete
+  */
+function promiseLoadChromeAppsStore(aDBFile) {
+  let deferred = Promise.defer();
+
+  let pinnedLinks = [];
+  let blockedLinks = [];
+
+  let db = Services.storage.openUnsharedDatabase(aDBFile);
+  let stmt = db.createAsyncStatement(
+    "SELECT key, value FROM webappsstore2 WHERE scope = 'batwen.:about'");
+  try {
+    stmt.executeAsync({
+      handleResult: function(aResultSet) {
+        for (let row = aResultSet.getNextRow(); row;
+             row = aResultSet.getNextRow()) {
+          let value = JSON.parse(row.getResultByName("value"));
+          if (row.getResultByName("key") == "pinnedLinks") {
+            pinnedLinks = value;
+          } else {
+            for (let url of Object.keys(value)) {
+              blockedLinks.push({ url: url, title: "" });
+            }
+          }
+        }
+      },
+      handleError: function(aError) {
+        deferred.reject(new Components.Exception("Error", Cr.NS_ERROR_FAILURE));
+      },
+      handleCompletion: function(aReason) {
+        if (aReason === Ci.mozIStorageStatementCallback.REASON_FINISHED) {
+          deferred.resolve([pinnedLinks, blockedLinks]);
+        }
+      }
+    });
+  } finally {
+    stmt.finalize();
+    db.asyncClose();
+  }
+
+  return deferred.promise;
+}
+
+function run_test() {
+  do_test_pending();
+
+  // First of all copy the chromeappsstore.sqlite file to the profile folder.
+  let dbFile = do_get_file("chromeappsstore.sqlite");
+  let profileDBFile = do_get_profile();
+  dbFile.copyTo(profileDBFile, "chromeappsstore.sqlite");
+  profileDBFile.append("chromeappsstore.sqlite");
+  do_check_true(profileDBFile.exists());
+
+  // Load test data from the database.
+  promiseLoadChromeAppsStore(dbFile).then(function success(aResults) {
+    let [pinnedLinks, blockedLinks] = aResults;
+
+    do_check_true(pinnedLinks.length > 0);
+    do_check_eq(pinnedLinks.length, NewTabUtils.pinnedLinks.links.length);
+    do_check_true(pinnedLinks.every(
+      function(aLink) NewTabUtils.pinnedLinks.isPinned(aLink)
+    ));
+
+    do_check_true(blockedLinks.length > 0);
+    do_check_eq(blockedLinks.length,
+                Object.keys(NewTabUtils.blockedLinks.links).length);
+    do_check_true(blockedLinks.every(
+      function(aLink) NewTabUtils.blockedLinks.isBlocked(aLink)
+    ));
+
+    try {
+      profileDBFile.remove(true);
+    } catch (ex) {
+      // May fail due to OS file locking, not a blocking error though.
+      do_print("Unable to remove chromeappsstore.sqlite file.");
+    }
+
+    do_test_finished();
+  }, do_report_unexpected_exception);
+}
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/unit/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+tail =
+
+[test_newtab-migrate-v1.js]
--- a/browser/themes/gnomestripe/devtools/common.css
+++ b/browser/themes/gnomestripe/devtools/common.css
@@ -1,13 +1,20 @@
 /* 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/. */
 
+/* Font for code */
+
+.devtools-monospace {
+  font: message-box;
+  font-family: monospace;
+}
+
 /* Toolbar and Toolbar items */
 
 .devtools-toolbar {
   -moz-appearance: none;
   padding: 4px 3px;
   box-shadow: 0 1px 0 0 hsla(210, 16%, 76%, .2) inset;
   background: -moz-linear-gradient(top, hsl(210,11%,36%), hsl(210,11%,18%));
   color: hsl(210,30%,85%);
--- a/browser/themes/gnomestripe/devtools/csshtmltree.css
+++ b/browser/themes/gnomestripe/devtools/csshtmltree.css
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 :root {
   -moz-appearance: none;
   background: -moz-Field;
   color: -moz-FieldText;
+  font: message-box;
+  font-family: monospace;
 }
 
 /* Take away these two :visited rules to get a core dumper     */
 /* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
 .link,
 .link:visited {
   color: #0091ff;
 }
--- a/browser/themes/gnomestripe/devtools/markup-view.css
+++ b/browser/themes/gnomestripe/devtools/markup-view.css
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 * {
   padding: 0;
   margin: 0;
 }
 
 body {
-  font: message-box;
   color: hsl(0,0%,50%);
 }
 
 .text {
   color: black;
 }
 
 .newattr {
--- a/browser/themes/gnomestripe/downloads/downloads.css
+++ b/browser/themes/gnomestripe/downloads/downloads.css
@@ -19,17 +19,17 @@
 }
 
 #downloadsHistory {
   background: transparent;
   color: -moz-nativehyperlinktext;
   cursor: pointer;
 }
 
-#downloadsPanel[hasdownloads] #downloadsFooter {
+#downloadsPanel[hasdownloads] > #downloadsFooter {
   border-top: 1px solid ThreeDShadow;
   background-image: -moz-linear-gradient(hsla(0,0%,0%,.15), hsla(0,0%,0%,.08) 6px);
 }
 
 #downloadsHistory > .button-box {
   margin: 1em;
 }
 
@@ -168,62 +168,62 @@ richlistitem[type="download"][state="1"]
 }
 .downloadButton.downloadRetry:hover {
   -moz-image-region: rect(32px, 32px, 48px, 16px);
 }
 .downloadButton.downloadRetry:active {
   -moz-image-region: rect(32px, 48px, 48px, 32px);
 }
 
-richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow {
+richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow {
   -moz-image-region: rect(48px, 16px, 64px, 0px);
 }
-richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:hover {
+richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:hover {
   -moz-image-region: rect(48px, 32px, 64px, 16px);
 }
-richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:active {
+richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:active {
   -moz-image-region: rect(48px, 48px, 64px, 32px);
 }
 
 /*** Status and progress indicator ***/
 
 #downloads-indicator-anchor {
   /* Makes the outermost stack element positioned, so that its contents are
      rendered over the main browser window in the Z order.  This is required by
      the animated event notification. */
   position: relative;
 }
 
-toolbar[iconsize="small"] #downloads-indicator-anchor {
+toolbar[iconsize="small"] > #downloads-indicator > #downloads-indicator-anchor {
   min-width: 16px;
   min-height: 16px;
 }
 
-toolbar[iconsize="large"] #downloads-indicator-anchor {
+toolbar[iconsize="large"] > #downloads-indicator > #downloads-indicator-anchor {
   min-width: 24px;
   min-height: 24px;
 }
 
 /*** Main indicator icon ***/
 
-toolbar[iconsize="small"] #downloads-indicator-icon {
+toolbar[iconsize="small"] > #downloads-indicator > #downloads-indicator-anchor > #downloads-indicator-icon {
   background: -moz-image-rect(url("chrome://browser/skin/Toolbar-small.png"),
                               0, 16, 16, 0) center no-repeat;
 }
 
-toolbar[iconsize="large"] #downloads-indicator-icon {
+toolbar[iconsize="large"] > #downloads-indicator > #downloads-indicator-anchor > #downloads-indicator-icon {
   background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"),
                               0, 24, 24, 0) center no-repeat;
 }
 
-toolbar[iconsize="small"] #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
+toolbar[iconsize="small"] > #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
   background-image: url("chrome://browser/skin/downloads/download-glow-small.png");
 }
 
-toolbar[iconsize="large"] #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
+toolbar[iconsize="large"] > #downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
   background-image: url("chrome://browser/skin/downloads/download-glow.png");
 }
 
 #downloads-indicator:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
   background: -moz-image-rect(url("chrome://browser/skin/Toolbar-small.png"),
                               0, 16, 16, 0) center no-repeat;
   background-size: 12px;
 }
--- a/browser/themes/pinstripe/devtools/common.css
+++ b/browser/themes/pinstripe/devtools/common.css
@@ -1,15 +1,22 @@
 /* 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/. */
 
 %include ../shared.inc
 
+/* Font for code */
+
+.devtools-monospace {
+  font: message-box;
+  font-family: monospace;
+}
+
 /* Toolbar and Toolbar items */
 
 .devtools-toolbar {
   -moz-appearance: none;
   padding: 4px 3px;
   box-shadow: 0 1px 0 0 hsla(210, 16%, 76%, .2) inset;
   background-image: url(background-noise-toolbar.png), -moz-linear-gradient(top, hsl(210,11%,36%), hsl(210,11%,18%));
   color: hsl(210,30%,85%);
--- a/browser/themes/pinstripe/devtools/csshtmltree.css
+++ b/browser/themes/pinstripe/devtools/csshtmltree.css
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 :root {
   -moz-appearance: none;
   background: -moz-Field;
   color: -moz-FieldText;
+  font: message-box;
+  font-family: monospace;
 }
 
 /* Take away these two :visited rules to get a core dumper     */
 /* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
 .link,
 .link:visited {
   color: #0091ff;
 }
--- a/browser/themes/pinstripe/devtools/markup-view.css
+++ b/browser/themes/pinstripe/devtools/markup-view.css
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 * {
   padding: 0;
   margin: 0;
 }
 
 body {
-  font: message-box;
   color: hsl(0,0%,50%);
 }
 
 .text {
   color: black;
 }
 
 .newattr {
--- a/browser/themes/pinstripe/downloads/downloads.css
+++ b/browser/themes/pinstripe/downloads/downloads.css
@@ -21,39 +21,39 @@
 #downloadsHistory {
   background: transparent;
   border-bottom-left-radius: 6px;
   border-bottom-right-radius: 6px;
   color: hsl(210,100%,75%);
   cursor: pointer;
 }
 
-#downloadsPanel:not([hasdownloads]) #downloadsHistory {
+#downloadsPanel:not([hasdownloads]) > #downloadsFooter > #downloadsHistory {
   border-top-left-radius: 6px;
   border-top-right-radius: 6px;
 }
 
-#downloadsPanel[hasdownloads] #downloadsFooter {
+#downloadsPanel[hasdownloads] > #downloadsFooter {
   background: #e5e5e5;
   border-top: 1px solid hsla(0,0%,0%,.1);
   box-shadow: 0 -1px hsla(0,0%,100%,.5) inset, 0 1px 1px hsla(0,0%,0%,.03) inset;
 }
 
 #downloadsHistory > .button-box {
   color: #808080;
   margin: 1em;
 }
 
 #downloadsHistory:-moz-focusring > .button-box {
   outline: 1px -moz-dialogtext dotted;
   border-top-left-radius: 6px;
   border-top-right-radius: 6px;
 }
 
-#downloadsPanel:not([hasdownloads]) #downloadsHistory:-moz-focusring > .button-box {
+#downloadsPanel:not([hasdownloads]) > #downloadsFooter > #downloadsHistory:-moz-focusring > .button-box {
   border-bottom-left-radius: 6px;
   border-bottom-right-radius: 6px;
 }
 
 /*** Downloads Summary and List items ***/
 
 #downloadsSummary,
 richlistitem[type="download"] {
@@ -159,23 +159,23 @@ richlistitem[type="download"][state="1"]
 
 .downloadButton.downloadCancel {
   -moz-image-region: rect(0px, 16px, 16px, 0px);
 }
 
 .downloadButton.downloadShow {
   -moz-image-region: rect(16px, 16px, 32px, 0px);
 }
-richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow {
+richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow {
   -moz-image-region: rect(16px, 32px, 32px, 16px);
 }
-richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:hover {
+richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:hover {
   -moz-image-region: rect(16px, 48px, 32px, 32px);
 }
-richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:active {
+richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:active {
   -moz-image-region: rect(16px, 64px, 32px, 48px);
 }
 
 .downloadButton.downloadRetry {
   -moz-image-region: rect(32px, 16px, 48px, 0px);
 }
 
 /*** Status and progress indicator ***/
@@ -214,27 +214,28 @@ richlistitem[type="download"][state="1"]
 }
 
 @media (min-resolution: 2dppx) {
   #downloads-indicator-icon:not(:-moz-lwtheme-brighttext) {
     background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 280, 40, 240);
     background-size: 20px;
   }
 
-  #downloads-indicator:not([counter]) #downloads-indicator-counter {
+  #downloads-indicator:not([counter]) > #downloads-indicator-anchor >
+  #downloads-indicator-progress-area > #downloads-indicator-counter {
     background-image: -moz-image-rect(url("chrome://browser/skin/Toolbar@2x.png"), 0, 280, 40, 240);
   }
 
-  #downloads-indicator[attention]
+  #downloads-indicator[attention] > #downloads-indicator-anchor >
   #downloads-indicator-icon {
     background-image: url("chrome://browser/skin/downloads/download-glow@2x.png");
   }
 
-  #downloads-indicator:not([counter])[attention]
-  #downloads-indicator-counter {
+  #downloads-indicator:not([counter])[attention] > #downloads-indicator-anchor >
+  #downloads-indicator-progress-area > #downloads-indicator-counter {
     background-image: url("chrome://browser/skin/downloads/download-glow@2x.png");
   }
 }
 
 /*** Event notification ***/
 
 #downloads-indicator-notification {
   opacity: 0;
--- a/browser/themes/winstripe/devtools/common.css
+++ b/browser/themes/winstripe/devtools/common.css
@@ -1,13 +1,20 @@
 /* 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/. */
 
+/* Font for code */
+
+.devtools-monospace {
+  font: message-box;
+  font-family: monospace;
+}
+
 /* Toolbar and Toolbar items */
 
 .devtools-toolbar {
   -moz-appearance: none;
   padding: 4px 3px;
   box-shadow: 0 1px 0 hsla(209,29%,72%,.25) inset;
   background-image: -moz-linear-gradient(top, hsl(209,18%,34%), hsl(210,24%,16%));
   color: hsl(210,30%,85%);
--- a/browser/themes/winstripe/devtools/csshtmltree.css
+++ b/browser/themes/winstripe/devtools/csshtmltree.css
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 :root {
   -moz-appearance: none;
   background: -moz-Field;
   color: -moz-FieldText;
+  font: message-box;
+  font-family: monospace;
 }
 
 /* Take away these two :visited rules to get a core dumper     */
 /* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
 .link,
 .link:visited {
   color: #0091ff;
 }
--- a/browser/themes/winstripe/devtools/markup-view.css
+++ b/browser/themes/winstripe/devtools/markup-view.css
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 * {
   padding: 0;
   margin: 0;
 }
 
 body {
-  font: message-box;
   color: hsl(0,0%,50%);
 }
 
 .text {
   color: black;
 }
 
 .newattr {
--- a/browser/themes/winstripe/downloads/downloads-aero.css
+++ b/browser/themes/winstripe/downloads/downloads-aero.css
@@ -2,17 +2,17 @@
  * 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/. */
 
 %define WINSTRIPE_AERO
 %include downloads.css
 %undef WINSTRIPE_AERO
 
 @media (-moz-windows-default-theme) {
-  #downloadsPanel[hasdownloads] #downloadsFooter {
+  #downloadsPanel[hasdownloads] > #downloadsFooter {
     background-color: #f1f5fb;
   }
 
   richlistitem[type="download"] {
     border: 1px solid transparent;
     border-bottom: 1px solid hsl(213,40%,90%);
   }
 
--- a/browser/themes/winstripe/downloads/downloads.css
+++ b/browser/themes/winstripe/downloads/downloads.css
@@ -24,17 +24,17 @@
   cursor: pointer;
 }
 
 #downloadsHistory > .button-box {
   margin: 1em;
 }
 
 @media (-moz-windows-default-theme) {
-  #downloadsPanel[hasdownloads] #downloadsFooter {
+  #downloadsPanel[hasdownloads] > #downloadsFooter {
     background-color: hsla(216,45%,88%,.98);
     box-shadow: 0px 1px 2px rgb(204,214,234) inset;
   }
 }
 
 /*** Downloads Summary and List items ***/
 
 #downloadsSummary,
@@ -167,23 +167,23 @@ richlistitem[type="download"][state="1"]
 .downloadButton.downloadRetry:active {
   -moz-image-region: rect(32px, 48px, 48px, 32px);
 }
 
 %ifdef WINSTRIPE_AERO
 @media not all and (-moz-windows-default-theme) {
 %endif
 
-richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow {
+richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow {
   -moz-image-region: rect(48px, 16px, 64px, 0px);
 }
-richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:hover {
+richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:hover {
   -moz-image-region: rect(48px, 32px, 64px, 16px);
 }
-richlistitem[type="download"][state="1"]:hover .downloadButton.downloadShow:active {
+richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:active {
   -moz-image-region: rect(48px, 48px, 64px, 32px);
 }
 
 %ifdef WINSTRIPE_AERO
 }
 %endif
 
 /*** Status and progress indicator ***/
rename from .gdbinit
rename to build/.gdbinit
--- a/.gdbinit
+++ b/build/.gdbinit
@@ -1,10 +1,16 @@
 # .gdbinit file for debugging Mozilla
 
+# You may need to put an 'add-auto-load-safe-path' command in your
+# $HOME/.gdbinit file to get GDB to trust this file. If your builds are
+# generally in $HOME/moz, then you can say:
+#
+#  add-auto-load-safe-path ~/moz
+
 # Don't stop for the SIG32/33/etc signals that Flash produces
 handle SIG32 noprint nostop pass
 handle SIG33 noprint nostop pass
 handle SIGPIPE noprint nostop pass
 
 # Show the concrete types behind nsIFoo
 set print object on
 
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -92,16 +92,22 @@ DEFINES += -DMOZ_PROFILE_MIGRATOR
 endif
 
 ifdef MOZ_EXTENSION_MANAGER
 DEFINES += -DMOZ_EXTENSION_MANAGER
 endif
 
 endif
 
+# Put a useful .gdbinit in the bin directory, to be picked up automatically
+# by GDB when we debug executables there.
+GDBINIT_FILES := .gdbinit
+GDBINIT_DEST = $(FINAL_TARGET)
+INSTALL_TARGETS += GDBINIT
+
 include $(topsrcdir)/config/rules.mk
 
 # we install to _leaktest/
 TARGET_DEPTH = ..
 include $(srcdir)/automation-build.mk
 
 _LEAKTEST_DIR = $(DEPTH)/_leaktest
 GARBAGE_DIRS += $(_LEAKTEST_DIR)
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -161,16 +161,17 @@ def checkForCrashes(dumpDir, symbolsPath
     symbolsPath = tempfile.mkdtemp()
     zfile = ZipFileReader(symbolsFile)
     zfile.extractall(symbolsPath)
 
   try:
     for d in dumps:
       stackwalkOutput = []
       stackwalkOutput.append("Crash dump filename: " + d)
+      topFrame = None
       if symbolsPath and stackwalkPath and os.path.exists(stackwalkPath):
         # run minidump stackwalk
         p = subprocess.Popen([stackwalkPath, d, symbolsPath],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE)
         (out, err) = p.communicate()
         if len(out) > 3:
           # minidump_stackwalk is chatty, so ignore stderr when it succeeds.
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1555,39 +1555,60 @@ endef
     $(eval $(call install_file_template,$(file),$($(category)_DEST),$($(category)_TARGET),$(IFLAGS2)))\
   )\
 )
 
 ################################################################################
 # Preprocessing rules
 #
 # The PP_TARGETS variable contains a list of all preprocessing target
-# categories. Each category defines a target path, and optional extra flags
-# like the following:
+# categories. Each category has associated variables listing input files, the
+# output directory, extra preprocessor flags, and so on. For example:
+#
+#   FOO := input-file
+#   FOO_PATH := target-directory
+#   FOO_FLAGS := -Dsome_flag
+#   PP_TARGETS += FOO
+#
+# If PP_TARGETS lists a category name <C> (like FOO, above), then we consult the
+# following make variables to see what to do:
 #
-# FOO_PATH := target_path
-# FOO_FLAGS := -Dsome_flag
-# PP_TARGETS += FOO
+# - <C> lists input files to be preprocessed with config/Preprocessor.py. We
+#   search VPATH for the names given here. If an input file name ends in '.in',
+#   that suffix is omitted from the output file name.
 #
-# Additionally, a FOO_TARGET variable may be added to indicate the target for
-# which the files and executables are installed. Default is "libs".
+# - <C>_PATH names the directory in which to place the preprocessed output
+#   files. We create this directory if it does not already exist. Setting
+#   this variable is optional; if unset, we install the files in $(CURDIR).
+#
+# - <C>_FLAGS lists flags to pass to Preprocessor.py, in addition to the usual
+#   bunch. Setting this variable is optional.
+#
+# - <C>_TARGET names the 'make' target that should depend on creating the output
+#   files. Setting this variable is optional; if unset, we preprocess the
+#   files for the 'libs' target.
 
 # preprocess_file_template defines preprocessing rules.
-# $(call preprocess_file_template, source_file, target_path, extra_flags)
+# $(call preprocess_file_template, source_file, output_file,
+#                                  makefile_target, extra_flags)
 define preprocess_file_template
-$(2)/$(notdir $(1)): $(1) $$(call mkdir_deps,$(2)) $$(GLOBAL_DEPS)
+$(2): $(1) $$(call mkdir_deps,$(dir $(2))) $$(GLOBAL_DEPS)
 	$$(RM) $$@
 	$$(PYTHON) $$(topsrcdir)/config/Preprocessor.py $(4) $$(DEFINES) $$(ACDEFINES) $$(XULPPFLAGS) $$< > $$@
-$(or $(3),libs):: $(2)/$(notdir $(1))
+$(3):: $(2)
 endef
 
-$(foreach category,$(PP_TARGETS),\
-  $(foreach file,$($(category)),\
-    $(eval $(call preprocess_file_template,$(file),$($(category)_PATH),$($(category)_TARGET),$($(category)_FLAGS)))\
-   )\
+$(foreach category,$(PP_TARGETS),						\
+  $(foreach file,$($(category)),						\
+    $(eval $(call preprocess_file_template,					\
+                  $(file),							\
+                  $(or $($(category)_PATH),$(CURDIR))/$(notdir $(file:.in=)),	\
+                  $(or $($(category)_TARGET),libs),				\
+                  $($(category)_FLAGS)))					\
+   )										\
  )
 
 ################################################################################
 # Special gmake rules.
 ################################################################################
 
 
 #
--- a/content/base/public/nsIDOMParser.idl
+++ b/content/base/public/nsIDOMParser.idl
@@ -87,31 +87,16 @@ interface nsIDOMParser : nsISupports
    *                     can be got.
    */
   [noscript] void init(in nsIPrincipal principal,
                        in nsIURI documentURI,
                        in nsIURI baseURI,
                        in nsIScriptGlobalObject scriptObject);
 };
 
-/**
- * The nsIDOMParserJS interface provides a scriptable way of calling init().
- * Do NOT use this interface from languages other than JavaScript.
- */
-[scriptable, uuid(ba6bcd6c-63d8-49b3-bc8a-1e5e895645bc)]
-interface nsIDOMParserJS : nsISupports
-{
-  /**
-   * Just like nsIDOMParser.init, but callable from JS.
-   */
-  void init([optional] in nsIPrincipal principal,
-            [optional] in nsIURI documentURI,
-            [optional] in nsIURI baseURI);
-};
-
 %{ C++
 #define NS_DOMPARSER_CID                            \
  { /* 3a8a3a50-512c-11d4-9a54-000064657374 */       \
    0x3a8a3a50, 0x512c, 0x11d4,                      \
   {0x9a, 0x54, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74} }
 #define NS_DOMPARSER_CONTRACTID \
 "@mozilla.org/xmlextras/domparser;1"
 %}
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -4658,17 +4658,16 @@ nsContentUtils::TriggerLink(nsIContent *
 
   nsILinkHandler *handler = aPresContext->GetLinkHandler();
   if (!handler) {
     return;
   }
 
   if (!aClick) {
     handler->OnOverLink(aContent, aLinkURI, aTargetSpec.get());
-
     return;
   }
 
   // Check that this page is allowed to load this URI.
   nsresult proceed = NS_OK;
 
   if (sSecurityManager) {
     uint32_t flag =
@@ -4676,19 +4675,35 @@ nsContentUtils::TriggerLink(nsIContent *
       (uint32_t)nsIScriptSecurityManager::STANDARD :
       (uint32_t)nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT;
     proceed =
       sSecurityManager->CheckLoadURIWithPrincipal(aContent->NodePrincipal(),
                                                   aLinkURI, flag);
   }
 
   // Only pass off the click event if the script security manager says it's ok.
+  // We need to rest aTargetSpec for forced downloads.
   if (NS_SUCCEEDED(proceed)) {
-    handler->OnLinkClick(aContent, aLinkURI, aTargetSpec.get(), nullptr, nullptr,
-                         aIsTrusted);
+
+    // A link/area element with a download attribute is allowed to set
+    // a pseudo Content-Disposition header.
+    // For security reasons we only allow websites to declare same-origin resources
+    // as downloadable. If this check fails we will just do the normal thing
+    // (i.e. navigate to the resource).
+    nsAutoString fileName;
+    if ((!aContent->IsHTML(nsGkAtoms::a) && !aContent->IsHTML(nsGkAtoms::area) &&
+         !aContent->IsSVG(nsGkAtoms::a)) ||
+        !aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::download, fileName) ||
+        NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, false, true))) {
+      fileName.SetIsVoid(true); // No actionable download attribute was found.
+    }
+
+    handler->OnLinkClick(aContent, aLinkURI,
+                         fileName.IsVoid() ? aTargetSpec.get() : EmptyString().get(),
+                         fileName, nullptr, nullptr, aIsTrusted);
   }
 }
 
 /* static */
 void
 nsContentUtils::GetLinkLocation(Element* aElement, nsString& aLocationString)
 {
   nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
--- a/content/base/src/nsDOMParser.cpp
+++ b/content/base/src/nsDOMParser.cpp
@@ -23,41 +23,55 @@
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
 #include "nsError.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/AutoRestore.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 nsDOMParser::nsDOMParser()
   : mAttemptedInit(false)
 {
+  SetIsDOMBinding();
 }
 
 nsDOMParser::~nsDOMParser()
 {
 }
 
 DOMCI_DATA(DOMParser, nsDOMParser)
 
 // QueryInterface implementation for nsDOMParser
-NS_INTERFACE_MAP_BEGIN(nsDOMParser)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMParser)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMParser)
   NS_INTERFACE_MAP_ENTRY(nsIDOMParser)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMParserJS)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMParser)
 NS_INTERFACE_MAP_END
 
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMParser, mOwner)
 
-NS_IMPL_ADDREF(nsDOMParser)
-NS_IMPL_RELEASE(nsDOMParser)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMParser)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMParser)
+
+already_AddRefed<nsIDocument>
+nsDOMParser::ParseFromString(const nsAString& aStr, SupportedType aType,
+                             ErrorResult& rv)
+{
+  nsCOMPtr<nsIDOMDocument> domDocument;
+  rv = nsDOMParser::ParseFromString(PromiseFlatString(aStr).get(),
+                                    SupportedTypeValues::strings[aType].value,
+                                    getter_AddRefs(domDocument));
+  nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
+  return document.forget();
+}
 
 NS_IMETHODIMP 
 nsDOMParser::ParseFromString(const PRUnichar *str, 
                              const char *contentType,
                              nsIDOMDocument **aResult)
 {
   NS_ENSURE_ARG(str);
   NS_ENSURE_ARG_POINTER(aResult);
@@ -98,16 +112,48 @@ nsDOMParser::ParseFromString(const PRUni
                              data.get(), data.Length(),
                              NS_ASSIGNMENT_DEPEND);
   if (NS_FAILED(rv))
     return rv;
 
   return ParseFromStream(stream, "UTF-8", data.Length(), contentType, aResult);
 }
 
+already_AddRefed<nsIDocument>
+nsDOMParser::ParseFromBuffer(const Sequence<uint8_t>& aBuf, uint32_t aBufLen,
+                             SupportedType aType, ErrorResult& rv)
+{
+  if (aBufLen > aBuf.Length()) {
+    rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY);
+    return nullptr;
+  }
+  nsCOMPtr<nsIDOMDocument> domDocument;
+  rv = nsDOMParser::ParseFromBuffer(aBuf.Elements(), aBufLen,
+                                    SupportedTypeValues::strings[aType].value,
+                                    getter_AddRefs(domDocument));
+  nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
+  return document.forget();
+}
+
+already_AddRefed<nsIDocument>
+nsDOMParser::ParseFromBuffer(const Uint8Array& aBuf, uint32_t aBufLen,
+                             SupportedType aType, ErrorResult& rv)
+{
+  if (aBufLen > aBuf.Length()) {
+    rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY);
+    return nullptr;
+  }
+  nsCOMPtr<nsIDOMDocument> domDocument;
+  rv = nsDOMParser::ParseFromBuffer(aBuf.Data(), aBufLen,
+                                    SupportedTypeValues::strings[aType].value,
+                                    getter_AddRefs(domDocument));
+  nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
+  return document.forget();
+}
+
 NS_IMETHODIMP 
 nsDOMParser::ParseFromBuffer(const uint8_t *buf,
                              uint32_t bufLen,
                              const char *contentType,
                              nsIDOMDocument **aResult)
 {
   NS_ENSURE_ARG_POINTER(buf);
   NS_ENSURE_ARG_POINTER(aResult);
@@ -119,16 +165,33 @@ nsDOMParser::ParseFromBuffer(const uint8
                                       bufLen, NS_ASSIGNMENT_DEPEND);
   if (NS_FAILED(rv))
     return rv;
 
   return ParseFromStream(stream, nullptr, bufLen, contentType, aResult);
 }
 
 
+already_AddRefed<nsIDocument>
+nsDOMParser::ParseFromStream(nsIInputStream* aStream,
+                             const nsAString& aCharset,
+                             int32_t aContentLength,
+                             SupportedType aType,
+                             ErrorResult& rv)
+{
+  nsCOMPtr<nsIDOMDocument> domDocument;
+  rv = nsDOMParser::ParseFromStream(aStream,
+                                    NS_ConvertUTF16toUTF8(aCharset).get(),
+                                    aContentLength,
+                                    SupportedTypeValues::strings[aType].value,
+                                    getter_AddRefs(domDocument));
+  nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
+  return document.forget();
+}
+
 NS_IMETHODIMP 
 nsDOMParser::ParseFromStream(nsIInputStream *stream, 
                              const char *charset, 
                              int32_t contentLength,
                              const char *contentType,
                              nsIDOMDocument **aResult)
 {
   NS_ENSURE_ARG(stream);
@@ -289,112 +352,71 @@ nsDOMParser::Init(nsIPrincipal* principa
   // documentURI as the base.  Otherwise for null principals we'll get
   // nsDocument::SetBaseURI giving errors.
 
   NS_POSTCONDITION(mPrincipal, "Must have principal");
   NS_POSTCONDITION(mOriginalPrincipal, "Must have original principal");
   NS_POSTCONDITION(mDocumentURI, "Must have document URI");
   return NS_OK;
 }
-  
-static nsQueryInterface
-JSvalToInterface(JSContext* cx, JS::Value val, nsIXPConnect* xpc, bool* wasNull)
-{
-  if (val.isNull()) {
-    *wasNull = true;
-    return nsQueryInterface(nullptr);
-  }
-  
-  *wasNull = false;
-  if (val.isObject()) {
-    JSObject* arg = &val.toObject();
 
-    nsCOMPtr<nsIXPConnectWrappedNative> native;
-    xpc->GetWrappedNativeOfJSObject(cx, arg, getter_AddRefs(native));
-
-    // do_QueryWrappedNative is not null-safe
-    if (native) {
-      return do_QueryWrappedNative(native);
-    }
-  }
-  
-  return nsQueryInterface(nullptr);
-}
-
-static nsresult
-GetInitArgs(JSContext *cx, uint32_t argc, jsval *argv,
-            nsIPrincipal** aPrincipal, nsIURI** aDocumentURI,
-            nsIURI** aBaseURI)
+/*static */already_AddRefed<nsDOMParser>
+nsDOMParser::Constructor(nsISupports* aOwner, nsIPrincipal* aPrincipal,
+                         nsIURI* aDocumentURI, nsIURI* aBaseURI,
+                         ErrorResult& rv)
 {
   if (!nsContentUtils::IsCallerChrome()) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-  
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-  
-  // First arg is our principal.  If someone passes something that's
-  // not a principal and not null, die to prevent privilege escalation.
-  bool wasNull;
-  nsCOMPtr<nsIPrincipal> prin = JSvalToInterface(cx, argv[0], xpc, &wasNull);
-  if (!prin && !wasNull) {
-    return NS_ERROR_INVALID_ARG;
+    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
   }
-
-  nsCOMPtr<nsIURI> documentURI;
-  nsCOMPtr<nsIURI> baseURI;
-  if (argc > 1) {
-    // Grab our document URI too.  Again, if it's something unexpected bail
-    // out.
-    documentURI = JSvalToInterface(cx, argv[1], xpc, &wasNull);
-    if (!documentURI && !wasNull) {
-      return NS_ERROR_INVALID_ARG;
-    }
-
-    if (argc > 2) {
-      // Grab our base URI as well
-      baseURI = JSvalToInterface(cx, argv[2], xpc, &wasNull);
-      if (!baseURI && !wasNull) {
-        return NS_ERROR_INVALID_ARG;
-      }
-    }
+  nsRefPtr<nsDOMParser> domParser = new nsDOMParser(aOwner);
+  rv = domParser->InitInternal(aOwner, aPrincipal, aDocumentURI, aBaseURI);
+  if (rv.Failed()) {
+    return nullptr;
   }
-
-  NS_IF_ADDREF(*aPrincipal = prin);
-  NS_IF_ADDREF(*aDocumentURI = documentURI);
-  NS_IF_ADDREF(*aBaseURI = baseURI);
-  return NS_OK;
+  return domParser.forget();
 }
 
-NS_IMETHODIMP
-nsDOMParser::Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
-                        uint32_t argc, jsval *argv)
+/*static */already_AddRefed<nsDOMParser>
+nsDOMParser::Constructor(nsISupports* aOwner, mozilla::ErrorResult& rv)
 {
-  AttemptedInitMarker marker(&mAttemptedInit);
   nsCOMPtr<nsIPrincipal> prin;
   nsCOMPtr<nsIURI> documentURI;
   nsCOMPtr<nsIURI> baseURI;
-  if (argc > 0) {
-    nsresult rv = GetInitArgs(cx, argc, argv, getter_AddRefs(prin),
-                              getter_AddRefs(documentURI),
-                              getter_AddRefs(baseURI));
-    NS_ENSURE_SUCCESS(rv, rv);
-  } else {
-    // No arguments; use the subject principal
-    nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-    NS_ENSURE_TRUE(secMan, NS_ERROR_UNEXPECTED);
+  // No arguments; use the subject principal
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  if (!secMan) {
+    rv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
 
-    nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(prin));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // We're called from JS; there better be a subject principal, really.
-    NS_ENSURE_TRUE(prin, NS_ERROR_UNEXPECTED);
+  rv = secMan->GetSubjectPrincipal(getter_AddRefs(prin));
+  if (rv.Failed()) {
+    return nullptr;
   }
 
-  NS_ASSERTION(prin, "Must have principal by now");
-  
+  // We're called from JS; there better be a subject principal, really.
+  if (!prin) {
+    rv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsRefPtr<nsDOMParser> domParser = new nsDOMParser(aOwner);
+  rv = domParser->InitInternal(aOwner, prin, documentURI, baseURI);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+  return domParser.forget();
+}
+
+nsresult
+nsDOMParser::InitInternal(nsISupports* aOwner, nsIPrincipal* prin,
+                          nsIURI* documentURI, nsIURI* baseURI)
+{
+  AttemptedInitMarker marker(&mAttemptedInit);
   if (!documentURI) {
     // No explicit documentURI; grab document and base URIs off the window our
     // constructor was called on. Error out if anything untoward happens.
 
     // Note that this is a behavior change as far as I can tell -- we're now
     // using the base URI and document URI of the window off of which the
     // DOMParser is created, not the window in which parse*() is called.
     // Does that matter?
@@ -417,42 +439,53 @@ nsDOMParser::Initialize(nsISupports* aOw
     baseURI = doc->GetDocBaseURI();
     documentURI = doc->GetDocumentURI();
   }
 
   nsCOMPtr<nsIScriptGlobalObject> scriptglobal = do_QueryInterface(aOwner);
   return Init(prin, documentURI, baseURI, scriptglobal);
 }
 
-NS_IMETHODIMP
-nsDOMParser::Init(nsIPrincipal *aPrincipal, nsIURI *aDocumentURI,
-                  nsIURI *aBaseURI)
+void
+nsDOMParser::Init(nsIPrincipal* aPrincipal, nsIURI* aDocumentURI,
+                  nsIURI* aBaseURI, mozilla::ErrorResult& rv)
 {
   AttemptedInitMarker marker(&mAttemptedInit);
 
   JSContext *cx = nsContentUtils::GetCurrentJSContext();
-  NS_ENSURE_TRUE(cx, NS_ERROR_UNEXPECTED);
+  if (!cx) {
+    rv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
 
   nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
 
   nsCOMPtr<nsIPrincipal> principal = aPrincipal;
 
   if (!principal && !aDocumentURI) {
     nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-    NS_ENSURE_TRUE(secMan, NS_ERROR_UNEXPECTED);
+    if (!secMan) {
+      rv.Throw(NS_ERROR_UNEXPECTED);
+      return;
+    }
 
-    nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(principal));
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = secMan->GetSubjectPrincipal(getter_AddRefs(principal));
+    if (rv.Failed()) {
+      return;
+    }
 
     // We're called from JS; there better be a subject principal, really.
-    NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED);
+    if (!principal) {
+      rv.Throw(NS_ERROR_UNEXPECTED);
+      return;
+    }
   }
 
-  return Init(principal, aDocumentURI, aBaseURI,
-              scriptContext ? scriptContext->GetGlobalObject() : nullptr);
+  rv = Init(principal, aDocumentURI, aBaseURI,
+            scriptContext ? scriptContext->GetGlobalObject() : nullptr);
 }
 
 nsresult
 nsDOMParser::SetUpDocument(DocumentFlavor aFlavor, nsIDOMDocument** aResult)
 {
   nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
     do_QueryReferent(mScriptHandlingObject);
   nsresult rv;
--- a/content/base/src/nsDOMParser.h
+++ b/content/base/src/nsDOMParser.h
@@ -1,61 +1,114 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef nsDOMParser_h__
-#define nsDOMParser_h__
+#ifndef nsDOMParser_h_
+#define nsDOMParser_h_
 
 #include "nsIDOMParser.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 #include "nsWeakReference.h"
 #include "nsIJSNativeInitializer.h"
 #include "nsIDocument.h"
+#include "nsWrapperCache.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/DOMParserBinding.h"
+#include "mozilla/dom/TypedArray.h"
 
-class nsDOMParser : public nsIDOMParser,
-                    public nsIDOMParserJS,
-                    public nsIJSNativeInitializer,
-                    public nsSupportsWeakReference
+class nsIInputStream;
+
+class nsDOMParser MOZ_FINAL : public nsIDOMParser,
+                              public nsSupportsWeakReference,
+                              public nsWrapperCache
 {
 public: 
   nsDOMParser();
   virtual ~nsDOMParser();
 
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDOMParser,
+                                                         nsIDOMParser)
 
   // nsIDOMParser
   NS_DECL_NSIDOMPARSER
 
-  // nsIDOMParserJS
-  NS_DECL_NSIDOMPARSERJS
+  // WebIDL API
+  static already_AddRefed<nsDOMParser>
+  Constructor(nsISupports* aOwner, mozilla::ErrorResult& rv);
+
+  static already_AddRefed<nsDOMParser>
+  Constructor(nsISupports* aOwner, nsIPrincipal* aPrincipal,
+              nsIURI* aDocumentURI, nsIURI* aBaseURI,
+              mozilla::ErrorResult& rv);
+
+  already_AddRefed<nsIDocument>
+  ParseFromString(const nsAString& aStr, mozilla::dom::SupportedType aType,
+                  mozilla::ErrorResult& rv);
+
+  already_AddRefed<nsIDocument>
+  ParseFromBuffer(const mozilla::dom::Sequence<uint8_t>& aBuf,
+                  uint32_t aBufLen, mozilla::dom::SupportedType aType,
+                  mozilla::ErrorResult& rv);
 
-  // nsIJSNativeInitializer
-  NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
-                        uint32_t argc, jsval *argv);
+  already_AddRefed<nsIDocument>
+  ParseFromBuffer(const mozilla::dom::Uint8Array& aBuf, uint32_t aBufLen,
+                  mozilla::dom::SupportedType aType,
+                  mozilla::ErrorResult& rv);
+
+  already_AddRefed<nsIDocument>
+  ParseFromStream(nsIInputStream* aStream, const nsAString& aCharset,
+                  int32_t aContentLength, mozilla::dom::SupportedType aType,
+                  mozilla::ErrorResult& rv);
+
+  void Init(nsIPrincipal* aPrincipal, nsIURI* aDocumentURI,
+            nsIURI* aBaseURI, mozilla::ErrorResult& rv);
+
+  nsISupports* GetParentObject() const
+  {
+    return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
+                               bool* aTriedToWrap) MOZ_OVERRIDE
+  {
+    return mozilla::dom::DOMParserBinding::Wrap(aCx, aScope, this,
+                                                aTriedToWrap);
+  }
 
 private:
+  nsDOMParser(nsISupports* aOwner) : mOwner(aOwner), mAttemptedInit(false)
+  {
+    MOZ_ASSERT(aOwner);
+    SetIsDOMBinding();
+  }
+
+  nsresult InitInternal(nsISupports* aOwner, nsIPrincipal* prin,
+                        nsIURI* documentURI, nsIURI* baseURI);
+
   nsresult SetUpDocument(DocumentFlavor aFlavor, nsIDOMDocument** aResult);
 
   class AttemptedInitMarker {
   public:
     AttemptedInitMarker(bool* aAttemptedInit) :
       mAttemptedInit(aAttemptedInit)
     {}
 
     ~AttemptedInitMarker() {
       *mAttemptedInit = true;
     }
 
   private:
     bool* mAttemptedInit;
   };
-  
+
+  nsCOMPtr<nsISupports> mOwner;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIPrincipal> mOriginalPrincipal;
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsIURI> mBaseURI;
   nsWeakPtr mScriptHandlingObject;
   
   bool mAttemptedInit;
 };
--- a/content/base/src/nsDOMSerializer.cpp
+++ b/content/base/src/nsDOMSerializer.cpp
@@ -10,36 +10,40 @@
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDocumentEncoder.h"
 #include "nsString.h"
 #include "nsContentCID.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 
+using namespace mozilla;
+
 nsDOMSerializer::nsDOMSerializer()
 {
 }
 
 nsDOMSerializer::~nsDOMSerializer()
 {
 }
 
 DOMCI_DATA(XMLSerializer, nsDOMSerializer)
 
 // QueryInterface implementation for nsDOMSerializer
-NS_INTERFACE_MAP_BEGIN(nsDOMSerializer)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMSerializer)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMSerializer)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XMLSerializer)
 NS_INTERFACE_MAP_END
 
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMSerializer, mOwner)
 
-NS_IMPL_ADDREF(nsDOMSerializer)
-NS_IMPL_RELEASE(nsDOMSerializer)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMSerializer)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMSerializer)
 
 
 static nsresult
 SetUpEncoder(nsIDOMNode *aRoot, const nsACString& aCharset,
              nsIDocumentEncoder **aEncoder)
 {
   *aEncoder = nullptr;
    
@@ -85,16 +89,23 @@ SetUpEncoder(nsIDOMNode *aRoot, const ns
   if (NS_SUCCEEDED(rv)) {
     *aEncoder = encoder.get();
     NS_ADDREF(*aEncoder);
   }
 
   return rv;
 }
 
+void
+nsDOMSerializer::SerializeToString(nsINode& aRoot, nsAString& aStr,
+                                   ErrorResult& rv)
+{
+  rv = nsDOMSerializer::SerializeToString(aRoot.AsDOMNode(), aStr);
+}
+
 NS_IMETHODIMP
 nsDOMSerializer::SerializeToString(nsIDOMNode *aRoot, nsAString& _retval)
 {
   NS_ENSURE_ARG_POINTER(aRoot);
   
   _retval.Truncate();
 
   if (!nsContentUtils::CanCallerAccess(aRoot)) {
@@ -104,16 +115,24 @@ nsDOMSerializer::SerializeToString(nsIDO
   nsCOMPtr<nsIDocumentEncoder> encoder;
   nsresult rv = SetUpEncoder(aRoot, EmptyCString(), getter_AddRefs(encoder));
   if (NS_FAILED(rv))
     return rv;
 
   return encoder->EncodeToString(_retval);
 }
 
+void
+nsDOMSerializer::SerializeToStream(nsINode& aRoot, nsIOutputStream* aStream,
+                                   const nsAString& aCharset, ErrorResult& rv)
+{
+  rv = nsDOMSerializer::SerializeToStream(aRoot.AsDOMNode(), aStream,
+                                          NS_ConvertUTF16toUTF8(aCharset));
+}
+
 NS_IMETHODIMP
 nsDOMSerializer::SerializeToStream(nsIDOMNode *aRoot, 
                                    nsIOutputStream *aStream, 
                                    const nsACString& aCharset)
 {
   NS_ENSURE_ARG_POINTER(aRoot);
   NS_ENSURE_ARG_POINTER(aStream);
   // The charset arg can be empty, in which case we get the document's
--- a/content/base/src/nsDOMSerializer.h
+++ b/content/base/src/nsDOMSerializer.h
@@ -1,25 +1,70 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef nsDOMSerializer_h__
-#define nsDOMSerializer_h__
+#ifndef nsDOMSerializer_h_
+#define nsDOMSerializer_h_
 
 #include "nsIDOMSerializer.h"
+#include "nsWrapperCache.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/XMLSerializerBinding.h"
 
-class nsDOMSerializer : public nsIDOMSerializer
+class nsINode;
+class nsIOutputStream;
+
+class nsDOMSerializer MOZ_FINAL : public nsIDOMSerializer,
+                                  public nsWrapperCache
 {
 public:
   nsDOMSerializer();
   virtual ~nsDOMSerializer();
 
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMSerializer)
 
   // nsIDOMSerializer
   NS_DECL_NSIDOMSERIALIZER
+
+  // WebIDL API
+  static already_AddRefed<nsDOMSerializer>
+  Constructor(nsISupports* aOwner, mozilla::ErrorResult& rv)
+  {
+    nsRefPtr<nsDOMSerializer> domSerializer = new nsDOMSerializer(aOwner);
+    return domSerializer.forget();
+  }
+
+  void
+  SerializeToString(nsINode& aRoot, nsAString& aStr,
+                    mozilla::ErrorResult& rv);
+
+  void
+  SerializeToStream(nsINode& aRoot, nsIOutputStream* aStream,
+                    const nsAString& aCharset, mozilla::ErrorResult& rv);
+
+  nsISupports* GetParentObject() const
+  {
+    return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
+                               bool* aTriedToWrap) MOZ_OVERRIDE
+  {
+    return mozilla::dom::XMLSerializerBinding::Wrap(aCx, aScope, this,
+                                                    aTriedToWrap);
+  }
+
+private:
+  nsDOMSerializer(nsISupports* aOwner) : mOwner(aOwner)
+  {
+    MOZ_ASSERT(aOwner);
+    SetIsDOMBinding();
+  }
+
+  nsCOMPtr<nsISupports> mOwner;
 };
 
 
 #endif
 
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -282,16 +282,17 @@ GK_ATOM(disableOutputEscaping, "disable-
 GK_ATOM(disabled, "disabled")
 GK_ATOM(display, "display")
 GK_ATOM(distinct, "distinct")
 GK_ATOM(div, "div")
 GK_ATOM(dl, "dl")
 GK_ATOM(doctypePublic, "doctype-public")
 GK_ATOM(doctypeSystem, "doctype-system")
 GK_ATOM(document, "document")
+GK_ATOM(download, "download")
 GK_ATOM(DOMAttrModified, "DOMAttrModified")
 GK_ATOM(DOMCharacterDataModified, "DOMCharacterDataModified")
 GK_ATOM(DOMNodeInserted, "DOMNodeInserted")
 GK_ATOM(DOMNodeInsertedIntoDocument, "DOMNodeInsertedIntoDocument")
 GK_ATOM(DOMNodeRemoved, "DOMNodeRemoved")
 GK_ATOM(DOMNodeRemovedFromDocument, "DOMNodeRemovedFromDocument")
 GK_ATOM(DOMSubtreeModified, "DOMSubtreeModified")
 GK_ATOM(double_, "double")
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -682,18 +682,16 @@ protected:
     nsCString value;
   };
   nsTArray<RequestHeader> mModifiedRequestHeaders;
 
   // Helper object to manage our XPCOM scriptability bits
   nsXMLHttpRequestXPCOMifier* mXPCOMifier;
 };
 
-#undef IMPL_EVENT_HANDLER
-
 // A shim class designed to expose the non-DOM interfaces of
 // XMLHttpRequest via XPCOM stuff.
 class nsXMLHttpRequestXPCOMifier MOZ_FINAL : public nsIStreamListener,
                                              public nsIChannelEventSink,
                                              public nsIProgressEventSink,
                                              public nsIInterfaceRequestor,
                                              public nsITimerCallback,
                                              public nsCycleCollectionParticipant
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -247,16 +247,17 @@ MOCHITEST_FILES_A = \
 		test_CrossSiteXHR_cache.html \
 		file_CrossSiteXHR_cache_server.sjs \
 		test_XHRDocURI.html \
 		file_XHRDocURI.xml \
 		file_XHRDocURI.xml^headers^ \
 		file_XHRDocURI.text \
 		file_XHRDocURI.text^headers^ \
 		test_DOMException.html \
+		test_domparsing.html \
 		test_meta_viewport0.html \
 		test_meta_viewport1.html \
 		test_meta_viewport2.html \
 		test_meta_viewport3.html \
 		test_meta_viewport4.html \
 		test_meta_viewport5.html \
 		test_meta_viewport6.html \
 		test_mutationobservers.html \
--- a/content/base/test/chrome/Makefile.in
+++ b/content/base/test/chrome/Makefile.in
@@ -44,11 +44,12 @@ MOCHITEST_CHROME_FILES = \
     test_bug752226-3.xul \
     test_bug752226-4.xul \
     test_bug682305.html \
     test_bug780199.xul \
     test_bug780529.xul \
     test_csp_bug768029.html \
     test_bug800386.xul \
     test_csp_bug773891.html \
+    test_domparsing.xul \
     $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/base/test/chrome/test_domparsing.xul
@@ -0,0 +1,139 @@
+<?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 title="Test for the Mozilla extesion of the DOM Parsing and Serialization API"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=816410"
+     target="_blank">Mozilla Bug 816410</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript;version=1.7"><![CDATA[
+"use strict";
+/** Test for Bug 816410 **/
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+function throws(a, type, message) {
+  for (let fn of Array.isArray(a) ? a : [a]) {
+    try {
+      fn();
+      ok(false, message);
+    } catch (e) {
+      if (type) {
+        is(e.name, type, message);
+      } else {
+        ok(true, message);
+      }
+    }
+  }
+}
+
+// DOMParser constructor should not throw for null arguments
+new DOMParser(undefined);
+new DOMParser(null);
+
+throws([
+  function() { new DOMParser(false); },
+  function() { new DOMParser(0); },
+  function() { new DOMParser(""); },
+  function() { new DOMParser({}); },
+], "TypeError", "DOMParser constructor should throw for extra arguments");
+
+{
+  let parser = new DOMParser();
+  throws(function() {
+    parser.init();
+  }, "NS_ERROR_UNEXPECTED", "init method should throw when DOMParser is created by constructor");
+}
+
+// XMLSerializer constructor should not throw for extra arguments
+new XMLSerializer(undefined);
+new XMLSerializer(null);
+new XMLSerializer(false);
+new XMLSerializer(0);
+new XMLSerializer("");
+new XMLSerializer({});
+
+runTest(new DOMParser(), new XMLSerializer());
+
+{
+  let parser = Cc["@mozilla.org/xmlextras/domparser;1"]
+               .createInstance(Ci.nsIDOMParser);
+  parser.init();
+  throws(function() {
+    parser.init();
+  }, "NS_ERROR_UNEXPECTED", "init method should throw when called twice");
+}
+
+runTest(Cc["@mozilla.org/xmlextras/domparser;1"]
+        .createInstance(Ci.nsIDOMParser),
+        Cc["@mozilla.org/xmlextras/xmlserializer;1"]
+        .createInstance(Ci.nsIDOMSerializer));
+
+function runTest(parser, serializer) {
+  is(typeof parser.parseFromString, "function", "parseFromString should exist");
+  is(typeof parser.parseFromBuffer, "function", "parseFromBuffer should exist");
+  is(typeof parser.parseFromStream, "function", "parseFromStream should exist");
+  is(typeof parser.init, "function", "init should exist");
+
+  is(typeof serializer.serializeToString, "function", "serializeToString should exist");
+  is(typeof serializer.serializeToStream, "function", "serializeToStream should exist");
+
+  let tests = [
+    {input: "<html></html>", type: "text/html",
+     expected: '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body></body></html>'},
+    {input: "<xml></xml>", type: "text/xml", expected: "<xml/>"},
+    {input: "<xml></xml>", type: "application/xml", expected: "<xml/>"},
+    {input: "<html></html>", type: "application/xhtml+xml", expected: "<html/>"},
+    {input: "<svg></svg>", type: "image/svg+xml", expected: "<svg/>"},
+  ];
+  for (let t of tests) {
+    is(serializer.serializeToString(parser.parseFromString(t.input, t.type)), t.expected,
+       "parseFromString test for " + t.type);
+
+    let ostream = {
+      data: "",
+      write: function(buf, count) { this.data += buf; return count; }
+    };
+    serializer.serializeToStream(parser.parseFromString(t.input, t.type), ostream, "UTF-8");
+    is(ostream.data, t.expected, "serializeToStream test for " + t.type);
+
+    if (t.type === "text/html") {
+      // parseFromBuffer and parseFromStream don't support "text/html".
+      continue;
+    }
+
+    let array = Array.map(t.input, function(c) { return c.charCodeAt(c); });
+    let inputs = [
+      {array: array, name: "parseFromBuffer (array)"},
+      {array: new Uint8Array(array), name: "parseFromBuffer (Uint8Array)"},
+    ];
+    for (let input of inputs) {
+      let a = input.array;
+      is(serializer.serializeToString(parser.parseFromBuffer(a, a.length, t.type)), t.expected,
+         input.name + " test for " + t.type);
+      throws(function() {
+          parser.parseFromBuffer(a, a.length + 1, t.type);
+        }, "NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY",
+        input.name + " should throw if bufLen parameter is greater than actual length"
+      );
+    }
+
+    let istream = Cc["@mozilla.org/io/string-input-stream;1"].
+                 createInstance(Ci.nsIStringInputStream);
+    istream.setData(t.input, -1);
+    is(serializer.serializeToString(parser.parseFromStream(istream, null, array.length, t.type)),
+       t.expected, "parseFromStream test for " + t.type);
+  }
+  throws(function() {
+    parser.parseFromString("<xml></xml>", "foo/bar");
+  }, "TypeError", "parseFromString should throw for the unknown type");
+}
+  ]]></script>
+</window>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_domparsing.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset=utf-8>
+  <title>Test for the DOM Parsing and Serialization Standard</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=816410">Mozilla Bug 816410</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+"use strict";
+/** Test for Bug 816410 **/
+
+function throws(fn, type, message) {
+  try {
+    fn();
+    ok(false, message);
+  } catch (e) {
+    if (type) {
+      is(e.name, type, message);
+    } else {
+      ok(true, message);
+    }
+  }
+}
+
+let parser = new DOMParser();
+is(typeof parser.parseFromString, "function", "parseFromString should exist");
+is(typeof parser.parseFromBuffer, "undefined", "parseFromBuffer should NOT be visible from unprivileged callers");
+is(typeof parser.parseFromStream, "undefined", "parseFromStream should NOT be visible from unprivileged callers");
+is(typeof parser.init, "undefined", "init should NOT be visible from unprivileged callers");
+
+// The three-arguments constructor should not be visible from
+// unprivileged callers for interoperability with other browsers.
+// But we have no way to do that right now.
+try {
+  new DOMParser(undefined);
+  new DOMParser(null);
+  new DOMParser(false);
+  new DOMParser(0);
+  new DOMParser("");
+  new DOMParser({});
+} catch (e) {
+  todo(false, "DOMParser constructor should not throw for extra arguments");
+}
+
+let serializer = new XMLSerializer();
+is(typeof serializer.serializeToString, "function", "serializeToString should exist");
+is(typeof serializer.serializeToStream, "undefined", "serializeToStream should NOT be visible from unprivileged callers");
+
+// XMLSerializer constructor should not throw for extra arguments
+new XMLSerializer(undefined);
+new XMLSerializer(null);
+new XMLSerializer(false);
+new XMLSerializer(0);
+new XMLSerializer("");
+new XMLSerializer({});
+
+let tests = [
+  {input: "<html></html>", type: "text/html",
+   expected: '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body></body></html>'},
+  {input: "<xml></xml>", type: "text/xml", expected: "<xml/>"},
+  {input: "<xml></xml>", type: "application/xml", expected: "<xml/>"},
+  {input: "<html></html>", type: "application/xhtml+xml", expected: "<html/>"},
+  {input: "<svg></svg>", type: "image/svg+xml", expected: "<svg/>"},
+];
+for (let t of tests) {
+  is(serializer.serializeToString(parser.parseFromString(t.input, t.type)), t.expected,
+     "parseFromString test for " + t.type);
+}
+
+throws(function() {
+  parser.parseFromString("<xml></xml>", "foo/bar");
+}, "TypeError", "parseFromString should throw for the unknown type");
+</script>
+</pre>
+</body>
+</html>
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -3,18 +3,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGLCONTEXT_H_
 #define WEBGLCONTEXT_H_
 
 #include "WebGLElementArrayCache.h"
 #include "WebGLObjectModel.h"
+#include "WebGLShader.h"
 #include "WebGLBuffer.h"
+#include "WebGLRenderbuffer.h"
 #include "WebGLVertexAttribData.h"
+#include "WebGLShaderPrecisionFormat.h"
 #include <stdarg.h>
 #include <vector>
 
 #include "nsTArray.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsCycleCollectionNoteChild.h"
 
@@ -34,18 +37,16 @@
 #include "mozilla/LinkedList.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/ImageData.h"
 
 #ifdef XP_MACOSX
 #include "ForceDiscreteGPUHelperCGL.h"
 #endif
 
-#include "angle/ShaderLang.h"
-
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingUtils.h"
 
 /* 
  * Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25
  *   https://bugzilla.mozilla.org/show_bug.cgi?id=686732
@@ -65,25 +66,21 @@
 #define MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS  8     // Page 164
 
 class nsIPropertyBag;
 
 namespace mozilla {
 
 class WebGLTexture;
 class WebGLProgram;
-class WebGLShader;
 class WebGLFramebuffer;
 class WebGLUniformLocation;
-class WebGLRenderbuffer;
 class WebGLMemoryPressureObserver;
-class WebGLRectangleObject;
 class WebGLContextBoundObject;
 class WebGLActiveInfo;
-class WebGLShaderPrecisionFormat;
 class WebGLExtensionBase;
 
 namespace dom {
 struct WebGLContextAttributes;
 struct WebGLContextAttributesInitializer;
 }
 
 enum FakeBlackStatus { DoNotNeedFakeBlack, DoNeedFakeBlack, DontKnowIfNeedFakeBlack };
@@ -146,57 +143,16 @@ using WebGLTexelConversions::WebGLTexelF
 WebGLTexelFormat GetWebGLTexelFormat(GLenum format, GLenum type);
 
 // Zero is not an integer power of two.
 inline bool is_pot_assuming_nonnegative(WebGLsizei x)
 {
     return x && (x & (x-1)) == 0;
 }
 
-// this class is a mixin for GL objects that have dimensions
-// that we need to track.
-class WebGLRectangleObject
-{
-public:
-    WebGLRectangleObject()
-        : mWidth(0), mHeight(0) { }
-
-    WebGLRectangleObject(WebGLsizei width, WebGLsizei height)
-        : mWidth(width), mHeight(height) { }
-
-    WebGLsizei Width() const { return mWidth; }
-    void width(WebGLsizei value) { mWidth = value; }
-
-    WebGLsizei Height() const { return mHeight; }
-    void height(WebGLsizei value) { mHeight = value; }
-
-    void setDimensions(WebGLsizei width, WebGLsizei height) {
-        mWidth = width;
-        mHeight = height;
-    }
-
-    void setDimensions(WebGLRectangleObject *rect) {
-        if (rect) {
-            mWidth = rect->Width();
-            mHeight = rect->Height();
-        } else {
-            mWidth = 0;
-            mHeight = 0;
-        }
-    }
-
-    bool HasSameDimensionsAs(const WebGLRectangleObject& other) const {
-        return Width() == other.Width() && Height() == other.Height(); 
-    }
-
-protected:
-    WebGLsizei mWidth;
-    WebGLsizei mHeight;
-};
-
 struct WebGLContextOptions {
     // these are defaults
     WebGLContextOptions();
 
     bool operator==(const WebGLContextOptions& other) const {
         return
             alpha == other.alpha &&
             depth == other.depth &&
@@ -1677,158 +1633,16 @@ public:
             if (mFakeBlackStatus == DontKnowIfNeedFakeBlack)
                 mFakeBlackStatus = DoNotNeedFakeBlack;
         }
 
         return mFakeBlackStatus == DoNeedFakeBlack;
     }
 };
 
-struct WebGLMappedIdentifier {
-    nsCString original, mapped; // ASCII strings
-    WebGLMappedIdentifier(const nsACString& o, const nsACString& m) : original(o), mapped(m) {}
-};
-
-struct WebGLUniformInfo {
-    uint32_t arraySize;
-    bool isArray;
-    ShDataType type;
-
-    WebGLUniformInfo(uint32_t s = 0, bool a = false, ShDataType t = SH_NONE)
-        : arraySize(s), isArray(a), type(t) {}
-
-    int ElementSize() const {
-        switch (type) {
-            case SH_INT:
-            case SH_FLOAT:
-            case SH_BOOL:
-            case SH_SAMPLER_2D:
-            case SH_SAMPLER_CUBE:
-                return 1;
-            case SH_INT_VEC2:
-            case SH_FLOAT_VEC2:
-            case SH_BOOL_VEC2:
-                return 2;
-            case SH_INT_VEC3:
-            case SH_FLOAT_VEC3:
-            case SH_BOOL_VEC3:
-                return 3;
-            case SH_INT_VEC4:
-            case SH_FLOAT_VEC4:
-            case SH_BOOL_VEC4:
-            case SH_FLOAT_MAT2:
-                return 4;
-            case SH_FLOAT_MAT3:
-                return 9;
-            case SH_FLOAT_MAT4:
-                return 16;
-            default:
-                NS_ABORT(); // should never get here
-                return 0;
-        }
-    }
-};
-
-class WebGLShader MOZ_FINAL
-    : public nsISupports
-    , public WebGLRefCountedObject<WebGLShader>
-    , public LinkedListElement<WebGLShader>
-    , public WebGLContextBoundObject
-    , public nsWrapperCache
-{
-    friend class WebGLContext;
-    friend class WebGLProgram;
-
-public:
-    WebGLShader(WebGLContext *context, WebGLenum stype)
-        : WebGLContextBoundObject(context)
-        , mType(stype)
-        , mNeedsTranslation(true)
-        , mAttribMaxNameLength(0)
-        , mCompileStatus(false)
-    {
-        SetIsDOMBinding();
-        mContext->MakeContextCurrent();
-        mGLName = mContext->gl->fCreateShader(mType);
-        mContext->mShaders.insertBack(this);
-    }
-
-    ~WebGLShader() {
-        DeleteOnce();
-    }
-    
-    size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const {
-        return aMallocSizeOf(this) +
-               mSource.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
-               mTranslationLog.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
-    }
-
-    void Delete() {
-        mSource.Truncate();
-        mTranslationLog.Truncate();
-        mContext->MakeContextCurrent();
-        mContext->gl->fDeleteShader(mGLName);
-        LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders);
-    }
-
-    WebGLuint GLName() { return mGLName; }
-    WebGLenum ShaderType() { return mType; }
-
-    void SetSource(const nsAString& src) {
-        // XXX do some quick gzip here maybe -- getting this will be very rare
-        mSource.Assign(src);
-    }
-
-    const nsString& Source() const { return mSource; }
-
-    void SetNeedsTranslation() { mNeedsTranslation = true; }
-    bool NeedsTranslation() const { return mNeedsTranslation; }
-
-    void SetCompileStatus (bool status) {
-        mCompileStatus = status;
-    }
-
-    bool CompileStatus() const {
-        return mCompileStatus;
-    }
-
-    void SetTranslationSuccess() {
-        mTranslationLog.SetIsVoid(true);
-        mNeedsTranslation = false;
-    }
-
-    void SetTranslationFailure(const nsCString& msg) {
-        mTranslationLog.Assign(msg); 
-    }
-
-    const nsCString& TranslationLog() const { return mTranslationLog; }
-
-    WebGLContext *GetParentObject() const {
-        return Context();
-    }
-
-    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap);
-
-    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLShader)
-
-protected:
-
-    WebGLuint mGLName;
-    WebGLenum mType;
-    nsString mSource;
-    nsCString mTranslationLog; // The translation log should contain only ASCII characters
-    bool mNeedsTranslation;
-    nsTArray<WebGLMappedIdentifier> mAttributes;
-    nsTArray<WebGLMappedIdentifier> mUniforms;
-    nsTArray<WebGLUniformInfo> mUniformInfos;
-    int mAttribMaxNameLength;
-    bool mCompileStatus;
-};
-
 /** Takes an ASCII string like "foo[i]", turns it into "foo" and returns "[i]" in bracketPart
   * 
   * \param string input/output: the string to split, becomes the string without the bracket part
   * \param bracketPart output: gets the bracket part.
   * 
   * Notice that if there are multiple brackets like "foo[i].bar[j]", only the last bracket is split.
   */
 static bool SplitLastSquareBracket(nsACString& string, nsCString& bracketPart)
@@ -2137,109 +1951,16 @@ protected:
 
     // post-link data
     std::vector<bool> mAttribsInUse;
     nsAutoPtr<CStringMap> mIdentifierMap, mIdentifierReverseMap;
     nsAutoPtr<CStringToUniformInfoMap> mUniformInfoMap;
     int mAttribMaxNameLength;
 };
 
-class WebGLRenderbuffer MOZ_FINAL
-    : public nsISupports
-    , public WebGLRefCountedObject<WebGLRenderbuffer>
-    , public LinkedListElement<WebGLRenderbuffer>
-    , public WebGLRectangleObject
-    , public WebGLContextBoundObject
-    , public nsWrapperCache
-{
-public:
-    WebGLRenderbuffer(WebGLContext *context)
-        : WebGLContextBoundObject(context)
-        , mInternalFormat(0)
-        , mInternalFormatForGL(0)
-        , mHasEverBeenBound(false)
-        , mInitialized(false)
-    {
-        SetIsDOMBinding();
-        mContext->MakeContextCurrent();
-        mContext->gl->fGenRenderbuffers(1, &mGLName);
-        mContext->mRenderbuffers.insertBack(this);
-    }
-
-    ~WebGLRenderbuffer() {
-        DeleteOnce();
-    }
-
-    void Delete() {
-        mContext->MakeContextCurrent();
-        mContext->gl->fDeleteRenderbuffers(1, &mGLName);
-        LinkedListElement<WebGLRenderbuffer>::removeFrom(mContext->mRenderbuffers);
-    }
-
-    bool HasEverBeenBound() { return mHasEverBeenBound; }
-    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
-    WebGLuint GLName() const { return mGLName; }
-
-    bool Initialized() const { return mInitialized; }
-    void SetInitialized(bool aInitialized) { mInitialized = aInitialized; }
-
-    WebGLenum InternalFormat() const { return mInternalFormat; }
-    void SetInternalFormat(WebGLenum aInternalFormat) { mInternalFormat = aInternalFormat; }
-    
-    WebGLenum InternalFormatForGL() const { return mInternalFormatForGL; }
-    void SetInternalFormatForGL(WebGLenum aInternalFormatForGL) { mInternalFormatForGL = aInternalFormatForGL; }
-    
-    int64_t MemoryUsage() const {
-        int64_t pixels = int64_t(Width()) * int64_t(Height());
-
-        // If there is no defined format, we're not taking up any memory
-        if (!mInternalFormatForGL) {
-            return 0;
-        }
-
-        switch (mInternalFormatForGL) {
-            case LOCAL_GL_STENCIL_INDEX8:
-                return pixels;
-            case LOCAL_GL_RGBA4:
-            case LOCAL_GL_RGB5_A1:
-            case LOCAL_GL_RGB565:
-            case LOCAL_GL_DEPTH_COMPONENT16:
-                return 2 * pixels;
-            case LOCAL_GL_RGB8:
-            case LOCAL_GL_DEPTH_COMPONENT24:
-                return 3*pixels;
-            case LOCAL_GL_RGBA8:
-            case LOCAL_GL_DEPTH24_STENCIL8:
-                return 4*pixels;
-            default:
-                break;
-        }
-        NS_ABORT();
-        return 0;
-    }
-
-    WebGLContext *GetParentObject() const {
-        return Context();
-    }
-
-    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap);
-
-    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLRenderbuffer)
-
-protected:
-
-    WebGLuint mGLName;
-    WebGLenum mInternalFormat;
-    WebGLenum mInternalFormatForGL;
-    bool mHasEverBeenBound;
-    bool mInitialized;
-
-    friend class WebGLFramebuffer;
-};
 
 class WebGLFramebufferAttachment
 {
     // deleting a texture or renderbuffer immediately detaches it
     WebGLRefPtr<WebGLTexture> mTexturePtr;
     WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
     WebGLenum mAttachmentPoint;
     WebGLint mTextureLevel;
@@ -2773,49 +2494,16 @@ public:
     NS_DECL_ISUPPORTS
 
 protected:
     WebGLint mSize;
     WebGLenum mType;
     nsString mName;
 };
 
-class WebGLShaderPrecisionFormat MOZ_FINAL
-    : public nsISupports
-    , public WebGLContextBoundObject
-{
-public:
-    WebGLShaderPrecisionFormat(WebGLContext *context, WebGLint rangeMin, WebGLint rangeMax, WebGLint precision) :
-        WebGLContextBoundObject(context),
-        mRangeMin(rangeMin),
-        mRangeMax(rangeMax),
-        mPrecision(precision)
-    {
-    }
-
-    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope);
-
-    NS_DECL_ISUPPORTS
-
-    // WebIDL WebGLShaderPrecisionFormat API
-    WebGLint RangeMin() const {
-        return mRangeMin;
-    }
-    WebGLint RangeMax() const {
-        return mRangeMax;
-    }
-    WebGLint Precision() const {
-        return mPrecision;
-    }
-
-protected:
-    WebGLint mRangeMin;
-    WebGLint mRangeMax;
-    WebGLint mPrecision;
-};
 
 inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const {
     return mBoundFramebuffer ? mBoundFramebuffer->RectangleObject()
                              : static_cast<const WebGLRectangleObject*>(this);
 }
 
 /**
  ** Template implementations
--- a/content/canvas/src/WebGLObjectModel.h
+++ b/content/canvas/src/WebGLObjectModel.h
@@ -278,16 +278,57 @@ public:
 
     WebGLContext *Context() const { return mContext; }
 
 protected:
     WebGLContext *mContext;
     uint32_t mContextGeneration;
 };
 
+// this class is a mixin for GL objects that have dimensions
+// that we need to track.
+class WebGLRectangleObject
+{
+public:
+    WebGLRectangleObject()
+        : mWidth(0), mHeight(0) { }
+
+    WebGLRectangleObject(WebGLsizei width, WebGLsizei height)
+        : mWidth(width), mHeight(height) { }
+
+    WebGLsizei Width() const { return mWidth; }
+    void width(WebGLsizei value) { mWidth = value; }
+
+    WebGLsizei Height() const { return mHeight; }
+    void height(WebGLsizei value) { mHeight = value; }
+
+    void setDimensions(WebGLsizei width, WebGLsizei height) {
+        mWidth = width;
+        mHeight = height;
+    }
+
+    void setDimensions(WebGLRectangleObject *rect) {
+        if (rect) {
+            mWidth = rect->Width();
+            mHeight = rect->Height();
+        } else {
+            mWidth = 0;
+            mHeight = 0;
+        }
+    }
+
+    bool HasSameDimensionsAs(const WebGLRectangleObject& other) const {
+        return Width() == other.Width() && Height() == other.Height(); 
+    }
+
+protected:
+    WebGLsizei mWidth;
+    WebGLsizei mHeight;
+};
+
 }// namespace mozilla
 
 template <typename T>
 inline void
 ImplCycleCollectionUnlink(mozilla::WebGLRefPtr<T>& aField)
 {
   aField = nullptr;
 }
--- a/content/canvas/src/WebGLRenderbuffer.cpp
+++ b/content/canvas/src/WebGLRenderbuffer.cpp
@@ -1,23 +1,74 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
+#include "WebGLRenderbuffer.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 
 using namespace mozilla;
 
 JSObject*
 WebGLRenderbuffer::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) {
     return dom::WebGLRenderbufferBinding::Wrap(cx, scope, this, triedToWrap);
 }
 
+WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext *context)
+    : WebGLContextBoundObject(context)
+    , mInternalFormat(0)
+    , mInternalFormatForGL(0)
+    , mHasEverBeenBound(false)
+    , mInitialized(false)
+{
+    SetIsDOMBinding();
+    mContext->MakeContextCurrent();
+    mContext->gl->fGenRenderbuffers(1, &mGLName);
+    mContext->mRenderbuffers.insertBack(this);
+}
+
+void
+WebGLRenderbuffer::Delete() {
+    mContext->MakeContextCurrent();
+    mContext->gl->fDeleteRenderbuffers(1, &mGLName);
+    LinkedListElement<WebGLRenderbuffer>::removeFrom(mContext->mRenderbuffers);
+}
+
+int64_t
+WebGLRenderbuffer::MemoryUsage() const {
+    int64_t pixels = int64_t(Width()) * int64_t(Height());
+
+    // If there is no defined format, we're not taking up any memory
+    if (!mInternalFormatForGL) {
+        return 0;
+    }
+
+    switch (mInternalFormatForGL) {
+        case LOCAL_GL_STENCIL_INDEX8:
+            return pixels;
+        case LOCAL_GL_RGBA4:
+        case LOCAL_GL_RGB5_A1:
+        case LOCAL_GL_RGB565:
+        case LOCAL_GL_DEPTH_COMPONENT16:
+            return 2 * pixels;
+        case LOCAL_GL_RGB8:
+        case LOCAL_GL_DEPTH_COMPONENT24:
+            return 3*pixels;
+        case LOCAL_GL_RGBA8:
+        case LOCAL_GL_DEPTH24_STENCIL8:
+            return 4*pixels;
+        default:
+            break;
+    }
+    NS_ABORT();
+    return 0;
+}
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLRenderbuffer)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLRenderbuffer)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLRenderbuffer)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLRenderbuffer)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLRenderbuffer.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGLRENDERBUFFER_H_
+#define WEBGLRENDERBUFFER_H_
+
+#include "WebGLObjectModel.h"
+
+#include "nsWrapperCache.h"
+
+#include "mozilla/LinkedList.h"
+
+namespace mozilla {
+
+class WebGLRenderbuffer MOZ_FINAL
+    : public nsISupports
+    , public WebGLRefCountedObject<WebGLRenderbuffer>
+    , public LinkedListElement<WebGLRenderbuffer>
+    , public WebGLRectangleObject
+    , public WebGLContextBoundObject
+    , public nsWrapperCache
+{
+public:
+    WebGLRenderbuffer(WebGLContext *context);
+
+    ~WebGLRenderbuffer() {
+        DeleteOnce();
+    }
+
+    void Delete();
+
+    bool HasEverBeenBound() { return mHasEverBeenBound; }
+    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
+    WebGLuint GLName() const { return mGLName; }
+
+    bool Initialized() const { return mInitialized; }
+    void SetInitialized(bool aInitialized) { mInitialized = aInitialized; }
+
+    WebGLenum InternalFormat() const { return mInternalFormat; }
+    void SetInternalFormat(WebGLenum aInternalFormat) { mInternalFormat = aInternalFormat; }
+
+    WebGLenum InternalFormatForGL() const { return mInternalFormatForGL; }
+    void SetInternalFormatForGL(WebGLenum aInternalFormatForGL) { mInternalFormatForGL = aInternalFormatForGL; }
+
+    int64_t MemoryUsage() const;
+
+    WebGLContext *GetParentObject() const {
+        return Context();
+    }
+
+    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap);
+
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLRenderbuffer)
+
+protected:
+
+    WebGLuint mGLName;
+    WebGLenum mInternalFormat;
+    WebGLenum mInternalFormatForGL;
+    bool mHasEverBeenBound;
+    bool mInitialized;
+
+    friend class WebGLFramebuffer;
+};
+} // namespace mozilla
+
+#endif
--- a/content/canvas/src/WebGLShader.cpp
+++ b/content/canvas/src/WebGLShader.cpp
@@ -1,23 +1,59 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "WebGLShader.h"
 #include "WebGLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 
 using namespace mozilla;
 
 JSObject*
 WebGLShader::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) {
     return dom::WebGLShaderBinding::Wrap(cx, scope, this, triedToWrap);
 }
 
+WebGLShader::WebGLShader(WebGLContext *context, WebGLenum stype)
+    : WebGLContextBoundObject(context)
+    , mType(stype)
+    , mNeedsTranslation(true)
+    , mAttribMaxNameLength(0)
+    , mCompileStatus(false)
+{
+    SetIsDOMBinding();
+    mContext->MakeContextCurrent();
+    mGLName = mContext->gl->fCreateShader(mType);
+    mContext->mShaders.insertBack(this);
+}
+
+void
+WebGLShader::Delete() {
+    mSource.Truncate();
+    mTranslationLog.Truncate();
+    mContext->MakeContextCurrent();
+    mContext->gl->fDeleteShader(mGLName);
+    LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders);
+}
+
+size_t
+WebGLShader::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const {
+    return aMallocSizeOf(this) +
+           mSource.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
+           mTranslationLog.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+}
+
+void
+WebGLShader::SetTranslationSuccess() {
+    mTranslationLog.SetIsVoid(true);
+    mNeedsTranslation = false;
+}
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShader)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLShader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLShader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLShader)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLShader.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGLSHADER_H_
+#define WEBGLSHADER_H_
+
+#include "WebGLObjectModel.h"
+
+#include "nsWrapperCache.h"
+
+#include "angle/ShaderLang.h"
+
+#include "mozilla/LinkedList.h"
+
+namespace mozilla {
+
+struct WebGLMappedIdentifier {
+    nsCString original, mapped; // ASCII strings
+    WebGLMappedIdentifier(const nsACString& o, const nsACString& m) : original(o), mapped(m) {}
+};
+
+struct WebGLUniformInfo {
+    uint32_t arraySize;
+    bool isArray;
+    ShDataType type;
+
+    WebGLUniformInfo(uint32_t s = 0, bool a = false, ShDataType t = SH_NONE)
+        : arraySize(s), isArray(a), type(t) {}
+
+    int ElementSize() const {
+        switch (type) {
+            case SH_INT:
+            case SH_FLOAT:
+            case SH_BOOL:
+            case SH_SAMPLER_2D:
+            case SH_SAMPLER_CUBE:
+                return 1;
+            case SH_INT_VEC2:
+            case SH_FLOAT_VEC2:
+            case SH_BOOL_VEC2:
+                return 2;
+            case SH_INT_VEC3:
+            case SH_FLOAT_VEC3:
+            case SH_BOOL_VEC3:
+                return 3;
+            case SH_INT_VEC4:
+            case SH_FLOAT_VEC4:
+            case SH_BOOL_VEC4:
+            case SH_FLOAT_MAT2:
+                return 4;
+            case SH_FLOAT_MAT3:
+                return 9;
+            case SH_FLOAT_MAT4:
+                return 16;
+            default:
+                NS_ABORT(); // should never get here
+                return 0;
+        }
+    }
+};
+
+class WebGLShader MOZ_FINAL
+    : public nsISupports
+    , public WebGLRefCountedObject<WebGLShader>
+    , public LinkedListElement<WebGLShader>
+    , public WebGLContextBoundObject
+    , public nsWrapperCache
+{
+    friend class WebGLContext;
+    friend class WebGLProgram;
+
+public:
+    WebGLShader(WebGLContext *context, WebGLenum stype);
+
+    ~WebGLShader() {
+        DeleteOnce();
+    }
+
+    size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+
+    WebGLuint GLName() { return mGLName; }
+    WebGLenum ShaderType() { return mType; }
+
+    void SetSource(const nsAString& src) {
+        // XXX do some quick gzip here maybe -- getting this will be very rare
+        mSource.Assign(src);
+    }
+
+    const nsString& Source() const { return mSource; }
+
+    void SetNeedsTranslation() { mNeedsTranslation = true; }
+    bool NeedsTranslation() const { return mNeedsTranslation; }
+
+    void SetCompileStatus (bool status) {
+        mCompileStatus = status;
+    }
+
+    void Delete();
+
+    bool CompileStatus() const {
+        return mCompileStatus;
+    }
+
+    void SetTranslationSuccess();
+
+    void SetTranslationFailure(const nsCString& msg) {
+        mTranslationLog.Assign(msg); 
+    }
+
+    const nsCString& TranslationLog() const { return mTranslationLog; }
+
+    WebGLContext *GetParentObject() const {
+        return Context();
+    }
+
+    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap);
+
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLShader)
+
+protected:
+
+    WebGLuint mGLName;
+    WebGLenum mType;
+    nsString mSource;
+    nsCString mTranslationLog; // The translation log should contain only ASCII characters
+    bool mNeedsTranslation;
+    nsTArray<WebGLMappedIdentifier> mAttributes;
+    nsTArray<WebGLMappedIdentifier> mUniforms;
+    nsTArray<WebGLUniformInfo> mUniformInfos;
+    int mAttribMaxNameLength;
+    bool mCompileStatus;
+};
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLShaderPrecisionFormat.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGLSHADERPRECISIONFORMAT_H_
+#define WEBGLSHADERPRECISIONFORMAT_H_
+
+#include "WebGLObjectModel.h"
+
+namespace mozilla {
+
+class WebGLBuffer;
+
+class WebGLShaderPrecisionFormat MOZ_FINAL
+    : public nsISupports
+    , public WebGLContextBoundObject
+{
+public:
+    WebGLShaderPrecisionFormat(WebGLContext *context, WebGLint rangeMin, WebGLint rangeMax, WebGLint precision) :
+        WebGLContextBoundObject(context),
+        mRangeMin(rangeMin),
+        mRangeMax(rangeMax),
+        mPrecision(precision)
+    {
+    }
+
+    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope);
+
+    NS_DECL_ISUPPORTS
+
+    // WebIDL WebGLShaderPrecisionFormat API
+    WebGLint RangeMin() const {
+        return mRangeMin;
+    }
+    WebGLint RangeMax() const {
+        return mRangeMax;
+    }
+    WebGLint Precision() const {
+        return mPrecision;
+    }
+
+protected:
+    WebGLint mRangeMin;
+    WebGLint mRangeMax;
+    WebGLint mPrecision;
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -376,26 +376,26 @@ nsDOMEvent::StopImmediatePropagation()
 {
   mEvent->flags |=
     (NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY | NS_EVENT_FLAG_STOP_DISPATCH);
   return NS_OK;
 }
 
 static nsIDocument* GetDocumentForReport(nsEvent* aEvent)
 {
-  nsCOMPtr<nsINode> node = do_QueryInterface(aEvent->currentTarget);
-  if (node)
+  nsIDOMEventTarget* target = aEvent->currentTarget;
+  if (nsCOMPtr<nsINode> node = do_QueryInterface(target)) {
     return node->OwnerDoc();
+  }
 
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aEvent->currentTarget);
-  if (!window)
-    return nullptr;
+  if (nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(target)) {
+    return window->GetExtantDoc();
+  }
 
-  nsCOMPtr<nsIDocument> doc(do_QueryInterface(window->GetExtantDocument()));
-  return doc;
+  return nullptr;
 }
 
 static void
 ReportUseOfDeprecatedMethod(nsEvent* aEvent, nsIDOMEvent* aDOMEvent,
                             const char* aWarning)
 {
   nsCOMPtr<nsIDocument> doc(GetDocumentForReport(aEvent));
 
--- a/content/html/content/src/nsHTMLAnchorElement.cpp
+++ b/content/html/content/src/nsHTMLAnchorElement.cpp
@@ -42,17 +42,17 @@ public:
 
   // nsIDOMHTMLElement
   NS_FORWARD_NSIDOMHTMLELEMENT_TO_GENERIC
 
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
   virtual bool Draggable() const MOZ_OVERRIDE;
 
   // nsIDOMHTMLAnchorElement
-  NS_DECL_NSIDOMHTMLANCHORELEMENT  
+  NS_DECL_NSIDOMHTMLANCHORELEMENT
 
   // DOM memory reporter participant
   NS_DECL_SIZEOF_EXCLUDING_THIS
 
   // nsILink
   NS_IMETHOD LinkAdded() { return NS_OK; }
   NS_IMETHOD LinkRemoved() { return NS_OK; }
 
@@ -87,17 +87,17 @@ public:
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsEventStates IntrinsicState() const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 
   virtual nsIDOMNode* AsDOMNode() { return this; }
-  
+
   virtual void OnDNSPrefetchDeferred();
   virtual void OnDNSPrefetchRequested();
   virtual bool HasDeferredDNSPrefetchRequest();
 
 protected:
   virtual void GetItemValueText(nsAString& text);
   virtual void SetItemValueText(const nsAString& text);
 };
@@ -155,16 +155,17 @@ NS_IMPL_STRING_ATTR(nsHTMLAnchorElement,
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Coords, coords)
 NS_IMPL_URI_ATTR(nsHTMLAnchorElement, Href, href)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Hreflang, hreflang)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Name, name)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rel, rel)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rev, rev)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Shape, shape)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Type, type)
+NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Download, download)
 
 int32_t
 nsHTMLAnchorElement::TabIndexDefault()
 {
   return 0;
 }
 
 void
--- a/content/html/content/src/nsHTMLAreaElement.cpp
+++ b/content/html/content/src/nsHTMLAreaElement.cpp
@@ -115,16 +115,17 @@ NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLA
 NS_IMPL_ELEMENT_CLONE(nsHTMLAreaElement)
 
 
 NS_IMPL_STRING_ATTR(nsHTMLAreaElement, Alt, alt)
 NS_IMPL_STRING_ATTR(nsHTMLAreaElement, Coords, coords)
 NS_IMPL_URI_ATTR(nsHTMLAreaElement, Href, href)
 NS_IMPL_BOOL_ATTR(nsHTMLAreaElement, NoHref, nohref)
 NS_IMPL_STRING_ATTR(nsHTMLAreaElement, Shape, shape)
+NS_IMPL_STRING_ATTR(nsHTMLAreaElement, Download, download)
 
 int32_t
 nsHTMLAreaElement::TabIndexDefault()
 {
   return 0;
 }
 
 void
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -878,16 +878,17 @@ nsHTMLFormElement::SubmitSubmission(nsFo
 
     nsCOMPtr<nsIInputStream> postDataStream;
     rv = aFormSubmission->GetEncodedSubmission(actionURI,
                                                getter_AddRefs(postDataStream));
     NS_ENSURE_SUBMIT_SUCCESS(rv);
 
     rv = linkHandler->OnLinkClickSync(this, actionURI,
                                       target.get(),
+                                      NullString(),
                                       postDataStream, nullptr,
                                       getter_AddRefs(docShell),
                                       getter_AddRefs(mSubmittingRequest));
     NS_ENSURE_SUBMIT_SUCCESS(rv);
   }
 
   // Even if the submit succeeds, it's possible for there to be no docshell
   // or request; for example, if it's to a named anchor within the same page
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -396,21 +396,22 @@ MediaDecoderStateMachine::MediaDecoderSt
     mRealTime = false;
 
   mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT_S;
   mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS;
 
   // If we've got more than mAmpleVideoFrames decoded video frames waiting in
   // the video queue, we will not decode any more video frames until some have
   // been consumed by the play state machine thread.
-#ifdef MOZ_WIDGET_GONK
-  // On B2G this is decided by a similar value which varies for each OMX decoder
-  // |OMX_PARAM_PORTDEFINITIONTYPE::nBufferCountMin|. This number must be less
-  // than the OMX equivalent or gecko will think it is chronically starved of
-  // video frames. All decoders seen so far have a value of at least 4.
+#if defined(MOZ_WIDGET_GONK) || defined(MOZ_MEDIA_PLUGINS)
+  // On B2G and Android this is decided by a similar value which varies for
+  // each OMX decoder |OMX_PARAM_PORTDEFINITIONTYPE::nBufferCountMin|. This
+  // number must be less than the OMX equivalent or gecko will think it is
+  // chronically starved of video frames. All decoders seen so far have a value
+  // of at least 4.
   mAmpleVideoFrames = Preferences::GetUint("media.video-queue.default-size", 3);
 #else
   mAmpleVideoFrames = Preferences::GetUint("media.video-queue.default-size", 10);
 #endif
   if (mAmpleVideoFrames < 2) {
     mAmpleVideoFrames = 2;
   }
 }
--- a/content/media/ogg/OggReader.cpp
+++ b/content/media/ogg/OggReader.cpp
@@ -681,18 +681,17 @@ bool OggReader::ReadOggChain()
     rate = mVorbisState->mInfo.rate;
     channels = mVorbisState->mInfo.channels;
     tags = mVorbisState->GetTags();
   }
 
 #ifdef MOZ_OPUS
   if ((newOpusState && ReadHeaders(newOpusState)) &&
       (mOpusState->mRate == newOpusState->mRate) &&
-      (mOpusState->mChannels == newOpusState->mChannels) &&
-      (mOpusState->mPreSkip == newOpusState->mPreSkip)) {
+      (mOpusState->mChannels == newOpusState->mChannels)) {
     mOpusState->Reset();
     mOpusState = newOpusState;
     mOpusSerial = mOpusState->mSerial;
     chained = true;
     rate = mOpusState->mRate;
     channels = mOpusState->mChannels;
     tags = mOpusState->GetTags();
   }
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -210,16 +210,17 @@ MOCHITEST_FILES += \
 		chain.ogg \
 		chain.opus \
 		variable-samplerate.ogg \
 		variable-samplerate.opus \
 		variable-channel.ogg \
 		variable-channel.opus \
 		chained-video.ogv \
 		chained-audio-video.ogg \
+		variable-preskip.opus \
 		dirac.ogg \
 		multiple-bos.ogg \
 		split.webm \
 		seek.ogv \
 		seek.webm \
 		seek.yuv \
 		short-video.ogv \
 		small-shot.ogg \
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -350,16 +350,19 @@ var gChainingTests = [
   // different samplerate accross links.
   { name:"variable-samplerate.opus", type:"audio/ogg; codec=opus", links: 2 },
   // A chained video file. We don't support those, so only one link should be
   // reported.
   { name:"chained-video.ogv", type:"video/ogg", links: 1 },
   // A file that consist in 4 links of audio, then another link that has video.
   // We should stop right after the 4 audio links.
   { name:"chained-audio-video.ogg", type:"video/ogg", links: 4 },
+  // An opus file that has two links, with a different preskip value for each
+  // link. We should be able to play both links.
+  { name:"variable-preskip.opus", type:"audio/ogg; codec=opus", links: 2 },
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
 // These are files with non-trivial tag sets.
 // Used by test_metadata.html.
 var gMetadataTests = [
   // Ogg Vorbis files
   { name:"short-video.ogv", tags: {
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a82e831ce664fad9600a7697aa48f48a1c0339da
GIT binary patch
literal 17660
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
B|5*S4
--- a/content/svg/content/src/nsSVGAElement.cpp
+++ b/content/svg/content/src/nsSVGAElement.cpp
@@ -63,16 +63,17 @@ nsSVGAElement::nsSVGAElement(already_Add
 
 /* readonly attribute nsIDOMSVGAnimatedString href; */
 NS_IMETHODIMP
 nsSVGAElement::GetHref(nsIDOMSVGAnimatedString * *aHref)
 {
   return mStringAttributes[HREF].ToDOMAnimatedString(aHref, this);
 }
 
+NS_IMPL_STRING_ATTR(nsSVGAElement, Download, download)
 
 //----------------------------------------------------------------------
 // nsINode methods
 
 nsresult
 nsSVGAElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
 {
   nsresult rv = Element::PreHandleEvent(aVisitor);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1496,16 +1496,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
         flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
 
     return InternalLoad(aURI,
                         referrer,
                         owner,
                         flags,
                         target.get(),
                         nullptr,         // No type hint
+                        NullString(),    // No forced download
                         postStream,
                         headersStream,
                         loadType,
                         nullptr,         // No SHEntry
                         aFirstParty,
                         nullptr,         // No nsIDocShell
                         nullptr);        // No nsIRequest
 }
@@ -4489,17 +4490,17 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, 
     errorPageUrl.AppendASCII(escapedDescription.get());
 
     nsCOMPtr<nsIURI> errorPageURI;
     nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return InternalLoad(errorPageURI, nullptr, nullptr,
                         INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr,
-                        nullptr, nullptr, LOAD_ERROR_PAGE,
+                        NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
                         nullptr, true, nullptr, nullptr);
 }
 
 
 NS_IMETHODIMP
 nsDocShell::Reload(uint32_t aReloadFlags)
 {
     if (!IsNavigationAllowed()) {
@@ -4520,17 +4521,17 @@ nsDocShell::Reload(uint32_t aReloadFlags
     nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
     bool canReload = true;
     if (rootSH) {
       shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
     }
 
     if (!canReload)
       return NS_OK;
-    
+
     /* If you change this part of code, make sure bug 45297 does not re-occur */
     if (mOSHE) {
         rv = LoadHistoryEntry(mOSHE, loadType);
     }
     else if (mLSHE) { // In case a reload happened before the current load is done
         rv = LoadHistoryEntry(mLSHE, loadType);
     }
     else {
@@ -4544,25 +4545,25 @@ nsDocShell::Reload(uint32_t aReloadFlags
         }
 
         rv = InternalLoad(mCurrentURI,
                           mReferrerURI,
                           principal,
                           INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document
                           nullptr,         // No window target
                           NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
+                          NullString(),    // No forced download
                           nullptr,         // No post data
                           nullptr,         // No headers data
-                          loadType,       // Load type
+                          loadType,        // Load type
                           nullptr,         // No SHEntry
                           true,
                           nullptr,         // No nsIDocShell
                           nullptr);        // No nsIRequest
     }
-    
 
     return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::Stop(uint32_t aStopFlags)
 {
     // Revoke any pending event related to content viewer restoration
@@ -6220,33 +6221,31 @@ nsDocShell::OnProgressChange(nsIWebProgr
 {
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
                           uint32_t aStateFlags, nsresult aStatus)
 {
-    nsresult rv;
-
     if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
         // Save timing statistics.
         nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
         nsCOMPtr<nsIURI> uri;
         channel->GetURI(getter_AddRefs(uri));
         nsAutoCString aURI;
         uri->GetAsciiSpec(aURI);
 
         nsCOMPtr<nsIWyciwygChannel>  wcwgChannel(do_QueryInterface(aRequest));
         nsCOMPtr<nsIWebProgress> webProgress =
             do_QueryInterface(GetAsSupports(this));
 
         // We don't update navigation timing for wyciwyg channels
         if (this == aProgress && !wcwgChannel){
-            rv = MaybeInitTiming();
+            MaybeInitTiming();
             if (mTiming) {
                 mTiming->NotifyFetchStart(uri, ConvertLoadTypeToNavigationType(mLoadType));
             } 
         }
 
         // Was the wyciwyg document loaded on this docshell?
         if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) {
             bool equalUri = true;
@@ -6280,21 +6279,21 @@ nsDocShell::OnStateChange(nsIWebProgress
                             ClearFrameHistory(entry);
                     }
                 }
 
                 // This is a document.write(). Get the made-up url
                 // from the channel and store it in session history.
                 // Pass false for aCloneChildren, since we're creating
                 // a new DOM here.
-                rv = AddToSessionHistory(uri, wcwgChannel, nullptr, false,
-                                         getter_AddRefs(mLSHE));
+                AddToSessionHistory(uri, wcwgChannel, nullptr, false,
+                                    getter_AddRefs(mLSHE));
                 SetCurrentURI(uri, aRequest, true, 0);
                 // Save history state of the previous page
-                rv = PersistLayoutHistoryState();
+                PersistLayoutHistoryState();
                 // We'll never get an Embed() for this load, so just go ahead
                 // and SetHistoryEntry now.
                 SetHistoryEntry(&mOSHE, mLSHE);
             }
         
         }
         // Page has begun to load
         mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
@@ -8315,22 +8314,23 @@ public:
         mLoadType(aLoadType),
         mFirstParty(aFirstParty)
     {
         // Make sure to keep null things null as needed
         if (aTypeHint) {
             mTypeHint = aTypeHint;
         }
     }
-    
+
     NS_IMETHOD Run() {
         return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
                                        nullptr, mTypeHint.get(),
-                                       mPostData, mHeadersData, mLoadType,
-                                       mSHEntry, mFirstParty, nullptr, nullptr);
+                                       NullString(), mPostData, mHeadersData,
+                                       mLoadType, mSHEntry, mFirstParty,
+                                       nullptr, nullptr);
     }
 
 private:
 
     // Use IDL strings so .get() returns null by default
     nsXPIDLString mWindowTarget;
     nsXPIDLCString mTypeHint;
 
@@ -8365,16 +8365,17 @@ nsDocShell::JustStartedNetworkLoad()
 
 NS_IMETHODIMP
 nsDocShell::InternalLoad(nsIURI * aURI,
                          nsIURI * aReferrer,
                          nsISupports * aOwner,
                          uint32_t aFlags,
                          const PRUnichar *aWindowTarget,
                          const char* aTypeHint,
+                         const nsAString& aFileName,
                          nsIInputStream * aPostData,
                          nsIInputStream * aHeadersData,
                          uint32_t aLoadType,
                          nsISHEntry * aSHEntry,
                          bool aFirstParty,
                          nsIDocShell** aDocShell,
                          nsIRequest** aRequest)
 {
@@ -8384,17 +8385,16 @@ nsDocShell::InternalLoad(nsIURI * aURI,
 #ifdef PR_LOGGING
     if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
         nsAutoCString spec;
         if (aURI)
             aURI->GetSpec(spec);
         PR_LogPrint("DOCSHELL %p InternalLoad %s\n", this, spec.get());
     }
 #endif
-    
     // Initialize aDocShell/aRequest
     if (aDocShell) {
         *aDocShell = nullptr;
     }
     if (aRequest) {
         *aRequest = nullptr;
     }
 
@@ -8599,16 +8599,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
         //
         if (NS_SUCCEEDED(rv) && targetDocShell) {
             rv = targetDocShell->InternalLoad(aURI,
                                               aReferrer,
                                               owner,
                                               aFlags,
                                               nullptr,         // No window target
                                               aTypeHint,
+                                              NullString(),    // No forced download
                                               aPostData,
                                               aHeadersData,
                                               aLoadType,
                                               aSHEntry,
                                               aFirstParty,
                                               aDocShell,
                                               aRequest);
             if (rv == NS_ERROR_NO_CONTENT) {
@@ -9093,18 +9094,18 @@ nsDocShell::InternalLoad(nsIURI * aURI,
 
             aSHEntry->SyncPresentationState();
         }
     }
 
     nsCOMPtr<nsIRequest> req;
     rv = DoURILoad(aURI, aReferrer,
                    !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
-                   owner, aTypeHint, aPostData, aHeadersData, aFirstParty,
-                   aDocShell, getter_AddRefs(req),
+                   owner, aTypeHint, aFileName, aPostData, aHeadersData,
+                   aFirstParty, aDocShell, getter_AddRefs(req),
                    (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
                    (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
                    (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0);
     if (req && aRequest)
         NS_ADDREF(*aRequest = req);
 
     if (NS_FAILED(rv)) {
         nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
@@ -9167,16 +9168,17 @@ nsDocShell::GetInheritedPrincipal(bool a
 }
 
 nsresult
 nsDocShell::DoURILoad(nsIURI * aURI,
                       nsIURI * aReferrerURI,
                       bool aSendReferrer,
                       nsISupports * aOwner,
                       const char * aTypeHint,
+                      const nsAString & aFileName,
                       nsIInputStream * aPostData,
                       nsIInputStream * aHeadersData,
                       bool aFirstParty,
                       nsIDocShell ** aDocShell,
                       nsIRequest ** aRequest,
                       bool aIsNewWindowTarget,
                       bool aBypassClassifier,
                       bool aForceAllowCookies)
@@ -9266,21 +9268,29 @@ nsDocShell::DoURILoad(nsIURI * aURI,
     // This is important for correct error page/session history interaction
     if (aRequest)
         NS_ADDREF(*aRequest = channel);
 
     channel->SetOriginalURI(aURI);
     if (aTypeHint && *aTypeHint) {
         channel->SetContentType(nsDependentCString(aTypeHint));
         mContentTypeHint = aTypeHint;
-    }
-    else {
+    } else {
         mContentTypeHint.Truncate();
     }
-    
+
+    if (!aFileName.IsVoid()) {
+        rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (!aFileName.IsEmpty()) {
+            rv = channel->SetContentDispositionFilename(aFileName);
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
+    }
+
     //hack
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
     nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
     if (httpChannelInternal) {
       if (aForceAllowCookies) {
         httpChannelInternal->SetForceAllowThirdPartyCookie(true);
       } 
       if (aFirstParty) {
@@ -10583,21 +10593,22 @@ nsDocShell::LoadHistoryEntry(nsISHEntry 
         return NS_BINDING_ABORTED;
     }
 
     rv = InternalLoad(uri,
                       referrerURI,
                       owner,
                       INTERNAL_LOAD_FLAGS_NONE, // Do not inherit owner from document (security-critical!)
                       nullptr,            // No window target
-                      contentType.get(), // Type hint
-                      postData,          // Post data stream
+                      contentType.get(),  // Type hint
+                      NullString(),       // No forced file download
+                      postData,           // Post data stream
                       nullptr,            // No headers stream
-                      aLoadType,         // Load type
-                      aEntry,            // SHEntry
+                      aLoadType,          // Load type
+                      aEntry,             // SHEntry
                       true,
                       nullptr,            // No nsIDocShell
                       nullptr);           // No nsIRequest
     return rv;
 }
 
 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(bool* aShould)
 {
@@ -11820,71 +11831,76 @@ nsDocShell::SelectNone(void)
 
 // link handling
 
 class OnLinkClickEvent : public nsRunnable {
 public:
   OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
                    nsIURI* aURI,
                    const PRUnichar* aTargetSpec,
-                   nsIInputStream* aPostDataStream, 
+                   const nsAString& aFileName,
+                   nsIInputStream* aPostDataStream,
                    nsIInputStream* aHeadersDataStream,
                    bool aIsTrusted);
 
   NS_IMETHOD Run() {
     nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
     nsAutoPopupStatePusher popupStatePusher(window, mPopupState);
 
     nsCxPusher pusher;
     if (mIsTrusted || pusher.Push(mContent)) {
       mHandler->OnLinkClickSync(mContent, mURI,
-                                mTargetSpec.get(), mPostDataStream,
-                                mHeadersDataStream,
+                                mTargetSpec.get(), mFileName,
+                                mPostDataStream, mHeadersDataStream,
                                 nullptr, nullptr);
     }
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsDocShell>     mHandler;
   nsCOMPtr<nsIURI>         mURI;
   nsString                 mTargetSpec;
+  nsString                mFileName;
   nsCOMPtr<nsIInputStream> mPostDataStream;
   nsCOMPtr<nsIInputStream> mHeadersDataStream;
   nsCOMPtr<nsIContent>     mContent;
   PopupControlState        mPopupState;
   bool                     mIsTrusted;
 };
 
 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
                                    nsIContent *aContent,
                                    nsIURI* aURI,
                                    const PRUnichar* aTargetSpec,
+                                   const nsAString& aFileName,
                                    nsIInputStream* aPostDataStream,
                                    nsIInputStream* aHeadersDataStream,
                                    bool aIsTrusted)
   : mHandler(aHandler)
   , mURI(aURI)
   , mTargetSpec(aTargetSpec)
+  , mFileName(aFileName)
   , mPostDataStream(aPostDataStream)
   , mHeadersDataStream(aHeadersDataStream)
   , mContent(aContent)
   , mIsTrusted(aIsTrusted)
 {
   nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
 
   mPopupState = window->GetPopupControlState();
 }
 
 //----------------------------------------
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClick(nsIContent* aContent,
                         nsIURI* aURI,
                         const PRUnichar* aTargetSpec,
+                        const nsAString& aFileName,
                         nsIInputStream* aPostDataStream,
                         nsIInputStream* aHeadersDataStream,
                         bool aIsTrusted)
 {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
   if (!IsOKToLoadURI(aURI)) {
     return NS_OK;
@@ -11914,25 +11930,26 @@ nsDocShell::OnLinkClick(nsIContent* aCon
     rv = browserChrome3->OnBeforeLinkTraversal(oldTarget, aURI,
                                                linkNode, mIsAppTab, target);
   }
   
   if (NS_FAILED(rv))
     target = aTargetSpec;  
 
   nsCOMPtr<nsIRunnable> ev =
-      new OnLinkClickEvent(this, aContent, aURI, target.get(),
+      new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName, 
                            aPostDataStream, aHeadersDataStream, aIsTrusted);
   return NS_DispatchToCurrentThread(ev);
 }
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClickSync(nsIContent *aContent,
                             nsIURI* aURI,
                             const PRUnichar* aTargetSpec,
+                            const nsAString& aFileName,
                             nsIInputStream* aPostDataStream,
                             nsIInputStream* aHeadersDataStream,
                             nsIDocShell** aDocShell,
                             nsIRequest** aRequest)
 {
   // Initialize the DocShell / Request
   if (aDocShell) {
     *aDocShell = nullptr;
@@ -12015,29 +12032,30 @@ nsDocShell::OnLinkClickSync(nsIContent *
   // Clone the URI now, in case a content policy or something messes
   // with it under InternalLoad; we do _not_ want to change the URI
   // our caller passed in.
   nsCOMPtr<nsIURI> clonedURI;
   aURI->Clone(getter_AddRefs(clonedURI));
   if (!clonedURI) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
-  
+
   nsresult rv = InternalLoad(clonedURI,                 // New URI
                              referer,                   // Referer URI
                              aContent->NodePrincipal(), // Owner is our node's
                                                         // principal
                              INTERNAL_LOAD_FLAGS_NONE,
                              target.get(),              // Window target
                              NS_LossyConvertUTF16toASCII(typeHint).get(),
+                             aFileName,                 // Download as file
                              aPostDataStream,           // Post data stream
                              aHeadersDataStream,        // Headers stream
                              LOAD_LINK,                 // Load type
-                             nullptr,                    // No SHEntry
-                             true,                   // first party site
+                             nullptr,                   // No SHEntry
+                             true,                      // first party site
                              aDocShell,                 // DocShell out-param
                              aRequest);                 // Request out-param
   if (NS_SUCCEEDED(rv)) {
     DispatchPings(aContent, referer);
   }
   return rv;
 }
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -192,22 +192,24 @@ public:
     // Need to implement (and forward) nsISecurityEventSink, because
     // nsIWebProgressListener has methods with identical names...
     NS_FORWARD_NSISECURITYEVENTSINK(nsDocLoader::)
 
     // nsILinkHandler
     NS_IMETHOD OnLinkClick(nsIContent* aContent,
         nsIURI* aURI,
         const PRUnichar* aTargetSpec,
+        const nsAString& aFileName,
         nsIInputStream* aPostDataStream,
         nsIInputStream* aHeadersDataStream,
         bool aIsTrusted);
     NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
         nsIURI* aURI,
         const PRUnichar* aTargetSpec,
+        const nsAString& aFileName,
         nsIInputStream* aPostDataStream = 0,
         nsIInputStream* aHeadersDataStream = 0,
         nsIDocShell** aDocShell = 0,
         nsIRequest** aRequest = 0);
     NS_IMETHOD OnOverLink(nsIContent* aContent,
         nsIURI* aURI,
         const PRUnichar* aTargetSpec);
     NS_IMETHOD OnLeaveLink();
@@ -286,16 +288,17 @@ protected:
     // Actually open a channel and perform a URI load.  Note: whatever owner is
     // passed to this function will be set on the channel.  Callers who wish to
     // not have an owner on the channel should just pass null.
     virtual nsresult DoURILoad(nsIURI * aURI,
                                nsIURI * aReferrer,
                                bool aSendReferrer,
                                nsISupports * aOwner,
                                const char * aTypeHint,
+                               const nsAString & aFileName,
                                nsIInputStream * aPostData,
                                nsIInputStream * aHeadersData,
                                bool firstParty,
                                nsIDocShell ** aDocShell,
                                nsIRequest ** aRequest,
                                bool aIsNewWindowTarget,
                                bool aBypassClassifier,
                                bool aForceAllowCookies);
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -34,17 +34,17 @@ interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 
-[scriptable, builtinclass, uuid(318CE516-3F7A-41F6-8F3D-3661650F7A46)]
+[scriptable, builtinclass, uuid(a106db7f-6449-4a6b-914f-834ba308c3e2)]
 interface nsIDocShell : nsISupports
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -113,28 +113,31 @@ interface nsIDocShell : nsISupports
    * @param aOwner          - Owner (security principal) 
    * @param aInheritOwner   - Flag indicating whether the owner of the current
    *                          document should be inherited if aOwner is null.
    * @param aStopActiveDoc  - Flag indicating whether loading the current
    *                          document should be stopped.
    * @param aWindowTarget   - Window target for the load.
    * @param aTypeHint       - A hint as to the content-type of the resulting
    *                          data.  May be null or empty if no hint.
+   * @param aFileName       - Non-null when the link should be downloaded as
+                              the given filename.
    * @param aPostDataStream - Post data stream (if POSTing)
    * @param aHeadersStream  - Stream containing "extra" request headers...
    * @param aLoadFlags      - Flags to modify load behaviour. Flags are defined
    *                          in nsIWebNavigation.
    * @param aSHEntry        - Active Session History entry (if loading from SH)
    */
   [noscript]void internalLoad(in nsIURI aURI,
                               in nsIURI aReferrer,
                               in nsISupports aOwner,
                               in uint32_t aFlags,
                               in wstring aWindowTarget,
                               in string aTypeHint,
+                              in AString aFileName,
                               in nsIInputStream aPostDataStream,
                               in nsIInputStream aHeadersStream,
                               in unsigned long aLoadFlags,
                               in nsISHEntry aSHEntry,
                               in boolean firstParty,
                               out nsIDocShell aDocShell,
                               out nsIRequest aRequest);
 
--- a/docshell/base/nsILinkHandler.h
+++ b/docshell/base/nsILinkHandler.h
@@ -11,62 +11,66 @@
 class nsIInputStream;
 class nsIDocShell;
 class nsIRequest;
 class nsString;
 class nsGUIEvent;
 
 // Interface ID for nsILinkHandler
 #define NS_ILINKHANDLER_IID \
-  { 0xd85670a1, 0x224a, 0x4562, \
-    { 0x87, 0xa9, 0x43, 0xa5, 0x24, 0xe7, 0xd0, 0x1b } }
+  { 0xceb9aade, 0x43da, 0x4f1a, \
+    { 0xac, 0x8a, 0xc7, 0x09, 0xfb, 0x22, 0x46, 0x64 } }
 
 /**
  * Interface used for handling clicks on links
  */
 class nsILinkHandler : public nsISupports {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ILINKHANDLER_IID)
 
   /**
    * Process a click on a link.
    *
    * @param aContent the content for the frame that generated the trigger
    * @param aURI a URI object that defines the destination for the link
    * @param aTargetSpec indicates where the link is targeted (may be an empty
    *        string)
    * @param aPostDataStream the POST data to send
+   * @param aFileName non-null when the link should be downloaded as the given file
    * @param aHeadersDataStream ???
    * @param aIsTrusted false if the triggerer is an untrusted DOM event.
    */
-  NS_IMETHOD OnLinkClick(nsIContent* aContent, 
+  NS_IMETHOD OnLinkClick(nsIContent* aContent,
                          nsIURI* aURI,
                          const PRUnichar* aTargetSpec,
+                         const nsAString& aFileName,
                          nsIInputStream* aPostDataStream,
                          nsIInputStream* aHeadersDataStream,
                          bool aIsTrusted) = 0;
 
   /**
    * Process a click on a link.
    *
    * Works the same as OnLinkClick() except it happens immediately rather than
    * through an event.
    *
    * @param aContent the content for the frame that generated the trigger
    * @param aURI a URI obect that defines the destination for the link
    * @param aTargetSpec indicates where the link is targeted (may be an empty
    *        string)
+   * @param aFileName non-null when the link should be downloaded as the given file
    * @param aPostDataStream the POST data to send
    * @param aHeadersDataStream ???
    * @param aDocShell (out-param) the DocShell that the request was opened on
    * @param aRequest the request that was opened
    */
-  NS_IMETHOD OnLinkClickSync(nsIContent* aContent, 
+  NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
                              nsIURI* aURI,
                              const PRUnichar* aTargetSpec,
+                             const nsAString& aFileName,
                              nsIInputStream* aPostDataStream = 0,
                              nsIInputStream* aHeadersDataStream = 0,
                              nsIDocShell** aDocShell = 0,
                              nsIRequest** aRequest = 0) = 0;
 
   /**
    * Process a mouse-over a link.
    *
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -77,16 +77,17 @@ PARALLEL_DIRS += \
   audiochannel \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 PARALLEL_DIRS += \
   telephony \
   wifi \
   icc \
+  cellbroadcast \
   $(NULL)
 endif
 
 ifdef MOZ_B2G_FM
 PARALLEL_DIRS += fm
 endif
 
 ifdef MOZ_PAY
--- a/dom/activities/src/Activity.cpp
+++ b/dom/activities/src/Activity.cpp
@@ -21,27 +21,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
 #ifdef MOZ_SYS_MSG
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozActivity)
 #endif
 NS_INTERFACE_MAP_END_INHERITING(DOMRequest)
 
 NS_IMPL_ADDREF_INHERITED(Activity, DOMRequest)
 NS_IMPL_RELEASE_INHERITED(Activity, DOMRequest)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(Activity)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Activity,
-                                                  DOMRequest)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProxy)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Activity,
-                                                DOMRequest)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mProxy)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(Activity, DOMRequest,
+                                     mProxy)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Activity, DOMRequest)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMETHODIMP
 Activity::Initialize(nsISupports* aOwner,
                      JSContext* aContext,
                      JSObject* aObject,
--- a/dom/apps/src/PermissionsInstaller.jsm
+++ b/dom/apps/src/PermissionsInstaller.jsm
@@ -228,16 +228,21 @@ this.PermissionsTable =  { geolocation: 
                                "pin-app"
                              ]
                            },
                            "background-sensors": {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
+                           cellbroadcast: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
                          };
 
 /**
  * Expand an access string into multiple permission names,
  *   e.g: perm 'contacts' with 'readwrite' =
  *   ['contacts-read', 'contacts-create', contacts-write']
  * @param string aPermName
  * @param string aAccess
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -460,20 +460,30 @@ WebappsApplication.prototype = {
 
   cancelDownload: function() {
     cpmm.sendAsyncMessage("Webapps:CancelDownload",
                           { manifestURL: this.manifestURL });
   },
 
   checkForUpdate: function() {
     let request = this.createRequest();
-    cpmm.sendAsyncMessage("Webapps:CheckForUpdate",
-                          { manifestURL: this.manifestURL,
-                            oid: this._id,
-                            requestID: this.getRequestId(request) });
+
+    // We can't update apps that are not removable.
+    if (!this.removable) {
+      Services.tm.currentThread.dispatch({
+        run: function checkUpdateFail() {
+          Services.DOMRequest.fireError(request, "NOT_UPDATABLE");
+        }
+      }, Ci.nsIEventTarget.DISPATCH_NORMAL)
+    } else {
+      cpmm.sendAsyncMessage("Webapps:CheckForUpdate",
+                            { manifestURL: this.manifestURL,
+                              oid: this._id,
+                              requestID: this.getRequestId(request) });
+    }
     return request;
   },
 
   launch: function(aStartPoint) {
     let request = this.createRequest();
     cpmm.sendAsyncMessage("Webapps:Launch", { origin: this.origin,
                                               manifestURL: this.manifestURL,
                                               startPoint: aStartPoint || "",
@@ -729,18 +739,22 @@ WebappsApplicationMgmt.prototype = {
           let app = msg.app;
           let event = new this._window.MozApplicationEvent("applicationinstall",
                            { application : createApplicationObject(this._window, app) });
           this._oninstall.handleEvent(event);
         }
         break;
       case "Webapps:Uninstall:Return:OK":
         if (this._onuninstall) {
+          let detail = {
+            manifestURL: msg.manifestURL,
+            origin: msg.origin
+          };
           let event = new this._window.MozApplicationEvent("applicationuninstall",
-                           { application : createApplicationObject(this._window, { origin: msg.origin }) });
+                           { application : createApplicationObject(this._window, detail) });
           this._onuninstall.handleEvent(event);
         }
         break;
     }
     this.removeRequest(msg.requestID);
   },
 
   classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -800,16 +800,18 @@ this.DOMApplicationRegistry = {
         let manifest = new ManifestHelper(jsonManifest, app.origin);
 
         if (manifest.appcache_path) {
           debug("appcache found");
           this.startOfflineCacheDownload(manifest, app, null, null, isUpdate);
         } else {
           // hosted app with no appcache, nothing to do, but we fire a
           // downloaded event
+          debug("No appcache found, sending 'downloaded' for " + aManifestURL);
+          app.downloadAvailable = false;
           DOMApplicationRegistry.broadcastMessage("Webapps:PackageEvent",
                                                   { type: "downloaded",
                                                     manifestURL: aManifestURL,
                                                     app: app,
                                                     manifest: jsonManifest });
         }
       }).bind(this));
 
@@ -974,17 +976,17 @@ this.DOMApplicationRegistry = {
         downloadSize: manifest.size
       }
       DOMApplicationRegistry._saveApps(function() {
         aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
       });
     }
 
     function updateHostedApp(aManifest) {
-      debug("updateHostedApp");
+      debug("updateHostedApp " + aData.manifestURL);
       let id = this._appId(app.origin);
 
       if (id in this._manifestCache) {
         delete this._manifestCache[id];
       }
 
       // Update the web apps' registration.
       this.notifyAppsRegistryStart();
@@ -1002,65 +1004,56 @@ this.DOMApplicationRegistry = {
       // Store the new manifest.
       let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
       let manFile = dir.clone();
       manFile.append("manifest.webapp");
       this._writeFile(manFile, JSON.stringify(aManifest), function() { });
 
       let manifest = new ManifestHelper(aManifest, app.origin);
 
-      if (manifest.appcache_path) {
-        app.installState = "updating";
-        app.downloadAvailable = true;
-        app.downloading = true;
-        app.downloadsize = 0;
-        app.readyToApplyDownload = false;
-      } else {
-        app.installState = "installed";
-        app.downloadAvailable = false;
-        app.downloading = false;
-        app.readyToApplyDownload = false;
-      }
+      app.installState = "installed";
+      app.downloading = false;
+      app.downloadsize = 0;
+      app.readyToApplyDownload = false;
+      app.downloadAvailable = !!manifest.appcache_path;
 
       app.name = aManifest.name;
       app.csp = aManifest.csp || "";
       app.updateTime = Date.now();
 
       // Update the registry.
       this.webapps[id] = app;
 
       this._saveApps(function() {
-        aData.event = "downloadapplied";
+        aData.app = app;
+        aData.event = manifest.appcache_path ? "downloadavailable"
+                                             : "downloadapplied";
         aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
       });
 
-      // Preload the appcache if needed.
-      this.startOfflineCacheDownload(manifest, app);
-
       // Update the permissions for this app.
       PermissionsInstaller.installPermissions({ manifest: aManifest,
                                                 origin: app.origin,
                                                 manifestURL: aData.manifestURL },
                                                 true);
     }
 
     // First, we download the manifest.
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
                 .createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("GET", aData.manifestURL, true);
+    xhr.responseType = "json";
     if (app.etag) {
       xhr.setRequestHeader("If-None-Match", app.etag);
     }
 
     xhr.addEventListener("load", (function() {
       if (xhr.status == 200) {
-        let manifest;
-        try {
-          manifest = JSON.parse(xhr.responseText);
-        } catch(e) {
+        let manifest = xhr.response;
+        if (manifest == null) {
           sendError("MANIFEST_PARSE_ERROR");
           return;
         }
         if (!AppsUtils.checkManifest(manifest)) {
           sendError("INVALID_MANIFEST");
         } else if (!AppsUtils.checkInstallAllowed(manifest, app.installOrigin)) {
           sendError("INSTALL_FROM_DENIED");
         } else {
@@ -1540,17 +1533,17 @@ this.DOMApplicationRegistry = {
 
   uninstall: function(aData, aMm) {
     for (let id in this.webapps) {
       let app = this.webapps[id];
       if (app.origin != aData.origin) {
         continue;
       }
 
-      if (!this.webapps[id].removable)
+      if (!app.removable)
         return;
 
       // Clean up the deprecated manifest cache if needed.
       if (id in this._manifestCache) {
         delete this._manifestCache[id];
       }
 
       // Clear private data first.
@@ -1571,16 +1564,17 @@ this.DOMApplicationRegistry = {
       let dir = this._getAppDir(id);
       try {
         dir.remove(true);
       } catch (e) {}
 
       delete this.webapps[id];
 
       this._saveApps((function() {
+        aData.manifestURL = app.manifestURL;
         this.broadcastMessage("Webapps:Uninstall:Return:OK", aData);
         Services.obs.notifyObservers(this, "webapps-sync-uninstall", appNote);
         this.broadcastMessage("Webapps:RemoveApp", { id: id });
       }).bind(this));
 
       return;
     }
 
@@ -1770,23 +1764,25 @@ this.DOMApplicationRegistry = {
           continue;
 
         // Clean up the deprecated manifest cache if needed.
         if (record.id in this._manifestCache) {
           delete this._manifestCache[record.id];
         }
 
         let origin = this.webapps[record.id].origin;
+        let manifestURL = this.webapps[record.id].manifestURL;
         delete this.webapps[record.id];
         let dir = this._getAppDir(record.id);
         try {
           dir.remove(true);
         } catch (e) {
         }
-        this.broadcastMessage("Webapps:Uninstall:Return:OK", { origin: origin });
+        this.broadcastMessage("Webapps:Uninstall:Return:OK", { origin: origin,
+                                                               manifestURL: manifestURL });
       } else {
         if (this.webapps[record.id]) {
           this.webapps[record.id] = record.value;
           delete this.webapps[record.id].manifest;
         } else {
           let data = { app: record.value };
           this.confirmInstall(data, true);
           this.broadcastMessage("Webapps:Install:Return:OK", data);
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -34,16 +34,17 @@
 #include "mozilla/Hal.h"
 #include "nsIWebNavigation.h"
 #include "nsISiteSpecificUserAgent.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "Connection.h"
 #ifdef MOZ_B2G_RIL
 #include "MobileConnection.h"
+#include "mozilla/dom/CellBroadcast.h"
 #endif
 #include "nsIIdleObserver.h"
 #include "nsIPermissionManager.h"
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
 #include "TimeManager.h"
 
 #ifdef MOZ_MEDIA_NAVIGATOR
@@ -122,16 +123,17 @@ NS_INTERFACE_MAP_BEGIN(Navigator)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorUserMedia)
 #endif
 #ifdef MOZ_B2G_RIL
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorNetwork)
 #ifdef MOZ_B2G_RIL
   NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorMobileConnection)
+  NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorCellBroadcast)
 #endif
 #ifdef MOZ_B2G_BT
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorBluetooth)
 #endif
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorCamera)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorSystemMessages)
 #ifdef MOZ_TIME_MANAGER
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorTime)
@@ -196,16 +198,20 @@ Navigator::Invalidate()
     mConnection = nullptr;
   }
 
 #ifdef MOZ_B2G_RIL
   if (mMobileConnection) {
     mMobileConnection->Shutdown();
     mMobileConnection = nullptr;
   }
+
+  if (mCellBroadcast) {
+    mCellBroadcast = nullptr;
+  }
 #endif
 
 #ifdef MOZ_B2G_BT
   if (mBluetooth) {
     mBluetooth = nullptr;
   }
 #endif
 
@@ -1162,16 +1168,41 @@ Navigator::GetMozSms(nsIDOMMozSmsManager
   NS_ADDREF(*aSmsManager = mSmsManager);
 
   return NS_OK;
 }
 
 #ifdef MOZ_B2G_RIL
 
 //*****************************************************************************
+//    Navigator::nsIMozNavigatorCellBroadcast
+//*****************************************************************************
+
+NS_IMETHODIMP
+Navigator::GetMozCellBroadcast(nsIDOMMozCellBroadcast** aCellBroadcast)
+{
+  *aCellBroadcast = nullptr;
+
+  if (!mCellBroadcast) {
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+    NS_ENSURE_TRUE(window, NS_OK);
+
+    if (!CheckPermission("cellbroadcast")) {
+      return NS_OK;
+    }
+
+    nsresult rv = NS_NewCellBroadcast(window, getter_AddRefs(mCellBroadcast));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  NS_ADDREF(*aCellBroadcast = mCellBroadcast);
+  return NS_OK;
+}
+
+//*****************************************************************************
 //    nsNavigator::nsIDOMNavigatorTelephony
 //*****************************************************************************
 
 NS_IMETHODIMP
 Navigator::GetMozTelephony(nsIDOMTelephony** aTelephony)
 {
   nsCOMPtr<nsIDOMTelephony> telephony = mTelephony;
 
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -15,16 +15,17 @@
 #include "nsINavigatorBattery.h"
 #include "nsIDOMNavigatorSms.h"
 #include "nsIDOMNavigatorNetwork.h"
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
 #include "nsINavigatorAudioChannelManager.h"
 #endif
 #ifdef MOZ_B2G_RIL
 #include "nsINavigatorMobileConnection.h"
+#include "nsINavigatorCellBroadcast.h"
 #endif
 #include "nsAutoPtr.h"
 #include "nsIDOMNavigatorTime.h"
 #include "nsWeakReference.h"
 #include "DeviceStorage.h"
 
 class nsPluginArray;
 class nsMimeTypeArray;
@@ -100,16 +101,17 @@ class Navigator : public nsIDOMNavigator
                 , public nsIDOMNavigatorUserMedia
 #endif
 #ifdef MOZ_B2G_RIL
                 , public nsIDOMNavigatorTelephony
 #endif
                 , public nsIDOMMozNavigatorNetwork
 #ifdef MOZ_B2G_RIL
                 , public nsIMozNavigatorMobileConnection
+                , public nsIMozNavigatorCellBroadcast
 #endif
 #ifdef MOZ_B2G_BT
                 , public nsIDOMNavigatorBluetooth
 #endif
                 , public nsIDOMNavigatorCamera
                 , public nsIDOMNavigatorSystemMessages
 #ifdef MOZ_TIME_MANAGER
                 , public nsIDOMMozNavigatorTime
@@ -135,16 +137,17 @@ public:
   NS_DECL_NSIDOMNAVIGATORUSERMEDIA
 #endif
 #ifdef MOZ_B2G_RIL
   NS_DECL_NSIDOMNAVIGATORTELEPHONY
 #endif
   NS_DECL_NSIDOMMOZNAVIGATORNETWORK
 #ifdef MOZ_B2G_RIL
   NS_DECL_NSIMOZNAVIGATORMOBILECONNECTION
+  NS_DECL_NSIMOZNAVIGATORCELLBROADCAST
 #endif
 
 #ifdef MOZ_B2G_BT
   NS_DECL_NSIDOMNAVIGATORBLUETOOTH
 #endif
   NS_DECL_NSIDOMNAVIGATORSYSTEMMESSAGES
 #ifdef MOZ_TIME_MANAGER
   NS_DECL_NSIDOMMOZNAVIGATORTIME
@@ -192,16 +195,17 @@ private:
   nsRefPtr<sms::SmsManager> mSmsManager;
 #ifdef MOZ_B2G_RIL
   nsCOMPtr<nsIDOMTelephony> mTelephony;
   nsCOMPtr<nsIDOMMozVoicemail> mVoicemail;
 #endif
   nsRefPtr<network::Connection> mConnection;
 #ifdef MOZ_B2G_RIL
   nsRefPtr<network::MobileConnection> mMobileConnection;
+  nsCOMPtr<nsIDOMMozCellBroadcast> mCellBroadcast;
 #endif
 #ifdef MOZ_B2G_BT
   nsCOMPtr<nsIDOMBluetoothManager> mBluetooth;
 #endif
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   nsRefPtr<system::AudioChannelManager> mAudioChannelManager;
 #endif
   nsRefPtr<nsDOMCameraManager> mCameraManager;
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -499,17 +499,19 @@ using mozilla::dom::indexedDB::IDBWrappe
 #ifdef MOZ_B2G_RIL
 #include "Telephony.h"
 #include "TelephonyCall.h"
 #include "CallEvent.h"
 #include "nsIDOMVoicemail.h"
 #include "nsIDOMVoicemailEvent.h"
 #include "nsIDOMIccManager.h"
 #include "StkCommandEvent.h"
-#endif
+#include "nsIDOMMozCellBroadcast.h"
+#include "nsIDOMMozCellBroadcastEvent.h"
+#endif // MOZ_B2G_RIL
 
 #ifdef MOZ_B2G_FM
 #include "FMRadio.h"
 #endif
 
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #include "BluetoothAdapter.h"
@@ -1500,16 +1502,19 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozConnection, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
 #ifdef MOZ_B2G_RIL
   NS_DEFINE_CLASSINFO_DATA(MozMobileConnection, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(MozCellBroadcast, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(USSDReceivedEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DataErrorEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
@@ -1703,71 +1708,62 @@ static nsDOMClassInfoData sClassInfoData
 #endif
 
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   NS_DEFINE_CLASSINFO_DATA(AudioChannelManager, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 #endif
 };
 
-// Objects that should be constructable through |new Name();|
-struct nsContractIDMapData
-{
-  int32_t mDOMClassInfoID;
-  const char *mContractID;
-};
-
 #define NS_DEFINE_CONTRACT_CTOR(_class, _contract_id)                           \
-  nsresult                                                                      \
+  static nsresult                                                               \
   _class##Ctor(nsISupports** aInstancePtrResult)                                \
   {                                                                             \
     nsresult rv = NS_OK;                                                        \
     nsCOMPtr<nsISupports> native = do_CreateInstance(_contract_id, &rv);        \
     native.forget(aInstancePtrResult);                                          \
     return rv;                                                                  \
   }
 
-NS_DEFINE_CONTRACT_CTOR(DOMParser, NS_DOMPARSER_CONTRACTID)
 NS_DEFINE_CONTRACT_CTOR(FileReader, NS_FILEREADER_CONTRACTID)
 NS_DEFINE_CONTRACT_CTOR(ArchiveReader, NS_ARCHIVEREADER_CONTRACTID)
 NS_DEFINE_CONTRACT_CTOR(FormData, NS_FORMDATA_CONTRACTID)
-NS_DEFINE_CONTRACT_CTOR(XMLSerializer, NS_XMLSERIALIZER_CONTRACTID)
 NS_DEFINE_CONTRACT_CTOR(XPathEvaluator, NS_XPATH_EVALUATOR_CONTRACTID)
 NS_DEFINE_CONTRACT_CTOR(XSLTProcessor,
                         "@mozilla.org/document-transformer;1?type=xslt")
 NS_DEFINE_CONTRACT_CTOR(EventSource, NS_EVENTSOURCE_CONTRACTID)
 NS_DEFINE_CONTRACT_CTOR(MutationObserver, NS_DOMMUTATIONOBSERVER_CONTRACTID)
 #ifdef MOZ_SYS_MSG
 NS_DEFINE_CONTRACT_CTOR(MozActivity, NS_DOMACTIVITY_CONTRACTID)
 #endif
 
 #undef NS_DEFINE_CONTRACT_CTOR
 
 #define NS_DEFINE_EVENT_CTOR(_class)                        \
-  nsresult                                                  \
+  static nsresult                                           \
   NS_DOM##_class##Ctor(nsISupports** aInstancePtrResult)    \
   {                                                         \
-    nsIDOMEvent* e = nullptr;                                \
-    nsresult rv = NS_NewDOM##_class(&e, nullptr, nullptr);    \
+    nsIDOMEvent* e = nullptr;                               \
+    nsresult rv = NS_NewDOM##_class(&e, nullptr, nullptr);  \
     *aInstancePtrResult = e;                                \
     return rv;                                              \
   }
 
 NS_DEFINE_EVENT_CTOR(Event)
 NS_DEFINE_EVENT_CTOR(UIEvent)
 NS_DEFINE_EVENT_CTOR(MouseEvent)
 NS_DEFINE_EVENT_CTOR(WheelEvent)
 
 #define MOZ_GENERATED_EVENT_LIST
 #define MOZ_GENERATED_EVENT(_event_interface) \
   NS_DEFINE_EVENT_CTOR(_event_interface)
 #include "GeneratedEvents.h"
 #undef MOZ_GENERATED_EVENT_LIST
 
-nsresult
+static nsresult
 NS_XMLHttpRequestCtor(nsISupports** aInstancePtrResult)
 {
   nsXMLHttpRequest* xhr = new nsXMLHttpRequest();
   return CallQueryInterface(xhr, aInstancePtrResult);
 }
 
 struct nsConstructorFuncMapData
 {
@@ -1792,31 +1788,32 @@ static const nsConstructorFuncMapData kC
 #ifdef MOZ_B2G_RIL
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozWifiStatusChangeEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozWifiConnectionInfoEvent)
 #endif
 #define MOZ_GENERATED_EVENT_LIST
 #define MOZ_GENERATED_EVENT(_event_interface) \
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(_event_interface)
 #include "GeneratedEvents.h"
+#undef MOZ_GENERATED_EVENT_LIST
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozSmsFilter, sms::SmsFilter::NewSmsFilter)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(XMLHttpRequest, NS_XMLHttpRequestCtor)
-  NS_DEFINE_CONSTRUCTOR_FUNC_DATA(DOMParser, DOMParserCtor)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(FileReader, FileReaderCtor)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(ArchiveReader, ArchiveReaderCtor)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(FormData, FormDataCtor)
-  NS_DEFINE_CONSTRUCTOR_FUNC_DATA(XMLSerializer, XMLSerializerCtor)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(XPathEvaluator, XPathEvaluatorCtor)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(XSLTProcessor, XSLTProcessorCtor)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(EventSource, EventSourceCtor)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MutationObserver, MutationObserverCtor)
 #ifdef MOZ_SYS_MSG
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozActivity, MozActivityCtor)
 #endif
 };
+#undef NS_DEFINE_CONSTRUCTOR_FUNC_DATA
+#undef NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA
 
 nsIXPConnect *nsDOMClassInfo::sXPConnect = nullptr;
 nsIScriptSecurityManager *nsDOMClassInfo::sSecMan = nullptr;
 bool nsDOMClassInfo::sIsInitialized = false;
 bool nsDOMClassInfo::sDisableDocumentAllSupport = false;
 bool nsDOMClassInfo::sDisableGlobalScopePollutionSupport = false;
 
 
@@ -1852,26 +1849,21 @@ jsid nsDOMClassInfo::sEnumerate_id      
 jsid nsDOMClassInfo::sNavigator_id       = JSID_VOID;
 jsid nsDOMClassInfo::sTop_id             = JSID_VOID;
 jsid nsDOMClassInfo::sDocument_id        = JSID_VOID;
 jsid nsDOMClassInfo::sFrames_id          = JSID_VOID;
 jsid nsDOMClassInfo::sSelf_id            = JSID_VOID;
 jsid nsDOMClassInfo::sOpener_id          = JSID_VOID;
 jsid nsDOMClassInfo::sAll_id             = JSID_VOID;
 jsid nsDOMClassInfo::sTags_id            = JSID_VOID;
-jsid nsDOMClassInfo::sAddEventListener_id= JSID_VOID;
 jsid nsDOMClassInfo::sBaseURIObject_id   = JSID_VOID;
 jsid nsDOMClassInfo::sNodePrincipal_id   = JSID_VOID;
 jsid nsDOMClassInfo::sDocumentURIObject_id=JSID_VOID;
 jsid nsDOMClassInfo::sWrappedJSObject_id = JSID_VOID;
 jsid nsDOMClassInfo::sURL_id             = JSID_VOID;
-jsid nsDOMClassInfo::sKeyPath_id         = JSID_VOID;
-jsid nsDOMClassInfo::sAutoIncrement_id   = JSID_VOID;
-jsid nsDOMClassInfo::sUnique_id          = JSID_VOID;
-jsid nsDOMClassInfo::sMultiEntry_id      = JSID_VOID;
 jsid nsDOMClassInfo::sOnload_id          = JSID_VOID;
 jsid nsDOMClassInfo::sOnerror_id         = JSID_VOID;
 
 static const JSClass *sObjectClass = nullptr;
 
 /**
  * Set our JSClass pointer for the Object class
  */
@@ -2129,26 +2121,21 @@ nsDOMClassInfo::DefineStaticJSVals(JSCon
   SET_JSID_TO_STRING(sNavigator_id,       cx, "navigator");
   SET_JSID_TO_STRING(sTop_id,             cx, "top");
   SET_JSID_TO_STRING(sDocument_id,        cx, "document");
   SET_JSID_TO_STRING(sFrames_id,          cx, "frames");
   SET_JSID_TO_STRING(sSelf_id,            cx, "self");
   SET_JSID_TO_STRING(sOpener_id,          cx, "opener");
   SET_JSID_TO_STRING(sAll_id,             cx, "all");
   SET_JSID_TO_STRING(sTags_id,            cx, "tags");
-  SET_JSID_TO_STRING(sAddEventListener_id,cx, "addEventListener");
   SET_JSID_TO_STRING(sBaseURIObject_id,   cx, "baseURIObject");
   SET_JSID_TO_STRING(sNodePrincipal_id,   cx, "nodePrincipal");
   SET_JSID_TO_STRING(sDocumentURIObject_id,cx,"documentURIObject");
   SET_JSID_TO_STRING(sWrappedJSObject_id, cx, "wrappedJSObject");
   SET_JSID_TO_STRING(sURL_id,             cx, "URL");
-  SET_JSID_TO_STRING(sKeyPath_id,         cx, "keyPath");
-  SET_JSID_TO_STRING(sAutoIncrement_id,   cx, "autoIncrement");
-  SET_JSID_TO_STRING(sUnique_id,          cx, "unique");
-  SET_JSID_TO_STRING(sMultiEntry_id,      cx, "multiEntry");
   SET_JSID_TO_STRING(sOnload_id,          cx, "onload");
   SET_JSID_TO_STRING(sOnerror_id,         cx, "onerror");
 
   return NS_OK;
 }
 
 // static
 bool
@@ -2482,16 +2469,17 @@ nsDOMClassInfo::Init()
 #endif
 #ifdef MOZ_B2G_RIL
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMMozNavigatorNetwork,
                                         network::IsAPIEnabled())
 #ifdef MOZ_B2G_RIL
     DOM_CLASSINFO_MAP_ENTRY(nsIMozNavigatorMobileConnection)
+    DOM_CLASSINFO_MAP_ENTRY(nsIMozNavigatorCellBroadcast)
 #endif
 #ifdef MOZ_B2G_BT
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorBluetooth)
 #endif
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorCamera)
 #ifdef MOZ_SYS_MSG
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorSystemMessages)
 #endif
@@ -3942,17 +3930,16 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN(StorageItem, nsIDOMStorageItem)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageItem)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMToString)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(DOMParser, nsIDOMParser)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMParser)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMParserJS)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XMLSerializer, nsIDOMSerializer)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSerializer)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(XMLHttpRequest, nsIXMLHttpRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequest)
@@ -4113,17 +4100,27 @@ nsDOMClassInfo::Init()
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
 #ifdef MOZ_B2G_RIL
   DOM_CLASSINFO_MAP_BEGIN(MozMobileConnection, nsIDOMMozMobileConnection)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozMobileConnection)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
-#endif
+
+  DOM_CLASSINFO_MAP_BEGIN(MozCellBroadcast, nsIDOMMozCellBroadcast)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCellBroadcast)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN(MozCellBroadcastEvent, nsIDOMMozCellBroadcastEvent)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCellBroadcastEvent)
+     DOM_CLASSINFO_EVENT_MAP_ENTRIES
+  DOM_CLASSINFO_MAP_END
+#endif // MOZ_B2G_RIL
 
   DOM_CLASSINFO_MAP_BEGIN(USSDReceivedEvent, nsIDOMUSSDReceivedEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMUSSDReceivedEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEvent)
   DOM_CLASSINFO_MAP_END
  
   DOM_CLASSINFO_MAP_BEGIN(DataErrorEvent, nsIDOMDataErrorEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataErrorEvent)
@@ -5222,25 +5219,20 @@ nsDOMClassInfo::ShutDown()
   sNavigator_id       = JSID_VOID;
   sTop_id             = JSID_VOID;
   sDocument_id        = JSID_VOID;
   sFrames_id          = JSID_VOID;
   sSelf_id            = JSID_VOID;
   sOpener_id          = JSID_VOID;
   sAll_id             = JSID_VOID;
   sTags_id            = JSID_VOID;
-  sAddEventListener_id= JSID_VOID;
   sBaseURIObject_id   = JSID_VOID;
   sNodePrincipal_id   = JSID_VOID;
   sDocumentURIObject_id=JSID_VOID;
   sWrappedJSObject_id = JSID_VOID;
-  sKeyPath_id         = JSID_VOID;
-  sAutoIncrement_id   = JSID_VOID;
-  sUnique_id          = JSID_VOID;
-  sMultiEntry_id      = JSID_VOID;
   sOnload_id          = JSID_VOID;
   sOnerror_id         = JSID_VOID;
 
   NS_IF_RELEASE(sXPConnect);
   NS_IF_RELEASE(sSecMan);
   sIsInitialized = false;
 }
 
@@ -7662,20 +7654,18 @@ nsNavigatorSH::NewResolve(nsIXPConnectWr
   }
 
   nsScriptNameSpaceManager *nameSpaceManager =
     nsJSRuntime::GetNameSpaceManager();
   NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
 
   nsDependentJSString name(id);
 
-  const nsGlobalNameStruct *name_struct = nullptr;
-
-  nameSpaceManager->LookupNavigatorName(name, &name_struct);
-
+  const nsGlobalNameStruct* name_struct =
+    nameSpaceManager->LookupNavigatorName(name);
   if (!name_struct) {
     return NS_OK;
   }
   NS_ASSERTION(name_struct->mType == nsGlobalNameStruct::eTypeNavigatorProperty,
                "unexpected type");
 
   nsresult rv = NS_OK;
 
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -252,28 +252,23 @@ public:
   static jsid sNavigator_id;
   static jsid sTop_id;
   static jsid sDocument_id;
   static jsid sFrames_id;
   static jsid sSelf_id;
   static jsid sOpener_id;
   static jsid sAll_id;
   static jsid sTags_id;
-  static jsid sAddEventListener_id;
   static jsid sBaseURIObject_id;
   static jsid sNodePrincipal_id;
   static jsid sDocumentURIObject_id;
   static jsid sJava_id;
   static jsid sPackages_id;
   static jsid sWrappedJSObject_id;
   static jsid sURL_id;
-  static jsid sKeyPath_id;
-  static jsid sAutoIncrement_id;
-  static jsid sUnique_id;
-  static jsid sMultiEntry_id;
   static jsid sOnload_id;
   static jsid sOnerror_id;
 
 protected:
   static JSPropertyOp sXPCNativeWrapperGetPropertyOp;
   static JSPropertyOp sXrayWrapperPropertyHolderGetPropertyOp;
 };
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -397,16 +397,17 @@ DOMCI_CLASS(MozSmsMessage)
 DOMCI_CLASS(MozSmsEvent)
 DOMCI_CLASS(MozSmsRequest)
 DOMCI_CLASS(MozSmsFilter)
 DOMCI_CLASS(MozSmsCursor)
 
 DOMCI_CLASS(MozConnection)
 #ifdef MOZ_B2G_RIL
 DOMCI_CLASS(MozMobileConnection)
+DOMCI_CLASS(MozCellBroadcast)
 #endif
 
 DOMCI_CLASS(USSDReceivedEvent)
 
 DOMCI_CLASS(DataErrorEvent)
 
 // @font-face in CSS
 DOMCI_CLASS(CSSFontFaceRule)
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -128,33 +128,23 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsFocusManager)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFocusManager)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveWindow)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedWindow)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedContent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBlurEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstFocusEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowBeingLowered)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFocusManager)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveWindow)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWindow)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedContent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBlurEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstFocusEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowBeingLowered)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_6(nsFocusManager,
+                           mActiveWindow,
+                           mFocusedWindow,
+                           mFocusedContent,
+                           mFirstBlurEvent,
+                           mFirstFocusEvent,
+                           mWindowBeingLowered)
 
 nsFocusManager* nsFocusManager::sInstance = nullptr;
 bool nsFocusManager::sMouseFocusesFormControl = false;
 bool nsFocusManager::sTestMode = false;
 
 static const char* kObservedPrefs[] = {
   "accessibility.browsewithcaret",
   "accessibility.tabfocus_applies_to_xul",
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11087,39 +11087,31 @@ nsGlobalChromeWindow::GetMessageManager(
   }
   CallQueryInterface(mMessageManager, aManager);
   return NS_OK;
 }
 
 // nsGlobalModalWindow implementation
 
 // QueryInterface implementation for nsGlobalModalWindow
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalModalWindow)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalModalWindow,
-                                                  nsGlobalWindow)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsGlobalModalWindow,
+                                     nsGlobalWindow,
+                                     mReturnValue)
 
 DOMCI_DATA(ModalContentWindow, nsGlobalModalWindow)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalModalWindow)
   NS_INTERFACE_MAP_ENTRY(nsIDOMModalContentWindow)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow)
 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
 
 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGlobalModalWindow,
-                                                nsGlobalWindow)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-
 NS_IMETHODIMP
 nsGlobalModalWindow::GetDialogArguments(nsIArray **aArguments)
 {
   FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
                                         NS_ERROR_NOT_INITIALIZED);
 
   bool subsumes = false;
   nsIPrincipal *self = GetPrincipal();
--- a/dom/base/nsScreen.cpp
+++ b/dom/base/nsScreen.cpp
@@ -58,16 +58,17 @@ nsScreen::Create(nsPIDOMWindow* aWindow)
   screen->mOrientation = config.orientation();
 
   return screen.forget();
 }
 
 nsScreen::nsScreen()
   : mEventListener(nullptr)
 {
+  SetIsDOMBinding();
 }
 
 void
 nsScreen::Reset()
 {
   hal::UnlockScreenOrientation();
 
   if (mEventListener) {
@@ -85,25 +86,17 @@ nsScreen::~nsScreen()
 {
   Reset();
   hal::UnregisterScreenConfigurationObserver(this);
 }
 
 
 DOMCI_DATA(Screen, nsScreen)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsScreen)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsScreen,
-                                                  nsDOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsScreen,
-                                                nsDOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_0(nsScreen, nsDOMEventTargetHelper)
 
 // QueryInterface implementation for nsScreen
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsScreen)
   NS_INTERFACE_MAP_ENTRY(nsIDOMScreen)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMScreen)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Screen)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -494,33 +494,30 @@ nsScriptNameSpaceManager::LookupNameInte
   }
 
   if (aClassName) {
     *aClassName = nullptr;
   }
   return nullptr;
 }
 
-nsresult
-nsScriptNameSpaceManager::LookupNavigatorName(const nsAString& aName,
-                                              const nsGlobalNameStruct **aNameStruct)
+const nsGlobalNameStruct*
+nsScriptNameSpaceManager::LookupNavigatorName(const nsAString& aName)
 {
   GlobalNameMapEntry *entry =
     static_cast<GlobalNameMapEntry *>
                (PL_DHashTableOperate(&mNavigatorNames, &aName,
                                      PL_DHASH_LOOKUP));
 
-  if (PL_DHASH_ENTRY_IS_BUSY(entry) &&
-      !((&entry->mGlobalName)->mDisabled)) {
-    *aNameStruct = &entry->mGlobalName;
-  } else {
-    *aNameStruct = nullptr;
+  if (!PL_DHASH_ENTRY_IS_BUSY(entry) ||
+      entry->mGlobalName.mDisabled) {
+    return nullptr;
   }
 
-  return NS_OK;
+  return &entry->mGlobalName;
 }
 
 nsresult
 nsScriptNameSpaceManager::RegisterClassName(const char *aClassName,
                                             int32_t aDOMClassInfoID,
                                             bool aPrivileged,
                                             bool aDisabled,
                                             const PRUnichar **aResult)
--- a/dom/base/nsScriptNameSpaceManager.h
+++ b/dom/base/nsScriptNameSpaceManager.h
@@ -104,18 +104,17 @@ public:
   {
     return LookupNameInternal(aName, aClassName);
   }
 
   // Returns a nsGlobalNameStruct for the navigator property aName, or
   // null if one is not found. The returned nsGlobalNameStruct is only
   // guaranteed to be valid until the next call to any of the methods
   // in this class.
-  nsresult LookupNavigatorName(const nsAString& aName,
-                               const nsGlobalNameStruct **aNameStruct);
+  const nsGlobalNameStruct* LookupNavigatorName(const nsAString& aName);
 
   nsresult RegisterClassName(const char *aClassName,
                              int32_t aDOMClassInfoID,
                              bool aPrivileged,
                              bool aDisabled,
                              const PRUnichar **aResult);
 
   nsresult RegisterClassProto(const char *aClassName,
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -34,29 +34,20 @@ nsWindowRoot::nsWindowRoot(nsPIDOMWindow
 
 nsWindowRoot::~nsWindowRoot()
 {
   if (mListenerManager) {
     mListenerManager->Disconnect();
   }
 }
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsWindowRoot)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsWindowRoot)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPopupNode)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsWindowRoot)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPopupNode)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_3(nsWindowRoot,
+                           mListenerManager,
+                           mPopupNode,
+                           mParent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsWindowRoot)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsPIWindowRoot)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsWindowRoot)
--- a/dom/battery/BatteryManager.cpp
+++ b/dom/battery/BatteryManager.cpp
@@ -22,25 +22,17 @@
 #define CHARGINGTIMECHANGE_EVENT_NAME    NS_LITERAL_STRING("chargingtimechange")
 
 DOMCI_DATA(BatteryManager, mozilla::dom::battery::BatteryManager)
 
 namespace mozilla {
 namespace dom {
 namespace battery {
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(BatteryManager)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BatteryManager,
-                                                  nsDOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BatteryManager,
-                                                nsDOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_0(BatteryManager, nsDOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BatteryManager)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBatteryManager)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BatteryManager)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BatteryManager, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BatteryManager, nsDOMEventTargetHelper)
--- a/dom/bindings/BindingGen.py
+++ b/dom/bindings/BindingGen.py
@@ -1,19 +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/.
 
 import os
 import cPickle
-import WebIDL
-from Configuration import *
+from Configuration import Configuration
 from Codegen import CGBindingRoot, replaceFileIfChanged
-# import Codegen in general, so we can set a variable on it
-import Codegen
 
 def generate_binding_header(config, outputprefix, webidlfile):
     """
     |config| Is the configuration object.
     |outputprefix| is a prefix to use for the header guards and filename.
     """
 
     filename = outputprefix + ".h"
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -162,16 +162,20 @@ DOMInterfaces = {
     'nativeType': 'nsIDocument',
 },
 {
     'nativeType': 'JSObject',
     'workers': True,
     'skipGen': True
 }],
 
+'DOMParser': {
+    'nativeType': 'nsDOMParser',
+},
+
 'DOMSettableTokenList': {
     'nativeType': 'nsDOMSettableTokenList',
     'binaryNames': {
         '__stringifier': 'Stringify'
     }
 },
 
 'DOMStringMap': {
@@ -190,21 +194,18 @@ DOMInterfaces = {
     'resultNotAddRefed': [ 'threshold', 'knee', 'ratio',
                            'reduction', 'attack', 'release' ],
     'binaryNames': {
         'release': 'getRelease'
     }
 }],
 
 'Element': {
-    # 'prefable' is True because some nodes are not on new bindings yet, so the
-    # wrapping code for Element return values needs to fall back to XPConnect as
-    # needed.
-    'prefable': True,
     'hasXPConnectImpls': True,
+    'register': False,
     'hasInstanceInterface': 'nsIDOMElement',
     'resultNotAddRefed': [
         'classList', 'attributes', 'children', 'firstElementChild',
         'lastElementChild', 'previousElementSibling', 'nextElementSibling',
         'getAttributeNode', 'getAttributeNodeNS'
     ]
 },
 
@@ -225,16 +226,20 @@ DOMInterfaces = {
     'concrete': False,
     'prefable': True,
 },
 {
     'workers': True,
     'concrete': False
 }],
 
+'FileHandle': {
+    'nativeType': 'mozilla::dom::file::FileHandle'
+},
+
 'FileList': {
     'nativeType': 'nsDOMFileList',
     'headerFile': 'nsDOMFile.h',
     'resultNotAddRefed': [ 'item' ]
 },
 
 'FileReaderSync': {
     'workers': True,
@@ -305,23 +310,16 @@ DOMInterfaces = {
     'notflattened': True
 },
 {
     'workers': True,
 }],
 
 'Node': {
     'nativeType': 'nsINode',
-    # 'prefable' is True because some nodes are not on new bindings yet, so the
-    # wrapping code for Node return values needs to fall back to XPConnect as
-    # needed.
-    'prefable': True,
-    # 'castable' is False because we don't support prefable castable arguments
-    # and we have Node arguments.
-    'castable': False,
     'hasXPConnectImpls': True,
     'hasInstanceInterface': 'nsIDOMNode',
     'resultNotAddRefed': [ 'ownerDocument', 'parentNode', 'parentElement',
                            'childNodes', 'firstChild', 'lastChild',
                            'previousSibling', 'nextSibling', 'insertBefore',
                            'appendChild', 'replaceChild', 'removeChild',
                            'attributes' ]
 },
@@ -536,16 +534,20 @@ DOMInterfaces = {
 {
     'nativeType': 'nsXMLHttpRequestUpload',
     'headerFile': 'nsXMLHttpRequest.h'
 },
 {
     'workers': True,
 }],
 
+'XMLSerializer': {
+    'nativeType': 'nsDOMSerializer',
+},
+
 ####################################
 # Test Interfaces of various sorts #
 ####################################
 
 'TestInterface' : {
         # Keep this in sync with TestExampleInterface
         'headerFile': 'TestBindingHeader.h',
         'register': False,
@@ -731,23 +733,27 @@ addExternalHTMLElement('HTMLOptGroupElem
 addExternalHTMLElement('HTMLVideoElement')
 addExternalIface('Attr')
 addExternalIface('CanvasGradient', headerFile='nsIDOMCanvasRenderingContext2D.h')
 addExternalIface('CanvasPattern', headerFile='nsIDOMCanvasRenderingContext2D.h')
 addExternalIface('ClientRect')
 addExternalIface('CSSRule')
 addExternalIface('CSSValue')
 addExternalIface('DocumentType', nativeType='nsIDOMDocumentType')
+addExternalIface('DOMRequest')
 addExternalIface('DOMStringList', nativeType='nsDOMStringList',
                  headerFile='nsDOMLists.h')
 addExternalIface('File')
 addExternalIface('HitRegionOptions', nativeType='nsISupports')
 addExternalIface('HTMLElement')
+addExternalIface('LockedFile')
 addExternalIface('MediaStream')
 addExternalIface('NamedNodeMap')
+addExternalIface('OutputStream', nativeType='nsIOutputStream',
+                 notflattened=True)
 addExternalIface('PaintRequest')
 addExternalIface('Principal', nativeType='nsIPrincipal',
                  headerFile='nsIPrincipal.h', notflattened=True)
 addExternalIface('SVGLength')
 addExternalIface('SVGMatrix')
 addExternalIface('SVGNumber')
 addExternalIface('SVGPathSeg')
 addExternalIface('SVGPoint')
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1,19 +1,20 @@
 # 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/.
 
 # Common codegen classes.
 
+import operator
 import os
+import re
 import string
-import operator
-
-from WebIDL import *
+
+from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType
 from Configuration import NoSuchDescriptorError
 
 AUTOGENERATED_WARNING_COMMENT = \
     "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
 ADDPROPERTY_HOOK_NAME = '_addProperty'
 FINALIZE_HOOK_NAME = '_finalize'
 TRACE_HOOK_NAME = '_trace'
 CONSTRUCT_HOOK_NAME = '_constructor'
@@ -1079,30 +1080,27 @@ class PropertyDefiner:
         # on the interface and interface prototype objects does not change when
         # pref control is added to members while still allowing us to define all
         # the members in the smallest number of JSAPI calls.
         assert(len(array) is not 0)
         lastPref = getPref(array[0]) # So we won't put a specTerminator
                                      # at the very front of the list.
         specs = []
         prefableSpecs = []
-        if doIdArrays:
-            prefableIds = []
 
         prefableTemplate = '  { true, &%s[%d] }'
         prefCacheTemplate = '&%s[%d].enabled'
         def switchToPref(props, pref):
             # Remember the info about where our pref-controlled
             # booleans live.
             if pref is not None:
                 props.prefCacheData.append(
                     (pref, prefCacheTemplate % (name, len(prefableSpecs)))
                     )
-            # Set up pointers to the new sets of specs and ids
-            # inside prefableSpecs and prefableIds
+            # Set up pointers to the new sets of specs inside prefableSpecs
             prefableSpecs.append(prefableTemplate %
                                  (name + "_specs", len(specs)))
 
         switchToPref(self, lastPref)
 
         for member in array:
             curPref = getPref(member)
             if lastPref != curPref:
@@ -2489,17 +2487,17 @@ for (uint32_t i = 0; i < length; ++i) {
         else:
             if forceOwningType:
                 declType = "OwningNonNull<" + typeName + ">"
             else:
                 declType = "NonNull<" + typeName + ">"
 
         templateBody = ""
         if descriptor.castable:
-            if descriptor.prefable:
+            if descriptor.prefable and not descriptor.hasXPConnectImpls:
                 raise TypeError("We don't support prefable castable object "
                                 "arguments (like %s), because we don't know "
                                 "how to handle them being preffed off" %
                                 descriptor.interface.identifier.name)
             if descriptor.interface.isConsequential():
                 raise TypeError("Consequential interface %s being used as an "
                                 "argument but flagged as castable" %
                                 descriptor.interface.identifier.name)
@@ -3202,17 +3200,17 @@ if (!returnArray) {
                 wrapMethod = "WrapNewBindingObject"
             else:
                 if not isCreator:
                     raise MethodNotCreatorError(descriptor.interface.identifier.name)
                 wrapMethod = "WrapNewBindingNonWrapperCachedObject"
             wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result)
             # We don't support prefable stuff in workers.
             assert(not descriptor.prefable or not descriptor.workers)
-            if not descriptor.prefable:
+            if not descriptor.prefable and not descriptor.hasXPConnectImpls:
                 # Non-prefable bindings can only fail to wrap as a new-binding object
                 # if they already threw an exception.  Same thing for
                 # non-prefable bindings.
                 failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
                           "%s" % exceptionCode)
             else:
                 if descriptor.notflattened:
                     raise TypeError("%s is prefable but not flattened; "
@@ -4447,25 +4445,29 @@ class CGMemberJITInfo(CGThing):
             return result
         raise TypeError("Illegal member type to CGPropertyJITInfo")
 
 def getEnumValueName(value):
     # Some enum values can be empty strings.  Others might have weird
     # characters in them.  Deal with the former by returning "_empty",
     # deal with possible name collisions from that by throwing if the
     # enum value is actually "_empty", and throw on any value
-    # containing chars other than [a-z] or '-' for now. Replace '-' with '_'.
-    value = value.replace('-', '_')
+    # containing non-ASCII chars for now. Replace all chars other than
+    # [0-9A-Za-z_] with '_'.
+    if re.match("[^\x20-\x7E]", value):
+        raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
+    if re.match("^[0-9]", value):
+        raise SyntaxError('Enum value "' + value + '" starts with a digit')
+    value = re.sub(r'[^0-9A-Za-z_]', '_', value)
+    if re.match("^_[A-Z]|__", value):
+        raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
     if value == "_empty":
         raise SyntaxError('"_empty" is not an IDL enum value we support yet')
     if value == "":
         return "_empty"
-    if not re.match("^[a-z_]+$", value):
-        raise SyntaxError('Enum value "' + value + '" contains characters '
-                          'outside [a-z_]')
     return MakeNativeName(value)
 
 class CGEnum(CGThing):
     def __init__(self, enum):
         CGThing.__init__(self)
         self.enum = enum
 
     def declare(self):
@@ -5400,17 +5402,16 @@ class CGProxySpecialOperation(CGPerSigna
     Base class for classes for calling an indexed or named special operation
     (don't use this directly, use the derived classes below).
     """
     def __init__(self, descriptor, operation):
         nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation))
         operation = descriptor.operations[operation]
         assert len(operation.signatures()) == 1
         signature = operation.signatures()[0]
-        extendedAttributes = descriptor.getExtendedAttributes(operation)
 
         (returnType, arguments) = signature
 
         # We pass len(arguments) as the final argument so that the
         # CGPerSignatureCall won't do any argument conversion of its own.
         CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName,
                                     False, descriptor, operation,
                                     len(arguments))
--- a/dom/bindings/ExampleGen.py
+++ b/dom/bindings/ExampleGen.py
@@ -1,19 +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/.
 
 import os
 import cPickle
-import WebIDL
-from Configuration import *
+from Configuration import Configuration
 from Codegen import CGExampleRoot, replaceFileIfChanged
-# import Codegen in general, so we can set a variable on it
-import Codegen
 
 def generate_interface_example(config, interfaceName):
     """
     |config| Is the configuration object.
     |interfaceName| is the name of the interface we're generating an example for.
     """
 
     root = CGExampleRoot(config, interfaceName)
--- a/dom/bindings/GlobalGen.py
+++ b/dom/bindings/GlobalGen.py
@@ -1,23 +1,20 @@
 # 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/.
 
 # We do one global pass over all the WebIDL to generate our prototype enum
 # and generate information for subsequent phases.
 
 import os
-import cStringIO
 import WebIDL
 import cPickle
-from Configuration import *
+from Configuration import Configuration
 from Codegen import GlobalGenRoots, replaceFileIfChanged
-# import Codegen in general, so we can set a variable on it
-import Codegen
 
 def generate_file(config, name, action):
 
     root = getattr(GlobalGenRoots, name)(config)
     if action is 'declare':
         filename = name + '.h'
         code = root.declare()
     else:
--- a/dom/bluetooth/BluetoothManager.cpp
+++ b/dom/bluetooth/BluetoothManager.cpp
@@ -20,25 +20,17 @@
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 
 using namespace mozilla;
 
 USING_BLUETOOTH_NAMESPACE
 
 DOMCI_DATA(BluetoothManager, BluetoothManager)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothManager)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothManager,
-                                                  nsDOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothManager,
-                                                nsDOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_0(BluetoothManager, nsDOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothManager)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBluetoothManager)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BluetoothManager)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothManager, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothManager, nsDOMEventTargetHelper)
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -15,25 +15,18 @@
 #include "DOMCameraControl.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 using namespace dom;
 
 DOMCI_DATA(CameraControl, nsICameraControl)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMCameraControl)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMCameraControl)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMCapabilities)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMCameraControl)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMCapabilities)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_1(nsDOMCameraControl,
+                           mDOMCapabilities)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraControl)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsICameraControl)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraControl)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraControl)
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -13,25 +13,18 @@
 #include "DictionaryHelpers.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 using namespace dom;
 
 DOMCI_DATA(CameraManager, nsIDOMCameraManager)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMCameraManager)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMCameraManager)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCameraThread)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMCameraManager)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCameraThread)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_1(nsDOMCameraManager,
+                           mCameraThread)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCameraManager)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraManager)
 NS_INTERFACE_MAP_END
 
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/Makefile.in
@@ -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/.
+
+DEPTH            = @DEPTH@
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+relativesrcdir   = @relativesrcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+PARALLEL_DIRS = interfaces src
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/interfaces/Makefile.in
@@ -0,0 +1,29 @@
+# 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/.
+
+DEPTH            = @DEPTH@
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+XPIDL_MODULE = dom_cellbroadcast
+
+include $(topsrcdir)/dom/dom-config.mk
+
+XPIDLSRCS = \
+  nsIDOMMozCellBroadcast.idl \
+  nsIDOMMozCellBroadcastEvent.idl \
+  nsIDOMMozCellBroadcastMessage.idl \
+  nsINavigatorCellBroadcast.idl \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+XPIDL_FLAGS += \
+  -I$(topsrcdir)/dom/base \
+  -I$(topsrcdir)/dom/interfaces/base \
+  -I$(topsrcdir)/dom/interfaces/events \
+  $(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/interfaces/nsIDOMMozCellBroadcast.idl
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIDOMEventTarget.idl"
+
+/**
+ * Cell Broadcast short message service (CBS) permits a number of
+ * unacknowledged general CBS messages to be broadcast to all receivers within
+ * a particular region.
+ */
+[scriptable, builtinclass, uuid(06bf2607-cd01-4307-9063-b6eac13b4613)]
+interface nsIDOMMozCellBroadcast : nsIDOMEventTarget
+{
+  [implicit_jscontext] attribute jsval onreceived;
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/interfaces/nsIDOMMozCellBroadcastEvent.idl
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIDOMEvent.idl"
+
+interface nsIDOMMozCellBroadcastMessage;
+
+[scriptable, builtinclass, uuid(0be33bb9-930f-410b-8d61-a0fc5f4dcf7d)]
+interface nsIDOMMozCellBroadcastEvent : nsIDOMEvent
+{
+  //[binaryname(MessageMoz)]
+  readonly attribute nsIDOMMozCellBroadcastMessage message;
+
+  [noscript] void initMozCellBroadcastEvent(in DOMString aType,
+                                            in boolean aCanBubble,
+                                            in boolean aCancelable,
+                                            in nsIDOMMozCellBroadcastMessage aMessage);
+};
+
+dictionary MozCellBroadcastEventInit : EventInit
+{
+  //[binaryname(MessageMoz)]
+  nsIDOMMozCellBroadcastMessage message;
+};
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/interfaces/nsIDOMMozCellBroadcastMessage.idl
@@ -0,0 +1,90 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMMozCellBroadcastEtwsInfo;
+
+/**
+ * MozCellBroadcastMessage encapsulates Cell Broadcast short message service
+ * (CBS) messages.
+ */
+[scriptable, uuid(cb210e8b-ad9c-4052-b70c-02ec0c25d669)]
+interface nsIDOMMozCellBroadcastMessage : nsISupports
+{
+  /**
+   * Indication of the geographical area over which the Message Code is unique,
+   * and the display mode.
+   *
+   * Possible values are: "cell-immediate", "plmn", "location-area" and "cell".
+   */
+  readonly attribute DOMString geographicalScope;
+
+  /**
+   * The Message Code differentiates between messages from the same source and
+   * type (e.g., with the same Message Identifier).
+   */
+  readonly attribute unsigned short messageCode;
+
+  /**
+   * Source and type of the message. For example, "Automotive Association"
+   * (= source), "Traffic Reports" (= type) could correspond to one value. The
+   * Message Identifier is coded in binary.
+   */
+  readonly attribute unsigned short messageId;
+
+  /**
+   * ISO-639-1 language code for this message. Null if unspecified.
+   */
+  readonly attribute DOMString language;
+
+  /**
+   * Text message carried by the message.
+   */
+  readonly attribute DOMString body;
+
+  /**
+   * Possible values are "normal", "class-0", "class-1", "class-2", "class-3",
+   * "user-1", and "user-2".
+   */
+  readonly attribute DOMString messageClass;
+
+  /**
+   * System time stamp at receival.
+   */
+  readonly attribute jsval timestamp; // jsval is for Date.
+
+  /**
+   * Additional ETWS-specific info.
+   */
+  readonly attribute nsIDOMMozCellBroadcastEtwsInfo etws;
+};
+
+/**
+ * ETWS (Earthquake and Tsunami Warning service) Primary Notification message
+ * specific information.
+ */
+[scriptable, uuid(af009d9a-f5e8-4573-a6ee-a85118465bed)]
+interface nsIDOMMozCellBroadcastEtwsInfo : nsISupports
+{
+  /**
+   * Warning type. Possible values are "earthquake", "tsunami",
+   * "earthquake-tsunami", "test" and "other".
+   */
+  readonly attribute ACString warningType;
+
+  /**
+   * Emergency user alert indication. It is used to command mobile terminals to
+   * activate emergency user alert upon the reception of ETWS primary
+   * notification.
+   */
+  readonly attribute boolean emergencyUserAlert;
+
+  /**
+   * Message popup indication. It is used to command mobile terminals to
+   * activate message popup upon the reception of ETWS primary notification.
+   */
+  readonly attribute boolean popup;
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/interfaces/nsINavigatorCellBroadcast.idl
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMMozCellBroadcast;
+
+[scriptable, uuid(010af082-22c5-471a-be21-0d738c50df67)]
+interface nsIMozNavigatorCellBroadcast : nsISupports
+{
+  readonly attribute nsIDOMMozCellBroadcast mozCellBroadcast;
+};
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/src/CellBroadcast.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CellBroadcast.h"
+#include "nsIDOMMozCellBroadcastEvent.h"
+#include "nsIDOMMozCellBroadcastMessage.h"
+#include "mozilla/Services.h"
+#include "nsDOMClassInfo.h"
+#include "nsRadioInterfaceLayer.h"
+#include "GeneratedEvents.h"
+
+DOMCI_DATA(MozCellBroadcast, mozilla::dom::CellBroadcast)
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * CellBroadcastCallback Implementation.
+ */
+
+class CellBroadcastCallback : public nsIRILCellBroadcastCallback
+{
+private:
+  CellBroadcast* mCellBroadcast;
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_FORWARD_NSIRILCELLBROADCASTCALLBACK(mCellBroadcast->)
+
+  CellBroadcastCallback(CellBroadcast* aCellBroadcast);
+};
+
+NS_IMPL_ISUPPORTS1(CellBroadcastCallback, nsIRILCellBroadcastCallback)
+
+CellBroadcastCallback::CellBroadcastCallback(CellBroadcast* aCellBroadcast)
+  : mCellBroadcast(aCellBroadcast)
+{
+  MOZ_ASSERT(mCellBroadcast, "Null pointer!");
+}
+
+/**
+ * CellBroadcast Implementation.
+ */
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(CellBroadcast)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CellBroadcast,
+                                                  nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CellBroadcast,
+                                                nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CellBroadcast)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozCellBroadcast)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozCellBroadcast)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozCellBroadcast)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(CellBroadcast, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(CellBroadcast, nsDOMEventTargetHelper)
+
+CellBroadcast::CellBroadcast(nsPIDOMWindow *aWindow,
+                             nsIRILContentHelper *aRIL)
+  : mRIL(aRIL)
+{
+  BindToOwner(aWindow);
+
+  mCallback = new CellBroadcastCallback(this);
+
+  nsresult rv = mRIL->RegisterCellBroadcastCallback(mCallback);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
+                   "Failed registering Cell Broadcast callback with RIL");
+
+  rv = mRIL->RegisterCellBroadcastMsg();
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
+                   "Failed registering Cell Broadcast callback with RIL");
+}
+
+CellBroadcast::~CellBroadcast()
+{
+  MOZ_ASSERT(mRIL && mCallback, "Null pointer!");
+
+  mRIL->UnregisterCellBroadcastCallback(mCallback);
+}
+
+NS_IMPL_EVENT_HANDLER(CellBroadcast, received)
+
+// Forwarded nsIRILCellBroadcastCallback methods
+
+NS_IMETHODIMP
+CellBroadcast::NotifyMessageReceived(nsIDOMMozCellBroadcastMessage* aMessage)
+{
+  nsCOMPtr<nsIDOMEvent> event;
+  NS_NewDOMMozCellBroadcastEvent(getter_AddRefs(event), nullptr, nullptr);
+
+  nsCOMPtr<nsIDOMMozCellBroadcastEvent> ce = do_QueryInterface(event);
+  nsresult rv = ce->InitMozCellBroadcastEvent(NS_LITERAL_STRING("received"),
+                                              true, false, aMessage);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return DispatchTrustedEvent(ce);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+nsresult
+NS_NewCellBroadcast(nsPIDOMWindow* aWindow,
+                    nsIDOMMozCellBroadcast** aCellBroadcast)
+{
+  nsPIDOMWindow* innerWindow = aWindow->IsInnerWindow() ?
+    aWindow :
+    aWindow->GetCurrentInnerWindow();
+
+  nsCOMPtr<nsIRILContentHelper> ril =
+    do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+  NS_ENSURE_STATE(ril);
+
+  nsRefPtr<mozilla::dom::CellBroadcast> cb =
+    new mozilla::dom::CellBroadcast(innerWindow, ril);
+  cb.forget(aCellBroadcast);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/src/CellBroadcast.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_CellBroadcast_h__
+#define mozilla_dom_CellBroadcast_h__
+
+#include "nsDOMEventTargetHelper.h"
+#include "nsIDOMMozCellBroadcast.h"
+#include "nsIRadioInterfaceLayer.h"
+#include "mozilla/Attributes.h"
+
+class nsPIDOMWindow;
+
+class nsIRILContentHelper;
+
+namespace mozilla {
+namespace dom {
+
+class CellBroadcast MOZ_FINAL : public nsDOMEventTargetHelper
+                              , public nsIDOMMozCellBroadcast
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMMOZCELLBROADCAST
+
+  /**
+   * Class CellBroadcast doesn't actually inherit nsIRILCellBroadcastCallback.
+   * Instead, it owns an nsIRILCellBroadcastCallback derived instance mCallback
+   * and passes it to RILContentHelper. The onreceived events are first
+   * delivered to mCallback and then forwarded to its owner, CellBroadcast.
+   */
+  NS_DECL_NSIRILCELLBROADCASTCALLBACK
+
+  NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CellBroadcast,
+                                           nsDOMEventTargetHelper)
+
+  CellBroadcast() MOZ_DELETE;
+  CellBroadcast(nsPIDOMWindow *aWindow,
+                nsIRILContentHelper* aRIL);
+  ~CellBroadcast();
+
+private:
+  nsCOMPtr<nsIRILContentHelper> mRIL;
+  nsCOMPtr<nsIRILCellBroadcastCallback> mCallback;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+nsresult
+NS_NewCellBroadcast(nsPIDOMWindow* aWindow,
+                    nsIDOMMozCellBroadcast** aCellBroadcast);
+
+#endif // mozilla_dom_CellBroadcast_h__
new file mode 100644
--- /dev/null
+++ b/dom/cellbroadcast/src/Makefile.in
@@ -0,0 +1,30 @@
+# 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/.
+
+DEPTH            = @DEPTH@
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+LIBRARY_NAME     = dom_cellbroadcast_s
+XPIDL_MODULE     = dom_cellbroadcast
+LIBXUL_LIBRARY   = 1
+FORCE_STATIC_LIB = 1
+FAIL_ON_WARNINGS := 1
+
+include $(topsrcdir)/dom/dom-config.mk
+
+EXPORTS_NAMESPACES = mozilla/dom
+
+EXPORTS_mozilla/dom = \
+  CellBroadcast.h \
+  $(NULL)
+
+CPPSRCS = \
+  CellBroadcast.cpp \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -851,25 +851,19 @@ private:
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageCursorRequest)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageCursorRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageCursorRequest)
-NS_IMPL_CYCLE_COLLECTION_CLASS(DeviceStorageCursorRequest)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DeviceStorageCursorRequest)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCursor)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DeviceStorageCursorRequest)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCursor)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_1(DeviceStorageCursorRequest,
+                           mCursor)
 
 
 class PostErrorEvent : public nsRunnable
 {
 public:
   PostErrorEvent(nsRefPtr<DOMRequest>& aRequest, const char* aMessage)
   {
     mRequest.swap(aRequest);
@@ -1668,41 +1662,26 @@ private:
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageRequest)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageRequest)
-NS_IMPL_CYCLE_COLLECTION_CLASS(DeviceStorageRequest)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DeviceStorageRequest)
-NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest)
-NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
-NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlob)
-NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceStorage)
-NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DeviceStorageRequest)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBlob)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorage)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_5(DeviceStorageRequest,
+                           mRequest,
+                           mWindow,
+                           mBlob,
+                           mDeviceStorage,
+                           mListener)
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMDeviceStorage)
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_0(nsDOMDeviceStorage, nsDOMEventTargetHelper)